284. Linear Programming

284.1. Mathematical Programs

284.1.1. Formulation


Vector

Matrix

284.1.2. Transformation


284.1.3. Constraints


284.1.4. Feasible Solutions

284.1.5. Feasible Region and Optimal Solution

284.1.6. Binding Constraint

Let be an inequality constraint and be a solution. is binding at if

Example

284.1.7. Strict and Weak Inequalities

284.1.8. Graphical Approach

Example

284.1.9. Types of LP

284.1.10. LP Formulation

Example

Production

We produce desks and tables

  • Producing a desk requires 3 units of wood, 1 hour of labor and 50 minutes of machine time
  • Producing a table requires 5 units of wood, 2 hours of labor and 20 minutes of machine time

For each day, we have

  • 200 workers that each work 8 hours
  • 50 machines that each run for 16 hours
  • A supply of 3600 units of wood

Desks and tables are sold $700 and $900 per unit, respectively

Everything that is produced is sold

1. Define Variables

: number of desks produced per day

: number of tables produced per day

2. Formulate Objective Function

3. Formulate Constraints

Resource Consumption per Total supply
Desk Table
Wood 3 units 5 units 3600 units
Labor 1 hours 2 hours 200 workers 8 hours
= 1600 hours
Machine
time
50 minutes 20 minutes 50 machines 16 hours
= 800 hours


4. Complete Formulation

Compact Formulation

Where:

  • : number of products
  • : number of resources
  • : indices of products
  • : indices of resources
  • : unit sale price of product
  • : supply limit of resource
  • : unit of resource to produce one unit of product
  • : production quantity for product ,
model.py
from pyomo.environ import *
from pyomo.dataportal import DataPortal

model = AbstractModel()

# Sets
model.Products = Set()
model.Resources = Set()

# Parameters
model.Profit = Param(model.Products)
model.Supply = Param(model.Resources)
model.Consumption = Param(model.Resources, model.Products)

# Variables
model.x = Var(model.Products, domain=NonNegativeReals)

# Objective: Maximize profit
def objective_rule(model):
    return sum(model.Profit[j] * model.x[j] for j in model.Products)
model.OBJ = Objective(rule=objective_rule, sense=maximize)

# Constraints: Do not exceed resource supply
def constraint_rule(model, i):
    return sum(model.Consumption[i, j] * model.x[j] for j in model.Products) <= model.Supply[i]
model.ResourceConstraint = Constraint(model.Resources, rule=constraint_rule)

# Load data from .dat file
data = DataPortal()
data.load(filename='data.dat', model=model)

# Create an instance of the model
instance = model.create_instance(data)

# Create solver
solver = SolverFactory('glpk')
solver.options['tmlim'] = 60

# Solve with solver timeout (optional)
results = solver.solve(instance, tee=True)

# Display results
instance.display()
data.dat
set Products := Desk Table ;
set Resources := Wood Labor Machine ;

param Profit :=
Desk   700
Table  900 ;

param Supply :=
Wood    3600
Labor   1600
Machine 48000 ;

param Consumption:
          Desk Table :=
Wood        3     5
Labor       1     2
Machine    50    20 ;

Output:

  Variables:
  x : Size=2, Index=Products
      Key   : Lower : Value            : Upper : Fixed : Stale : Domain
      Desk :     0 : 884.210526315789 :  None : False : False : NonNegativeReals
      Table :     0 : 189.473684210526 :  None : False : False : NonNegativeReals

Objectives:
  OBJ : Size=1, Index=None, Active=True
      Key  : Active : Value
      None :   True : 789473.6842105257

Constraints:
  ResourceConstraint : Size=3
      Key     : Lower : Body              : Upper
        Labor :  None : 1263.157894736841 :  1600.0
      Machine :  None : 47999.99999999997 : 48000.0
        Wood :  None : 3599.999999999997 :  3600.0

)

Example

Multi Period (Inventory)

We produce and sell products

For the coming 4 days, the demand will be

  • Days 1: 100
  • Days 2: 150
  • Days 3: 200
  • Days 4: 170

The unit production costs are different for different days

  • Days 1: $9
  • Days 2: $12
  • Days 3: $10
  • Days 4: $12

Prices are all fixed. So maximizing profit is equivalent to minimizing costs.

We may store products and sell them later. Inventory cost is $1 per unit per day.

E.g., producing 620 units on day 1 to fulfill all demands:

  • : inevntory at the end of day (i.e., beginning inventory for day )

  • : units produced on day

  • : demand (or sales) on dat

  • : inventory at the end of day

Inventory costs are calculated according to ending inventory

  • : production quantity of day ,

  • : ending inventory of day ,

Objective function:

Inventory balancing constraints:

  • Day 1:

  • Day 2:

  • Day 3:

  • Day 4:

Demand fulfillment constraints:

Non-negativity constraints:

Complete Formulation

Simplification (Redundant Constraints)

  • Inventory balancing & non-negativity imply fulfillment

    • E.g., in day 1, and means
  • No need to have ending inventory in period 4 (costly but useless)

Compact Formulation

Where:

  • : demand on day
  • : ending inventory of day
  • : unit production cost on day
Example

Personnel Scheduling

Scheduling employees

Each employee must work for 5 consecutive days and then take 2 consecutive rest days

Number of employees required for each day

Mon Tue Wed Thu Fri Sat Sun
110 80 150 30 70 160 120

Seven shifts

  • Mon to Fri
  • Tue to Sat

Minimize number of employees hired

Let be the number of employees assigned to work from day for 5 consecutive days

Objective function:

Demand Fullfilment Constraints

  • 110 employeeds needed Monday

An employee that works on Tues () or Wed () does not work on Mon ()

  • 80 employees needed Tuesday

An employee that works on Wed () or Thu () does not work on Tue ()

  • 120 employees needed Sunday

An employee that works on Mon () or Tue () does not work on Sun ()

Non-Nagativity Constraints

Complete Formulation

model.py
from pyomo.environ import *
from pyomo.dataportal import DataPortal

model = AbstractModel()

# Sets
model.Days = Set(ordered=True)           # Days 1..7 (Mon..Sun)
model.Shifts = Set(ordered=True)         # Shifts starting on days 1..7

# Parameters
model.Demand = Param(model.Days)         # Daily staffing requirements
model.Cover = Param(model.Days, model.Shifts, within=Binary)  # Coverage matrix (1 if shift j covers day i)

# Variables
model.x = Var(model.Shifts, domain=NonNegativeReals)

# Objective: Minimize total employees hired
def obj_rule(model):
    return sum(model.x[s] for s in model.Shifts)
model.OBJ = Objective(rule=obj_rule, sense=minimize)

# Constraints: Cover daily demand
def demand_rule(model, d):
    return sum(model.Cover[d, s] * model.x[s] for s in model.Shifts) >= model.Demand[d]
model.DemandConstraint = Constraint(model.Days, rule=demand_rule)

# Load data from .dat file
data = DataPortal()
data.load(filename='data.dat', model=model)

# Create an instance of the model
instance = model.create_instance(data)

# Create solver
solver = SolverFactory('glpk')
solver.options['tmlim'] = 60

# Solve with solver timeout (optional)
results = solver.solve(instance, tee=True)

# Display results
instance.display()
data.dat
set Days := Mon Tue Wed Thu Fri Sat Sun ;
set Shifts := s1 s2 s3 s4 s5 s6 s7 ;

param Demand :=
Mon 110
Tue 80
Wed 150
Thu 30
Fri 70
Sat 160
Sun 120 ;

# Each shift covers 5 consecutive days starting on the shift's day
# 1 if shift s_j covers day d_i, 0 otherwise

param Cover:
        s1 s2 s3 s4 s5 s6 s7 :=
Mon     1  0  0  1  1  1  1
Tue     1  1  0  0  1  1  1
Wed     1  1  1  0  0  1  1
Thu     1  1  1  1  0  0  1
Fri     1  1  1  1  1  0  0
Sat     0  1  1  1  1  1  0
Sun     0  0  1  1  1  1  1 ;

Output:

  Variables:
  x : Size=7, Index=Shifts
      Key : Lower : Value            : Upper : Fixed : Stale : Domain
        s1 :     0 : 3.33333333333333 :  None : False : False : NonNegativeReals
        s2 :     0 :             40.0 :  None : False : False : NonNegativeReals
        s3 :     0 : 13.3333333333333 :  None : False : False : NonNegativeReals
        s4 :     0 :              0.0 :  None : False : False : NonNegativeReals
        s5 :     0 : 13.3333333333333 :  None : False : False : NonNegativeReals
        s6 :     0 : 93.3333333333333 :  None : False : False : NonNegativeReals
        s7 :     0 :              0.0 :  None : False : False : NonNegativeReals

Objectives:
  OBJ : Size=1, Index=None, Active=True
      Key  : Active : Value
      None :   True : 163.33333333333323

Constraints:
  DemandConstraint : Size=7
      Key : Lower : Body               : Upper
      Fri :  70.0 :  69.99999999999993 :  None
      Mon : 110.0 : 109.99999999999993 :  None
      Sat : 160.0 :  159.9999999999999 :  None
      Sun : 120.0 :  119.9999999999999 :  None
      Thu :  30.0 :  56.66666666666663 :  None
      Tue :  80.0 : 149.99999999999994 :  None
      Wed : 150.0 : 149.99999999999994 :  None