AMPLPY: Roll Cutting - Revision 1 & 2

Documentation: http://amplpy.readthedocs.io

GitHub Repository: https://github.com/ampl/amplpy

PyPI Repository: https://pypi.python.org/pypi/amplpy

Imports


In [1]:
from __future__ import print_function
from amplpy import AMPL
from math import floor
import os

Roll Cutting - Revision 1


In [2]:
with open(os.path.join('models', 'cutRev1.mod'), 'r') as f:
    print(f.read())


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];


In [3]:
with open(os.path.join('models', 'cutRev1.dat'), 'r') as f:
    print(f.read())


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 ;

Set up AMPL model


In [4]:
# Initialize
ampl = AMPL()
ampl.option['solver'] = 'gurobi'
ampl.read(os.path.join('models', 'cutRev1.mod'))
ampl.readData(os.path.join('models', 'cutRev1.dat'))

Optimal solution for the objective Number


In [5]:
ampl.eval('objective Number; solve;')
ampl.display('Number', 'Waste')


Gurobi 7.5.1: optimal solution; objective 20
3 simplex iterations
1 branch-and-cut nodes
Number = 20
Waste = 63.62

Optimal solution for the objective Waste


In [6]:
ampl.eval('objective Waste; solve;')
ampl.display('Number', 'Waste')


Gurobi 7.5.1: optimal solution; objective 15.62
2 simplex iterations
1 branch-and-cut nodes
Number = 35
Waste = 15.62

Roll Cutting - Revision 2


In [7]:
with open(os.path.join('models', 'cutRev2.mod'), 'r') as f:
    print(f.read())


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;


In [8]:
with open(os.path.join('models', 'cutRev2.dat'), 'r') as f:
    print(f.read())


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 ;

Set up AMPL model & initial solve


In [14]:
# Initialize
ampl = AMPL()
ampl.option['solver'] = 'gurobi'
ampl.read(os.path.join('models', 'cutRev2.mod'))
ampl.readData(os.path.join('models', 'cutRev2.dat'))

ampl.eval('objective Number; solve;')
min_number = ampl.getValue('Number')
min_numwaste = ampl.getValue('Waste')
ampl.eval('objective Waste;')


Gurobi 7.5.1: optimal solution; objective 20
7 simplex iterations
1 branch-and-cut nodes

In [15]:
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.eval('solve;')    
    if ampl.getValue('solve_result') == 'infeasible':
        break
    number = ampl.getValue('Number')
    if number < prev_number:
        min_waste[k] = ampl.getValue('Waste')
        min_wastenum[k] = number
        prev_number = number
    if number == min_number:
        break


Gurobi 7.5.1: optimal solution; objective 46.72
7 simplex iterations
1 branch-and-cut nodes
Gurobi 7.5.1: optimal solution; objective 46.72
4 simplex iterations
Gurobi 7.5.1: optimal solution; objective 47.89
5 simplex iterations
Gurobi 7.5.1: optimal solution; objective 49.16
4 simplex iterations
Gurobi 7.5.1: optimal solution; objective 54.76
7 simplex iterations
1 branch-and-cut nodes

Report


In [12]:
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 63.62

Over	Waste	Number
9	46.72	22
7	47.89	21
5	54.76	20