knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
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.
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
| 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.
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")
You can temporarily exclude solvers from automatic selection without uninstalling them:
available_solvers() exclude_solvers("SCS") available_solvers() include_solvers("SCS")
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")
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")
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)
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)
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)
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")
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)
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")
| 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 |
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
Not(), And(), Or(), Xor(), implies(), iff() — for
mixed-integer programming with boolean variables.
perspective(f, s) for perspective functionsFiniteSet(expr, values) constraint for discrete optimizationceil_expr(), floor_expr() for DQCP problemscondition_number(), gen_lambda_max(), dist_ratio() for DQCPTo migrate code from CVXR 1.x to 1.8:
result <- solve(problem) with opt_val <- psolve(problem)result$getValue(x) with value(x)result$status with status(problem)result$getDualValue(con) with dual_value(con)axis = NA with axis = NULL (axis values 1 and 2 are unchanged)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.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.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.