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

Linearizations and approximations via SOS and piecewise linear (pwl) expressions

Description
Various examples of using SOS (Special Ordered Sets of type 1 or 2) to represent nonlinear functions in MIP models implemented with the Xpress Solver C++ API.
  • approximating a continuous nonlinear function in a single variable via SOS-1 (soslin.*);
  • approximating a continuous nonlinear function in two variables via SOS-2 (sosquad.*);
Further explanation of this example: Whitepaper 'MIP formulations and linearizations', Section Non-linear functions

pwlinsoscpp.zip[download all files]

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





sosquad.cpp

/******************************************************
   Xpress C++ Example Problems
   ===========================

   file sosquad.cpp
   ````````````````
   Approximation of a nonlinear function in two variables 
   by two SOS-2.
   - Example discussed in mipformref whitepaper -  

   (c) 2024 Fair Isaac Corporation
       author: D. Salvagnin, Sep. 2024
*******************************************************/
#include <iostream>
#include <xpress.hpp>

using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::sum;
using namespace std;

int main()
{
  XpressProblem prob;

  // problem data
  static const int NX = 10;
  static const int NY = 10;

  std::array<double,NX> X;
  for (int i = 0; i < NX; i++)  X[i] = (double)(i+1);
  std::array<double,NY> Y;
  for (int j = 0; j < NY; j++)  Y[j] = (double)(j+1);
  std::array<std::array<double,NY>, NX> FXY;
  for (int i = 0; i < NX; i++) {
    for (int j = 0; j < NY; j++) {
      FXY[i][j] = (double)(i-4)*(j-4);
    }
  }

  // Create the decision variables
  auto x = prob.addVariable("x");
  auto y = prob.addVariable("y");
  auto f = prob.addVariable(XPRS_MINUSINFINITY, XPRS_PLUSINFINITY, ColumnType::Continuous, "f");
  auto wx = prob.addVariables(NX).withName("wx_%d").toArray();
  auto wy = prob.addVariables(NY).withName("wy_%d").toArray();
  auto wxy = prob.addVariables(NX, NY).withName("wxy_%d_%d").toArray();

  // Define the SOS-2 for coordinate x
  prob.addConstraint(SOS::sos(SetType::SOS2, wx, X, "sos_x")); 
  // Define the SOS-2 for coordinate y
  prob.addConstraint(SOS::sos(SetType::SOS2, wy, Y, "sos_y")); 

  // Weights must sum up to 1 along the two coordinates
  prob.addConstraint(sum(NX, [&](auto i) { return wx[i]; }) == 1.0);
  prob.addConstraint(sum(NY, [&](auto j) { return wy[j]; }) == 1.0);

  // wx, wy and wxy must be consistent
  prob.addConstraints(NX, [&](auto i) {
    return wx[i] == sum(NY, [&](auto j) { return wxy[i][j]; });
  });
  prob.addConstraints(NY, [&](auto j) {
    return wy[j] == sum(NX, [&](auto i) { return wxy[i][j]; });
  });
  
  // The coordinates and the corresponding function value we want to approximate
  prob.addConstraint(x == sum(NX, [&](auto i) { return X[i]*wx[i]; }));
  prob.addConstraint(y == sum(NY, [&](auto j) { return Y[j]*wy[j]; }));
  prob.addConstraint(f == sum(NX, [&](auto i) {
    return sum(NY, [&](auto j) { return FXY[i][j]*wxy[i][j]; });
  }));

  // Set a lower bound on x and y to make the problem more interesting
  x.setLB(2.0);
  y.setLB(2.0);

  // Set objective
  prob.setObjective(f);

  // Write out the model in case we want to look at it.
  prob.writeProb("sosquad.lp", "l");

  // Solve the problem
  prob.optimize();

  auto mipStatus = prob.attributes.getMipStatus();
  switch (mipStatus) {
    case MIPStatus::NotLoaded:
    case MIPStatus::LPNotOptimal:
      cout << "Solving not started" << endl;
      break;
    case MIPStatus::LPOptimal:
      cout << "Root LP solved" << endl;
      break;
    case MIPStatus::Unbounded:
      cout << "LP unbounded" << endl;
      break;
    case MIPStatus::NoSolutionFound:
    case MIPStatus::Infeasible:
      cout << "MIP search started, no solution" << endl;
      break;
    case MIPStatus::Solution:
    case MIPStatus::Optimal:
      cout << "MIP solution: " << prob.attributes.getObjVal() << endl;
      break;
  }

  cout << x.getName() << ": " << x.getSolution() << endl;
  cout << y.getName() << ": " << y.getSolution() << endl;
  cout << f.getName() << ": " << f.getSolution() << endl;

  return 0;
}

Back to examples browserNext example