> Discrete Optimization > Cutting Stock: Column Generation > Complete Program

You can see the entire program here or online in the standard distribution of ILOG CPLEX in the file /examples/src/cutstock.cpp. To run the example, you need only a license for ILOG CPLEX. It is also possible to write your own pattern generation models to use with algorithms of ILOG Solver and ILOG Hybrid Optimizers. In that case, you need a license for those products.

// -------------------------------------------------------------- -*- C++ -*-
// File: examples/src/cutstock.cpp
// Version 9.0    
// --------------------------------------------------------------------------
//  Copyright (C) 1999-2003 by ILOG.
//  All Rights Reserved.
//  Permission is expressly granted to use this example in the
//  course of developing applications that use ILOG products.
// --------------------------------------------------------------------------
//

#include <ilcplex/ilocplex.h>

ILOSTLBEGIN

#define RC_EPS 1.0e-6

typedef IloArray<IloNumArray> IloNumArray2;


static void readData (const char* filename, IloNum& rollWidth,
                      IloNumArray& size, IloNumArray& amount);
static void report1 (IloCplex& cutSolver, IloNumVarArray Cut, IloRangeArray Fill);
static void report2 (IloAlgorithm& patSolver, 
                     IloNumVarArray Use, 
                     IloObjective obj);
static void report3 (IloCplex& cutSolver, IloNumVarArray Cut);



/// MAIN PROGRAM ///

int
main(int argc, char **argv)
{
   IloEnv env;
   try {
      IloInt  i, j;

      IloNum      rollWidth;
      IloNumArray amount(env);
      IloNumArray size(env);

      if ( argc > 1 )
         readData(argv[1], rollWidth, size, amount);
      else
         readData("../../../examples/data/cutstock.dat", rollWidth, size, amount);

      /// CUTTING-OPTIMIZATION PROBLEM ///

      IloModel cutOpt (env);

      IloObjective   RollsUsed = IloAdd(cutOpt, IloMinimize(env));
      IloRangeArray  Fill = IloAdd(cutOpt, IloRangeArray(env, amount, IloInfinity));
      IloNumVarArray Cut(env);

      IloInt nWdth = size.getSize();
      for (j = 0; j < nWdth; j++) {
         Cut.add(IloNumVar(RollsUsed(1) + Fill[j](int(rollWidth / size[j]))));
      }
      
      IloCplex cutSolver(cutOpt);

      /// PATTERN-GENERATION PROBLEM ///

      IloModel patGen (env);

      IloObjective ReducedCost = IloAdd(patGen, IloMinimize(env, 1));
      IloNumVarArray Use(env, nWdth, 0, IloInfinity, ILOINT);
      patGen.add(IloScalProd(size, Use) <= rollWidth);

      IloCplex patSolver(patGen);

      /// COLUMN-GENERATION PROCEDURE ///

      IloNumArray price(env, nWdth);
      IloNumArray newPatt(env, nWdth);

      /// COLUMN-GENERATION PROCEDURE ///

      for (;;) {
         /// OPTIMIZE OVER CURRENT PATTERNS ///
       
         cutSolver.solve();
         report1 (cutSolver, Cut, Fill);
       
         /// FIND AND ADD A NEW PATTERN ///
       
         for (i = 0; i < nWdth; i++) {
           price[i] = -cutSolver.getDual(Fill[i]);
         }
         ReducedCost.setCoef(Use, price);
       
         patSolver.solve();
         report2 (patSolver, Use, ReducedCost);
       
         if (patSolver.getValue(ReducedCost) > -RC_EPS) break;
       
         patSolver.getValues(newPatt, Use);
         Cut.add( IloNumVar(RollsUsed(1) + Fill(newPatt)) );
      }

      cutOpt.add(IloConversion(env, Cut, ILOINT));

      cutSolver.solve();
      report3 (cutSolver, Cut);
   }
   catch (IloException& ex) {
      cerr << "Error: " << ex << endl;
   }
   catch (...) {
      cerr << "Error" << endl;
   }

   env.end();

   return 0;
}


static void readData (const char* filename, IloNum& rollWidth,
                      IloNumArray& size, IloNumArray& amount)
{
   ifstream in(filename);
   if (in) {
      in >> rollWidth;
      in >> size;
      in >> amount;
   }
   else {
      cerr << "No such file: " << filename << endl;
      throw(1);
   }
}

static void report1 (IloCplex& cutSolver, IloNumVarArray Cut, IloRangeArray Fill)
{
   cout << endl;
   cout << "Using " << cutSolver.getObjValue() << " rolls" << endl;
   cout << endl;
   for (IloInt j = 0; j < Cut.getSize(); j++) {
      cout << "  Cut" << j << " = " << cutSolver.getValue(Cut[j]) << endl;
   }
   cout << endl;
   for (IloInt i = 0; i < Fill.getSize(); i++) {
      cout << "  Fill" << i << " = " << cutSolver.getDual(Fill[i]) << endl;
   }
   cout << endl;
}

static void report2 (IloAlgorithm& patSolver, IloNumVarArray Use, IloObjective obj)
{
   cout << endl;
   cout << "Reduced cost is " << patSolver.getValue(obj) << endl;
   cout << endl;
   if (patSolver.getValue(obj) <= -RC_EPS) {
      for (IloInt i = 0; i < Use.getSize(); i++)  {
         cout << "  Use" << i << " = " << patSolver.getValue(Use[i]) << endl;
      }
      cout << endl;
   }
}

static void report3 (IloCplex& cutSolver, IloNumVarArray Cut)
{
   cout << endl;
   cout << "Best integer solution uses " 
        << cutSolver.getObjValue() << " rolls" << endl;
   cout << endl;
   for (IloInt j = 0; j < Cut.getSize(); j++) {
      cout << "  Cut" << j << " = " << cutSolver.getValue(Cut[j]) << endl;
   }
}