Optimisation with CVXPY

The previous post considered using SciPy to solve a simple optimisation problem.

Install CVXPY

Use pip to install the cvxpy package. I suggest doing this in a virtual environment.

pip install cvxpy

You can also install support for various additional solvers. For example:

pip install "cvxpy[CVXOPT,GLPK,GUROBI,MOSEK]"

📢 You’ll need to get licenses to use the GUROBI and MOSEK solvers.

Solvers

I tried the following solvers and all produced reasonable results:

  • CLARABEL
  • CVXOPT (rather slow!)
  • GLPK
  • MOSEK (see my installation notes for the MOSEK optimiser)
  • OSQP (which was used for the results reported here)
  • SCS and
  • SCIPY.

Optimisation with CVXPY

I set up the reference problem for CVXPY in a Jupter notebook. Here are the key components:

  1. Import a collection of packages.
  2. Specify the problem via a set of constants.
  3. Create CVXPY Variable objects for inflow, outflow and tank level.
  4. Set up constraints (as a list of relationships on the Variable objects, which is intuitive).
  5. Set up the objective function using the CVXPY Minimize class, specifying a term for the difference between outflow and demand, and smoothing terms for inflow and outflow.
  6. Create a CVXPY Problem object, supplying the objective function object and the list of constraints.
  7. Run the solve() method on the Problem object.

Results

I ran the problem using three optimisers: SCS, OSQP and MOSEK. The results are presented below.

SCS

The SCS (Splitting Conic Solver) solver is nominally for solving large-scale convex quadratic cone problems. The solver is Open Source, with code available on GitHub.

Optimised inflow and outflow rates as determined by CVXPY using the SCS optimiser.

The tank is initially a quarter full and the optimised solution has the inflow pump working hard to fill the tank at the start. The demand kicks in shortly after the start and is immediately satisfied. After the first demand cycle the inflow drops sharply to a lower rate that can satisfy all subsequent demand cycles. There is some (seemingly random) variability in the inflow rate during this second phase.

The SCS optimiser took 3.467 seconds to converge and the final value of the objective function was 7.279.

OSQP

The OSQP (Operator Splitting Quadratic Programming) solver is developed at the University of Oxford and is intended for solving quadratic programming problems. The solver is Open Source, with code available on GitHub.

Optimised inflow and outflow rates as determined by CVXPY using the OSQP optimiser.

The solution from the OSQP solver looks similar to that from the SCS solver.

The OSQP optimiser took 2.046 seconds to converge and the final value of the objective function was 7.515.

MOSEK

MOSEK provides a powerful and versatile optimisation package designed to solve a range of problem types. It is fast and reliable. The package is supported on Windows, macOS and Linux, and has interfaces for various other programming languages (notably both Python and R). It’s commercial software with academic and trial licenses available.

You can find my notes on installing the MOSEK solver here.

Optimised inflow and outflow rates as determined by CVXPY using the MOSEK optimiser.

The results from the MOSEK solver are grossly similar to those from the previous two solvers, however, after the rapid drop in inflow the rate during the second phase is essentially constant (whereas there was some apparently random variability produced by the other solvers).

The OSQP optimiser took 0.485 seconds to converge and the final value of the objective function was 7.143.

Summary

Here’s a summary of results for a selection of optimisers:

Optimiser Objective Time (s) Timestamp
MOSEK 7.143 0.485 2025-01-05 05:26:54
SCS 7.279 3.467 2025-01-05 05:26:43
OSQP 7.515 2.046 2025-01-05 05:27:12

The MOSEK optimiser achieved the best solution (lowest value of the objective function) and did so in the least time. For the purpose of comparison all of the optimisers have been run using their default values and no effort has been made to tune their performance (although in some cases the maximum number of iterations allowed was increased to ensure convergence).

The following post will use Pyomo to solve the same problem.