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

Catenary - Solving a QCQP

Description
This model finds the shape of a hanging chain by minimizing its potential energy.

catenary_cpp.zip[download all files]

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





Catenary.cpp

// (c) 2024-2025 Fair Isaac Corporation
#include <stdexcept> // For throwing exceptions
#include <xpress.hpp>

using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::pow;

/*
 * QCQP problem (linear objective, convex quadratic constraints) Based on AMPL
 * model catenary.mod (Source:
 * http://www.orfe.princeton.edu/~rvdb/ampl/nlmodels/) This model finds the
 * shape of a hanging chain by minimizing its potential energy.
 */

const int N = 100;            // Number of chainlinks
const int L = 1;              // Difference in x-coordinates of endlinks
const double H = 2.0 * L / N; // Length of each link

int main() {
  try {
    XpressProblem prob;

    /* VARIABLES */

    // Continuous variables to denote x and y positions of each chainlink
    std::vector<Variable> x =
        prob.addVariables(N + 1)
            .withName("x(%d)")
            .withLB(XPRS_MINUSINFINITY) // Overwrite default lowerbound from 0
                                        // to minus infinity
            .toArray();

    std::vector<Variable> y =
        prob.addVariables(N + 1)
            .withName("y(%d)")
            .withLB(XPRS_MINUSINFINITY) // Overwrite default lowerbound from 0
                                        // to minus infinity
            .toArray();

    /* CONSTRAINTS */

    // Position of left endpoint / anchor
    x[0].fix(0);
    y[0].fix(0);
    // Position of right endpoint / anchor
    x[N].fix(L);
    y[N].fix(0);

    // Positions of chainlinks: forall(j in 1..N) (x(j)-x(j-1))^2 +
    // (y(j)-y(j-1))^2 <= H^2
    for (int j = 1; j <= N; j++) {
      Expression link_j = pow(x[j] - x[j - 1], 2.0) + pow(y[j] - y[j - 1], 2.0);
      prob.addConstraint(link_j <= H * H).setName(xpress::format("link_%d", j));
    }

    /* OBJECTIVE */

    // Minimise the potential energy: sum(j in 1..N) (y(j-1) + y(j)) / 2
    LinTermList obj;
    for (int i = 1; i <= N; i++) {
      obj.addTerm(0.5, y[i - 1])
          .addTerm(0.5, y[i]); // Add ( y[j-1] + y[j] ) / 2
    }
    obj.compress(); // Add coefficients of the same variable together: 0.5*y[1]
                    // + 0.5*y[1] ---> 1*y[1]
    prob.setObjective(obj, ObjSense::Minimize);

    /* SOLVE & PRINT */

    prob.writeProb("Catenary.lp");
    prob.optimize("");

    // Check the solution status
    if (prob.attributes.getSolStatus() != SolStatus::Optimal &&
        prob.attributes.getSolStatus() != SolStatus::Feasible) {
      std::ostringstream oss;
      oss << prob.attributes
                 .getSolStatus(); // Convert xpress::SolStatus to String
      throw std::runtime_error("Optimization failed with status " + oss.str());
    }

    std::vector<double> sol = prob.getSolution();

    std::cout << "*** Solution ***" << std::endl;
    std::cout << "Objective value: " << prob.attributes.getObjVal()
              << std::endl;
    for (int i = 0; i <= N; i++) {
      std::cout << i << ": " << x[i].getValue(sol) << ", " << y[i].getValue(sol)
                << std::endl;
    }
    return 0;
  } catch (std::exception &e) {
    std::cout << "Exception: " << e.what() << std::endl;
    return -1;
  }
}

Back to examples browserPrevious exampleNext example