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
|
#' @title Get lower / upper bounds and allowed discrete values for parameters.
#'
#' @description `getLower` and `getUpper` return a numerical vector of lower and
#' upper bounds, `getValues` returns a list of possible value sets for discrete
#' parameters.
#'
#' Parameters for which such bound make no sense - due to their type - are not
#' present in the result.
#'
#' @param obj ([Param()] | [ParamSet()] | `list`)\cr
#' Parameter, parameter set or list of parameters, whose boundaries and/or
#' values should be extracted. In case the boundaries or values contain
#' expressions, they will be evaluated using the provided dictionary `dict`.
#' @param with.nr (`logical(1)`)\cr
#' Should number from 1 to length be appended to names of vector params?
#' Default is `FALSE`.
#' @template arg_dict
#' @return `vector` | `list`. Named by parameter ids.
#' @export
#' @examples
#' ps = makeParamSet(
#' makeNumericParam("u"),
#' makeDiscreteParam("v", values = c("a", "b")),
#' makeIntegerParam("w", lower = expression(ceiling(p / 3)), upper = 2),
#' makeDiscreteParam("x", values = 1:2),
#' makeNumericVectorParam("y", len = 2, lower = c(0, 10), upper = c(1, 11)),
#' keys = "p"
#' )
#' getLower(ps, dict = list(p = 7))
#' getUpper(ps)
#'
#' ps = makeParamSet(
#' makeNumericParam("u"),
#' makeDiscreteParam("w", values = list(a = list(), b = NULL))
#' )
#' getValues(ps)
#'
#' par.vals = list(
#' u = makeNumericParam("u"),
#' v = makeIntegerParam("v", lower = 1, upper = 2),
#' w = makeDiscreteParam("w", values = 1:2),
#' x = makeNumericVectorParam("x", len = 2, lower = c(3, 1), upper = expression(n))
#' )
#' getLower(par.vals)
#' getUpper(par.vals, dict = list(n = 12))
getLower = function(obj, with.nr = FALSE, dict = NULL) {
getBounds(obj, type.of.bounds = "lower", with.nr = with.nr, dict = dict)
}
#' @export
#' @rdname getLower
getUpper = function(obj, with.nr = FALSE, dict = NULL) {
getBounds(obj, type.of.bounds = "upper", with.nr = with.nr, dict = dict)
}
#' @export
#' @rdname getLower
getValues = function(obj, dict = NULL) {
UseMethod("getValues")
}
#' @export
getValues.Param = function(obj, dict = NULL) {
assertClass(obj, "Param")
assertList(dict, names = "unique", null.ok = TRUE)
# values are only possible for params of the types above
if (!isDiscrete(obj)) {
return(NULL)
}
# error if dict is not defined, but values contains expression
if (is.null(dict) && hasExpression(obj)) {
stop("You need to provide a dictionary to get the values.")
}
eval(obj$values, envir = dict)
}
#' @export
getValues.ParamSet = function(obj, dict = NULL) {
assertClass(obj, "ParamSet")
assertList(dict, names = "unique", null.ok = TRUE)
types = getParamTypes(obj)
is.disc = types %fin% getTypeStringsDiscrete()
# only consider params with one of the types from above
if (!any(is.disc)) {
return(list())
}
# error if dict is not defined, but at least one value contains an expression
if (missing(dict) && any(vlapply(obj$pars[is.disc], hasExpression))) {
stop("You need to provide a dictionary to get the values.")
}
lapply(obj$pars[is.disc], function(p) eval(p$values, envir = dict))
}
#' @export
getValues.list = function(obj, dict = NULL) {
assertClass(obj, "list")
assertList(dict, names = "unique", null.ok = TRUE)
# create a pseudo-ParamSet and use of getValues.ParamSet
par.set = list(pars = obj)
class(par.set) = "ParamSet"
values = getValues(obj = par.set, dict = dict)
return(values)
}
getBounds = function(obj, type.of.bounds, with.nr = FALSE, dict = NULL) {
UseMethod("getBounds")
}
# workhorse for getLower and getUpper (for Param)
#' @export
getBounds.Param = function(obj, type.of.bounds, with.nr = FALSE, dict = NULL) {
assertClass(obj, "Param")
assertList(dict, names = "unique", null.ok = TRUE)
# if the Param is non-numeric, return NULL
if (!(obj$type %fin% getTypeStringsNumeric())) {
return(NULL)
}
# filter to numerics, and get bounds, flat-join them and name them
bound = obj[[type.of.bounds]]
# if the bound is an expression, it needs to be evaluated first
if (is.expression(bound)) {
if (is.null(dict)) {
stop("You need to provide a dictionary to get the bounds.")
}
bound = eval(bound, envir = dict)
}
# assure that the length of the bound corresponds to the pre-defined length
len = obj$len
if (length(bound) == 1L && !is.na(len) && len > 1L) {
bound = rep(bound, len)
}
setNames(bound, getParamIds(obj, repeated = TRUE, with.nr = with.nr))
}
# workhorse for getLower and getUpper for ParamSet
#' @export
getBounds.ParamSet = function(obj, type.of.bounds, with.nr = FALSE, dict = NULL) {
assertClass(obj, "ParamSet")
assertList(dict, names = "unique", null.ok = TRUE)
# if we don't have numerics, return empty vector
if (!hasNumeric(obj, include.int = TRUE)) {
return(numeric(0L))
}
# filter to numerics
psnum = filterParamsNumeric(obj, include.int = TRUE)
# get bounds of all numeric Params, flat-join and name them
bounds = lapply(psnum$pars, function(p) {
getBounds(obj = p, type.of.bounds = type.of.bounds, with.nr = with.nr, dict = dict)
})
setNames(unlist(bounds), getParamIds(psnum, repeated = TRUE, with.nr = with.nr))
}
# workhorse for of getLower and getUpper for regular lists
#' @export
getBounds.list = function(obj, type.of.bounds, with.nr = FALSE, dict = NULL) {
assertClass(obj, "list")
assertList(dict, names = "unique", null.ok = TRUE)
# create a pseudo-ParamSet and make use of the existing functions
par.set = list(pars = obj)
class(par.set) = "ParamSet"
bounds = getBounds(obj = par.set, type.of.bounds = type.of.bounds, with.nr = with.nr, dict = dict)
return(bounds)
}
|