Initializing help system before first use

Modify problem: add an extra variable within an additional constraint


Type: Cutting stock
Rating: 3 (intermediate)
Description: We take the trimloss problem described in trimloss.mps, in which each integer variable x(p) represents the number of rolls cut to pattern p. We define a new integer variable y=SUM(p)x(p) and add the associated constraint
		          x(1)+x(2)+...+x(N)-y = 0
We do this by first adding a row containing the (unitary) coefficients of the x(p), and then a column corresponding to y. We output the revised matrix to a file and then solve the revised MIP, giving y the highest branching priority. Finally, we output the solution, both to the screen and to an ASCII solution file.
File(s): trimloss.c
Data file(s): trimloss.mps


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

   file trimloss.c
   ```````````````
   Modify a problem by adding an extra variable within an additional
   constraint.

   We take a trimloss problem in which each integer variable x(p)
   represents the number of rolls cut to pattern p.
   We define a new integer variable y=SUM(p)x(p) and add the associated
   constraint
                           x(1)+x(2)+...+x(N)-y = 0
   We do this by first adding a row containing the (unitary)
   coefficients of the x(p), and then a column corresponding to y.
   We output the revised matrix to a file and then solve the revised
   MIP, giving y the highest branching priority. Finally, we output
   the solution, both to the screen and to an ASCII solution file.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xprs.h"                 /* Optimizer header file */

/* 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 void XPRS_CC messagecb(XPRSprob cbprob, void* cbdata,
                              const char *msg, int len, int msgtype);

int main()
{
  int returnCode = 0;
  XPRSprob prob = NULL;                  /* The problem instance */
  char   sProblem[]="../data/trimloss";  /* Problem name */
  char   sLogFile[]="trimloss.log";      /* Log file name */

  int    nRow;            /* Number of rows */
  int    nCol;            /* Number of columns */
  int    i;               /* Loop counter */

  /* New row variables */
  int    nNewRow = 1;     /* Number of new rows */
  int    nRowElem;        /* Number of non-zero elements in the new row */
  double *pRowVal;        /* Values of the new row elements */
  int    *pColInd = NULL; /* Column indices of the new row elements */
  int    nRowStart[1];    /* Start position of the row in pRowVal */
  char   sRowType[1];     /* New row type */
  double dRHS[1];         /* New RHS value */
  char   sRowName[9];     /* New row name */

  /* New column variables */
  int    nNewCol = 1;     /* Number of new columns */
  int    nColElem;        /* Number of non-zero elements in the new column */
  double *pColVal = NULL; /* Values of the new column elements */
  int    *pRowInd = NULL; /* Row indices of the new column elements */
  int    nColStart[1];    /* Start position of the column in pColVal */
  double dObjCoef[1];     /* Objective coefficient of the new column */
  double dUpperBnd[1];    /* Upper bound on the new column */
  double dLowerBnd[1];    /* Lower bound on the new column */
  char   sColType[1];     /* New column type */
  int    nNewColInd[1];   /* New column index */
  int    nTypeChg = 1;    /* Number of column types to change - to make y
                             an integer variable */
  char   sColName[9];     /* New column name */
  int    nColPrior[1];    /* Branching priority of the new column */

  /* Solution variables */
  double dObjVal;         /* Objective value of the best integer solution found
                           */
  double *x = NULL;       /* Primal solution values */

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

  /* Remove and define log file */
  remove(sLogFile);
  CHECK_RETURN( XPRSsetlogfile(prob, sLogFile) );

  /* Turn off presolve and permit no cuts */
  CHECK_RETURN( XPRSsetintcontrol(prob, XPRS_PRESOLVE, 0) );
  CHECK_RETURN( XPRSsetintcontrol(prob, XPRS_CUTSTRATEGY, 0) );

  /* Read the problem file */
  CHECK_RETURN( XPRSreadprob(prob, sProblem,"") );

  /* Get the number of rows and columns */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_ROWS, &nRow) );
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCol) );

  /*** Construct and add the new row ***/

  /* Assign the number of new row elements*/
  nRowElem = nCol;

  /* Allocate memory for the new row arrays */
  pRowVal=malloc(nRowElem * sizeof(*pRowVal));
  pColInd=malloc(nRowElem * sizeof(*pColInd));
  if (!pRowVal || !pColInd) {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }

  /* Construct the new row */
  dRHS[0]       = 0.0;
  nRowStart[0]  = 0;
  sRowType[0]   = 'E';
  /* Store the coefficents of the x(p) */
  for (i = 0; i < nCol; i++) {
    pRowVal[i] = 1.0;
    pColInd[i] = i;
  }

  /* Add the new row */
  CHECK_RETURN( XPRSaddrows(prob, nNewRow, nRowElem, sRowType, dRHS, NULL,
                            nRowStart, pColInd, pRowVal) );

  /* Update the number of rows */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_ROWS, &nRow) );

  /* Add new row name */
  strcpy(sRowName,"NewRow");
  CHECK_RETURN( XPRSaddnames(prob, 1, sRowName, nRow-1, nRow-1) );

  /*** Construct and add the new column ***/

  /* Assign the number of new column elements*/
  nColElem = 1;

  /* Allocate memory for the new column arrays */
  pColVal=malloc(nColElem  * sizeof(*pColVal));
  pRowInd=malloc(nColElem  * sizeof(*pRowInd));
  if (!pColVal || !pRowInd ) {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }

  /* Construct the new column */
  dObjCoef[0]  = 0.0;
  nColStart[0] = 0;
  pColVal[0]   = -1.0; /* the coefficent of -y */
  pRowInd[0]   = nRow-1;
  dLowerBnd[0] = 0.0;
  dUpperBnd[0] = 90;   /* the sum of the bounds on the x(p) */

  /* Add the new column */
  CHECK_RETURN( XPRSaddcols(prob, nNewCol, nColElem, dObjCoef, nColStart,
                            pRowInd, pColVal, dLowerBnd, dUpperBnd) );

  /* Update the number of columns */
  CHECK_RETURN( XPRSgetintattrib(prob, XPRS_COLS, &nCol) );

  /* Make the new column an integer variable */
  nTypeChg=1;
  nNewColInd[0]=nCol-1;
  sColType[0]= 'I';

  CHECK_RETURN( XPRSchgcoltype(prob, nTypeChg, nNewColInd, sColType) );

  /* Add new column name */
  strcpy(sColName,"y");
  CHECK_RETURN( XPRSaddnames(prob, 2, sColName, nCol-1, nCol-1) );

  /*** Output the revised matrix ***/
  CHECK_RETURN( XPRSwriteprob(prob,"revised","") );

  /*** Solve the revised MIP ***/

  /* Assign the y variable a high branching priority (1)
     - by default the original x(p) variables have low priority (500) */
  nColPrior[0] = 1;

  /* Load this directive */
  CHECK_RETURN( XPRSloaddirs(prob, 1, nNewColInd, nColPrior, NULL, NULL, NULL)
    );

  /* Solve the MIP */
  printf("Solving problem %s:\n\n", sProblem);
  CHECK_RETURN( XPRSmipoptimize(prob,"") );

  /* Get the objective value of the best integer solution found */
  CHECK_RETURN( XPRSgetdblattrib(prob, XPRS_MIPOBJVAL, &dObjVal) );

  /* Allocate memory to the solution array */
  x = malloc(nCol*sizeof(*x));
  if (!x) {
    perror("malloc");
    returnCode = -2;
    goto cleanup;
  }

  /* Get the primal solution values */
  CHECK_RETURN( XPRSgetsolution(prob, NULL, x, 0, nCol - 1) );

  /* Display the objective and solution values */
  printf("   Minimum loss is %g\n\n", dObjVal);
  printf("   The solution values are\n");
  for(i = 0; i < nCol-1; i++)
    printf("        x(%d)=%g\n", i+1, x[i]);
  printf("        y = %g\n\n", x[nCol-1]);

  /* Save the solution to an ASCII file */
  CHECK_RETURN( XPRSwritesol(prob,"revised","") );

 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).
   */
  free(pRowVal);
  free(pColInd);
  free(pColVal);
  free(pRowInd);
  free(x);
  XPRSdestroyprob(prob);
  XPRSfree();

  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.