Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions final_ls_cuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
##@file lotsizing_lazy.py
# @brief solve the single-item lot-sizing problem.
"""
Approaches:
- sils: solve the problem using the standard formulation
- sils_cut: solve the problem using cutting planes

Copyright (c) by Joao Pedro PEDROSO and Mikio KUBO, 2012
"""
from pyscipopt import Model, Conshdlr, quicksum, multidict, SCIP_RESULT, SCIP_PRESOLTIMING, SCIP_PROPTIMING


class Conshdlr_sils(Conshdlr):

def addcut(self, checkonly, sol):
D, Ts = self.data
y, x, I = self.model.data
cutsadded = False

for ell in Ts:
lhs = 0
S, L = [], []
for t in range(1, ell + 1):
yt = self.model.getSolVal(sol, y[t])
xt = self.model.getSolVal(sol, x[t])
if D[t, ell] * yt < xt:
S.append(t)
lhs += D[t, ell] * yt
else:
L.append(t)
lhs += xt
if lhs < D[1, ell]:
if checkonly:
return True
else:
# add cutting plane constraint
self.model.addCons(quicksum([x[t] for t in L]) + \
quicksum(D[t, ell] * y[t] for t in S)
>= D[1, ell], removable=True)
cutsadded = True
return cutsadded

def conscheck(self, constraints, solution, checkintegrality, checklprows, printreason, completely):
if self.addcut(checkonly=True, sol=solution):
return {"result": SCIP_RESULT.INFEASIBLE}
else:
return {"result": SCIP_RESULT.FEASIBLE}

def consenfolp(self, constraints, nusefulconss, solinfeasible):
if self.addcut(checkonly=False, sol=None):
return {"result": SCIP_RESULT.CONSADDED}
else:
return {"result": SCIP_RESULT.FEASIBLE}

def conslock(self, constraint, locktype, nlockspos, nlocksneg):
pass


def sils(T, f, c, d, h):
"""sils -- LP lotsizing for the single item lot sizing problem
Parameters:
- T: number of periods
- P: set of products
- f[t]: set-up costs (on period t)
- c[t]: variable costs
- d[t]: demand values
- h[t]: holding costs
Returns a model, ready to be solved.
"""
model = Model("single item lotsizing")
Ts = range(1, T + 1)
M = sum(d[t] for t in Ts)
y, x, I = {}, {}, {}
for t in Ts:
y[t] = model.addVar(vtype="I", ub=1, name="y(%s)" % t)
x[t] = model.addVar(vtype="C", ub=M, name="x(%s)" % t)
I[t] = model.addVar(vtype="C", name="I(%s)" % t)
I[0] = 0

for t in Ts:
model.addCons(x[t] <= M * y[t], "ConstrUB(%s)" % t)
model.addCons(I[t - 1] + x[t] == I[t] + d[t], "FlowCons(%s)" % t)

model.setObjective( \
quicksum(f[t] * y[t] + c[t] * x[t] + h[t] * I[t] for t in Ts), \
"minimize")

model.data = y, x, I
return model


def sils_cut(T, f, c, d, h, conshdlr):
"""solve_sils -- solve the lot sizing problem with cutting planes
- start with a relaxed model
- used lazy constraints to elimitate fractional setup variables with cutting planes
Parameters:
- T: number of periods
- P: set of products
- f[t]: set-up costs (on period t)
- c[t]: variable costs
- d[t]: demand values
- h[t]: holding costs
Returns the final model solved, with all necessary cuts added.
"""
Ts = range(1, T + 1)

model = sils(T, f, c, d, h)
y, x, I = model.data

# relax integer variables
for t in Ts:
model.chgVarType(y[t], "C")
model.addVar(vtype="B", name="fake") # for making the problem MIP

# compute D[i,j] = sum_{t=i}^j d[t]
D = {}
for t in Ts:
s = 0
for j in range(t, T + 1):
s += d[j]
D[t, j] = s

# include the lot sizing constraint handler
model.includeConshdlr(conshdlr, "SILS", "Constraint handler for single item lot sizing",
sepapriority=0, enfopriority=-1, chckpriority=-1, sepafreq=-1, propfreq=-1,
eagerfreq=-1, maxprerounds=0, delaysepa=False, delayprop=False, needscons=False,
presoltiming=SCIP_PRESOLTIMING.FAST, proptiming=SCIP_PROPTIMING.BEFORELP)
conshdlr.data = D, Ts

model.data = y, x, I
return model


def mk_example():
"""mk_example: book example for the single item lot sizing"""
T = 5
_, f, c, d, h = multidict({
1: [3, 1, 5, 1],
2: [3, 1, 7, 1],
3: [3, 3, 3, 1],
4: [3, 3, 6, 1],
5: [3, 3, 4, 1],
})

return T, f, c, d, h


if __name__ == "__main__":
T, f, c, d, h = mk_example()

model = sils(T, f, c, d, h)
y, x, I = model.data
model.optimize()
sils_obj = model.getObjVal()
print("\nOptimal value [standard]:", model.getObjVal())
print("%8s%8s%8s%8s%8s%8s%12s%12s" % ("t", "fix", "var", "h", "dem", "y", "x", "I"))
for t in range(1, T + 1):
print("%8d%8d%8d%8d%8d%8.1f%12.1f%12.1f" % (
t, f[t], c[t], h[t], d[t], model.getVal(y[t]), model.getVal(x[t]), model.getVal(I[t])))

conshdlr = Conshdlr_sils()
model = sils_cut(T, f, c, d, h, conshdlr)
model.setBoolParam("misc/allowstrongdualreds", 0)
model.optimize()
sils_cut_obj = model.getObjVal()
y, x, I = model.data
print("\nOptimal value [cutting planes]:", model.getObjVal())
print("%8s%8s%8s%8s%8s%8s%12s%12s" % ("t", "fix", "var", "h", "dem", "y", "x", "I"))
for t in range(1, T + 1):
print("%8d%8d%8d%8d%8d%8.1f%12.1f%12.1f" % (
t, f[t], c[t], h[t], d[t], model.getVal(y[t]), model.getVal(x[t]), model.getVal(I[t])))

assert abs(sils_obj - sils_cut_obj) <= 1e-6
Loading