Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ Imports:
mlr3misc (>= 0.15.1),
R6
Suggests:
adagio,
emoa,
GenSA,
irace (>= 4.0.0),
knitr,
libcmaesr,
mirai,
nloptr,
processx,
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# bbotk (development version)

* BREAKING CHANGE: Replace `adagio::pureCMAES()` with `libcmaesr::cmaes()` in `OptimizerBatchCmaes`.

# bbotk 1.7.0

* BREAKING CHANGE: Replace `OptimizerBatchLocalSearch` with a faster C implementation.
Expand Down
1 change: 0 additions & 1 deletion R/OptimInstanceBatch.R
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ OptimInstanceBatch = R6Class("OptimInstanceBatch",

objective_function = function(x, inst, direction) {
xs = set_names(as.list(x), inst$search_space$ids())
inst$search_space$assert(xs)
xdt = as.data.table(xs)
res = inst$eval_batch(xdt)
y = as.numeric(res[, inst$objective$codomain$target_ids, with = FALSE])
Expand Down
131 changes: 93 additions & 38 deletions R/OptimizerBatchCmaes.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,58 @@
#' @name mlr_optimizers_cmaes
#'
#' @description
#' `OptimizerBatchCmaes` class that implements CMA-ES. Calls [adagio::pureCMAES()]
#' from package \CRANpkg{adagio}. The algorithm is typically applied to search
#' space dimensions between three and fifty. Lower search space dimensions might
#' crash.
#' `OptimizerBatchCmaes` class that implements CMA-ES.
#' Calls `cmaes()` from package \CRANpkg{libcmaesr}.
#'
#' @templateVar id cmaes
#' @template section_dictionary_optimizers
#'
#' @section Parameters:
#' \describe{
#' \item{`sigma`}{`numeric(1)`}
#' \item{`x0`}{`numeric()`\cr
#' Initial parameter values.
#' Use `start_values` parameter to create `"random"` or `"center"` initial values.}
#' \item{`start_values`}{`character(1)`\cr
#' Create `"random"` start values or based on `"center"` of search space?
#' In the latter case, it is the center of the parameters before a trafo is applied.
#' If set to `"custom"`, the start values can be passed via the `start` parameter.}
#' \item{`start`}{`numeric()`\cr
#' Custom start values. Only applicable if `start_values` parameter is set to `"custom"`.}
#' Create `"random"` start values or based on `"center"` of search space?
#' In the latter case, it is the center of the parameters before a trafo is applied.
#' Custom start values can be passed via the `x0` parameter.}
#' }
#'
#' For the meaning of the control parameters, see [adagio::pureCMAES()]. Note
#' that we have removed all control parameters which refer to the termination of
#' the algorithm and where our terminators allow to obtain the same behavior.
#' For the meaning of the control parameters, see `libcmaesr::cmaes_control()`.
#'
#' @section Internal Termination Parameters:
#' The algorithm can terminated with all [Terminator]s.
#' Additionally, the following internal termination parameters can be used:
#'
#' \describe{
#' \item{`max_fevals`}{`integer(1)`\cr
#' Maximum number of function evaluations.
#' Original default is `100`.
#' Deactivate with `NA`.
#' Overwritten with `NA`.}
#' \item{`max_iter`}{`integer(1)`\cr
#' Maximum number of iterations.
#' Deactivate with `NA`.
#' Default is `NA`.}
#' \item{`ftarget`}{`numeric(1)`\cr
#' Target function value.
#' Deactivate with `NA`.
#' Default is `NA`.}
#' \item{`f_tolerance`}{`numeric(1)`\cr
#' Function tolerance.
#' Deactivate with `NA`.
#' Default is `NA`.}
#' \item{`x_tolerance`}{`numeric(1)`\cr
#' Parameter tolerance.
#' Deactivate with `NA`.
#' Default is `NA`.}
#' }
#'
#' @template section_progress_bars
#'
#' @export
#' @examples
#' if (requireNamespace("adagio")) {
#' if (requireNamespace("libcmaesr")) {
#' search_space = domain = ps(
#' x1 = p_dbl(-10, 10),
#' x2 = p_dbl(-5, 5)
Expand All @@ -48,7 +72,7 @@
#' domain = domain,
#' codomain = codomain)
#'
#' instance = OptimInstanceBatchSingleCrit$new(
#' instance = oi(
#' objective = objective,
#' search_space = search_space,
#' terminator = trm("evals", n_evals = 10))
Expand All @@ -72,19 +96,46 @@ OptimizerBatchCmaes = R6Class("OptimizerBatchCmaes",
#' Creates a new instance of this [R6][R6::R6Class] class.
initialize = function() {
param_set = ps(
sigma = p_dbl(default = 0.5),
start_values = p_fct(default = "random", levels = c("random", "center", "custom")),
start = p_uty(default = NULL, depends = start_values == "custom")
x0 = p_uty(default = NULL),
algo = p_fct(default = "acmaes", levels = c(
"cmaes",
"ipop",
"bipop",
"acmaes",
"aipop",
"abipop",
"sepcmaes",
"sepipop",
"sepbipop",
"sepacmaes",
"sepaipop",
"sepabipop",
"vdcma",
"vdipopcma",
"vdbipopcma")),
lambda = p_int(lower = 1L, default = NA_integer_, special_vals = list(NA_integer_)),
sigma = p_dbl(default = NA_real_, special_vals = list(NA_real_)),
max_restarts = p_int(lower = 1L, special_vals = list(NA), default = NA),
tpa = p_int(default = NA_integer_, special_vals = list(NA_integer_)),
tpa_dsigma = p_dbl(default = NA_real_, special_vals = list(NA_real_)),
seed = p_int(default = NA_integer_, special_vals = list(NA_integer_)),
quiet = p_lgl(default = FALSE),
# bbotk parameters
start_values = p_fct(init = "random", levels = c("random", "center")),
# internal termination criteria
max_fevals = p_int(lower = 1L, init = NA_integer_, special_vals = list(NA_integer_)),
max_iter = p_int(lower = 1L, default = NA_integer_, special_vals = list(NA_integer_)),
ftarget = p_dbl(default = NA_real_, special_vals = list(NA_real_)),
f_tolerance = p_dbl(default = NA_real_, special_vals = list(NA_real_)),
x_tolerance = p_dbl(default = NA_real_, special_vals = list(NA_real_))
)
param_set$values$start_values = "random"
param_set$values$start = NULL

super$initialize(
id = "cmaes",
param_set = param_set,
param_classes = "ParamDbl",
properties = "single-crit",
packages = "adagio",
packages = "libcmaesr",
label = "Covariance Matrix Adaptation Evolution Strategy",
man = "bbotk::mlr_optimizers_cmaes"
)
Expand All @@ -94,29 +145,33 @@ OptimizerBatchCmaes = R6Class("OptimizerBatchCmaes",
private = list(
.optimize = function(inst) {
pv = self$param_set$values
direction = inst$objective$codomain$direction
lower = inst$search_space$lower
upper = inst$search_space$upper

if (pv$start_values == "custom") {
pv$par = pv$start
pv$start_values = NULL
pv$start = NULL
x0 = if (!is.null(pv$x0)) {
set_names(pv$x0, inst$search_space$ids())
} else {
pv$par = search_start(inst$search_space, type = pv$start_values)
pv$start_values = NULL
pv$start = NULL
search_start(inst$search_space, type = pv$start_values)
}

pv$stopeval = .Machine$integer.max # make sure pureCMAES does not stop
pv$stopfitness = -Inf

if (length(pv$par) < 2L) {
warning("CMA-ES is typically applied to search space dimensions between three and fifty. A lower search space dimension might crash.")
wrapper = function(xmat) {
xdt = set_names(as.data.table(xmat), inst$search_space$ids())
res = inst$eval_batch(xdt)
y = res[, inst$objective$codomain$target_ids, with = FALSE][[1]]
y * direction
}

invoke(adagio::pureCMAES,
fun = inst$objective_function,
lower = inst$search_space$lower,
upper = inst$search_space$upper,
.args = pv)
control = invoke(libcmaesr::cmaes_control, maximize = direction == -1L,
.args = pv[which(names(pv) %in% formalArgs(libcmaesr::cmaes_control))])

libcmaesr::cmaes(
objective = wrapper,
x0 = x0,
lower = lower,
upper = upper,
batch = TRUE,
control = control)
}
)
)
Expand Down
52 changes: 39 additions & 13 deletions man/mlr_optimizers_cmaes.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/testthat/_snaps/OptimizerBatchCmaes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
* Parameters: start_values=random
* Parameter classes: <ParamDbl>
* Properties: single-crit
* Packages: bbotk and adagio
* Packages: bbotk and libcmaesr

39 changes: 39 additions & 0 deletions tests/testthat/_snaps/OptimizerBatchLocalSearch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# OptimizerBatchLocalSearch

Code
z$optimizer
Output

-- <OptimizerBatchLocalSearch> - Local Search ----------------------------------
* Parameters: n_initial_points=3, initial_random_sample_size=100,
neighbors_per_point=10, mutation_sd=0.1
* Parameter classes: <ParamLgl>, <ParamInt>, <ParamDbl>, and <ParamFct>
* Properties: dependencies and single-crit
* Packages: bbotk

# OptimizerBatchLocalSearch mixed hierarchical search space

Code
optimizer
Output

-- <OptimizerBatchLocalSearch> - Local Search ----------------------------------
* Parameters: n_initial_points=3, initial_random_sample_size=100,
neighbors_per_point=10, mutation_sd=0.1
* Parameter classes: <ParamLgl>, <ParamInt>, <ParamDbl>, and <ParamFct>
* Properties: dependencies and single-crit
* Packages: bbotk

# OptimizerBatchLocalSearch trafo

Code
optimizer
Output

-- <OptimizerBatchLocalSearch> - Local Search ----------------------------------
* Parameters: n_initial_points=3, initial_random_sample_size=100,
neighbors_per_point=10, mutation_sd=0.1
* Parameter classes: <ParamLgl>, <ParamInt>, <ParamDbl>, and <ParamFct>
* Properties: dependencies and single-crit
* Packages: bbotk

34 changes: 3 additions & 31 deletions tests/testthat/test_OptimizerBatchCmaes.R
Original file line number Diff line number Diff line change
@@ -1,36 +1,8 @@
test_that("OptimizerBatchCmaes", {
skip_if_not_installed("adagio")

search_space = domain = ps(
x1 = p_dbl(-10, 10),
x2 = p_dbl(-5, 5)
)

codomain = ps(y = p_dbl(tags = "maximize"))

objective_function = function(xs) {
c(y = -(xs[[1]] - 2)^2 - (xs[[2]] + 3)^2 + 10)
}

objective = ObjectiveRFun$new(
fun = objective_function,
domain = domain,
codomain = codomain)

instance = OptimInstanceBatchSingleCrit$new(
objective = objective,
search_space = search_space,
terminator = trm("evals", n_evals = 10L))

z = test_optimizer(instance, "cmaes", real_evals = 10L)
skip_if_not_installed("libcmaesr")

z = test_optimizer_1d("cmaes", term_evals = 100L)
expect_class(z$optimizer, "OptimizerBatchCmaes")
expect_snapshot(z$optimizer)

expect_error(test_optimizer_2d("cmaes", term_evals = 10L), "multi-crit objectives")

instance$archive$clear()
optimizer = opt("cmaes", start_values = "custom", start = c(-9.1, 1.3))
optimizer$optimize(instance)
# start values are used for the initial mean vector so a deterministic test is not applicable
})

Loading