CVXPYgen takes a parametrized CVXPY convex optimization problem and generates a custom C solver tailored to that problem family. The generated code provides interfaces in C/C++ (for embedded applications) and Python/CVXPY (for prototyping and desktop applications).
- Problem types: LP, QP, SOCP
- Solvers: OSQP, QOCOGEN, CLARABEL, SCS, ECOS, PDAQP
- Extras: differentiable QPs, explicit solutions for QPs
Problems need to be modeled according to disciplined parametrized programming (DPP). For more details, see our slides and manuscript. If you use this code, please cite
@article{schaller2022embedded,
title={Embedded code generation with CVXPY},
author={Schaller, Maximilian and Banjac, Goran and Diamond, Steven and Agrawal, Akshay and Stellato, Bartolomeo and Boyd, Stephen},
journal={IEEE Control Systems Letters},
volume={6},
pages={2653--2658},
year={2022},
publisher={IEEE}
}
pip install cvxpygen
The CLARABEL solver requires Rust and Eigen. Example notebooks require marimo and matplotlib.
import cvxpy as cp
from cvxpygen import cpg
# define CVXPY problem
m, n = 3, 2
A = cp.Parameter((m, n), name='A')
b = cp.Parameter(m, name='b')
x = cp.Variable(n, name='x')
problem = cp.Problem(cp.Minimize(cp.sum_squares(A @ x - b)), [x >= 0])
# generate code
cpg.generate_code(problem, code_dir='nonneg_ls')Important: Set name= on variables and parameters. You'll identify them with these names after code generation.
An extended version of this example is available under quick_start.py.
An HTML summary of the generated code is written to nonneg_ls/README.html, with instructions for using the generated code.
C interface
Here is how to compile the auto-generated example nonneg_ls/c/src/cpg_example.c (requires CMake). Parameters and variables are stored as Fortran-order (column-major) flat arrays in C.
Unix:
cd nonneg_ls/c/build
cmake ..
cmake --build . --target cpg_example
./cpg_exampleWindows:
cd nonneg_ls\c\build
cmake ..
cmake --build . --target cpg_example --config release
Release\cpg_exampleCVXPY interface
Here is how to register the Python module as a custom method for CVXPY.from nonneg_ls.cpg_solver import cpg_solve
problem.register_solve('CPG', cpg_solve)
b.value = ...
problem.solve(method='CPG', updated_params=['b'])
solution = x.valueYou may pass updated_params to tell the solver which parameters changed (omit it to update all).
| Argument | Type | Default | Description |
|---|---|---|---|
code_dir |
str |
'cpg_code' |
Output directory |
solver |
str |
'OSQP' for QPs 'QOCOGEN' for SOCPs |
Solver backend |
solver_opts |
dict |
{} |
Options forwarded to the solver |
enable_settings |
list[str] |
[] |
Solver settings to keep tuneable at runtime |
prefix |
str |
'' |
Symbol prefix for multi-problem codegen |
gradient |
bool |
False |
Enable gradient computation (QPs only) |
wrapper |
bool |
True |
Compile Python wrapper |
| Domain | Marimo notebook |
|---|---|
| Actuator allocation | actuator.py |
| Approximate dynamic programming | adp.py |
| Model predictive control | mpc.py |
| Portfolio construction | portfolio.py |
| Resource allocation | resource.py |
| Energy management | energy.py |
| Network flow optimization | network.py |
Run any marimo notebook as:
marimo edit actuator.py
CVXPYgen supports differentiating through quadratic programs, with an interface to CVXPYlayers (≤ 0.1.9):
cpg.generate_code(problem, code_dir='nonneg_ls_diff', gradient=True)
from nonneg_ls_diff.cpg_solver import forward, backward
from cvxpylayers.torch import CvxpyLayer
layer = CvxpyLayer(problem, parameters=[A, b], variables=[x],
custom_method=(forward, backward))As long as the problem is a QP, also conic solvers such as QOCOGEN or CLARABEL are supported. For more details, see our manuscript on differentiable QPs and examples/paper_grad for examples from the manuscript. If you use this feature, please cite
@article{schaller2025code,
title={Code generation for solving and differentiating through convex optimization problems},
author={Schaller, Maximilian and Boyd, Stephen},
journal={Optimization and Engineering},
pages={1--25},
year={2025},
publisher={Springer}
}
For QPs whose parameters are all vectors (no matrix parameters), the solution is a piecewise affine function of the parameters.
CVXPYgen integrates with PDAQP to precompute this map offline and generates code for instant evaluation online.
Advantages of such an explicit solver can include transparency, interpretability, reliability, and speed.
The number of inequality constraints (and non-differentiable atoms such as cp.norm1) should be small, since the number of affine pieces typically grows exponentially with those.
Important: Limit parameters with standard CVXPY constraints to keep the number of affine pieces tractable. For example, specify l <= p, p <= u where p is a CVXPY parameter and l and u are arrays:
prob = cp.Problem(obj, constr + [l <= p, p <= u])
cpg.generate_code(prob, code_dir='code_explicit', solver='explicit')To enable the computation of dual variables, set solver_opts={'dual': True}.
Control complexity via 'max_floats' (number of floating point numbers in the explicit solution, default 1e6) and 'max_regions' (maximum number of affine pieces, default 500) in solver_opts.
Store the explicit solution in half precision (instead of single precision) by setting 'fp16': True in solver_opts.
For more details, see our slides and manuscript on explicit solutions for QPs. If you use this feature, please cite
@article{schaller2025automatic,
title={Automatic generation of explicit quadratic programming solvers},
author={Schaller, Maximilian and Arnstr{\"o}m, Daniel and Bemporad, Alberto and Boyd, Stephen},
journal={arXiv preprint arXiv:2506.11513},
year={2025}
}
CVXPYgen 1.0 supports gradient=True along with solver='explicit'.
pip install pytest
cd tests
pytest