Callback Examples#
This section includes source code for all of the Gurobi callback examples.
The same source code can be found in the examples
directory of the
Gurobi distribution.
/* Copyright 2024, Gurobi Optimization, LLC */
/*
This example reads a model from a file, sets up a callback that
monitors optimization progress and implements a custom
termination strategy, and outputs progress information to the
screen and to a log file.
The termination strategy implemented in this callback stops the
optimization of a MIP model once at least one of the following two
conditions have been satisfied:
1) The optimality gap is less than 10%
2) At least 10000 nodes have been explored, and an integer feasible
solution has been found.
Note that termination is normally handled through Gurobi parameters
(MIPGap, NodeLimit, etc.). You should only use a callback for
termination if the available parameters don't capture your desired
termination criterion.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "gurobi_c.h"
/* Define structure to pass my data to the callback function */
struct callback_data {
double lastiter;
double lastnode;
double *solution;
FILE *logfile;
};
/* Define my callback function */
int __stdcall
mycallback(GRBmodel *model,
void *cbdata,
int where,
void *usrdata)
{
struct callback_data *mydata = (struct callback_data *) usrdata;
if (where == GRB_CB_POLLING) {
/* Ignore polling callback */
} else if (where == GRB_CB_PRESOLVE) {
/* Presolve callback */
int cdels, rdels;
GRBcbget(cbdata, where, GRB_CB_PRE_COLDEL, &cdels);
GRBcbget(cbdata, where, GRB_CB_PRE_ROWDEL, &rdels);
if (cdels || rdels) {
printf("%7d columns and %7d rows are removed\n", cdels, rdels);
}
} else if (where == GRB_CB_SIMPLEX) {
/* Simplex callback */
double itcnt, obj, pinf, dinf;
int ispert;
char ch;
GRBcbget(cbdata, where, GRB_CB_SPX_ITRCNT, &itcnt);
if (itcnt - mydata->lastiter >= 100) {
mydata->lastiter = itcnt;
GRBcbget(cbdata, where, GRB_CB_SPX_OBJVAL, &obj);
GRBcbget(cbdata, where, GRB_CB_SPX_ISPERT, &ispert);
GRBcbget(cbdata, where, GRB_CB_SPX_PRIMINF, &pinf);
GRBcbget(cbdata, where, GRB_CB_SPX_DUALINF, &dinf);
if (ispert == 0) ch = ' ';
else if (ispert == 1) ch = 'S';
else ch = 'P';
printf("%7.0f %14.7e%c %13.6e %13.6e\n", itcnt, obj, ch, pinf, dinf);
}
} else if (where == GRB_CB_MIP) {
/* General MIP callback */
double nodecnt, objbst, objbnd, actnodes, itcnt;
int solcnt, cutcnt;
GRBcbget(cbdata, where, GRB_CB_MIP_NODCNT, &nodecnt);
GRBcbget(cbdata, where, GRB_CB_MIP_OBJBST, &objbst);
GRBcbget(cbdata, where, GRB_CB_MIP_OBJBND, &objbnd);
GRBcbget(cbdata, where, GRB_CB_MIP_SOLCNT, &solcnt);
if (nodecnt - mydata->lastnode >= 100) {
mydata->lastnode = nodecnt;
GRBcbget(cbdata, where, GRB_CB_MIP_NODLFT, &actnodes);
GRBcbget(cbdata, where, GRB_CB_MIP_ITRCNT, &itcnt);
GRBcbget(cbdata, where, GRB_CB_MIP_CUTCNT, &cutcnt);
printf("%7.0f %7.0f %8.0f %13.6e %13.6e %7d %7d\n",
nodecnt, actnodes, itcnt, objbst, objbnd, solcnt, cutcnt);
}
if (fabs(objbst - objbnd) < 0.1 * (1.0 + fabs(objbst))) {
printf("Stop early - 10%% gap achieved\n");
GRBterminate(model);
}
if (nodecnt >= 10000 && solcnt) {
printf("Stop early - 10000 nodes explored\n");
GRBterminate(model);
}
} else if (where == GRB_CB_MIPSOL) {
/* MIP solution callback */
double nodecnt, obj;
int solcnt;
GRBcbget(cbdata, where, GRB_CB_MIPSOL_NODCNT, &nodecnt);
GRBcbget(cbdata, where, GRB_CB_MIPSOL_OBJ, &obj);
GRBcbget(cbdata, where, GRB_CB_MIPSOL_SOLCNT, &solcnt);
GRBcbget(cbdata, where, GRB_CB_MIPSOL_SOL, mydata->solution);
printf("**** New solution at node %.0f, obj %g, sol %d, x[0] = %.2f ****\n",
nodecnt, obj, solcnt, mydata->solution[0]);
} else if (where == GRB_CB_MIPNODE) {
int status;
/* MIP node callback */
printf("**** New node ****\n");
GRBcbget(cbdata, where, GRB_CB_MIPNODE_STATUS, &status);
if (status == GRB_OPTIMAL) {
GRBcbget(cbdata, where, GRB_CB_MIPNODE_REL, mydata->solution);
GRBcbsolution(cbdata, mydata->solution, NULL);
}
} else if (where == GRB_CB_BARRIER) {
/* Barrier callback */
int itcnt;
double primobj, dualobj, priminf, dualinf, compl;
GRBcbget(cbdata, where, GRB_CB_BARRIER_ITRCNT, &itcnt);
GRBcbget(cbdata, where, GRB_CB_BARRIER_PRIMOBJ, &primobj);
GRBcbget(cbdata, where, GRB_CB_BARRIER_DUALOBJ, &dualobj);
GRBcbget(cbdata, where, GRB_CB_BARRIER_PRIMINF, &priminf);
GRBcbget(cbdata, where, GRB_CB_BARRIER_DUALINF, &dualinf);
GRBcbget(cbdata, where, GRB_CB_BARRIER_COMPL, &compl);
printf("%d %.4e %.4e %.4e %.4e %.4e\n",
itcnt, primobj, dualobj, priminf, dualinf, compl);
} else if (where == GRB_CB_IIS) {
int constrmin, constrmax, constrguess, boundmin, boundmax, boundguess;
GRBcbget(cbdata, where, GRB_CB_IIS_CONSTRMIN, &constrmin);
GRBcbget(cbdata, where, GRB_CB_IIS_CONSTRMAX, &constrmax);
GRBcbget(cbdata, where, GRB_CB_IIS_CONSTRGUESS, &constrguess);
GRBcbget(cbdata, where, GRB_CB_IIS_BOUNDMIN, &boundmin);
GRBcbget(cbdata, where, GRB_CB_IIS_BOUNDMAX, &boundmax);
GRBcbget(cbdata, where, GRB_CB_IIS_BOUNDGUESS, &boundguess);
printf("IIS: %d,%d,%d %d,%d,%d\n",
constrmin, constrmax, constrguess,
boundmin, boundmax, boundguess);
} else if (where == GRB_CB_MESSAGE) {
/* Message callback */
char *msg;
GRBcbget(cbdata, where, GRB_CB_MSG_STRING, &msg);
fprintf(mydata->logfile, "%s", msg);
}
return 0;
}
int
main(int argc,
char *argv[])
{
GRBenv *env = NULL;
GRBmodel *model = NULL;
int error = 0;
int numvars, solcount, optimstatus, j;
double objval, x;
char *varname;
struct callback_data mydata;
mydata.lastiter = -GRB_INFINITY;
mydata.lastnode = -GRB_INFINITY;
mydata.solution = NULL;
mydata.logfile = NULL;
if (argc < 2) {
fprintf(stderr, "Usage: callback_c filename\n");
goto QUIT;
}
/* Open log file */
mydata.logfile = fopen("cb.log", "w");
if (!mydata.logfile) {
fprintf(stderr, "Cannot open cb.log for callback message\n");
goto QUIT;
}
/* Create environment */
error = GRBloadenv(&env, NULL);
if (error) goto QUIT;
/* Turn off display and heuristics */
error = GRBsetintparam(env, GRB_INT_PAR_OUTPUTFLAG, 0);
if (error) goto QUIT;
error = GRBsetdblparam(env, GRB_DBL_PAR_HEURISTICS, 0.0);
if (error) goto QUIT;
/* Read model from file */
error = GRBreadmodel(env, argv[1], &model);
if (error) goto QUIT;
/* Allocate space for solution */
error = GRBgetintattr(model, GRB_INT_ATTR_NUMVARS, &numvars);
if (error) goto QUIT;
mydata.solution = malloc(numvars*sizeof(double));
if (mydata.solution == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
exit(1);
}
/* Set callback function */
error = GRBsetcallbackfunc(model, mycallback, (void *) &mydata);
if (error) goto QUIT;
/* Solve model */
error = GRBoptimize(model);
if (error) goto QUIT;
/* Capture solution information */
printf("\nOptimization complete\n");
error = GRBgetintattr(model, GRB_INT_ATTR_SOLCOUNT, &solcount);
if (error) goto QUIT;
error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimstatus);
if (error) goto QUIT;
if (solcount == 0) {
printf("No solution found, optimization status = %d\n", optimstatus);
goto QUIT;
}
error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &objval);
if (error) goto QUIT;
printf("Solution found, objective = %.4e\n", objval);
for ( j = 0; j < numvars; ++j ) {
error = GRBgetstrattrelement(model, GRB_STR_ATTR_VARNAME, j, &varname);
if (error) goto QUIT;
error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, j, &x);
if (error) goto QUIT;
if (x != 0.0) {
printf("%s %f\n", varname, x);
}
}
QUIT:
/* Error reporting */
if (error) {
printf("ERROR: %s\n", GRBgeterrormsg(env));
exit(1);
}
/* Close log file */
if (mydata.logfile)
fclose(mydata.logfile);
/* Free solution */
if (mydata.solution)
free(mydata.solution);
/* Free model */
GRBfreemodel(model);
/* Free environment */
GRBfreeenv(env);
return 0;
}
/* Copyright 2024, Gurobi Optimization, LLC */
/*
This example reads a model from a file, sets up a callback that
monitors optimization progress and implements a custom
termination strategy, and outputs progress information to the
screen and to a log file.
The termination strategy implemented in this callback stops the
optimization of a MIP model once at least one of the following two
conditions have been satisfied:
1) The optimality gap is less than 10%
2) At least 10000 nodes have been explored, and an integer feasible
solution has been found.
Note that termination is normally handled through Gurobi parameters
(MIPGap, NodeLimit, etc.). You should only use a callback for
termination if the available parameters don't capture your desired
termination criterion.
*/
#include "gurobi_c++.h"
#include <fstream>
#include <cmath>
using namespace std;
class mycallback: public GRBCallback
{
public:
double lastiter;
double lastnode;
int numvars;
GRBVar* vars;
ofstream* logfile;
mycallback(int xnumvars, GRBVar* xvars, ofstream* xlogfile) {
lastiter = lastnode = -GRB_INFINITY;
numvars = xnumvars;
vars = xvars;
logfile = xlogfile;
}
protected:
void callback () {
try {
if (where == GRB_CB_POLLING) {
// Ignore polling callback
} else if (where == GRB_CB_PRESOLVE) {
// Presolve callback
int cdels = getIntInfo(GRB_CB_PRE_COLDEL);
int rdels = getIntInfo(GRB_CB_PRE_ROWDEL);
if (cdels || rdels) {
cout << cdels << " columns and " << rdels
<< " rows are removed" << endl;
}
} else if (where == GRB_CB_SIMPLEX) {
// Simplex callback
double itcnt = getDoubleInfo(GRB_CB_SPX_ITRCNT);
if (itcnt - lastiter >= 100) {
lastiter = itcnt;
double obj = getDoubleInfo(GRB_CB_SPX_OBJVAL);
int ispert = getIntInfo(GRB_CB_SPX_ISPERT);
double pinf = getDoubleInfo(GRB_CB_SPX_PRIMINF);
double dinf = getDoubleInfo(GRB_CB_SPX_DUALINF);
char ch;
if (ispert == 0) ch = ' ';
else if (ispert == 1) ch = 'S';
else ch = 'P';
cout << itcnt << " " << obj << ch << " "
<< pinf << " " << dinf << endl;
}
} else if (where == GRB_CB_MIP) {
// General MIP callback
double nodecnt = getDoubleInfo(GRB_CB_MIP_NODCNT);
double objbst = getDoubleInfo(GRB_CB_MIP_OBJBST);
double objbnd = getDoubleInfo(GRB_CB_MIP_OBJBND);
int solcnt = getIntInfo(GRB_CB_MIP_SOLCNT);
if (nodecnt - lastnode >= 100) {
lastnode = nodecnt;
int actnodes = (int) getDoubleInfo(GRB_CB_MIP_NODLFT);
int itcnt = (int) getDoubleInfo(GRB_CB_MIP_ITRCNT);
int cutcnt = getIntInfo(GRB_CB_MIP_CUTCNT);
cout << nodecnt << " " << actnodes << " " << itcnt
<< " " << objbst << " " << objbnd << " "
<< solcnt << " " << cutcnt << endl;
}
if (fabs(objbst - objbnd) < 0.1 * (1.0 + fabs(objbst))) {
cout << "Stop early - 10% gap achieved" << endl;
abort();
}
if (nodecnt >= 10000 && solcnt) {
cout << "Stop early - 10000 nodes explored" << endl;
abort();
}
} else if (where == GRB_CB_MIPSOL) {
// MIP solution callback
int nodecnt = (int) getDoubleInfo(GRB_CB_MIPSOL_NODCNT);
double obj = getDoubleInfo(GRB_CB_MIPSOL_OBJ);
int solcnt = getIntInfo(GRB_CB_MIPSOL_SOLCNT);
double* x = getSolution(vars, numvars);
cout << "**** New solution at node " << nodecnt
<< ", obj " << obj << ", sol " << solcnt
<< ", x[0] = " << x[0] << " ****" << endl;
delete[] x;
} else if (where == GRB_CB_MIPNODE) {
// MIP node callback
cout << "**** New node ****" << endl;
if (getIntInfo(GRB_CB_MIPNODE_STATUS) == GRB_OPTIMAL) {
double* x = getNodeRel(vars, numvars);
setSolution(vars, x, numvars);
delete[] x;
}
} else if (where == GRB_CB_BARRIER) {
// Barrier callback
int itcnt = getIntInfo(GRB_CB_BARRIER_ITRCNT);
double primobj = getDoubleInfo(GRB_CB_BARRIER_PRIMOBJ);
double dualobj = getDoubleInfo(GRB_CB_BARRIER_DUALOBJ);
double priminf = getDoubleInfo(GRB_CB_BARRIER_PRIMINF);
double dualinf = getDoubleInfo(GRB_CB_BARRIER_DUALINF);
double cmpl = getDoubleInfo(GRB_CB_BARRIER_COMPL);
cout << itcnt << " " << primobj << " " << dualobj << " "
<< priminf << " " << dualinf << " " << cmpl << endl;
} else if (where == GRB_CB_MESSAGE) {
// Message callback
string msg = getStringInfo(GRB_CB_MSG_STRING);
*logfile << msg;
}
} catch (GRBException e) {
cout << "Error number: " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
} catch (...) {
cout << "Error during callback" << endl;
}
}
};
int
main(int argc,
char *argv[])
{
if (argc < 2) {
cout << "Usage: callback_c++ filename" << endl;
return 1;
}
// Open log file
ofstream logfile("cb.log");
if (!logfile.is_open()) {
cout << "Cannot open cb.log for callback message" << endl;
return 1;
}
GRBEnv *env = 0;
GRBVar *vars = 0;
try {
// Create environment
env = new GRBEnv();
// Read model from file
GRBModel model = GRBModel(*env, argv[1]);
// Turn off display and heuristics
model.set(GRB_IntParam_OutputFlag, 0);
model.set(GRB_DoubleParam_Heuristics, 0.0);
// Create a callback object and associate it with the model
int numvars = model.get(GRB_IntAttr_NumVars);
vars = model.getVars();
mycallback cb = mycallback(numvars, vars, &logfile);
model.setCallback(&cb);
// Solve model and capture solution information
model.optimize();
cout << endl << "Optimization complete" << endl;
if (model.get(GRB_IntAttr_SolCount) == 0) {
cout << "No solution found, optimization status = "
<< model.get(GRB_IntAttr_Status) << endl;
} else {
cout << "Solution found, objective = "
<< model.get(GRB_DoubleAttr_ObjVal) << endl;
for (int j = 0; j < numvars; j++) {
GRBVar v = vars[j];
double x = v.get(GRB_DoubleAttr_X);
if (x != 0.0) {
cout << v.get(GRB_StringAttr_VarName) << " " << x << endl;
}
}
}
} catch (GRBException e) {
cout << "Error number: " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
} catch (...) {
cout << "Error during optimization" << endl;
}
// Close log file
logfile.close();
delete[] vars;
delete env;
return 0;
}
/* Copyright 2024, Gurobi Optimization, LLC */
/*
This example reads a model from a file, sets up a callback that
monitors optimization progress and implements a custom
termination strategy, and outputs progress information to the
screen and to a log file.
The termination strategy implemented in this callback stops the
optimization of a MIP model once at least one of the following two
conditions have been satisfied:
1) The optimality gap is less than 10%
2) At least 10000 nodes have been explored, and an integer feasible
solution has been found.
Note that termination is normally handled through Gurobi parameters
(MIPGap, NodeLimit, etc.). You should only use a callback for
termination if the available parameters don't capture your desired
termination criterion.
*/
using System;
using System.IO;
using Gurobi;
class callback_cs : GRBCallback
{
private double lastiter;
private double lastnode;
private GRBVar[] vars;
private StreamWriter logfile;
public callback_cs(GRBVar[] xvars, StreamWriter xlogfile)
{
lastiter = lastnode = -GRB.INFINITY;
vars = xvars;
logfile = xlogfile;
}
protected override void Callback()
{
try {
if (where == GRB.Callback.POLLING) {
// Ignore polling callback
} else if (where == GRB.Callback.PRESOLVE) {
// Presolve callback
int cdels = GetIntInfo(GRB.Callback.PRE_COLDEL);
int rdels = GetIntInfo(GRB.Callback.PRE_ROWDEL);
if (cdels != 0 || rdels != 0) {
Console.WriteLine(cdels + " columns and " + rdels
+ " rows are removed");
}
} else if (where == GRB.Callback.SIMPLEX) {
// Simplex callback
double itcnt = GetDoubleInfo(GRB.Callback.SPX_ITRCNT);
if (itcnt - lastiter >= 100) {
lastiter = itcnt;
double obj = GetDoubleInfo(GRB.Callback.SPX_OBJVAL);
int ispert = GetIntInfo(GRB.Callback.SPX_ISPERT);
double pinf = GetDoubleInfo(GRB.Callback.SPX_PRIMINF);
double dinf = GetDoubleInfo(GRB.Callback.SPX_DUALINF);
char ch;
if (ispert == 0) ch = ' ';
else if (ispert == 1) ch = 'S';
else ch = 'P';
Console.WriteLine(itcnt + " " + obj + ch + " "
+ pinf + " " + dinf);
}
} else if (where == GRB.Callback.MIP) {
// General MIP callback
double nodecnt = GetDoubleInfo(GRB.Callback.MIP_NODCNT);
double objbst = GetDoubleInfo(GRB.Callback.MIP_OBJBST);
double objbnd = GetDoubleInfo(GRB.Callback.MIP_OBJBND);
int solcnt = GetIntInfo(GRB.Callback.MIP_SOLCNT);
if (nodecnt - lastnode >= 100) {
lastnode = nodecnt;
int actnodes = (int) GetDoubleInfo(GRB.Callback.MIP_NODLFT);
int itcnt = (int) GetDoubleInfo(GRB.Callback.MIP_ITRCNT);
int cutcnt = GetIntInfo(GRB.Callback.MIP_CUTCNT);
Console.WriteLine(nodecnt + " " + actnodes + " "
+ itcnt + " " + objbst + " " + objbnd + " "
+ solcnt + " " + cutcnt);
}
if (Math.Abs(objbst - objbnd) < 0.1 * (1.0 + Math.Abs(objbst))) {
Console.WriteLine("Stop early - 10% gap achieved");
Abort();
}
if (nodecnt >= 10000 && solcnt > 0) {
Console.WriteLine("Stop early - 10000 nodes explored");
Abort();
}
} else if (where == GRB.Callback.MIPSOL) {
// MIP solution callback
int nodecnt = (int) GetDoubleInfo(GRB.Callback.MIPSOL_NODCNT);
double obj = GetDoubleInfo(GRB.Callback.MIPSOL_OBJ);
int solcnt = GetIntInfo(GRB.Callback.MIPSOL_SOLCNT);
double[] x = GetSolution(vars);
Console.WriteLine("**** New solution at node " + nodecnt
+ ", obj " + obj + ", sol " + solcnt
+ ", x[0] = " + x[0] + " ****");
} else if (where == GRB.Callback.MIPNODE) {
// MIP node callback
Console.WriteLine("**** New node ****");
if (GetIntInfo(GRB.Callback.MIPNODE_STATUS) == GRB.Status.OPTIMAL) {
double[] x = GetNodeRel(vars);
SetSolution(vars, x);
}
} else if (where == GRB.Callback.BARRIER) {
// Barrier callback
int itcnt = GetIntInfo(GRB.Callback.BARRIER_ITRCNT);
double primobj = GetDoubleInfo(GRB.Callback.BARRIER_PRIMOBJ);
double dualobj = GetDoubleInfo(GRB.Callback.BARRIER_DUALOBJ);
double priminf = GetDoubleInfo(GRB.Callback.BARRIER_PRIMINF);
double dualinf = GetDoubleInfo(GRB.Callback.BARRIER_DUALINF);
double cmpl = GetDoubleInfo(GRB.Callback.BARRIER_COMPL);
Console.WriteLine(itcnt + " " + primobj + " " + dualobj + " "
+ priminf + " " + dualinf + " " + cmpl);
} else if (where == GRB.Callback.MESSAGE) {
// Message callback
string msg = GetStringInfo(GRB.Callback.MSG_STRING);
if (msg != null) logfile.Write(msg);
}
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode);
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
} catch (Exception e) {
Console.WriteLine("Error during callback");
Console.WriteLine(e.StackTrace);
}
}
static void Main(string[] args)
{
if (args.Length < 1) {
Console.Out.WriteLine("Usage: callback_cs filename");
return;
}
StreamWriter logfile = null;
try {
// Create environment
GRBEnv env = new GRBEnv();
// Read model from file
GRBModel model = new GRBModel(env, args[0]);
// Turn off display and heuristics
model.Parameters.OutputFlag = 0;
model.Parameters.Heuristics = 0.0;
// Open log file
logfile = new StreamWriter("cb.log");
// Create a callback object and associate it with the model
GRBVar[] vars = model.GetVars();
callback_cs cb = new callback_cs(vars, logfile);
model.SetCallback(cb);
// Solve model and capture solution information
model.Optimize();
Console.WriteLine("");
Console.WriteLine("Optimization complete");
if (model.SolCount == 0) {
Console.WriteLine("No solution found, optimization status = "
+ model.Status);
} else {
Console.WriteLine("Solution found, objective = " + model.ObjVal);
string[] vnames = model.Get(GRB.StringAttr.VarName, vars);
double[] x = model.Get(GRB.DoubleAttr.X, vars);
for (int j = 0; j < vars.Length; j++) {
if (x[j] != 0.0) Console.WriteLine(vnames[j] + " " + x[j]);
}
}
// Dispose of model and environment
model.Dispose();
env.Dispose();
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode);
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
} catch (Exception e) {
Console.WriteLine("Error during optimization");
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
} finally {
// Close log file
if (logfile != null) logfile.Close();
}
}
}
/* Copyright 2024, Gurobi Optimization, LLC */
/*
This example reads a model from a file, sets up a callback that
monitors optimization progress and implements a custom
termination strategy, and outputs progress information to the
screen and to a log file.
The termination strategy implemented in this callback stops the
optimization of a MIP model once at least one of the following two
conditions have been satisfied:
1) The optimality gap is less than 10%
2) At least 10000 nodes have been explored, and an integer feasible
solution has been found.
Note that termination is normally handled through Gurobi parameters
(MIPGap, NodeLimit, etc.). You should only use a callback for
termination if the available parameters don't capture your desired
termination criterion.
*/
import com.gurobi.gurobi.*;
import java.io.FileWriter;
import java.io.IOException;
public class Callback extends GRBCallback {
private double lastiter;
private double lastnode;
private GRBVar[] vars;
private FileWriter logfile;
public Callback(GRBVar[] xvars, FileWriter xlogfile) {
lastiter = lastnode = -GRB.INFINITY;
vars = xvars;
logfile = xlogfile;
}
protected void callback() {
try {
if (where == GRB.CB_POLLING) {
// Ignore polling callback
} else if (where == GRB.CB_PRESOLVE) {
// Presolve callback
int cdels = getIntInfo(GRB.CB_PRE_COLDEL);
int rdels = getIntInfo(GRB.CB_PRE_ROWDEL);
if (cdels != 0 || rdels != 0) {
System.out.println(cdels + " columns and " + rdels
+ " rows are removed");
}
} else if (where == GRB.CB_SIMPLEX) {
// Simplex callback
double itcnt = getDoubleInfo(GRB.CB_SPX_ITRCNT);
if (itcnt - lastiter >= 100) {
lastiter = itcnt;
double obj = getDoubleInfo(GRB.CB_SPX_OBJVAL);
int ispert = getIntInfo(GRB.CB_SPX_ISPERT);
double pinf = getDoubleInfo(GRB.CB_SPX_PRIMINF);
double dinf = getDoubleInfo(GRB.CB_SPX_DUALINF);
char ch;
if (ispert == 0) ch = ' ';
else if (ispert == 1) ch = 'S';
else ch = 'P';
System.out.println(itcnt + " " + obj + ch + " "
+ pinf + " " + dinf);
}
} else if (where == GRB.CB_MIP) {
// General MIP callback
double nodecnt = getDoubleInfo(GRB.CB_MIP_NODCNT);
double objbst = getDoubleInfo(GRB.CB_MIP_OBJBST);
double objbnd = getDoubleInfo(GRB.CB_MIP_OBJBND);
int solcnt = getIntInfo(GRB.CB_MIP_SOLCNT);
if (nodecnt - lastnode >= 100) {
lastnode = nodecnt;
int actnodes = (int) getDoubleInfo(GRB.CB_MIP_NODLFT);
int itcnt = (int) getDoubleInfo(GRB.CB_MIP_ITRCNT);
int cutcnt = getIntInfo(GRB.CB_MIP_CUTCNT);
System.out.println(nodecnt + " " + actnodes + " "
+ itcnt + " " + objbst + " " + objbnd + " "
+ solcnt + " " + cutcnt);
}
if (Math.abs(objbst - objbnd) < 0.1 * (1.0 + Math.abs(objbst))) {
System.out.println("Stop early - 10% gap achieved");
abort();
}
if (nodecnt >= 10000 && solcnt > 0) {
System.out.println("Stop early - 10000 nodes explored");
abort();
}
} else if (where == GRB.CB_MIPSOL) {
// MIP solution callback
int nodecnt = (int) getDoubleInfo(GRB.CB_MIPSOL_NODCNT);
double obj = getDoubleInfo(GRB.CB_MIPSOL_OBJ);
int solcnt = getIntInfo(GRB.CB_MIPSOL_SOLCNT);
double[] x = getSolution(vars);
System.out.println("**** New solution at node " + nodecnt
+ ", obj " + obj + ", sol " + solcnt
+ ", x[0] = " + x[0] + " ****");
} else if (where == GRB.CB_MIPNODE) {
// MIP node callback
System.out.println("**** New node ****");
if (getIntInfo(GRB.CB_MIPNODE_STATUS) == GRB.OPTIMAL) {
double[] x = getNodeRel(vars);
setSolution(vars, x);
}
} else if (where == GRB.CB_BARRIER) {
// Barrier callback
int itcnt = getIntInfo(GRB.CB_BARRIER_ITRCNT);
double primobj = getDoubleInfo(GRB.CB_BARRIER_PRIMOBJ);
double dualobj = getDoubleInfo(GRB.CB_BARRIER_DUALOBJ);
double priminf = getDoubleInfo(GRB.CB_BARRIER_PRIMINF);
double dualinf = getDoubleInfo(GRB.CB_BARRIER_DUALINF);
double cmpl = getDoubleInfo(GRB.CB_BARRIER_COMPL);
System.out.println(itcnt + " " + primobj + " " + dualobj + " "
+ priminf + " " + dualinf + " " + cmpl);
} else if (where == GRB.CB_MESSAGE) {
// Message callback
String msg = getStringInfo(GRB.CB_MSG_STRING);
if (msg != null) logfile.write(msg);
}
} catch (GRBException e) {
System.out.println("Error code: " + e.getErrorCode());
System.out.println(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.out.println("Error during callback");
e.printStackTrace();
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: java Callback filename");
System.exit(1);
}
FileWriter logfile = null;
try {
// Create environment
GRBEnv env = new GRBEnv();
// Read model from file
GRBModel model = new GRBModel(env, args[0]);
// Turn off display and heuristics
model.set(GRB.IntParam.OutputFlag, 0);
model.set(GRB.DoubleParam.Heuristics, 0.0);
// Open log file
logfile = new FileWriter("cb.log");
// Create a callback object and associate it with the model
GRBVar[] vars = model.getVars();
Callback cb = new Callback(vars, logfile);
model.setCallback(cb);
// Solve model and capture solution information
model.optimize();
System.out.println("");
System.out.println("Optimization complete");
if (model.get(GRB.IntAttr.SolCount) == 0) {
System.out.println("No solution found, optimization status = "
+ model.get(GRB.IntAttr.Status));
} else {
System.out.println("Solution found, objective = "
+ model.get(GRB.DoubleAttr.ObjVal));
String[] vnames = model.get(GRB.StringAttr.VarName, vars);
double[] x = model.get(GRB.DoubleAttr.X, vars);
for (int j = 0; j < vars.length; j++) {
if (x[j] != 0.0) System.out.println(vnames[j] + " " + x[j]);
}
}
// Dispose of model and environment
model.dispose();
env.dispose();
} catch (GRBException e) {
System.out.println("Error code: " + e.getErrorCode());
System.out.println(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.out.println("Error during optimization");
e.printStackTrace();
} finally {
// Close log file
if (logfile != null) {
try { logfile.close(); } catch (IOException e) {}
}
}
}
}
#!/usr/bin/env python3.11
# Copyright 2024, Gurobi Optimization, LLC
# This example reads a model from a file, sets up a callback that
# monitors optimization progress and implements a custom
# termination strategy, and outputs progress information to the
# screen and to a log file.
#
# The termination strategy implemented in this callback stops the
# optimization of a MIP model once at least one of the following two
# conditions have been satisfied:
# 1) The optimality gap is less than 10%
# 2) At least 10000 nodes have been explored, and an integer feasible
# solution has been found.
# Note that termination is normally handled through Gurobi parameters
# (MIPGap, NodeLimit, etc.). You should only use a callback for
# termination if the available parameters don't capture your desired
# termination criterion.
import sys
from functools import partial
import gurobipy as gp
from gurobipy import GRB
class CallbackData:
def __init__(self, modelvars):
self.modelvars = modelvars
self.lastiter = -GRB.INFINITY
self.lastnode = -GRB.INFINITY
def mycallback(model, where, *, cbdata, logfile):
"""
Callback function. 'model' and 'where' arguments are passed by gurobipy
when the callback is invoked. The other arguments must be provided via
functools.partial:
1) 'cbdata' is an instance of CallbackData, which holds the model
variables and tracks state information across calls to the callback.
2) 'logfile' is a writeable file handle.
"""
if where == GRB.Callback.POLLING:
# Ignore polling callback
pass
elif where == GRB.Callback.PRESOLVE:
# Presolve callback
cdels = model.cbGet(GRB.Callback.PRE_COLDEL)
rdels = model.cbGet(GRB.Callback.PRE_ROWDEL)
if cdels or rdels:
print(f"{cdels} columns and {rdels} rows are removed")
elif where == GRB.Callback.SIMPLEX:
# Simplex callback
itcnt = model.cbGet(GRB.Callback.SPX_ITRCNT)
if itcnt - cbdata.lastiter >= 100:
cbdata.lastiter = itcnt
obj = model.cbGet(GRB.Callback.SPX_OBJVAL)
ispert = model.cbGet(GRB.Callback.SPX_ISPERT)
pinf = model.cbGet(GRB.Callback.SPX_PRIMINF)
dinf = model.cbGet(GRB.Callback.SPX_DUALINF)
if ispert == 0:
ch = " "
elif ispert == 1:
ch = "S"
else:
ch = "P"
print(f"{int(itcnt)} {obj:g}{ch} {pinf:g} {dinf:g}")
elif where == GRB.Callback.MIP:
# General MIP callback
nodecnt = model.cbGet(GRB.Callback.MIP_NODCNT)
objbst = model.cbGet(GRB.Callback.MIP_OBJBST)
objbnd = model.cbGet(GRB.Callback.MIP_OBJBND)
solcnt = model.cbGet(GRB.Callback.MIP_SOLCNT)
if nodecnt - cbdata.lastnode >= 100:
cbdata.lastnode = nodecnt
actnodes = model.cbGet(GRB.Callback.MIP_NODLFT)
itcnt = model.cbGet(GRB.Callback.MIP_ITRCNT)
cutcnt = model.cbGet(GRB.Callback.MIP_CUTCNT)
print(
f"{nodecnt:.0f} {actnodes:.0f} {itcnt:.0f} {objbst:g} "
f"{objbnd:g} {solcnt} {cutcnt}"
)
if abs(objbst - objbnd) < 0.1 * (1.0 + abs(objbst)):
print("Stop early - 10% gap achieved")
model.terminate()
if nodecnt >= 10000 and solcnt:
print("Stop early - 10000 nodes explored")
model.terminate()
elif where == GRB.Callback.MIPSOL:
# MIP solution callback
nodecnt = model.cbGet(GRB.Callback.MIPSOL_NODCNT)
obj = model.cbGet(GRB.Callback.MIPSOL_OBJ)
solcnt = model.cbGet(GRB.Callback.MIPSOL_SOLCNT)
x = model.cbGetSolution(cbdata.modelvars)
print(
f"**** New solution at node {nodecnt:.0f}, obj {obj:g}, "
f"sol {solcnt:.0f}, x[0] = {x[0]:g} ****"
)
elif where == GRB.Callback.MIPNODE:
# MIP node callback
print("**** New node ****")
if model.cbGet(GRB.Callback.MIPNODE_STATUS) == GRB.OPTIMAL:
x = model.cbGetNodeRel(cbdata.modelvars)
model.cbSetSolution(cbdata.modelvars, x)
elif where == GRB.Callback.BARRIER:
# Barrier callback
itcnt = model.cbGet(GRB.Callback.BARRIER_ITRCNT)
primobj = model.cbGet(GRB.Callback.BARRIER_PRIMOBJ)
dualobj = model.cbGet(GRB.Callback.BARRIER_DUALOBJ)
priminf = model.cbGet(GRB.Callback.BARRIER_PRIMINF)
dualinf = model.cbGet(GRB.Callback.BARRIER_DUALINF)
cmpl = model.cbGet(GRB.Callback.BARRIER_COMPL)
print(f"{itcnt:.0f} {primobj:g} {dualobj:g} {priminf:g} {dualinf:g} {cmpl:g}")
elif where == GRB.Callback.MESSAGE:
# Message callback
msg = model.cbGet(GRB.Callback.MSG_STRING)
logfile.write(msg)
# Parse arguments
if len(sys.argv) < 2:
print("Usage: callback.py filename")
sys.exit(0)
model_file = sys.argv[1]
# This context block manages several resources to ensure they are properly
# closed at the end of the program:
# 1) A Gurobi environment, with console output and heuristics disabled.
# 2) A Gurobi model, read from a file provided by the user.
# 3) A Python file handle which the callback will write to.
with gp.Env(params={"OutputFlag": 0, "Heuristics": 0}) as env, gp.read(
model_file, env=env
) as model, open("cb.log", "w") as logfile:
# Set up callback function with required arguments
callback_data = CallbackData(model.getVars())
callback_func = partial(mycallback, cbdata=callback_data, logfile=logfile)
# Solve model and print solution information
model.optimize(callback_func)
print("")
print("Optimization complete")
if model.SolCount == 0:
print(f"No solution found, optimization status = {model.Status}")
else:
print(f"Solution found, objective = {model.ObjVal:g}")
for v in model.getVars():
if v.X != 0.0:
print(f"{v.VarName} {v.X:g}")
' Copyright 2024, Gurobi Optimization, LLC
' This example reads a model from a file, sets up a callback that
' monitors optimization progress and implements a custom
' termination strategy, and outputs progress information to the
' screen and to a log file.
'
' The termination strategy implemented in this callback stops the
' optimization of a MIP model once at least one of the following two
' conditions have been satisfied:
' 1) The optimality gap is less than 10%
' 2) At least 10000 nodes have been explored, and an integer feasible
' solution has been found.
' Note that termination is normally handled through Gurobi parameters
' (MIPGap, NodeLimit, etc.). You should only use a callback for
' termination if the available parameters don't capture your desired
' termination criterion.
Imports System
Imports Gurobi
Class callback_vb
Inherits GRBCallback
Private vars As GRBVar()
Private lastnode As Double
Private lastiter As Double
Public Sub New(ByVal xvars As GRBVar())
vars = xvars
lastnode = lastiter = -1
End Sub
Protected Overloads Overrides Sub Callback()
Try
If where = GRB.Callback.PRESOLVE Then
' Presolve callback
Dim cdels As Integer = GetIntInfo(GRB.Callback.PRE_COLDEL)
Dim rdels As Integer = GetIntInfo(GRB.Callback.PRE_ROWDEL)
Console.WriteLine(cdels & " columns and " & rdels & " rows are removed")
ElseIf where = GRB.Callback.SIMPLEX Then
' Simplex callback
Dim itcnt As Double = GetDoubleInfo(GRB.Callback.SPX_ITRCNT)
If itcnt Mod - lastiter >= 100 Then
lastiter = itcnt
Dim obj As Double = GetDoubleInfo(GRB.Callback.SPX_OBJVAL)
Dim pinf As Double = GetDoubleInfo(GRB.Callback.SPX_PRIMINF)
Dim dinf As Double = GetDoubleInfo(GRB.Callback.SPX_DUALINF)
Dim ispert As Integer = GetIntInfo(GRB.Callback.SPX_ISPERT)
Dim ch As Char
If ispert = 0 Then
ch = " "c
ElseIf ispert = 1 Then
ch = "S"c
Else
ch = "P"c
End If
Console.WriteLine(itcnt & " " & obj & ch & " " & pinf & " " & dinf)
End If
ElseIf where = GRB.Callback.MIP Then
' General MIP callback
Dim nodecnt As Double = GetDoubleInfo(GRB.Callback.MIP_NODCNT)
If nodecnt - lastnode >= 100 Then
lastnode = nodecnt
Dim objbst As Double = GetDoubleInfo(GRB.Callback.MIP_OBJBST)
Dim objbnd As Double = GetDoubleInfo(GRB.Callback.MIP_OBJBND)
If Math.Abs(objbst - objbnd) < 0.1 * (1.0R + Math.Abs(objbst)) Then
Abort()
End If
Dim actnodes As Integer = CInt(GetDoubleInfo(GRB.Callback.MIP_NODLFT))
Dim itcnt As Integer = CInt(GetDoubleInfo(GRB.Callback.MIP_ITRCNT))
Dim solcnt As Integer = GetIntInfo(GRB.Callback.MIP_SOLCNT)
Dim cutcnt As Integer = GetIntInfo(GRB.Callback.MIP_CUTCNT)
Console.WriteLine(nodecnt & " " & actnodes & " " & itcnt & " " & _
objbst & " " & objbnd & " " & solcnt & " " & cutcnt)
End If
ElseIf where = GRB.Callback.MIPSOL Then
' MIP solution callback
Dim obj As Double = GetDoubleInfo(GRB.Callback.MIPSOL_OBJ)
Dim nodecnt As Integer = CInt(GetDoubleInfo(GRB.Callback.MIPSOL_NODCNT))
Dim x As Double() = GetSolution(vars)
Console.WriteLine("**** New solution at node " & nodecnt & ", obj " & _
obj & ", x(0) = " & x(0) & "****")
End If
Catch e As GRBException
Console.WriteLine("Error code: " & e.ErrorCode & ". " & e.Message)
Console.WriteLine(e.StackTrace)
End Try
End Sub
Shared Sub Main(ByVal args As String())
If args.Length < 1 Then
Console.WriteLine("Usage: callback_vb filename")
Return
End If
Try
Dim env As New GRBEnv()
Dim model As New GRBModel(env, args(0))
Dim vars As GRBVar() = model.GetVars()
' Create a callback object and associate it with the model
model.SetCallback(New callback_vb(vars))
model.Optimize()
Dim x As Double() = model.Get(GRB.DoubleAttr.X, vars)
Dim vnames As String() = model.Get(GRB.StringAttr.VarName, vars)
For j As Integer = 0 To vars.Length - 1
If x(j) <> 0.0R Then
Console.WriteLine(vnames(j) & " " & x(j))
End If
Next
' Dispose of model and env
model.Dispose()
env.Dispose()
Catch e As GRBException
Console.WriteLine("Error code: " & e.ErrorCode & ". " & e.Message)
Console.WriteLine(e.StackTrace)
End Try
End Sub
End Class