Callback.java#
/* 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) {}
}
}
}
}