Convex Optimization with CVXPY

From Solvers to Modeling Languages

SciPy's minimize requires you to hand-write objective functions, gradients, and constraint functions. For convex problems, CVXPY provides a higher-level abstraction: you declare variables, write the objective and constraints using natural mathematical syntax, and CVXPY automatically verifies convexity, transforms the problem into standard form, and dispatches to an appropriate solver.

This separation of modeling from solving is the same philosophy behind MATLAB's CVX, Julia's Convex.jl, and AMPL.

Definition:

Convex Optimization Problem

A convex optimization problem has the form:

minxf(x)s.t.gi(x)0,  Ax=b\min_{\mathbf{x}} f(\mathbf{x}) \quad \text{s.t.} \quad g_i(\mathbf{x}) \le 0, \; \mathbf{A}\mathbf{x} = \mathbf{b}

where ff and all gig_i are convex functions, and the equality constraints are affine.

Key property: every local minimum is a global minimum. This makes convex problems fundamentally easier than general nonlinear programs.

import cvxpy as cp

x = cp.Variable(n)
objective = cp.Minimize(cp.sum_squares(A @ x - b))
constraints = [x >= 0, cp.sum(x) == 1]
prob = cp.Problem(objective, constraints)
prob.solve()
print(x.value)

Definition:

Disciplined Convex Programming (DCP) Rules

CVXPY uses DCP rules to verify convexity at problem construction time. Every expression is tagged with its curvature (convex, concave, affine, constant) and sign (positive, negative, unknown).

Composition rules (simplified):

  • convex+convex=convex\text{convex} + \text{convex} = \text{convex}
  • convexaffine=convex\text{convex} \circ \text{affine} = \text{convex}
  • nondecreasing convexconvex=convex\text{nondecreasing convex} \circ \text{convex} = \text{convex}
  • αconvex=convex\alpha \cdot \text{convex} = \text{convex} for α>0\alpha > 0

If CVXPY cannot verify convexity via DCP rules, it raises DCPError even if the problem is actually convex.

# DCP-compliant:
cp.norm(x, 1)           # convex
cp.quad_form(x, P)      # convex if P is PSD
cp.log_sum_exp(x)       # convex

# NOT DCP-compliant (even if convex):
cp.sqrt(cp.sum_squares(x))  # use cp.norm(x, 2) instead

Think of DCP as a type system for convexity. It trades generality for reliability: if DCP accepts your problem, it is guaranteed convex.

Definition:

Common CVXPY Atoms

CVXPY provides a library of atoms — functions with known convexity:

Atom Formula Curvature
cp.norm(x, p) xp\|\mathbf{x}\|_p Convex
cp.sum_squares(x) xi2\sum x_i^2 Convex
cp.quad_form(x, P) xTPx\mathbf{x}^T\mathbf{P}\mathbf{x} Convex (P PSD)
cp.log(x) log(x)\log(x) Concave
cp.exp(x) exe^x Convex
cp.log_sum_exp(x) logexi\log\sum e^{x_i} Convex
cp.lambda_max(X) λmax(X)\lambda_{\max}(\mathbf{X}) Convex
cp.lambda_min(X) λmin(X)\lambda_{\min}(\mathbf{X}) Concave
cp.trace(X) tr(X)\mathrm{tr}(\mathbf{X}) Affine

Definition:

Semidefinite Programming (SDP) in CVXPY

An SDP optimizes over positive semidefinite (PSD) matrices:

minXtr(CX)s.t.tr(AiX)=bi,  X0\min_{\mathbf{X}} \mathrm{tr}(\mathbf{C}\mathbf{X}) \quad \text{s.t.} \quad \mathrm{tr}(\mathbf{A}_i \mathbf{X}) = b_i, \; \mathbf{X} \succeq 0

In CVXPY:

X = cp.Variable((n, n), symmetric=True)
constraints = [X >> 0]  # PSD constraint
constraints += [cp.trace(A_i @ X) == b_i for ...]
prob = cp.Problem(cp.Minimize(cp.trace(C @ X)), constraints)
prob.solve(solver=cp.SCS)

SDPs arise in relaxations of combinatorial problems (MAX-CUT), robust beamforming, and covariance estimation.

Definition:

Second-Order Cone Programming (SOCP)

An SOCP involves second-order cone constraints:

Aix+bi2ciTx+di\|\mathbf{A}_i \mathbf{x} + \mathbf{b}_i\|_2 \le \mathbf{c}_i^T \mathbf{x} + d_i

In CVXPY, SOCP constraints arise naturally from norm expressions:

x = cp.Variable(n)
constraints = [cp.norm(A @ x + b, 2) <= c @ x + d]

SOCP is more general than LP (linear programming) and QP (quadratic programming) but less general than SDP. Many practical problems — robust least squares, Markowitz portfolio optimization, antenna beamforming — are naturally SOCPs.

Theorem: Strong Duality for Convex Problems

For a convex optimization problem satisfying Slater's condition (there exists a strictly feasible point), strong duality holds:

p=dp^\star = d^\star

where pp^\star is the primal optimal value and dd^\star is the dual optimal value. The dual variables at optimality are the Lagrange multipliers.

Strong duality means you can solve either the primal or the dual and get the same optimal value. CVXPY makes both primal and dual variables available after solving.

Example: LASSO Regression with CVXPY

Solve the LASSO problem minx12Axb22+λx1\min_{\mathbf{x}} \frac{1}{2}\|\mathbf{A}\mathbf{x} - \mathbf{b}\|_2^2 + \lambda \|\mathbf{x}\|_1 for sparse signal recovery.

Example: SDP for Maximum Cut Relaxation

Relax the MAX-CUT problem on a graph with n=6n=6 nodes using semidefinite programming.

LASSO Regularization Path

Explore how the LASSO solution changes as the regularization parameter lambda\\lambda varies from strong (all zeros) to weak (least squares).

Parameters

CVXPY Examples

python
LASSO, SDP, SOCP examples with CVXPY including warm starts and parameter sweeps.
# Code from: ch08/python/cvxpy_examples.py
# Load from backend supplements endpoint

Quick Check

Which of these is NOT a valid DCP expression in CVXPY?

cp.norm(x, 1) + cp.sum_squares(A @ x)

cp.sqrt(cp.sum_squares(x))

cp.quad_form(x, P) where P is PSD

cp.log_sum_exp(x)

Common Mistake: Reformulating to Satisfy DCP Rules

Mistake:

Writing cp.sqrt(x @ x) instead of cp.norm(x, 2). Both compute x2\|\mathbf{x}\|_2, but the first violates DCP (concave of convex) while the second is a recognized convex atom.

Correction:

Use CVXPY's built-in atoms whenever possible. Common reformulations:

  • xTx\sqrt{\mathbf{x}^T\mathbf{x}} \to cp.norm(x, 2)
  • x22\|\mathbf{x}\|_2^2 \to cp.sum_squares(x)
  • max(f,g)\max(f, g) \to cp.maximum(f, g) (both must be convex)
  • Minimize x2\|\mathbf{x}\|_2 \to minimize cp.sum_squares(x) (equivalent)

Key Takeaway

Use CVXPY for convex problems, SciPy for non-convex. CVXPY guarantees global optimality for convex problems and catches non-convex formulations at construction time. For non-convex problems, fall back to scipy.optimize.minimize with multiple random restarts.

Disciplined Convex Programming (DCP)

A ruleset for verifying convexity of optimization problems by tracking the curvature (convex/concave/affine) and sign of every sub-expression.

Related: Convex Function

Convex Function

A function ff satisfying f(λx+(1λ)y)λf(x)+(1λ)f(y)f(\lambda x + (1-\lambda)y) \le \lambda f(x) + (1-\lambda) f(y) for all x,yx, y and λ[0,1]\lambda \in [0,1].

Related: Disciplined Convex Programming (DCP)

Semidefinite Program (SDP)

An optimization problem where the variable is a symmetric matrix constrained to be positive semidefinite, with a linear objective and affine constraints.

Historical Note: CVXPY and the Convex Optimization Revolution

2000s

Stephen Boyd and Lieven Vandenberghe's textbook Convex Optimization (2004) made the theory accessible. Michael Grant's CVX (2004, MATLAB) and Steven Diamond's CVXPY (2016, Python) turned the theory into practical tools. The key insight was DCP: instead of checking convexity of arbitrary functions (undecidable in general), restrict to compositions of known-convex atoms.

Hierarchy of Convex Problem Classes

ClassObjectiveConstraintsSolverComplexity
LPLinearLinearHiGHS (simplex/IPM)Polynomial
QPQuadratic (convex)LinearOSQPPolynomial
SOCPLinearSOC + linearECOS, SCSPolynomial
SDPLinear (trace)PSD + affineSCS, MOSEKPolynomial
GPPosynomialPosynomialECOS (after transform)Polynomial