Skip to content
Module 10 of 1055 min readAdvanced

Solvers in practice — cvxpy, scipy, MOSEK

How to formulate a problem so a solver eats it. Modelling languages, warm-starting, infeasibility diagnosis, the pre-flight checklist before you trust a number.

100%

Listen along

Read “Solvers in practice — cvxpy, scipy, MOSEK” aloud

Plays in your browser using on-device text-to-speech — nothing leaves the page.

The theory of optimisation collides with practice at the solver. Choosing the right solver, formulating the problem in a way the solver can digest, diagnosing infeasibility, and validating the answer are the skills that turn a textbook problem statement into a number you put on a trade ticket.

The modelling layer — cvxpy

cvxpy (Python) and JuMP (Julia) are domain-specific languages for disciplined convex programming. Write the problem in math-like syntax; the library compiles it into the canonical form expected by underlying solvers (ECOS, SCS, MOSEK, GUROBI). This abstraction eliminates the bug class where you 'thought' a problem was convex but the solver couldn't tell.

python
import cvxpy as cp
import numpy as np
n = len(mu)
w = cp.Variable(n)
risk = cp.quad_form(w, Sigma)
ret = mu @ w
objective = cp.Minimize(0.5 * risk - lam * ret)
constraints = [cp.sum(w) == 1, w >= 0]
prob = cp.Problem(objective, constraints)
prob.solve(solver=cp.OSQP)

Choosing a solver

  • LP: GLPK (free), HiGHS (free), Gurobi, CPLEX, MOSEK (commercial).
  • QP: OSQP (free, ADMM-based), Gurobi, MOSEK.
  • SOCP / SDP: ECOS, SCS (free), MOSEK (commercial, best-in-class).
  • MILP / MIQP: SCIP, HiGHS, Gurobi, CPLEX (commercial dominate for hard MIPs).
  • General non-linear: IPOPT (interior point), Knitro.

Warm-starting

Solving a sequence of similar problems (rolling-horizon optimisation, efficient-frontier sweep) is much faster if each new solve starts from the previous solution. OSQP, ECOS, Gurobi, and MOSEK all support warm-starting; cvxpy's warm_start=True flag forwards primal/dual variables between solves.

Pre-flight checklist

  1. Is the problem convex? Run cp.Problem.is_dcp() — cvxpy's DCP check.
  2. Are constraints feasible? Try minimising 0 subject to constraints; if infeasible, problem reports infeasible.
  3. Are inputs sane? Check that Σ is PSD (eigvals ≥ 0), μ has no NaNs/infs, return targets are achievable.
  4. Is the solution unique? Strict convexity + strict feasibility imply uniqueness; for QPs, PD P ensures unique x*.
  5. Are dual variables sane? Lagrange multipliers should be non-negative on inequality constraints, finite, and report shadow prices that make economic sense.

Diagnosing infeasibility

Solver reports status 'infeasible' — what now? Strategies:

  • Relax inequality constraints one at a time; identify the binding offender.
  • Solve the elastic-mode reformulation: replace each constraint g ≤ b with g - b ≤ s, s ≥ 0; minimise Σ s. Constraints whose s > 0 in the relaxed solution are the infeasible ones.
  • Check the dimensionality: is your constraint matrix accidentally rank-deficient with conflicting rows?

Numerical scaling

Solvers are happiest when the variables and constants are all in the unit range. A portfolio with weights in [-1, 1], returns in [-0.5, 0.5], variances around 0.01 — fine. A portfolio whose objective combines variance (10⁻⁴) and dollar P&L (10⁹) — solver will struggle. Pre-scale objectives and constraints to unit-order before passing to the solver.

Validation

Never trust a solver's answer without independent checks:

  • Re-evaluate the objective at the reported solution.
  • Check constraint residuals are within tolerance.
  • Spot-check edge cases (zero-weight assets, binding constraints, etc.).
  • Run a second solver on the same problem — agreement is strong evidence of correctness.
  • Stress-test by perturbing inputs slightly and confirming the solution moves smoothly.

Production patterns

  • Caching: results of identical inputs should be cached.
  • Logging: capture inputs, solver status, runtime, KKT residuals.
  • Fallbacks: if MOSEK fails, retry with ECOS or relaxed tolerance.
  • Regression tests: maintain a corpus of known problems and their expected solutions; run on every code change.
  • Sensitivities: compute and display ∂x*/∂μ, ∂x*/∂Σ; let users see how an input change moves the answer.

Exercise

A portfolio optimisation in production starts returning 'solver infeasible' on Monday morning. The same code worked Friday. What's your diagnostic playbook?

Loading progress…
LeadAfrikPublic Economics Hub