Up Next

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=1naijxjbi 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 K0i=1mbi/⌊l/li⌋⌉ and introduce the above variable sets and constraint for K0 boards. The constraints Σj=1K0xijbi 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
m
Σ
i=1
lixij+wj=lxj
so that unused boards have zero cost in the objective function. The complete problem formulation is then:
P: minimize z =
K0
Σ
j=1
wj
     
subject to
K0
Σ
j=1
xij
bi
        ∀ i
m
Σ
i=1
lixi,j+wj
wj
xi,j
xj
=
lxj
{ 0,…,l }
{ 0,…,hi }   ∀ i
{ 0, 1 }
  



j
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).

Up Next