Initializing help system before first use

Maximizing the area of a polygon using tokens based input - using a 'map' userfunction


Type: Programming
Rating: 3 (intermediate)
Description: Demonstrates how to solve a nonlinear problem in C
File(s): Polygon_userfunc_map.c


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

      file Polygon_userfunc_map.c
   ``````````````````````````

   Maximize the area of polygon of N vertices and diameter of 1
   The position of vertices is indicated as (rho,theta) coordinates
   where rho denotes the distance to the base point
   (vertex with number N) and theta the angle from the x-axis.
   The nonlinear expressions are described text based input.

   (c) 2021-2025 Fair Isaac Corporation

***********************************************************************

   Polygon example: maximise the area of an N sided polygon

   *** Demonstrating using a simple map (R->R) userfunction ***

   Variables:

    rho   : 0..N-1   ! Distance of vertex from the base point
    theta : 0..N-1   ! Angle from x-axis

   Objective:
     (sum (i in 1..N-2) (rho(i)*rho(i-1)*sin(theta(i)-theta(i-1)))) * 0.5

   Constraints:
     Vertices in increasing degree order:
        theta(i) >= theta(i-1) +.01  : i = 1..N-2
     Boundary conditions:
        theta(N-1) <= Pi
        0.1 <= rho(i) <= 1   : i = 0..N-2
     Third side of all triangles <= 1
        rho(i)^2 + rho(j)^2 - rho(i)*rho(j)*2*cos(theta(j)-theta(i)) <= 1    : i in 0..N-3, j in i..N-2

***********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "xprs.h"

#define PI 3.14159

/* Perform the Xpress specified function call and check its return value. */
#define CHECK_XPRSCALL(call)                             \
  do {                                                   \
    int result_ = call;                                  \
    if ( result_ != 0 ) {                                \
      fprintf(stderr, "Line %d: Failed call to `%s`.\n", \
              __LINE__, #call);                          \
      goto returnWithError;                              \
    }                                                    \
  } while (0)

// userfunction returning a 'sin'
double XPRS_CC mySin(double x, void* context)
{
  (void)context;
  return (sin(x));
}
// userfunction returning a 'cos'
double XPRS_CC myCos(double x, void* context)
{
  (void)context;
  return (cos(x));
}

// Message callback
void XPRS_CC message(XPRSprob prob, void* my_object, const char* msg, int len, int msgtype) {
  (void)prob;
  (void)my_object;
  switch (msgtype)
  {
  case 4:  /* error */
  case 3:  /* warning */
  case 2:  /* dialogue */
  case 1:  /* information */
    if (len == 0)
      printf("\n");
    else
      printf("%s\n", msg);
    break;
  default: /* exiting - buffers need flushing */
    fflush(stdout);
    break;
  }
}

int main(void) {
  XPRSprob xprob = NULL;

  // Number of side of the Polygon
  int nSide = 5;
  int maxNameLength = 256;
  int ReturnValue = 0;
  int nameCursor = 0;
  int nameBufferSizeRows = 0;
  int nameBufferSizeCols = 0;

  double* RHS = NULL;
  char* RowType = NULL;
  char* RowNames = NULL;

  double* OBJ = NULL;
  double* Lower = NULL;
  double* Upper = NULL;

  int* ColStart = NULL;
  double* Element = NULL;
  int* RowIndex = NULL;
  char* ColNames = NULL;

  char* CoefBuffer = NULL;
  int coefBufferCursor = 0;
  int coefBufferSize = 0;

  int i, j;

  /* Initialisation */
  CHECK_XPRSCALL(XPRSinit(NULL));
  CHECK_XPRSCALL(XPRScreateprob(&xprob));

  /* Messaging */
  CHECK_XPRSCALL(XPRSaddcbmessage(xprob, message, NULL, 0));

  printf("####################\n");
  printf("# Polygon example  #\n");
  printf("####################\n");

  /* Create rows */
  int nRow = nSide - 2 + (nSide - 1) * (nSide - 2) / 2 + 1;

  RHS = (double*)malloc(nRow * sizeof(double));
  if (!RHS) goto MemoryReturn;

  for (i = 0; i < nRow; i++) RHS[i] = 0;

  RowType = (char*)malloc((nRow + 1) * sizeof(char));
  if (!RowType) goto MemoryReturn;
  nameBufferSizeRows = maxNameLength * (nRow + 1);
  RowNames = (char*)malloc(nameBufferSizeRows * sizeof(char));
  if (!RowNames) goto MemoryReturn;

  nRow = 0;

  // Objective constraint
  RowType[nRow] = 'E'; /* OBJEQ */
  nameCursor += snprintf(RowNames + nameCursor, nameBufferSizeRows - nameCursor, "OBJEQ");
  nameCursor++;
  if (nameCursor + 1 > nameBufferSizeRows) goto BufferReturn;
  nRow++;

  // Vertices in increasing degree order
  for (i = 1; i < nSide - 1; i++)
  {
    RowType[nRow] = 'G'; /* T2T1 .. T4T3 */
    nameCursor += snprintf(RowNames + nameCursor, nameBufferSizeRows - nameCursor, "T%iT%i", i + 1, i);
    nameCursor++;
    if (nameCursor + 1 > nameBufferSizeRows) goto BufferReturn;
    nRow++;
    RHS[i] = 0.001;
  }

  // Third side of all triangles <= 1
  for (i = 1; i < nSide - 1; i++)
  {
    for (j = i + 1; j < nSide; j++)
    {
      RowType[nRow] = 'L';
      RHS[nRow] = 1.0;
      nameCursor += snprintf(RowNames + nameCursor, nameBufferSizeRows - nameCursor, "V%iV%i", i, j);
      nameCursor++;
      if (nameCursor + 1 > nameBufferSizeRows) goto BufferReturn;
      nRow++;
    }
  }
  RowType[nRow] = '\0';

  /* Columns and linear coefficients*/
  int nCol = (nSide - 1) * 2 + 2;
  int nElement = 0;

  OBJ = (double*)malloc((nCol + 1) * sizeof(double));
  if (!OBJ) goto MemoryReturn;
  Lower = (double*)malloc((nCol + 1) * sizeof(double));
  if (!Lower) goto MemoryReturn;
  Upper = (double*)malloc((nCol + 1) * sizeof(double));
  if (!Upper) goto MemoryReturn;

  for (i = 0; i < nCol; i++)
  {
    OBJ[i] = 0;           /* objective function */
    Lower[i] = 0;         /* lower bound normally zero */
    Upper[i] = XPRS_PLUSINFINITY; /* upper bound infinity */
  }

  // Arrays to load linear coefficients
  ColStart = (int*)malloc((nRow + 1) * sizeof(int));
  if (!ColStart) goto MemoryReturn;
  Element = (double*)malloc((10 * nCol) * sizeof(double));
  if (!Element) goto MemoryReturn;
  RowIndex = (int*)malloc((10 * nCol) * sizeof(int));
  if (!RowIndex) goto MemoryReturn;
  nameBufferSizeCols = maxNameLength * (nCol);
  ColNames = (char*)malloc(nameBufferSizeCols * sizeof(char));
  if (!ColNames) goto MemoryReturn;

  nameCursor = 0;

  /* OBJX */
  nCol = 0;
  ColStart[nCol] = nElement;
  OBJ[nCol] = 1.0;
  nameCursor += snprintf(ColNames + nameCursor, nameBufferSizeCols - nameCursor, "OBJX");
  nameCursor++;
  if (nameCursor + 1 > nameBufferSizeCols) goto BufferReturn;
  Lower[nCol++] = XPRS_MINUSINFINITY; /* free column */
  Element[nElement] = -1.0;
  RowIndex[nElement++] = 0;

  /* THETA1 - THETA 4 */
  int iRow = 0;
  for (i = 1; i < nSide; i++)
  {
    nameCursor += snprintf(ColNames + nameCursor, nameBufferSizeCols - nameCursor, "THETA%i", i);
    nameCursor++;
    if (nameCursor + 1 > nameBufferSizeCols) goto BufferReturn;
    ColStart[nCol++] = nElement;
    if (i < nSide - 1)
    {
      Element[nElement] = -1;
      RowIndex[nElement++] = iRow + 1;
    }
    if (i > 1)
    {
      Element[nElement] = 1;
      RowIndex[nElement++] = iRow;
    }
    iRow++;
  }

  Upper[nCol - 1] = PI;

  /* Rho's */
  for (i = 1; i < nSide; i++)
  {
    Lower[nCol] = 0.01;   /* lower bound */
    Upper[nCol] = 1;
    nameCursor += snprintf(ColNames + nameCursor, nameBufferSizeCols - nameCursor, "RHO%i", i);
    nameCursor++;
    if (nameCursor + 1 > nameBufferSizeCols) goto BufferReturn;
    ColStart[nCol++] = nElement;
  }
  ColStart[nCol] = nElement;

  CHECK_XPRSCALL(XPRSloadlp(xprob, "Polygon", nCol, nRow, RowType, RHS, NULL, OBJ, ColStart, NULL, RowIndex, Element, Lower, Upper));

  CHECK_XPRSCALL(XPRSaddnames(xprob, 1, RowNames, 0, nRow - 1));
  CHECK_XPRSCALL(XPRSaddnames(xprob, 2, ColNames, 0, nCol - 1));

  /* Add the user function*/
  CHECK_XPRSCALL(XPRSnlpadduserfunction(xprob, "mySin", XPRS_USERFUNCTION_MAP, 1 /* #inputs arguments */, 1 /* #output arguments*/, 0, (XPRSfunctionptr)mySin, NULL, NULL));
  CHECK_XPRSCALL(XPRSnlpadduserfunction(xprob, "myCos", XPRS_USERFUNCTION_MAP, 1 /* #inputs arguments */, 1 /* #output arguments*/, 0, (XPRSfunctionptr)myCos, NULL, NULL));

  /* Build up nonlinear coefficients */
  coefBufferSize = 1000 * maxNameLength;
  CoefBuffer = (char*)malloc(coefBufferSize);
  if (!CoefBuffer) goto MemoryReturn;

  /* Area */
  coefBufferCursor += snprintf(CoefBuffer + coefBufferCursor, coefBufferSize - coefBufferCursor,
    "0.5 * ( ");
  if (coefBufferCursor + 1 > coefBufferSize) goto BufferReturn;
  for (i = 1; i < nSide - 1; i++)
  {
    if (i > 1)
    {
      coefBufferCursor += snprintf(CoefBuffer + coefBufferCursor, coefBufferSize - coefBufferCursor,
        " + ");
      if (coefBufferCursor + 1 > coefBufferSize) goto BufferReturn;
    }
    coefBufferCursor += snprintf(CoefBuffer + coefBufferCursor, coefBufferSize - coefBufferCursor,
      "RHO%i * RHO%i * mySin ( THETA%i - THETA%i )", i + 1, i, i + 1, i);
    if (coefBufferCursor + 1 > coefBufferSize) goto BufferReturn;
  }
  coefBufferCursor += snprintf(CoefBuffer + coefBufferCursor, coefBufferSize - coefBufferCursor,
    " )");
  if (coefBufferCursor + 1 > coefBufferSize) goto BufferReturn;
  CHECK_XPRSCALL(XPRSnlpchgformulastr(xprob, 0, CoefBuffer));

  /* Distances */
  for (i = 1; i < nSide - 1; i++)
  {
    for (j = i + 1; j < nSide; j++)
    {
      coefBufferCursor = 0;
      coefBufferCursor += snprintf(CoefBuffer + coefBufferCursor, coefBufferSize - coefBufferCursor,
        "RHO%i ^ 2 + RHO%i ^ 2 - 2 * RHO%i * RHO%i * myCos ( THETA%i - THETA%i )", j, i, j, i, j, i);
      CHECK_XPRSCALL(XPRSnlpchgformulastr(xprob, iRow, CoefBuffer));
      iRow++;
    }
  }

  CHECK_XPRSCALL(XPRSchgobjsense(xprob, XPRS_OBJ_MAXIMIZE));

  CHECK_XPRSCALL(XPRSoptimize(xprob, "s", NULL, NULL));

  goto NormalReturn;
BufferReturn:
  printf("\nWorking buffer too short\n");
  goto NormalReturn;
MemoryReturn:
  printf("\nOut of memory\n");
  goto NormalReturn;
returnWithError:
  ReturnValue = -1;
NormalReturn:

  // Retrieve error from Xpress
  if (ReturnValue) {
    fprintf(stderr, "An error was detected during execution.\n");
    if (xprob) {
      int errorcode = 0;
      char errorMessage[512];
      XPRSgetintattrib(xprob, XPRS_ERRORCODE, &errorcode);
      XPRSgetlasterror(xprob, errorMessage);
      fprintf(stderr, "Optimizer returned error code '%i' with message:\n%s\n", errorcode, errorMessage);
    }
  }

  XPRSdestroyprob(xprob);
  XPRSfree();

  free(RHS);
  free(RowType);
  free(RowNames);
  free(OBJ);
  free(Lower);
  free(Upper);
  free(ColStart);
  free(Element);
  free(RowIndex);
  free(ColNames);
  free(CoefBuffer);

  return(ReturnValue);
}

© 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.