| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
|
Column generation for a cutting stock problem Description
Further explanation of this example: paper.mos, papers.mos, papersn.mos: 'Mosel User Guide', Section 11.2 Column generation, and the Xpress Whitepaper 'Embedding Optimization Algorithms', Section 'Column generation for cutting-stock' (also discusses a generalization to bin-packing problems); for the multiple model versions paperp.mos, paperms.mos see the Xpress Whitepaper 'Multiple models and parallel solving with Mosel', Section 'Column generation: solving different models in sequence'.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
cutstk.mos
(!*******************************************************
* Mosel Example Problems *
* ====================== *
* *
* file cutstk.mos *
* ``````````````` *
* Example for the use of the Mosel language *
* (Cutting stock problem, solved by column (= cutting *
* pattern) generation heuristic looping over the *
* root node) *
* *
* (c) 2008 Fair Isaac Corporation *
* author: S. Heipcke, 2001 *
*******************************************************!)
model CutStock ! Start a new model
uses "mmxprs","mmsystem" ! Load the optimizer library
! Declare functions and procedures
! that are defined later
forward function colgen:real
forward function knapsack(c:array(range) of real, a:array(range) of real,
b:real, N:integer, xbest:array(range) of integer):real
declarations
NWIDTHS = 4 ! Number of different widths
RW = 1..NWIDTHS ! Range of widths
MAXWIDTH = 91 ! Max. roll width
EPS = 1e-6 ! Zero tolerance
WIDTH: array(RW) of real ! Possible widths
DEMAND: array(RW) of integer ! Demand per width
PATTERNS: array(RW, RW) of integer ! (Basic) cutting patterns
pat: array(range) of mpvar ! Rolls per pattern
dem: array(RW) of linctr ! Demand constraints
minRolls: linctr ! Objective function
solpat: array(RP:range) of real ! Solution values for variables pat
end-declarations
WIDTH:: [25.5, 22.5, 20.0, 15.0]
DEMAND:: [78, 40, 30, 30]
PATTERNS:: [3,0,0,0,
0,4,0,0,
0,0,4,0,
0,0,0,6]
forall(j in RW) create(pat(j)) ! Create NWIDTHS variables pat
minRolls:= sum(j in RW) pat(j) ! Objective: minimize no. of rolls
! Satisfy all demands
forall(i in RW) dem(i):= sum(j in RW) PATTERNS(i,j) * pat(j) >= DEMAND(i)
forall(j in RW) do
pat(j) is_integer ! Variables are integer and bounded
pat(j) <= integer(ceil(DEMAND(j)/PATTERNS(j,j)))
end-do
setparam("zerotol", EPS) ! Set Mosel comparison tolerance
starttime:= gettime
objval:=colgen ! Solve the problem by using
! column generation
! Solution printing
writeln("(", gettime-starttime, " sec) Optimal solution: ",
objval, " rolls, ", RP.size, " patterns")
write(" Rolls per pattern: ")
forall(i in RP) write(solpat(i),", ")
writeln
!************************************************************************
! Column generation loop at the top node:
! solve the LP and save the basis
! get the solution values
! generate new column(s) (=cutting pattern)
! load the modified problem and load the saved basis
!************************************************************************
function colgen:real
declarations
MAXCOL = 10 ! Max. no. of patterns to be generated
dualdem: array(RW) of real ! Dual values of demand constraints
c,a: array(RW) of real
indx,xbest: array(RW) of integer
dw,zbest: real
bas: basis
end-declarations
setparam("XPRS_CUTSTRATEGY", 0) ! Disable automatic cuts
setparam("XPRS_PRESOLVE", 0) ! Switch presolve off
npatt:=NWIDTHS
npass:=1
while(npass<=MAXCOL) do
minimize(XPRS_LIN, minRolls) ! Solve the LP
savebasis(bas) ! Save the current basis
returned:= getobjval ! Get the objective value
! Get the solution values
forall(j in 1..npatt) solpat(j):=pat(j).sol
forall(i in RW) dualdem(i):=dem(i).dual
! Sort (basic) patterns into descending order shadow_price/width:
forall(j in RW) do
dw:=0
forall(i in RW)
if (dualdem(i)/WIDTH(i) > dw) then
dw:= dualdem(i)/WIDTH(i)
k:= i
end-if
c(j):= dualdem(k)
a(j):= WIDTH(k)
indx(j):= k
dualdem(k):= 0
end-do
zbest:=knapsack(c,a,MAXWIDTH,NWIDTHS,xbest)
write("(", gettime-starttime, " sec) Pass ", npass, ": ")
if zbest < 1 then
writeln("no profitable column found.\n")
break
else ! Print the new pattern
writeln("new pattern found with marginal cost ", zbest-1)
write(" Widths distribution: ")
dw:=0
forall(i in 1..NWIDTHS) do
write(WIDTH(indx(i)), ":", xbest(i), " ")
dw += WIDTH(indx(i))*xbest(i)
end-do
writeln("Total width: ", dw)
npatt+=1
create(pat(npatt)) ! Create a new var. for this pattern
pat(npatt) is_integer
minRolls+= pat(npatt) ! Add new var. to the objective
dw:=0
forall(i in RW)
if xbest(i) > 0 then
dem(indx(i))+= xbest(i)*pat(npatt) ! Add new var. to demand constr.s
if (ceil(DEMAND(indx(i))/xbest(i)) > dw) then
dw:= integer(ceil(DEMAND(indx(i))/xbest(i)))
end-if
end-if
pat(npatt) <= dw ! Set upper bound on the new var.
loadprob(minRolls) ! Reload the problem
loadbasis(bas) ! Load the saved basis
end-if
npass+=1
end-do
end-function
!************************************************************************
! Solve (heuristically) the integer knapsack problem
! z = max{cx : ax<=b, x in Z^N}
! with b=MAXWIDTH, N=NWIDTHS
!************************************************************************
function knapsack(c:array(range) of real, a:array(range) of real,
b:real, N:integer, xbest:array(range) of integer):real
declarations
x: array(1..N) of integer
z,ax,zbest: real
end-declarations
(!
write ("Solving z = max{cx : ax <= b; x in Z^n}\n c = ")
forall(j in 1..N) write (c(j),", ")
write("\n a = ")
forall(j in 1..N) write(a(j),", ")
write("\n c/a = ")
forall(j in 1..N) write(c(j)/a(j),", ")
writeln("\n b = ", b)
!)
z:=0 ! Value of working solution
ax:=0 ! Resource use of current solution
zbest:=0 ! Value of best solution so far
k:=0
repeat
! Construct a greedy solution using remaining items (k+1..N):
forall(j in k+1..N) do
x(j):= integer(floor((b-ax)/a(j)))
z += c(j)*x(j)
ax += a(j)*x(j)
end-do
! Compare new solution to best so far and update best if necessary:
if z > zbest then
zbest:= z
forall(j in 1..N) xbest(j):= x(j)
end-if
! Backtrack:
! For each item type k used in solution, starting from least profitable,
! try deleting one item and replacing it by items of type k+1.
! If this gives a better linear solution, construct new integer solution.
! Otherwise, remove all items k and consider next item up (k--).
k:=N-1
while(k>0)
if (x(k) <> 0) then
z -= c(k)
ax -= a(k)
x(k)-=1
if (z + c(k+1) * (b-ax)/a(k+1) >= zbest + EPS) then
break
else
z -= x(k) * c(k)
ax -= x(k) * a(k)
x(k):= 0
k -=1
end-if
else
k -=1
end-if
until (k<1)
(!
write(" z = ", zbest, "\n x = ")
forall(j in 1..N) write(xbest(j), ", ")
writeln
!)
returned:=zbest
end-function
end-model
| |||||||||||||||||||||||||||
| © Copyright 2025 Fair Isaac Corporation. |