Epigraph
Convex Optimization in C++
Epigraph Documentation

Epigraph is a modern C++ interface to formulate and solve linear, quadratic and second order cone problems. It makes use of Eigen types and operator overloading for straightforward problem formulation.

Features

  • Flexible and intuitive way to formulate LPs, QPs and SOCPs
  • Dynamic parameters that can be changed without re-formulating the problem
  • Automatically clean up the problem and remove unused variables
  • Print the problem formulation and solver data for inspection

Dependencies

Supported Solvers

The solvers are included as submodules for convenience. Note that some solvers have more restrictive licenses which automatically override the Epigraph license when activated. Pass the listed argument to cmake during configuration to enable the solvers.

QP

  • OSQP -DENABLE_OSQP=TRUE. Apache-2.0 License.

SOCP

  • ECOS -DENABLE_ECOS=TRUE. GPLv3 License.

Usage

Download

git clone --recurse-submodules https://github.com/EmbersArc/Epigraph

CMake

To use Epigraph with a cmake project, simply enable the desired solvers, include the subdirectory and link the library.

set(ENABLE_OSQP TRUE)
set(ENABLE_ECOS TRUE)
add_subdirectory(Epigraph)
target_link_libraries(my_library epigraph)

Documentation

While the example below is likely enough to get you started, the full documentation can be found here.

Example

#include "epigraph.hpp"
#include <iostream>
// This example solves the portfolio optimization problem in QP form
using namespace cvx;
int main()
{
size_t n = 5; // Assets
size_t m = 2; // Factors
// Set up problem data.
double gamma = 0.5; // risk aversion parameter
Eigen::VectorXd mu(n); // vector of expected returns
Eigen::MatrixXd F(n, m); // factor-loading matrix
Eigen::VectorXd D(n); // diagonal of idiosyncratic risk
Eigen::MatrixXd Sigma(n, n); // asset return covariance
mu.setRandom();
F.setRandom();
D.setRandom();
mu = mu.cwiseAbs();
F = F.cwiseAbs();
D = D.cwiseAbs();
Sigma = F * F.transpose();
Sigma.diagonal() += D;
// Formulate QP.
// Declare variables with...
// addVariable(name) for scalars,
// addVariable(name, rows) for vectors and
// addVariable(name, rows, cols) for matrices.
VectorX x = op.addVariable("x", n);
// Available constraint types are equalTo(), lessThan(), greaterThan() and box()
qp.addConstraint(equalTo(x.sum(), 1.));
// Make mu dynamic in the cost function so we can change it later
qp.addCostTerm(x.transpose() * par(gamma * Sigma) * x - dynpar(mu).dot(x));
// Print the problem formulation for inspection
std::cout << qp << "\n";
// Create and initialize the solver instance.
osqp::OSQPSolver solver(qp);
// Print the canonical problem formulation for inspection
std::cout << solver << "\n";
// Solve problem and show solver output
const bool verbose = true;
solver.solve(verbose);
std::cout << "Solver message: " << solver.getResultString() << "\n";
std::cout << "Solver exitcode: " << solver.getExitCode() << "\n";
// Call eval() to get the variable values
std::cout << "Solution:\n" << eval(x) << "\n";
// Update data
mu.setRandom();
mu = mu.cwiseAbs();
// Solve again
// OSQP will warm start automatically
solver.solve(verbose);
std::cout << "Solution after changing the cost function:\n" << eval(x) << "\n";
}

See the [tests](tests) for more examples, including the same problem in SOCP form.

Problem Formulation

The following terms may be passed to the constraint functions:

Function Allowed expressions
equalTo() Affine == Affine
lessThan() Affine <= Affine or Norm2 + Affine <= Affine (SOCP)
greaterThan() Affine >= Affine or Affine >= Norm2 + Affine (SOCP)
box() Affine <= Affine <= Affine
addCostTerm() Affine (SOCP) or QuadForm + Affine (QP)

With the following expressions:

Expression Form
Affine p1 * x1 + p2 * x2 + ... + c
Norm2 (Affine1^2 + Affine2^2 + ...)^(1/2)
QuadForm x' * P * x where P is Hermitian
cvx::greaterThan
Constraint greaterThan(const Scalar &lhs, const Scalar &rhs)
Create a greater than or equal constraint: lhs >= rhs.
cvx::par
Scalar par(double p)
Creates a constant parameter.
cvx::equalTo
Constraint equalTo(const Scalar &lhs, const Scalar &rhs)
Create an equality constraint: lhs == rhs.
cvx::dynpar
Scalar dynpar(double &p)
Creates a dynamic parameter.
cvx::OptimizationProblem
Definition: problem.hpp:14
cvx::eval
double eval(const Scalar &s)
Evaluates the scalar.
cvx::OptimizationProblem::addCostTerm
void addCostTerm(const Scalar &term)
Add a cost term to the problem's cost function. This has to be a scalar.
cvx::OptimizationProblem::addConstraint
void addConstraint(const Constraint &constraint)
Add a single constraint to the problem. found.
cvx::OptimizationProblem::addVariable
Scalar addVariable(const std::string &name)
Creates and returns a variable.
epigraph.hpp
The Epigraph library. Enabled solvers will be included here.