-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathREADME.Rmd
More file actions
217 lines (162 loc) · 7.37 KB
/
Copy pathREADME.Rmd
File metadata and controls
217 lines (162 loc) · 7.37 KB
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
fig.align = "center",
warning = FALSE
)
```
# optimizeR <img src="man/figures/logo.png" align="right" height="139" />
<!-- badges: start -->
[](https://CRAN.R-project.org/package=optimizeR)
[](https://cran.r-project.org/package=optimizeR)
[](https://github.com/loelschlaeger/optimizeR/actions)
[](https://app.codecov.io/gh/loelschlaeger/optimizeR)
<!-- badges: end -->
The `{optimizeR}` package provides an object-oriented framework for optimizer
functions in R and adds convenience features for minimization and maximization.
**You probably do not need this package if you...**
- already know which optimizer you want to use and are comfortable with its
constraints, such as minimization only over the first function argument;
- want to compare optimizers that are already covered by
[`{optimx}`][optimx], a framework for comparing about 30 optimizers;
- are searching for new optimization algorithms, because this package does not
implement optimizer functions itself.
**You might find the package useful if you want to...**
- compare optimizer functions not covered by `{optimx}` or other frameworks
(see the [CRAN Task View: Optimization and Mathematical
Programming][ctv-optimization] for an overview);
- have consistently named inputs and outputs across different optimizers;
- view optimizers as objects, which can help when implementing packages that
depend on optimization;
- use optimizers for both minimization and maximization,
- optimize over more than one function argument,
- measure computation time or set a time limit for long optimization tasks.
## How to use the package?
The following demo is a bit artificial, but it showcases the package purpose.
Assume we want to
- maximize a function over two of its arguments,
- interrupt optimization if it exceeds 10 seconds,
- compare the performance of `stats::nlm` and `pracma::nelder_mead`.
We can do this task with `{optimizeR}`. You can install the released package
version from [CRAN][cran] with:
```{r, install released version, eval = FALSE}
install.packages("optimizeR")
```
Then load the package via `library("optimizeR")`.
```{r, load optimizeR, include = FALSE}
library("optimizeR")
```
**1. Define the objective function**
Let $f:\mathbb{R}^4\to\mathbb{R}$ with
```{r, ackley}
f <- function(a, b, x, y) {
a * exp(-0.2 * sqrt(0.5 * (x^2 + y^2))) + exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) - exp(1) - b
}
```
For `a = b = 20`, this is the negative [Ackley function][ackley] with a
global maximum in `x = y = 0`:
```{r, plot-ackley, echo = FALSE, out.width = "50%"}
x <- y <- seq(-3, 3, length.out = 100)
grid <- expand.grid(x, y)
z <- mapply(f, x = grid[, 1], y = grid[, 2], a = 20, b = 20)
data <- data.frame(x = grid[, 1], y = grid[, 2], z = z)
library("ggplot2")
ggplot(data = data, aes(x, y, z = z)) +
geom_contour_filled() +
theme_minimal()
```
We want to keep `a` and `b` fixed here and optimize over `x` and `y`, which
are both single numeric values.
Two problems would occur if we optimized `f` directly with, say, `stats::nlm`:
1. There are two target arguments (`x` and `y`).
2. The target arguments are not the first arguments of `f`.
Both features are unsupported by `stats::nlm` and most other optimizers, but
they are supported by `{optimizeR}`. We just have to define an objective object
that we can later pass to the optimizers:
```{r, define objective}
objective <- Objective$new(
f = f, # f is our objective function
target = c("x", "y"), # x and y are the target arguments
npar = c(1, 1), # the target arguments have both a length of 1
"a" = 20,
"b" = 20 # a and b have fixed values
)
```
**2. Create the optimizer objects**
Now that we have defined the objective function, let's define the optimizer
objects. For `stats::nlm`, this is a one-liner:
```{r, define nlm}
nlm <- Optimizer$new(which = "stats::nlm")
```
The `{optimizeR}` package provides a dictionary of optimizers that can be
selected directly via the `which` argument. For an overview of available
optimizers, see:
```{r, optimizer dictionary}
optimizer_dictionary
```
Any optimizer that is not contained in the dictionary can still be put into the
`{optimizeR}` framework by setting `which = "custom"` first:
```{r, define nelder mead 1}
nelder_mead <- Optimizer$new(which = "custom")
```
Then use the `$definition()` method:
```{r, define nelder mead 2}
nelder_mead$definition(
algorithm = pracma::nelder_mead, # the optimization function
arg_objective = "fn", # the objective function argument name
arg_initial = "x0", # the argument name for the initial values
out_value = "fmin", # the optimal function value element
out_parameter = "xmin", # the optimal parameter element
direction = "min" # the optimizer minimizes
)
```
**3. Set a time limit**
Each optimizer object has a field called `$seconds`, which equals `Inf` by
default. You can set a different single numeric value to define a time limit in
seconds for the optimization:
```{r, set time limit}
nlm$seconds <- 10
nelder_mead$seconds <- 10
```
Note that not everything, especially compiled C code, can technically be timed
out. See the help page `help("withTimeout", package = "R.utils")` for more
details.
**4. Maximize the objective function**
Each optimizer object has the methods `$maximize()` and `$minimize()` for
function maximization and minimization, respectively. Both methods require
values for two arguments:
1. `objective` (either an objective object as defined above or just a
function) and
2. `initial` (an initial parameter vector where the optimizer should start)
They optionally accept additional arguments for the objective function.
Optimizer-specific arguments should be set when creating the optimizer object
or via `$set_arguments()`.
```{r, maximize with nlm}
nlm$maximize(objective = objective, initial = c(3, 3))
```
```{r, maximize with nelder mead}
nelder_mead$maximize(objective = objective, initial = c(3, 3))
```
Note that
- the inputs for the objective function and initial parameter values are named
consistently across optimizers,
- the output values for the optimal parameter vector and maximum function
value are also named consistently across optimizers,
- the output contains the initial parameter values, the optimization time in
seconds, and other optimizer-specific elements,
- `pracma::nelder_mead` outperforms `stats::nlm` here both in optimization time
and in convergence to the global maximum.
## Getting in touch
If you have any questions, found a bug, or need a feature, please
[file an issue on GitHub][github-issues].
[ackley]: https://en.wikipedia.org/wiki/Ackley_function
[cran]: https://CRAN.R-project.org
[ctv-optimization]: https://CRAN.R-project.org/view=Optimization
[github-issues]: https://github.com/loelschlaeger/optimizeR/issues/new/choose
[optimx]: https://CRAN.R-project.org/package=optimx