FICO
FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious exampleNext example

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.

xbcutstkjava.zip[download all files]

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





xbcutstk.java

/********************************************************
 * Xpress-BCL Java Example Problems
 * ================================
 *
 * file xbcutstk.java
 * ``````````````````
 * Cutting stock problem, solved by column (= cutting
 * pattern) generation heuristic looping over the
 * root node.
 *
 * (c) 2008-2024 Fair Isaac Corporation
 * author: S.Heipcke, 2001, rev. Mar. 2014
 ********************************************************/

import com.dashoptimization.*;

public class xbcutstk {
  static final int NWIDTHS = 5;
  static final int MAXWIDTH = 94;

  static final double EPS = 1e-6;
  static final int MAXCOL = 10;

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

  static final int[] DEMAND = {150, 96, 48, 108, 227}; /* Demand per width */
  static int[][] PATTERNS; /* (Basic) cutting patterns */

  static XPRBvar[] pat; /* Rolls per pattern */
  static XPRBctr[] dem; /* Demand constraints */
  static XPRBctr cobj; /* Objective function */

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

  static void modCutStock(XPRBprob p) throws XPRSexception {
    int i, j;
    XPRBexpr le;

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

    /****VARIABLES****/
    pat = new XPRBvar[NWIDTHS + MAXCOL];
    for (j = 0; j < NWIDTHS; j++)
      pat[j] =
          p.newVar(
              "pat_" + (j + 1), XPRB.UI, 0, (int) Math.ceil((double) DEMAND[j] / PATTERNS[j][j]));

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

    /****CONSTRAINTS****/
    dem = new XPRBctr[NWIDTHS];
    for (i = 0; i < NWIDTHS; i++) {
        /* Satisfy the demand per width */
      le = new XPRBexpr();
      for (j = 0; j < NWIDTHS; j++) le.add(pat[j].mul(PATTERNS[i][j]));
      dem[i] = p.newCtr("Demand", le.gEql(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                  */
  /**************************************************************************/
  static void solveCutStock(XPRB bcl, XPRBprob p) throws XPRSexception {
    double objval = 0; /* Objective value */
    int i, j;
    int starttime;
    int npatt, npass; /* Counters for columns and passes */
    double[] solpat; /* Solution values for variables pat */
    double[] dualdem; /* Dual values of demand constraints */
    XPRBbasis basis;
    double dw, z;
    int[] x;

    x = new int[NWIDTHS];
    solpat = new double[NWIDTHS + MAXCOL];
    dualdem = new double[NWIDTHS];

    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(bcl, NWIDTHS, dualdem, WIDTH, (double) MAXWIDTH, DEMAND, x);
      /* Force garbage collection to clean up subproblem: */
      /*   System.gc();
           System.runFinalization();
      */
      System.out.print(
          "(" + (XPRB.getTime() - starttime) / 1000.0 + " sec) Pass " + (npass + 1) + ": ");

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

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

        cobj.add(pat[npatt]); /* Add new var. to the objective */
        dw = 0;
        for (i = 0; i < NWIDTHS; i++) /* Add new var. to demand constraints*/
          if (x[i] > EPS) {
            dem[i].add(pat[npatt].mul(x[i]));
            if ((int) Math.ceil((double) DEMAND[i] / x[i]) > dw)
              dw = (int) Math.ceil((double) DEMAND[i] / x[i]);
          }
        pat[npatt].setUB(dw); /* Change the upper bound on the new var.*/
        npatt++;

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

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

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

  /**************************************************************************/
  /* 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                                  */
  /**************************************************************************/
  static double knapsack(XPRB bcl, int N, double[] c, double[] a, double R, int[] d, int[] xbest) {
    /*
      System.out.println("Solving z = max{cx : ax <= b; x in Z^n}");
      System.out.print("   c   =");
      for(j = 0; j < N; j++)  System.out.print(" " + c[j] + ",");
      System.out.println();
      System.out.print("   a   =");
      for(j = 0; j < N; j++)  System.out.print(" " + a[j] + ",");
      System.out.println();
      System.out.print("   c/a =");
      for (j = 0; j < N; j++)  System.out.print(" " + c[j]/a[j] + ",");
      System.out.println();
      System.out.print("   b   = " + R);
    */

    try (XPRBprob pk = bcl.newProb("Knapsack")) {
      XPRBvar[] x = new XPRBvar[N];
      for (int j = 0; j < N; j++) x[j] = pk.newVar("x", XPRB.UI, 0, d[j]);

      XPRBexpr klobj = new XPRBexpr();
      for (int j = 0; j < N; j++) klobj.add(x[j].mul(c[j]));
      pk.setObj(klobj);
      XPRBexpr knap = new XPRBexpr();
      for (int j = 0; j < N; j++) knap.add(x[j].mul(a[j]));
      pk.newCtr("KnapXPRBctr", knap.lEql(R));
      pk.setSense(XPRB.MAXIM);
      pk.mipOptimize("");

      double zbest = pk.getObjVal();
      for (int j = 0; j < N; j++) xbest[j] = (int) Math.floor(x[j].getSol() + 0.5);
      /*
        System.out.println("   z = " + zbest);
        System.out.print("   x =");
        for(j=0; j<N; j++) System.out.print(" " + xbest[j] + ",");
        System.out.println();
      */

      return (zbest);
    }
  }

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

  public static void main(String[] args) throws XPRSexception {
    try (XPRB bcl = new XPRB(); /* Initialize BCL */
        XPRBprob p = bcl.newProb("Els"); /* Create a new problem */
        XPRBexprContext context =
            new XPRBexprContext(); /* Release XPRBexpr instances at end of block. */
        XPRS xprs = new XPRS()) {
        /* Initialize Xpress-Optimizer */
      modCutStock(p); /* Model the problem */
      solveCutStock(bcl, p); /* Solve the problem */
    }
  }
}

Back to examples browserPrevious exampleNext example