R/mbo_defaults.R

Defines functions default_result_assigner default_acqoptimizer default_acqfunction default_surrogate default_rf default_gp default_loop_function

Documented in default_acqfunction default_acqoptimizer default_gp default_loop_function default_result_assigner default_rf default_surrogate

#' @title Defaults for OptimizerMbo
#' @name mbo_defaults
#'
#' @description
#' The following defaults are set for [OptimizerMbo] during optimization
#' if the respective fields are not set during initialization.
#'
#' * **Loop Function** ([default_loop_function]): The Bayesian optimization flavor.
#'   Defaults to [bayesopt_ego] for single-objective and [bayesopt_smsego] for multi-objective optimization.
#' * **Surrogate** ([default_surrogate]): The surrogate model.
#'   Uses a Gaussian process ([default_gp]) for purely numeric parameter spaces without dependencies,
#'   and a random forest ([default_rf]) otherwise.
#'   For multi-objective optimization, one surrogate learner per objective is used.
#' * **Acquisition Function** ([default_acqfunction]): The criterion used to propose future points.
#'   Defaults to [mlr_acqfunctions_cb] (confidence bound) for synchronous single-objective optimization
#'   (`lambda = 3` for numeric, `lambda = 1` for mixed spaces),
#'   [mlr_acqfunctions_smsego] for synchronous multi-objective optimization,
#'   and [mlr_acqfunctions_stochastic_cb] for asynchronous single-objective optimization.
#' * **Acquisition Function Optimizer** ([default_acqoptimizer]): The optimizer for the acquisition function.
#'   Defaults to [AcqOptimizerLocalSearch].
#' * **Result Assigner** ([default_result_assigner]): Determines how the final result is assigned.
#'   Defaults to [ResultAssignerArchive].
#'
#' @family mbo_defaults
NULL

#' @title Default Loop Function
#'
#' @description
#' Chooses a default [loop_function], i.e. the Bayesian Optimization flavor to be used for optimization.
#' For single-objective optimization, defaults to [bayesopt_ego].
#' For multi-objective optimization, defaults to [bayesopt_smsego].
#'
#' @param instance ([bbotk::OptimInstance])\cr
#'   An object that inherits from [bbotk::OptimInstance].
#' @return [loop_function]
#' @family mbo_defaults
#' @export
default_loop_function = function(instance) {
  if (inherits(instance, "OptimInstanceBatchSingleCrit")) {
    bayesopt_ego
  } else if (inherits(instance, "OptimInstanceBatchMultiCrit")) {
    bayesopt_smsego
  } else {
    stopf("There are no loop functions for %s.", class(instance)[1L])
  }
}

#' @title Default Gaussian Process
#'
#' @description
#' This is a helper function that constructs a default Gaussian Process [mlr3learners::LearnerRegrKM]
#' which is for example used in [default_surrogate()].
#'
#' Constructs a Kriging learner `"regr.km"` with kernel `"matern5_2"`.
#' If `noisy = FALSE` (default) a small nugget effect is added `nugget.stability = 10^-8`
#' to increase numerical stability to hopefully prevent crashes of `DiceKriging`.
#' If `noisy = TRUE` the nugget effect will be estimated with `nugget.estim = TRUE`.
#' If `noisy = TRUE` `jitter` is set to `TRUE` to circumvent a problem with `DiceKriging`
#' where already trained input values produce the exact trained output.
#' In general, instead of the default `"BFGS"` optimization method we use rgenoud (`"gen"`),
#' which is a hybrid algorithm,
#' to combine global search based on genetic algorithms and local search based on gradients.
#' This may improve the model fit and will less frequently produce a constant model prediction.
#'
#' @param noisy (`logical(1)`)\cr
#'   Whether the learner will be used in a noisy objective function scenario.
#'   See above.
#'
#' @return [mlr3learners::LearnerRegrKM]
#' @family mbo_defaults
#' @export
default_gp = function(noisy = FALSE) {
  assert_flag(noisy)
  require_namespaces("mlr3learners")
  require_namespaces("DiceKriging")
  require_namespaces("rgenoud")

  learner = lrn(
    "regr.km",
    predict_type = "se",
    control = list(trace = FALSE),
    optim.method = "gen",
    covtype = "matern5_2",
    scaling = FALSE
  )

  if (noisy) {
    learner$param_set$set_values(nugget.estim = TRUE, jitter = 1e-12)
  } else {
    learner$param_set$set_values(nugget.stability = 10^-8)
  }
  learner
}

#' @title Default Random Forest
#'
#' @description
#' This is a helper function that constructs a default random forest [mlr3learners::LearnerRegrRanger]
#' which is for example used in [default_surrogate()].
#'
#' @param noisy (`logical(1)`)\cr
#'   Whether the learner will be used in a noisy objective function scenario.
#'   See [default_surrogate()].
#'   Currently, the constructed random forest is identical in the noisy and deterministic case,
#'   and the argument only exists for consistency with [default_gp()].
#' @return [mlr3learners::LearnerRegrRanger]
#' @family mbo_defaults
#' @export
default_rf = function(noisy = FALSE) {
  assert_flag(noisy)
  require_namespaces("mlr3learners")
  require_namespaces("ranger")
  lrn(
    "regr.ranger",
    num.trees = 500L,
    se.method = "law_of_total_variance",
    splitrule = "variance",
    predict_type = "se",
    keep.inbag = TRUE,
    sample.fraction = 1,
    min.node.size = 3,
    min.bucket = 3,
    mtry.ratio = 5 / 6
  )
}

#' @title Default Surrogate
#'
#' @description
#' This is a helper function that constructs a default [Surrogate]
#' based on properties of the [bbotk::OptimInstance].
#'
#' For purely numeric (including integers) parameter spaces without any dependencies
#' a Gaussian Process is constricted via [default_gp()].
#' For mixed numeric-categorical parameter spaces, or spaces with conditional parameters
#' a random forest is constructed via [default_rf()].
#'
#' These default learners are encapsulated using `"evaluate"`, and a fallback learner is set,
#' in cases where the surrogate learner errors.
#' A user-supplied `learner` is used as is, without encapsulation or a fallback learner.
#' Currently, the following learner is used as a fallback:
#' `lrn("regr.ranger", num.trees = 10L, keep.inbag = TRUE, se.method = "jack")`.
#'
#' If additionally dependencies are present in the parameter space,
#' inactive conditional parameters are represented by missing `NA` values in the training design data.
#' We simply handle those with the internal `NA` handling method `na.action = "na_learn` of \CRANpkg{ranger}.
#'
#' If `n_learner` is `1`, the learner is wrapped as a [SurrogateLearner].
#' Otherwise, if `n_learner` is larger than `1`,
#' multiple deep clones of the learner are wrapped as a [SurrogateLearnerCollection].
#'
#' @param instance ([bbotk::OptimInstance])\cr
#'   An object that inherits from [bbotk::OptimInstance].
#' @param learner (`NULL` | [mlr3::Learner]).
#'   If specified, this learner will be used instead of the defaults described above.
#' @param n_learner (`NULL` | `integer(1)`).
#'  Number of learners to be considered in the construction of the [Surrogate].
#'  If not specified will be based on the number of objectives as stated by the instance.
#' @param force_random_forest (`logical(1)`).
#'  If `TRUE`, a random forest is constructed even if the parameter space is purely numeric.
#' @return [Surrogate]
#' @family mbo_defaults
#' @export
default_surrogate = function(instance, learner = NULL, n_learner = NULL, force_random_forest = FALSE) {
  assert_multi_class(instance, c("OptimInstance", "OptimInstanceBatch", "OptimInstanceAsync"))
  assert_r6(learner, "Learner", null.ok = TRUE)
  assert_int(n_learner, lower = 1L, null.ok = TRUE)
  noisy = "noisy" %in% instance$objective$properties

  if (is.null(learner)) {
    require_namespaces(c("ranger", "mlr3learners"))

    learner = if (instance$search_space$all_numeric && !instance$search_space$has_deps && !force_random_forest) {
      default_gp(noisy)
    } else {
      default_rf(noisy)
    }

    fallback = lrn("regr.ranger", num.trees = 10L, keep.inbag = TRUE, se.method = "jack", predict_type = "se")
    learner$encapsulate("evaluate", fallback)
  }

  if (is.null(n_learner)) {
    n_learner = length(instance$archive$cols_y)
  }

  if (n_learner == 1L) {
    SurrogateLearner$new(learner, output_trafo = OutputTrafoLog$new(invert_posterior = FALSE))
  } else {
    learners = replicate(n_learner, learner$clone(deep = TRUE), simplify = FALSE)
    SurrogateLearnerCollection$new(learners, output_trafo = OutputTrafoLog$new(invert_posterior = FALSE))
  }
}

#' @title Default Acquisition Function
#'
#' @description
#' Chooses a default acquisition function, i.e. the criterion used to propose future points.
#' For synchronous single-objective optimization and a purely numeric parameter space,
#' defaults to [mlr_acqfunctions_cb] with `lambda = 3`.
#' For synchronous single-objective optimization and a mixed numeric-categorical parameter space,
#' defaults to [mlr_acqfunctions_cb] with `lambda = 1`.
#' For synchronous multi-objective optimization, defaults to [mlr_acqfunctions_smsego].
#' For asynchronous single-objective optimization, defaults to [mlr_acqfunctions_stochastic_cb].
#'
#' @param instance ([bbotk::OptimInstance]).
#'   An object that inherits from [bbotk::OptimInstance].
#' @return [AcqFunction]
#' @family mbo_defaults
#' @export
default_acqfunction = function(instance) {
  assert_r6(instance, classes = "OptimInstance")
  if (inherits(instance, "OptimInstanceBatchSingleCrit") && instance$search_space$all_numeric) {
    AcqFunctionCB$new(lambda = 3)
  } else if (inherits(instance, "OptimInstanceBatchSingleCrit") && !instance$search_space$all_numeric) {
    AcqFunctionCB$new(lambda = 1)
  } else if (inherits(instance, "OptimInstanceAsyncSingleCrit")) {
    AcqFunctionStochasticCB$new()
  } else if (inherits(instance, "OptimInstanceBatchMultiCrit")) {
    AcqFunctionSmsEgo$new()
  } else {
    stopf("Currently, there is no default acquisition function for %s.", class(instance)[1L])
  }
}

#' @title Default Acquisition Function Optimizer
#'
#' @description
#' Chooses a default acquisition function optimizer.
#' Defaults to [AcqOptimizerLocalSearch] with `n_searches = 10`, `n_steps = ceiling(100 * D^2 / 300)`,
#' and `n_neighs = 30`, where `D` is the dimension of the search space.
#'
#' @param acq_function ([AcqFunction]).
#' @param instance ([bbotk::OptimInstance]).
#' @return [AcqOptimizer]
#' @family mbo_defaults
#' @export
default_acqoptimizer = function(acq_function, instance) {
  assert_r6(acq_function, classes = "AcqFunction")
  assert_instance(instance)

  dim = instance$search_space$length
  budget = 100L * dim^2
  acqo("local_search", acq_function = acq_function, n_searches = 10L, n_steps = ceiling(budget / 300L), n_neighs = 30L)
}

#' @title Default Result Assigner
#'
#' @description
#' Chooses a default result assigner.
#' Defaults to [ResultAssignerArchive].
#'
#' @param instance ([bbotk::OptimInstance])\cr
#'   An object that inherits from [bbotk::OptimInstance].
#' @return [ResultAssigner]
#' @family mbo_defaults
#' @export
default_result_assigner = function(instance) {
  assert_r6(instance, classes = "OptimInstance")
  ResultAssignerArchive$new()
}

Try the mlr3mbo package in your browser

Any scripts or data that you put into this service are public.

mlr3mbo documentation built on July 21, 2026, 5:08 p.m.