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

Goal Programming

Description
Implementation of preemptive and Archimedian Goal Programming algorithms working on objective functions or constraints. The Goal Programming directives are input from the configuration files *.gol.


Source Files

Data Files





goal_example.c

/***********************************************************************
   Xpress Optimizer Examples
   =========================

   file goal_example.c
   ```````````````````
   Demonstrates how to implement traditional goal programming
   using Xpress.

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

#include <stdlib.h>
#include <stdio.h>
#include "xprs.h"

#include <math.h>
#include <memory.h>
#include <string.h>
#include <ctype.h>

#if defined(WIN32) || defined(_WIN32)
#else
#define strcmpi stricmp
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif

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

/* XPRS optimizer message callback */
static 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;
  }
}

struct GoalConstraint_s {
  double dCost;
  int iRow;
};
typedef struct GoalConstraint_s * GoalConstraint;

struct GoalObjective_s {
  double dTolerance;
  int iRow;
  int iOptimizationSense;
  int bFractionIsPercentage;
};
typedef struct GoalObjective_s * GoalObjective;

struct SlackPair_s {
  int iSlack_Pos;
  int iSlack_Neg;
};
typedef struct SlackPair_s * SlackPair;

struct Goal_s {
  int bArchimedianOtherwisePreemptive;
  int bConstraintsOtherwiseObjective;
  GoalConstraint gc;
  int gc_count;
  GoalObjective go;
  int go_count;
};
typedef struct Goal_s * Goal;

static int AddSlack(XPRSprob prob, int iRow, int iSlackSign,
                    double dSlackCost, int *iNewColIndex)
{
  int returnCode = 0;
  double obj = dSlackCost, dmatval = (iSlackSign > 0 ? 1.0 : -1.0);
  double bdl = 0.0, bdu = XPRS_PLUSINFINITY;
  int mrwind = iRow, mstart[] = {0, 1};

  /* Slack will go on the end of the column set */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, iNewColIndex) );
  CHECK_RETURN( XPRSaddcols(prob, 1 /*newcol*/, 1 /*newnz*/, &obj, mstart,
                            &mrwind, &dmatval, &bdl, &bdu) );
 cleanup:
  return returnCode;
}

static int GcArray_AddRecord(GoalConstraint *gc, int *gc_count)
{
  int returnCode = 0;
  if(!*gc) {
    if(!(*gc = malloc(sizeof(**gc) * (*gc_count + 1)))) {
      perror("malloc");
      returnCode = -2;
      goto cleanup;
    }
  } else {
    GoalConstraint gc_temp;
    if(!(gc_temp = realloc(*gc, sizeof(**gc) * (*gc_count + 1)))) {
      perror("realloc");
      returnCode = -2;
      goto cleanup;
    }
    *gc = gc_temp;
  }
  (*gc_count)++;
 cleanup:
  return returnCode;
}

static int GoArray_AddRecord(GoalObjective *go, int *go_count)
{
  int returnCode = 0;
  if(!*go) {
    if(!(*go = malloc(sizeof(**go) * (*go_count + 1)))) {
      perror("malloc");
      returnCode = -2;
      goto cleanup;
    }
  } else {
    GoalObjective go_temp;
    if(!(go_temp = realloc(*go, sizeof(**go) * (*go_count + 1)))) {
      perror("realloc");
      returnCode = -2;
      goto cleanup;
    }
    *go = go_temp;
  }
  (*go_count)++;
 cleanup:
  return returnCode;
}

static int SpArray_AddRecord(SlackPair *sp, int *sp_count)
{
  int returnCode = 0;
  if(!*sp) {
    if(!(*sp = malloc(sizeof(**sp) * (*sp_count + 1)))) {
      perror("malloc");
      returnCode = -2;
      goto cleanup;
    }
  } else {
    SlackPair sp_temp;
    if(!(sp_temp = realloc(*sp, sizeof(**sp) * (*sp_count + 1)))) {
      perror("realloc");
      returnCode = -2;
      goto cleanup;
    }
    *sp = sp_temp;
  }
  (*sp_count)++;
 cleanup:
  return returnCode;
}

static int RelaxConstraint(XPRSprob prob, int iRow,
                           int bArchimedianOtherwisePreemptive,
                           double dArchimedianWeight, SlackPair *sp,
                           int * sp_count)
{
  int returnCode = 0;
  char qrtype;
  double dCost = (bArchimedianOtherwisePreemptive ? dArchimedianWeight : 0.0);
  SlackPair sp_;
  CHECK_RETURN( XPRSgetrowtype(prob, &qrtype, iRow, iRow) );
  CHECK_RETURN( SpArray_AddRecord(sp, sp_count) );
  sp_ = &(*sp)[*sp_count - 1];
  sp_->iSlack_Pos = -1;
  sp_->iSlack_Neg = -1;
  switch(qrtype) {
  case('N') :
    break;
  case('L') :
  case('P') :
    AddSlack(prob, iRow, -1 /*iSlackSign*/, dCost, &sp_->iSlack_Neg);
    break;
  case('G') :
  case('Q') :
    AddSlack(prob, iRow,  1 /*iSlackSign*/, dCost, &sp_->iSlack_Pos);
    break;
  case('E') :
  case('R') :
    AddSlack(prob, iRow,  1 /*iSlackSign*/, dCost, &sp_->iSlack_Pos);
    AddSlack(prob, iRow, -1 /*iSlackSign*/, dCost, &sp_->iSlack_Neg);
    break;
  default :
    fprintf(stderr, "Unexpected row type '%c'\n", qrtype);
    returnCode = -1;
    goto cleanup;
  }

 cleanup:
  return returnCode;
}

static int RunGoal_Preemptive_Constraint(XPRSprob prob, int bRunMip,
                                         GoalConstraint GoalRows,
                                         int iGoalRows_Count,
                                         int *bIsInfeasible)
{
  int returnCode = 0;
  int i, j, nRows, nCols, iStatus, bHaveBasis;
  double dObjVal, dZero = 0.0, dOne = 1.0;
  SlackPair sp = NULL;
  int sp_count = 0;
  char cBoth = 'B';
  int *rstatus = NULL, *cstatus = NULL;
  int bWriteGoalProbs = 0;

  *bIsInfeasible = 0;

  /* Ensure the problem is in the original state */
  CHECK_RETURN( XPRSpostsolve(prob) );

  /* Add the appropriate slacks for the goals */
  for(i = 0; i < iGoalRows_Count; i++) {
    CHECK_RETURN( RelaxConstraint(prob, GoalRows[i].iRow,
                                  0 /*bArchimedianOtherwisePreemptive*/,
                                  0.0 /*dArchimedianWeight*/,
                                  &sp, &sp_count) );
  }

  /* Setup arrays to store basis and zero all costs */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_ROWS, &nRows) );
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCols) );

  if(!(rstatus = malloc(sizeof(*rstatus) * nRows)) ||
     !(cstatus = malloc(sizeof(*cstatus) * nCols))) {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }

  for(j = 0; j < nCols; j++) {
    CHECK_RETURN( XPRSchgobj(prob, 1, &j, &dZero) );
  }

  if(bWriteGoalProbs) {
    CHECK_RETURN( XPRSwriteprob(prob, "goal\\pc_goal0", "") );
  }

  /* Loop through the goal constraints */
  bHaveBasis = 0;
  for(i = 0; i < sp_count; i++) {

    /* Skip non-binding goal rows */
    if(sp[i].iSlack_Neg < 0 && sp[i].iSlack_Pos < 0) continue;

    /* Set the slack cost(s) on the goal row to 1.0 */
    if(sp[i].iSlack_Neg >= 0) CHECK_RETURN( XPRSchgobj(prob, 1,
                                                       &sp[i].iSlack_Neg, &dOne) );
    if(sp[i].iSlack_Pos >= 0) CHECK_RETURN( XPRSchgobj(prob, 1,
                                                       &sp[i].iSlack_Pos, &dOne) );

    if(bHaveBasis) {
      /* We have a basis from the last goal solve use this to hot start this
         solve */
      CHECK_RETURN( XPRSloadbasis(prob, rstatus, cstatus) );
    }

    if(bWriteGoalProbs) {
      char buff[256];
      sprintf(buff, "goal\\pc_goal%i", i + 1);
      CHECK_RETURN( XPRSwriteprob(prob, buff, "") );
    }

    /* Solve the LP and get the solve status */
    CHECK_RETURN( XPRSmipoptimize(prob, "l") );
    CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPSTATUS, &iStatus) );

    /* Exit if the problem is infeasible even with the slacks we have added to
       the rows */
    if(iStatus != XPRS_MIP_OPTIMAL) {
      *bIsInfeasible = 1;
      goto cleanup;
    }

    CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_LPOBJVAL, &dObjVal) );
    if(dObjVal != 0.0) {
      /* Exit if we can't satisfy the current goal constraint */
      *bIsInfeasible = 1;
      goto cleanup;
    }

    /* Update the hot start basis */
    if(!bHaveBasis) {
      CHECK_RETURN( XPRSgetbasis(prob, rstatus, cstatus) );
      bHaveBasis = 1;
    }

    /* Run the MIP search if required */
    if(bRunMip) {
      /* Solve the MIP search and get the solve status */
      CHECK_RETURN( XPRSmipoptimize(prob, "") );
      CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPSTATUS, &iStatus) );

      /* Exit if the problem is infeasible even with the slacks we have added
         to the rows */
      if(iStatus != XPRS_MIP_OPTIMAL) {
        *bIsInfeasible = 1;
        goto cleanup;
      }

      CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_MIPOBJVAL, &dObjVal) );
      if(dObjVal != 0.0) {
        /* Exit if we can't satisfy the current goal constraint */
        *bIsInfeasible = 1;
        goto cleanup;
      }
    }

    /*
      Restore the problem to the original state and fix the
      slacks on the current goal row to zero ready to process
      the next goal row.
    */
    CHECK_RETURN( XPRSpostsolve(prob) );
    if(sp[i].iSlack_Neg >= 0) CHECK_RETURN( XPRSchgbounds(prob, 1,
                                                          &sp[i].iSlack_Neg, &cBoth, &dZero) );
    if(sp[i].iSlack_Pos >= 0) CHECK_RETURN( XPRSchgbounds(prob, 1,
                                                          &sp[i].iSlack_Pos, &cBoth, &dZero) );

  }

 cleanup:
  free(sp);
  free(rstatus);
  free(cstatus);
  return returnCode;
}

static int RunGoal_Preemptive_Objective(XPRSprob prob, int bRunMip,
                                        GoalObjective GoalObjs,
                                        int iGoalGoalObjs_Count,
                                        int *bIsInfeasible, double *dObjRhs_)
{
  int returnCode = 0;
  int i, j, nRows, nCols, iStatus, bHaveBasis, ncoeffs;
  double dObjVal, dZero = 0.0;
  int *rstatus = NULL, *cstatus = NULL;
  int bWriteGoalProbs = 0;
  int mstart[2];
  int *mclind = NULL;
  double *dcmatval = NULL;
  double dRhs, dMatRhs, dMatObRhs;

  *bIsInfeasible = 0;

  /* Ensure the problem is in the original state */
  CHECK_RETURN( XPRSpostsolve(prob) );

  /* Setup arrays to store basis and zero all costs */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_ROWS, &nRows) );
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCols) );

  if(!(rstatus = malloc(sizeof(*rstatus) * nRows)) ||
     !(cstatus = malloc(sizeof(*cstatus) * nCols)) ||
     !(mclind = malloc(sizeof(*mclind) * nCols)) ||
     !(dcmatval = malloc(sizeof(*dcmatval) * nCols)))
  {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }

  if(bWriteGoalProbs) {
    CHECK_RETURN( XPRSwriteprob(prob, "goal\\po_goal0", "") );
  }

  /* Loop through the goal objectives */
  bHaveBasis = 0;
  for(i = 0; i < iGoalGoalObjs_Count; i++) {

    CHECK_RETURN( XPRSgetrhs(prob, &dMatRhs, GoalObjs[i].iRow,
                             GoalObjs[i].iRow) );
    CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_OBJRHS, &dMatObRhs) );

    /* Copy the row coefficients into the objective */
    CHECK_RETURN( XPRSgetrows(prob, mstart, mclind, dcmatval, nCols, &ncoeffs,
                              GoalObjs[i].iRow, GoalObjs[i].iRow) );
    for(j = 0; j < nCols; j++) {
      CHECK_RETURN( XPRSchgobj(prob, 1, &j, &dZero) );
    }
    CHECK_RETURN( XPRSchgobj(prob, ncoeffs, mclind, dcmatval) );

    if(bHaveBasis) {
      /* We have a basis from the last goal solve use this to hot start this
         solve */
      CHECK_RETURN( XPRSloadbasis(prob, rstatus, cstatus) );
    }

    if(bRunMip) {
      /* Ensure that we restart the MIP solve each time */
      CHECK_RETURN( XPRSpostsolve(prob) );
    }

    if(bWriteGoalProbs) {
      char buff[256];
      sprintf(buff, "goal\\po_goal%i", i + 1);
      CHECK_RETURN( XPRSwriteprob(prob, buff, "") );
    }

    /* Solve the LP and get the solve status */
    if(GoalObjs[i].iOptimizationSense >= 0) {
      CHECK_RETURN( XPRSchgobjsense(prob, XPRS_OBJ_MINIMIZE) );
    } else {
      CHECK_RETURN( XPRSchgobjsense(prob, XPRS_OBJ_MAXIMIZE) );
    }
	CHECK_RETURN( XPRSmipoptimize(prob, "l") );
    CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPSTATUS, &iStatus) );

    /* Exit if the problem is infeasible even with the slacks we have added to
       the rows */
    if(iStatus != XPRS_MIP_LP_OPTIMAL) {
      *bIsInfeasible = 1;
      goto cleanup;
    }

    CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_LPOBJVAL, &dObjVal) );

    /* Update the hot start basis */
    if(!bHaveBasis) {
      CHECK_RETURN( XPRSgetbasis(prob, rstatus, cstatus) );
      bHaveBasis = 1;
    }

    /* Run the MIP search if required */
    if(bRunMip) {
      /* Solve the MIP search and get the solve status */
      CHECK_RETURN( XPRSmipoptimize(prob, "") );
      CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPSTATUS, &iStatus) );

      /* Exit if the problem is infeasible even with the slacks we have added
         to the rows */
      if(iStatus != XPRS_MIP_OPTIMAL) {
        *bIsInfeasible = 1;
        goto cleanup;
      }

      CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_MIPOBJVAL, &dObjVal) );
    }

    /* Constraint the row to the objective we have calculated */
    dObjVal -= dMatRhs + dMatObRhs;
    if(GoalObjs[i].iOptimizationSense >= 0) {
      char qLE = 'L';
      CHECK_RETURN( XPRSchgrowtype(prob, 1, &GoalObjs[i].iRow, &qLE) );
      if(GoalObjs[i].bFractionIsPercentage) {
        dRhs = dObjVal + fabs(dObjVal) * GoalObjs[i].dTolerance * 0.01 +
          dMatRhs;
      } else {
        dRhs = dObjVal + GoalObjs[i].dTolerance + dMatRhs;
      }
    } else {
      char qGE = 'G';
      CHECK_RETURN( XPRSchgrowtype(prob, 1, &GoalObjs[i].iRow, &qGE) );
      if(GoalObjs[i].bFractionIsPercentage) {
        dRhs = dObjVal - fabs(dObjVal) * GoalObjs[i].dTolerance * 0.01 +
          dMatRhs;
      } else {
        dRhs = dObjVal - GoalObjs[i].dTolerance + dMatRhs;
      }
    }
    if(dObjRhs_) *dObjRhs_ = dMatRhs + dMatObRhs;
    CHECK_RETURN( XPRSchgrhs(prob, 1, &GoalObjs[i].iRow, &dRhs) );

  }

 cleanup:
  free(rstatus);
  free(cstatus);
  free(mclind);
  free(dcmatval);
  return returnCode;
}

static int RunGoal_Archimedian_Constraint(XPRSprob prob, int bRunMip,
                                          GoalConstraint GoalRows,
                                          int iGoalRows_Count,
                                          int *bIsInfeasible)
{
  int returnCode = 0;
  int i, j, iStatus, nCols;
  double dZero = 0.0;
  SlackPair sp = NULL;
  int sp_count = 0;
  int bWriteGoalProbs = 0;

  *bIsInfeasible = 0;

  /* Ensure the problem is in the original state */
  CHECK_RETURN( XPRSpostsolve(prob) );

  /* Zero all costs */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCols) );
  for(j = 0; j < nCols; j++) {
    CHECK_RETURN( XPRSchgobj(prob, 1, &j, &dZero) );
  }

  /* Add the appropriate slacks for the goals (with associated costs for
     satisfying those goals) */
  for(i = 0; i < iGoalRows_Count; i++) {
    CHECK_RETURN( RelaxConstraint(prob, GoalRows[i].iRow,
                                  1 /*bArchimedianOtherwisePreemptive*/,
                                  GoalRows[i].dCost /*dArchimedianWeight*/,
                                  &sp, &sp_count) );
  }

  if(bWriteGoalProbs) {
    CHECK_RETURN( XPRSwriteprob(prob, "goal\\ac_goal", "") );
  }

  /* Solve the LP and get the solve status */
  CHECK_RETURN( XPRSlpoptimize(prob, "") );
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_LPSTATUS, &iStatus) );

  /* Exit if the problem is infeasible even with the slacks we have added to
     the rows */
  if(iStatus != XPRS_LP_OPTIMAL) {
    *bIsInfeasible = 1;
    goto cleanup;
  }

  /* Run the MIP search if required */
  if(bRunMip) {
    /* Solve the MIP search and get the solve status */
    CHECK_RETURN( XPRSmipoptimize(prob, "") );
    CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPSTATUS, &iStatus) );

    /* Exit if the problem is infeasible even with the slacks we have added to
       the rows */
    if(iStatus != XPRS_MIP_OPTIMAL) {
      *bIsInfeasible = 1;
      goto cleanup;
    }
  }

 cleanup:
  free(sp);
  return returnCode;
}

static int RunGoal_Archimedian_Objective(XPRSprob prob, int bRunMip,
                                         GoalObjective GoalObjs,
                                         int iGoalGoalObjs_Count,
                                         int *bIsInfeasible,
                                         double *dObjRhs_)
{
  int returnCode = 0;
  int i, j, nRows, nCols, iStatus, ncoeffs;
  double dObjVal;
  int bWriteGoalProbs = 0;
  int mstart[2];
  int *mclind = NULL;
  double *dcmatval = NULL;
  double *dcost = NULL;
  double dObjRhs, dMatRhs;

  *bIsInfeasible = 0;

  /* Ensure the problem is in the original state */
  CHECK_RETURN( XPRSpostsolve(prob) );

  /* Setup arrays to store basis and zero all costs */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_ROWS, &nRows) );
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCols) );

  if(!(mclind = malloc(sizeof(*mclind) * nCols)) ||
     !(dcmatval = malloc(sizeof(*dcmatval) * nCols)) ||
     !(dcost = malloc(sizeof(*dcost) * nCols)))
  {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }
  memset(dcost, 0, sizeof(double) * nCols);

  /* Loop through the goal objectives and sum up the row coefficients into the
     cost function */
  CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_OBJRHS, &dObjRhs) );
  for(i = 0; i < iGoalGoalObjs_Count; i++) {

    CHECK_RETURN( XPRSgetrows(prob, mstart, mclind, dcmatval, nCols, &ncoeffs,
                              GoalObjs[i].iRow, GoalObjs[i].iRow) );

    for(j = 0; j < ncoeffs; j++) {
      dcost[mclind[j]] += dcmatval[j] * GoalObjs[i].dTolerance *
        GoalObjs[i].iOptimizationSense;
    }

    CHECK_RETURN( XPRSgetrhs(prob, &dMatRhs, GoalObjs[i].iRow,
                             GoalObjs[i].iRow) );

    dObjRhs += dMatRhs * GoalObjs[i].dTolerance *
      GoalObjs[i].iOptimizationSense;

  }

  /* Add the new cost function to the problem */
  for(j = 0; j < nCols; j++) {
    CHECK_RETURN( XPRSchgobj(prob, 1, &j, &dcost[j]) );
  }

  if(bWriteGoalProbs) {
    CHECK_RETURN( XPRSwriteprob(prob, "goal\\ao_goal", "") );
  }

  if(bRunMip) {
    /* Ensure that we restart the MIP solve each time */
    CHECK_RETURN( XPRSpostsolve(prob) );
  }

  /* Solve the LP and get the solve status */
  CHECK_RETURN( XPRSchgobjsense(prob, XPRS_OBJ_MINIMIZE) );
  CHECK_RETURN( XPRSlpoptimize(prob, "") );

  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_LPSTATUS, &iStatus) );

  /* Exit if the problem is infeasible even with the slacks we have added to
     the rows */
  if(iStatus != XPRS_LP_OPTIMAL) {
    *bIsInfeasible = 1;
    goto cleanup;
  }

  CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_LPOBJVAL, &dObjVal) );

  /* Run the MIP search if required */
  if(bRunMip) {
    /* Solve the MIP search and get the solve status */
    CHECK_RETURN( XPRSmipoptimize(prob, "") );
    CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPSTATUS, &iStatus) );

    /* Exit if the problem is infeasible even with the slacks we have added to
       the rows */
    if(iStatus != XPRS_MIP_OPTIMAL) {
      *bIsInfeasible = 1;
      goto cleanup;
    }

    CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_MIPOBJVAL, &dObjVal) );
  }

 cleanup:
  if(dObjRhs_) *dObjRhs_ = dObjRhs;
  free(dcost);
  free(mclind);
  free(dcmatval);
  return returnCode;
}

static int Read_Record_GoalObjective(XPRSprob prob, FILE *f,
                                     int bArchimedianOtherwisePreemptive,
                                     GoalObjective go, int *bEof)
{
  int returnCode = 0;
  char buff[1024], *p;

  *bEof = 0;
  memset(go, -1, sizeof(*go));

  /* Read the row name */
  if(!fgets(buff, sizeof(buff), f)) {
    *bEof = 1;
    goto cleanup;
  }
  buff[strlen(buff) - 1] = '\0';
  if(!*buff) {
    *bEof = 1;
    goto cleanup;
  }
  CHECK_RETURN( XPRSgetindex(prob, 1, buff, &go->iRow) );
  if(go->iRow < 0) {
    returnCode = -1;
    goto cleanup;
  }

  if(bArchimedianOtherwisePreemptive) {
    /* Read multiplier */
    if(!fgets(buff, sizeof(buff), f)) {
      returnCode = -1;
      goto cleanup;
    }
    go->dTolerance = strtod(buff, &p);
    if(!isspace(*p)) {
      returnCode = -1;
      goto cleanup;
    }

    /* Read sense */
    if(!fgets(buff, sizeof(buff), f)) {
      returnCode = -1;
      goto cleanup;
    }
    if(strnicmp(buff, "mi", 2) == 0) {
      go->iOptimizationSense = 1;
    } else if(strnicmp(buff, "ma", 2) == 0) {
      go->iOptimizationSense = -1;
    } else {
      returnCode = -1;
      goto cleanup;
    }
  } else {
    /* Read sense */
    if(!fgets(buff, sizeof(buff), f)) {
      returnCode = -1;
      goto cleanup;
    }
    if(strnicmp(buff, "mi", 2) == 0) {
      go->iOptimizationSense = 1;
    } else if(strnicmp(buff, "ma", 2) == 0) {
      go->iOptimizationSense = -1;
    } else {
      returnCode = -1;
      goto cleanup;
    }

    /* Read multiplier interpretation */
    if(!fgets(buff, sizeof(buff), f)) {
      returnCode = -1;
      goto cleanup;
    }
    if(tolower(*buff) == 'p') {
      go->bFractionIsPercentage = 1;
    } else if(tolower(*buff) == 'd') {
      go->bFractionIsPercentage = 0;
    } else {
      returnCode = -1;
      goto cleanup;
    }

    /* Read multiplier */
    if(!fgets(buff, sizeof(buff), f)) {
      returnCode = -1;
      goto cleanup;
    }
    go->dTolerance = strtod(buff, &p);
    if(!isspace(*p)) {
      returnCode = -1;
      goto cleanup;
    }
  }
 cleanup:
  return returnCode;
}

static int Read_Record_GoalConstraint(XPRSprob prob, FILE *f,
                                      int bArchimedianOtherwisePreemptive,
                                      GoalConstraint gc, int *bEof)
{
  int returnCode = 0;
  char buff[1024], *p;

  *bEof = 0;
  memset(gc, -1, sizeof(*gc));

  /* Read the row name */
  if(!fgets(buff, sizeof(buff), f)) {
    *bEof = 1;
    goto cleanup;
  }
  buff[strlen(buff) - 1] = '\0';
  if(!*buff) {
    *bEof = 1;
    goto cleanup;
  }
  CHECK_RETURN( XPRSgetindex(prob, 1, buff, &gc->iRow) );
  if(gc->iRow < 0) {
    returnCode = -1;
    goto cleanup;
  }

  if(bArchimedianOtherwisePreemptive) {
    /* Read multiplier */
    if(!fgets(buff, sizeof(buff), f)) {
      returnCode = -1;
      goto cleanup;
    }
    gc->dCost = strtod(buff, &p);
    if(!isspace(*p)) {
      returnCode = -1;
      goto cleanup;
    }
  }
 cleanup:
  return returnCode;
}


static int Read_Goal_Directives(XPRSprob prob, const char *sFileName, Goal g)
{
  int returnCode = 0;
  FILE *f = NULL;
  char buff[1024];
  int bEof;

  memset(g, 0, sizeof(*g));

  if(strlen(sFileName) + strlen(".gol") >= sizeof(buff)) {
    returnCode = -1;
    goto cleanup;
  }

  if(strlen(sFileName) < strlen(".gol")) {
    strcpy(buff, sFileName);
    strcat(buff, ".gol");
  } else {
    strcpy(buff, sFileName);
    if(stricmp(buff + strlen(buff) - strlen(".gol"), ".gol")) {
      strcat(buff, ".gol");
    }
  }

  if(!(f = fopen(buff, "r"))) {
    returnCode = -1;
    goto cleanup;
  }

  /* Read Archimedian or Preemptive */
  if(!fgets(buff, sizeof(buff), f)) {
    returnCode = -1;
    goto cleanup;
  }
  switch(tolower(buff[0])) {
  case('a') : g->bArchimedianOtherwisePreemptive = 1; break;
  case('p') : g->bArchimedianOtherwisePreemptive = 0; break;
  default : returnCode = -1; goto cleanup;
  }

  /* Read Constraints or Objective */
  if(!fgets(buff, sizeof(buff), f)) {
    returnCode = -1;
    goto cleanup;
  }
  switch(tolower(buff[0])) {
  case('c') : g->bConstraintsOtherwiseObjective = 1; break;
  case('o') : g->bConstraintsOtherwiseObjective = 0; break;
  default : returnCode = -1; goto cleanup;
  }

  /* Read records to end-of-file */
  for(;;) {
    if(g->bConstraintsOtherwiseObjective) {
      CHECK_RETURN( GcArray_AddRecord(&g->gc, &g->gc_count) );
      CHECK_RETURN( Read_Record_GoalConstraint(prob, f,
                                               g->bArchimedianOtherwisePreemptive,
                                               &g->gc[g->gc_count - 1], &bEof) );
      if(bEof) {g->gc_count--; break;}
    } else {
      CHECK_RETURN( GoArray_AddRecord(&g->go, &g->go_count) );
      CHECK_RETURN( Read_Record_GoalObjective(prob, f,
                                              g->bArchimedianOtherwisePreemptive,
                                              &g->go[g->go_count - 1], &bEof) );
      if(bEof) {g->go_count--; break;}
    }
  }

 cleanup:
  if (f) {
    fclose(f);
  }
  return returnCode;
}


int Goal_Run_One(XPRSprob prob, const char *sGoalFileBaseName, double *dResult)
{
  int returnCode = 0;
  int bIsInfeasible, iMipEnts, bRunMip;
  struct Goal_s g = {0};
  double dObjVal, dObjRhs;

  /* Read .gol file directives */
  CHECK_RETURN( Read_Goal_Directives(prob,  sGoalFileBaseName, &g) );

  /* Decide if we are solving a MIP */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_MIPENTS, &iMipEnts) );
  bRunMip = !iMipEnts ? 0 : 1;

  /* Run the goal */
  dObjRhs = 0;
  if(g.bConstraintsOtherwiseObjective) {
    if(g.bArchimedianOtherwisePreemptive) {
      CHECK_RETURN( RunGoal_Archimedian_Constraint(prob, bRunMip, g.gc,
                                                   g.gc_count,
                                                   &bIsInfeasible) );
    } else {
      CHECK_RETURN( RunGoal_Preemptive_Constraint(prob, bRunMip, g.gc,
                                                  g.gc_count,
                                                  &bIsInfeasible) );
    }
  } else {
    if(g.bArchimedianOtherwisePreemptive) {
      CHECK_RETURN( RunGoal_Archimedian_Objective(prob, bRunMip, g.go,
                                                  g.go_count,
                                                  &bIsInfeasible, &dObjRhs) );
    } else {
      CHECK_RETURN( RunGoal_Preemptive_Objective(prob, bRunMip, g.go,
                                                 g.go_count,
                                                 &bIsInfeasible, &dObjRhs) );
    }
  }

  /* Log results */
  CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_LPOBJVAL, &dObjVal) );
  dObjVal -= dObjRhs;
  *dResult = dObjVal;

 cleanup:
  /* Free memory allocated in Read_Goal_Directives */
  if (g.go) { free(g.go); }
  if (g.gc) { free(g.gc); }

  return returnCode;
}

int main(void) {
  int returnCode = 0;
  XPRSprob prob = NULL;
  double dObjVal;

  /* Initialize the optimizer. */
  if ( XPRSinit("") != 0 ) {
    char message[512];
    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) );

#define DIRECTORY_MATRICES ""
#define DIRECTORY_GOALFILE ""

  CHECK_RETURN( XPRSsetintcontrol(prob, XPRS_KEEPNROWS, 1) );

  /* Preemptive Objective */
  CHECK_RETURN( XPRSreadprob(prob, DIRECTORY_MATRICES "goalobj1","") );
  CHECK_RETURN( Goal_Run_One(prob, DIRECTORY_GOALFILE "gc1", &dObjVal) );

  /* Archimedian Objective */
  CHECK_RETURN( XPRSreadprob(prob, DIRECTORY_MATRICES "goalobj1","") );
  CHECK_RETURN( Goal_Run_One(prob, DIRECTORY_GOALFILE "gd1", &dObjVal) );

  /* Preemptive Constraint */
  CHECK_RETURN( XPRSreadprob(prob, DIRECTORY_MATRICES "goalcon3","") );
  CHECK_RETURN( Goal_Run_One(prob, DIRECTORY_GOALFILE "ga3", &dObjVal) );

  /* Archimedian Constraint */
  CHECK_RETURN( XPRSreadprob(prob, DIRECTORY_MATRICES "goalcon3","") );
  CHECK_RETURN( Goal_Run_One(prob, DIRECTORY_GOALFILE "gb3", &dObjVal) );

 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] = {0};
      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).
   */
  XPRSdestroyprob(prob);
  XPRSfree();

  return returnCode;
}

Back to examples browserPrevious example