Diagnostic Callbacks

Diagnostic callbacks allow you to monitor an ongoing optimization, and optionally abort it. These callbacks are distinguished by the place where they are called during an optimization. There are 10 such places where diagnostic callbacks are called:

Implementing Callbacks In CPLEX with Concert Technology

With IloCplex, callbacks are accessed via a the IloCplex::Callback handle class. It points to an implementation object of a subclass of IloCplex::CallbackI. One such implementation class is provided for each type of callback. The implementation class provides the functions that can be used for the particular callback as protected member functions. To reflect the fact that some callbacks share part of their protected API, the callback classes are organized in a class hierarchy as shown by this diagram:

           IloCplex::CallbackI
              |
              +--- IloCplex::PresolveCallbackI
              |
              +--- IloCplex::CrossoverCallbackI
              |
              +--- IloCplex::NetworkCallbackI
              |
              +--- IloCplex::LPCallbackI
              |       |
              |       +--- IloCplex::BarrierCallbackI
              |       |
              |       +--- IloCplex::PrimalSimplexCallbackI
              |       |
              |       +--- IloCplex::DualSimplexCallbackI
              |
              +--- IloCplex::MIPCallbackI
                      |
                      +--- IloCplex::ProbingCallback
                      | 
                      +--- IloCplex::FractionalCutCallbackI
                      |
                      +--- IloCplex::DisjunctiveCutCallbackI

This means that, for example, all functions available for the MIP callback are also available for the probing, fractional cut, and disjunctive cut callbacks. In particular, the function to abort the current optimization is provided by the class IloCplex::CallbackI and is thus available to all callbacks.

There are two ways of implementing callbacks for IloCplex: a more complex way that exposes all the C++ implementation details, and a simplified way that uses macros to handle the C++ technicalities. We will first expose the more complex way and discuss the underlying design. To quickly implement your callback without details on the internal design, proceed directly to Writing Callbacks with Macros.

Writing Callback Classes by Hand

To implement your own callback for IloCplex, first select the callback class corresponding to the callback you want implemented. From it derive your own implementation class and overwrite the virtual method main(). This is where you implement the callback actions, using the protected member functions of the callback class from which you derived your callback or one of its base classes.

Next write a function that creates a new object of your implementation class using the environment operator new and returning it as an IloCplex::Callback handle object. Here is an example implementation of such a function:

IloCplex::Callback MyCallback(IloEnv env, IloInt num) {
return (new (env) MyCallbackI(num));
}

Once the implementation is completed, use it with IloCplex by calling cplex.use() with the handle object returned by your callback function. To remove a callback that is being used by a cplex object, call callback.end() on the IloCplex::Callback handle callback.

One object of a callback implementation class can be used with only one IloCplex object at a time. Thus, when you use a callback with more than one cplex object, a copy of the implementation object is created every time cplex.use() is called except for the first time. Method IloCplex::use() returns a handle to the callback object that has actually been installed to enable calling end() on it.

To construct the copies of the callback objects, class IloCplex::CallbackI defines another pure virtual method:

virtual IloCplex::CallbackI* IloCplex::CallbackI::makeClone() const = 0;

which must be implemented for your callback class. This method will be called to create the copies needed for using a callback on different cplex objects or on one cplex object with a parallel optimizer.

In most cases you can avoid writing callback classes by hand, using supplied macros that make the process as easy as implementing a function. You must implement a callback by hand only if the callback manages internal data not passed as arguments, or if the callback requires eight or more parameters.

Writing Callbacks with Macros

Here is how to implement a callback using macros. First, determine which callback you want to implement and how many arguments to pass to the callback function. These two pieces of information determine the macro you need to use.

For example, to implement a dual simplex callback with one parameter, the macro is ILODUALSIMPLEXCALLBACK1. Generally, for every callback type XXX and any number of parameters n from 0 to 7 there is a macro called ILOXXXCALLBACKn. The following table lists the callbacks and the corresponding macros and classes (where n is a placeholder for 0..7):

Table 8.4 Callback Macros

Callback 
Macro 
Class 
presolve 
ILOPRESOLVECALLBACKn 
IloCplex::PresolveCallbackI 
LP 
ILOLPCALLBACKn 
IloCplex::LPCallbackI 
primal simplex 
ILOPRIMALSIMPLEXCALLBACKn 
IloCplex::PrimalSimpleXCallbackI 
dual simplex 
ILODUALSIMPLEXCALLBACKn 
IloCplex::DualSimpleXCallbackI 
barrier 
ILOBARRIERCALLBACKn 
IloCplex::BarrierCallbackI 
crossover 
ILOCROSSOVERCALLBACKn 
IloCplex::CrossoverCallbackI 
network 
ILONETWORKCALLBACKn 
IloCplex::NetworkCallbackI 
MIP 
ILOMIPCALLBACKn 
IloCplex::MIPCallbackI 
probing 
ILOPROBINGCALLBACKn 
IloCplex::ProbingCallbackI 
fractional cut  
ILOFRACTIONALCUTCALLBACKn 
IloCplex::FractionalCutCallbackI 
disjunctive cut 
ILODISJUNCTIVECUTCALLBACKn 
IloCplex::DisjunctiveCutCallbackI 

The protected member functions of the corresponding class and its base classes determine the functions that can be called for implementing your callback (see the ILOG CPLEX Reference Manual).

Here is an example of how to implement a dual simplex callback with the name MyCallback that takes one parameter:

ILODUALSIMPLEXCALLBACK1(MyCallback, IloInt, num) {
  if ( getNiterations() == num ) abort();
}

This callback aborts the dual simplex algorithm at the numth iteration. It queries the current iteration number by calling function getNiterations(), which is a protected member function of class IloCplex::LPCallbackI.

To use this callback with an IloCplex object cplex, simply call:

IloCplex::Callback mycallback = cplex.use(MyCallback(env, 10));

The callback that is added to cplex is returned by the method use and stored in variable mycallback. This allows you to call mycallback.end()to remove the callback from cplex. If you do not intend accessing your callback, for example in order to delete it before ending the environment, you may safely leave out the declaration and initialization of variable mycallback.

Callback Interface

Two callback classes in the hierarchy need extra attention. The first is the base class IloCplex::CallbackI. Since there is no corresponding callback in CPLEX, this class cannot be used for implementing user callbacks. Instead, its purpose is to provide an interface common to all callback functions. This consists of the methods getModel(), which returns the model that is extracted to the CPLEX object that is calling the callback, getEnv(), which returns the corresponding environment, and abort(), which aborts the current optimization. Further, methods getNrows() and getNcols() allow you to query the number of rows and columns of the current cplex LP matrix. These methods can be called from all callbacks.

The LP Callback

The second special callback class is IloCplex::LPCallbackI. If you create an LP callback and use it with an IloCplex object, this callback will be used for all of the barrier, dual simplex, and primal simplex callbacks. In other words, implementing and using one LP callback is equivalent to writing and using these three callbacks independently.

Example: Deriving the Primal Simplex Callback

This example demonstrates the use of the primal simplex callback to print logging information at each iteration. It is a modification of example ilolpex1.cpp, so we will restrict our discussion to the differences. The following code:

ILOPRIMALSIMPLEXCALLBACKI0(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 {}.

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

Here is how the macro ILOPRIMALSIMPLEXCALLBACK0 is expanded:

class MyCallbackI : public IloCplex::PrimalSimplexCallbackI {

void main();

IloCplex::CallbackI* makeClone() 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 in the macro indicates that 0 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 of this using the cut callback, see Example: Controlling Cuts. 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));

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 primal simplex callback object (possibly with different subclasses), only the last one passed to CPLEX with the use method is actually used during primal simplex. On the other hand, IloCplex can handle one callback for each callback class at the same time. For example a primal simplex callback and a MIP callback can be used at the same time.

Complete Program: ilolpex4.cpp

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

#include <ilcplex/ilocplex.h>
ILOSTLBEGIN


ILOPRIMALSIMPLEXCALLBACK0(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.setRootAlgorithm(IloCplex::Primal);
      cplex.use(MyCallback(env));
      cplex.solve();

      cplex.out() << "Solution status = " << cplex.getStatus() << endl;
      cplex.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), 35.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

Implementing Callbacks in the Callable C Library

ILOG CPLEX optimization routines in the Callable Library incorporate a callback facility to allow your application to transfer control temporarily from ILOG CPLEX to the calling application. Using callbacks, your application can implement interrupt capability, for example, or create displays of optimization progress. Once control is transferred back to a function in the calling application, the calling application can retrieve specific information about the current optimization from the routine CPXgetcallbackinfo(). Optionally, the calling application can then tell ILOG CPLEX to discontinue optimization.

To implement and use a callback in your application, you must first write the callback function and then tell ILOG CPLEX about it. For more information about the ILOG CPLEX Callable Library routines for callbacks, see the ILOG CPLEX Reference Manual.

Setting Callbacks

In the Callable Library, control callbacks are grouped into two groups: LP callbacks and MIP callbacks. For each group, one callback function can be set, by calling functions CPXsetlpcallbackfunc() and CPXsetmipcallbackfunc(), respectively. The function CPXsetlpcallbackfunc() is called for callbacks 1 through 6, while the function CPXsetmipcallbackfunc() is called for callbacks 7 through 10. You can distinguish between the actual callbacks by querying the parameter wherefrom which is passed to the callback function as parameter by CPLEX.

Callbacks for LPs and for MIPs

ILOG CPLEX will evaluate two user-defined callback functions, one during the solution of LP problems and one during the solution of MIP problems (if you are licensed to use the MIP optimizer). ILOG CPLEX calls the LP callback once per iteration during the solution of an LP problem and periodically during the presolve of LP and MIP preprocessing. ILOG CPLEX calls the MIP callback once before each subproblem is solved in the branch & cut process.

Every user-defined callback must have these arguments:

The arguments wherefrom and cbhandle should be used only in calls to CPXgetcallbackinfo().

Return Values for Callbacks

A user-written callback should return a nonzero value if the user wishes to stop the optimization and a value of zero otherwise.

For LP problems, if the callback returns a nonzero value, the solution process will terminate. If the process was not terminated during the presolve process, the status returned by the function IloCplex::getStatus or the routines CPXsolution() or CPXgetstat() will be one of the values in Table 8.5.

Table 8.5 Status of nonzero callbacks for LPs

Value 
Symbolic constant 
Meaning 
12 
CPX_ABORT_FEAS 
aborted in Phase II (simplex) 
13 
CPX_ABORT_INFEAS 
aborted in Phase I (simplex) 
14 
CPX_ABORT_DUAL_INFEAS 
primal feasible, dual infeasible (barrier) 
15 
CPX_ABORT_PRIM_INFEAS 
primal infeasible, dual feasible (barrier) 
16 
CPX_ABORT_PRIM_DUAL_INFEAS 
primal and dual both infeasible (barrier) 
17 
CPX_ABORT_PRIM_DUAL_FEAS 
primal and dual both feasible (barrier) 
18 
CPX_ABORT_CROSSOVER 
aborted in crossover (barrier) 

For both LP and MIP problems, if the LP callback returns a nonzero value during presolve preprocessing, the optimizer will return the value CPXERR_PRESLV_ABORT, and no solution information will be available.

For MIP problems, if the callback returns a nonzero value, the solution process will terminate and the status returned by IloCplex::getStatus() or CPXgetstat() will be one of the values in Table 8.6.

Table 8.6 Status of nonzero callbacks for MIPs

Value 
Symbolic constant 
Meaning 
113 
CPXMIP_ABORT_FEAS 
current solution integer feasible 
114 
CPXMIP_ABORT_INFEAS 
no integer feasible solution found 

Interaction Between Callbacks and CPLEX Parallel Optimizers

When you use callback routines, and invoke the parallel version of CPLEX optimizers, you need to be aware that the CPLEX environment passed to the callback routine corresponds to an individual CPLEX thread rather than to the original environment created. CPLEX frees this environment when finished with the thread. This does not affect most uses of the callback function. However, keep in mind that CPLEX associates problem objects, parameter settings, and message channels with the environment that specifies them. CPLEX therefore frees these items when it removes that environment; if the callback uses routines like CPXcreateprob, CPXcloneprob or CPXgetchannels, those objects remain allocated only as long as the associated environment does. Similarly, setting parameters with routines like CPXsetintparam affects settings only within the thread. So, applications that access CPLEX objects in the callback should use the original environment you created by if they need to access these objects outside the scope of the callback function.

Example: Using Callbacks

This example shows you how to use callbacks effectively with routines from the ILOG CPLEX Callable Library. It is based on lpex1.c, a program described in the manual Getting Started with ILOG CPLEX. This example about callbacks differs from that simpler one in several ways:

The function then prints these values to indicate progress.

This callback function offers a model for graphic user interfaces that display optimization progress as well as those GUIs that allow a user to interrupt and stop optimization. If you want to provide your end-user a facility like that to interrupt and stop optimization, then you should make mycallback() return a nonzero value to indicate the end-user interrupt.

Complete Program: lpex4.c

The complete program, lpex4.c, appears here or online in the standard distribution.

#include <ilcplex/cplex.h>

/* Bring in the declarations for the string functions */

#include <string.h>

/* Include declaration for function at end of program */

#ifndef  CPX_PROTOTYPE_MIN

static int
   populatebycolumn  (CPXENVptr env, CPXLPptr lp);

static int CPXPUBLIC
   mycallback (CPXENVptr env, void *cbdata, int wherefrom, 
               void *cbhandle);

#else

static int
   populatebycolumn ();

static int CPXPUBLIC
   mycallback ();


#endif


/* The problem we are optimizing will have 2 rows, 3 columns 
   and 6 nonzeros.  */

#define NUMROWS    2
#define NUMCOLS    3
#define NUMNZ      6

#ifndef  CPX_PROTOTYPE_MIN
int
main (void)
#else
int
main ()
#endif
{
   char     probname[16];  /* Problem name is max 16 characters */

   /* Declare and allocate space for the variables and arrays where we
      will store the optimization results including the status, objective
      value, variable values, dual values, row slacks and variable
      reduced costs. */

   int      solstat;
   double   objval;
   double   x[NUMCOLS];
   double   pi[NUMROWS];
   double   slack[NUMROWS];
   double   dj[NUMCOLS];


   CPXENVptr     env = NULL;
   CPXLPptr      lp = NULL;
   int           status;
   int           i, j;
   int           cur_numrows, cur_numcols;

   /* Initialize the CPLEX environment */

   env = CPXopenCPLEX (&status);

   /* If an error occurs, the status value indicates the reason for
      failure.  The error message will be printed at the end of the
      program. */

   if ( env == NULL ) {
      fprintf (stderr, "Could not open CPLEX environment.\n");
      goto TERMINATE;
   }

   /* Turn *off* output to the screen since we'll be producing it
      via the callback function.  This also means we won't see any
      CPLEX generated errors, but we'll handle that at the end of
      the program. */

   status = CPXsetintparam (env, CPX_PARAM_SCRIND, CPX_OFF);
   if ( status ) {
      fprintf (stderr, 
               "Failure to turn off screen indicator, error %d.\n", status);
      goto TERMINATE;
   }

   /* Create the problem. */

   strcpy (probname, "example");
   lp = CPXcreateprob (env, &status, probname);

   /* A returned pointer of NULL may mean that not enough memory
      was available or there was some other problem.  In the case of 
      failure, an error message will have been written to the error 
      channel from inside CPLEX.  In this example, we wouldn't see
      an error message from CPXcreateprob since we turned off the 
      CPX_PARAM_SCRIND parameter above.  The only way to see this message
      would be to use the CPLEX message handler, but that clutters up
      the simplicity of this example, which has a point of illustrating
      the CPLEX callback functionality.   */

   if ( lp == NULL ) {
      fprintf (stderr, "Failed to create LP.\n");
      goto TERMINATE;
   }

   /* Now populate the problem with the data. */

   status = populatebycolumn (env, lp);

   if ( status ) {
      fprintf (stderr, "Failed to populate problem data.\n");
      goto TERMINATE;
   }

   status = CPXsetlpcallbackfunc (env, mycallback, NULL);
   if ( status ) {
      fprintf (stderr, "Failed to set callback function.\n");
      goto TERMINATE;
   }

   /* Optimize the problem and obtain solution. */

   status = CPXsetintparam (env, CPX_PARAM_LPMETHOD, CPX_ALG_PRIMAL);
   if ( status ) {
      fprintf (stderr, 
               "Failed to set the optimization method, error %d.\n", status);
      goto TERMINATE;
   }

   status = CPXlpopt (env, lp);
   if ( status ) {
      fprintf (stderr, "Failed to optimize LP.\n");
      goto TERMINATE;
   }

   /* Turn off the callback function.  This isn't strictly necessary,
      but is good practice.  Note that the cast in front of NULL
      is only necessary for some compilers.   */

#ifndef CPX_PROTOTYPE_MIN
   status = CPXsetlpcallbackfunc (env,
              (int (CPXPUBLIC *)(CPXENVptr, void *, int, void *)) NULL, NULL);
#else
   status = CPXsetlpcallbackfunc (env, (int (CPXPUBLIC *)()) NULL, NULL);
#endif
   if ( status ) {
      fprintf (stderr, "Failed to turn off callback function.\n");
      goto TERMINATE;
   }

   status = CPXsolution (env, lp, &solstat, &objval, x, pi, slack, dj);
   if ( status ) {
      fprintf (stderr, "Failed to obtain solution.\n");
      goto TERMINATE;
   }


   /* Write the output to the screen. */

   printf ("\nSolution status = %d\n", solstat);
   printf ("Solution value  = %f\n\n", objval);

   /* The size of the problem should be obtained by asking CPLEX what
      the actual size is, rather than using sizes from when the problem
      was built.  cur_numrows and cur_numcols store the current number 
      of rows and columns, respectively.  */

   cur_numrows = CPXgetnumrows (env, lp);
   cur_numcols = CPXgetnumcols (env, lp);
   for (i = 0; i < cur_numrows; i++) {
      printf ("Row %d:  Slack = %10f  Pi = %10f\n", i, slack[i], pi[i]);
   }

   for (j = 0; j < cur_numcols; j++) {
      printf ("Column %d:  Value = %10f  Reduced cost = %10f\n",
              j, x[j], dj[j]);
   }

   /* Finally, write a copy of the problem to a file. */

   status = CPXwriteprob (env, lp, "lpex4.lp", NULL);
   if ( status ) {
      fprintf (stderr, "Failed to write LP to disk.\n");
      goto TERMINATE;
   }
   
TERMINATE:

   /* Free up the problem as allocated by CPXcreateprob, if necessary */

   if ( lp != NULL ) {
      int  frstatus;
      frstatus = CPXfreeprob (env, &lp);
      if ( frstatus ) {
         fprintf (stderr, "CPXfreeprob failed, error code %d.\n", frstatus);
         if (( !status ) && frstatus )  status = frstatus;
      }
   }

   /* Free up the CPLEX environment, if necessary */

   if ( env != NULL ) {
      int  clstatus;
      clstatus = CPXcloseCPLEX (&env);

      if ( clstatus ) {
         fprintf (stderr, "CPXcloseCPLEX failed, error code %d.\n", clstatus);
         if (( !status ) && clstatus )  status = clstatus;
      }
   }

   if ( status ) {
      char  errmsg[1024];

      /* Note that since we have turned off the CPLEX screen indicator,
         we'll need to print the error message ourselves. */

      CPXgeterrorstring (env, status, errmsg);
      fprintf (stderr, "%s", errmsg);
   }
     
   return (status);

}  /* END main */


/* This function builds by column the linear program:

      Maximize
       obj: x1 + 2 x2 + 3 x3
      Subject To
       c1: - x1 + x2 + x3 <= 20
       c2: x1 - 3 x2 + x3 <= 30
      Bounds
       35 <= x1 <= 40
      End
 */

#ifndef  CPX_PROTOTYPE_MIN
static int
populatebycolumn (CPXENVptr env, CPXLPptr lp)
#else
static int
populatebycolumn (env, lp)
CPXENVptr  env;
CPXLPptr   lp;
#endif
{
   int      status    = 0;
   double   obj[NUMCOLS];
   double   lb[NUMCOLS];
   double   ub[NUMCOLS];
   char     *colname[NUMCOLS];
   int      matbeg[NUMCOLS];
   int      matind[NUMNZ];
   double   matval[NUMNZ];
   double   rhs[NUMROWS];
   char     sense[NUMROWS];
   char     *rowname[NUMROWS];

   /* To build the problem by column, create the rows, and then 
      add the columns. */

   CPXchgobjsen (env, lp, CPX_MAX);  /* Problem is maximization */

   /* Now create the new rows.  First, populate the arrays. */

   rowname[0] = "c1";
   sense[0]   = `L';
   rhs[0]     = 20.0;

   rowname[1] = "c2";
   sense[1]   = `L';
   rhs[1]     = 30.0;

   status = CPXnewrows (env, lp, NUMROWS, rhs, sense, NULL, rowname);
   if ( status )   goto TERMINATE;

   /* Now add the new columns.  First, populate the arrays. */

       obj[0] = 1.0;      obj[1] = 2.0;           obj[2] = 3.0;

    matbeg[0] = 0;     matbeg[1] = 2;          matbeg[2] = 4;
      
    matind[0] = 0;     matind[2] = 0;          matind[4] = 0;
    matval[0] = -1.0;  matval[2] = 1.0;        matval[4] = 1.0;
 
    matind[1] = 1;     matind[3] = 1;          matind[5] = 1;
    matval[1] = 1.0;   matval[3] = -3.0;       matval[5] = 1.0;

        lb[0] = 35.0;      lb[1] = 0.0;           lb[2]  = 0.0;
        ub[0] = 40.0;      ub[1] = CPX_INFBOUND;  ub[2]  = CPX_INFBOUND;

   colname[0] = "x1"; colname[1] = "x2";      colname[2] = "x3";

   status = CPXaddcols (env, lp, NUMCOLS, NUMNZ, obj, matbeg, matind,
                        matval, lb, ub, colname);
   if ( status )  goto TERMINATE;

TERMINATE:

   return (status);

}  /* END populatebycolumn */


/* The callback function will print out the Phase of the simplex method,
   the sum of infeasibilities if in Phase 1, or the objective if in Phase 2.
   If any of our requests fails, we'll return an indication to abort.
 */

#ifndef  CPX_PROTOTYPE_MIN
static int CPXPUBLIC
mycallback (CPXENVptr env, void *cbdata, int wherefrom, void *cbhandle)
#else
static int CPXPUBLIC
mycallback (env, cbdata, wherefrom, cbhandle)
CPXENVptr  env;
void       *cbdata;
int        wherefrom;
void       *cbhandle;
#endif
{
   int    status = 0;
   int    phase  = -1;
   double suminf_or_objective;
   int    itcnt = -1;

   if ( wherefrom == CPX_CALLBACK_PRIMAL ) {
      status = CPXgetcallbackinfo (env, cbdata, wherefrom, 
                                   CPX_CALLBACK_INFO_ITCOUNT, &itcnt);
      if ( status )  goto TERMINATE;

      status = CPXgetcallbackinfo (env, cbdata, wherefrom, 
                                   CPX_CALLBACK_INFO_PRIMAL_FEAS, &phase);
      if ( status )  goto TERMINATE;

      if ( phase == 0 ) {
         status = CPXgetcallbackinfo (env, cbdata, wherefrom,
                                      CPX_CALLBACK_INFO_PRIMAL_INFMEAS,
                                      &suminf_or_objective);
         if ( status )  goto TERMINATE;

         printf ("Iteration %d: Infeasibility measure = %f\n", 
                  itcnt, suminf_or_objective);
      }
      else {
         status = CPXgetcallbackinfo (env, cbdata, wherefrom,
                                      CPX_CALLBACK_INFO_PRIMAL_OBJ,
                                      &suminf_or_objective);
         if ( status )  goto TERMINATE;

         printf ("Iteration %d: Objective = %f\n", 
                  itcnt, suminf_or_objective);
      }

   }

TERMINATE:

   return (status);

} /* END mycallback */


Previous Page: Using Callbacks  Return to Top Next Page: Control Callbacks for IloCplex