> Advanced Programming Techniques > Using Callbacks > Example: Deriving the Simplex Callback ilolpex4.cpp

Example ilolpex4.cpp demonstrates the use of the simplex callback to print logging information at each iteration. It is a modification of example ilolpex1.cpp, so this discussion concentrates on the differences. The following code:

ILOSIMPLEXCALLBACK0(MyCallback) {
  cout << "Iteration " << getNiterations() << ": ";
  if ( isFeasible() ) {
    cout << "Objective = " << getObjValue() << endl;
  } 
  else {
    cout << "Infeasibility measure = " << getInfeasibility() << endl;
  } 
}

defines the callback MyCallback without parameters with the code enclosed in the outer {}. In Java the same callback is defined as:

static class MyCallback extends IloCplex.ContinuousCallback {
   public void main() throws IloException {
      System.out.print("Iteration " + getNiterations() + ": ");
      if ( isFeasible() )
         System.out.println("Objective = " + getObjValue());
      else
         System.out.println("Infeasibility measure = " 
                             + getInfeasibility());
   }
}

The callback prints the iteration number. Then, depending on whether the current solution is feasible or not, it prints the objective value or infeasibility measure. The functions getNiterations, isFeasible, getObjValue, and getInfeasibility are methods provided in the callback's base class IloCplex::ContinuousCallbackI (IloCplex.ContinuousCallback). See the ILOG CPLEX Reference Manual for the complete list of methods provided for each callback class.

Here is how the macro ILOSIMPLEXCALLBACK0 is expanded:

class MyCallbackI : public IloCplex::SimplexCallbackI {
  void main();
  IloCplex::CallbackI* duplicateCallback() const {
    return (new (getEnv()) MyCallbackI(*this));
  }
};
IloCplex::Callback MyCallback(IloEnv env) {
  return (IloCplex::Callback(new (env) MyCallbackI()));
}
void MyCallbackI::main() {
  cout << "Iteration " << getNiterations() << ": ";
  if ( isFeasible() ) {
    cout << "Objective = " << getObjValue() << endl;
  } 
  else {
     cout << "Infeasibility measure = " << getInfeasibility() << endl;
  }
}

The 0 (zero) in the macro indicates that no parameters are passed to the constructor of the callback. For callbacks requiring up to 7 parameters, similar macros are defined where the 0 is replaced by the number of parameters, ranging from 1 through 7. For an example using the cut callback, see Example: Controlling Cuts iloadmipex5.cpp. If you need more than 7 parameters, you will need to derive your callback class yourself without the help of a macro.

After the callback MyCallback is defined, it can be used with the line:

cplex.use(MyCallback(env));

in C++ or

cplex.use(new MyCallback());

in Java.

In the case of C++, function MyCallback creates an instance of the implementation class MyCallbackI. A handle to this implementation object is passed to cplex method use.

If your application defines more than one simplex callback object (possibly with different subclasses), only the last one passed to ILOG CPLEX with the use method is actually used during simplex. On the other hand, IloCplex can handle one callback for each callback class at the same time. For example, a simplex callback and a MIP callback can be used at the same time.

The complete program, ilolpex4.cpp, appears here and online in the standard distribution.

// -------------------------------------------------------------- -*- C++ -*-
// File: examples/src/ilolpex4.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.
// --------------------------------------------------------------------------
//
// ilolpex4.cpp - Illustrating the CPLEX callback functionality.
// 
// This is a modification of ilolpex1.c, where we use a callback
// function to print the iteration info, rather than have CPLEX
// do it.

#include <ilcplex/ilocplex.h>
ILOSTLBEGIN

ILOSIMPLEXCALLBACK0(MyCallback) {
  cout << "Iteration " << getNiterations() << ": ";
  if ( isFeasible() ) {
     cout << "Objective = " << getObjValue() << endl;
  } else {
     cout << "Infeasibility measure = " << getInfeasibility() << endl;
  }
}


static void
   populatebycolumn (IloModel model, IloNumVarArray var, IloRangeArray rng);

int
main (int argc, char **argv)
{
   IloEnv env;
   try {
      IloModel model(env, "example");

      IloNumVarArray var(env);
      IloRangeArray  rng(env);
      populatebycolumn (model, var, rng);

      IloCplex cplex(model);
      cplex.setOut(env.getNullStream());
      cplex.setParam(IloCplex::RootAlg, IloCplex::Primal);
      cplex.use(MyCallback(env));
      cplex.solve();

      env.out() << "Solution status = " << cplex.getStatus() << endl;
      env.out() << "Solution value  = " << cplex.getObjValue() << endl;

      IloNumArray vals(env);
      cplex.getValues(vals, var);
      env.out() << "Values        = " << vals << endl;
      cplex.getSlacks(vals, rng);
      env.out() << "Slacks        = " << vals << endl;
      cplex.getDuals(vals, rng);
      env.out() << "Duals         = " << vals << endl;
      cplex.getReducedCosts(vals, var);
      env.out() << "Reduced Costs = " << vals << endl;

      cplex.exportModel("lpex4.lp");
   }
   catch (IloException& e) {
      cerr << "Concert exception caught: " << e << endl;
   }
   catch (...) {
      cerr << "Unknown exception caught" << endl;
   }

   env.end();
   return 0;
}  // END main


// To populate by column, we first create the rows, and then add the
// columns.

static void
populatebycolumn (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   IloObjective obj = IloMaximize(env);
   c.add(IloRange(env, -IloInfinity, 20.0));
   c.add(IloRange(env, -IloInfinity, 30.0));

   x.add(IloNumVar(obj(1.0) + c[0](-1.0) + c[1]( 1.0), 0.0, 40.0));
   x.add(obj(2.0) + c[0]( 1.0) + c[1](-3.0));
   x.add(obj(3.0) + c[0]( 1.0) + c[1]( 1.0));

   model.add(obj);
   model.add(c);
}  // END populatebycolumn