CVXR is an R package that provides an object-oriented
modeling language for convex optimization, similar to CVXPY for Python. It allows you to
formulate and solve convex optimization problems in a natural
mathematical syntax.
Consider a simple linear regression problem where we want to estimate parameters using a least squares criterion.
We generate synthetic data where we know the true model:
\[ Y = X\beta + \epsilon \]
where \(Y\) is a \(100 \times 1\) vector, \(X\) is a \(100 \times 10\) matrix, \(\beta = (-4, -3, \ldots, 5)^\top\) is a \(10 \times 1\) vector, and \(\epsilon \sim N(0, 1)\).
set.seed(123)
n <- 100
p <- 10
beta <- -4:5
X <- matrix(rnorm(n * p), nrow = n)
Y <- X %*% beta + rnorm(n)Using base R, we can estimate \(\beta\) via lm:
The same problem can be expressed as:
\[ \underset{\beta}{\text{minimize}} \quad \|Y - X\beta\|_2^2 \]
In CVXR, this translates directly:
library(CVXR)
#>
#> Attaching package: 'CVXR'
#> The following objects are masked from 'package:stats':
#>
#> convolve, power, sd, var
#> The following objects are masked from 'package:base':
#>
#> diag, norm, outer
betaHat <- Variable(p)
objective <- Minimize(sum((Y - X %*% betaHat)^2))
problem <- Problem(objective)
result <- psolve(problem) ## use default solverThe optimal value and estimated coefficients:
cat("Optimal value:", result, "\n")
#> Optimal value: 97.84759
cbind(CVXR = round(value(betaHat), 3),
lm = round(coef(ls.model), 3))
#> lm
#> X1 -3.920 -3.920
#> X2 -3.012 -3.012
#> X3 -2.125 -2.125
#> X4 -0.867 -0.867
#> X5 0.091 0.091
#> X6 0.949 0.949
#> X7 2.076 2.076
#> X8 3.127 3.127
#> X9 3.961 3.961
#> X10 5.135 5.135The real power of CVXR is the ability to add constraints easily.
Suppose we know the \(\beta\)s should be nonnegative:
Now suppose \(\beta_2 + \beta_3 \le 0\) and all other \(\beta\)s are nonnegative:
A <- matrix(c(0, 1, 1, rep(0, 7)), nrow = 1)
B <- diag(c(1, 0, 0, rep(1, 7)))
constraint1 <- A %*% betaHat <= 0
constraint2 <- B %*% betaHat >= 0
problem <- Problem(objective, constraints = list(constraint1, constraint2))
result <- psolve(problem, solver = "CLARABEL", verbose = TRUE) ## verbose = TRUE for details
#> ────────────────────────────────── CVXR v1.9.1 ─────────────────────────────────
#> ℹ Problem: 1 variable, 2 constraints (QP)
#> ℹ Compilation: "CLARABEL" via CVXR::Dcp2Cone -> CVXR::CvxAttr2Constr -> CVXR::ConeMatrixStuffing -> CVXR::Clarabel_Solver
#> ℹ Compile time: 0.037s
#> ─────────────────────────────── Numerical solver ───────────────────────────────
#> ──────────────────────────────────── Summary ───────────────────────────────────
#> ✔ Status: optimal
#> ✔ Optimal value: 1287.63
#> ℹ Compile time: 0.037s
#> ℹ Solver time: 0.029s
round(value(betaHat), 3)
#> [,1]
#> [1,] 0.000
#> [2,] -2.845
#> [3,] -1.711
#> [4,] 0.000
#> [5,] 0.664
#> [6,] 1.178
#> [7,] 2.329
#> [8,] 2.414
#> [9,] 4.212
#> [10,] 4.948This demonstrates the chief advantage of CVXR: flexibility. Users can quickly modify and re-solve a problem, making the package ideal for prototyping new statistical methods. Its syntax is simple and mathematically intuitive.
CVXR supports 15 solvers, both open source and commercial: Clarabel, SCS, OSQP, HiGHS, MOSEK, Gurobi, GLPK, GLPK_MI, ECOS, ECOS_BB, CPLEX, CVXOPT, PIQP, SCIP, and XPRESS. Smooth nonlinear programs additionally use the IPOPT and UNO solvers.
You can specify a solver explicitly:
Recent releases add disciplined nonlinear programming
(psolve(prob, nlp = TRUE)), bounds propagation through
expressions (get_bounds()), a derivative /
sensitivity-analysis API (requires_grad = TRUE), and new
atoms such as convolve(). CVXR also supports element-wise
matrix indexing using R’s native idioms:
ind <- which(!is.na(Rmiss), arr.ind = TRUE)
prob <- Problem(Minimize(obj), list(X[ind] == Rmiss[ind]))For a release-by-release summary see
vignette("whats_new"), and
news(package = "CVXR") for the full details.
sessionInfo()
#> R version 4.6.0 (2026-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] CVXR_1.9.1 rmarkdown_2.31
#>
#> loaded via a namespace (and not attached):
#> [1] cli_3.6.6 knitr_1.51 rlang_1.2.0 xfun_0.58
#> [5] clarabel_0.11.2 otel_0.2.0 highs_1.14.0-2 scs_3.2.7
#> [9] S7_0.2.2 jsonlite_2.0.0 backports_1.5.1 buildtools_1.0.0
#> [13] htmltools_0.5.9 maketools_1.3.2 sys_3.4.3 gmp_0.7-5.1
#> [17] sass_0.4.10 grid_4.6.0 evaluate_1.0.5 jquerylib_0.1.4
#> [21] fastmap_1.2.0 yaml_2.3.12 lifecycle_1.0.5 compiler_4.6.0
#> [25] Rcpp_1.1.1-1.1 osqp_1.0.0 lattice_0.22-9 digest_0.6.39
#> [29] R6_2.6.1 checkmate_2.3.4 bslib_0.11.0 Matrix_1.7-5
#> [33] tools_4.6.0 cachem_1.1.0