File: examples.Rmd

package info (click to toggle)
r-cran-ecosolver 0.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 10,196 kB
  • sloc: ansic: 11,160; makefile: 108; python: 26; sh: 23
file content (156 lines) | stat: -rw-r--r-- 3,616 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
---
title: "ECOSolveR Examples"
date: '`r Sys.Date()`'
output:
  html_document:
  fig_caption: yes
  theme: cerulean
  toc: yes
  toc_depth: 2
vignette: >
  %\VignetteIndexEntry{ECOSolveR Examples}
  %\VignetteEngine{knitr::rmarkdown}
  \usepackage[utf8]{inputenc}
---

```{r echo=F}
### get knitr just the way we like it

knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  error = FALSE,
  tidy = FALSE,
  cache = FALSE
)

```
## $L_1$ minimization (Linear Programming)

We solve the following problem that arises for example in sparse
signal reconstruction problems such as compressed sensing:
$$
\mbox{minimize } ||x||_1  \mbox{   ($L_1$)   }\\
\mbox{subject to } Ax = b
$$

with $x\in R^n$, $A \in R^{m \times n}$ and $m\leq n.$ Reformulate the
problem expressing the $L_1$ norm of $x$ as follows
$$
x \leq u\\
-x \leq u\\
$$

where $u\in R^n$ and we minimize the sum of $u$. The reformulated
problem using the stacked variables

$$
z = \begin{pmatrix}x\\u\end{pmatrix}
$$

is now
$$
\mbox{minimize } c^{\top}z\\
\mbox{subject to } \tilde{A}x = b  \mbox{   (LP)  }\\
     Gx \leq h
$$
where the inequality is with respective to the positive orthant.

Here is the R code that generates a random instance of this problem
and solves it.

```{r}
library(ECOSolveR)
library(Matrix)
set.seed(182391)
n <- 1000L
m <- 10L
density <- 0.01
c <- c(rep(0.0, n), rep(1.0, n))
```

First, a function to generate random sparse matrices with normal
entries.

```{r}
sprandn <- function(nrow, ncol, density) {
    items <- ceiling(nrow * ncol * density)
    matrix(c(rnorm(items),
             rep(0, nrow * ncol - items)),
           nrow = nrow)
}
```

```{r}
A <- sprandn(m, n, density)
Atilde <- Matrix(cbind(A, matrix(rep(0.0, m * n), nrow = m)), sparse = TRUE)
b <- rnorm(m)
I <- diag(n)
G <- rbind(cbind(I, -I),
           cbind(-I, -I))
G <- as(G, "dgCMatrix")
h <- rep(0.0, 2L * n)
dims <- list(l = 2L * n, q = NULL, e = 0L)
```

Note how ECOS expects sparse matrices, not ordinary matrices.

```{r}
## Solve the problem
z <- ECOS_csolve(c = c, G = G, h = h, dims = dims, A = Atilde, b = b)
```

We check that the solution was found.

```{r}
names(z)
z$infostring
```

Extract the solution.

```{r}
x <- z$x[1:n]
u <- z$x[(n+1):(2*n)]
nnzx = sum(abs(x) > 1e-8)
sprintf("x reconstructed with %d non-zero entries", nnzx / length(x) * 100)
```

## Parametric Optimization with the Update-Solve Loop

When solving a sequence of related problems that share the same
sparsity structure but differ in numerical data, the multi-step
lifecycle API avoids repeating the expensive symbolic analysis and
KKT ordering phase.  The pattern is:

1. `ECOS_setup()` — one-time setup (symbolic analysis)
2. Loop: `ECOS_update()` + `ECOS_solve()` — cheap re-solves
3. `ECOS_cleanup()` — free workspace

Here we vary the right-hand side $h$ of the LP from the previous
example, tracing how the optimal value changes as we scale $h$.

```{r}
## Set up workspace once
ws <- ECOS_setup(c = c, G = G, h = h, dims = dims, A = Atilde, b = b)

## Sweep a parameter: scale h from 0 to 1
alphas <- seq(0, 1, length.out = 11)
pcosts <- numeric(length(alphas))

for (i in seq_along(alphas)) {
    ECOS_update(ws, h = h + alphas[i])
    res <- ECOS_solve(ws)
    pcosts[i] <- res$summary[["pcost"]]
}

ECOS_cleanup(ws)

## Show results
data.frame(alpha = alphas, pcost = round(pcosts, 4))
```

For comparison, the same sweep using `ECOS_csolve()` in a loop
would call `ECOS_setup` and `ECOS_cleanup` at every iteration.
The lifecycle API is particularly beneficial when the problem
dimensions are large and setup dominates solve time.