#' @title Create optimization path.
#'
#' @description Optimizers can iteratively log their evaluated points into this
#' object. Can be converted into a data.frame with `as.data.frame(x,
#' discretes.as.factor = TRUE / FALSE)`.
#'
#' A optimization path has a number of path elements, where each element
#' consists of: the value of the decision variables at this point, the values of
#' the performance measures at this point, the date-of-birth (dob) of this
#' point, the end-of-life (eol) of this point and possibly an error message. See
#' also [addOptPathEl()].
#'
#' For discrete parameters always the name of the value is stored as a
#' character. When you retrieve an element with [getOptPathEl()], this name is
#' converted to the actual discrete value.
#'
#' If parameters have associated transformation you are free to decide whether
#' you want to add x values before or after transformation, see argument
#' `add.transformed.x` and [trafoOptPath()].
#'
#' The S3 class is a list which stores at least these elements:
#' \describe{
#' \item{par.set [ParamSet()]}{See argument of same name.}
#' \item{y.names [`character`]}{See argument of same name.}
#' \item{minimize [`logical`]}{See argument of same name.}
#' \item{add.transformed.x `logical(1)`}{See argument of same name.}
#' \item{env [`environment`]}{Environment which stores the optimization path.
#' Contents depend on implementation.}
#' }
#'
#' @template arg_parset
#' @param y.names (`character`)\cr
#' Names of performance measures that are optimized or logged.
#' @param minimize (`logical`)\cr
#' Which of the performance measures in y.names should be minimized?
#' Vector of booleans in the same order as `y.names`.
#' @param add.transformed.x (`logical(1)`)\cr
#' If some parameters have associated transformations, are you going to add x
#' values after they have been transformed? Default is `FALSE`.
#' @param include.error.message (`logical(1)`)\cr
#' Should it be possible to include an error message string (or NA if no error
#' occurred) into the path for each evaluation? This is useful if you have
#' complex, long running objective evaluations that might fail. Default is
#' `FALSE`.
#' @param include.exec.time (`logical(1)`)\cr
#' Should it be possible to include execution time of evaluations into the
#' path for each evaluation? Note that execution time could also be entered in
#' `y.names` as a direct performance measure. If you use this option here,
#' time is regarded as an extra measurement you might be curious about.
#' Default is `FALSE`.
#' @param include.extra (`logical(1)`)\cr
#' Should it be possible to include extra info
#' into the path for each evaluation?
#' Default is `FALSE`.
#' @name OptPath
#' @rdname OptPath
#' @family optpath
NULL
makeOptPath = function(par.set, y.names, minimize, add.transformed.x = FALSE,
include.error.message = FALSE, include.exec.time = FALSE, include.extra = FALSE) {
n.y = length(y.names)
ok = c("numeric", "integer", "numericvector", "integervector", "logical",
"logicalvector", "discrete", "discretevector", "character", "charactervector")
if (length(par.set$pars) > length(filterParams(par.set, type = ok)$pars)) {
stop("OptPath can currently only be used for: ", paste(ok, collapse = ","))
}
x.names = getParamIds(par.set)
# be really sure that x and y columns are uniquely named
x.names2 = c(getParamIds(par.set, with.nr = TRUE), getParamIds(par.set, with.nr = FALSE))
if (length(intersect(x.names2, y.names)) > 0) {
stop("'x.names' and 'y.names' must not contain common elements!")
}
if (length(minimize) != n.y) {
stop("'y.names' and 'minimize' must be of the same length!")
}
if (is.character(names(minimize)) && !setequal(names(minimize), y.names)) {
stop("Given names for 'minimize' must be the same as 'y.names'!")
}
if (is.null(names(minimize))) {
names(minimize) = y.names
}
if (any(c("dob", "eol", "error.message") %in% (union(x.names, y.names)))) {
stop("'dob', 'eol' and 'error.message' are not allowed in parameter names or 'y.names'!")
}
ee = new.env(parent = emptyenv())
ee$dob = ee$eol = integer(0)
# potentially init error.message and exec.time in env
ee$error.message = if (include.error.message) character(0L) else NULL
ee$exec.time = if (include.exec.time) numeric(0L) else NULL
ee$extra = if (include.extra) list() else NULL
makeS3Obj("OptPath",
par.set = par.set,
y.names = y.names,
minimize = minimize,
add.transformed.x = add.transformed.x,
env = ee
)
}
#' @export
print.OptPath = function(x, ...) {
n = getOptPathLength(x)
em = x$env$error.message
et = x$env$exec.time
ex = x$env$extra
catf("Optimization path")
catf(" Dimensions: x = %i/%i, y = %i",
length(x$par.set$pars), sum(getParamLengths(x$par.set)), length(x$y.names))
catf(" Length: %i", n)
catf(" Add x values transformed: %s", x$add.transformed.x)
s = if (is.null(em)) "" else sprintf(" Errors: %i / %i.", sum(!is.na(em)), n)
catf(" Error messages: %s.%s", !is.null(em), s)
s = if (is.null(et)) {
s = ""
} else {
ntimes = sum(!is.na(et))
ntime.nas = length(et) - ntimes
# no non-na exec times in path
if (ntimes == 0L) {
et1 = 0
et2 = 0
} else {
et1 = min(et, na.rm = TRUE)
et2 = max(et, na.rm = TRUE)
}
s = sprintf(" Range: %g - %g. %i NAs.", et1, et2, ntime.nas)
}
catf(" Exec times: %s.%s", !is.null(et), s)
if (!is.null(ex)) {
nondot.extra.length = ifelse(length(ex) > 0L,
length(removeDotEntries(ex[[1L]])), NA)
catf(" Extras: %i columns", nondot.extra.length)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.