# Getting Faster Results

## Warning

The solution described below is useful when you mathematically know a problem is DCP-compliant and none of your data inputs will change the nature of the problem. We recommend that users check the DCP-compliance of a problem (via a call to `is_dcp(prob)`

for example) at least once to ensure this is the case. *Not verifying DCP-compliance may result in garbage!*

## 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.

## 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 \(\beta \in {\mathbf R}^m\) by solving

\[ \begin{array}{ll} \underset{\beta}{\mbox{Minimize}} & \frac{1}{2}\sum_{i=1}^m (y_i - \beta_i)^2 + \lambda \sum_{i=1}^{m-1}(\beta_i - \beta_{i+1})_+, \end{array} \] where \(\lambda \geq 0\) is a penalty parameter and \(x_+ =\max(x,0)\).

This can be solved as follows.

```
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 <- solve(prob)
betaHat <- soln$getValue(beta)
```

This is the *recommended way* to solve a problem.

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)
suppressMessages(suppressWarnings(library(CVXR)))
data(cdiac)
y <- cdiac$annual
profvis({
beta <- Variable(m)
obj <- Minimize(0.5 * sum((y - beta)^2) + lambda * sum(pos(diff(beta))))
prob <- Problem(obj)
soln <- solve(prob)
betaHat <- soln$getValue(beta)
})
```

It is especially instructive to click on the *data* tab and open up the tree for `solve`

to see the sequence of calls and cumulative time used.

The profile shows that most of the total time (2400ms for one of our runs) time is spent in the call to `is_dcp`

generic (about 2000ms). This generic is responsible to ensuring that all the problem is DCP-compliant by checking the nature of each of the components that make up the problem. The actual solving took a *much smaller* fraction of the time.

## Directly Calling the Solver

We are mathematically certain that the above is convex and so we can avoid the `is_dcp`

hit. We can obtain the the problem data for a particular solver (like ECOS or SCS) using the function `get_problem_data`

and directly hand that data to the solver to get the solution.

`prob_data <- get_problem_data(prob, solver = "ECOS")`

*ASIDE*: How did we know ECOS was the solver to use? Future versions will provode a function to match a solver to a problem. (Actually, it is available already, but not exported yet!). For now, a single call to `solve`

with the verbose option set to `TRUE`

can provide that information.

`soln <- solve(prob, verbose = TRUE)`

Now that we have the problem data and know which solver to use, we can call the ECOS solver with the right arguments. (The ECOS solver is provided by the package `ECOSolveR`

which `CVXR`

imports.)

```
solver_output <- ECOSolveR::ECOS_csolve(c = prob_data[["c"]],
G = prob_data[["G"]],
h = prob_data[["h"]],
dims = prob_data[["dims"]],
A = prob_data[["A"]],
b = prob_data[["b"]])
```

Finally, we can obtain the results by asking `CVXR`

to *unpack* the solver results for us. (See `?unpack_results`

for further examples.)

`direct_soln <- unpack_results(prob, "ECOS", solver_output)`

## 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 <- get_problem_data(prob, solver = "ECOS")
solver_output <- ECOSolveR::ECOS_csolve(c = prob_data[["c"]],
G = prob_data[["G"]],
h = prob_data[["h"]],
dims = prob_data[["dims"]],
A = prob_data[["A"]],
b = prob_data[["b"]])
direct_soln <- unpack_results(prob, "ECOS", solver_output)
})
```

For one of our runs, the total time went down from \(2400ms\) to \(690ms\), more than a 3-fold speedup! In cases where the objective function and constraints are more complex, the speedup can be more than 10-fold.

## Same Answer?

Of course, we should also verify that the results obtained in both cases are same.

`identical(betaHat, direct_soln$getValue(beta))`

`## [1] TRUE`

## Session Info

`sessionInfo()`

```
## R version 3.5.0 (2018-04-23)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS High Sierra 10.13.4
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib
##
## locale:
## [1] C
##
## attached base packages:
## [1] stats graphics grDevices datasets utils methods base
##
## other attached packages:
## [1] profvis_0.3.5 CVXR_0.99
##
## loaded via a namespace (and not attached):
## [1] Rcpp_0.12.17 knitr_1.20 magrittr_1.5
## [4] bit_1.1-13 lattice_0.20-35 R6_2.2.2
## [7] stringr_1.3.1 tools_3.5.0 grid_3.5.0
## [10] xfun_0.1 R.oo_1.22.0 scs_1.1-1
## [13] htmltools_0.3.6 bit64_0.9-7 yaml_2.1.19
## [16] rprojroot_1.3-2 digest_0.6.15 bookdown_0.7
## [19] Matrix_1.2-14 gmp_0.5-13.1 ECOSolveR_0.4
## [22] htmlwidgets_1.2 R.utils_2.6.0 evaluate_0.10.1
## [25] rmarkdown_1.9.14 blogdown_0.6.3 stringi_1.2.2
## [28] compiler_3.5.0 Rmpfr_0.7-0 backports_1.1.2
## [31] R.methodsS3_1.7.1 jsonlite_1.5
```