License Portal

Search
Close this search box.

Additional Scripts: Looping and Testing – 2

Implementing algorithms through AMPL scripts

Implements

Script

Uses

Gilmore-Gomory column generation procedure for the cutting-stock (roll trim) problem

# ----------------------------------------
# GILMORE-GOMORY METHOD FOR
# CUTTING STOCK PROBLEM
# ----------------------------------------

option solver cplex;
option solution_round 6;

model cut1.mod;
data cut.dat;

problem Cutting_Opt: Cut, Number, Fill;
   option relax_integrality 1;
   option presolve 0;

problem Pattern_Gen: Use, Reduced_Cost, Width_Limit;
   option relax_integrality 0;
   option presolve 1;

let nPAT := 0;

for {i in WIDTHS} {
   let nPAT := nPAT + 1;
   let nbr[i,nPAT] := floor (roll_width/i);
   let {i2 in WIDTHS: i2 <> i} nbr[i2,nPAT] := 0;
   };

repeat {
   solve Cutting_Opt;

   let {i in WIDTHS} price[i] := Fill[i].dual;

   solve Pattern_Gen;

   if Reduced_Cost < -0.00001 then {
      let nPAT := nPAT + 1;
      let {i in WIDTHS} nbr[i,nPAT] := Use[i];
      }
   else break;
   };

display nbr; 
display Cut;

option Cutting_Opt.relax_integrality 0;
option Cutting_Opt.presolve 10;
solve Cutting_Opt;

display Cut;
# ----------------------------------------
# CUTTING STOCK USING PATTERNS
# ----------------------------------------

param roll_width > 0;         # width of raw rolls
 
set WIDTHS;                   # set of widths to be cut
param orders {WIDTHS} > 0;    # number of each width to be cut

param nPAT integer >= 0;      # number of patterns
set PATTERNS := 1..nPAT;      # set of patterns

param nbr {WIDTHS,PATTERNS} integer >= 0;

   check {j in PATTERNS}: 
      sum {i in WIDTHS} i * nbr[i,j] <= roll_width;

                            # defn of patterns: nbr[i,j] = number
                            # of rolls of width i in pattern j

var Cut {PATTERNS} integer >= 0;   # rolls cut using each pattern

minimize Number:                   # minimize total raw rolls cut
   sum {j in PATTERNS} Cut[j];   

subj to Fill {i in WIDTHS}:
   sum {j in PATTERNS} nbr[i,j] * Cut[j] >= orders[i];

                                   # for each width, total
                                   # rolls cut meets total orders

# ----------------------------------------
# KNAPSACK SUBPROBLEM FOR CUTTING STOCK
# ----------------------------------------

param price {WIDTHS} default 0.0;

var Use {WIDTHS} integer >= 0;

minimize Reduced_Cost:  
   1 - sum {i in WIDTHS} price[i] * Use[i];

subj to Width_Limit:  
   sum {i in WIDTHS} i * Use[i] <= roll_width;
param roll_width := 110 ;

param: WIDTHS: orders :=
          20     48
          45     35
          50     24
          55     10
          75      8  ;

Same as cut1.run, but using an alternative arrangement wherein problems are defined immediately before before their members are declared

# ----------------------------------------
# GILMORE-GOMORY METHOD FOR
# CUTTING STOCK PROBLEM
# ----------------------------------------

option solver cplex;
option solution_round 6;

model cut2.mod;
data cut.dat;

problem Cutting_Opt;
   option relax_integrality 1;
   option presolve 0;

problem Pattern_Gen;
   option relax_integrality 0;
   option presolve 1;

let nPAT := 0;

for {i in WIDTHS} {
   let nPAT := nPAT + 1;
   let nbr[i,nPAT] := floor (roll_width/i);
   let {i2 in WIDTHS: i2 <> i} nbr[i2,nPAT] := 0;
   };

repeat {
   solve Cutting_Opt;

   let {i in WIDTHS} price[i] := Fill[i].dual;

   solve Pattern_Gen;

   if Reduced_Cost < -0.00001 then {
      let nPAT := nPAT + 1;
      let {i in WIDTHS} nbr[i,nPAT] := Use[i];
      }
   else break;
   };

display nbr; 
display Cut;

option Cutting_Opt.relax_integrality 0;
option Cutting_Opt.presolve 10;
solve Cutting_Opt;

display Cut;
  problem Cutting_Opt;
# ----------------------------------------

param nPAT integer >= 0, default 0;
param roll_width;

set PATTERNS := 1..nPAT;
set WIDTHS;

param orders {WIDTHS} > 0;
param nbr {WIDTHS,PATTERNS} integer >= 0;

   check {j in PATTERNS}: sum {i in WIDTHS} i * nbr[i,j] <= roll_width;

var Cut {PATTERNS} integer >= 0;

minimize Number: sum {j in PATTERNS} Cut[j];

subj to Fill {i in WIDTHS}:
   sum {j in PATTERNS} nbr[i,j] * Cut[j] >= orders[i];


  problem Pattern_Gen;
# ----------------------------------------

param price {WIDTHS} default 0;

var Use {WIDTHS} integer >= 0;

minimize Reduced_Cost:  
   1 - sum {i in WIDTHS} price[i] * Use[i];

subj to Width_Limit:  
   sum {i in WIDTHS} i * Use[i] <= roll_width;
param roll_width := 110 ;

param: WIDTHS: orders :=
          20     48
          45     35
          50     24
          55     10
          75      8  ;

Same as cut1.run, but with better formatting of output

# ----------------------------------------
# GILMORE-GOMORY METHOD FOR
# CUTTING STOCK PROBLEM
# ----------------------------------------

option solver cplex;
option solution_round 6;
option solver_msg 0;

model cut1.mod;
data cut.dat;

problem Cutting_Opt: Cut, Number, Fill;
   option relax_integrality 1;
   option presolve 0;

problem Pattern_Gen: Use, Reduced_Cost, Width_Limit;
   option relax_integrality 0;
   option presolve 1;

let nPAT := 0;

for {i in WIDTHS} {
   let nPAT := nPAT + 1;
   let nbr[i,nPAT] := floor (roll_width/i);
   let {i2 in WIDTHS: i2 <> i} nbr[i2,nPAT] := 0;
   };

repeat {
   solve Cutting_Opt >/dev/null;

   let {i in WIDTHS} price[i] := Fill[i].dual;

   solve Pattern_Gen >/dev/null;

   printf "\n%7.2f%11.2e  ", Number, Reduced_Cost;

   if Reduced_Cost < -0.00001 then {
      let nPAT := nPAT + 1;
      let {i in WIDTHS} nbr[i,nPAT] := Use[i];
      }
   else break;

   for {i in WIDTHS} printf "%3i", Use[i];
   };

let {j in PATTERNS} Cut[j] := ceil(Cut[j]);

printf "\n\n\nRounded up to integer: %3i rolls", sum {j in PATTERNS} Cut[j];
printf "\n\nCut  ";
printf {j in PATTERNS}: "%4i", Cut[j];
printf "\n\n";
for {i in WIDTHS} {
   printf "%3i  ", i;
   printf {j in PATTERNS}: "%4i", nbr[i,j];
   printf "\n";
   }

printf "\nWASTE = %5.2f%%\n\n\n", 
   100 * (1 - (sum {i in WIDTHS} i * orders[i])  / (roll_width * Number));

option Cutting_Opt.relax_integrality 0;
option Cutting_Opt.presolve 10;
solve Cutting_Opt >/dev/null;

printf "Best integer: %3i rolls", sum {j in PATTERNS} Cut[j];
printf "\n\nCut  ";
printf {j in PATTERNS}: "%4i", Cut[j];
printf "\n\n";
for {i in WIDTHS} {
   printf "%3i  ", i;
   printf {j in PATTERNS}: "%4i", nbr[i,j];
   printf "\n";
   }

printf "\nWASTE = %5.2f%%\n\n", 
   100 * (1 - (sum {i in WIDTHS} i * orders[i])  / (roll_width * Number));
# ----------------------------------------
# CUTTING STOCK USING PATTERNS
# ----------------------------------------

param roll_width > 0;         # width of raw rolls
 
set WIDTHS;                   # set of widths to be cut
param orders {WIDTHS} > 0;    # number of each width to be cut

param nPAT integer >= 0;      # number of patterns
set PATTERNS := 1..nPAT;      # set of patterns

param nbr {WIDTHS,PATTERNS} integer >= 0;

   check {j in PATTERNS}: 
      sum {i in WIDTHS} i * nbr[i,j] <= roll_width;

                            # defn of patterns: nbr[i,j] = number
                            # of rolls of width i in pattern j

var Cut {PATTERNS} integer >= 0;   # rolls cut using each pattern

minimize Number:                   # minimize total raw rolls cut
   sum {j in PATTERNS} Cut[j];   

subj to Fill {i in WIDTHS}:
   sum {j in PATTERNS} nbr[i,j] * Cut[j] >= orders[i];

                                   # for each width, total
                                   # rolls cut meets total orders

# ----------------------------------------
# KNAPSACK SUBPROBLEM FOR CUTTING STOCK
# ----------------------------------------

param price {WIDTHS} default 0.0;

var Use {WIDTHS} integer >= 0;

minimize Reduced_Cost:  
   1 - sum {i in WIDTHS} price[i] * Use[i];

subj to Width_Limit:  
   sum {i in WIDTHS} i * Use[i] <= roll_width;
param roll_width := 110 ;

param: WIDTHS: orders :=
          20     48
          45     35
          50     24
          55     10
          75      8  ;

Dantzig-Wolfe decomposition for a multi-commodity transportation problem, using a single subproblem

# ----------------------------------------
# DANTZIG-WOLFE DECOMPOSITION FOR
# MULTI-COMMODITY TRANSPORTATION
# ----------------------------------------

model multi1.mod;
data multi1.dat;

let nPROP := 0;
let price_convex := 1;
let {i in ORIG, j in DEST} price[i,j] := 0;

option solver minos;
option omit_zero_rows 1;
option display_1col 10;
option display_eps .000001;

# ----------------------------------------------------------

problem MasterI: Artificial, Weight, Excess, Multi, Convex;
problem SubI: Artif_Reduced_Cost, Trans, Supply, Demand;

repeat { printf "\nPHASE I -- ITERATION %d\n\n", nPROP+1;

   solve SubI;
   printf "\n";
   display Trans;

   if Artif_Reduced_Cost >= - 0.00001 then {
      printf "\n*** NO FEASIBLE SOLUTION ***\n";
      break;
      }
   else {
      let nPROP := nPROP + 1;
      let {i in ORIG, j in DEST}
         prop_ship[i,j,nPROP] := sum {p in PROD} Trans[i,j,p];
      let prop_cost[nPROP] := 
         sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p];
      };

   solve MasterI;
   printf "\n";
   display Weight; display Multi.dual;
   display {i in ORIG, j in DEST} 
      limit[i,j] - sum {k in 1..nPROP} prop_ship[i,j,k] * Weight[k];

   if Excess <= 0.00001 then break;
   else {
      let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
      let price_convex := Convex.dual;
   };
};

# ----------------------------------------------------------

printf "\nSETTING UP FOR PHASE II\n\n";

problem MasterII: Total_Cost, Weight, Multi, Convex;
problem SubII: Reduced_Cost, Trans, Supply, Demand;

solve MasterII;
printf "\n";
display Weight; display Multi.dual; display Multi.slack;

let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
let price_convex := Convex.dual;

repeat { printf "\nPHASE II -- ITERATION %d\n\n", nPROP+1;

   solve SubII;
   printf "\n";
   display Trans;

   if Reduced_Cost >= - 0.00001 then {
      printf "\n*** OPTIMAL SOLUTION ***\n";
      break;
      }
   else {
      let nPROP := nPROP + 1;
      let {i in ORIG, j in DEST}
         prop_ship[i,j,nPROP] := sum {p in PROD} Trans[i,j,p];
      let prop_cost[nPROP] := 
         sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p];
      };

   solve MasterII;
	
   printf "\n";
   display Weight;

   let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
   let price_convex := Convex.dual;
};

# ----------------------------------------------------------

printf "\nPHASE III\n\n";

problem MasterIII: Opt_Cost, Trans, Supply, Demand, Opt_Multi;

let {i in ORIG, j in DEST}
   opt_ship[i,j] := sum {k in 1..nPROP} prop_ship[i,j,k] * Weight[k];

solve MasterIII;
printf "\n";
display Trans;
# ----------------------------------------
# MULTI-COMMODITY FLOW USING
# DANTZIG-WOLFE DECOMPOSITION
# ----------------------------------------

### SUBPROBLEM ###

set ORIG;   # origins
set DEST;   # destinations
set PROD;   # products

param supply {ORIG,PROD} >= 0;  # amounts available at origins
param demand {DEST,PROD} >= 0;  # amounts required at destinations

   check {p in PROD}:
      sum {i in ORIG} supply[i,p] = sum {j in DEST} demand[j,p];

param price_convex;                  # dual price on convexity constr
param price {ORIG,DEST} <= 0.000001; # dual price on shipment limit
param cost {ORIG,DEST,PROD} >= 0;    # shipment costs per unit

var Trans {ORIG,DEST,PROD} >= 0;   # units to be shipped

minimize Artif_Reduced_Cost:
   sum {i in ORIG, j in DEST, p in PROD}
      (- price[i,j]) * Trans[i,j,p] - price_convex;

minimize Reduced_Cost:
   sum {i in ORIG, j in DEST, p in PROD}
      (cost[i,j,p] - price[i,j]) * Trans[i,j,p] - price_convex;

subject to Supply {i in ORIG, p in PROD}:
   sum {j in DEST} Trans[i,j,p] = supply[i,p];

subject to Demand {j in DEST, p in PROD}:
   sum {i in ORIG} Trans[i,j,p] = demand[j,p];

### MASTER PROBLEM ###

param limit {ORIG,DEST} >= 0;   # max shipped on each link

param nPROP integer >= 0;
param prop_ship {ORIG,DEST,1..nPROP} >= -0.000001;
param prop_cost {1..nPROP} >= 0;

            # For each proposal from the subproblem:
            # amount it ships over each link, and its cost

var Weight {1..nPROP} >= 0;
var Excess >= 0;

minimize Artificial: Excess;

minimize Total_Cost:
   sum {k in 1..nPROP} prop_cost[k] * Weight[k];

subject to Multi {i in ORIG, j in DEST}:
   sum {k in 1..nPROP} prop_ship[i,j,k] * Weight[k] - Excess <= limit[i,j];

subject to Convex: sum {k in 1..nPROP} Weight[k] = 1;

### PHASE III PROBLEM ###

param opt_ship {ORIG,DEST} >= -0.000001;

minimize Opt_Cost:
   sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p];

subject to Opt_Multi {i in ORIG, j in DEST}:
   sum {p in PROD} Trans[i,j,p] = opt_ship[i,j];
set ORIG := GARY CLEV PITT ;
set DEST := FRA DET LAN WIN STL FRE LAF ;
set PROD := bands coils plate ;

param supply (tr):  GARY   CLEV   PITT :=
            bands    400    700    800
            coils    800   1600   1800
            plate    200    300    300 ;

param demand (tr):
           FRA   DET   LAN   WIN   STL   FRE   LAF :=
   bands   300   300   100    75   650   225   250
   coils   500   750   400   250   950   850   500
   plate   100   100     0    50   200   100   250 ;

param limit default 625 ;

param cost :=

 [*,*,bands]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    30   10    8   10   11   71    6
        CLEV    22    7   10    7   21   82   13
        PITT    19   11   12   10   25   83   15

 [*,*,coils]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    39   14   11   14   16   82    8
        CLEV    27    9   12    9   26   95   17
        PITT    24   14   17   13   28   99   20

 [*,*,plate]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    41   15   12   16   17   86    8
        CLEV    29    9   13    9   28   99   18
        PITT    26   14   17   13   31  104   20 ;

Same as multi1.run, but using the same repeat loop for both phase I (infeasible) and phase II (feasible).

# ----------------------------------------
# DANTZIG-WOLFE DECOMPOSITION FOR
# MULTI-COMMODITY TRANSPORTATION
# ----------------------------------------

model multi1.mod;
data multi1.dat;

let nPROP := 0;
let price_convex := 1;
let {i in ORIG, j in DEST} price[i,j] := 0;

param phase symbolic, default "I";

option solver minos;
option omit_zero_rows 1;
option display_1col 10;
option display_eps .000001;

# ----------------------------------------------------------

problem Master: Artificial, Weight, Excess, Multi, Convex;
problem Sub: Artif_Reduced_Cost, Trans, Supply, Demand;

repeat { printf "\nPHASE %s -- ITERATION %d\n\n", phase, nPROP+1;

   solve Sub;
   printf "\n";
   display Trans;

   if phase = "I" then {
      if Artif_Reduced_Cost >= - 0.00001 then {
         printf "\n*** NO FEASIBLE SOLUTION ***\n";
         break;
         }
      }
   else {
      if Reduced_Cost >= - 0.00001 then {
         printf "\n*** OPTIMAL SOLUTION ***\n";
         break;
         }
      };

   let nPROP := nPROP + 1;
   let {i in ORIG, j in DEST}
      prop_ship[i,j,nPROP] := sum {p in PROD} Trans[i,j,p];
   let prop_cost[nPROP] := 
      sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p];

   solve Master;
   printf "\n";
   display Weight; 

   if phase = "I" then {
      display Multi.dual;
      display {i in ORIG, j in DEST} 
         limit[i,j] - sum {k in 1..nPROP} prop_ship[i,j,k] * Weight[k];

      if Excess <= 0.00001 then {
         printf "\nSETTING UP FOR PHASE II\n\n";
         let phase := "II";

         problem Master;
            drop Artificial; restore Total_Cost; fix Excess;
         problem Sub;
            drop Artif_Reduced_Cost; restore Reduced_Cost;

      #  problem Master; show obj; solve;
         solve Master;
         printf "\n";
         display Weight; display Multi.dual; display Multi.slack;
         };
      };

   let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
   let price_convex := Convex.dual;
};

# ----------------------------------------------------------

printf "\nPHASE III\n\n";

problem MasterIII: Opt_Cost, Trans, Supply, Demand, Opt_Multi;

let {i in ORIG, j in DEST}
   opt_ship[i,j] := sum {k in 1..nPROP} prop_ship[i,j,k] * Weight[k];

solve MasterIII;
printf "\n";
display Trans;
# ----------------------------------------
# MULTI-COMMODITY FLOW USING
# DANTZIG-WOLFE DECOMPOSITION
# ----------------------------------------

### SUBPROBLEM ###

set ORIG;   # origins
set DEST;   # destinations
set PROD;   # products

param supply {ORIG,PROD} >= 0;  # amounts available at origins
param demand {DEST,PROD} >= 0;  # amounts required at destinations

   check {p in PROD}:
      sum {i in ORIG} supply[i,p] = sum {j in DEST} demand[j,p];

param price_convex;                  # dual price on convexity constr
param price {ORIG,DEST} <= 0.000001; # dual price on shipment limit
param cost {ORIG,DEST,PROD} >= 0;    # shipment costs per unit

var Trans {ORIG,DEST,PROD} >= 0;   # units to be shipped

minimize Artif_Reduced_Cost:
   sum {i in ORIG, j in DEST, p in PROD}
      (- price[i,j]) * Trans[i,j,p] - price_convex;

minimize Reduced_Cost:
   sum {i in ORIG, j in DEST, p in PROD}
      (cost[i,j,p] - price[i,j]) * Trans[i,j,p] - price_convex;

subject to Supply {i in ORIG, p in PROD}:
   sum {j in DEST} Trans[i,j,p] = supply[i,p];

subject to Demand {j in DEST, p in PROD}:
   sum {i in ORIG} Trans[i,j,p] = demand[j,p];

### MASTER PROBLEM ###

param limit {ORIG,DEST} >= 0;   # max shipped on each link

param nPROP integer >= 0;
param prop_ship {ORIG,DEST,1..nPROP} >= -0.000001;
param prop_cost {1..nPROP} >= 0;

            # For each proposal from the subproblem:
            # amount it ships over each link, and its cost

var Weight {1..nPROP} >= 0;
var Excess >= 0;

minimize Artificial: Excess;

minimize Total_Cost:
   sum {k in 1..nPROP} prop_cost[k] * Weight[k];

subject to Multi {i in ORIG, j in DEST}:
   sum {k in 1..nPROP} prop_ship[i,j,k] * Weight[k] - Excess <= limit[i,j];

subject to Convex: sum {k in 1..nPROP} Weight[k] = 1;

### PHASE III PROBLEM ###

param opt_ship {ORIG,DEST} >= -0.000001;

minimize Opt_Cost:
   sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p];

subject to Opt_Multi {i in ORIG, j in DEST}:
   sum {p in PROD} Trans[i,j,p] = opt_ship[i,j];
set ORIG := GARY CLEV PITT ;
set DEST := FRA DET LAN WIN STL FRE LAF ;
set PROD := bands coils plate ;

param supply (tr):  GARY   CLEV   PITT :=
            bands    400    700    800
            coils    800   1600   1800
            plate    200    300    300 ;

param demand (tr):
           FRA   DET   LAN   WIN   STL   FRE   LAF :=
   bands   300   300   100    75   650   225   250
   coils   500   750   400   250   950   850   500
   plate   100   100     0    50   200   100   250 ;

param limit default 625 ;

param cost :=

 [*,*,bands]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    30   10    8   10   11   71    6
        CLEV    22    7   10    7   21   82   13
        PITT    19   11   12   10   25   83   15

 [*,*,coils]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    39   14   11   14   16   82    8
        CLEV    27    9   12    9   26   95   17
        PITT    24   14   17   13   28   99   20

 [*,*,plate]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    41   15   12   16   17   86    8
        CLEV    29    9   13    9   28   99   18
        PITT    26   14   17   13   31  104   20 ;

Same as multi1.run, but using a separate subproblem for each product; subproblems are represented in AMPL by an indexed collection of named problems

# ----------------------------------------
# DANTZIG-WOLFE DECOMPOSITION FOR
# MULTI-COMMODITY TRANSPORTATION
# ----------------------------------------

model multi2.mod;
data multi2.dat;

param nIter default 0;

let {p in PROD} nPROP[p] := 0;
let {p in PROD} price_convex[p] := 1;
let {i in ORIG, j in DEST} price[i,j] := 0;

option omit_zero_rows 1;
option display_1col 0;
option display_eps .000001;

# ----------------------------------------------------------

problem MasterI: Artificial, Weight, Excess, Multi, Convex;

problem SubI {p in PROD}: Artif_Reduced_Cost[p], 
   {i in ORIG, j in DEST} Trans[i,j,p], 
   {i in ORIG} Supply[i,p], {j in DEST} Demand[j,p];

repeat { 

   let nIter := nIter + 1;
   printf "\nPHASE I -- ITERATION %d\n", nIter;

   for {p in PROD} { printf "\nPRODUCT %s\n\n", p;

      solve SubI[p];
      printf "\n";
      display {i in ORIG, j in DEST} Trans[i,j,p];

      if Artif_Reduced_Cost[p] < - 0.00001 then {
         let nPROP[p] := nPROP[p] + 1;
         let {i in ORIG, j in DEST}
            prop_ship[i,j,p,nPROP[p]] := Trans[i,j,p];
         let prop_cost[p,nPROP[p]] := 
            sum {i in ORIG, j in DEST} cost[i,j,p] * Trans[i,j,p];
      };
   };

   if min {p in PROD} Artif_Reduced_Cost[p] >= - 0.00001 then {
      printf "\n*** NO FEASIBLE SOLUTION ***\n";
      break;
   };

   solve MasterI;
   printf "\n";
   display Weight; display Multi.dual;
   display {i in ORIG, j in DEST} 
      limit[i,j] - sum {p in PROD, k in 1..nPROP[p]} 
         prop_ship[i,j,p,k] * Weight[p,k];

   if Excess <= 0.00001 then break;
   else {
      let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
      let {p in PROD} price_convex[p] := Convex[p].dual;
   };
};

# ----------------------------------------------------------

printf "\nSETTING UP FOR PHASE II\n";

problem MasterII: Total_Cost, Weight, Multi, Convex;

problem SubII {p in PROD}: Reduced_Cost[p], 
   {i in ORIG, j in DEST} Trans[i,j,p], 
   {i in ORIG} Supply[i,p], {j in DEST} Demand[j,p];

solve MasterII;
printf "\n";
display Weight; display Multi.dual; display Multi.slack;

let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
let {p in PROD} price_convex[p] := Convex[p].dual;

repeat {

   let nIter := nIter + 1;
   printf "\nPHASE II -- ITERATION %d\n\n", nIter;

   for {p in PROD} { printf "\nPRODUCT %s\n\n", p;

      solve SubII[p];
      printf "\n";
      display {i in ORIG, j in DEST} Trans[i,j,p];

      if Reduced_Cost[p] < - 0.00001 then  {
         let nPROP[p] := nPROP[p] + 1;
         let {i in ORIG, j in DEST}
            prop_ship[i,j,p,nPROP[p]] := Trans[i,j,p];
         let prop_cost[p,nPROP[p]] := 
            sum {i in ORIG, j in DEST} cost[i,j,p] * Trans[i,j,p];
      };
   };

   if min {p in PROD} Reduced_Cost[p] >= - 0.00001 then break;

   solve MasterII;
	
   printf "\n";
   display Weight;

   let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
   let {p in PROD} price_convex[p] := Convex[p].dual;
};

# ----------------------------------------------------------

printf "\nPHASE III\n";

let {i in ORIG, j in DEST, p in PROD}
   Trans[i,j,p] := sum {k in 1..nPROP[p]} prop_ship[i,j,p,k] * Weight[p,k];

param true_Total_Cost 
   := sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p].val;

printf "\n";
display true_Total_Cost;
display Trans;
# ----------------------------------------
# MULTI-COMMODITY FLOW USING
# DANTZIG-WOLFE DECOMPOSITION
# (one subproblem for each product)
# ----------------------------------------

### SUBPROBLEM ###

set ORIG;   # origins
set DEST;   # destinations
set PROD;   # products

param supply {ORIG,PROD} >= 0;  # amounts available at origins
param demand {DEST,PROD} >= 0;  # amounts required at destinations

   check {p in PROD}:
      sum {i in ORIG} supply[i,p] = sum {j in DEST} demand[j,p];

param price_convex {PROD};           # dual price on convexity constr
param price {ORIG,DEST} <= 0.000001; # dual price on shipment limit
param cost {ORIG,DEST,PROD} >= 0;    # shipment costs per unit

var Trans {ORIG,DEST,PROD} >= 0;   # units to be shipped

minimize Artif_Reduced_Cost {p in PROD}:
   sum {i in ORIG, j in DEST}
      (- price[i,j]) * Trans[i,j,p] - price_convex[p];

minimize Reduced_Cost {p in PROD}:
   sum {i in ORIG, j in DEST}
      (cost[i,j,p] - price[i,j]) * Trans[i,j,p] - price_convex[p];

subject to Supply {i in ORIG, p in PROD}:
   sum {j in DEST} Trans[i,j,p] = supply[i,p];

subject to Demand {j in DEST, p in PROD}:
   sum {i in ORIG} Trans[i,j,p] = demand[j,p];

### MASTER PROBLEM ###

param limit {ORIG,DEST} >= 0;   # max shipped on each link

param nPROP {PROD} integer >= 0;
param prop_ship {ORIG, DEST, p in PROD, 1..nPROP[p]} >= -0.000001;
param prop_cost {p in PROD, 1..nPROP[p]} >= 0;

            # For each proposal from each subproblem:
            # amount it ships over each link, and its cost

var Weight {p in PROD, 1..nPROP[p]} >= 0;
var Excess >= 0;

minimize Artificial: Excess;

minimize Total_Cost:
   sum {p in PROD, k in 1..nPROP[p]} prop_cost[p,k] * Weight[p,k];

subject to Multi {i in ORIG, j in DEST}:
   sum {p in PROD, k in 1..nPROP[p]} 
      prop_ship[i,j,p,k] * Weight[p,k] - Excess <= limit[i,j];

subject to Convex {p in PROD}: sum {k in 1..nPROP[p]} Weight[p,k] = 1;
set ORIG := GARY CLEV PITT ;
set DEST := FRA DET LAN WIN STL FRE LAF ;
set PROD := bands coils plate ;

param supply (tr):  GARY   CLEV   PITT :=
            bands    400    700    800
            coils    800   1600   1800
            plate    200    300    300 ;

param demand (tr):
           FRA   DET   LAN   WIN   STL   FRE   LAF :=
   bands   300   300   100    75   650   225   250
   coils   500   750   400   250   950   850   500
   plate   100   100     0    50   200   100   250 ;

param limit default 625 ;

param cost :=

 [*,*,bands]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    30   10    8   10   11   71    6
        CLEV    22    7   10    7   21   82   13
        PITT    19   11   12   10   25   83   15

 [*,*,coils]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    39   14   11   14   16   82    8
        CLEV    27    9   12    9   26   95   17
        PITT    24   14   17   13   28   99   20

 [*,*,plate]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    41   15   12   16   17   86    8
        CLEV    29    9   13    9   28   99   18
        PITT    26   14   17   13   31  104   20 ;

Same as multi2.run, except that the separate subproblems are realized by changing the data to a single AMPL named problem

# ----------------------------------------
# DANTZIG-WOLFE DECOMPOSITION FOR
# MULTI-COMMODITY TRANSPORTATION
# ----------------------------------------

model multi3.mod;
data multi3.dat;

param nIter default 0;
param nNegRC;

let {p in PROD} nPROP[p] := 0;
let {p in PROD} price_convex[p] := 1;
let {i in ORIG, j in DEST} price[i,j] := 0;

option omit_zero_rows 1;
option display_1col 0;
option display_eps .000001;

# ----------------------------------------------------------

problem MasterI: Artificial, Weight, Excess, Multi, Convex;
problem SubI: Artif_Reduced_Cost, Trans, Supply, Demand;

repeat {

   let nIter := nIter + 1;
   let nNegRC := 0;
   printf "\nPHASE I -- ITERATION %d\n", nIter;
   problem SubI;

   for {p in PROD} { printf "\nPRODUCT %s\n\n", p;

      let P := p;
      fix Trans; unfix {i in ORIG, j in DEST} Trans[i,j,p];

      solve SubI;
      printf "\n";
      display {i in ORIG, j in DEST} Trans[i,j,p];

      if Artif_Reduced_Cost < - 0.00001 then {
         let nNegRC := nNegRC + 1;
         let nPROP[p] := nPROP[p] + 1;
         let {i in ORIG, j in DEST}
            prop_ship[i,j,p,nPROP[p]] := Trans[i,j,p];
         let prop_cost[p,nPROP[p]] := 
            sum {i in ORIG, j in DEST} cost[i,j,p] * Trans[i,j,p];
      };
   };

   if nNegRC = 0 then {
      printf "\n*** NO FEASIBLE SOLUTION ***\n";
      break;
   };

   solve MasterI;
   printf "\n";
   display Weight; display Multi.dual;
   display {i in ORIG, j in DEST} 
      limit[i,j] - sum {p in PROD, k in 1..nPROP[p]} 
         prop_ship[i,j,p,k] * Weight[p,k];

   if Excess <= 0.00001 then break;
   else {
      let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
      let {p in PROD} price_convex[p] := Convex[p].dual;
   };
};

# ----------------------------------------------------------

printf "\nSETTING UP FOR PHASE II\n";

problem MasterII: Total_Cost, Weight, Multi, Convex;
problem SubII: Reduced_Cost, Trans, Supply, Demand;

solve MasterII;
printf "\n";
display Weight; display Multi.dual; display Multi.slack;

let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
let {p in PROD} price_convex[p] := Convex[p].dual;

repeat {

   let nIter := nIter + 1;
   let nNegRC := 0;
   printf "\nPHASE II -- ITERATION %d\n\n", nIter;
   problem SubII;

   for {p in PROD} { printf "\nPRODUCT %s\n\n", p;

      let P := p;
      fix Trans; unfix {i in ORIG, j in DEST} Trans[i,j,p];

      solve SubII;
      printf "\n";
      display {i in ORIG, j in DEST} Trans[i,j,p];

      if Reduced_Cost < - 0.00001 then  {
         let nNegRC := nNegRC + 1;
         let nPROP[p] := nPROP[p] + 1;
         let {i in ORIG, j in DEST}
            prop_ship[i,j,p,nPROP[p]] := Trans[i,j,p];
         let prop_cost[p,nPROP[p]] := 
            sum {i in ORIG, j in DEST} cost[i,j,p] * Trans[i,j,p];
      };
   };

   if nNegRC = 0 then break;

   solve MasterII;
	
   printf "\n";
   display Weight;

   let {i in ORIG, j in DEST} price[i,j] := Multi[i,j].dual;
   let {p in PROD} price_convex[p] := Convex[p].dual;
};

# ----------------------------------------------------------

printf "\nPHASE III\n";

let {i in ORIG, j in DEST, p in PROD}
   Trans[i,j,p] := sum {k in 1..nPROP[p]} prop_ship[i,j,p,k] * Weight[p,k];

param true_Total_Cost 
   := sum {i in ORIG, j in DEST, p in PROD} cost[i,j,p] * Trans[i,j,p].val;

printf "\n";
display true_Total_Cost;
display Trans;
# ----------------------------------------
# MULTI-COMMODITY FLOW USING
# DANTZIG-WOLFE DECOMPOSITION
# (one single-product subproblem model)
# ----------------------------------------

### SUBPROBLEM ###

set ORIG;   # origins
set DEST;   # destinations
set PROD;   # products

param supply {ORIG,PROD} >= 0;  # amounts available at origins
param demand {DEST,PROD} >= 0;  # amounts required at destinations

   check {p in PROD}:
      sum {i in ORIG} supply[i,p] = sum {j in DEST} demand[j,p];

param price_convex {PROD};           # dual price on convexity constr
param price {ORIG,DEST} <= 0.000001; # dual price on shipment limit
param cost {ORIG,DEST,PROD} >= 0;    # shipment costs per unit

var Trans {ORIG,DEST,PROD} >= 0;     # units to be shipped

# ----------------------------------------

param P symbolic within PROD;

minimize Artif_Reduced_Cost:
   sum {i in ORIG, j in DEST}
      (- price[i,j]) * Trans[i,j,P] - price_convex[P];

minimize Reduced_Cost:
   sum {i in ORIG, j in DEST}
      (cost[i,j,P] - price[i,j]) * Trans[i,j,P] - price_convex[P];

subject to Supply {i in ORIG}:
   sum {j in DEST} Trans[i,j,P] = supply[i,P];

subject to Demand {j in DEST}:
   sum {i in ORIG} Trans[i,j,P] = demand[j,P];


### MASTER PROBLEM ###

param limit {ORIG,DEST} >= 0;   # max shipped on each link

param nPROP {PROD} integer >= 0;
param prop_ship {ORIG, DEST, p in PROD, 1..nPROP[p]} >= -0.000001;
param prop_cost {p in PROD, 1..nPROP[p]} >= 0;

            # For each proposal from each subproblem:
            # amount it ships over each link, and its cost

var Weight {p in PROD, 1..nPROP[p]} >= 0;
var Excess >= 0;

minimize Artificial: Excess;

minimize Total_Cost:
   sum {p in PROD, k in 1..nPROP[p]} prop_cost[p,k] * Weight[p,k];

subject to Multi {i in ORIG, j in DEST}:
   sum {p in PROD, k in 1..nPROP[p]} 
      prop_ship[i,j,p,k] * Weight[p,k] - Excess <= limit[i,j];

subject to Convex {p in PROD}: sum {k in 1..nPROP[p]} Weight[p,k] = 1;
set ORIG := GARY CLEV PITT ;
set DEST := FRA DET LAN WIN STL FRE LAF ;
set PROD := bands coils plate ;

param supply (tr):  GARY   CLEV   PITT :=
            bands    400    700    800
            coils    800   1600   1800
            plate    200    300    300 ;

param demand (tr):
           FRA   DET   LAN   WIN   STL   FRE   LAF :=
   bands   300   300   100    75   650   225   250
   coils   500   750   400   250   950   850   500
   plate   100   100     0    50   200   100   250 ;

param limit default 625 ;

param cost :=

 [*,*,bands]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    30   10    8   10   11   71    6
        CLEV    22    7   10    7   21   82   13
        PITT    19   11   12   10   25   83   15

 [*,*,coils]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    39   14   11   14   16   82    8
        CLEV    27    9   12    9   26   95   17
        PITT    24   14   17   13   28   99   20

 [*,*,plate]:  FRA  DET  LAN  WIN  STL  FRE  LAF :=
        GARY    41   15   12   16   17   86    8
        CLEV    29    9   13    9   28   99   18
        PITT    26   14   17   13   31  104   20 ;

Benders decomposition for a stochastic programming variant of a multi-period production problem (see Exercise 4-5)

# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

model stoch1.mod;
data stoch.dat;

option solver cplex;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Make1, Inv1, Sell1, Min_Stage2_Profit,
   Expected_Profit, Cut_Defn, Time1, Balance1;

problem Sub: Make, Inv, Sell, Stage2_Profit, Time, Balance2, Balance;

let nCUT := 0;
let Min_Stage2_Profit := Infinity;
let {p in PROD} inv1[p] := 0;

param GAP default Infinity;

for {1..50} { printf "\nITERATION %d\n\n", nCUT+1;

   solve Sub;
   printf "\n";

   if Stage2_Profit < Min_Stage2_Profit - 0.00001 then {
      let GAP := min (GAP, Min_Stage2_Profit - Stage2_Profit);
      option display_1col 0;
      display GAP, Make, Sell, Inv;
      let nCUT := nCUT + 1;
      let {t in 2..T, s in SCEN} time_price[t,s,nCUT] := Time[t,s].dual;
      let {p in PROD, s in SCEN} bal2_price[p,s,nCUT] := Balance2[p,s].dual;
      let {p in PROD, t in 2..T, s in SCEN} 
         sell_lim_price[p,t,s,nCUT] := Sell[p,t,s].urc;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   option display_1col 20;
   display Make1, Inv1, Sell1;

   let {p in PROD} inv1[p] := Inv1[p];
};

printf "\nOPTIMAL SOLUTION FOUND\nExpected Profit = %f\n\n", Expected_Profit;
option display_1col 0;

param MAKE {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Make1[p].val else Make[p,t,s].val;
param SELL {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Sell1[p].val else Sell[p,t,s].val;
param INV {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Inv1[p].val else Inv[p,t,s].val;

for {s in SCEN} {
   printf "SCENARIO %s\n", s;
   display {p in PROD, t in 1..T} 
      (MAKE[p,t,s], SELL[p,t,s], INV[p,t,s]);
   }
# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

### SUBPROBLEM (always feasible) ###

set PROD;     # products
param T > 0;  # number of weeks
set SCEN;     # number of scenarios

param rate {PROD} > 0;          # tons per hour produced
param avail {1..T} >= 0;        # hours available in week
param market {PROD,1..T} >= 0;  # limit on tons sold in week

param prodcost {PROD} >= 0;     # cost per ton produced
param invcost {PROD} >= 0;      # carrying cost/ton of inventory

param revenue {PROD,1..T,SCEN} >= 0;  # projected revenue/ton

param prob {SCEN} >= 0, <= 1;
   check: 0.99999 < sum {s in SCEN} prob[s] < 1.00001;

param inv1 {PROD} >= 0;  # inventory at end of first period

var Make {PROD,2..T,SCEN} >= 0;        # tons produced
var Inv {PROD,2..T,SCEN} >= 0;         # tons inventoried
var Sell {p in PROD, t in 2..T, SCEN}  # tons sold
   >= 0, <= market[p,t];

maximize Stage2_Profit:
   sum {s in SCEN} prob[s] *  
      sum {p in PROD, t in 2..T} (revenue[p,t,s]*Sell[p,t,s] -
         prodcost[p]*Make[p,t,s] - invcost[p]*Inv[p,t,s]);

subject to Time {t in 2..T, s in SCEN}:
   sum {p in PROD} (1/rate[p]) * Make[p,t,s] <= avail[t];

subject to Balance2 {p in PROD, s in SCEN}:
   Make[p,2,s] + inv1[p] = Sell[p,2,s] + Inv[p,2,s];

subject to Balance {p in PROD, t in 3..T, s in SCEN}:
   Make[p,t,s] + Inv[p,t-1,s] = Sell[p,t,s] + Inv[p,t,s];

### MASTER PROBLEM ###

param inv0 {PROD} >= 0;  # initial inventory

param nCUT >= 0 integer;

param time_price {2..T,SCEN,1..nCUT} >= -0.000001;
param bal2_price {PROD,SCEN,1..nCUT};
param sell_lim_price {PROD,2..T,SCEN,1..nCUT} >= -0.000001;

var Make1 {PROD} >= 0;
var Inv1 {PROD} >= 0;
var Sell1 {p in PROD} >= 0, <= market[p,1];

var Min_Stage2_Profit >= 0;

maximize Expected_Profit:
   sum {s in SCEN} prob[s] *  
     sum {p in PROD} (revenue[p,1,s]*Sell1[p] - 
        prodcost[p]*Make1[p] - invcost[p]*Inv1[p]) +
   Min_Stage2_Profit;

subj to Cut_Defn {k in 1..nCUT}:
   Min_Stage2_Profit <= 
      sum {t in 2..T, s in SCEN} time_price[t,s,k] * avail[t] +
      sum {p in PROD, s in SCEN} bal2_price[p,s,k] * (-Inv1[p]) +
      sum {p in PROD, t in 2..T, s in SCEN}
         sell_lim_price[p,t,s,k] * market[p,t];

subject to Time1:
   sum {p in PROD} (1/rate[p]) * Make1[p] <= avail[1];

subject to Balance1 {p in PROD}:
   Make1[p] + inv0[p] = Sell1[p] + Inv1[p];
# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

model stoch2.mod;
data stoch.dat;

option solver cplex;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Make1, Inv1, Sell1, Min_Stage2_Profit,
   Expected_Profit, Cut_Defn, Time1, Balance1;

problem Sub {s in SCEN}: 
   {p in PROD, t in 2..T} Make[p,t,s], 
   {p in PROD, t in 2..T} Inv[p,t,s], 
   {p in PROD, t in 2..T} Sell[p,t,s], 
   Exp_Stage2_Profit[s], 
   {t in 2..T} Time[t,s], 
   {p in PROD} Balance2[p,s], 
   {p in PROD, t in 3..T} Balance[p,t,s];

let nCUT := 0;
let Min_Stage2_Profit := Infinity;
let {p in PROD} inv1[p] := 0;

param GAP default Infinity;
param newGAP;

for {1..50} { printf "\nITERATION %d\n\n", nCUT+1;

   let nCUT := nCUT + 1;

   for {s in SCEN} { 
      solve Sub[s];
      let {t in 2..T} time_price[t,s,nCUT] := Time[t,s].dual;
      let {p in PROD} bal2_price[p,s,nCUT] := Balance2[p,s].dual;
      let {p in PROD, t in 2..T} 
         sell_lim_price[p,t,s,nCUT] := Sell[p,t,s].urc;
      }

   let newGAP := Min_Stage2_Profit - sum {s in SCEN} Exp_Stage2_Profit[s];
   printf "\n";

   if newGAP > 0.00001 then {
      let GAP := min (GAP, newGAP);
      option display_1col 0;
      display GAP, Make, Sell, Inv;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   option display_1col 20;
   display Make1, Inv1, Sell1;

   let {p in PROD} inv1[p] := Inv1[p];
};

printf "\nOPTIMAL SOLUTION FOUND\nExpected Profit = %f\n\n", Expected_Profit;
option display_1col 0;

param MAKE {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Make1[p].val else Make[p,t,s].val;
param SELL {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Sell1[p].val else Sell[p,t,s].val;
param INV {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Inv1[p].val else Inv[p,t,s].val;

for {s in SCEN} {
   printf "SCENARIO %s\n", s;
   display {p in PROD, t in 1..T} 
      (MAKE[p,t,s], SELL[p,t,s], INV[p,t,s]);
   }

Same as stoch1.run, but using a separate subproblem for each scenario; subproblems are represented in AMPL by an indexed collection of named problems

# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

model stoch2.mod;
data stoch.dat;

option solver cplex;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Make1, Inv1, Sell1, Min_Stage2_Profit,
   Expected_Profit, Cut_Defn, Time1, Balance1;

problem Sub {s in SCEN}: 
   {p in PROD, t in 2..T} Make[p,t,s], 
   {p in PROD, t in 2..T} Inv[p,t,s], 
   {p in PROD, t in 2..T} Sell[p,t,s], 
   Exp_Stage2_Profit[s], 
   {t in 2..T} Time[t,s], 
   {p in PROD} Balance2[p,s], 
   {p in PROD, t in 3..T} Balance[p,t,s];

let nCUT := 0;
let Min_Stage2_Profit := Infinity;
let {p in PROD} inv1[p] := 0;

param GAP default Infinity;
param newGAP;

for {1..50} { printf "\nITERATION %d\n\n", nCUT+1;

   let nCUT := nCUT + 1;

   for {s in SCEN} { 
      solve Sub[s];
      let {t in 2..T} time_price[t,s,nCUT] := Time[t,s].dual;
      let {p in PROD} bal2_price[p,s,nCUT] := Balance2[p,s].dual;
      let {p in PROD, t in 2..T} 
         sell_lim_price[p,t,s,nCUT] := Sell[p,t,s].urc;
      }

   let newGAP := Min_Stage2_Profit - sum {s in SCEN} Exp_Stage2_Profit[s];
   printf "\n";

   if newGAP > 0.00001 then {
      let GAP := min (GAP, newGAP);
      option display_1col 0;
      display GAP, Make, Sell, Inv;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   option display_1col 20;
   display Make1, Inv1, Sell1;

   let {p in PROD} inv1[p] := Inv1[p];
};

printf "\nOPTIMAL SOLUTION FOUND\nExpected Profit = %f\n\n", Expected_Profit;
option display_1col 0;

param MAKE {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Make1[p].val else Make[p,t,s].val;
param SELL {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Sell1[p].val else Sell[p,t,s].val;
param INV {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Inv1[p].val else Inv[p,t,s].val;

for {s in SCEN} {
   printf "SCENARIO %s\n", s;
   display {p in PROD, t in 1..T} 
      (MAKE[p,t,s], SELL[p,t,s], INV[p,t,s]);
   }
# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

### SUBPROBLEM ###

set PROD;     # products
param T > 0;  # number of weeks
set SCEN;     # number of scenarios

param rate {PROD} > 0;          # tons per hour produced
param avail {1..T} >= 0;        # hours available in week
param market {PROD,1..T} >= 0;  # limit on tons sold in week

param prodcost {PROD} >= 0;     # cost per ton produced
param invcost {PROD} >= 0;      # carrying cost/ton of inventory

param revenue {PROD,1..T,SCEN} >= 0;  # projected revenue/ton

param prob {SCEN} >= 0, <= 1;
   check: 0.99999 < sum {s in SCEN} prob[s] < 1.00001;

param inv1 {PROD} >= 0;  # inventory at end of first period

var Make {PROD,2..T,SCEN} >= 0;        # tons produced
var Inv {PROD,2..T,SCEN} >= 0;         # tons inventoried
var Sell {p in PROD, t in 2..T, SCEN}  # tons sold
   >= 0, <= market[p,t];

maximize Exp_Stage2_Profit {s in SCEN}: prob[s] *
   sum {p in PROD, t in 2..T} (revenue[p,t,s]*Sell[p,t,s] -
      prodcost[p]*Make[p,t,s] - invcost[p]*Inv[p,t,s]);

subject to Time {t in 2..T, s in SCEN}:
   sum {p in PROD} (1/rate[p]) * Make[p,t,s] <= avail[t];

subject to Balance2 {p in PROD, s in SCEN}:
   Make[p,2,s] + inv1[p] = Sell[p,2,s] + Inv[p,2,s];

subject to Balance {p in PROD, t in 3..T, s in SCEN}:
   Make[p,t,s] + Inv[p,t-1,s] = Sell[p,t,s] + Inv[p,t,s];

### MASTER PROBLEM ###

param inv0 {PROD} >= 0;  # initial inventory

param nCUT >= 0 integer;

param time_price {2..T,SCEN,1..nCUT} >= -0.000001 default 0;
param bal2_price {PROD,SCEN,1..nCUT} default 0;
param sell_lim_price {PROD,2..T,SCEN,1..nCUT} >= -0.000001 default 0;

var Make1 {PROD} >= 0;
var Inv1 {PROD} >= 0;
var Sell1 {p in PROD} >= 0, <= market[p,1];

var Min_Stage2_Profit >= 0;

maximize Expected_Profit:
   sum {s in SCEN} prob[s] *  
     sum {p in PROD} (revenue[p,1,s]*Sell1[p] - 
        prodcost[p]*Make1[p] - invcost[p]*Inv1[p]) +
   Min_Stage2_Profit;

subj to Cut_Defn {k in 1..nCUT}:
   Min_Stage2_Profit <= 
      sum {t in 2..T, s in SCEN} time_price[t,s,k] * avail[t] +
      sum {p in PROD, s in SCEN} bal2_price[p,s,k] * (-Inv1[p]) +
      sum {p in PROD, t in 2..T, s in SCEN}
         sell_lim_price[p,t,s,k] * market[p,t];

subject to Time1:
   sum {p in PROD} (1/rate[p]) * Make1[p] <= avail[1];

subject to Balance1 {p in PROD}:
   Make1[p] + inv0[p] = Sell1[p] + Inv1[p];
# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

model stoch2.mod;
data stoch.dat;

option solver cplex;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Make1, Inv1, Sell1, Min_Stage2_Profit,
   Expected_Profit, Cut_Defn, Time1, Balance1;

problem Sub {s in SCEN}: 
   {p in PROD, t in 2..T} Make[p,t,s], 
   {p in PROD, t in 2..T} Inv[p,t,s], 
   {p in PROD, t in 2..T} Sell[p,t,s], 
   Exp_Stage2_Profit[s], 
   {t in 2..T} Time[t,s], 
   {p in PROD} Balance2[p,s], 
   {p in PROD, t in 3..T} Balance[p,t,s];

let nCUT := 0;
let Min_Stage2_Profit := Infinity;
let {p in PROD} inv1[p] := 0;

param GAP default Infinity;
param newGAP;

for {1..50} { printf "\nITERATION %d\n\n", nCUT+1;

   let nCUT := nCUT + 1;

   for {s in SCEN} { 
      solve Sub[s];
      let {t in 2..T} time_price[t,s,nCUT] := Time[t,s].dual;
      let {p in PROD} bal2_price[p,s,nCUT] := Balance2[p,s].dual;
      let {p in PROD, t in 2..T} 
         sell_lim_price[p,t,s,nCUT] := Sell[p,t,s].urc;
      }

   let newGAP := Min_Stage2_Profit - sum {s in SCEN} Exp_Stage2_Profit[s];
   printf "\n";

   if newGAP > 0.00001 then {
      let GAP := min (GAP, newGAP);
      option display_1col 0;
      display GAP, Make, Sell, Inv;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   option display_1col 20;
   display Make1, Inv1, Sell1;

   let {p in PROD} inv1[p] := Inv1[p];
};

printf "\nOPTIMAL SOLUTION FOUND\nExpected Profit = %f\n\n", Expected_Profit;
option display_1col 0;

param MAKE {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Make1[p].val else Make[p,t,s].val;
param SELL {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Sell1[p].val else Sell[p,t,s].val;
param INV {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Inv1[p].val else Inv[p,t,s].val;

for {s in SCEN} {
   printf "SCENARIO %s\n", s;
   display {p in PROD, t in 1..T} 
      (MAKE[p,t,s], SELL[p,t,s], INV[p,t,s]);
   }

Same as stoch2.run, except that the separate subproblems are realized by changing the data to a single AMPL named problem

# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

model stoch3.mod;
data stoch.dat;

option solver cplex;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Make1, Inv1, Sell1, Min_Stage2_Profit,
   Expected_Profit, Cut_Defn, Time1, Balance1;

problem Sub: Make, Inv, Sell, Exp_Stage2_Profit, Time, Balance2, Balance;

let nCUT := 0;
let Min_Stage2_Profit := Infinity;
let {p in PROD} inv1[p] := 0;

param GAP default Infinity;
param newGAP;

for {1..50} { printf "\nITERATION %d\n\n", nCUT+1;

   let nCUT := nCUT + 1;
   let newGAP := Min_Stage2_Profit;

   for {s in SCEN} { 
      let S := s;
      solve Sub;
      let newGAP := newGAP - Exp_Stage2_Profit;
      let {t in 2..T} time_price[t,S,nCUT] := Time[t].dual;
      let {p in PROD} bal2_price[p,S,nCUT] := Balance2[p].dual;
      let {p in PROD, t in 2..T} 
         sell_lim_price[p,t,S,nCUT] := Sell[p,t,S].urc;
      }

   printf "\n";

   if newGAP > 0.00001 then {
      let GAP := min (GAP, newGAP);
      option display_1col 0;
      display GAP, Make, Sell, Inv;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   option display_1col 20;
   display Make1, Inv1, Sell1;

   let {p in PROD} inv1[p] := Inv1[p];
};

printf "\nOPTIMAL SOLUTION FOUND\nExpected Profit = %f\n\n", Expected_Profit;
option display_1col 0;


param MAKE {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Make1[p].val else Make[p,t,s].val;
param SELL {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Sell1[p].val else Sell[p,t,s].val;
param INV {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Inv1[p].val else Inv[p,t,s].val;

for {s in SCEN} {
   printf "SCENARIO %s\n", s;
   display {p in PROD, t in 1..T} 
      (MAKE[p,t,s], SELL[p,t,s], INV[p,t,s]);
   }
# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

### SUBPROBLEMS ###

set PROD;     # products
param T > 0;  # number of weeks
set SCEN;     # number of scenarios

param rate {PROD} > 0;          # tons per hour produced
param avail {1..T} >= 0;        # hours available in week
param market {PROD,1..T} >= 0;  # limit on tons sold in week

param prodcost {PROD} >= 0;     # cost per ton produced
param invcost {PROD} >= 0;      # carrying cost/ton of inventory

param revenue {PROD,1..T,SCEN} >= 0;  # projected revenue/ton

param prob {SCEN} >= 0, <= 1;
   check: 0.99999 < sum {s in SCEN} prob[s] < 1.00001;

param inv1 {PROD} >= 0;  # inventory at end of first period

var Make {PROD,2..T,SCEN} >= 0;       # tons produced
var Inv {PROD,2..T,SCEN} >= 0;        # tons inventoried
var Sell {p in PROD, t in 2..T,SCEN}  # tons sold
   >= 0, <= market[p,t];

# ----------------------------------------

param S symbolic in SCEN;       # scenario of subproblem

maximize Exp_Stage2_Profit: prob[S] *
   sum {p in PROD, t in 2..T} (revenue[p,t,S]*Sell[p,t,S] -
      prodcost[p]*Make[p,t,S] - invcost[p]*Inv[p,t,S]);

subject to Time {t in 2..T}:
   sum {p in PROD} (1/rate[p]) * Make[p,t,S] <= avail[t];

subject to Balance2 {p in PROD}:
   Make[p,2,S] + inv1[p] = Sell[p,2,S] + Inv[p,2,S];

subject to Balance {p in PROD, t in 3..T}:
   Make[p,t,S] + Inv[p,t-1,S] = Sell[p,t,S] + Inv[p,t,S];

### MASTER PROBLEM ###

param inv0 {PROD} >= 0;  # initial inventory

param nCUT >= 0 integer;

param time_price {2..T,SCEN,1..nCUT} >= -0.000001 default 0;
param bal2_price {PROD,SCEN,1..nCUT} default 0;
param sell_lim_price {PROD,2..T,SCEN,1..nCUT} >= -0.000001 default 0;

var Make1 {PROD} >= 0;
var Inv1 {PROD} >= 0;
var Sell1 {p in PROD} >= 0, <= market[p,1];

var Min_Stage2_Profit >= 0;

maximize Expected_Profit:
   sum {s in SCEN} prob[s] *  
     sum {p in PROD} (revenue[p,1,s]*Sell1[p] - 
        prodcost[p]*Make1[p] - invcost[p]*Inv1[p]) +
   Min_Stage2_Profit;

subj to Cut_Defn {k in 1..nCUT}:
   Min_Stage2_Profit <= 
      sum {t in 2..T, s in SCEN} time_price[t,s,k] * avail[t] +
      sum {p in PROD, s in SCEN} bal2_price[p,s,k] * (-Inv1[p]) +
      sum {p in PROD, t in 2..T, s in SCEN}
         sell_lim_price[p,t,s,k] * market[p,t];

subject to Time1:
   sum {p in PROD} (1/rate[p]) * Make1[p] <= avail[1];

subject to Balance1 {p in PROD}:
   Make1[p] + inv0[p] = Sell1[p] + Inv1[p];
# ----------------------------------------
# STOCHASTIC PROGRAMMING PROBLEM 
# USING BENDERS DECOMPOSITION
# ----------------------------------------

model stoch2.mod;
data stoch.dat;

option solver cplex;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Make1, Inv1, Sell1, Min_Stage2_Profit,
   Expected_Profit, Cut_Defn, Time1, Balance1;

problem Sub {s in SCEN}: 
   {p in PROD, t in 2..T} Make[p,t,s], 
   {p in PROD, t in 2..T} Inv[p,t,s], 
   {p in PROD, t in 2..T} Sell[p,t,s], 
   Exp_Stage2_Profit[s], 
   {t in 2..T} Time[t,s], 
   {p in PROD} Balance2[p,s], 
   {p in PROD, t in 3..T} Balance[p,t,s];

let nCUT := 0;
let Min_Stage2_Profit := Infinity;
let {p in PROD} inv1[p] := 0;

param GAP default Infinity;
param newGAP;

for {1..50} { printf "\nITERATION %d\n\n", nCUT+1;

   let nCUT := nCUT + 1;

   for {s in SCEN} { 
      solve Sub[s];
      let {t in 2..T} time_price[t,s,nCUT] := Time[t,s].dual;
      let {p in PROD} bal2_price[p,s,nCUT] := Balance2[p,s].dual;
      let {p in PROD, t in 2..T} 
         sell_lim_price[p,t,s,nCUT] := Sell[p,t,s].urc;
      }

   let newGAP := Min_Stage2_Profit - sum {s in SCEN} Exp_Stage2_Profit[s];
   printf "\n";

   if newGAP > 0.00001 then {
      let GAP := min (GAP, newGAP);
      option display_1col 0;
      display GAP, Make, Sell, Inv;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   option display_1col 20;
   display Make1, Inv1, Sell1;

   let {p in PROD} inv1[p] := Inv1[p];
};

printf "\nOPTIMAL SOLUTION FOUND\nExpected Profit = %f\n\n", Expected_Profit;
option display_1col 0;

param MAKE {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Make1[p].val else Make[p,t,s].val;
param SELL {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Sell1[p].val else Sell[p,t,s].val;
param INV {p in PROD, t in 1..T, s in SCEN}
   := if t = 1 then Inv1[p].val else Inv[p,t,s].val;

for {s in SCEN} {
   printf "SCENARIO %s\n", s;
   display {p in PROD, t in 1..T} 
      (MAKE[p,t,s], SELL[p,t,s], INV[p,t,s]);
   }

Benders decomposition for a location-transportation problem (original model in trnloc.mod)

# ----------------------------------------
# BENDERS DECOMPOSITION FOR
# THE LOCATION-TRANSPORTATION PROBLEM
# ----------------------------------------

model trnloc1.mod;
data trnloc.dat;

option omit_zero_rows 1;
option display_eps .000001;

problem Master: Build, Max_Ship_Cost, Total_Cost, Cut_Defn;
   option display_1col 20;
   option solver cplex, cplex_options '';

problem Sub: Ship, Ship_Cost, Supply, Demand;
   option presolve 0;
   option display_1col 0;
   option solver cplex, cplex_options 'netopt 2 netfind 2';

let nCUT := 0;
let Max_Ship_Cost := 0;
let {i in ORIG} build[i] := 1;

param GAP default Infinity;

suffix dunbdd;

repeat { printf "\nITERATION %d\n\n", nCUT+1;

   solve Sub;
   printf "\n";

   if Sub.result = "infeasible" then {
      let nCUT := nCUT + 1;
      let cut_type[nCUT] := "ray";
      let {i in ORIG} supply_price[i,nCUT] := Supply[i].dunbdd;
      let {j in DEST} demand_price[j,nCUT] := Demand[j].dunbdd;
      expand Cut_Defn[nCUT];
      }
   else if Ship_Cost > Max_Ship_Cost + 0.00001 then {
      let GAP := min (GAP, Ship_Cost - Max_Ship_Cost);
      display GAP, Ship;
      let nCUT := nCUT + 1;
      let cut_type[nCUT] := "point";
      let {i in ORIG} supply_price[i,nCUT] := Supply[i].dual;
      let {j in DEST} demand_price[j,nCUT] := Demand[j].dual;
      }
   else break;

   printf "\nRE-SOLVING MASTER PROBLEM\n\n";

   solve Master;
   printf "\n";
   display Build;

   let {i in ORIG} build[i] := Build[i];
};

option display_1col 0;
display Ship;
# ----------------------------------------
# LOCATION-TRANSPORTATION PROBLEM 
# USING BENDERS DECOMPOSITION
# (using primal formulation of subproblem)
# ----------------------------------------

### SUBPROBLEM ###

set ORIG;   # shipment origins (warehouses)
set DEST;   # shipment destinations (stores)

param supply {ORIG} > 0;
param demand {DEST} > 0;

param fix_cost {ORIG} > 0;
param var_cost {ORIG,DEST} > 0;

param build {ORIG} binary;  # = 1 iff warehouse built at i

var Ship {ORIG,DEST} >= 0;  # amounts shipped

minimize Ship_Cost:
   sum {i in ORIG, j in DEST} var_cost[i,j] * Ship[i,j];

subj to Supply {i in ORIG}:
   sum {j in DEST} Ship[i,j] <= supply[i] * build[i];

subj to Demand {j in DEST}:
   sum {i in ORIG} Ship[i,j] = demand[j];

### MASTER PROBLEM ###

param nCUT >= 0 integer;
param cut_type {1..nCUT} symbolic within {"point","ray"};
param supply_price {ORIG,1..nCUT} <= 0.000001;
param demand_price {DEST,1..nCUT};

var Build {ORIG} binary;   # = 1 iff warehouse built at i
var Max_Ship_Cost >= 0;

minimize Total_Cost:
   sum {i in ORIG} fix_cost[i] * Build[i] + Max_Ship_Cost;

subj to Cut_Defn {k in 1..nCUT}:
   if cut_type[k] = "point" then Max_Ship_Cost >= 
      sum {i in ORIG} supply_price[i,k] * supply[i] * Build[i] + 
      sum {j in DEST} demand_price[j,k] * demand[j];
param: ORIG: supply := 
   1 23070    6 21090   11 22690   16 17110   21 16720
   2 18290    7 16650   12 19360   17 15380   22 21540
   3 20010    8 18420   13 22330   18 18690   23 16500
   4 15080    9 19160   14 15440   19 20720   24 15310
   5 17540   10 18860   15 19330   20 21220   25 18970 ;

param: DEST: demand :=
        A3    12000
        A6    12000
        A8    14000
        A9    13500
        B2    25000
        B4    29000 ;

param fix_cost  default 500000 ;

param var_cost:

          A3      A6      A8       A9      B2      B4  :=
    1   73.78   14.76   86.82    91.19   51.03   76.49
    2   60.28   20.92   76.43    83.99   58.84   68.86
    3   58.18   21.64   69.84    72.39   61.64   58.39
    4   50.37   21.74   61.49    65.72   60.48   56.68
    5   42.73   35.19   44.11    58.08   65.76   55.51
    6   44.62   39.21   44.44    48.32   76.12   51.17
    7   49.31   51.72   36.27    42.96   84.52   49.61
    8   50.79   59.25   22.53    33.22   94.30   49.66
    9   51.93   72.13   21.66    29.39   93.52   49.63
   10   65.90   13.07   79.59    86.07   46.83   69.55
   11   50.79    9.99   67.83    78.81   49.34   60.79
   12   47.51   12.95   59.57    67.71   51.13   54.65
   13   39.36   19.01   56.39    62.37   57.25   47.91
   14   33.55   30.16   40.66    48.50   60.83   42.51
   15   34.17   40.46   40.23    47.10   66.22   38.94
   16   41.68   53.03   22.56    30.89   77.22   35.88
   17   42.75   62.94   18.58    27.02   80.36   40.11
   18   46.46   71.17   17.17    21.16   91.65   41.56
   19   56.83    8.84   83.99    91.88   41.38   67.79
   20   46.21    2.92   68.94    76.86   38.89   60.38
   21   41.67   11.69   61.05    70.06   43.24   48.48
   22   25.57   17.59   54.93    57.07   44.93   43.97
   23   28.16   29.39   38.64    46.48   50.16   34.20
   24   26.97   41.62   29.72    40.61   59.56   31.21
   25   34.24   54.09   22.13    28.43   69.68   24.09 ;

Same as trnloc2a.run, but model has upper limits on the Ship variables: LP relaxation bound is still poor, but subproblem does not have the integrality property and considerable improvement is made

# ----------------------------------------
# LAGRANGIAN RELAXATION FOR
# THE LOCATION-TRANSPORTATION PROBLEM
# ----------------------------------------

printf "\nLP RELAXATION\n\n";

model trnloc2b.mod;
data trnloc2.dat;

option omit_zero_rows 1;
option display_eps .000001;
option solution_round 8;

option solver cplex;
option solver_msg 0;

option relax_integrality 1;
objective Shipping_Cost;

solve;

param LB; param UB;
let LB := Shipping_Cost.val;
let UB := sum {j in CITY} max {i in CITY} ship_cost[i,j];

option relax_integrality 0;

problem LowerBound: Build, Ship, Supply, Limit, Lagrangian;
problem UpperBound: Ship, Supply, Demand, Limit, Shipping_Cost;

let {j in CITY} mult[j] := 0;

param slack {CITY};
param scale  default 1;
param norm;
param step;

param same  default 0;
param same_limit := 3;

param iter_limit := 20;
param LBlog {0..iter_limit};  let LBlog[0] := LB;
param UBlog {0..iter_limit};  let UBlog[0] := UB;
param scalelog {1..iter_limit};
param steplog {1..iter_limit};

for {k in 1..iter_limit} { printf "\nITERATION %d\n\n", k;
   solve LowerBound;

   let {j in CITY} slack[j] := sum {i in CITY} Ship[i,j] - demand[j];

   if Lagrangian > LB + 0.000001 then {
      let LB := Lagrangian;
      let same := 0; }
   else let same := same + 1;

   if same = same_limit then {
      let scale := scale / 2;
      let same := 0;
      };

   let norm := sum {j in CITY} slack[j]^2;
   let step := scale * (UB - Lagrangian) / norm;

   let {j in CITY} mult[j] := mult[j] less step * slack[j];

   if sum {i in CITY} supply[i] * Build[i] 
         >= sum {j in CITY} demand[j] - 1e-8 then { 
      solve UpperBound;
      let UB := min (UB, Shipping_Cost);
      }

   let LBlog[k] := LB;
   let UBlog[k] := UB;
   let scalelog[k] := scale;
   let steplog[k] := step;

   if UB - LB < 0.9999 then {break};
   if step < 1.0e-8 then {break};
   }

printf "\n\n";
display LBlog, UBlog, scalelog, steplog;
# ----------------------------------------
# LOCATION-TRANSPORTATION PROBLEM 
# USING LAGRANGIAN RELAXATION
# (with upper bounds on Ship variables)
# ----------------------------------------

set CITY;

param build_limit integer;

param demand {i in CITY} integer > 0;
param supply {CITY} integer > 0;

param ship_cost {i in CITY, j in CITY} >= 0;

param mult {CITY} >= 0;   # Lagrange multipliers for Demand constr

var Build {CITY} integer >= 0 <= 1;  # = 1 iff warehouse built at i
var Ship {i in CITY, j in CITY}      # amounts shipped
   >= 0, <= demand[j];

minimize Lagrangian:
   sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j] +
   sum {j in CITY} mult[j] * (demand[j] - sum {i in CITY} Ship[i,j]);

minimize Shipping_Cost:
   sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j];

subj to Supply {i in CITY}:
   sum {j in CITY} Ship[i,j] <= supply[i] * Build[i];

subj to Demand {j in CITY}:
   sum {i in CITY} Ship[i,j] >= demand[j];

subj to Limit:  sum {i in CITY} Build[i] <= build_limit;
param build_limit := 8;

param: CITY: supply demand :=
         1     19     4
         2     13     1
         3     20     2
         4     18     7
         5     23     9
         6     25     9
         7     22     3
         8     28     9
         9     18     5
        10     16     2
        11     17     4
        12     14     1
        13     22     9
        14     13     3
        15     18     1
        16     20     7
        17     15     5
        18     23     9
        19     25     2
        20     19     4
        21     18     9
        22     19     6
        23     22     5
        24     22     4
        25     21     5
        26     19     6
        27     17     8
        28     21     9
        29     15     5
        30     25     6 ;

param ship_cost

:    1    2   3    4   5   6   7    8   9   10  11   12  13   14  15 :=
1    0   61  35   35  53  16  11   58  28  34   43  18   48  30   68
2   61    0  96   88  29  77  50   92  57  75   48  47   76  32   77
3   35   96   0   18  88  21  45   72  58  53   65  49   51  66   93
4   35   88  18    0  87  27  42   84  62  62   51  42   33  62  100
5   53   29  88   87   0  67  45   66  37  54   62  47   85  26   49
6   16   77  21   27  67   0  27   57  36  34   55  32   51  46   73
7   11   50  45   42  45  27   0   64  29  40   35   8   46  20   68
8   58   92  72   84  66  57  64    0  37  24   99  72  106  65   31
9   28   57  58   62  37  36  29   37   0  18   62  36   74  28   40
10  34   75  53   62  54  34  40   24  18   0   75  48   81  45   41
11  43   48  65   51  62  55  35   99  62  75    0  27   29  39   99
12  18   47  49   42  47  32   8   72  36  48   27   0   40  21   74
13  48   76  51   33  85  51  46  106  74  81   29  40    0  60  114
14  30   32  66   62  26  46  20   65  28  45   39  21   60   0   60
15  68   77  93  100  49  73  68   31  40  41   99  74  114  60    0
16  15   74  22   20  68  10  24   67  41  43   46  27   41  44   80
17  27   34  61   55  35  43  16   72  34  50   30  13   51  10   69
18  53   75  76   85  48  57  55   19  26  24   88  62  100  51   17
19  26   87  14   26  77  10  37   60  45  39   63  42   55  56   79
20  32   68  40   25  73  37  31   90  59  66   27  27   15  48   99
21  13   70  33   39  57  12  23   47  24  23   57  31   59  38   61
22  41   38  76   76  14  54  35   54  23  40   59  39   79  19   42
23  54    8  88   81  27  69  43   87  51  69   41  39   69  25   75
24  36   87  43   55  67  29  44   30  30  15   79  52   80  55   53
25  51   71  76   83  44  57  52   23  24  24   85  60   98  48   17
26  68   82  91  100  54  73  69   26  41  39  101  77  115  63    6
27  29   39  64   63  24  43  22   56  19  37   48  26   66  10   51
28  20   78  17   16  73  12  29   69  46  46   49  31   40  49   85
29  49  104  43   59  85  38  60   38  48  32   92  67   89  72   67
30  48  106  33   51  90  34  59   49  54  39   89  65   82  74   77

:   16  17  18   19  20   21  22  23  24  25  26   27  28  29   30 :=
1   15  27   53  26   32  13  41  54  36  51   68  29  20   49   48
2   74  34   75  87   68  70  38   8  87  71   82  39  78  104  106
3   22  61   76  14   40  33  76  88  43  76   91  64  17   43   33
4   20  55   85  26   25  39  76  81  55  83  100  63  16   59   51
5   68  35   48  77   73  57  14  27  67  44   54  24  73   85   90
6   10  43   57  10   37  12  54  69  29  57   73  43  12   38   34
7   24  16   55  37   31  23  35  43  44  52   69  22  29   60   59
8   67  72   19  60   90  47  54  87  30  23   26  56  69   38   49
9   41  34   26  45   59  24  23  51  30  24   41  19  46   48   54
10  43  50   24  39   66  23  40  69  15  24   39  37  46   32   39
11  46  30   88  63   27  57  59  41  79  85  101  48  49   92   89
12  27  13   62  42   27  31  39  39  52  60   77  26  31   67   65
13  41  51  100  55   15  59  79  69  80  98  115  66  40   89   82
14  44  10   51  56   48  38  19  25  55  48   63  10  49   72   74
15  80  69   17  79   99  61  42  75  53  17    6  51  85   67   77
16   0  39   65  17   26  20  56  66  39  63   80  44   5   48   43
17  39   0   59  53   39  38  29  27  58  56   72  18  44   74   74
18  65  59    0  63   85  45  37  70  37   4   15  42  69   51   60
19  17  53   63   0   42  21  64  80  30  63   78  53  15   34   27
20  26  39   85  42    0  44  65  61  65  83  100  52  27   75   69
21  20  38   45  21   44   0  44  63  22  44   61  34  24   36   36
22  56  29   37  64   65  44   0  34  53  33   46  13  61   71   76
23  66  27   70  80   61  63  34   0  80  67   79  32  71   97   99
24  39  58   37  30   65  22  53  80   0  38   51  48  41   18   25
25  63  56    4  63   83  44  33  67  38   0   17  38  68   53   62
26  80  72   15  78  100  61  46  79  51  17    0  54  84   63   73
27  44  18   42  53   52  34  13  32  48  38   54   0  49   66   69
28   5  44   69  15   27  24  61  71  41  68   84  49   0   48   42
29  48  74   51  34   75  36  71  97  18  53   63  66  48    0   12
30  43  74   60  27   69  36  76  99  25  62   73  69  42   12    0 ;

Same as trnloc2b.run, but model has 0-1 constraints disaggregated: LP relaxation bound is good, but subproblem has the integrality property and no further improvement can be made

# ----------------------------------------
# LAGRANGIAN RELAXATION FOR
# THE LOCATION-TRANSPORTATION PROBLEM
# ----------------------------------------

printf "\nLP RELAXATION\n\n";

model trnloc2c.mod;
data trnloc2.dat;

option omit_zero_rows 1;
option display_eps .000001;
option solution_round 8;

option solver cplex;
option solver_msg 0;

option relax_integrality 1;
objective Shipping_Cost;

solve;

param LB; param UB;
let LB := Shipping_Cost.val;
let UB := sum {j in CITY} max {i in CITY} ship_cost[i,j];

option relax_integrality 0;

problem LowerBound: Build, Ship, Supply, Build_Def, Limit, Lagrangian;
problem UpperBound: Ship, Supply, Build_Def, Demand, Limit, Shipping_Cost;

let {j in CITY} mult[j] := 0;

param slack {CITY};
param scale  default 1;
param norm;
param step;

param same  default 0;
param same_limit := 3;

param iter_limit := 20;
param LBlog {0..iter_limit};  let LBlog[0] := LB;
param UBlog {0..iter_limit};  let UBlog[0] := UB;
param scalelog {1..iter_limit};
param steplog {1..iter_limit};

for {k in 1..iter_limit} { printf "\nITERATION %d\n\n", k;
   solve LowerBound;

   let {j in CITY} slack[j] := sum {i in CITY} Ship[i,j] - demand[j];

   if Lagrangian > LB + 0.000001 then {
      let LB := Lagrangian;
      let same := 0; }
   else let same := same + 1;

   if same = same_limit then {
      let scale := scale / 2;
      let same := 0;
      };

   let norm := sum {j in CITY} slack[j]^2;
   let step := scale * (UB - Lagrangian) / norm;

   let {j in CITY} mult[j] := mult[j] less step * slack[j];

   if sum {i in CITY} supply[i] * Build[i] 
         >= sum {j in CITY} demand[j] - 1e-8 then { 
      solve UpperBound;
      let UB := min (UB, Shipping_Cost);
      }

   let LBlog[k] := LB;
   let UBlog[k] := UB;
   let scalelog[k] := scale;
   let steplog[k] := step;
   }

printf "\n\n";
display LBlog, UBlog, scalelog, steplog;
# ----------------------------------------
# LOCATION-TRANSPORTATION PROBLEM 
# USING LAGRANGIAN RELAXATION
# (with separate Supply, Build_Def constr)
# ----------------------------------------

set CITY;

param build_limit integer;

param demand {i in CITY} integer > 0;
param supply {CITY} integer > 0;

param ship_cost {i in CITY, j in CITY} >= 0;

param mult {CITY} >= 0;   # Lagrange multipliers for Demand constr

var Build {CITY} integer >= 0 <= 1;  # = 1 iff warehouse built at i
var Ship {i in CITY, j in CITY}      # amounts shipped
   >= 0, <= demand[j];

minimize Lagrangian:
   sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j] +
   sum {j in CITY} mult[j] * (demand[j] - sum {i in CITY} Ship[i,j]);

minimize Shipping_Cost:
   sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j];

subj to Supply {i in CITY}:
   sum {j in CITY} Ship[i,j] <= supply[i];

subj to Demand {j in CITY}:
   sum {i in CITY} Ship[i,j] >= demand[j];

subj to Build_Def {i in CITY, j in CITY}:
   Ship[i,j] <= demand[j] * Build[i];

subj to Limit:  sum {i in CITY} Build[i] <= build_limit;
param build_limit := 8;

param: CITY: supply demand :=
         1     19     4
         2     13     1
         3     20     2
         4     18     7
         5     23     9
         6     25     9
         7     22     3
         8     28     9
         9     18     5
        10     16     2
        11     17     4
        12     14     1
        13     22     9
        14     13     3
        15     18     1
        16     20     7
        17     15     5
        18     23     9
        19     25     2
        20     19     4
        21     18     9
        22     19     6
        23     22     5
        24     22     4
        25     21     5
        26     19     6
        27     17     8
        28     21     9
        29     15     5
        30     25     6 ;

param ship_cost

:    1    2   3    4   5   6   7    8   9   10  11   12  13   14  15 :=
1    0   61  35   35  53  16  11   58  28  34   43  18   48  30   68
2   61    0  96   88  29  77  50   92  57  75   48  47   76  32   77
3   35   96   0   18  88  21  45   72  58  53   65  49   51  66   93
4   35   88  18    0  87  27  42   84  62  62   51  42   33  62  100
5   53   29  88   87   0  67  45   66  37  54   62  47   85  26   49
6   16   77  21   27  67   0  27   57  36  34   55  32   51  46   73
7   11   50  45   42  45  27   0   64  29  40   35   8   46  20   68
8   58   92  72   84  66  57  64    0  37  24   99  72  106  65   31
9   28   57  58   62  37  36  29   37   0  18   62  36   74  28   40
10  34   75  53   62  54  34  40   24  18   0   75  48   81  45   41
11  43   48  65   51  62  55  35   99  62  75    0  27   29  39   99
12  18   47  49   42  47  32   8   72  36  48   27   0   40  21   74
13  48   76  51   33  85  51  46  106  74  81   29  40    0  60  114
14  30   32  66   62  26  46  20   65  28  45   39  21   60   0   60
15  68   77  93  100  49  73  68   31  40  41   99  74  114  60    0
16  15   74  22   20  68  10  24   67  41  43   46  27   41  44   80
17  27   34  61   55  35  43  16   72  34  50   30  13   51  10   69
18  53   75  76   85  48  57  55   19  26  24   88  62  100  51   17
19  26   87  14   26  77  10  37   60  45  39   63  42   55  56   79
20  32   68  40   25  73  37  31   90  59  66   27  27   15  48   99
21  13   70  33   39  57  12  23   47  24  23   57  31   59  38   61
22  41   38  76   76  14  54  35   54  23  40   59  39   79  19   42
23  54    8  88   81  27  69  43   87  51  69   41  39   69  25   75
24  36   87  43   55  67  29  44   30  30  15   79  52   80  55   53
25  51   71  76   83  44  57  52   23  24  24   85  60   98  48   17
26  68   82  91  100  54  73  69   26  41  39  101  77  115  63    6
27  29   39  64   63  24  43  22   56  19  37   48  26   66  10   51
28  20   78  17   16  73  12  29   69  46  46   49  31   40  49   85
29  49  104  43   59  85  38  60   38  48  32   92  67   89  72   67
30  48  106  33   51  90  34  59   49  54  39   89  65   82  74   77

:   16  17  18   19  20   21  22  23  24  25  26   27  28  29   30 :=
1   15  27   53  26   32  13  41  54  36  51   68  29  20   49   48
2   74  34   75  87   68  70  38   8  87  71   82  39  78  104  106
3   22  61   76  14   40  33  76  88  43  76   91  64  17   43   33
4   20  55   85  26   25  39  76  81  55  83  100  63  16   59   51
5   68  35   48  77   73  57  14  27  67  44   54  24  73   85   90
6   10  43   57  10   37  12  54  69  29  57   73  43  12   38   34
7   24  16   55  37   31  23  35  43  44  52   69  22  29   60   59
8   67  72   19  60   90  47  54  87  30  23   26  56  69   38   49
9   41  34   26  45   59  24  23  51  30  24   41  19  46   48   54
10  43  50   24  39   66  23  40  69  15  24   39  37  46   32   39
11  46  30   88  63   27  57  59  41  79  85  101  48  49   92   89
12  27  13   62  42   27  31  39  39  52  60   77  26  31   67   65
13  41  51  100  55   15  59  79  69  80  98  115  66  40   89   82
14  44  10   51  56   48  38  19  25  55  48   63  10  49   72   74
15  80  69   17  79   99  61  42  75  53  17    6  51  85   67   77
16   0  39   65  17   26  20  56  66  39  63   80  44   5   48   43
17  39   0   59  53   39  38  29  27  58  56   72  18  44   74   74
18  65  59    0  63   85  45  37  70  37   4   15  42  69   51   60
19  17  53   63   0   42  21  64  80  30  63   78  53  15   34   27
20  26  39   85  42    0  44  65  61  65  83  100  52  27   75   69
21  20  38   45  21   44   0  44  63  22  44   61  34  24   36   36
22  56  29   37  64   65  44   0  34  53  33   46  13  61   71   76
23  66  27   70  80   61  63  34   0  80  67   79  32  71   97   99
24  39  58   37  30   65  22  53  80   0  38   51  48  41   18   25
25  63  56    4  63   83  44  33  67  38   0   17  38  68   53   62
26  80  72   15  78  100  61  46  79  51  17    0  54  84   63   73
27  44  18   42  53   52  34  13  32  48  38   54   0  49   66   69
28   5  44   69  15   27  24  61  71  41  68   84  49   0   48   42
29  48  74   51  34   75  36  71  97  18  53   63  66  48    0   12
30  43  74   60  27   69  36  76  99  25  62   73  69  42   12    0 ;

Lagrangian relaxation for a location-transportation problem: LP relaxation bound is poor, and subproblem has the integrality property so no improvement can be made

# ----------------------------------------
# LAGRANGIAN RELAXATION FOR
# THE LOCATION-TRANSPORTATION PROBLEM
# ----------------------------------------

printf "\nLP RELAXATION\n\n";

model trnloc2a.mod;
data trnloc2.dat;

option omit_zero_rows 1;
option display_eps .000001;
option solution_round 8;

option solver cplex;
option solver_msg 0;

option relax_integrality 1;
objective Shipping_Cost;

solve;

param LB; param UB;
let LB := Shipping_Cost.val;
let UB := sum {j in CITY} max {i in CITY} ship_cost[i,j];

option relax_integrality 0;

problem LowerBound: Build, Ship, Supply, Limit, Lagrangian;
problem UpperBound: Ship, Supply, Demand, Limit, Shipping_Cost;

let {j in CITY} mult[j] := 0;

param slack {CITY};
param scale  default 1;
param norm;
param step;

param same  default 0;
param same_limit := 3;

param iter_limit := 20;
param LBlog {0..iter_limit};  let LBlog[0] := LB;
param UBlog {0..iter_limit};  let UBlog[0] := UB;
param scalelog {1..iter_limit};
param steplog {1..iter_limit};

for {k in 1..iter_limit} { printf "\nITERATION %d\n\n", k;
   solve LowerBound;

   let {j in CITY} slack[j] := sum {i in CITY} Ship[i,j] - demand[j];

   if Lagrangian > LB + 0.000001 then {
      let LB := Lagrangian;
      let same := 0; }
   else let same := same + 1;

   if same = same_limit then {
      let scale := scale / 2;
      let same := 0;
      };

   let norm := sum {j in CITY} slack[j]^2;
   let step := scale * (UB - Lagrangian) / norm;

   let {j in CITY} mult[j] := mult[j] less step * slack[j];

   if sum {i in CITY} supply[i] * Build[i] 
         >= sum {j in CITY} demand[j] - 1e-8 then { 
      solve UpperBound;
      let UB := min (UB, Shipping_Cost);
      }

   let LBlog[k] := LB;
   let UBlog[k] := UB;
   let scalelog[k] := scale;
   let steplog[k] := step;
   }

printf "\n\n";
display LBlog, UBlog, scalelog, steplog;
# ----------------------------------------
# LOCATION-TRANSPORTATION PROBLEM 
# USING LAGRANGIAN RELAXATION
# ----------------------------------------

set CITY;

param build_limit integer;

param demand {i in CITY} integer > 0;
param supply {CITY} integer > 0;

param ship_cost {i in CITY, j in CITY} >= 0;

param mult {CITY} >= 0;   # Lagrange multipliers for Demand constr

var Build {CITY} integer >= 0 <= 1;    # = 1 iff warehouse built at i
var Ship {i in CITY, j in CITY} >= 0;  # amounts shipped

minimize Lagrangian:
   sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j] +
   sum {j in CITY} mult[j] * (demand[j] - sum {i in CITY} Ship[i,j]);

minimize Shipping_Cost:
   sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j];

subj to Supply {i in CITY}:
   sum {j in CITY} Ship[i,j] <= supply[i] * Build[i];

subj to Demand {j in CITY}:
   sum {i in CITY} Ship[i,j] >= demand[j];

subj to Limit:  sum {i in CITY} Build[i] <= build_limit;
param build_limit := 8;

param: CITY: supply demand :=
         1     19     4
         2     13     1
         3     20     2
         4     18     7
         5     23     9
         6     25     9
         7     22     3
         8     28     9
         9     18     5
        10     16     2
        11     17     4
        12     14     1
        13     22     9
        14     13     3
        15     18     1
        16     20     7
        17     15     5
        18     23     9
        19     25     2
        20     19     4
        21     18     9
        22     19     6
        23     22     5
        24     22     4
        25     21     5
        26     19     6
        27     17     8
        28     21     9
        29     15     5
        30     25     6 ;

param ship_cost

:    1    2   3    4   5   6   7    8   9   10  11   12  13   14  15 :=
1    0   61  35   35  53  16  11   58  28  34   43  18   48  30   68
2   61    0  96   88  29  77  50   92  57  75   48  47   76  32   77
3   35   96   0   18  88  21  45   72  58  53   65  49   51  66   93
4   35   88  18    0  87  27  42   84  62  62   51  42   33  62  100
5   53   29  88   87   0  67  45   66  37  54   62  47   85  26   49
6   16   77  21   27  67   0  27   57  36  34   55  32   51  46   73
7   11   50  45   42  45  27   0   64  29  40   35   8   46  20   68
8   58   92  72   84  66  57  64    0  37  24   99  72  106  65   31
9   28   57  58   62  37  36  29   37   0  18   62  36   74  28   40
10  34   75  53   62  54  34  40   24  18   0   75  48   81  45   41
11  43   48  65   51  62  55  35   99  62  75    0  27   29  39   99
12  18   47  49   42  47  32   8   72  36  48   27   0   40  21   74
13  48   76  51   33  85  51  46  106  74  81   29  40    0  60  114
14  30   32  66   62  26  46  20   65  28  45   39  21   60   0   60
15  68   77  93  100  49  73  68   31  40  41   99  74  114  60    0
16  15   74  22   20  68  10  24   67  41  43   46  27   41  44   80
17  27   34  61   55  35  43  16   72  34  50   30  13   51  10   69
18  53   75  76   85  48  57  55   19  26  24   88  62  100  51   17
19  26   87  14   26  77  10  37   60  45  39   63  42   55  56   79
20  32   68  40   25  73  37  31   90  59  66   27  27   15  48   99
21  13   70  33   39  57  12  23   47  24  23   57  31   59  38   61
22  41   38  76   76  14  54  35   54  23  40   59  39   79  19   42
23  54    8  88   81  27  69  43   87  51  69   41  39   69  25   75
24  36   87  43   55  67  29  44   30  30  15   79  52   80  55   53
25  51   71  76   83  44  57  52   23  24  24   85  60   98  48   17
26  68   82  91  100  54  73  69   26  41  39  101  77  115  63    6
27  29   39  64   63  24  43  22   56  19  37   48  26   66  10   51
28  20   78  17   16  73  12  29   69  46  46   49  31   40  49   85
29  49  104  43   59  85  38  60   38  48  32   92  67   89  72   67
30  48  106  33   51  90  34  59   49  54  39   89  65   82  74   77

:   16  17  18   19  20   21  22  23  24  25  26   27  28  29   30 :=
1   15  27   53  26   32  13  41  54  36  51   68  29  20   49   48
2   74  34   75  87   68  70  38   8  87  71   82  39  78  104  106
3   22  61   76  14   40  33  76  88  43  76   91  64  17   43   33
4   20  55   85  26   25  39  76  81  55  83  100  63  16   59   51
5   68  35   48  77   73  57  14  27  67  44   54  24  73   85   90
6   10  43   57  10   37  12  54  69  29  57   73  43  12   38   34
7   24  16   55  37   31  23  35  43  44  52   69  22  29   60   59
8   67  72   19  60   90  47  54  87  30  23   26  56  69   38   49
9   41  34   26  45   59  24  23  51  30  24   41  19  46   48   54
10  43  50   24  39   66  23  40  69  15  24   39  37  46   32   39
11  46  30   88  63   27  57  59  41  79  85  101  48  49   92   89
12  27  13   62  42   27  31  39  39  52  60   77  26  31   67   65
13  41  51  100  55   15  59  79  69  80  98  115  66  40   89   82
14  44  10   51  56   48  38  19  25  55  48   63  10  49   72   74
15  80  69   17  79   99  61  42  75  53  17    6  51  85   67   77
16   0  39   65  17   26  20  56  66  39  63   80  44   5   48   43
17  39   0   59  53   39  38  29  27  58  56   72  18  44   74   74
18  65  59    0  63   85  45  37  70  37   4   15  42  69   51   60
19  17  53   63   0   42  21  64  80  30  63   78  53  15   34   27
20  26  39   85  42    0  44  65  61  65  83  100  52  27   75   69
21  20  38   45  21   44   0  44  63  22  44   61  34  24   36   36
22  56  29   37  64   65  44   0  34  53  33   46  13  61   71   76
23  66  27   70  80   61  63  34   0  80  67   79  32  71   97   99
24  39  58   37  30   65  22  53  80   0  38   51  48  41   18   25
25  63  56    4  63   83  44  33  67  38   0   17  38  68   53   62
26  80  72   15  78  100  61  46  79  51  17    0  54  84   63   73
27  44  18   42  53   52  34  13  32  48  38   54   0  49   66   69
28   5  44   69  15   27  24  61  71  41  68   84  49   0   48   42
29  48  74   51  34   75  36  71  97  18  53   63  66  48    0   12
30  43  74   60  27   69  36  76  99  25  62   73  69  42   12    0 ;