FICO Xpress Optimization Examples Repository
 FICO Optimization Community FICO Xpress Optimization Home

Cutstk - Column generation for a cutting stock problem

Description
This example features iteratively adding new variables, basis in/output and working with subproblems. The column generation algorithm is implemented as a loop over the root node of the MIP problem.

 xbcutstkcs.zip [download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
 xbcutstk.cs [download] xbcutstk.csproj [download]

xbcutstk.cs

/********************************************************/
/*  Xpress-BCL C# Example Problems                      */
/*  ==============================                      */
/*                                                      */
/*  file xbcutstk.cs                                    */
/*                                      */
/*  Example for the use of Xpress-BCL                   */
/*  (Cutting stock problem, solved by column (= cutting */
/*   pattern) generation heuristic looping over the     */
/*   root node)                                         */
/*                                                      */
/*  (c) 2008 Fair Isaac Corporation                     */
/*      authors: S.Heipcke, D.Brett, rev. Mar. 2014     */
/********************************************************/

using System;
using System.Text;
using System.IO;
using Optimizer;
using BCL;

namespace Examples
{
public class TestAdvCutstk
{
const int NWIDTHS = 5;
const int MAXWIDTH = 94;

const double EPS = 1e-6;
const int MAXCOL = 10;

/****DATA****/
/* Possible widths */
double[] WIDTH = {17, 21, 22.5,  24, 29.5};

/* Demand per width */
int[] DEMAND =  {150, 96,   48, 108,  227};

/* (Basic) cutting patterns */
int[,] PATTERNS = new int[NWIDTHS,NWIDTHS];

/* Rolls per pattern */
XPRBvar[] pat = new XPRBvar[NWIDTHS+MAXCOL];

/* Demand constraints */
XPRBctr[] dem = new XPRBctr[NWIDTHS];

/* Objective function */
XPRBctr cobj;

/* Initialize a new problem in BCL */
XPRBprob p = new XPRBprob("CutStock");

/*********************************************************************/

public void modCutStock()
{
int i,j;
XPRBexpr le;

for(j=0;j<NWIDTHS;j++)
PATTERNS[j,j]=(int)Math.Floor(MAXWIDTH/WIDTH[j]);

/****VARIABLES****/
for(j=0;j<NWIDTHS;j++)
pat[j]=p.newVar("pat_" + (j+1), BCLconstant.XPRB_UI, 0,
(int)Math.Ceiling((double)DEMAND[j]/PATTERNS[j,j]));

/****OBJECTIVE****/
le = new XPRBexpr();
for(j=0;j<NWIDTHS;j++)
le += pat[j];    /* Minimize total number of rolls */
cobj = p.newCtr("OBJ", le);
p.setObj(cobj);

/****CONSTRAINTS****/
/* Satisfy the demand per width */
for(i=0;i<NWIDTHS;i++)
{
le = new XPRBexpr(0);
for(j=0;j<NWIDTHS;j++)
le += PATTERNS[i,j] * pat[j];
dem[i] = p.newCtr("Demand", le >= DEMAND[i]);
}
}

/*********************************************************************/
/*  Column generation loop at the top node:                          */
/*    solve the LP and save the basis                                */
/*    get the solution values                                        */
/*    generate new column(s) (=cutting pattern)                      */
/*    load the modified problem and load the saved basis             */
/*********************************************************************/
public void solveCutStock()
{
double objval;                /* Objective value */
int i,j;
int starttime;
int npatt, npass;             /* Counters for columns and passes */

/* Solution values for variables pat */
double[] solpat = new double[NWIDTHS+MAXCOL];

/* Dual values of demand constraints */
double[] dualdem = new double[NWIDTHS];

XPRBbasis basis;
double dw,z;
int[] x = new int[NWIDTHS];
XPRSprob xprs = p.getXPRSprob();

starttime=XPRB.getTime();
npatt = NWIDTHS;

for(npass=0;npass<MAXCOL;npass++)
{
p.lpOptimize();                  /* Solve the LP */
basis = p.saveBasis();         /* Save the current basis */
objval = p.getObjVal();        /* Get the objective value */

/* Get the solution values: */
for(j=0;j<npatt;j++)  solpat[j]=pat[j].getSol();
for(i=0;i<NWIDTHS;i++)  dualdem[i]=dem[i].getDual();

/* Solve integer knapsack problem  z = min{cx : ax<=r, x in Z^n}
with r=MAXWIDTH, n=NWIDTHS */
z = knapsack(NWIDTHS, dualdem, WIDTH, (double)MAXWIDTH, DEMAND, x);
System.Console.Write("(" + (XPRB.getTime()-starttime)/1000.0 +
" sec) Pass " + (npass+1) + ": ");

if(z < 1+EPS)
{
System.Console.WriteLine("no profitable column found.");
System.Console.WriteLine();
basis.reset();       /* No need to keep basis any longer */
break;
}
else
{
/* Print the new pattern: */
System.Console.WriteLine("new pattern found with marginal"+
" cost " + (z-1));
System.Console.Write("   Widths distribution: ");
dw=0;
for(i=0;i<NWIDTHS;i++)
{
System.Console.Write(WIDTH[i] + ":" + x[i] + "  ");
dw += WIDTH[i]*x[i];
}
System.Console.WriteLine("Total width: " + dw);

/* Create a new variable for this pattern: */
pat[npatt]=p.newVar("pat_"+(npatt+1), BCLconstant.XPRB_UI);

/* Add new var. to the objective */
cobj += pat[npatt];

/* Add new var. to demand constraints*/
dw=0;
for(i=0; i<NWIDTHS; i++)
if(x[i] > EPS)
{
dem[i] += (x[i]*pat[npatt]);
if((int)Math.Ceiling((double)DEMAND[i]/x[i]) > dw)
dw = (int)Math.Ceiling((double)DEMAND[i]/x[i]);
}

/* Change the upper bound on the new var.*/
pat[npatt].setUB(dw);

npatt++;

p.loadMat();         /* Reload the problem */
p.loadBasis(basis);  /* Load the saved basis */
basis.reset();       /* No need to keep basis any longer */
}
}

p.mipOptimize();                     /* Solve the MIP */

System.Console.WriteLine("(" + (XPRB.getTime()-starttime)/1000.0 +
" sec) Optimal solution: " + p.getObjVal() + " rolls, " +
npatt + " patterns");
System.Console.Write("   Rolls per pattern: ");
for(i=0;i<npatt;i++) System.Console.Write(pat[i].getSol() + ", ");
System.Console.WriteLine();
}

/*********************************************************************/
/* Integer Knapsack Algorithm for solving the integer knapsack       */
/* problem:                                                          */
/*    z = max{cx : ax <= R, x <= d, x in Z^N}                        */
/* where there is an unlimited number of each type of item available.*/
/*                                                                   */
/* Input data:                                                       */
/*   N:        Number of item types                                  */
/*   c[i]:     Unit profit of item type i, i=1..n                    */
/*   a[i]:     Unit resource use of item type i, i=1..n              */
/*   R:        Total resource available                              */
/*   d[i]:     Demand for item type i, i=1..n                        */
/* Return values:                                                    */
/*   xbest[i]: Number of items of type i used in optimal solution    */
/*   zbest:    Value of optimal solution                             */
/*********************************************************************/
public double knapsack(int N, double[] c, double[] a, double R,
int[] d, int[] xbest)
{
int j;
double zbest = 0.0;
XPRBvar[] x;
XPRBexpr klobj, knap;
XPRBprob pk = new XPRBprob("Knapsack");

x = new XPRBvar[N];
if(x==null)
System.Console.WriteLine("Allocating memory for variables "
+"failed.");
for(j=0;j<N;j++) x[j]=pk.newVar("x", BCLconstant.XPRB_UI, 0, d[j]);

klobj = new XPRBexpr();
knap = new XPRBexpr();
for(j=0;j<N;j++) klobj += c[j]*x[j];
pk.setObj(pk.newCtr("OBJ",klobj));
for(j=0;j<N;j++) knap += a[j]*x[j];
pk.newCtr("KnapXPRBctr", knap <= R);
pk.setSense(BCLconstant.XPRB_MAXIM);
pk.mipOptimize();

zbest = pk.getObjVal();
for(j=0;j<N;j++) xbest[j]=(int)Math.Floor(x[j].getSol() + 0.5);

return (zbest);
}

/*********************************************************************/

public static void Main()
{
XPRB.init();
TestAdvCutstk TestInstance = new TestAdvCutstk();

TestInstance.modCutStock();                 /* Model the problem */
TestInstance.solveCutStock();               /* Solve the problem */

return;
}
}
}