suppressMessages(suppressWarnings(library(CVXR)))
data(cdiac)
y <- cdiac$annual
m <- length(y)
lambda <- 0.44
beta <- Variable(m)
obj <- 0.5 * sum((y - beta)^2) + lambda * sum(pos(diff(beta)))
prob <- Problem(Minimize(obj))
soln <- psolve(prob, solver = "CLARABEL")
check_solver_status(prob)
betaHat <- value(beta)Getting Faster Results
Introduction
As was remarked in the introduction to CVXR, its chief advantage is flexibility: you can specify a problem in close to mathematical form and CVXR solves it for you, if it can. Behind the scenes, CVXR compiles the domain specific language and verifies the convexity of the problem before sending it off to solvers. If the problem violates the rules of Disciplined Convex Programming it is rejected.
Therefore, it is generally slower than tailor-made solutions to a given problem.
Recommended: Use DPP for Repeated Solves
If you need to solve the same problem structure repeatedly with different data (e.g., tuning a regularization parameter, computing bootstrap confidence intervals), the recommended approach is Disciplined Parametrized Programming (DPP). By using Parameter objects, CVXR compiles the problem once and caches the mapping from parameters to problem data. Subsequent solves skip re-compilation and can be significantly faster.
See the DPP tutorial for a detailed example showing roughly a 2x speed-up on a regularized least-squares problem.
Advanced: Directly Calling the Solver
If DPP is not sufficient for your performance needs, you can bypass the DSL entirely and call the solver directly. This avoids all overhead from CVXR’s compilation and DCP verification on each solve, but requires working with low-level solver data structures.
CVXR provides a three-step public API for this, mirroring the CVXPY interface:
problem_data()— compile the problem into solver-ready datasolve_via_data()— call the solver on the compiled dataproblem_unpack_results()— invert the compilation chain and populate variable values
We recommend checking DCP-compliance at least once (via is_dcp(prob)) before bypassing verification. Not verifying DCP-compliance may result in garbage!
An Example
To understand the speed issues, let us consider the global warming data from the Carbon Dioxide Information Analysis Center (CDIAC) again. The data points are the annual temperature anomalies relative to the 1961–1990 mean. We will fit the nearly-isotonic approximation
This can be solved as follows.
This is the recommended way to solve a problem. After solving, you can inspect timing via solver_stats():
stats <- solver_stats(prob)
statsSolverStats(solver=CLARABEL, time=0.0011, iters=10)
The SolverStats object reports the solver’s own wall-clock time, setup time, and iteration count.
However, suppose we wished to construct bootstrap confidence intervals for the estimate using 100 resamples. It is clear that this computation time can quickly become limiting.
Below, we show how one can get at the problem data and directly call a solver to get faster results.
Profile the Code
Profiling a single fit to the model is useful to figure out where most of the time is spent.
library(profvis)
y <- cdiac$annual
profvis({
beta <- Variable(m)
obj <- Minimize(0.5 * sum((y - beta)^2) + lambda * sum(pos(diff(beta))))
prob <- Problem(obj)
soln <- psolve(prob, solver = "CLARABEL")
check_solver_status(prob)
betaHat <- value(beta)
})It is especially instructive to click on the data tab and open up the tree for psolve to see the sequence of calls and cumulative time used.
Directly Calling the Solver
We are mathematically certain that the above is convex and so we can avoid the repeated DCP checks. The three steps are:
Step 1. Extract the compiled problem data using problem_data(). This returns the low-level solver data, the compilation chain, and inverse data needed to reconstruct the solution.
prob_data <- problem_data(prob, solver = "CLARABEL")Step 2. Call the solver directly via solve_via_data() on the compilation chain. This bypasses DCP verification entirely.
solver_output <- solve_via_data(prob_data$chain, prob_data$data,
warm_start = FALSE, verbose = FALSE,
solver_opts = list())Step 3. Unpack the solver results back into the problem’s variables using problem_unpack_results(). This reverses the compilation chain and populates value(beta) etc.
problem_unpack_results(prob, solver_output,
prob_data$chain, prob_data$inverse_data)Profile the Direct Call
We can profile this direct call now.
profvis({
beta <- Variable(m)
obj <- Minimize(0.5 * sum((y - beta)^2) + lambda * sum(pos(diff(beta))))
prob <- Problem(obj)
prob_data <- problem_data(prob, solver = "CLARABEL")
solver_output <- solve_via_data(prob_data$chain, prob_data$data,
warm_start = FALSE, verbose = FALSE,
solver_opts = list())
problem_unpack_results(prob, solver_output,
prob_data$chain, prob_data$inverse_data)
})Same Answer?
Of course, we should also verify that the results obtained in both cases are the same.
identical(betaHat, value(beta))[1] TRUE
Session Info
R version 4.5.2 (2025-10-31)
Platform: aarch64-apple-darwin20
Running under: macOS Tahoe 26.3
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/Los_Angeles
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] profvis_0.4.0 CVXR_1.8.1
loaded via a namespace (and not attached):
[1] slam_0.1-55 cli_3.6.5 knitr_1.51 ECOSolveR_0.6.1
[5] rlang_1.1.7 xfun_0.56 clarabel_0.11.2 gurobi_13.0-1
[9] otel_0.2.0 Rglpk_0.6-5.1 highs_1.12.0-3 cccp_0.3-3
[13] scs_3.2.7 S7_0.2.1 jsonlite_2.0.0 Rcplex_0.3-8
[17] backports_1.5.0 Rmosek_11.1.1 rprojroot_2.1.1 htmltools_0.5.9
[21] gmp_0.7-5.1 piqp_0.6.2 rmarkdown_2.30 grid_4.5.2
[25] evaluate_1.0.5 fastmap_1.2.0 yaml_2.3.12 compiler_4.5.2
[29] codetools_0.2-20 htmlwidgets_1.6.4 Rcpp_1.1.1 here_1.0.2
[33] osqp_1.0.0 lattice_0.22-9 digest_0.6.39 checkmate_2.3.4
[37] Matrix_1.7-4 tools_4.5.2