| |||||||||||
Goal programming Description This example tries to construct a production plan which meets four different
goals in order of priority.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
goalprog.c /*************************************************************** Goal programming example ======================== An example of lexicographic goal programming using the Xpress multi-objective API. A company produces two electrical products, A and B. Both require two stages of production: wiring and assembly. The production plan must meet several goals: 1. A profit of $200 2. A contractual requirement of 40 units of product B 3. To fully utilize the available wiring department hours 4. To avoid overtime in the assembly department (c) 2022-2024 Fair Isaac Corporation *****************************************************************/ #include <stdio.h> #include "xprs.h" #define COLS 8 /* Number of columns */ #define ROWS 4 /* Number of rows */ #define ENTITIES 2 /* Number of entities */ /* Column indices */ #define COL_PRODUCE_A 0 #define COL_PRODUCE_B 1 #define COL_SURPLUS_WIRING 2 #define COL_DEFICIT_WIRING 3 #define COL_SURPLUS_ASSEM 4 #define COL_DEFICIT_ASSEM 5 #define COL_DEFICIT_PROFIT 6 #define COL_DEFICIT_PROD_B 7 /* Calls an Xpress optimizer function and checks the return code. * If the call fails then the macro * - 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 cbMessage(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; } } int main(void) { int returnCode = 0; XPRSprob prob = NULL; int i, solvestatus, solstatus; /* 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, cbMessage, NULL, 0) ); /* Define columns */ { double objcoef[COLS]; double lb[COLS]; double ub[COLS]; int start[COLS + 1]; for (i = 0; i < COLS; i++) { objcoef[i] = 0; lb[i] = 0; ub[i] = XPRS_PLUSINFINITY; start[i] = 0; } start[i] = 0; CHECK_RETURN( XPRSaddcols(prob, COLS, 0, objcoef, start, NULL, NULL, lb, ub) ); } /* First two columns are integer */ { int entind[ENTITIES] = {COL_PRODUCE_A, COL_PRODUCE_B}; char coltype[ENTITIES] = {'I', 'I'}; CHECK_RETURN( XPRSchgcoltype(prob, 2, entind, coltype) ); } /* Add one row per goal */ { double rhs[] = { 200, /* Profit goal is $200 */ 40, /* Production goal for product B is 40 units */ 120, /* 120 wiring hours are available */ 300 /* 300 assembly hours are available */ }; char rowtype[] = { 'G', 'G', /* Profit and production goals should be met or exceeded */ 'E', 'E' /* Workforce utilization goals should be met */ }; int start[] = {0, 2, 4, 8, 12}; double rowcoef[] = { 7, 6, 1, /* Profit for products A and B + deficit in profit */ 1, 1, /* Units of product B produced + deficit in units produced */ 2, 3, -1, 1, /* Products A/B require 2/3 hours of wiring, - surplus + deficit */ 6, 5, -1, 1 /* Products A/B require 6/5 hours of wiring, - surplus + deficit */ }; int colind[] = { COL_PRODUCE_A, COL_PRODUCE_B, COL_DEFICIT_PROFIT, COL_PRODUCE_B, COL_DEFICIT_PROD_B, COL_PRODUCE_A, COL_PRODUCE_B, COL_SURPLUS_WIRING, COL_DEFICIT_WIRING, COL_PRODUCE_A, COL_PRODUCE_B, COL_SURPLUS_ASSEM, COL_DEFICIT_ASSEM }; CHECK_RETURN( XPRSaddrows(prob, ROWS, sizeof(colind) / sizeof(int), rowtype, rhs, NULL, start, colind, rowcoef) ); } /* Define objectives to minimize deviations, in priority order */ { int colind[2]; double objcoef[2] = {1, 1}; colind[0] = COL_DEFICIT_PROFIT; /* Goal 1: minimize profit deficit */ CHECK_RETURN (XPRSchgobj(prob, 1, colind, objcoef) ); colind[0] = COL_DEFICIT_PROD_B; /* Goal 2: minimize production deficit */ CHECK_RETURN (XPRSchgobjn(prob, 1, 1, colind, objcoef) ); colind[0] = COL_SURPLUS_WIRING; /* Goal 3: minimize deviation from wiring hours target */ colind[1] = COL_DEFICIT_WIRING; CHECK_RETURN (XPRSchgobjn(prob, 2, 2, colind, objcoef) ); colind[0] = COL_SURPLUS_ASSEM; /* Goal 4: minimize deviation from assembly hours target */ colind[1] = COL_DEFICIT_ASSEM; CHECK_RETURN (XPRSchgobjn(prob, 3, 2, colind, objcoef) ); /* Set up objective priorities and tolerances */ for (i = 0; i < 4; i++) { CHECK_RETURN (XPRSsetobjintcontrol(prob, i, XPRS_OBJECTIVE_PRIORITY, 4 - i) ); CHECK_RETURN (XPRSsetobjdblcontrol(prob, i, XPRS_OBJECTIVE_ABSTOL, 0) ); CHECK_RETURN (XPRSsetobjdblcontrol(prob, i, XPRS_OBJECTIVE_RELTOL, 0) ); } } /* Solve the problem */ CHECK_RETURN( XPRSoptimize(prob, "", &solvestatus, &solstatus) ); /* Print the result */ if (solvestatus == XPRS_SOLVESTATUS_COMPLETED && solstatus == XPRS_SOLSTATUS_OPTIMAL) { double sol[COLS]; CHECK_RETURN (XPRSgetmipsol(prob, sol, NULL) ); printf("Production plan:\n"); printf("Product A: %d units\n", (int) sol[COL_PRODUCE_A]); printf("Product B: %d units\n", (int) sol[COL_PRODUCE_B]); printf("Profit: $%d\n", (int) (7 * sol[COL_PRODUCE_A] + 6 * sol[COL_PRODUCE_B])); if (sol[COL_DEFICIT_PROFIT] > 0) { printf("Profit goal missed by $%d\n", (int) sol[COL_DEFICIT_PROFIT]); } if (sol[COL_DEFICIT_PROD_B] > 0) { printf("Contractual goal for product B missed by %d units\n", (int) sol[COL_DEFICIT_PROD_B]); } if (sol[COL_SURPLUS_WIRING] > 0) { printf("Unused wiring department hours: %d\n", (int) sol[COL_SURPLUS_WIRING]); } if (sol[COL_DEFICIT_WIRING] > 0) { printf("Wiring department overtime: %d\n", (int) sol[COL_DEFICIT_WIRING]); } if (sol[COL_SURPLUS_ASSEM] > 0) { printf("Unused assembly department hours: %d\n", (int) sol[COL_SURPLUS_ASSEM]); } if (sol[COL_DEFICIT_ASSEM] > 0) { printf("Assembly department overtime: %d\n", (int) sol[COL_DEFICIT_ASSEM]); } } else { printf("Problem could not be solved\n"); } 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). */ XPRSdestroyprob(prob); XPRSfree(); return returnCode; } | |||||||||||
© Copyright 2024 Fair Isaac Corporation. |