/*********************************************************************** 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-2024 Fair Isaac Corporation ***********************************************************************/ #include #include #include #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; 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; /* 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; } }