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

Polygon - Defining nonlinear constraints

Description
Maximize the area of polygon of N vertices and diameter of 1. This example demonstrates how to build nonlinear formulae from strings and token sequences.

polygon_cpp.zip[download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
PolygonObjects.cpp[download]





PolygonObjects.cpp

// (c) 2024-2024 Fair Isaac Corporation

/**
 * Maximize the area of a polygon of N vertices and a diameter of 1. The
 * position of vertices is indicated as (r,theta) coordinates where r denotes
 * the distance to the base point (vertex with number N) and theta the angle
 * from the x-axis.
 */
#ifdef _WIN32
   // To get M_PI
#  define _USE_MATH_DEFINES
#endif
#include <cmath>
#include <xpress.hpp>

using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::cos;
using xpress::objects::utils::sin;
using xpress::objects::utils::sum;

// the number of vertices/sides of the polygon
#define NSIDES 5

int main() {
  XpressProblem prob;
  // Output all messages.
  prob.callbacks.addMessageCallback(XpressProblem::console);

  /**** VARIABLES ****/

  // r corresponds to the distance from the base point to vertex i
  auto r = prob.addVariables(NSIDES - 1).withName("r_%d").withUB(1.0).toArray();

  // theta corresponds to the angle relative to the x-axis of vertex i
  auto theta =
      prob.addVariables(NSIDES - 1).withName("theta_%d").withUB(M_PI).toArray();

  /**** OBJECTIVE ****/

  // objective transfer column
  Variable objtransfercol =
      prob.addVariable(XPRS_MINUSINFINITY, XPRS_PLUSINFINITY,
                       ColumnType::Continuous, "objTransferCol");

  // Set objective: maximize area of the polygon:
  // r_i*r_j*sin(theta_i+1-theta_i)/2
  Expression area = sum(NSIDES - 2, [&](auto i) {
    return r[i] * r[i + 1] * sin(theta[i + 1] - theta[i]) / 0.5;
  });

  // To make the objective linear, just maximize the objtransfercol
  prob.setObjective(objtransfercol, ObjSense::Maximize);

  // Add the objective transfer row: area = objtransfercol
  prob.addConstraint(area == objtransfercol);

  /**** CONSTRAINTS ****/

  // any two non-origin nodes should have a distance <= 1 to satisfy the
  // diameter
  for (int i = 0; i < NSIDES - 1; i++) {
    for (int j = i + 1; j < NSIDES - 1; j++) {
      // r_i^2 + r_j^2 - 2 * r_i * r_j * cos(theta_j - theta_i) <= 1
      prob.addConstraint(r[i].square() + r[j].square() -
                             2 * r[i] * r[j] * cos(theta[j] - theta[i]) <=
                         1.0);
    }
  }

  // Ordering of the vertices: theta_i+1 >= theta_i
  prob.addConstraints(NSIDES - 2,
                      [&](auto i) { return theta[i + 1] >= theta[i]; });

  // Dump the problem to disk so that we can inspect it.
  prob.writeProb("polygon.lp", "lp");

  // Solve to local optimality
  prob.controls.setNlpSolver(XPRS_NLPSOLVER_LOCAL);

  // Solve
  prob.optimize();
  if (prob.attributes.getSolStatus() != SolStatus::Optimal &&
      prob.attributes.getSolStatus() != SolStatus::Feasible)
    throw std::runtime_error("optimization failed with status " +
                             to_string(prob.attributes.getSolStatus()));
  auto sol = prob.getSolution();
  std::cout << "Objective: " << prob.attributes.getObjVal() << std::endl;
  // Print out the solution
  for (auto &var : r) {
    std::cout << var.getName() << ":" << var.getValue(sol) << " ";
  }
  std::cout << std::endl;
  for (auto &var : theta) {
    std::cout << var.getName() << ":" << var.getValue(sol) << " ";
  }
  std::cout << std::endl;

  return 0;
}

Back to examples browserPrevious exampleNext example