Roll Cutting - Revision 1 & 2

pattern_tradeoff.ipynb Open In Colab Kaggle Gradient Open In SageMaker Studio Lab Hits

Description: Pattern tradeoff example with amplpy

Tags: amplpy, example

Notebook author: Filipe Brandão <fdabrandao@gmail.com>

Model author: N/A

References: N/A

# Install dependencies
%pip install -q amplpy
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
    modules=["gurobi"],  # modules to install
    license_uuid="default",  # license to use
)  # instantiate AMPL object and register magics

Roll Cutting - Revision 1

%%ampl_eval
param roll_width > 0;

set WIDTHS;
param orders {WIDTHS} > 0;

param nPAT integer >= 0;
param nbr {WIDTHS,1..nPAT} integer >= 0;

var Cut {1..nPAT} integer >= 0;

minimize Number:
   sum {j in 1..nPAT} Cut[j];
minimize Waste:
   sum {j in 1..nPAT} Cut[j] * (roll_width - sum {i in WIDTHS} i * nbr[i,j]);

subj to Fulfill {i in WIDTHS}:
   sum {j in 1..nPAT} nbr[i,j] * Cut[j] >= orders[i];
%%ampl_eval
data;
param roll_width := 64.5;

param: WIDTHS: orders :=
         6.77    10
         7.56    40
        17.46    33
        18.76    10 ;

param nPAT := 9 ;

param nbr:  1  2  3  4  5  6  7  8  9 :=
     6.77   0  1  1  0  3  2  0  1  4
     7.56   1  0  2  1  1  4  6  5  2
    17.46   0  1  0  2  1  0  1  1  1
    18.76   3  2  2  1  1  1  0  0  0 ;

Optimal solution for the objective Number

ampl.option["solver"] = "gurobi"
ampl.eval("objective Number; solve;")
ampl.display("Number", "Waste")
Gurobi 9.5.0: optimal solution; objective 20
3 simplex iterations
1 branch-and-cut nodes
Number = 20
Waste = 63.62

Optimal solution for the objective Waste

ampl.eval("objective Waste; solve;")
ampl.display("Number", "Waste")
Gurobi 9.5.0: optimal solution; objective 15.62
2 simplex iterations
1 branch-and-cut nodes
Number = 35
Waste = 15.62

Reset session in order to load a new model

ampl.reset()

Roll Cutting - Revision 2

%%ampl_eval
param roll_width > 0;
param over_lim integer >= 0;

set WIDTHS;
param orders {WIDTHS} > 0;

param nPAT integer >= 0;
param nbr {WIDTHS,1..nPAT} integer >= 0;

var Cut {1..nPAT} integer >= 0;

minimize Number:
   sum {j in 1..nPAT} Cut[j];
minimize Waste:
   sum {j in 1..nPAT} Cut[j] * (roll_width - sum {i in WIDTHS} i * nbr[i,j]);

subj to Fulfill {i in WIDTHS}:
   orders[i] <= sum {j in 1..nPAT} nbr[i,j] * Cut[j] <= orders[i] + over_lim;
%%ampl_eval
data;
param roll_width := 64.5;
param over_lim := 10 ;

param: WIDTHS: orders :=
         6.77    10
         7.56    40
        17.46    33
        18.76    10 ;

param nPAT := 9 ;

param nbr:  1  2  3  4  5  6  7  8  9 :=
     6.77   0  1  1  0  3  2  0  1  4
     7.56   1  0  2  1  1  4  6  5  2
    17.46   0  1  0  2  1  0  1  1  1
    18.76   3  2  2  1  1  1  0  0  0 ;

Initial solve

ampl.option["solver"] = "gurobi"
ampl.eval("objective Number; solve;")
min_number = ampl.get_value("Number")
min_numwaste = ampl.get_value("Waste")
ampl.eval("objective Waste;")
Gurobi 9.5.0: optimal solution; objective 20
7 simplex iterations
1 branch-and-cut nodes
over_lim = int(ampl.param["over_lim"].value())
prev_number = float("inf")
min_waste = {}
min_wastenum = {}
for k in reversed(range(over_lim)):
    ampl.param["over_lim"] = k
    ampl.solve()
    if ampl.solve_result == "infeasible":
        break
    number = ampl.get_value("Number")
    if number < prev_number:
        min_waste[k] = ampl.get_value("Waste")
        min_wastenum[k] = number
        prev_number = number
    if number == min_number:
        break
Gurobi 9.5.0: optimal solution; objective 46.72
4 simplex iterations
1 branch-and-cut nodes
Gurobi 9.5.0: optimal solution; objective 46.72
4 simplex iterations
1 branch-and-cut nodes
Gurobi 9.5.0: optimal solution; objective 47.89
4 simplex iterations
1 branch-and-cut nodes
Gurobi 9.5.0: optimal solution; objective 49.16
4 simplex iterations
1 branch-and-cut nodes
Gurobi 9.5.0: optimal solution; objective 54.76
5 simplex iterations
1 branch-and-cut nodes

Report

print("Min{:3.0f} rolls with waste{:6.2f}\n".format(min_number, min_numwaste))
print("Over\tWaste\tNumber")
for k in sorted(min_waste.keys(), reverse=True):
    print("{}\t{}\t{:.0f}".format(k, min_waste[k], min_wastenum[k]))
Min 20 rolls with waste 62.04

Over	Waste	Number
9	46.71999999999988	22
7	47.88999999999987	21
5	54.75999999999988	20