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 9 such places where diagnostic callbacks are called:

Implementing Callbacks In ILOG CPLEX with Concert Technology

Callbacks are accessed via the IloCplex::Callback handle class in the C++ version of IloCplex. It points to an implementation object of a subclass of IloCplex::CallbackI. In java, there is no handle class and one only deals with implementation classes which are subclasses of IloCplex.Callback. 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 for C++:

             IloCplex::CallbackI
                |
                +--- IloCplex::PresolveCallbackI
                |
                +--- IloCplex::CrossoverCallbackI
                |
                +--- IloCplex::NetworkCallbackI
                |
                +--- IloCplex::ContinuousCallbackI
                |       |
                |       +--- IloCplex::BarrierCallbackI
                |       |
                |       +--- IloCplex::SimplexCallbackI
                | 
                +--- IloCplex::MIPCallbackI
                        |
                        +--- IloCplex::ProbingCallback
                        | 
                        +--- IloCplex::FractionalCutCallbackI
                        |
                        +--- IloCplex::DisjunctiveCutCallbackI

The callback class hierarchy for Java is exactly the same, but the class names differ, in that there is no 'I' at the end. For example the corresponding Java implementation class for IloCplex::ContinuousCallbackI is IloCplex.ContinuousCallback.

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 (IloCplex.Callback) 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. Since the Java programing language does not provide macros, only the more complex way is available for Java. We will first explain the more complex way and discuss the underlying design. To quickly implement your callback without details about 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));

}

It is not customary to write such a function for Java, but new is called explicitly for creating a callback object when needed. Once an implementation object of your callback is created (either with the constructor function in C++ or by directly calling the new operator for Java), use it with IloCplex by calling cplex.use() with the callback object as parameter. In C++, to remove a callback that is being used by a cplex object, call callback.end() on the IloCplex::Callback handle callback. In java there is no way of removing individual callbacks from your IloCplex object. Instead, you can remove all callbacks by calling cplex.clearCallbacks(). Since Java uses garbage collection for memory management, there is nothing equivalent to the end() method for callbacks in the Java API.

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. In C++, the 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 in C++, class IloCplex::CallbackI defines another pure virtual method:

virtual IloCplex::CallbackI* IloCplex::CallbackI::duplicateCallback() 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

This is how to implement a callback using macros. Since macros are not supported in java, this technique will only apply to C++.

Start by determining 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 simplex callback with one parameter, the macro is ILOSIMPLEXCALLBACK1. 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 9.4 Callback Macros

Callback 
Macro 
Class 
presolve 
ILOPRESOLVECALLBACKn 
IloCplex::PresolveCallbackI 
continuous 
ILOCONTINUOUSCALLBACKn 
IloCplex::ContinuousCallbackI 
simplex 
ILOSIMPLEXCALLBACKn 
IloCplex::SimplexCallbackI 
primal simplex 
ILOPRIMALSIMPLEXCALLBACKn 
IloCplex::SimplexCallbackI 
dual simplex 
ILODUALSIMPLEXCALLBACKn 
IloCplex::SimplexCallbackI 
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 simplex callback with the name MyCallback that takes one parameter:

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

This callback aborts the 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::ContinuousCallbackI.

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 (IloCplex.CallbackI). Since there is no corresponding callback in the Callable Library, 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 IloCplex object that is calling the callback, getEnv(), which returns the corresponding environment (C++ only), 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 Continuous Callback

The second special callback class is IloCplex::ContinuousCallbackI (IloCplex.ContinuousCallback). If you create a Continuous callback and use it with an IloCplex object, it will be used for both the barrier and the simplex callback. In other words, implementing and using one Continuous callback is equivalent to writing and using these two callbacks independently.

Example: Deriving the Simplex Callback

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 we will restrict our discussion to 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 member functions 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 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));

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 primal simplex callback object (possibly with different subclasses), only the last one passed to ILOG 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 simplex callback and a MIP callback can be used at the same time.

Complete Program: ilolpex4.cpp

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

  // -------------------------------------------------------------- -*- C++ -*-
  // File: examples/src/ilolpex4.cpp
  // Version 8.1
  // --------------------------------------------------------------------------
  //  Copyright (C) 1999-2002 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
  
  

Implementing Callbacks in the Callable 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, diagnostic 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. You can distinguish between the actual callbacks by querying the parameter wherefrom which is passed to the callback function as parameter by ILOG CPLEX.

Callbacks for LPs and for MIPs

ILOG CPLEX will evaluate two user-defined callback functions, one during the solution of LP and QP problems and one during the solution of MIP problems. ILOG CPLEX calls the LP callback once per iteration during the solution of an LP or QP problem and periodically during the presolve. ILOG CPLEX calls the MIP callback periodically during the probing phase of MIP preprocessing, periodically during cut generation, and once before each subproblem is solved in the branch and cut process.

Every user-defined callback must have these arguments:

The arguments wherefrom and cbdata 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 or QP 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 CPX_STAT_ABORT_USER (value 13).

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 9.5.

Table 9.5 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 ILOG CPLEX Parallel Optimizers

When you use callback routines, and invoke the parallel version of ILOG CPLEX optimizers, you need to be aware that the ILOG CPLEX environment passed to the callback routine corresponds to an individual ILOG CPLEX thread rather than to the original environment created. ILOG CPLEX frees this environment when finished with the thread. This does not affect most uses of the callback function. However, keep in mind that ILOG CPLEX associates problem objects, parameter settings, and message channels with the environment that specifies them. ILOG 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 ILOG CPLEX objects in the callback should use the original environment you created 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 ILOG CPLEX Getting Started manual. 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 and online in the standard distribution.

  /*------------------------------------------------------------------------*/
  /*  File: examples/src/lpex4.c                                            */
  /*  Version 8.1                                                           */
  /*------------------------------------------------------------------------*/
  /*  Copyright (C) 1997-2002 by ILOG.                                      */
  /*  All Rights Reserved.                                                  */
  /*  Permission is expressly granted to use this example in the            */
  /*  course of developing applications that use ILOG products.             */
  /*------------------------------------------------------------------------*/
  
  /* lpex4.c - Illustrating the CPLEX callback functionality.
  
     This is a modification of lpex1.c, where we use a callback
     function to print the iteration info, rather than have CPLEX
     do it. */
  
  /* Bring in the CPLEX function declarations and the C library
     header file stdio.h with the following single include. */
  
  #include <ilcplex/cplex.h>
  
  /* Bring in the declarations for the string functions */
  
  #include <string.h>
  
  /* Include declaration for function at end of program */
  
  static int
     populatebycolumn  (CPXENVptr env, CPXLPptr lp);
  
  static int CPXPUBLIC
     mycallback (CPXCENVptr env, void *cbdata, int wherefrom,
                 void *cbhandle);
  
  
  
  /* The problem we are optimizing will have 2 rows, 3 columns
     and 6 nonzeros.  */
  
  #define NUMROWS    2
  #define NUMCOLS    3
  #define NUMNZ      6
  
  int
  main (void)
  {
     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.   */
  
     status = CPXsetlpcallbackfunc (env,
                (int (CPXPUBLIC *)(CPXCENVptr, void *, int, void *)) NULL, NULL);
  
     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
         0 <= x1 <= 40
        End
   */
  
  static int
  populatebycolumn (CPXENVptr env, CPXLPptr lp)
  {
     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] = 0.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.
   */
  
  static int CPXPUBLIC
  mycallback (CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle)
  {
     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