diff --git a/DESCRIPTION b/DESCRIPTION index d5be268ea..9874c21d9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -34,11 +34,11 @@ Imports: mlr3misc (>= 0.15.1), R6 Suggests: - adagio, emoa, GenSA, irace (>= 4.0.0), knitr, + libcmaesr, mirai, nloptr, processx, diff --git a/NEWS.md b/NEWS.md index a05a82379..ec51dc731 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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. diff --git a/R/OptimInstanceBatch.R b/R/OptimInstanceBatch.R index 603f0213d..d0321f4e3 100644 --- a/R/OptimInstanceBatch.R +++ b/R/OptimInstanceBatch.R @@ -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]) diff --git a/R/OptimizerBatchCmaes.R b/R/OptimizerBatchCmaes.R index 7a83129e6..2b633d992 100644 --- a/R/OptimizerBatchCmaes.R +++ b/R/OptimizerBatchCmaes.R @@ -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) @@ -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)) @@ -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" ) @@ -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) } ) ) diff --git a/man/mlr_optimizers_cmaes.Rd b/man/mlr_optimizers_cmaes.Rd index b78f599d3..30c01c67b 100644 --- a/man/mlr_optimizers_cmaes.Rd +++ b/man/mlr_optimizers_cmaes.Rd @@ -5,10 +5,8 @@ \alias{OptimizerBatchCmaes} \title{Optimization via Covariance Matrix Adaptation Evolution Strategy} \description{ -\code{OptimizerBatchCmaes} class that implements CMA-ES. Calls \code{\link[adagio:cmaes]{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. +\code{OptimizerBatchCmaes} class that implements CMA-ES. +Calls \code{cmaes()} from package \CRANpkg{libcmaesr}. } \section{Dictionary}{ @@ -23,18 +21,46 @@ opt("cmaes") \section{Parameters}{ \describe{ -\item{\code{sigma}}{\code{numeric(1)}} +\item{\code{x0}}{\code{numeric()}\cr +Initial parameter values. +Use \code{start_values} parameter to create \code{"random"} or \code{"center"} initial values.} \item{\code{start_values}}{\code{character(1)}\cr Create \code{"random"} start values or based on \code{"center"} of search space? In the latter case, it is the center of the parameters before a trafo is applied. -If set to \code{"custom"}, the start values can be passed via the \code{start} parameter.} -\item{\code{start}}{\code{numeric()}\cr -Custom start values. Only applicable if \code{start_values} parameter is set to \code{"custom"}.} +Custom start values can be passed via the \code{x0} parameter.} } -For the meaning of the control parameters, see \code{\link[adagio:cmaes]{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 \code{libcmaesr::cmaes_control()}. +} + +\section{Internal Termination Parameters}{ + +The algorithm can terminated with all \link{Terminator}s. +Additionally, the following internal termination parameters can be used: + +\describe{ +\item{\code{max_fevals}}{\code{integer(1)}\cr +Maximum number of function evaluations. +Original default is \code{100}. +Deactivate with \code{NA}. +Overwritten with \code{NA}.} +\item{\code{max_iter}}{\code{integer(1)}\cr +Maximum number of iterations. +Deactivate with \code{NA}. +Default is \code{NA}.} +\item{\code{ftarget}}{\code{numeric(1)}\cr +Target function value. +Deactivate with \code{NA}. +Default is \code{NA}.} +\item{\code{f_tolerance}}{\code{numeric(1)}\cr +Function tolerance. +Deactivate with \code{NA}. +Default is \code{NA}.} +\item{\code{x_tolerance}}{\code{numeric(1)}\cr +Parameter tolerance. +Deactivate with \code{NA}. +Default is \code{NA}.} +} } \section{Progress Bars}{ @@ -46,7 +72,7 @@ combined with a \link{Terminator}. Simply wrap the function in } \examples{ -if (requireNamespace("adagio")) { +if (requireNamespace("libcmaesr")) { search_space = domain = ps( x1 = p_dbl(-10, 10), x2 = p_dbl(-5, 5) @@ -63,7 +89,7 @@ if (requireNamespace("adagio")) { domain = domain, codomain = codomain) - instance = OptimInstanceBatchSingleCrit$new( + instance = oi( objective = objective, search_space = search_space, terminator = trm("evals", n_evals = 10)) diff --git a/tests/testthat/_snaps/OptimizerBatchCmaes.md b/tests/testthat/_snaps/OptimizerBatchCmaes.md index 2102466c6..ba59f4f55 100644 --- a/tests/testthat/_snaps/OptimizerBatchCmaes.md +++ b/tests/testthat/_snaps/OptimizerBatchCmaes.md @@ -8,5 +8,5 @@ * Parameters: start_values=random * Parameter classes: * Properties: single-crit - * Packages: bbotk and adagio + * Packages: bbotk and libcmaesr diff --git a/tests/testthat/_snaps/OptimizerBatchLocalSearch.md b/tests/testthat/_snaps/OptimizerBatchLocalSearch.md new file mode 100644 index 000000000..c9bc271ea --- /dev/null +++ b/tests/testthat/_snaps/OptimizerBatchLocalSearch.md @@ -0,0 +1,39 @@ +# OptimizerBatchLocalSearch + + Code + z$optimizer + Output + + -- - Local Search ---------------------------------- + * Parameters: n_initial_points=3, initial_random_sample_size=100, + neighbors_per_point=10, mutation_sd=0.1 + * Parameter classes: , , , and + * Properties: dependencies and single-crit + * Packages: bbotk + +# OptimizerBatchLocalSearch mixed hierarchical search space + + Code + optimizer + Output + + -- - Local Search ---------------------------------- + * Parameters: n_initial_points=3, initial_random_sample_size=100, + neighbors_per_point=10, mutation_sd=0.1 + * Parameter classes: , , , and + * Properties: dependencies and single-crit + * Packages: bbotk + +# OptimizerBatchLocalSearch trafo + + Code + optimizer + Output + + -- - Local Search ---------------------------------- + * Parameters: n_initial_points=3, initial_random_sample_size=100, + neighbors_per_point=10, mutation_sd=0.1 + * Parameter classes: , , , and + * Properties: dependencies and single-crit + * Packages: bbotk + diff --git a/tests/testthat/test_OptimizerBatchCmaes.R b/tests/testthat/test_OptimizerBatchCmaes.R index 5bb912616..583f7455f 100644 --- a/tests/testthat/test_OptimizerBatchCmaes.R +++ b/tests/testthat/test_OptimizerBatchCmaes.R @@ -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 }) +