
Catching Up with CVXPY — A Progress Report
Stanford University
2026-02-19
As you probably guessed, that was a joke.
Really, this is hard work.
Let me give you a proper progress report.
1.0-15 (S4 classes)But:
For the Python developers in the room:
“Everything that exists is an object. Everything that happens is a function call.”
— John Chambers
R has four major OOP systems. Yes, four.
S3 — Duck typing
S4 — Formal & strict
R6 — Reference semantics
The Abs atom in old CVXR — one of 100+ atoms to implement:
.Abs <- setClass("Abs", representation(x = "Expression"),
contains = "Elementwise")
Abs <- function(x) { .Abs(x = x) }
setMethod("initialize", "Abs", function(.Object, ..., x) {
.Object@x <- x
callNextMethod(.Object, ..., atom_args = list(.Object@x))
})
setMethod("to_numeric", "Abs", function(object, values) abs(values[[1]]))
setMethod("sign_from_args", "Abs", function(object) c(TRUE, FALSE))
setMethod("is_atom_convex", "Abs", function(object) TRUE)
setMethod("is_atom_concave", "Abs", function(object) FALSE)
setMethod("is_incr", "Abs", function(object, idx) is_nonneg(object@args[[idx]]))
setMethod("is_decr", "Abs", function(object, idx) is_nonpos(object@args[[idx]]))
setMethod("is_pwl", "Abs", function(object) is_pwl(object@args[[1]]))
# ... and more for .grad, .domain, graph_implementation ...Now imagine doing this 100+ times, keeping in sync with CVXPY.
CVXR/
├── DESCRIPTION # Metadata, dependencies, version
├── R/ # All R source files ← FLAT directory!
│ ├── expression.R
│ ├── variable.R
│ ├── abs.R
│ └── ...246 files...
├── src/ # C/C++ code (canonicalization)
├── man/ # Documentation (one .Rd per export)
├── tests/ # Test files
└── vignettes/ # Long-form documentation
The problem: CVXPY has a deep directory tree with naturally occurring filename clashes (e.g. atoms/affine/sum.py vs atoms/axis_atom/sum.py).
R packages require a flat R/ directory.
How do you flatten without collisions — and still find things?
rsrc_tree SolutionDevelop in an isomorphic tree:
rsrc_tree/
├── atoms/
│ ├── elementwise/
│ │ ├── abs.R
│ │ └── exp.R
│ └── affine/
│ ├── reshape.R
│ └── index.R
├── reductions/
│ └── dcp2cone/
│ └── canonicalizers/
└── ...
Every file starts with:
Build script flattens to R/:
R/
├── 045_atoms_elementwise_abs.R
├── 046_atoms_elementwise_exp.R
├── 047_atoms_affine_reshape.R
├── 048_atoms_affine_index.R
├── 112_reductions_dcp2cone_...R
└── ...
Numeric prefixes control load order.
One script. Automatic. Reversible.
The next generation R OOP system (R Consortium, 2024):
Abs <- new_class("Abs", parent = Elementwise,
constructor = function(x) {
x <- as_expr(x)
new_object(S7_object(), args = list(x), shape = x@shape)
}
)
method(sign_from_args, Abs) <- function(x) list(is_nonneg = TRUE, is_nonpos = FALSE)
method(is_atom_convex, Abs) <- function(x) TRUE
method(is_atom_concave, Abs) <- function(x) FALSE
method(is_incr, Abs) <- function(x, idx, ...) is_nonneg(x@args[[idx + 1L]])
method(is_decr, Abs) <- function(x, idx, ...) is_nonpos(x@args[[idx + 1L]])
method(numeric_value, Abs) <- function(x, values, ...) abs(values[[1L]])Clean. Compact. One file per atom.
CVXPY (Python)
CVXR (S7/R)
Abs <- new_class("Abs",
parent = Elementwise, ...)
method(sign_from_args, Abs) <-
function(x)
list(is_nonneg = TRUE,
is_nonpos = FALSE)
method(is_atom_convex, Abs) <-
function(x) TRUE
method(is_atom_concave, Abs) <-
function(x) FALSE
method(numeric_value, Abs) <-
function(x, values, ...)
abs(values[[1L]])The structure maps almost 1:1. Perhaps an AI can translate it…
A systematic, AI-assisted rewrite of CVXR.
With guardrails:
Structural
.py maps to an R .R at the same pathmethod()) — handles unary -x correctlyPractical
@lazyprop with R environmentsdecisions.md)3,247 test blocks, each annotated:
Cross-referenced with 1,641 CVXPY tests
Validation scripts to find gaps
Coverage:
| Category | Count |
|---|---|
| CVXPY parity | 514 |
| R-specific | 2,197 |
| Documented N/A | 699 |
| Total | 3,247 |
Features that old CVXR never had:
Well, let’s see…
67 working examples
From basic LP to advanced DGP/DPP/DQCP
All tested. All running. All documented.
Getting Started
Regression (9)
ML & Finance
Applications (14)
Advanced
Solvers
| Metric | Value |
|---|---|
| Expectations passing | 7,199 |
| Failures | 0 |
test_that() blocks |
3,247 |
| CVXPY parity tests | 514 / 1,641 |
| Source files | 246 |
| Solvers | 13 |
| Metric | Value |
|---|---|
| Atoms | 100+ |
| Canonicalizers | 47 |
| Documented examples | 67 |
| Design decisions | 150+ |
3,247 test blocks × ~2 expectations each = 7,199. 514 blocks map 1:1 to CVXPY tests; the rest are R-specific or documented N/A.
The S4 overhead that plagued old CVXR is gone:
S4 (old)
1.16 min
1,000 variables
2.54 GB memory
S7 (new)
228 ms
1,000 variables
828 KB memory
305x faster. 3,100x less memory.
Lasso regression, CLARABEL solver, median of 5 runs:

Build time (problem construction):
| Size | Old (S4) | New (S7) |
|---|---|---|
| 200x50 | 10 ms | 2 ms |
| 500x100 | 8 ms | 2 ms |
| 1000x200 | 8 ms | 2 ms |
| 2000x500 | 9 ms | 2 ms |
5x faster builds, consistent across sizes. Solver time dominates for large problems.
Up to 3.3x faster for small/medium problems. For large problems, solver dominates — which is exactly where you want the bottleneck.
“Premature optimization is the root of all evil.”
— Donald Knuth
profvis package)v1.8.0-9044
Aligned with CVXPY 1.8.1
The isomorphic structure isn’t just for the rewrite — it’s the maintenance strategy:
rsrc_tree/ + ## CVXPY SOURCE: annotations make diffs obviousNo-regression guarantee:
Every test_that() block is annotated:
If something breaks:
The breadcrumbs lead you straight back to CVXPY’s ground truth.
The same process that built 1.8.1 in 10 days keeps us in sync going forward.
Coming soon:
R CMD check clean)Deferred:
diffcp)We welcome feedback from the CVXPY and CVXR community.
Is this a good plan? Have we overlooked something? Tell us.
Links:
Contact:

We welcome your feedback!
Yes, this presentation was also made by Claude — in about an hour, from a one-page brief. It read the git history of 3 repos, ran benchmarks comparing old and new CVXR, synthesized 150+ design decisions, and wrote the typing demo. Even the SVG terminal jokes.

CVXR | Narasimhan & Fu