136 lines
4.8 KiB
Python
136 lines
4.8 KiB
Python
"""
|
|
The Full Sponge Roll Problem for the PuLP Modeller
|
|
|
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
|
"""
|
|
|
|
def calculatePatterns(totalRollLength,lenOpts,head):
|
|
"""
|
|
Recursively calculates the list of options lists for a cutting stock problem. The input
|
|
'tlist' is a pointer, and will be the output of the function call.
|
|
|
|
The inputs are:
|
|
totalRollLength - the length of the roll
|
|
lenOpts - a list of the sizes of remaining cutting options
|
|
head - the current list that has been passed down though the recusion
|
|
|
|
Returns the list of patterns
|
|
|
|
Authors: Bojan Blazevic, Dr Stuart Mitchell 2007
|
|
"""
|
|
if lenOpts:
|
|
patterns =[]
|
|
#take the first option off lenOpts
|
|
opt = lenOpts[0]
|
|
for rep in range(int(totalRollLength/opt)+1):
|
|
#reduce the length
|
|
l = totalRollLength - rep*opt
|
|
h = head[:]
|
|
h.append(rep)
|
|
|
|
patterns.extend(calculatePatterns(l, lenOpts[1:], h))
|
|
else:
|
|
#end of the recursion
|
|
patterns = [head]
|
|
return patterns
|
|
|
|
def makePatterns(totalRollLength,lenOpts):
|
|
"""
|
|
Makes the different cutting patterns for a cutting stock problem.
|
|
|
|
The inputs are:
|
|
totalRollLength : the length of the roll
|
|
lenOpts: a list of the sizes of cutting options as strings
|
|
|
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
|
"""
|
|
|
|
# calculatePatterns is called to create a list of the feasible cutting options in 'tlist'
|
|
patterns = calculatePatterns(totalRollLength,lenOpts,[])
|
|
|
|
# The list 'PatternNames' is created
|
|
PatternNames = []
|
|
for i in range(len(patterns)):
|
|
PatternNames += ["P"+str(i)]
|
|
|
|
# The amount of trim (unused material) for each pattern is calculated and added to the dictionary
|
|
# 'trim', with the reference key of the pattern name.
|
|
trim = {}
|
|
for name,pattern in zip(PatternNames,patterns):
|
|
ssum = 0
|
|
for rep,l in zip(pattern,lenOpts):
|
|
ssum += rep*l
|
|
trim[name] = totalRollLength - ssum
|
|
# The different cutting lengths are printed, and the number of each roll of that length in each
|
|
# pattern is printed below. This is so the user can see what each pattern contains.
|
|
print("Lens: %s" %lenOpts)
|
|
for name,pattern in zip(PatternNames,patterns):
|
|
print(name + " = %s"%pattern)
|
|
|
|
return (PatternNames,patterns,trim)
|
|
|
|
|
|
# Import PuLP modeler functions
|
|
from pulp import *
|
|
|
|
# The Total Roll Length is entered
|
|
totalRollLength = 20
|
|
|
|
# The cost of each 20cm long sponge roll used
|
|
cost = 1
|
|
|
|
# The sale value of each cm of trim
|
|
trimValue = 0.04
|
|
|
|
# A list of all the roll lengths is created
|
|
LenOpts = ["5","7","9"]
|
|
|
|
rollData = {#Length Demand SalePrice
|
|
"5": [150, 0.25],
|
|
"7": [200, 0.33],
|
|
"9": [300, 0.40]}
|
|
|
|
# The pattern names and the patterns are created as lists, and the associated trim with each pattern
|
|
# is created as a dictionary. The inputs are the total roll length and the list (as integers) of
|
|
# cutting options.
|
|
(PatternNames,patterns,trim) = makePatterns(totalRollLength,[int(l) for l in LenOpts])
|
|
|
|
# The RollData is made into separate dictionaries
|
|
(rollDemand,surplusPrice) = splitDict(rollData)
|
|
|
|
# The pattern data is made into a dictionary so it can be called by patterns["7"]["P3"] for example.
|
|
# This will return the number of rolls of length "7" in pattern "P3"
|
|
patterns = makeDict([PatternNames,LenOpts],patterns,0)
|
|
|
|
# The variable 'prob' is created
|
|
prob = LpProblem("Cutting Stock Problem",LpMinimize)
|
|
|
|
# The problem variables of the number of each pattern to make are created
|
|
pattVars = LpVariable.dicts("Patt",PatternNames,0,None,LpInteger)
|
|
|
|
# The problem variables of the number of surplus rolls for each length are created
|
|
surplusVars = LpVariable.dicts("Surp",LenOpts,0,None,LpInteger)
|
|
|
|
# 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]*cost for i in PatternNames]) - lpSum([surplusVars[i]*surplusPrice[i] for i in LenOpts]) - lpSum([pattVars[i]*trim[i]*trimValue for i in PatternNames]),"Net Production Cost"
|
|
|
|
# The demand minimum constraint is entered
|
|
for j in LenOpts:
|
|
prob += lpSum([pattVars[i]*patterns[i][j] for i in PatternNames]) - surplusVars[j]>=rollDemand[j],"Ensuring enough %s cm rolls"%j
|
|
|
|
# The problem data is written to an .lp file
|
|
prob.writeLP("SpongeRollProblem.lp")
|
|
|
|
# The problem is solved using PuLP's choice of Solver
|
|
prob.solve()
|
|
|
|
# The status of the solution is printed to the screen
|
|
print("Status:", LpStatus[prob.status])
|
|
|
|
# Each of the variables is printed with it's resolved optimum value
|
|
for v in prob.variables():
|
|
print(v.name, "=", v.varValue)
|
|
|
|
# The optimised objective function value is printed to the screen
|
|
print("Production Costs = ", value(prob.objective))
|