File: getBounds.R

package info (click to toggle)
r-cran-paramhelpers 1.14.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 992 kB
  • sloc: ansic: 102; sh: 13; makefile: 2
file content (173 lines) | stat: -rw-r--r-- 5,753 bytes parent folder | download
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)
}