library(CVXR)
x <- Variable(2)
prob <- Problem(Minimize(sum_squares(x)), list(x >= 1))
opt_val <- psolve(prob) # returns optimal value directly
x_val <- value(x) # extract variable value
prob_status <- status(prob) # check statusWhat’s New in CVXR 1.8.x
Complete Rewrite Using S7
CVXR 1.8.x is a ground-up rewrite using R’s S7 object system, designed to be isomorphic with CVXPY 1.8.1 for long-term maintainability. This page summarizes the key changes from CVXR 1.x that may affect users.
Note: Package authors encountering Rmosek issues while submitting to CRAN should especially see CRAN Submission Tip below.
New Features
- S7 class system replaces S4 for all expression, constraint, and problem classes. Significantly faster construction and method dispatch.
- 13 solvers: CLARABEL (default), SCS, OSQP, HiGHS, MOSEK, Gurobi, GLPK, GLPK_MI, ECOS, ECOS_BB, CPLEX, CVXOPT, and PIQP.
- Mixed-integer programming via GLPK_MI, ECOS_BB, Gurobi, CPLEX, or HiGHS (
boolean = TRUEorinteger = TRUEinVariable()). - Parameter support via
Parameter()class andEvalParamsreduction. - 50+ atom classes covering LP, QP, SOCP, SDP, exponential cone, and power cone problems.
- DPP (Disciplined Parameterized Programming) for efficient parameter re-solve with compilation caching.
- DGP (Disciplined Geometric Programming) via
psolve(prob, gp = TRUE). - DQCP (Disciplined Quasiconvex Programming) via
psolve(prob, qcp = TRUE). - Complex variable support via
Variable(n, complex = TRUE). - Warm-start support for 6 solvers (OSQP, SCS, Gurobi, MOSEK, CLARABEL, HiGHS).
- Matrix package interoperability via
as_cvxr_expr(). Matrix package objects (dgCMatrix,dgeMatrix,dsCMatrix,ddiMatrix,sparseVector) use S4 dispatch which preempts S7/S3, so they cannot be used directly with CVXR operators. Wrapping withas_cvxr_expr()converts them to CVXRConstantobjects while preserving sparsity (unlikeas.matrix()which densifies). Base Rmatrixandnumericobjects work natively without wrapping.
Breaking Changes from CVXR 1.x
New solve interface
The primary solve function is now psolve(), which returns the optimal value directly:
The old solve() still works but returns a compatibility list:
result <- solve(prob)
result$value # optimal value
result$getValue(x) # variable value (deprecated)
result$status # problem statusAPI changes
| Old API | New API |
|---|---|
solve(problem) |
psolve(problem) |
result$getValue(x) |
value(x) |
result$value |
return value of psolve() |
result$status |
status(problem) |
result$getDualValue(con) |
dual_value(con) |
problem_status(prob) |
status(prob) |
problem_solution(prob) |
solution(prob) |
get_problem_data(prob, solver) |
problem_data(prob, solver) |
Axis parameter changes
The axis parameter now uses R’s apply() convention (1-based indexing):
| Old CVXR | New CVXR | Meaning |
|---|---|---|
axis = 1 |
axis = 1 |
Row-wise reduction (unchanged) |
axis = 2 |
axis = 2 |
Column-wise reduction (unchanged) |
axis = NA |
axis = NULL |
All entries |
Passing axis = 0 now produces an informative error with migration guidance.
PSD constraints
PSD constraints use PSD(A - B) instead of A %>>% B (though %>>% and %<<% operators are still available for backward compatibility).
Solver changes
- Removed: CBC
- Added: HiGHS (LP, QP, MILP), Gurobi (LP, QP, SOCP, MIP), CVXOPT (LP, SOCP), PIQP (QP)
- Default solver: CLARABEL (replaces ECOS)
Supported solvers
| Solver | R Package | Type | Problem Classes |
|---|---|---|---|
| CLARABEL | clarabel (>= 0.11.2) |
Conic | LP, QP, SOCP, SDP, ExpCone, PowCone |
| SCS | scs (>= 3.2.7) |
Conic | LP, QP, SOCP, SDP, ExpCone, PowCone |
| MOSEK | Rmosek (>= 11.1.1) |
Conic | LP, QP, SOCP, SDP, ExpCone, PowCone |
| ECOS | ECOSolveR (>= 0.6) |
Conic | LP, SOCP, ExpCone |
| ECOS_BB | ECOSolveR (>= 0.6) |
Conic | LP, SOCP, ExpCone + MI |
| GUROBI | gurobi (>= 13.0.1) |
Conic/QP | LP, QP, SOCP, MI |
| GLPK | Rglpk (>= 0.6.5.1) |
Conic | LP |
| GLPK_MI | Rglpk (>= 0.6.5.1) |
Conic | LP, MILP |
| HIGHS | highs (>= 1.12.0.3) |
Conic/QP | LP, QP, MILP |
| CVXOPT | cccp (>= 0.3.3) |
Conic | LP, SOCP |
| OSQP | osqp (>= 1.0.0) |
QP | LP, QP |
| CPLEX | Rcplex (>= 0.3.8) |
QP | LP, QP, MI |
| PIQP | piqp (>= 0.6.2) |
QP | LP, QP |
New Atoms and Functions
Convenience atoms
| Function | Description |
|---|---|
ptp(x, axis, keepdims) |
Peak-to-peak (range): max(x) - min(x) |
cvxr_mean(x, axis, keepdims) |
Arithmetic mean along an axis |
cvxr_std(x, axis, keepdims, ddof) |
Standard deviation |
cvxr_var(x, axis, keepdims, ddof) |
Variance |
vdot(x, y) |
Vector dot product (inner product) |
cvxr_outer(x, y) |
Outer product of two vectors |
inv_prod(x) |
Reciprocal of product of entries |
loggamma(x) |
Elementwise log of gamma function |
log_normcdf(x) |
Elementwise log of standard normal CDF |
cummax_expr(x, axis) |
Cumulative maximum along an axis |
dotsort(X, W) |
Weighted sorted dot product |
Boolean logic atoms
For mixed-integer programming: Not(), And(), Or(), Xor(), implies(), iff().
Other new atoms
perspective(f, s)for perspective functionsFiniteSet(expr, values)constraint for discrete optimizationceil_expr(),floor_expr()for DQCP problems
Backward-Compatibility Aliases
tv()is an alias fortotal_variation()norm2(x)is an alias forp_norm(x, 2)multiply(x, y)is an alias for elementwise multiplicationinstalled_solvers()lists available solver packages- Old
solve()still works and returns a compatibility list - Old function names (
problem_status,getValue, etc.) still work but emit once-per-session deprecation warnings
Migration Guide
To migrate code from CVXR 1.x to 1.8.x:
Replace
result <- solve(problem)withopt_val <- psolve(problem)Replace
result$getValue(x)withvalue(x)Replace
result$valuewith the return value frompsolve()Replace
result$statuswithstatus(problem)Replace
result$getDualValue(con)withdual_value(con)Replace
add_to_solver_blacklist()calls (no longer needed)Update solver names:
"ECOS"→"CLARABEL","GLPK"→"HIGHS"Update
axisarguments:axis = NA→axis = NULL(row/column axis values 1 and 2 are unchanged)Replace
A %>>% BwithPSD(A - B)if desiredWrap Matrix package objects with
as_cvxr_expr()before using them in CVXR expressions (e.g.,as_cvxr_expr(A) %*% xinstead ofA %*% xwhenAis adgCMatrixor other Matrix class). This preserves sparsity. Base R matrices need no wrapping.Dimension-preserving operations. CVXR 1.8 preserves 2D shapes throughout, matching CVXPY. In particular, axis reductions like
sum_entries(X, axis = 2)now return a proper row vector of shape(1, n)rather than collapsing to a 1D vector. When comparing such a result with an R numeric vector (which CVXR treats as a column), you may need to uset()ormatrix(..., nrow = 1)to match shapes:## Old (worked in CVXR 1.x because axis reductions were 1D): sum_entries(X, axis = 2) == target_vec ## New (wrap target as row vector to match the (1, n) shape): sum_entries(X, axis = 2) == t(target_vec)Similarly, if you extract a scalar from a CVXR result and need a plain numeric value, use
as.numeric()to drop the matrix dimensions.
CRAN Submission Tip
If you encounter issues involving Rmosek package while submitting your package to CRAN, just include the following code in <your_pkg>/R/zzz.R to resolve the issue.
## Content of <your_pkg>/R/zzz.R
.onLoad <- function(libname, pkgname) {
CVXR::exclude_solvers("MOSEK")
}
.onUnload <- function(libname, pkgname) {
CVXR::include_solvers("MOSEK")
}Known Limitations
- Matrix package objects (
dgCMatrix,dgeMatrix,ddiMatrix,sparseVector) cannot be used directly with CVXR operators due to S4 dispatch preempting S7/S3. Wrap withas_cvxr_expr()first. Base Rmatrix/numericwork natively. This is an R dispatch limitation requiring upstream changes in S7 and/or Matrix. - Warm-start for HiGHS is blocked (R
highspackage lackssetSolution()API). - Derivative/sensitivity API deferred (requires
diffcp, no R equivalent).