FICO
FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious exampleNext example

Branching rule branching on the most violated Integer/Binary

Description
Demonstrates the Xpress Optimizer change branch callbacks

Further explanation of this example: 'Xpress Optimizer Reference Manual', Section 5.6 Using the Callbacks

mostviolated.zip[download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
mostviolated.c[download]





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-2024 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;
  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( XPRSgetpresolvesol(prob, (*nodeData).dSolution, NULL, NULL,
                                   NULL) );
  /* 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 */

  /* Check number of arguments and tell user to add an input file otherwise. */
  if (argc != 2) {
    printf("syntax: mostviolated <filename> \n");
    return 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, argv[1], "") );

  printf("Start solving problem '%s'.\n", argv[1]);
  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;
  }
}

Back to examples browserPrevious exampleNext example