Issues implementing rolling horizon methodology in supply chain problem
AnsweredHi,
I am fairly new to programming optimization models using Gurobi. This time, I am working on an optimization model using rolling horizon methodology.
The model is intended to exemplify the operation of a supply chain that has a main supplier, a backup supplier and a manufacturer. In addition, there are two types of inventory: (1) raw material inventory and (2) finished product inventory. The model is supposed to minimize the total cost for the given time horizon (4 periods) taking into account the constraints.
When I run m.printAttr('X'), it can be seent that the solutions for each period are not fixed, but rather, for example, the values of the variables in period 1 are changed by the model when it optimizes for period 2 and again when it optimizes for period 3, and so on.
I ran m.display() and saw that the objective function only shows the total cost for the last period but not the sum of the total cost for all periods. I am not sure if that is the cause of the problem I mentioned previously. In any case, below I add the code to which I referred and where one can reproduce the solution found by the model.
import gurobipy as gp
from gurobipy import GRB
from itertools import product
from math import sqrt
import numpy as np
import pandas as pd
# Parameters
demand = [1980,1980,3465,3465]
main_supplier_availability = [0.99,0.99,0.49,0.49]
backup_supplier_availability = [0.99,0.99, 0.49,0.49]
manufacturer_availability = [0.99, 0.99, 0.49, 0.49]
K = 4300
KF = 4300
CO_main_sup = 5
CO_backup_sup = 8
CIM = 1.5
CIP = 1.5
CP = 5
CF = 200
CE = 8
# Variables
X ={}
X_backup = {}
IM = {}
IP = {}
F = {}
E = {}
P = {}
# Model
m = gp.Model("test")
# First solution variables
X[0] = m.addVar(name="main_supplier_orders0", vtype=GRB.INTEGER, lb = 0)
X_backup[0] = m.addVar(name="backup_supplier_orders0", vtype=GRB.INTEGER, lb = 0)
IM[0] = m.addVar(name="raw_material_inventory0", vtype=GRB.INTEGER, lb = 0)
IP[0] = m.addVar(name="finished_product_inventory0", vtype=GRB.INTEGER, lb = 0)
F[0] = m.addVar(name="shortages0", vtype=GRB.INTEGER, lb = 0)
E[0] = m.addVar(name="units_sent0", vtype=GRB.INTEGER, lb = 0)
P[0] = m.addVar(name="units_produced0", vtype=GRB.INTEGER, lb = 0)
# Set the initial values of the variables
IP_start = 200
IM_start = 50
F_start = 0
# Objective: Minimize cost
order_cost_main_sup = CO_main_sup*X[0]
order_cost_backup_sup = CO_backup_sup*X_backup[0]
material_inv_cost = CIM*IM[0]
finished_prod_cost = CIP*IP[0]
shortage_cost = CF*F[0]
production_cost = CP*P[0]
delivery_cost = CE*E[0]
obj1 = order_cost_main_sup + order_cost_backup_sup + delivery_cost + material_inv_cost + finished_prod_cost + shortage_cost + production_cost
m.setObjective(obj1)
# First solution constraints
# Service level constraint
m.addConstr((-1*((E[0] - F_start)/demand[0]) <= -0.5),
name="constraint_f20")
# Main supplier capacity constraint
m.addConstr(X[0] <= K*main_supplier_availability[0],
name="main_sup_capacity0")
# Backup supplier capacity constraint
m.addConstr(X_backup[0] <= K*backup_supplier_availability[0],
name= "backup_sup_capacity0")
# Compute inventory level of raw material for first period
m.addConstr(IM[0] == IM_start + X[0] + X_backup[0] - P[0],
name="compute_material_inv0")
# Compute inventory level of finished product for first period
m.addConstr(IP[0] == IP_start + P[0] - E[0],
name="compute_product_inv0")
# Compute shortages for first period
m.addConstr(F[0] == F_start + demand[0] - E[0],
name="compute_shortages0")
# Manufacturer capacity constraint
m.addConstr(P[0]<= KF*manufacturer_availability[0],
name="manuf_capacity0")
m.optimize()
m.printAttr('X')
for t in range(1, 4):
# Sequential solution variables
X[t] = m.addVar(name="main_supplier_orders"+str(t), vtype=GRB.INTEGER, lb = 0)
X_backup[t] = m.addVar(name="backup_supplier_orders"+str(t), vtype=GRB.INTEGER, lb = 0)
IM[t] = m.addVar(name="raw_material_inventory"+str(t), vtype=GRB.INTEGER, lb = 0)
IP[t] = m.addVar(name="finished_product_inventory"+str(t), vtype=GRB.INTEGER, lb = 0)
F[t] = m.addVar(name="shortages"+str(t), vtype=GRB.INTEGER, lb = 0)
E[t] = m.addVar(name="units_sent"+str(t), vtype=GRB.INTEGER, lb = 0)
P[t] = m.addVar(name="units_produced"+str(t), vtype=GRB.INTEGER, lb = 0)
# Objective: Minimize cost
order_cost_main_sup = CO_main_sup*X[t]
order_cost_backup_sup = CO_backup_sup*X_backup[t]
material_inv_cost = CIM*IM[t]
finished_prod_cost = CIP*IP[t]
shortage_cost = CF*F[t]
production_cost = CP*P[t]
delivery_cost = CE*E[t]
obj1 = order_cost_main_sup + order_cost_backup_sup + delivery_cost + material_inv_cost + finished_prod_cost + shortage_cost + production_cost
m.setObjective(obj1)
# Sequential solution constraints
# Service level constraint
m.addConstr((-1*((E[t] - F[t-1])/demand[t]) <= -0.5),
name="constraint_f2"+str(t))
# Main supplier capacity constraint
m.addConstr(X[t] <= K*main_supplier_availability[t],
name="main_sup_capacity"+str(t))
# Backup supplier capacity constraint
m.addConstr(X_backup[t] <= K*backup_supplier_availability[t],
name= "backup_sup_capacity"+str(t))
# Compute inventory level of raw material for each period
m.addConstr(IM[t] == IM[t-1] + X[t] + X_backup[t] - P[t],
name="compute_material_inv" + str(t))
# Compute inventory level of finished product for each period
m.addConstr(IP[t] == IP[t-1] + P[t] - E[t],
name="compute_product_inv"+str(t))
# Compute shortages for each period
m.addConstr(F[t] == F[t-1] + demand[t] - E[t],
name="compute_shortages"+str(t))
# Manufacturer capacity constraint
m.addConstr(P[t]<= KF*manufacturer_availability[t],
name="manuf_capacity"+str(t))
m.optimize()
m.printAttr('X')
I would appreciate any help or feedback on this that would help me address the aforementioned problem.
-
Hi Jose,
I am not sure whether I understand your problem correctly. But you might be not aware of the following:
When you generate a model and optimize it in a first step and then you add additional variables and/or constraints in a second step, the model is only expanded, no fixations are done. If then the model is optimized, the solution of the first step could be used as a starting solution but all variables are free to be changed. Hence, it is perfectly possible that the optimal solution in the second step has completely different variable values compared to the optimal solution in the first step.If you want to solve the expanded model based on the solution values of the initial model, you need to fix the variables to their optimal value. You could do this for example by fixing the variables bounds of the previous period directly after the for loop is started:
for t in range(1, 4):
if m.Status == gp.GRB.OPTIMAL:
X[t-1].lb = X[t-1].X
X[t-1].ub = X[t-1].X
X_backup[t-1].lb = X_backup[t-1].X
X_backup[t-1].ub = X_backup[t-1].X
...Let me know if this does not address your issue,
Marika0 -
Hi Marika,
As you mentioned, I was not aware that when adding new steps, the model did not do fixations, but expanded.
Your input has helped me to solve the expanded model based on the initial solution, which is what I wanted to do although I may not have been clear before.
Thank you very much for taking the time to clarify my question, it has been valuable for me.
0
Please sign in to leave a comment.
Comments
2 comments