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

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[download]





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

Back to examples browserPrevious example