What's New in CVXR 1.8

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

Overview

CVXR 1.8 is a ground-up rewrite using R's S7 object system, designed to mirror CVXPY 1.8 for long-term maintainability. It is approximately 4--5x faster than the previous S4-based release and adds many new features.

This vignette summarizes the key changes from CVXR 1.x that may affect existing users. For the introductory tutorial, see vignette("cvxr_intro"). For worked examples, visit the CVXR website.

New Solve Interface

The primary solve function is now psolve(), which returns the optimal value directly. Variable values are extracted with value() and problem status with status():

library(CVXR)
x <- Variable(2, name = "x")
prob <- Problem(Minimize(sum_squares(x)), list(x >= 1))
opt_val <- psolve(prob, solver = "CLARABEL")
opt_val
value(x)
status(prob)

The old solve() still works and returns a backward-compatible list:

result <- solve(prob, solver = "CLARABEL")
result$value
result$status

API Changes

| Old (CVXR 1.x) | New (CVXR 1.8) | |-----------------|----------------| | 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) | | get_problem_data(prob, solver) | problem_data(prob, solver) |

Old function names still work but emit once-per-session deprecation warnings.

13 Solvers

CVXR 1.8 ships with four built-in solvers and supports nine additional solvers via optional packages:

installed_solvers()

| Solver | R Package | Problem Classes | |--------|-----------|-----------------| | CLARABEL | clarabel | LP, QP, SOCP, SDP, ExpCone, PowCone | | SCS | scs | LP, QP, SOCP, SDP, ExpCone, PowCone | | OSQP | osqp | LP, QP | | HIGHS | highs | LP, QP, MILP | | MOSEK | Rmosek | LP, QP, SOCP, SDP, ExpCone, PowCone | | GUROBI | gurobi | LP, QP, SOCP, MIP | | ECOS / ECOS_BB | ECOSolveR | LP, SOCP, ExpCone (+ MIP) | | GLPK / GLPK_MI | Rglpk | LP (+ MILP) | | CPLEX | Rcplex | LP, QP, MIP | | CVXOPT | cccp | LP, SOCP, SDP | | PIQP | piqp | LP, QP |

CLARABEL is the default solver and handles the widest range of problem types among the built-in solvers. You can specify a solver explicitly:

psolve(prob, solver = "SCS")

Solver exclusion

You can temporarily exclude solvers from automatic selection without uninstalling them:

available_solvers()
exclude_solvers("SCS")
available_solvers()
include_solvers("SCS")

Disciplined Programming Extensions

DGP (Geometric Programming)

Geometric programs are solved by converting to convex form via log-log transformation:

x <- Variable(pos = TRUE, name = "x")
y <- Variable(pos = TRUE, name = "y")
obj <- Minimize(x * y)
constr <- list(x * y >= 40, x <= 20, y >= 2)
prob <- Problem(obj, constr)
cat("Is DGP:", is_dgp(prob), "\n")
opt_val <- psolve(prob, gp = TRUE, solver = "CLARABEL")
cat("Optimal:", opt_val, " x =", value(x), " y =", value(y), "\n")

DQCP (Quasiconvex Programming)

Quasiconvex problems are solved via bisection:

x <- Variable(name = "x")
prob <- Problem(Minimize(ceil_expr(x)), list(x >= 0.7, x <= 1.5))
cat("Is DQCP:", is_dqcp(prob), "\n")
opt_val <- psolve(prob, qcp = TRUE, solver = "CLARABEL")
cat("Optimal:", opt_val, " x =", value(x), "\n")

DPP (Parameterized Programming)

Parameters allow efficient re-solving when only data changes:

n <- 5
x <- Variable(n, name = "x")
lam <- Parameter(nonneg = TRUE, name = "lambda", value = 1.0)

set.seed(42)
A <- matrix(rnorm(10 * n), 10, n)
b <- rnorm(10)

prob <- Problem(Minimize(sum_squares(A %*% x - b) + lam * p_norm(x, 1)))
cat("Is DPP:", is_dpp(prob), "\n")
psolve(prob, solver = "CLARABEL")
value(x)

Changing the parameter and re-solving reuses the cached compilation:

value(lam) <- 10.0
psolve(prob, solver = "CLARABEL")
value(x)

Mixed-Integer Programming

Variables can be declared boolean or integer:

x <- Variable(3, integer = TRUE, name = "x")
prob <- Problem(Minimize(sum(x)), list(x >= 0.5, x <= 3.5))
psolve(prob, solver = "HIGHS")
value(x)

Complex Variables

CVXR supports complex-valued optimization:

z <- Variable(2, complex = TRUE, name = "z")
prob <- Problem(Minimize(p_norm(z, 2)), list(z[1] == 1 + 2i))
psolve(prob, solver = "CLARABEL")
value(z)

Boolean Logic

For mixed-integer formulations, boolean logic atoms are available:

b1 <- Variable(boolean = TRUE, name = "b1")
b2 <- Variable(boolean = TRUE, name = "b2")
## implies() returns an Expression; compare with == 1 to make a constraint
prob <- Problem(Maximize(b1 + b2),
                list(implies(b1, b2) == 1, b1 + b2 <= 1))
psolve(prob, solver = "HIGHS")
cat("b1 =", value(b1), " b2 =", value(b2), "\n")

Decomposed Solve API

For bootstrapping or simulation, you can compile once and solve many times:

pd <- problem_data(prob, solver = "CLARABEL")
chain <- pd$chain

# In a loop:
raw <- solve_via_data(chain, warm_start = FALSE, verbose = FALSE)
result <- problem_unpack_results(prob, raw$solution, chain, raw$inverse_data)

Visualization

visualize() provides problem introspection in text or interactive HTML:

x <- Variable(3, name = "x")
prob <- Problem(Minimize(p_norm(x, 1) + sum_squares(x)),
                list(x >= -1, sum(x) == 1))
visualize(prob)

For interactive HTML output with collapsible expression trees, DCP analysis, and curvature coloring:

visualize(prob, html = "my_problem.html")

New Atoms

Convenience atoms

| Function | Description | |----------|-------------| | ptp(x) | Peak-to-peak (range): max(x) - min(x) | | cvxr_mean(x) | Arithmetic mean along an axis | | cvxr_std(x) | Standard deviation | | cvxr_var(x) | Variance | | vdot(x, y) | Vector dot product | | cvxr_outer(x, y) | Outer product | | inv_prod(x) | Reciprocal of product | | loggamma(x) | Log of gamma function | | log_normcdf(x) | Log of standard normal CDF | | cummax_expr(x) | Cumulative maximum | | dotsort(X, W) | Weighted sorted dot product |

Math function dispatch

Standard R math functions work directly on CVXR expressions:

x <- Variable(3, name = "x")
abs(x)     # elementwise absolute value
sqrt(x)    # elementwise square root
sum(x)     # sum of entries
max(x)     # maximum entry
norm(x, "2")  # Euclidean norm

Boolean logic atoms

Not(), And(), Or(), Xor(), implies(), iff() — for mixed-integer programming with boolean variables.

Other new atoms

Migration Guide

To migrate code from CVXR 1.x to 1.8:

  1. Replace result <- solve(problem) with opt_val <- psolve(problem)
  2. Replace result$getValue(x) with value(x)
  3. Replace result$status with status(problem)
  4. Replace result$getDualValue(con) with dual_value(con)
  5. Replace axis = NA with axis = NULL (axis values 1 and 2 are unchanged)
  6. Update solver preferences: the default is now CLARABEL (was ECOS)
  7. Wrap Matrix package objects with as_cvxr_expr() before using them with CVXR operators (e.g., as_cvxr_expr(A_sparse) %*% x instead of A_sparse %*% x). This preserves sparsity --- unlike as.matrix(), which densifies. Base R matrix and numeric objects work natively without wrapping.
  8. 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 vector), you may need to use t() or matrix(..., nrow = 1) to match shapes. For example: r ## 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.

All old function names continue to work with deprecation warnings.

Further Reading



Try the CVXR package in your browser

Any scripts or data that you put into this service are public.

CVXR documentation built on March 6, 2026, 9:10 a.m.