""" Column Generation Functions Authors: Antony Phillips, Dr Stuart Mitchell 2008 """ # Import PuLP modeler functions from pulp import * class Pattern: """ Information on a specific pattern in the SpongeRoll Problem """ cost = 1 trimValue = 0.04 totalRollLength = 20 lenOpts = ["5", "7", "9"] def __init__(self, name, lengths = None): self.name = name self.lengthsdict = dict(zip(self.lenOpts,lengths)) def __str__(self): return self.name def trim(self): return Pattern.totalRollLength - sum([int(i)*int(self.lengthsdict[i]) for i in self.lengthsdict]) def masterSolve(Patterns, rollData, relax = True): # The rollData is made into separate dictionaries (rollDemand,surplusPrice) = splitDict(rollData) # The variable 'prob' is created prob = LpProblem("Cutting Stock Problem",LpMinimize) # vartype represents whether or not the variables are relaxed if relax: vartype = LpContinuous else: vartype = LpInteger # The problem variables are created pattVars = LpVariable.dicts("Pattern", Patterns, 0, None, vartype) surplusVars = LpVariable.dicts("Surplus", Pattern.lenOpts, 0, None, vartype) # The objective function is entered: (the total number of large rolls used * the cost of each) - # (the value of the surplus stock) - (the value of the trim) prob += lpSum([pattVars[i]*Pattern.cost for i in Patterns]) - lpSum([surplusVars[i]*surplusPrice[i]\ for i in Pattern.lenOpts]) - lpSum([pattVars[i]*i.trim()*Pattern.trimValue for i in Patterns]) # The demand minimum constraint is entered for j in Pattern.lenOpts: prob += lpSum([pattVars[i]*i.lengthsdict[j] for i in Patterns]) - surplusVars[j]>=rollDemand[j],"Min%s"%j # The problem is solved prob.solve() # The variable values are rounded prob.roundSolution() if relax: # Creates a dual variables list duals = {} for name,i in zip(['Min5','Min7','Min9'],Pattern.lenOpts): duals[i] = prob.constraints[name].pi return duals else: # Creates a dictionary of the variables and their values varsdict = {} for v in prob.variables(): varsdict[v.name] = v.varValue # The number of rolls of each length in each pattern is printed for i in Patterns: print i, " = %s"%[i.lengthsdict[j] for j in Pattern.lenOpts] return value(prob.objective), varsdict def subSolve(Patterns, duals): # The variable 'prob' is created prob = LpProblem("SubProb",LpMinimize) # The problem variables are created vars = LpVariable.dicts("Roll Length", Pattern.lenOpts, 0, None, LpInteger) trim = LpVariable("Trim", 0 ,None,LpInteger) # The objective function is entered: the reduced cost of a new pattern prob += (Pattern.cost - Pattern.trimValue*trim) - lpSum([vars[i]*duals[i] for i in Pattern.lenOpts]), "Objective" # The conservation of length constraint is entered prob += lpSum([vars[i]*int(i) for i in Pattern.lenOpts]) + trim == Pattern.totalRollLength, "lengthEquate" # The problem is solved prob.solve() # The variable values are rounded prob.roundSolution() # The new pattern is written to a dictionary varsdict = {} newPattern = {} for v in prob.variables(): varsdict[v.name] = v.varValue for i,j in zip(Pattern.lenOpts,["Roll_Length_5","Roll_Length_7","Roll_Length_9"]): newPattern[i] = int(varsdict[j]) # Check if there are more patterns which would reduce the master LP objective function further if value(prob.objective) < -10**-5: morePatterns = True # continue adding patterns Patterns += [Pattern("P" + str(len(Patterns)), [newPattern[i] for i in ["5","7","9"]])] else: morePatterns = False # all patterns have been added return Patterns, morePatterns