This section includes source code for all of the Gurobi callback examples.The same source code can be found in theexamples directory of theGurobi distribution.
/* Copyright 2025, 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 */structcallback_data{doublelastiter;doublelastnode;double*solution;FILE*logfile;};/* Define my callback function */int__stdcallmycallback(GRBmodel*model,void*cbdata,intwhere,void*usrdata){structcallback_data*mydata=(structcallback_data*)usrdata;if(where==GRB_CB_POLLING){/* Ignore polling callback */}elseif(where==GRB_CB_PRESOLVE){/* Presolve callback */intcdels,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);}}elseif(where==GRB_CB_SIMPLEX){/* Simplex callback */doubleitcnt,obj,pinf,dinf;intispert;charch;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=' ';elseif(ispert==1)ch='S';elsech='P';printf("%7.0f %14.7e%c %13.6e %13.6e\n",itcnt,obj,ch,pinf,dinf);}}elseif(where==GRB_CB_MIP){/* General MIP callback */doublenodecnt,objbst,objbnd,actnodes,itcnt;intsolcnt,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);}}elseif(where==GRB_CB_MIPSOL){/* MIP solution callback */doublenodecnt,obj;intsolcnt;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]);}elseif(where==GRB_CB_MIPNODE){intstatus;/* 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);}}elseif(where==GRB_CB_BARRIER){/* Barrier callback */intitcnt;doubleprimobj,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);}elseif(where==GRB_CB_IIS){intconstrmin,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);}elseif(where==GRB_CB_MESSAGE){/* Message callback */char*msg;GRBcbget(cbdata,where,GRB_CB_MSG_STRING,&msg);fprintf(mydata->logfile,"%s",msg);}return0;}intmain(intargc,char*argv[]){GRBenv*env=NULL;GRBmodel*model=NULL;interror=0;intnumvars,solcount,optimstatus,j;doubleobjval,x;char*varname;structcallback_datamydata;mydata.lastiter=-GRB_INFINITY;mydata.lastnode=-GRB_INFINITY;mydata.solution=NULL;mydata.logfile=NULL;if(argc<2){fprintf(stderr,"Usage: callback_c filename\n");gotoQUIT;}/* Open log file */mydata.logfile=fopen("cb.log","w");if(!mydata.logfile){fprintf(stderr,"Cannot open cb.log for callback message\n");gotoQUIT;}/* Create environment */error=GRBloadenv(&env,NULL);if(error)gotoQUIT;/* Turn off display and heuristics */error=GRBsetintparam(env,GRB_INT_PAR_OUTPUTFLAG,0);if(error)gotoQUIT;error=GRBsetdblparam(env,GRB_DBL_PAR_HEURISTICS,0.0);if(error)gotoQUIT;/* Read model from file */error=GRBreadmodel(env,argv[1],&model);if(error)gotoQUIT;/* Allocate space for solution */error=GRBgetintattr(model,GRB_INT_ATTR_NUMVARS,&numvars);if(error)gotoQUIT;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)gotoQUIT;/* Solve model */error=GRBoptimize(model);if(error)gotoQUIT;/* Capture solution information */printf("\nOptimization complete\n");error=GRBgetintattr(model,GRB_INT_ATTR_SOLCOUNT,&solcount);if(error)gotoQUIT;error=GRBgetintattr(model,GRB_INT_ATTR_STATUS,&optimstatus);if(error)gotoQUIT;if(solcount==0){printf("No solution found, optimization status = %d\n",optimstatus);gotoQUIT;}error=GRBgetdblattr(model,GRB_DBL_ATTR_OBJVAL,&objval);if(error)gotoQUIT;printf("Solution found, objective = %.4e\n",objval);for(j=0;j<numvars;++j){error=GRBgetstrattrelement(model,GRB_STR_ATTR_VARNAME,j,&varname);if(error)gotoQUIT;error=GRBgetdblattrelement(model,GRB_DBL_ATTR_X,j,&x);if(error)gotoQUIT;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);return0;}
/* Copyright 2025, 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>usingnamespacestd;classmycallback:publicGRBCallback{public:doublelastiter;doublelastnode;intnumvars;GRBVar*vars;ofstream*logfile;mycallback(intxnumvars,GRBVar*xvars,ofstream*xlogfile){lastiter=lastnode=-GRB_INFINITY;numvars=xnumvars;vars=xvars;logfile=xlogfile;}protected:voidcallback(){try{if(where==GRB_CB_POLLING){// Ignore polling callback}elseif(where==GRB_CB_PRESOLVE){// Presolve callbackintcdels=getIntInfo(GRB_CB_PRE_COLDEL);intrdels=getIntInfo(GRB_CB_PRE_ROWDEL);if(cdels||rdels){cout<<cdels<<" columns and "<<rdels<<" rows are removed"<<endl;}}elseif(where==GRB_CB_SIMPLEX){// Simplex callbackdoubleitcnt=getDoubleInfo(GRB_CB_SPX_ITRCNT);if(itcnt-lastiter>=100){lastiter=itcnt;doubleobj=getDoubleInfo(GRB_CB_SPX_OBJVAL);intispert=getIntInfo(GRB_CB_SPX_ISPERT);doublepinf=getDoubleInfo(GRB_CB_SPX_PRIMINF);doubledinf=getDoubleInfo(GRB_CB_SPX_DUALINF);charch;if(ispert==0)ch=' ';elseif(ispert==1)ch='S';elsech='P';cout<<itcnt<<" "<<obj<<ch<<" "<<pinf<<" "<<dinf<<endl;}}elseif(where==GRB_CB_MIP){// General MIP callbackdoublenodecnt=getDoubleInfo(GRB_CB_MIP_NODCNT);doubleobjbst=getDoubleInfo(GRB_CB_MIP_OBJBST);doubleobjbnd=getDoubleInfo(GRB_CB_MIP_OBJBND);intsolcnt=getIntInfo(GRB_CB_MIP_SOLCNT);if(nodecnt-lastnode>=100){lastnode=nodecnt;intactnodes=(int)getDoubleInfo(GRB_CB_MIP_NODLFT);intitcnt=(int)getDoubleInfo(GRB_CB_MIP_ITRCNT);intcutcnt=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();}}elseif(where==GRB_CB_MIPSOL){// MIP solution callbackintnodecnt=(int)getDoubleInfo(GRB_CB_MIPSOL_NODCNT);doubleobj=getDoubleInfo(GRB_CB_MIPSOL_OBJ);intsolcnt=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;}elseif(where==GRB_CB_MIPNODE){// MIP node callbackcout<<"**** New node ****"<<endl;if(getIntInfo(GRB_CB_MIPNODE_STATUS)==GRB_OPTIMAL){double*x=getNodeRel(vars,numvars);setSolution(vars,x,numvars);delete[]x;}}elseif(where==GRB_CB_BARRIER){// Barrier callbackintitcnt=getIntInfo(GRB_CB_BARRIER_ITRCNT);doubleprimobj=getDoubleInfo(GRB_CB_BARRIER_PRIMOBJ);doubledualobj=getDoubleInfo(GRB_CB_BARRIER_DUALOBJ);doublepriminf=getDoubleInfo(GRB_CB_BARRIER_PRIMINF);doubledualinf=getDoubleInfo(GRB_CB_BARRIER_DUALINF);doublecmpl=getDoubleInfo(GRB_CB_BARRIER_COMPL);cout<<itcnt<<" "<<primobj<<" "<<dualobj<<" "<<priminf<<" "<<dualinf<<" "<<cmpl<<endl;}elseif(where==GRB_CB_MESSAGE){// Message callbackstringmsg=getStringInfo(GRB_CB_MSG_STRING);*logfile<<msg;}}catch(GRBExceptione){cout<<"Error number: "<<e.getErrorCode()<<endl;cout<<e.getMessage()<<endl;}catch(...){cout<<"Error during callback"<<endl;}}};intmain(intargc,char*argv[]){if(argc<2){cout<<"Usage: callback_c++ filename"<<endl;return1;}// Open log fileofstreamlogfile("cb.log");if(!logfile.is_open()){cout<<"Cannot open cb.log for callback message"<<endl;return1;}GRBEnv*env=0;GRBVar*vars=0;try{// Create environmentenv=newGRBEnv();// Read model from fileGRBModelmodel=GRBModel(*env,argv[1]);// Turn off display and heuristicsmodel.set(GRB_IntParam_OutputFlag,0);model.set(GRB_DoubleParam_Heuristics,0.0);// Create a callback object and associate it with the modelintnumvars=model.get(GRB_IntAttr_NumVars);vars=model.getVars();mycallbackcb=mycallback(numvars,vars,&logfile);model.setCallback(&cb);// Solve model and capture solution informationmodel.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(intj=0;j<numvars;j++){GRBVarv=vars[j];doublex=v.get(GRB_DoubleAttr_X);if(x!=0.0){cout<<v.get(GRB_StringAttr_VarName)<<" "<<x<<endl;}}}}catch(GRBExceptione){cout<<"Error number: "<<e.getErrorCode()<<endl;cout<<e.getMessage()<<endl;}catch(...){cout<<"Error during optimization"<<endl;}// Close log filelogfile.close();delete[]vars;deleteenv;return0;}
/* Copyright 2025, 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.*/usingSystem;usingSystem.IO;usingGurobi;classcallback_cs:GRBCallback{privatedoublelastiter;privatedoublelastnode;privateGRBVar[]vars;privateStreamWriterlogfile;publiccallback_cs(GRBVar[]xvars,StreamWriterxlogfile){lastiter=lastnode=-GRB.INFINITY;vars=xvars;logfile=xlogfile;}protectedoverridevoidCallback(){try{if(where==GRB.Callback.POLLING){// Ignore polling callback}elseif(where==GRB.Callback.PRESOLVE){// Presolve callbackintcdels=GetIntInfo(GRB.Callback.PRE_COLDEL);intrdels=GetIntInfo(GRB.Callback.PRE_ROWDEL);if(cdels!=0||rdels!=0){Console.WriteLine(cdels+" columns and "+rdels+" rows are removed");}}elseif(where==GRB.Callback.SIMPLEX){// Simplex callbackdoubleitcnt=GetDoubleInfo(GRB.Callback.SPX_ITRCNT);if(itcnt-lastiter>=100){lastiter=itcnt;doubleobj=GetDoubleInfo(GRB.Callback.SPX_OBJVAL);intispert=GetIntInfo(GRB.Callback.SPX_ISPERT);doublepinf=GetDoubleInfo(GRB.Callback.SPX_PRIMINF);doubledinf=GetDoubleInfo(GRB.Callback.SPX_DUALINF);charch;if(ispert==0)ch=' ';elseif(ispert==1)ch='S';elsech='P';Console.WriteLine(itcnt+" "+obj+ch+" "+pinf+" "+dinf);}}elseif(where==GRB.Callback.MIP){// General MIP callbackdoublenodecnt=GetDoubleInfo(GRB.Callback.MIP_NODCNT);doubleobjbst=GetDoubleInfo(GRB.Callback.MIP_OBJBST);doubleobjbnd=GetDoubleInfo(GRB.Callback.MIP_OBJBND);intsolcnt=GetIntInfo(GRB.Callback.MIP_SOLCNT);if(nodecnt-lastnode>=100){lastnode=nodecnt;intactnodes=(int)GetDoubleInfo(GRB.Callback.MIP_NODLFT);intitcnt=(int)GetDoubleInfo(GRB.Callback.MIP_ITRCNT);intcutcnt=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();}}elseif(where==GRB.Callback.MIPSOL){// MIP solution callbackintnodecnt=(int)GetDoubleInfo(GRB.Callback.MIPSOL_NODCNT);doubleobj=GetDoubleInfo(GRB.Callback.MIPSOL_OBJ);intsolcnt=GetIntInfo(GRB.Callback.MIPSOL_SOLCNT);double[]x=GetSolution(vars);Console.WriteLine("**** New solution at node "+nodecnt+", obj "+obj+", sol "+solcnt+", x[0] = "+x[0]+" ****");}elseif(where==GRB.Callback.MIPNODE){// MIP node callbackConsole.WriteLine("**** New node ****");if(GetIntInfo(GRB.Callback.MIPNODE_STATUS)==GRB.Status.OPTIMAL){double[]x=GetNodeRel(vars);SetSolution(vars,x);}}elseif(where==GRB.Callback.BARRIER){// Barrier callbackintitcnt=GetIntInfo(GRB.Callback.BARRIER_ITRCNT);doubleprimobj=GetDoubleInfo(GRB.Callback.BARRIER_PRIMOBJ);doubledualobj=GetDoubleInfo(GRB.Callback.BARRIER_DUALOBJ);doublepriminf=GetDoubleInfo(GRB.Callback.BARRIER_PRIMINF);doubledualinf=GetDoubleInfo(GRB.Callback.BARRIER_DUALINF);doublecmpl=GetDoubleInfo(GRB.Callback.BARRIER_COMPL);Console.WriteLine(itcnt+" "+primobj+" "+dualobj+" "+priminf+" "+dualinf+" "+cmpl);}elseif(where==GRB.Callback.MESSAGE){// Message callbackstringmsg=GetStringInfo(GRB.Callback.MSG_STRING);if(msg!=null)logfile.Write(msg);}}catch(GRBExceptione){Console.WriteLine("Error code: "+e.ErrorCode);Console.WriteLine(e.Message);Console.WriteLine(e.StackTrace);}catch(Exceptione){Console.WriteLine("Error during callback");Console.WriteLine(e.StackTrace);}}staticvoidMain(string[]args){if(args.Length<1){Console.Out.WriteLine("Usage: callback_cs filename");return;}StreamWriterlogfile=null;try{// Create environmentGRBEnvenv=newGRBEnv();// Read model from fileGRBModelmodel=newGRBModel(env,args[0]);// Turn off display and heuristicsmodel.Parameters.OutputFlag=0;model.Parameters.Heuristics=0.0;// Open log filelogfile=newStreamWriter("cb.log");// Create a callback object and associate it with the modelGRBVar[]vars=model.GetVars();callback_cscb=newcallback_cs(vars,logfile);model.SetCallback(cb);// Solve model and capture solution informationmodel.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(intj=0;j<vars.Length;j++){if(x[j]!=0.0)Console.WriteLine(vnames[j]+" "+x[j]);}}// Dispose of model and environmentmodel.Dispose();env.Dispose();}catch(GRBExceptione){Console.WriteLine("Error code: "+e.ErrorCode);Console.WriteLine(e.Message);Console.WriteLine(e.StackTrace);}catch(Exceptione){Console.WriteLine("Error during optimization");Console.WriteLine(e.Message);Console.WriteLine(e.StackTrace);}finally{// Close log fileif(logfile!=null)logfile.Close();}}}
/* Copyright 2025, 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.*/importcom.gurobi.gurobi.*;importjava.io.FileWriter;importjava.io.IOException;publicclassCallbackextendsGRBCallback{privatedoublelastiter;privatedoublelastnode;privateGRBVar[]vars;privateFileWriterlogfile;publicCallback(GRBVar[]xvars,FileWriterxlogfile){lastiter=lastnode=-GRB.INFINITY;vars=xvars;logfile=xlogfile;}protectedvoidcallback(){try{if(where==GRB.CB_POLLING){// Ignore polling callback}elseif(where==GRB.CB_PRESOLVE){// Presolve callbackintcdels=getIntInfo(GRB.CB_PRE_COLDEL);intrdels=getIntInfo(GRB.CB_PRE_ROWDEL);if(cdels!=0||rdels!=0){System.out.println(cdels+" columns and "+rdels+" rows are removed");}}elseif(where==GRB.CB_SIMPLEX){// Simplex callbackdoubleitcnt=getDoubleInfo(GRB.CB_SPX_ITRCNT);if(itcnt-lastiter>=100){lastiter=itcnt;doubleobj=getDoubleInfo(GRB.CB_SPX_OBJVAL);intispert=getIntInfo(GRB.CB_SPX_ISPERT);doublepinf=getDoubleInfo(GRB.CB_SPX_PRIMINF);doubledinf=getDoubleInfo(GRB.CB_SPX_DUALINF);charch;if(ispert==0)ch=' ';elseif(ispert==1)ch='S';elsech='P';System.out.println(itcnt+" "+obj+ch+" "+pinf+" "+dinf);}}elseif(where==GRB.CB_MIP){// General MIP callbackdoublenodecnt=getDoubleInfo(GRB.CB_MIP_NODCNT);doubleobjbst=getDoubleInfo(GRB.CB_MIP_OBJBST);doubleobjbnd=getDoubleInfo(GRB.CB_MIP_OBJBND);intsolcnt=getIntInfo(GRB.CB_MIP_SOLCNT);if(nodecnt-lastnode>=100){lastnode=nodecnt;intactnodes=(int)getDoubleInfo(GRB.CB_MIP_NODLFT);intitcnt=(int)getDoubleInfo(GRB.CB_MIP_ITRCNT);intcutcnt=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();}}elseif(where==GRB.CB_MIPSOL){// MIP solution callbackintnodecnt=(int)getDoubleInfo(GRB.CB_MIPSOL_NODCNT);doubleobj=getDoubleInfo(GRB.CB_MIPSOL_OBJ);intsolcnt=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]+" ****");}elseif(where==GRB.CB_MIPNODE){// MIP node callbackSystem.out.println("**** New node ****");if(getIntInfo(GRB.CB_MIPNODE_STATUS)==GRB.OPTIMAL){double[]x=getNodeRel(vars);setSolution(vars,x);}}elseif(where==GRB.CB_BARRIER){// Barrier callbackintitcnt=getIntInfo(GRB.CB_BARRIER_ITRCNT);doubleprimobj=getDoubleInfo(GRB.CB_BARRIER_PRIMOBJ);doubledualobj=getDoubleInfo(GRB.CB_BARRIER_DUALOBJ);doublepriminf=getDoubleInfo(GRB.CB_BARRIER_PRIMINF);doubledualinf=getDoubleInfo(GRB.CB_BARRIER_DUALINF);doublecmpl=getDoubleInfo(GRB.CB_BARRIER_COMPL);System.out.println(itcnt+" "+primobj+" "+dualobj+" "+priminf+" "+dualinf+" "+cmpl);}elseif(where==GRB.CB_MESSAGE){// Message callbackStringmsg=getStringInfo(GRB.CB_MSG_STRING);if(msg!=null)logfile.write(msg);}}catch(GRBExceptione){System.out.println("Error code: "+e.getErrorCode());System.out.println(e.getMessage());e.printStackTrace();}catch(Exceptione){System.out.println("Error during callback");e.printStackTrace();}}publicstaticvoidmain(String[]args){if(args.length<1){System.out.println("Usage: java Callback filename");System.exit(1);}FileWriterlogfile=null;try{// Create environmentGRBEnvenv=newGRBEnv();// Read model from fileGRBModelmodel=newGRBModel(env,args[0]);// Turn off display and heuristicsmodel.set(GRB.IntParam.OutputFlag,0);model.set(GRB.DoubleParam.Heuristics,0.0);// Open log filelogfile=newFileWriter("cb.log");// Create a callback object and associate it with the modelGRBVar[]vars=model.getVars();Callbackcb=newCallback(vars,logfile);model.setCallback(cb);// Solve model and capture solution informationmodel.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(intj=0;j<vars.length;j++){if(x[j]!=0.0)System.out.println(vnames[j]+" "+x[j]);}}// Dispose of model and environmentmodel.dispose();env.dispose();}catch(GRBExceptione){System.out.println("Error code: "+e.getErrorCode());System.out.println(e.getMessage());e.printStackTrace();}catch(Exceptione){System.out.println("Error during optimization");e.printStackTrace();}finally{// Close log fileif(logfile!=null){try{logfile.close();}catch(IOExceptione){}}}}}
#!/usr/bin/env python3# Copyright 2025, 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.importsysfromfunctoolsimportpartialimportgurobipyasgpfromgurobipyimportGRBclassCallbackData:def__init__(self,modelvars):self.modelvars=modelvarsself.lastiter=-GRB.INFINITYself.lastnode=-GRB.INFINITYdefmycallback(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. """ifwhere==GRB.Callback.POLLING:# Ignore polling callbackpasselifwhere==GRB.Callback.PRESOLVE:# Presolve callbackcdels=model.cbGet(GRB.Callback.PRE_COLDEL)rdels=model.cbGet(GRB.Callback.PRE_ROWDEL)ifcdelsorrdels:print(f"{cdels} columns and{rdels} rows are removed")elifwhere==GRB.Callback.SIMPLEX:# Simplex callbackitcnt=model.cbGet(GRB.Callback.SPX_ITRCNT)ifitcnt-cbdata.lastiter>=100:cbdata.lastiter=itcntobj=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)ifispert==0:ch=" "elifispert==1:ch="S"else:ch="P"print(f"{int(itcnt)}{obj:g}{ch}{pinf:g}{dinf:g}")elifwhere==GRB.Callback.MIP:# General MIP callbacknodecnt=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)ifnodecnt-cbdata.lastnode>=100:cbdata.lastnode=nodecntactnodes=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}")ifabs(objbst-objbnd)<0.1*(1.0+abs(objbst)):print("Stop early - 10% gap achieved")model.terminate()ifnodecnt>=10000andsolcnt:print("Stop early - 10000 nodes explored")model.terminate()elifwhere==GRB.Callback.MIPSOL:# MIP solution callbacknodecnt=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} ****")elifwhere==GRB.Callback.MIPNODE:# MIP node callbackprint("**** New node ****")ifmodel.cbGet(GRB.Callback.MIPNODE_STATUS)==GRB.OPTIMAL:x=model.cbGetNodeRel(cbdata.modelvars)model.cbSetSolution(cbdata.modelvars,x)elifwhere==GRB.Callback.BARRIER:# Barrier callbackitcnt=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}")elifwhere==GRB.Callback.MESSAGE:# Message callbackmsg=model.cbGet(GRB.Callback.MSG_STRING)logfile.write(msg)# Parse argumentsiflen(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.withgp.Env(params={"OutputFlag":0,"Heuristics":0})asenv,gp.read(model_file,env=env)asmodel,open("cb.log","w")aslogfile:# Set up callback function with required argumentscallback_data=CallbackData(model.getVars())callback_func=partial(mycallback,cbdata=callback_data,logfile=logfile)# Solve model and print solution informationmodel.optimize(callback_func)print("")print("Optimization complete")ifmodel.SolCount==0:print(f"No solution found, optimization status ={model.Status}")else:print(f"Solution found, objective ={model.ObjVal:g}")forvinmodel.getVars():ifv.X!=0.0:print(f"{v.VarName}{v.X:g}")
' Copyright 2025, 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.ImportsSystemImportsGurobiClasscallback_vbInheritsGRBCallbackPrivatevarsAsGRBVar()PrivatelastnodeAsDoublePrivatelastiterAsDoublePublicSubNew(ByValxvarsAsGRBVar())vars=xvarslastnode=lastiter=-1EndSubProtectedOverloadsOverridesSubCallback()TryIfwhere=GRB.Callback.PRESOLVEThen' Presolve callbackDimcdelsAsInteger=GetIntInfo(GRB.Callback.PRE_COLDEL)DimrdelsAsInteger=GetIntInfo(GRB.Callback.PRE_ROWDEL)Console.WriteLine(cdels&" columns and "&rdels&" rows are removed")ElseIfwhere=GRB.Callback.SIMPLEXThen' Simplex callbackDimitcntAsDouble=GetDoubleInfo(GRB.Callback.SPX_ITRCNT)IfitcntMod-lastiter>=100Thenlastiter=itcntDimobjAsDouble=GetDoubleInfo(GRB.Callback.SPX_OBJVAL)DimpinfAsDouble=GetDoubleInfo(GRB.Callback.SPX_PRIMINF)DimdinfAsDouble=GetDoubleInfo(GRB.Callback.SPX_DUALINF)DimispertAsInteger=GetIntInfo(GRB.Callback.SPX_ISPERT)DimchAsCharIfispert=0Thench=" "cElseIfispert=1Thench="S"cElsech="P"cEndIfConsole.WriteLine(itcnt&" "&obj&ch&" "&pinf&" "&dinf)EndIfElseIfwhere=GRB.Callback.MIPThen' General MIP callbackDimnodecntAsDouble=GetDoubleInfo(GRB.Callback.MIP_NODCNT)Ifnodecnt-lastnode>=100Thenlastnode=nodecntDimobjbstAsDouble=GetDoubleInfo(GRB.Callback.MIP_OBJBST)DimobjbndAsDouble=GetDoubleInfo(GRB.Callback.MIP_OBJBND)IfMath.Abs(objbst-objbnd)<0.1*(1.0R+Math.Abs(objbst))ThenAbort()EndIfDimactnodesAsInteger=CInt(GetDoubleInfo(GRB.Callback.MIP_NODLFT))DimitcntAsInteger=CInt(GetDoubleInfo(GRB.Callback.MIP_ITRCNT))DimsolcntAsInteger=GetIntInfo(GRB.Callback.MIP_SOLCNT)DimcutcntAsInteger=GetIntInfo(GRB.Callback.MIP_CUTCNT)Console.WriteLine(nodecnt&" "&actnodes&" "&itcnt&" "&_objbst&" "&objbnd&" "&solcnt&" "&cutcnt)EndIfElseIfwhere=GRB.Callback.MIPSOLThen' MIP solution callbackDimobjAsDouble=GetDoubleInfo(GRB.Callback.MIPSOL_OBJ)DimnodecntAsInteger=CInt(GetDoubleInfo(GRB.Callback.MIPSOL_NODCNT))DimxAsDouble()=GetSolution(vars)Console.WriteLine("**** New solution at node "&nodecnt&", obj "&_obj&", x(0) = "&x(0)&"****")EndIfCatcheAsGRBExceptionConsole.WriteLine("Error code: "&e.ErrorCode&". "&e.Message)Console.WriteLine(e.StackTrace)EndTryEndSubSharedSubMain(ByValargsAsString())Ifargs.Length<1ThenConsole.WriteLine("Usage: callback_vb filename")ReturnEndIfTryDimenvAsNewGRBEnv()DimmodelAsNewGRBModel(env,args(0))DimvarsAsGRBVar()=model.GetVars()' Create a callback object and associate it with the modelmodel.SetCallback(Newcallback_vb(vars))model.Optimize()DimxAsDouble()=model.Get(GRB.DoubleAttr.X,vars)DimvnamesAsString()=model.Get(GRB.StringAttr.VarName,vars)ForjAsInteger=0Tovars.Length-1Ifx(j)<>0.0RThenConsole.WriteLine(vnames(j)&" "&x(j))EndIfNext' Dispose of model and envmodel.Dispose()env.Dispose()CatcheAsGRBExceptionConsole.WriteLine("Error code: "&e.ErrorCode&". "&e.Message)Console.WriteLine(e.StackTrace)EndTryEndSubEndClass
Help and Feedback