Hide navigation sidebar
Hide table of contents sidebar
Back to top
Toggle table of contents sidebar

# callback_c.c

    /* 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 */

    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;
    }

Help and Feedback

Press `/` or click to search this documentation | Gurobi website | Gurobi community forum
Go to other Gurobi documentation | Gurobi download | Contact support