Initializing help system before first use

Branching rule branching on the most violated Integer/Binary


Type: Programming
Rating: 4 (medium-difficult)
Description: Demonstrates the Xpress Optimizer change branch callbacks
File(s): mostviolated.c


mostviolated.c
/***********************************************************************
   Xpress Optimizer Examples
   =========================

   file mostviolated.c
   ```````````````````
   A Branching rule branching on the most violated Integer/Binary

   Demonstrate the Xpress-MP change branch callbacks.

   (c) 2017-2025 Fair Isaac Corporation
***********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "xprs.h"

/* Calls an Xpress optimizer function and checks the return code.
 * If the call fails then the function
 * - prints a short error message to stderr,
 * - sets variable 'returnCode' to the error,
 * - and branches to label 'cleanup'.
 */
#define CHECK_RETURN(call) do {                         \
    int result_ = call;                                 \
    if ( result_ != 0 ) {                               \
      fprintf(stderr, "Line %d: %s failed with %d\n",   \
              __LINE__, #call, result_);                \
      returnCode = result_;                             \
      goto cleanup;                                     \
    }                                                   \
  } while (0)

static int callbackError = 0;       /* Indicates an error in a callback. */

typedef struct {
  double* dSolution;
  char* cType;
  double dTolerance;
  int nColumns;
} solutionData;

static void XPRS_CC messagecb(XPRSprob cbprob, void* cbdata,
                              const char *msg, int len, int msgtype);

/* Branch on the most fractional integer */

static void XPRS_CC variableSelection(XPRSprob prob, void* data, XPRSbranchobject branch, XPRSbranchobject* p_newbranch)
{
  int returnCode = 0;
  double dDist, dUpDist, dDownDist,  dGreatestDist;
  int iCol, nCols;
  int branchCol = -1;
  solutionData *nodeData = (solutionData*)data;
  XPRSbranchobject newbranch = NULL;

  (void)branch; /* Not interested in what Xpress has planned. */

  /* Get solution in the presolved space. */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCols) );
  CHECK_RETURN( XPRSgetcallbackpresolvesolution(prob, NULL, (*nodeData).dSolution, 0, nCols - 1) );
  /* Find the most fractional column. */
  dGreatestDist = 0;
  for( iCol = 0; iCol < nodeData->nColumns; iCol++ ) {
    if( nodeData->cType[iCol] == 'I' || nodeData->cType[iCol] == 'B' ) {
      dUpDist = ceil(nodeData->dSolution[iCol]) -
        nodeData->dSolution[iCol];
      dDownDist = nodeData->dSolution[iCol] -
        floor(nodeData->dSolution[iCol]);
      dDist = (dUpDist>dDownDist)?dUpDist:dDownDist;
      if( dDownDist > nodeData->dTolerance && dUpDist >
          nodeData->dTolerance )
		if( dDist > dGreatestDist ) {
          branchCol = iCol;
          dGreatestDist = dDist;
		}
    }
  }

  /* If we found a column to branch on then create a branching object
   * (in the presolved space) that reflects branching on that column and
   * return it to the caller.
   */
  if (branchCol >= 0) {
    double bndU = floor(nodeData->dSolution[branchCol]);
    double bndL = ceil(nodeData->dSolution[branchCol]);
    if ( XPRS_bo_create(&newbranch, prob, 0) ||
         XPRS_bo_addbranches(newbranch, 2) ||
         XPRS_bo_addbounds(newbranch, 0, 1, "U", &branchCol, &bndU) ||
         XPRS_bo_addbounds(newbranch, 1, 1, "L", &branchCol, &bndL) )
      goto cleanup;
    *p_newbranch = newbranch;
    newbranch = NULL;
  }

 cleanup:
  if (newbranch != NULL)
    XPRS_bo_destroy(newbranch);
  if (returnCode) {
    /* There was an error in the callback. We have to stop the optimizer
     * and also set a global flag that indicates that failure.
     */
    int errorCode = -1;
    char errorMessage[512] = {0};
    XPRSgetintattrib(prob, XPRS_ERRORCODE, &errorCode);
    XPRSgetlasterror(prob, errorMessage);
    fprintf(stderr, "Error %d in callback: %s\n", errorCode, errorMessage);
    callbackError = 1;
    XPRSinterrupt(prob, XPRS_STOP_USER);
  }
}


int main( int argc, char *argv[] )
{
  int returnCode = 0;
  XPRSprob prob = NULL;
  FILE* logFile = NULL;
  solutionData nodeData = {0};
  char message[512];                         /* For early error messages */
  char const *modelfile = "../data/burglar"; /* Default model file. */

  /* Check number of arguments and tell user to add an input file otherwise. */
  if (argc != 1 && argc != 2) {
    printf("syntax: mostviolated [filename] \n");
    return 1;
  }
  if (argc == 2)
    modelfile = argv[1];

  logFile = fopen("mvb.log","w+");
  if ( logFile == NULL ) {
    perror("fopen");
    return 1;
  }

  /* Initialize the optimizer. */
  if ( XPRSinit("") != 0 ) {
    XPRSgetlicerrmsg(message, sizeof(message));
    fprintf(stderr, "Licensing error: %s\n", message);
    return 1;
  }

  /* Create a new problem and immediately register a message handler.
   * Once we have a message handler installed, errors will produce verbose
   * error messages on the console and we can limit ourselves to minimal
   * error handling in the code here.
   */
  CHECK_RETURN( XPRScreateprob(&prob) );
  CHECK_RETURN( XPRSaddcbmessage(prob, messagecb, NULL, 0) );

  CHECK_RETURN( XPRSreadprob(prob, modelfile, "") );

  /* Turn off dual presolve reductions for this example, otherwise the problem
     is reduced to an empty problem.
   */
  CHECK_RETURN( XPRSsetintcontrol(prob, XPRS_MIPDUALREDUCTIONS, 0) );

  printf("Start solving problem '%s'.\n", modelfile);
  CHECK_RETURN( XPRSmipoptimize(prob,"l") );
  if (callbackError) {
    returnCode = -3;
    goto cleanup;
  }

  CHECK_RETURN( XPRSsetintcontrol(prob, XPRS_MIPLOG, 3) );
  CHECK_RETURN( XPRSsetintcontrol(prob, XPRS_CUTSTRATEGY, 0) );

  /* setup data */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &(nodeData.nColumns)) );
  CHECK_RETURN( XPRSgetdblcontrol(prob, XPRS_MATRIXTOL, &(nodeData.dTolerance))
    );
  nodeData.dSolution = malloc(sizeof(double)*nodeData.nColumns);
  nodeData.cType = malloc(sizeof(char)*nodeData.nColumns);
  if (!nodeData.dSolution || !nodeData.cType) {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }
  CHECK_RETURN( XPRSgetcoltype(prob, nodeData.cType, 0, nodeData.nColumns-1) );

  CHECK_RETURN( XPRSaddcbchgbranchobject(prob, variableSelection, &nodeData, 0) );

  CHECK_RETURN( XPRSmipoptimize(prob,"") );
  if (callbackError) {
    returnCode = -3;
    goto cleanup;
  }

 cleanup:
  if (returnCode > 0) {
    /* There was an error with the solver. Get the error code and error message.
     * If prob is still NULL then the error was in XPRScreateprob() and
     * we cannot find more detailed error information.
     */
    if (prob != NULL) {
      int errorCode = -1;
      char errorMessage[512];
      XPRSgetintattrib(prob, XPRS_ERRORCODE, &errorCode);
      XPRSgetlasterror(prob, errorMessage);
      fprintf(stderr, "Error %d: %s\n", errorCode, errorMessage);
    }
  }

  /* Free the resources (variables are initialized so that this is valid
   * even in case of error).
   */

  free(nodeData.dSolution);
  free(nodeData.cType);

  XPRSfree();
  printf("Solving complete.\n");
  if (logFile != NULL)
    fclose(logFile);
  return returnCode;
}

/* XPRS optimizer message callback */
void XPRS_CC messagecb(XPRSprob cbprob, void* cbdata,
                       const char *msg, int len, int msgtype)
{
  (void)cbprob;   /* unused (the problem for which the message is issued) */
  (void)cbdata;   /* unused (the data passed to XPRSaddcbmessage()) */
  switch(msgtype)
  {
  case 4:  /* error */
  case 3:  /* warning */
  case 2:  /* not used */
  case 1:  /* information */
    printf("%*s\n", len, msg);
    break;
  default: /* exiting - buffers need flushing */
    fflush(stdout);
    break;
  }
}

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.