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.

Source Files

xbcutstk.c

```/********************************************************
BCL Example Problems
====================

file xbcutstk.c
```````````````
Cutting stock problem, solved by column (= cutting
pattern) generation heuristic looping over the root
node.

(c) 2008 Fair Isaac Corporation
author: S.Heipcke, 2001, rev. Mar. 2014
********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "xprb.h"
#include "xprs.h"

#define NWIDTHS 5
#define MAXWIDTH 94

#define EPS 1e-6
#define MAXCOL 10

/****DATA****/
double WIDTH[] = {17, 21, 22.5,  24, 29.5};  /* Possible widths */
int DEMAND[] =  {150, 96,   48, 108,  227};  /* Demand per width */
int PATTERNS[NWIDTHS][NWIDTHS];              /* (Basic) cutting patterns */

XPRBvar use[NWIDTHS+MAXCOL];               /* Rolls per pattern */
XPRBctr dem[NWIDTHS];                      /* Demand constraints */
XPRBctr cobj;                              /* Objective function */

double knapsack(int N, double *c, double *a, double R, int *d, int *xbest);

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

void modcutstock(XPRBprob prob)
{
int i,j;

for(j=0;j<NWIDTHS;j++) PATTERNS[j][j]=(int)floor(MAXWIDTH/WIDTH[j]);

/****VARIABLES****/
for(j=0;j<NWIDTHS;j++)
use[j]=XPRBnewvar(prob,XPRB_UI, XPRBnewname("pat_%d",j+1),0,
(int)ceil((double)DEMAND[j]/PATTERNS[j][j]));

/****OBJECTIVE****/
cobj = XPRBnewctr(prob,"OBJ",XPRB_N);     /* Minimize total number of rolls */
for(j=0;j<NWIDTHS;j++)
XPRBsetobj(prob,cobj);

/****CONSTRAINTS****/
for(i=0;i<NWIDTHS;i++)
{
dem[i] = XPRBnewctr(prob,"Demand",XPRB_G); /* Satisfy the demand per width */
for(j=0;j<NWIDTHS;j++)
}
}

/**************************************************************************/
/*  Column generation loop at the top node:                               */
/*    solve the LP and save the basis                                     */
/*    get the solution values                                             */
/*    generate new column(s) (=cutting pattern)                           */
/**************************************************************************/
void solvecutstock(XPRBprob prob)
{
double objval;                  /* Objective value */
int i,j;
int starttime;
int npatt, npass;               /* Counters for columns and passes */
double soluse[NWIDTHS+MAXCOL];  /* Solution values for variables pat */
double dualdem[NWIDTHS];        /* Dual values of demand constraints */
XPRBbasis basis;
double dw,z;
int x[NWIDTHS];

starttime=XPRBgettime();
npatt = NWIDTHS;

for(npass=0;npass<MAXCOL;npass++)
{
XPRBlpoptimize(prob,"");     /* Solve the LP */
basis=XPRBsavebasis(prob);     /* Save the current basis */
objval = XPRBgetobjval(prob);  /* Get the objective value */

/* Get the solution values: */
for(j=0;j<npatt;j++)  soluse[j]=XPRBgetsol(use[j]);
for(i=0;i<NWIDTHS;i++)  dualdem[i]=XPRBgetdual(dem[i]);

/*
for(j=0;j<npatt;j++) printf("%g, ",soluse[j]); printf("\n");
*/

/* 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);
printf("(%g sec) Pass %d: ",(XPRBgettime()-starttime)/1000.0, npass+1);

if(z < 1+EPS)
{
printf("no profitable column found.\n\n");
XPRBdelbasis(basis);              /* No need to keep the basis any longer */
break;
}
else
{
/* Print the new pattern: */
printf("new pattern found with marginal cost %g\n   ", z-1);
printf("Widths distribution: ");
dw=0;
for(i=0;i<NWIDTHS;i++)
{
printf ("%g:%d  ", WIDTH[i], x[i]);
dw += WIDTH[i]*x[i];
}
printf("Total width: %g\n", dw);

/* Create a new variable for this pattern: */
use[npatt]=XPRBnewvar(prob,XPRB_UI,
XPRBnewname("pat_%d",npatt+1),0,XPRB_INFINITY);

dw=0;
for(i=0; i<NWIDTHS; i++)          /* Add new var. to demand constraints*/
if(x[i] > EPS)
{
if((int)ceil((double)DEMAND[i]/x[i]) > dw)
dw = (int)ceil((double)DEMAND[i]/x[i]);
}
XPRBsetub(use[npatt],dw);         /* Change the upper bound on the new var.*/

npatt++;

XPRBdelbasis(basis);              /* No need to keep the basis any longer */
}
}

XPRBmipoptimize(prob,"c");          /* Solve the MIP */

printf("(%g sec) Optimal solution: %g rolls, %d patterns\n   ",
(XPRBgettime()-starttime)/1000.0, XPRBgetobjval(prob), npatt);
printf("Rolls per pattern: ");
for(i=0;i<npatt;i++) printf("%g, ", XPRBgetsol(use[i]));
printf("\n");
}

/**************************************************************************/
/* Integer Knapsack Algorithm for solving the integer knapsack problem    */
/*    z = max{cx : ax <= R, x <= d, x in Z^N}                             */
/*                                                                        */
/* 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                                  */
/**************************************************************************/
double knapsack(int N, double *c, double *a, double R, int *d, int *xbest)
{
int j;
double zbest = 0.0;
XPRBarrvar x;
XPRBctr cobj;
XPRBprob kprob;
/*
printf ("Solving z = max{cx : ax <= b; x in Z^n}\n   c   =");
for (j = 0; j < N; j++) printf (" %g,", c[j]);
printf("\n   a   =");
for (j = 0; j < N; j++) printf (" %g,", a[j]);
printf("\n   c/a =");
for (j = 0; j < N; j++) printf (" %g,", c[j]/a[j]);
printf("\n   b   = %f\n", R);
*/

kprob=XPRBnewprob("Knapsack");
x=XPRBnewarrvar(kprob,N, XPRB_UI, "x", 0, XPRB_INFINITY);
for(j=0;j<N;j++) XPRBsetub(x[j], d[j]);

cobj = XPRBnewarrsum(kprob,"OBJ", x, c, XPRB_N, 0);
XPRBsetobj(kprob,cobj);
XPRBnewarrsum(kprob,"KnapCtr", x, a, XPRB_L, R);
XPRBsetsense(kprob,XPRB_MAXIM);
XPRBmipoptimize(kprob,"");

zbest = XPRBgetobjval(kprob);
for(j=0;j<N;j++) xbest[j]=(int)floor(XPRBgetsol(x[j])+0.5);
/*
printf ("   z = %f\n   x =", zbest);
for(j=0; j<N; j++) printf (" %d,", xbest[j]);
printf("\n");
*/

XPRBdelprob(kprob);

return (zbest);
}

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

int main(int argc, char **argv)
{
XPRBprob prob;

prob=XPRBnewprob("CutStock");         /* Initialize a new problem in BCL */
modcutstock(prob);                    /* Model the problem */
solvecutstock(prob);                  /* Solve the problem */

XPRBdelprob(prob);                    /* Delete the main problem */
XPRBfree();                           /* Terminate BCL */

return 0;
}

```