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