18.1 The LP Model
In modeling this problem as a MILP we could choose to introduce a
variable xj for each feasible way of cutting a board of length
l into boards of length li with coefficients aij
representing the number of boards of length li obtained from the
cutting associated with xj and a constraint
∑j=1naijxj≥ bi specifying the number of boards
bi required for each length li; for realistic problems there
will frequently be very many feasible cuttings and associated
variables xj and as these must be enumerated before problem
solution can begin this approach may be impractical. We could instead
introduce for each stock board used a set of variables xi,j for
each demand i indicating the cutting required, a variable wj
representing the resulting waste and a constraint
∑i=1mlixi,j + wj = l ensuring the cutting is
valid. Although we do not know how many boards will be required in the
optimal solution, we do have an upper bound on this number
K0=∑i=1m⌈bi/⌊l/li⌋⌉ and introduce the above variable
sets and constraint for K0 boards. The constraints
∑j=1K0xij≥ bi specify the number of boards
bi required for each length li. Since all K0 boards may
not be required we introduce a variable xj denoting whether a
board is used and modify the valid cutting constraint
so that unused boards have zero cost in the objective function. The complete problem formulation is then:
where hi=⌊l/li⌋. This problem formulation is modeled and solved in ECLiPSe as follows:
:- lib(eplex).
% eplex instance creation
:- eplex_instance(cut_stock).
lp_cut_stock(Lengths, Demands, StockLength, Vars, Cost) :-
(
foreach(Li, Lengths),
foreach(Bi, Demands),
foreach([], XijVars0),
foreach(Maxi, Bounds),
fromto(0, KIn, KOut, K0),
param(StockLength)
do
KOut is KIn + fix(ceiling(Bi/floor(StockLength/Li))),
Maxi is fix(floor(StockLength/Li))
),
(
for(J, 1, K0),
foreach(Wj, Obj),
foreach(Xj:Used, Vars),
fromto(XijVars0, VIn, VOut, XijVars),
param(Lengths, StockLength, Bounds)
do
cut_stock:integers([Xj,Wj]),
% Xj variable bounds
cut_stock:(Xj::0..1),
% Wj variable bounds
cut_stock:(Wj::0..StockLength),
(
foreach(Li, Lengths),
foreach(Xij, Used),
foreach(Li*Xij, Knapsack),
foreach(XiVars, VIn),
foreach([Xij|XiVars], VOut),
foreach(Maxi, Bounds),
param(Xj)
do
% Xij variable bounds
cut_stock:integers(Xij),
cut_stock:(Xij::0..Maxi)
),
% cutting knapsack constraint
cut_stock:(sum(Knapsack) + Wj =:= StockLength*Xj)
),
(
foreach(Bi, Demands),
foreach(Xijs, XijVars)
do
% demand constraint
cut_stock:(sum(Xijs) >= Bi)
),
cut_stock:eplex_solver_setup(min(sum(Obj))),
% optimization call
cut_stock:eplex_solve(Cost).