From 7ea0d92e40232cb16689cf8551d72006a3791854 Mon Sep 17 00:00:00 2001 From: mb706 Date: Mon, 22 Mar 2021 20:24:20 +0100 Subject: [PATCH 01/64] faster --- R/Param.R | 63 +++++++----- R/ParamDbl.R | 44 ++++----- R/ParamFct.R | 17 ++-- R/ParamInt.R | 28 +++--- R/ParamSet.R | 84 +++++++++++----- R/ParamSetCollection.R | 116 +++++++++++++++++------ R/ParamUty.R | 16 ++-- R/helper.R | 26 +++-- man-roxygen/field_params_unid.R | 6 +- tests/testthat/test_ParamSet.R | 32 +++---- tests/testthat/test_ParamSetCollection.R | 11 +++ tests/testthat/test_Param_rep.R | 9 -- tests/testthat/test_sampler.R | 12 ++- 13 files changed, 288 insertions(+), 176 deletions(-) diff --git a/R/Param.R b/R/Param.R index 37bf5d99..65cdcf64 100644 --- a/R/Param.R +++ b/R/Param.R @@ -22,18 +22,6 @@ Param = R6Class("Param", #' Identifier of the object. id = NULL, - #' @field special_vals (`list()`)\cr - #' Arbitrary special values this parameter is allowed to take. - special_vals = NULL, - - #' @field default (`any`)\cr - #' Default value. - default = NULL, - - #' @field tags (`character()`)\cr - #' Arbitrary tags to group and subset parameters. - tags = NULL, - #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' @@ -47,9 +35,9 @@ Param = R6Class("Param", assert_character(tags, any.missing = FALSE, unique = TRUE) self$id = id - self$special_vals = special_vals - self$default = default - self$tags = tags + private$.special_vals = special_vals + private$.default = default + private$.tags = tags if (!is_nodefault(default)) { # check that default is feasible self$assert(default) } @@ -72,7 +60,7 @@ Param = R6Class("Param", }, error = function(e) paste("tune token invalid:", conditionMessage(e)))) } ch = private$.check(x) - ifelse(isTRUE(ch) || has_element(self$special_vals, x), TRUE, ch) + ifelse(isTRUE(ch) || has_element(private$.special_vals, x), TRUE, ch) }, #' @description @@ -100,18 +88,26 @@ Param = R6Class("Param", #' Each parameter is named "\[id\]_rep_\[k\]" and gets the additional tag "\[id\]_rep". #' #' @param n (`integer(1)`). - #' @return [ParamSet]. + #' @return [`ParamSet`]. rep = function(n) { assert_count(n) pid = self$id join_id = paste0(pid, "_rep") - ps = replicate(n, self$clone(), simplify = FALSE) - for (i in 1:n) { - p = ps[[i]] - p$id = paste0(join_id, "_", i) - p$tags = c(p$tags, join_id) - } - ParamSet$new(ps) + taggedself = self$with_tags(c(self$tags, join_id)) + repeatedself = structure(rep(list(taggedself), n), names = sprintf("%s_%s", join_id, seq_len(n))) + ParamSet$new(repeatedself, ignore_ids = TRUE) + }, + + #' @description + #' Creates a clone of this parameter with changed `$tags` slot. + #' @param tags (`character`) tags of the clone. + #' @return [`Param`]. + with_tags = function(tags) { + assert_character(tags, any.missing = FALSE, unique = TRUE) + origtags = private$.tags + on.exit({private$.tags = origtags}) + private$.tags = tags + self$clone(deep = TRUE) }, #' @description @@ -159,6 +155,18 @@ Param = R6Class("Param", ), active = list( + #' @field special_vals (`list()`)\cr + #' Arbitrary special values this parameter is allowed to take. + special_vals = function() private$.special_vals, + + #' @field default (`any`)\cr + #' Default value. + default = function() private$.default, + + #' @field tags (`character()`)\cr + #' Arbitrary tags to group and subset parameters. + tags = function() private$.tags, + #' @field class (`character(1)`)\cr #' R6 class name. Read-only. class = function() class(self)[[1L]], @@ -173,12 +181,15 @@ Param = R6Class("Param", #' @field has_default (`logical(1)`)\cr #' Is there a default value? - has_default = function() !is_nodefault(self$default) + has_default = function() !is_nodefault(private$.default) ), private = list( .check = function(x) stop("abstract"), - .qunif = function(x) stop("abstract") # should be implemented by subclasses, argcheck happens in Param$qunif + .qunif = function(x) stop("abstract"), # should be implemented by subclasses, argcheck happens in Param$qunif + .special_vals = NULL, + .default = NULL, + .tags = NULL ) ) diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 84a9e31e..32e23bc2 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -23,23 +23,12 @@ #' ParamDbl$new("ratio", lower = 0, upper = 1, default = 0.5) ParamDbl = R6Class("ParamDbl", inherit = Param, public = list( - #' @template field_lower - lower = NULL, - - #' @template field_upper - upper = NULL, - - #' @field tolerance (`numeric(1)`)\cr - #' tolerance of values to accept beyond `$lower` and `$upper`. - #' Used both for relative and absolute tolerance. - tolerance = NULL, - #' @description #' Creates a new instance of this [R6][R6::R6Class] class. initialize = function(id, lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps)) { - self$lower = assert_number(lower) - self$upper = assert_number(upper) - self$tolerance = assert_number(tolerance, lower = 0) + private$.lower = assert_number(lower) + private$.upper = assert_number(upper) + private$.tolerance = assert_number(tolerance, lower = 0) assert_true(lower <= upper) super$initialize(id, special_vals = special_vals, default = default, tags = tags) }, @@ -52,35 +41,46 @@ ParamDbl = R6Class("ParamDbl", inherit = Param, #' @param x (`numeric(1)`)\cr #' Value to convert. convert = function(x) { - min(max(x, self$lower), self$upper) + min(max(x, private$.lower), private$.upper) } ), active = list( + #' @template field_lower + lower = function() private$.lower, + #' @template field_upper + upper = function() private$.upper, + #' @field tolerance (`numeric(1)`)\cr + #' tolerance of values to accept beyond `$lower` and `$upper`. + #' Used both for relative and absolute tolerance. + tolerance = function() private$.tolerance, #' @template field_levels levels = function() NULL, #' @template field_nlevels nlevels = function() Inf, #' @template field_is_bounded - is_bounded = function() is.finite(self$lower) && is.finite(self$upper), + is_bounded = function() is.finite(private$.lower) && is.finite(private$.upper), #' @template field_storage_type storage_type = function() "numeric" ), private = list( .check = function(x) { - # Accept numbers between lower and upper bound, with tolerance self$tolerance + # Accept numbers between lower and upper bound, with tolerance `$tolerance` # Tolerance is both absolute & relative tolerance (if either tolerance is # undercut the value is accepted: - # Values that go beyond the bound by less than `self$tolerance` are also + # Values that go beyond the bound by less than `tolerance` are also # accepted (absolute tolerance) - # Values that go beyond the bound by less than `abs() * self$tolerance` + # Values that go beyond the bound by less than `abs() * tolerance` # are also accepted (relative tolerance) checkNumber(x, - lower = self$lower - self$tolerance * max(1, abs(self$lower)), - upper = self$upper + self$tolerance * max(1, abs(self$upper)) + lower = private$.lower - private$.tolerance * max(1, abs(private$.lower)), + upper = private$.upper + private$.tolerance * max(1, abs(private$.upper)) ) }, - .qunif = function(x) x * self$upper - (x-1) * self$lower + .qunif = function(x) x * private$.upper - (x-1) * private$.lower, + .lower = NULL, + .upper = NULL, + .tolerance = NULL ) ) diff --git a/R/ParamFct.R b/R/ParamFct.R index e703e732..80ebeb60 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -17,9 +17,6 @@ ParamFct = R6Class("ParamFct", inherit = Param, public = list( - #' @template field_levels - levels = NULL, - #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' @@ -27,18 +24,20 @@ ParamFct = R6Class("ParamFct", inherit = Param, #' Set of allowed levels. initialize = function(id, levels, special_vals = list(), default = NO_DEF, tags = character()) { assert_character(levels, any.missing = FALSE, unique = TRUE) - self$levels = levels + private$.levels = levels super$initialize(id, special_vals = special_vals, default = default, tags = tags) } ), active = list( + #' @template field_levels + levels = function() private$.levels, #' @template field_lower lower = function() NA_real_, #' @template field_upper upper = function() NA_real_, #' @template field_nlevels - nlevels = function() length(self$levels), + nlevels = function() length(private$.levels), #' @template field_is_bounded is_bounded = function() TRUE, #' @template field_storage_type @@ -46,11 +45,13 @@ ParamFct = R6Class("ParamFct", inherit = Param, ), private = list( - .check = function(x) check_choice(x, choices = self$levels), + .check = function(x) check_choice(x, choices = private$.levels), .qunif = function(x) { z = floor(x * self$nlevels * (1 - 1e-16)) + 1 # make sure we dont map to upper+1 - self$levels[z] - } + private$.levels[z] + }, + + .levels = NULL ) ) diff --git a/R/ParamInt.R b/R/ParamInt.R index 6dcabdcc..a85c70d8 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -20,24 +20,18 @@ #' ParamInt$new("count", lower = 0, upper = 10, default = 1) ParamInt = R6Class("ParamInt", inherit = Param, public = list( - #' @template field_lower - lower = NULL, - - #' @template field_upper - upper = NULL, - #' @description #' Creates a new instance of this [R6][R6::R6Class] class. initialize = function(id, lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character()) { if (isTRUE(is.infinite(lower))) { - self$lower = lower + private$.lower = lower } else { - self$lower = assert_int(lower) + private$.lower = assert_int(lower) } if (isTRUE(is.infinite(upper))) { - self$upper = upper + private$.upper = upper } else { - self$upper = assert_int(upper) + private$.upper = assert_int(upper) } assert_true(lower <= upper) super$initialize(id, special_vals = special_vals, default = default, tags = tags) @@ -53,18 +47,24 @@ ParamInt = R6Class("ParamInt", inherit = Param, ), active = list( + #' @template field_lower + lower = function() private$.lower, + #' @template field_upper + upper = function() private$.upper, #' @template field_levels levels = function() NULL, #' @template field_nlevels - nlevels = function() (self$upper - self$lower) + 1L, + nlevels = function() (private$.upper - private$.lower) + 1L, #' @template field_is_bounded - is_bounded = function() is.finite(self$lower) && is.finite(self$upper), + is_bounded = function() is.finite(private$.lower) && is.finite(private$.upper), #' @template field_storage_type storage_type = function() "integer" ), private = list( - .check = function(x) checkInt(x, lower = self$lower, upper = self$upper, tol = 1e-300), - .qunif = function(x) as.integer(floor(x * self$nlevels * (1 - 1e-16)) + self$lower) # make sure we dont map to upper+1 + .check = function(x) checkInt(x, lower = private$.lower, upper = private$.upper, tol = 1e-300), + .qunif = function(x) as.integer(floor(x * self$nlevels * (1 - 1e-16)) + private$.lower), # make sure we dont map to upper+1 + .lower = NULL, + .upper = NULL ) ) diff --git a/R/ParamSet.R b/R/ParamSet.R index 4f7e5c24..8b690d59 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -50,36 +50,55 @@ ParamSet = R6Class("ParamSet", #' #' @param params (`list()`)\cr #' List of [Param], named with their respective ID. - #' Parameters are cloned. - initialize = function(params = named_list()) { + #' `params` are cloned on-demand, so the input does not need + #' to be cloned. + #' @param ignore_ids (`logical(1)`)\cr + #' Ignore `$id` slots of `params` and instead use the names instead. + #' When this is `TRUE`, then `params` must be named. + #' This can be used to create a `ParamSet` with certain [`Param`] `id`s + #' without having to clone said [`Param`]s. + #' Default `FALSE`. + initialize = function(params = named_list(), ignore_ids = FALSE) { assert_list(params, types = "Param") - ids = map_chr(params, "id") - assert_names(ids, type = "strict") - private$.params = set_names(map(params, function(p) p$clone(deep = TRUE)), ids) + if (ignore_ids) { + ids = names(params) + } else { + ids = map_chr(params, "id") + params = set_names(params, ids) + } + assert_names(ids, type = "strict", .var.name = "params") + private$.params_unid = params self$set_id = "" }, #' @description - #' Adds a single param or another set to this set, all params are cloned. + #' Adds a single param or another set to this set, all params are cloned on-demand, + #' so the input does not need to be cloned. #' #' @param p ([Param] | [ParamSet]). add = function(p) { assert_multi_class(p, c("Param", "ParamSet")) - p = if (inherits(p, "Param")) { # level-up param to set - ParamSet$new(list(p)) + if (inherits(p, "Param")) { # level-up param to set + pparams = structure(list(p), names = p$id) + ptrafo = NULL + pvalues = NULL + pdeps = NULL } else { - p$clone(deep = TRUE) + pparams = p$params_unid + ptrafo = p$trafo + pvalues = p$values + pdeps = p$deps } - pparams = p$params - nn = c(names(private$.params), names(pparams)) + + nn = c(names(private$.params_unid), names(pparams)) assert_names(nn, type = "strict") - if (!is.null(p$trafo)) { + if (!is.null(ptrafo)) { stop("Cannot add a param set with a trafo.") } - private$.params = c(private$.params, pparams) - private$.values = c(private$.values, p$values) - private$.deps = rbind(private$.deps, p$deps) + private$.params_unid = c(private$.params_unid, pparams) + private$.values = c(private$.values, pvalues) + private$.deps = rbind(private$.deps, pdeps) invisible(self) }, @@ -173,7 +192,8 @@ ParamSet = R6Class("ParamSet", "\nIf you still want to do that, manipulate '$deps' yourself."), str_collapse(pids_not_there)) } } - private$.params = private$.params[ids] + private$.params_unid = private$.params_unid[ids] + private$.params = private$.params[intersect(ids, names(private$.params))] # restrict to ids already in pvals ids2 = union(intersect(ids, names(private$.values)), setdiff(names(private$.values), param_ids)) private$.values = private$.values[ids2] @@ -393,14 +413,30 @@ ParamSet = R6Class("ParamSet", if (!missing(rhs) && !identical(rhs, private$.params)) { stop("$params is read-only.") } + # clone on-demand + punid = self$params_unid + truenames = names(punid) + uncloned = setdiff(truenames, names(private$.params)) + if (length(uncloned)) { + private$.params = c(private$.params, + sapply(uncloned, function(u) { + p = punid[[u]] + if (p$id != u) { + p = p$clone(deep = TRUE) + p$id = u + } + p + }, simplify = FALSE) + )[truenames] + } private$.params }, #' @template field_params_unid params_unid = function(rhs) { - if (!missing(rhs) && !identical(rhs, private$.params)) { + if (!missing(rhs) && !identical(rhs, private$.params_unid)) { stop("$params_unid is read-only.") } - self$params + private$.params_unid }, #' @template field_deps @@ -596,12 +632,13 @@ ParamSet = R6Class("ParamSet", private = list( .set_id = NULL, .trafo = NULL, - .params = NULL, + .params = named_list(), + .params_unid = NULL, .values = named_list(), .deps = data.table(id = character(0L), on = character(0L), cond = list()), # return a slot / AB, as a named vec, named with id (and can enforce a certain vec-type) get_member_with_idnames = function(member, astype) { - params = self$params + params = self$params_unid set_names(astype(map(params, member)), names(params)) }, get_tune_ps = function(values) { @@ -613,7 +650,8 @@ ParamSet = R6Class("ParamSet", idmapping = map(partsets, function(x) x$ids()) pars = ps_union(partsets) pars$set_id = self$set_id - parsnames = names(pars$params) + parsparams = pars$params_unid + parsnames = names(parsparams) # only add the dependencies that are also in the tuning PS on = id = NULL # pacify static code check pmap(self$deps[id %in% names(idmapping) & on %in% names(partsets), c("on", "id", "cond")], function(on, id, cond) { @@ -624,7 +662,7 @@ ParamSet = R6Class("ParamSet", } # remove infeasible values from condition cond = cond$clone(deep = TRUE) - cond$rhs = keep(cond$rhs, pars$params[[on]]$test) + cond$rhs = keep(cond$rhs, parsparams[[on]]$test) if (!length(cond$rhs)) { # no value is feasible, but there may be a trafo that fixes this # so we are forgiving here. @@ -639,7 +677,7 @@ ParamSet = R6Class("ParamSet", deep_clone = function(name, value) { switch(name, - .params = map(value, function(x) x$clone(deep = TRUE)), + .params = named_list(), .deps = { value = copy(value) value$cond = lapply(value$cond, function(x) x$clone(deep = TRUE)) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index ee7aef2a..80d2f8c4 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -29,20 +29,26 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Creates a new instance of this [R6][R6::R6Class] class. #' #' @param sets (`list()` of [ParamSet])\cr - #' Parameter objects are cloned. - initialize = function(sets) { + #' ParamSet objects are not cloned. + #' @param ignore_ids (`logical(1)`)\cr + #' Ignore `$set_id` slots of `sets` and instead use the names instead. + #' When this is `TRUE`, then `sets` should be named when id prefixes are desired. + #' This can be used to create a `ParamSetCollection` with changed [`ParamSet`] `set_id`s + #' without having to clone said [`ParamSet`]s. However, when underlying [`ParamSet`]s' + #' `$set_id`s change, then this change is no longer reflected in the ParamSetCollection. + #' Default `FALSE`. + initialize = function(sets, ignore_ids = FALSE) { assert_list(sets, types = "ParamSet") - split_sets = split(sets, map_lgl(sets, function(x) x$set_id == "")) + if (!ignore_ids) { + names(sets) = map(sets, "set_id") + } + split_sets = split(sets, names(sets) == "") nameless_sets = split_sets$`TRUE` named_sets = split_sets$`FALSE` - setids = map_chr(named_sets, "set_id") - assert_names(setids, type = "unique") + assert_names(names2(named_sets), type = "strict") assert_names(unlist(map(nameless_sets, function(x) names(x$params_unid))) %??% character(0), type = "unique") - if (any(map_lgl(sets, "has_trafo"))) { - # we need to be able to have a trafo on the collection, not sure how to mix this with individual trafos yet. - stop("Building a collection out sets, where a ParamSet has a trafo is currently unsupported!") - } + if (!ignore_ids) sets = unname(sets) # when private$.sets is unnamed, then the set_ids are used. private$.sets = sets self$set_id = "" pnames = names(self$params_unid) @@ -59,7 +65,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' @param p ([ParamSet]). add = function(p) { assert_r6(p, "ParamSet") - setnames = map_chr(private$.sets, "set_id") + setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") if (p$set_id == "") { unnamed_set_parnames = map(private$.sets[setnames == ""], function(x) names(x$params_unid)) } else if (p$set_id %in% setnames) { @@ -76,7 +82,12 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, if (length(nameclashes)) { stopf("Adding parameter set would lead to nameclashes: %s", str_collapse(nameclashes)) } - private$.sets = c(private$.sets, list(p)) + set_addition = list(p) + if (!is.null(names(private$.sets))) { + # ignoring the other ParamSet's set_id in favor of names(private$.sets), so add the name here as well. + names(set_addition) = p$set_id + } + private$.sets = c(private$.sets, set_addition) invisible(self) }, @@ -85,7 +96,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' #' @param ids (`character()`). remove_sets = function(ids) { - setnames = map_chr(private$.sets, "set_id") + setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") assert_subset(ids, setnames) private$.sets[setnames %in% ids] = NULL invisible(self) @@ -101,22 +112,41 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, active = list( #' @template field_params - params = function(v) { - ps_all = self$params_unid - imap(ps_all, function(x, n) { - x = x$clone(deep = TRUE) - x$id = n - x - }) + params = function(rhs) { + if (!missing(rhs) && !identical(rhs, private$.params)) { + stop("$params is read-only.") + } + # clone on-demand, whenever underlying class changed + punid = self$params_unid + truenames = names(punid) + + # 'changed' can be either because an underlying ParamSet's $params changed though $add or so, + # or because we are calling $params for the first time (in which case all are 'changed' + # as the original is NULL). + changed = as.logical(imap(punid, function(x, n) !identical(private$.params_cloned[[n]], x))) + + if (any(changed)) { + changed_names = truenames[changed] + private$.params = c(private$.params[setdiff(names(private$.params), changed_names)], # don't regenerate Params that were not changed. + sapply(changed_names, function(u) { + p = punid[[u]]$clone(deep = TRUE) + p$id = u + p + }, simplify = FALSE) + )[truenames] + } + private$.params_cloned = punid + private$.params }, #' @template field_params_unid params_unid = function(v) { sets = private$.sets - names(sets) = map_chr(sets, "set_id") + if (is.null(names(sets))) { + names(sets) = map(private$.sets, "set_id") + } if (length(sets) == 0L) { return(named_list()) } - private$.params = named_list() # clone each param into new params-list and prefix id ps_all = lapply(sets, function(s) s$params_unid) ps_all = unlist(ps_all, recursive = FALSE) @@ -125,12 +155,16 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, }, #' @template field_deps deps = function(v) { - d_all = lapply(private$.sets, function(s) { + sets = private$.sets + if (is.null(names(sets))) { + names(sets) = map(private$.sets, "set_id") + } + d_all = imap(sets, function(s, id) { # copy all deps and rename ids to prefixed versions dd = copy(s$deps) ids_old = s$ids() - if (s$set_id != "" && nrow(dd)) { - ids_new = sprintf("%s.%s", s$set_id, ids_old) + if (id != "" && nrow(dd)) { + ids_new = sprintf("%s.%s", id, ids_old) dd$id = map_values(dd$id, ids_old, ids_new) dd$on = map_values(dd$on, ids_old, ids_new) } @@ -142,20 +176,24 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' @template field_values values = function(xs) { sets = private$.sets - names(sets) = map_chr(sets, "set_id") + if (is.null(names(sets))) { + names(sets) = map(private$.sets, "set_id") + } if (!missing(xs)) { assert_list(xs) self$assert(xs) # make sure everything is valid and feasible - for (s in sets) { + for (i in seq_along(sets)) { + s = sets[[i]] + sid = names(sets)[[i]] # retrieve sublist for each set, then assign it in set (after removing prefix) psids = names(s$params_unid) - if (s$set_id != "") { - psids = sprintf("%s.%s", s$set_id, psids) + if (sid != "") { + psids = sprintf("%s.%s", sid, psids) } pv = xs[intersect(psids, names(xs))] - if (s$set_id != "") { - names(pv) = substr(names(pv), nchar(s$set_id) + 2, nchar(names(pv))) + if (sid != "") { + names(pv) = substr(names(pv), nchar(sid) + 2, nchar(names(pv))) } s$values = pv } @@ -168,6 +206,22 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, ), private = list( - .sets = NULL + .sets = NULL, + .params_cloned = NULL, + deep_clone = function(name, value) { + switch(name, + .params = named_list(), + .params_cloned = NULL, + .deps = { + value = copy(value) + value$cond = lapply(value$cond, function(x) x$clone(deep = TRUE)) + value + }, + .sets = map(value, function(x) { + x$clone(deep = TRUE) + }), + value + ) + } ) ) diff --git a/R/ParamUty.R b/R/ParamUty.R index 51bf917a..2f05dd1d 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -15,10 +15,6 @@ #' ParamUty$new("untyped", default = Inf) ParamUty = R6Class("ParamUty", inherit = Param, public = list( - #' @field custom_check (`function()`)\cr - #' Custom function to check the feasibility. - custom_check = NULL, - #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' @@ -31,15 +27,18 @@ ParamUty = R6Class("ParamUty", inherit = Param, # super class calls private$.check, so this must be set BEFORE # we initialize the super class if (is.null(custom_check)) { - self$custom_check = function(x) TRUE + private$.custom_check = function(x) TRUE } else { - self$custom_check = assert_function(custom_check, "x") + private$.custom_check = assert_function(custom_check, "x") } super$initialize(id, special_vals = list(), default = default, tags = tags) } ), active = list( + #' @field custom_check (`function()`)\cr + #' Custom function to check the feasibility. + custom_check = function() private$.custom_check, #' @template field_lower lower = function() NA_real_, #' @template field_upper @@ -55,7 +54,8 @@ ParamUty = R6Class("ParamUty", inherit = Param, ), private = list( - .check = function(x) self$custom_check(x), - .qunif = function(x) stop("undefined") + .check = function(x) private$.custom_check(x), + .qunif = function(x) stop("undefined"), + .custom_check = NULL ) ) diff --git a/R/helper.R b/R/helper.R index 4a58ddbd..46e8e86f 100644 --- a/R/helper.R +++ b/R/helper.R @@ -39,18 +39,14 @@ transpose = function(data, ps = NULL, filter_na = TRUE, trafo = TRUE) { # from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will # have their changed to .. This is also reflected in deps and in `$trafo`. # @param sets: list of ParamSet -ps_union = function(sets) { +ps_union = function(sets, ignore_ids = FALSE) { assert_list(sets, types = "ParamSet") - assert_names(discard(map_chr(sets, "set_id"), `==`, ""), type = "unique") - psc = ParamSetCollection$new(map(sets, function(x) { - if (x$has_trafo) { - # PSC can not use ParamSet with a `$trafo` that is set. - x = x$clone() - x$trafo = NULL - } - x - })) + if (!ignore_ids) { + names(sets) = map(sets, "set_id") + } + + psc = ParamSetCollection$new(sets, ignore_ids = TRUE) newps = ParamSet$new()$add(psc) @@ -65,20 +61,20 @@ ps_union = function(sets) { # Why is this needed? If the $trafo() is given a value `list(sid.pid = 1)`, then # `forward_name_translation` can be used to rename this to `list(pid = 1)`, which is what the # original trafo expects. - setinfo = map(unname(sets), function(s) { + setinfo = unname(imap(sets, function(s, n) { sparams = s$params # avoid slow ParamSetCollection $params active binding sinfo = list( trafo = s$trafo, - set_id = s$set_id, + set_id = n, forward_name_translation = names2(sparams) ) psids = names2(sparams) - if (s$set_id != "") { - psids = sprintf("%s.%s", s$set_id, psids) + if (n != "") { + psids = sprintf("%s.%s", n, psids) } names(sinfo$forward_name_translation) = psids sinfo - }) + })) if (any(map_lgl(sets, "has_trafo"))) { # allnames: names of all parameters, as seen from the outside diff --git a/man-roxygen/field_params_unid.R b/man-roxygen/field_params_unid.R index 5fcf3b61..62f22723 100644 --- a/man-roxygen/field_params_unid.R +++ b/man-roxygen/field_params_unid.R @@ -1,5 +1,7 @@ #' @field params_unid (named `list()`)\cr #' List of [Param], named with their true ID. However, #' this field has the [Param]'s `$id` value set to a -#' potentially invalid value. This active binding should -#' only be used internally. +#' potentially invalid value, and it is furthermore not +#' cloned when the `ParamSet` is cloned. This active +#' binding should be used internally, or to avoid unnecessary +#' cloning when that becomes a bottleneck. diff --git a/tests/testthat/test_ParamSet.R b/tests/testthat/test_ParamSet.R index 2785b3ef..03c41913 100644 --- a/tests/testthat/test_ParamSet.R +++ b/tests/testthat/test_ParamSet.R @@ -145,33 +145,33 @@ test_that("ParamSet$print", { } }) -test_that("ParamSet does a deep copy of params on construction", { +test_that("ParamSet does a delayed deep copy of params on construction", { p = ParamDbl$new("x", lower = 1, upper = 3) - ps = ParamSet$new(list(p)) - p$lower = 2 - expect_equal(p$lower, 2) - expect_equal(ps$lower, c(x = 1)) - expect_equal(ps$params[["x"]]$lower, 1) + ps = ParamSet$new(list(y = p), ignore_ids = TRUE) + expect_equal(ps$params_unid, list(y = p)) + expect_equal(ps$params, list(y = ParamDbl$new("y", lower = 1, upper = 3))) }) -test_that("ParamSet does a deep copy of param on add", { +test_that("ParamSet does a delayed deep copy of param on add", { p = ParamDbl$new("x", lower = 1, upper = 3) - ps = ParamSet$new(list())$add(p) - p$lower = 2 - expect_equal(p$lower, 2) - expect_equal(ps$lower, c(x = 1)) - expect_equal(ps$params[["x"]]$lower, 1) + ps = ParamSet$new(list())$add(ParamSet$new(list(y = p), ignore_ids = TRUE)) + expect_equal(ps$params_unid, list(y = p)) + expect_equal(ps$params, list(y = ParamDbl$new("y", lower = 1, upper = 3))) + }) test_that("ParamSet$clone can be deep", { + p1 = ParamDbl$new("x", lower = 1, upper = 3) p2 = ParamFct$new("y", levels = c("a", "b")) ps1 = ParamSet$new(list(p1, p2)) ps2 = ps1$clone(deep = TRUE) - pp = ps2$params[["x"]] - pp$lower = 9 - expect_equal(ps2$lower, c(x = 9, y = NA)) - expect_equal(ps1$lower, c(x = 1, y = NA)) + + p3 = ParamFct$new("z", levels = c("a", "b")) + ps2$add(p3) + + expect_equal(ps2$params, list(x = p1, y = p2, z = p3)) + expect_equal(ps1$params, list(x = p1, y = p2)) # now lets add a dep, see if that gets clones properly ps1$add_dep("x", on = "y", CondEqual$new("a")) diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index 541707d4..ed9e55ea 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -57,6 +57,12 @@ test_that("ParamSet basic stuff works", { expect_equal(x$s2.th_param_int, 99) } + # Generate cached values for comparisons + ps1$params + ps2$params + ps1clone$params + ps2clone$params + # ps1 and ps2 should not be changed expect_equal(ps1, ps1clone) expect_equal(ps2, ps2clone) @@ -165,6 +171,11 @@ test_that("values", { setindex(ps1$deps, NULL) setindex(ps2$deps, NULL) + ps1$params + ps2$params + ps1clone$params + ps2clone$params + expect_equal(ps1clone, ps1) expect_equal(ps2clone, ps2) }) diff --git a/tests/testthat/test_Param_rep.R b/tests/testthat/test_Param_rep.R index 1ba731b0..4b4fe859 100644 --- a/tests/testthat/test_Param_rep.R +++ b/tests/testthat/test_Param_rep.R @@ -21,12 +21,3 @@ test_that("rep params work", { } }) - -test_that("rep params deep copies", { - p = ParamDbl$new(id = "x", lower = 1, upper = 3) - ps = p$rep(1L) - # lets change the first param - p$lower = 99 - expect_equal(p$lower, 99) - expect_equal(ps$params[["x_rep_1"]]$lower, 1) -}) diff --git a/tests/testthat/test_sampler.R b/tests/testthat/test_sampler.R index 8e42d2ba..876ca01d 100644 --- a/tests/testthat/test_sampler.R +++ b/tests/testthat/test_sampler.R @@ -104,8 +104,16 @@ test_that("we had a bug where creating the joint sampler changed the ps-ref of t s1 = Sampler1DCateg$new(p1) s2 = Sampler1DUnif$new(p2) s = SamplerJointIndep$new(list(s1, s2)) - expect_equal(s1$param_set, ParamSet$new(list(th_param_fct()))) - expect_equal(s2$param_set, ParamSet$new(list(th_param_dbl()))) + + s1_expected = ParamSet$new(list(th_param_fct())) + s1_expected$params + s1$param_set$params + expect_equal(s1$param_set, s1_expected) + + s2_expected = ParamSet$new(list(th_param_dbl())) + s2_expected$params + s2$param_set$params + expect_equal(s2$param_set, s2_expected) }) test_that("Sampler1DRfun with 0 samples (#338)", { From 8e59aabfb7f3b3bb33a8cb08c20fc825f3767fdb Mon Sep 17 00:00:00 2001 From: mb706 Date: Mon, 22 Mar 2021 20:50:06 +0100 Subject: [PATCH 02/64] extend tests a little --- R/ParamSetCollection.R | 2 +- tests/testthat/test_ParamSetCollection.R | 47 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 80d2f8c4..053cc265 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -125,7 +125,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # as the original is NULL). changed = as.logical(imap(punid, function(x, n) !identical(private$.params_cloned[[n]], x))) - if (any(changed)) { + if (any(changed) || length(punid) != length(private$.params_cloned)) { # if there was a strict subset operation we wouldn't notice it otherwise changed_names = truenames[changed] private$.params = c(private$.params[setdiff(names(private$.params), changed_names)], # don't regenerate Params that were not changed. sapply(changed_names, function(u) { diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index ed9e55ea..7d241a79 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -282,3 +282,50 @@ test_that("set_id inference in values assignment works now", { expect_error(ParamSetCollection$new(list(pscol1, pstest)), "duplicated parameter.* a\\.c\\.paramc") }) + +test_that("cloning and changing underlying params works", { + ps1 = th_paramset_dbl1() + ps1$set_id = "s1" + ps2 = th_paramset_full() + ps2$set_id = "s2" + ps3 = th_paramset_dbl1() + ps3$set_id = "" + psc1 = ParamSetCollection$new(list(ps1, ps2)) + psc2 = ParamSetCollection$new(list(psc1, ps3)) + + expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in any psc + expect_length(psc2$.__enclos_env__$private$.params, 0) + + expect_equal(psc2$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) + expect_equal(psc2$params_unid[6], ps3$params[1]) + expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in psc1 + expect_length(psc2$.__enclos_env__$private$.params, 0) + + pe = th_paramset_dbl1()$clone(deep = TRUE)$params[[1]] + pe$id = "s1.th_param_dbl" + expect_equal(psc2$params[1], list(s1.th_param_dbl = pe)) + + expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in psc1 + expect_length(psc2$.__enclos_env__$private$.params, 6) # but psc2 has .params now + + ps1$params[[1]]$id = "test" + expect_equal(psc2$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) + expect_equal(psc2$params[1], list(s1.th_param_dbl = pe)) + + psc2_clone = psc2$clone(deep = TRUE) + + psc1$remove_sets("s2") + expect_equal(psc2$params_unid, list(s1.th_param_dbl = ps1$params[[1]], th_param_dbl = ps3$params[[1]])) + expect_equal(psc2$params, list(s1.th_param_dbl = pe, th_param_dbl = ps3$params[[1]])) + + ps1$add(ParamInt$new("x")) + + expect_equal(psc2$params_unid, list(s1.th_param_dbl = ps1$params[[1]], s1.x = ps1$params[[2]], th_param_dbl = ps3$params[[1]])) + expect_equal(psc2$params, list(s1.th_param_dbl = pe, s1.x = ParamInt$new("s1.x"), th_param_dbl = ps3$params[[1]])) + + expect_equal(psc2_clone$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) + expect_equal(psc2_clone$params_unid[6], ps3$params[1]) + expect_equal(psc2_clone$params[1], list(s1.th_param_dbl = pe)) + expect_equal(psc2_clone$params[6], ps3$params[1]) + +}) From ae1e32ded28773183201aecb511f1007c513c071 Mon Sep 17 00:00:00 2001 From: mb706 Date: Mon, 22 Mar 2021 22:08:45 +0100 Subject: [PATCH 03/64] minor improvements --- R/NoDefault.R | 2 +- R/ParamSet.R | 4 +++- R/ParamSetCollection.R | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/R/NoDefault.R b/R/NoDefault.R index 31de1240..44b2e57c 100644 --- a/R/NoDefault.R +++ b/R/NoDefault.R @@ -22,4 +22,4 @@ NoDefault = R6Class("NoDefault", #' @export NO_DEF = NoDefault$new() # nolint -is_nodefault = function(x) test_r6(x, "NoDefault") +is_nodefault = function(x) inherits(x, "NoDefault") diff --git a/R/ParamSet.R b/R/ParamSet.R index 8b690d59..1851870c 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -702,7 +702,9 @@ ParamSet = R6Class("ParamSet", #' @export as.data.table.ParamSet = function(x, ...) { # nolint - map_dtr(x$params, as.data.table) + punid = x$params_unid + id = NULL + map_dtr(punid, as.data.table)[, id := names(punid)][] } #' @export diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 053cc265..786aa432 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -47,7 +47,6 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, nameless_sets = split_sets$`TRUE` named_sets = split_sets$`FALSE` assert_names(names2(named_sets), type = "strict") - assert_names(unlist(map(nameless_sets, function(x) names(x$params_unid))) %??% character(0), type = "unique") if (!ignore_ids) sets = unname(sets) # when private$.sets is unnamed, then the set_ids are used. private$.sets = sets self$set_id = "" From 1fa2d7354bf406a0f5d466bd8ad354ac6a9895bb Mon Sep 17 00:00:00 2001 From: mb706 Date: Mon, 22 Mar 2021 22:08:51 +0100 Subject: [PATCH 04/64] document --- man/Param.Rd | 37 +++++++++++++++++++++++++++++-------- man/ParamDbl.Rd | 12 ++++-------- man/ParamFct.Rd | 12 ++++-------- man/ParamInt.Rd | 12 ++++-------- man/ParamLgl.Rd | 1 + man/ParamSet.Rd | 21 ++++++++++++++++----- man/ParamSetCollection.Rd | 18 ++++++++++++++---- man/ParamUty.Rd | 12 ++++-------- 8 files changed, 76 insertions(+), 49 deletions(-) diff --git a/man/Param.Rd b/man/Param.Rd index e8d5103d..7635f342 100644 --- a/man/Param.Rd +++ b/man/Param.Rd @@ -30,7 +30,12 @@ Other Params: \describe{ \item{\code{id}}{(\code{character(1)})\cr Identifier of the object.} - +} +\if{html}{\out{}} +} +\section{Active bindings}{ +\if{html}{\out{
}} +\describe{ \item{\code{special_vals}}{(\code{list()})\cr Arbitrary special values this parameter is allowed to take.} @@ -39,12 +44,7 @@ Default value.} \item{\code{tags}}{(\code{character()})\cr Arbitrary tags to group and subset parameters.} -} -\if{html}{\out{
}} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ + \item{\code{class}}{(\code{character(1)})\cr R6 class name. Read-only.} @@ -67,6 +67,7 @@ Is there a default value?} \item \href{#method-assert}{\code{Param$assert()}} \item \href{#method-test}{\code{Param$test()}} \item \href{#method-rep}{\code{Param$rep()}} +\item \href{#method-with_tags}{\code{Param$with_tags()}} \item \href{#method-format}{\code{Param$format()}} \item \href{#method-print}{\code{Param$print()}} \item \href{#method-qunif}{\code{Param$qunif()}} @@ -205,7 +206,27 @@ Each parameter is named "[id]\emph{rep}[k]" and gets the additional tag "[id]_re \if{html}{\out{
}} } \subsection{Returns}{ -\link{ParamSet}. +\code{\link{ParamSet}}. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-with_tags}{}}} +\subsection{Method \code{with_tags()}}{ +Creates a clone of this parameter with changed \verb{$tags} slot. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{Param$with_tags(tags)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{tags}}{(\code{character}) tags of the clone.} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +\code{\link{Param}}. } } \if{html}{\out{
}} diff --git a/man/ParamDbl.Rd b/man/ParamDbl.Rd index f3a50bdb..01b1a858 100644 --- a/man/ParamDbl.Rd +++ b/man/ParamDbl.Rd @@ -26,8 +26,8 @@ Other Params: \section{Super class}{ \code{\link[paradox:Param]{paradox::Param}} -> \code{ParamDbl} } -\section{Public fields}{ -\if{html}{\out{
}} +\section{Active bindings}{ +\if{html}{\out{
}} \describe{ \item{\code{lower}}{(\code{numeric(1)})\cr Lower bound. @@ -40,12 +40,7 @@ Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} \item{\code{tolerance}}{(\code{numeric(1)})\cr tolerance of values to accept beyond \verb{$lower} and \verb{$upper}. Used both for relative and absolute tolerance.} -} -\if{html}{\out{
}} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ + \item{\code{levels}}{(\code{character()} | \code{NULL})\cr Set of allowed levels. Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. @@ -90,6 +85,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} } diff --git a/man/ParamFct.Rd b/man/ParamFct.Rd index fed2a0ad..9cdaa938 100644 --- a/man/ParamFct.Rd +++ b/man/ParamFct.Rd @@ -21,19 +21,14 @@ Other Params: \section{Super class}{ \code{\link[paradox:Param]{paradox::Param}} -> \code{ParamFct} } -\section{Public fields}{ -\if{html}{\out{
}} +\section{Active bindings}{ +\if{html}{\out{
}} \describe{ \item{\code{levels}}{(\code{character()} | \code{NULL})\cr Set of allowed levels. Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. Always \code{c(TRUE, FALSE)} for \link{ParamLgl}.} -} -\if{html}{\out{
}} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ + \item{\code{lower}}{(\code{numeric(1)})\cr Lower bound. Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} @@ -81,6 +76,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} } diff --git a/man/ParamInt.Rd b/man/ParamInt.Rd index 63796507..9bbe9fcd 100644 --- a/man/ParamInt.Rd +++ b/man/ParamInt.Rd @@ -26,8 +26,8 @@ Other Params: \section{Super class}{ \code{\link[paradox:Param]{paradox::Param}} -> \code{ParamInt} } -\section{Public fields}{ -\if{html}{\out{
}} +\section{Active bindings}{ +\if{html}{\out{
}} \describe{ \item{\code{lower}}{(\code{numeric(1)})\cr Lower bound. @@ -36,12 +36,7 @@ Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} \item{\code{upper}}{(\code{numeric(1)})\cr Upper bound. Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} -} -\if{html}{\out{
}} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ + \item{\code{levels}}{(\code{character()} | \code{NULL})\cr Set of allowed levels. Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. @@ -86,6 +81,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} } diff --git a/man/ParamLgl.Rd b/man/ParamLgl.Rd index 4f8257c3..8904a0e1 100644 --- a/man/ParamLgl.Rd +++ b/man/ParamLgl.Rd @@ -76,6 +76,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} } diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 14fca66c..5232f6fa 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -65,8 +65,10 @@ List of \link{Param}, named with their respective ID.} \item{\code{params_unid}}{(named \code{list()})\cr List of \link{Param}, named with their true ID. However, this field has the \link{Param}'s \verb{$id} value set to a -potentially invalid value. This active binding should -only be used internally.} +potentially invalid value, and it is furthermore not +cloned when the \code{ParamSet} is cloned. This active +binding should be used internally, or to avoid unnecessary +cloning when that becomes a bottleneck.} \item{\code{deps}}{(\code{\link[data.table:data.table]{data.table::data.table()}})\cr Table has cols \code{id} (\code{character(1)}) and \code{on} (\code{character(1)}) and \code{cond} (\link{Condition}). @@ -190,7 +192,7 @@ Has the set parameter dependencies?} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$new(params = named_list())}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$new(params = named_list(), ignore_ids = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -198,7 +200,15 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \describe{ \item{\code{params}}{(\code{list()})\cr List of \link{Param}, named with their respective ID. -Parameters are cloned.} +\code{params} are cloned on-demand, so the input does not need +to be cloned.} + +\item{\code{ignore_ids}}{(\code{logical(1)})\cr +Ignore \verb{$id} slots of \code{params} and instead use the names instead. +When this is \code{TRUE}, then \code{params} must be named. +This can be used to create a \code{ParamSet} with certain \code{\link{Param}} \code{id}s +without having to clone said \code{\link{Param}}s. +Default \code{FALSE}.} } \if{html}{\out{
}} } @@ -207,7 +217,8 @@ Parameters are cloned.} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-add}{}}} \subsection{Method \code{add()}}{ -Adds a single param or another set to this set, all params are cloned. +Adds a single param or another set to this set, all params are cloned on-demand, +so the input does not need to be cloned. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$add(p)}\if{html}{\out{
}} } diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 0ec29b62..0221c505 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -40,8 +40,10 @@ List of \link{Param}, named with their respective ID.} \item{\code{params_unid}}{(named \code{list()})\cr List of \link{Param}, named with their true ID. However, this field has the \link{Param}'s \verb{$id} value set to a -potentially invalid value. This active binding should -only be used internally.} +potentially invalid value, and it is furthermore not +cloned when the \code{ParamSet} is cloned. This active +binding should be used internally, or to avoid unnecessary +cloning when that becomes a bottleneck.} \item{\code{deps}}{(\code{\link[data.table:data.table]{data.table::data.table()}})\cr Table has cols \code{id} (\code{character(1)}) and \code{on} (\code{character(1)}) and \code{cond} (\link{Condition}). @@ -91,14 +93,22 @@ When you set values, all previously set values will be unset / removed.} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSetCollection$new(sets)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSetCollection$new(sets, ignore_ids = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{sets}}{(\code{list()} of \link{ParamSet})\cr -Parameter objects are cloned.} +ParamSet objects are not cloned.} + +\item{\code{ignore_ids}}{(\code{logical(1)})\cr +Ignore \verb{$set_id} slots of \code{sets} and instead use the names instead. +When this is \code{TRUE}, then \code{sets} should be named when id prefixes are desired. +This can be used to create a \code{ParamSetCollection} with changed \code{\link{ParamSet}} \code{set_id}s +without having to clone said \code{\link{ParamSet}}s. However, when underlying \code{\link{ParamSet}}s' +\verb{$set_id}s change, then this change is no longer reflected in the ParamSetCollection. +Default \code{FALSE}.} } \if{html}{\out{
}} } diff --git a/man/ParamUty.Rd b/man/ParamUty.Rd index 55f898c0..68886644 100644 --- a/man/ParamUty.Rd +++ b/man/ParamUty.Rd @@ -21,17 +21,12 @@ Other Params: \section{Super class}{ \code{\link[paradox:Param]{paradox::Param}} -> \code{ParamUty} } -\section{Public fields}{ -\if{html}{\out{
}} -\describe{ -\item{\code{custom_check}}{(\verb{function()})\cr -Custom function to check the feasibility.} -} -\if{html}{\out{
}} -} \section{Active bindings}{ \if{html}{\out{
}} \describe{ +\item{\code{custom_check}}{(\verb{function()})\cr +Custom function to check the feasibility.} + \item{\code{lower}}{(\code{numeric(1)})\cr Lower bound. Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} @@ -84,6 +79,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} } From ca6c1c395492ffb243c7df3cbf30a645d25cf82c Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 00:35:59 +0100 Subject: [PATCH 05/64] more efficient deps handling in PSC --- R/ParamSetCollection.R | 4 ++-- R/helper.R | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 786aa432..1cd48233 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -160,9 +160,9 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, } d_all = imap(sets, function(s, id) { # copy all deps and rename ids to prefixed versions - dd = copy(s$deps) - ids_old = s$ids() + dd = s$deps if (id != "" && nrow(dd)) { + ids_old = s$ids() ids_new = sprintf("%s.%s", id, ids_old) dd$id = map_values(dd$id, ids_old, ids_new) dd$on = map_values(dd$on, ids_old, ids_new) diff --git a/R/helper.R b/R/helper.R index 46e8e86f..3c48ff44 100644 --- a/R/helper.R +++ b/R/helper.R @@ -61,8 +61,8 @@ ps_union = function(sets, ignore_ids = FALSE) { # Why is this needed? If the $trafo() is given a value `list(sid.pid = 1)`, then # `forward_name_translation` can be used to rename this to `list(pid = 1)`, which is what the # original trafo expects. - setinfo = unname(imap(sets, function(s, n) { - sparams = s$params # avoid slow ParamSetCollection $params active binding + setinfo = unname(imap(keep(sets, function(x) x$has_trafo), function(s, n) { + sparams = s$params_unid # avoid slow ParamSetCollection $params active binding sinfo = list( trafo = s$trafo, set_id = n, @@ -76,7 +76,7 @@ ps_union = function(sets, ignore_ids = FALSE) { sinfo })) - if (any(map_lgl(sets, "has_trafo"))) { + if (length(setinfo)) { # allnames: names of all parameters, as seen from the outside allnames = names2(unlist(map(setinfo, "forward_name_translation"))) assert_set_equal(allnames, names2(newps$params)) # this should always be the case @@ -87,17 +87,17 @@ ps_union = function(sets, ignore_ids = FALSE) { # get the parameter values that the current trafo should operate on, # as identified by the names in forward_name_translation pv = x[match(names(s$forward_name_translation), names(x), nomatch = 0)] - if (!is.null(trafo)) { - # translate name from "." to "" - names(pv) = s$forward_name_translation[names(pv)] - pv = trafo(pv) - - # append prefix again. trafo() could have changed parameter names, so - # we can't use any cached name_translation magic here - if (s$set_id != "") { - names(pv) = sprintf("%s.%s", s$set_id, names(pv)) - } + + # translate name from "." to "" + names(pv) = s$forward_name_translation[names(pv)] + pv = trafo(pv) + + # append prefix again. trafo() could have changed parameter names, so + # we can't use any cached name_translation magic here + if (s$set_id != "") { + names(pv) = sprintf("%s.%s", s$set_id, names(pv)) } + pv }), recursive = FALSE) From 3592457b9627fb1713d68cb78292d00d17d60d52 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 01:09:13 +0100 Subject: [PATCH 06/64] fix ps_union --- R/helper.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/helper.R b/R/helper.R index 3c48ff44..e7dc1aa8 100644 --- a/R/helper.R +++ b/R/helper.R @@ -79,7 +79,7 @@ ps_union = function(sets, ignore_ids = FALSE) { if (length(setinfo)) { # allnames: names of all parameters, as seen from the outside allnames = names2(unlist(map(setinfo, "forward_name_translation"))) - assert_set_equal(allnames, names2(newps$params)) # this should always be the case + assert_subset(allnames, names2(newps$params_unid)) # just check, this should always be the case newps$trafo = crate(function(x, param_set) { res = unlist(mlr3misc::map(setinfo, function(s) { From edc10443b8f653b1935e2a19083ac6f5754da099 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 01:44:14 +0100 Subject: [PATCH 07/64] transparent tags --- R/Param.R | 31 ++++++++++++++++++++++++------- R/ParamSet.R | 33 +++++++++++++++++++++------------ R/ParamSetCollection.R | 27 ++++++++++++++++++++++----- R/ps.R | 12 +++--------- 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/R/Param.R b/R/Param.R index 65cdcf64..e9abafa2 100644 --- a/R/Param.R +++ b/R/Param.R @@ -18,10 +18,6 @@ #' @export Param = R6Class("Param", public = list( - #' @field id (`character(1)`)\cr - #' Identifier of the object. - id = NULL, - #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' @@ -34,7 +30,7 @@ Param = R6Class("Param", assert_list(special_vals) assert_character(tags, any.missing = FALSE, unique = TRUE) - self$id = id + private$.id = id private$.special_vals = special_vals private$.default = default private$.tags = tags @@ -100,16 +96,32 @@ Param = R6Class("Param", #' @description #' Creates a clone of this parameter with changed `$tags` slot. + #' If the tags do not change, no clone is created. #' @param tags (`character`) tags of the clone. #' @return [`Param`]. with_tags = function(tags) { - assert_character(tags, any.missing = FALSE, unique = TRUE) origtags = private$.tags + if (identical(tags, origtags)) return(self) + assert_character(tags, any.missing = FALSE, unique = TRUE) on.exit({private$.tags = origtags}) private$.tags = tags self$clone(deep = TRUE) }, + #' @description + #' Creates a clone of this parameter with changed `$id` slot. + #' If the `id` does not change, no clone is created. + #' @param id (`character(1)`) id of the clone. + #' @return [`Param`]. + with_id = function(id) { + if (identical(id, self$id)) return(self) + assert_id(id) + origid = private$.id + on.exit({private$.id = origid}) + private$.id = id + self$clone(deep = TRUE) + }, + #' @description #' Helper for print outputs. format = function() { @@ -155,6 +167,10 @@ Param = R6Class("Param", ), active = list( + #' @field id (`character(1)`)\cr + #' Identifier of the object. + id = function() private$.id, + #' @field special_vals (`list()`)\cr #' Arbitrary special values this parameter is allowed to take. special_vals = function() private$.special_vals, @@ -189,7 +205,8 @@ Param = R6Class("Param", .qunif = function(x) stop("abstract"), # should be implemented by subclasses, argcheck happens in Param$qunif .special_vals = NULL, .default = NULL, - .tags = NULL + .tags = NULL, + .id = NULL ) ) diff --git a/R/ParamSet.R b/R/ParamSet.R index 1851870c..5d0ab830 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -81,11 +81,13 @@ ParamSet = R6Class("ParamSet", assert_multi_class(p, c("Param", "ParamSet")) if (inherits(p, "Param")) { # level-up param to set pparams = structure(list(p), names = p$id) + ptags = structure(list(p$tags), names = p$id) ptrafo = NULL pvalues = NULL pdeps = NULL } else { pparams = p$params_unid + ptags = p$tags ptrafo = p$trafo pvalues = p$values pdeps = p$deps @@ -98,6 +100,7 @@ ParamSet = R6Class("ParamSet", } private$.params_unid = c(private$.params_unid, pparams) private$.values = c(private$.values, pvalues) + if (!is.null(private$.tags)) = private$.tags = c(private$.tags, ptags) private$.deps = rbind(private$.deps, pdeps) invisible(self) }, @@ -133,7 +136,7 @@ ParamSet = R6Class("ParamSet", } if (!is.null(tags)) { - ii = ii & map_lgl(params, function(p) all(tags %in% p$tags)) + ii = ii & map_lgl(self$tags, function(tg) all(tags %in% tg)) } ids[ii] @@ -167,7 +170,7 @@ ParamSet = R6Class("ParamSet", } if(check_required) { - required = setdiff(names(keep(params, function(p) "required" %in% p$tags)), ns) + required = setdiff(names(keep(self$tags, function(tg) "required" %in% tg)), ns) if (length(required) > 0L) { stop(sprintf("Missing required parameters: %s", str_collapse(required))) } @@ -194,6 +197,7 @@ ParamSet = R6Class("ParamSet", } private$.params_unid = private$.params_unid[ids] private$.params = private$.params[intersect(ids, names(private$.params))] + if (!is.null(private$.tags)) private$.tags = private$.tags[intersect(ids, names(private$.tags))] # restrict to ids already in pvals ids2 = union(intersect(ids, names(private$.values)), setdiff(names(private$.values), param_ids)) private$.values = private$.values[ids2] @@ -420,12 +424,7 @@ ParamSet = R6Class("ParamSet", if (length(uncloned)) { private$.params = c(private$.params, sapply(uncloned, function(u) { - p = punid[[u]] - if (p$id != u) { - p = p$clone(deep = TRUE) - p$id = u - } - p + punid[[u]]$with_id(u) }, simplify = FALSE) )[truenames] } @@ -534,8 +533,16 @@ ParamSet = R6Class("ParamSet", #' @field tags (named `list()` of `character()`)\cr #' Can be used to group and subset parameters. #' Named with parameter IDs. - tags = function() { - private$get_member_with_idnames("tags", as.list) + tags = function(v) { + if (is.null(private$.tags)) { + private$.tags = private$get_member_with_idnames("tags", as.list) + } + if (!missing(v)) { + assert_list(v, any.missing = FALSE, types = "character") + assert_names(names(v), identical.to = names(self$params_unid)) + private$.tags = v + } + private$.tags }, #' @field storage_type (`character()`)\cr @@ -635,6 +642,7 @@ ParamSet = R6Class("ParamSet", .params = named_list(), .params_unid = NULL, .values = named_list(), + .tags = NULL, .deps = data.table(id = character(0L), on = character(0L), cond = list()), # return a slot / AB, as a named vec, named with id (and can enforce a certain vec-type) get_member_with_idnames = function(member, astype) { @@ -672,6 +680,7 @@ ParamSet = R6Class("ParamSet", pars$add_dep(idname, on, cond) } }) + pars$tags = self$tags[parsnames] pars }, @@ -703,8 +712,8 @@ ParamSet = R6Class("ParamSet", #' @export as.data.table.ParamSet = function(x, ...) { # nolint punid = x$params_unid - id = NULL - map_dtr(punid, as.data.table)[, id := names(punid)][] + tg = x$tags + map_dtr(punid, as.data.table)[, `:=`(id = names(punid), tags = tg)][] } #' @export diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 1cd48233..1cbc51af 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -36,6 +36,8 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' This can be used to create a `ParamSetCollection` with changed [`ParamSet`] `set_id`s #' without having to clone said [`ParamSet`]s. However, when underlying [`ParamSet`]s' #' `$set_id`s change, then this change is no longer reflected in the ParamSetCollection. + #' Also note that the `$values` will have undefined behavior when `sets` contains a + #' single [`ParamSet`] multiple times (by reference). #' Default `FALSE`. initialize = function(sets, ignore_ids = FALSE) { @@ -127,11 +129,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, if (any(changed) || length(punid) != length(private$.params_cloned)) { # if there was a strict subset operation we wouldn't notice it otherwise changed_names = truenames[changed] private$.params = c(private$.params[setdiff(names(private$.params), changed_names)], # don't regenerate Params that were not changed. - sapply(changed_names, function(u) { - p = punid[[u]]$clone(deep = TRUE) - p$id = u - p - }, simplify = FALSE) + sapply(changed_names, function(u) punid[[u]]$with_id(u), simplify = FALSE) )[truenames] } private$.params_cloned = punid @@ -172,6 +170,25 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, rbindlist(c(d_all, list(private$.deps)), use.names = TRUE) }, + #' @field tags (named `list()` of `character()`)\cr + #' Can be used to group and subset parameters. + #' Named with parameter IDs. + tags = function(v) { + if (is.null(private$.tags)) { + sets = private$.sets + if (is.null(names(sets))) { + names(sets) = map(private$.sets, "set_id") + } + private$.tags = unlist(map(sets, "tags"), recursive = FALSE) + } + if (!missing(v)) { + assert_list(v, any.missing = FALSE, types = "character") + assert_names(names(v), identical.to = names(self$params_unid)) + private$.tags = v + } + private$.tags + }, + #' @template field_values values = function(xs) { sets = private$.sets diff --git a/R/ps.R b/R/ps.R index 710e7f33..5020580c 100644 --- a/R/ps.R +++ b/R/ps.R @@ -63,18 +63,12 @@ ps = function(..., .extra_trafo = NULL, .allow_dangling_dependencies = FALSE) { assert_list(args, names = "unique", types = c("Param", "Domain")) assert_function(.extra_trafo, null.ok = TRUE) - # generate Params (with correct id) from Domain objects + # Extract Param from Domain objects params = imap(args, function(p, name) { - if (inherits(p, "Param")) { - p = p$clone(deep = TRUE) - } else { - p = p$param$clone(deep = TRUE) - } - p$id = name - p + if (inherits(p, "Param")) p else p$param }) - paramset = ParamSet$new(params) + paramset = ParamSet$new(params, ignore_ids = TRUE) # add Dependencies imap(args, function(p, name) { From fa26af8e21eb142161d4905ba7aa811d551e2ba0 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 02:45:05 +0100 Subject: [PATCH 08/64] dynamic tags shadow --- R/ParamSet.R | 3 +- R/ParamSetCollection.R | 15 ++++++- R/ps.R | 2 +- R/to_tune.R | 3 +- man/Param.Rd | 34 ++++++++++++---- man/ParamDbl.Rd | 1 + man/ParamFct.Rd | 1 + man/ParamInt.Rd | 1 + man/ParamLgl.Rd | 1 + man/ParamSetCollection.Rd | 6 +++ man/ParamUty.Rd | 1 + tests/testthat/helper_03_domain.R | 27 ++++++++++++ tests/testthat/test_ParamSetCollection.R | 16 +++++--- tests/testthat/test_domain.R | 52 ++++++++++++------------ tests/testthat/test_sampler.R | 2 + tests/testthat/test_to_tune.R | 6 +-- 16 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 tests/testthat/helper_03_domain.R diff --git a/R/ParamSet.R b/R/ParamSet.R index 5d0ab830..5a5a715b 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -100,7 +100,7 @@ ParamSet = R6Class("ParamSet", } private$.params_unid = c(private$.params_unid, pparams) private$.values = c(private$.values, pvalues) - if (!is.null(private$.tags)) = private$.tags = c(private$.tags, ptags) + if (!is.null(private$.tags)) private$.tags = c(private$.tags, ptags) private$.deps = rbind(private$.deps, pdeps) invisible(self) }, @@ -680,7 +680,6 @@ ParamSet = R6Class("ParamSet", pars$add_dep(idname, on, cond) } }) - pars$tags = self$tags[parsnames] pars }, diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 1cbc51af..bc62b280 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -88,6 +88,11 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # ignoring the other ParamSet's set_id in favor of names(private$.sets), so add the name here as well. names(set_addition) = p$set_id } + if (!is.null(private$.tags)) { + tagsaddition = p$tags + names(tagsaddition) = sprintf("%s.%s", p$set_id, names(tagsaddition)) + private$.tags = c(private$.tags, tagsaddition) + } private$.sets = c(private$.sets, set_addition) invisible(self) }, @@ -99,6 +104,14 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, remove_sets = function(ids) { setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") assert_subset(ids, setnames) + + if (!is.null(private$.tags)) { + droptagnames = pmap(list(setnames[setnames %in% ids], private$.sets[setnames %in% ids]), function(n, s) { + sprintf("%s.%s", n, names(s$tags)) + }) + private$.tags = private$.tags[!names(private$.tags) %in% droptagnames] + } + private$.sets[setnames %in% ids] = NULL invisible(self) }, @@ -179,7 +192,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, if (is.null(names(sets))) { names(sets) = map(private$.sets, "set_id") } - private$.tags = unlist(map(sets, "tags"), recursive = FALSE) + private$.tags = unlist(map(sets, "tags"), recursive = FALSE) %??% named_list() } if (!missing(v)) { assert_list(v, any.missing = FALSE, types = "character") diff --git a/R/ps.R b/R/ps.R index 5020580c..73a64e90 100644 --- a/R/ps.R +++ b/R/ps.R @@ -68,7 +68,7 @@ ps = function(..., .extra_trafo = NULL, .allow_dangling_dependencies = FALSE) { if (inherits(p, "Param")) p else p$param }) - paramset = ParamSet$new(params, ignore_ids = TRUE) + paramset = ParamSet$new(params, ignore_ids = length(params) > 0) # if length is 0 then no names are present # add Dependencies imap(args, function(p, name) { diff --git a/R/to_tune.R b/R/to_tune.R index 0c4d4cdf..2cd6f910 100644 --- a/R/to_tune.R +++ b/R/to_tune.R @@ -275,8 +275,7 @@ pslike_to_ps.Domain = function(pslike, call, param, id, usersupplied = TRUE) { } pslike_to_ps.Param = function(pslike, call, param, id, usersupplied = TRUE) { - pslike = pslike$clone(deep = TRUE) - pslike$id = id + pslike = pslike$with_id(id) pslike = ParamSet$new(list(pslike)) pslike_to_ps(pslike, call, param, id, usersupplied = FALSE) } diff --git a/man/Param.Rd b/man/Param.Rd index 7635f342..ec653e7e 100644 --- a/man/Param.Rd +++ b/man/Param.Rd @@ -25,17 +25,12 @@ Other Params: \code{\link{ParamUty}} } \concept{Params} -\section{Public fields}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} -} -\if{html}{\out{
}} -} \section{Active bindings}{ \if{html}{\out{
}} \describe{ +\item{\code{id}}{(\code{character(1)})\cr +Identifier of the object.} + \item{\code{special_vals}}{(\code{list()})\cr Arbitrary special values this parameter is allowed to take.} @@ -68,6 +63,7 @@ Is there a default value?} \item \href{#method-test}{\code{Param$test()}} \item \href{#method-rep}{\code{Param$rep()}} \item \href{#method-with_tags}{\code{Param$with_tags()}} +\item \href{#method-with_id}{\code{Param$with_id()}} \item \href{#method-format}{\code{Param$format()}} \item \href{#method-print}{\code{Param$print()}} \item \href{#method-qunif}{\code{Param$qunif()}} @@ -214,6 +210,7 @@ Each parameter is named "[id]\emph{rep}[k]" and gets the additional tag "[id]_re \if{latex}{\out{\hypertarget{method-with_tags}{}}} \subsection{Method \code{with_tags()}}{ Creates a clone of this parameter with changed \verb{$tags} slot. +If the tags do not change, no clone is created. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Param$with_tags(tags)}\if{html}{\out{
}} } @@ -230,6 +227,27 @@ Creates a clone of this parameter with changed \verb{$tags} slot. } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-with_id}{}}} +\subsection{Method \code{with_id()}}{ +Creates a clone of this parameter with changed \verb{$id} slot. +If the \code{id} does not change, no clone is created. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{Param$with_id(id)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{id}}{(\code{character(1)}) id of the clone.} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +\code{\link{Param}}. +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-format}{}}} \subsection{Method \code{format()}}{ diff --git a/man/ParamDbl.Rd b/man/ParamDbl.Rd index 01b1a858..68cb0e5a 100644 --- a/man/ParamDbl.Rd +++ b/man/ParamDbl.Rd @@ -85,6 +85,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} diff --git a/man/ParamFct.Rd b/man/ParamFct.Rd index 9cdaa938..e54aaf17 100644 --- a/man/ParamFct.Rd +++ b/man/ParamFct.Rd @@ -76,6 +76,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} diff --git a/man/ParamInt.Rd b/man/ParamInt.Rd index 9bbe9fcd..c1129f8c 100644 --- a/man/ParamInt.Rd +++ b/man/ParamInt.Rd @@ -81,6 +81,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} diff --git a/man/ParamLgl.Rd b/man/ParamLgl.Rd index 8904a0e1..d122f5dc 100644 --- a/man/ParamLgl.Rd +++ b/man/ParamLgl.Rd @@ -76,6 +76,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 0221c505..555f303d 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -51,6 +51,10 @@ Lists all (direct) dependency parents of a param, through parameter IDs. Internally created by a call to \code{add_dep}. Settable, if you want to remove dependencies or perform other changes.} +\item{\code{tags}}{(named \code{list()} of \code{character()})\cr +Can be used to group and subset parameters. +Named with parameter IDs.} + \item{\code{values}}{(named \code{list()})\cr Currently set / fixed parameter values. Settable, and feasibility of values will be checked when you set them. @@ -108,6 +112,8 @@ When this is \code{TRUE}, then \code{sets} should be named when id prefixes are This can be used to create a \code{ParamSetCollection} with changed \code{\link{ParamSet}} \code{set_id}s without having to clone said \code{\link{ParamSet}}s. However, when underlying \code{\link{ParamSet}}s' \verb{$set_id}s change, then this change is no longer reflected in the ParamSetCollection. +Also note that the \verb{$values} will have undefined behavior when \code{sets} contains a +single \code{\link{ParamSet}} multiple times (by reference). Default \code{FALSE}.} } \if{html}{\out{
}} diff --git a/man/ParamUty.Rd b/man/ParamUty.Rd index 68886644..42f8c9cd 100644 --- a/man/ParamUty.Rd +++ b/man/ParamUty.Rd @@ -79,6 +79,7 @@ Always \code{"list"} for \link{ParamUty}.} \item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} +\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} \item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} } \out{} diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R new file mode 100644 index 00000000..26c46389 --- /dev/null +++ b/tests/testthat/helper_03_domain.R @@ -0,0 +1,27 @@ + +# compare ParamSets, but ignore Param ID + +expect_equal_ps = function(a, b) { + assert_class(a, "ParamSet") + assert_class(b, "ParamSet") + a = a$clone(deep = TRUE) + a$params + a$tags + b = b$clone(deep = TRUE) + b$params + b$tags + a = as.list(a) + b = as.list(b) + a$.__enclos_env__$private$.params_unid = lapply(a$.__enclos_env__$private$.params_unid, function(x) { + x = x$clone(deep = TRUE) + x$.__enclos_env__$private$.id = "" + x + }) + b$.__enclos_env__$private$.params_unid = lapply(b$.__enclos_env__$private$.params_unid, function(x) { + x = x$clone(deep = TRUE) + x$.__enclos_env__$private$.id = "" + x + }) + expect_equal(a, b) +} + diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index 7d241a79..c6548986 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -22,8 +22,7 @@ test_that("ParamSet basic stuff works", { expect_equal(psc$length, ps1$length + ps2$length + ps3$length) # check that param internally in collection is constructed correctly p = psc$params[[2L]] - p = p$clone() - p$id = "th_param_int" + p = p$with_id("th_param_int") expect_equal(p, ps2$params[[1L]]) expect_equal(psc$ids(), c(paste0("s1.", ps1$ids()), paste0("s2.", ps2$ids()), ps3$ids())) expect_equal(psc$lower, my_c(ps1$lower, ps2$lower, ps3$lower)) @@ -62,6 +61,10 @@ test_that("ParamSet basic stuff works", { ps2$params ps1clone$params ps2clone$params + ps1$tags + ps2$tags + ps1clone$tags + ps2clone$tags # ps1 and ps2 should not be changed expect_equal(ps1, ps1clone) @@ -175,6 +178,10 @@ test_that("values", { ps2$params ps1clone$params ps2clone$params + ps1$tags + ps2$tags + ps1clone$tags + ps2clone$tags expect_equal(ps1clone, ps1) expect_equal(ps2clone, ps2) @@ -301,14 +308,13 @@ test_that("cloning and changing underlying params works", { expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in psc1 expect_length(psc2$.__enclos_env__$private$.params, 0) - pe = th_paramset_dbl1()$clone(deep = TRUE)$params[[1]] - pe$id = "s1.th_param_dbl" + pe = th_paramset_dbl1()$params[[1]]$with_id("s1.th_param_dbl") expect_equal(psc2$params[1], list(s1.th_param_dbl = pe)) expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in psc1 expect_length(psc2$.__enclos_env__$private$.params, 6) # but psc2 has .params now - ps1$params[[1]]$id = "test" + ps1$params[[1]]$.__enclos_env__$private$.id = "test" expect_equal(psc2$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) expect_equal(psc2$params[1], list(s1.th_param_dbl = pe)) diff --git a/tests/testthat/test_domain.R b/tests/testthat/test_domain.R index 5303f142..60edb5a9 100644 --- a/tests/testthat/test_domain.R +++ b/tests/testthat/test_domain.R @@ -23,32 +23,32 @@ test_that("p_xxx printers", { test_that("ps(p_xxx(...)) creates ParamSets", { - expect_equal(ps(x = p_int()), ParamSet$new(list(ParamInt$new("x")))) - expect_equal(ps(x = p_dbl()), ParamSet$new(list(ParamDbl$new("x")))) - expect_equal(ps(x = p_uty()), ParamSet$new(list(ParamUty$new("x")))) - expect_equal(ps(x = p_lgl()), ParamSet$new(list(ParamLgl$new("x")))) - expect_equal(ps(x = p_fct(letters)), ParamSet$new(list(ParamFct$new("x", letters)))) + expect_equal_ps(ps(x = p_int()), ParamSet$new(list(ParamInt$new("x")))) + expect_equal_ps(ps(x = p_dbl()), ParamSet$new(list(ParamDbl$new("x")))) + expect_equal_ps(ps(x = p_uty()), ParamSet$new(list(ParamUty$new("x")))) + expect_equal_ps(ps(x = p_lgl()), ParamSet$new(list(ParamLgl$new("x")))) + expect_equal_ps(ps(x = p_fct(letters)), ParamSet$new(list(ParamFct$new("x", letters)))) - expect_equal(ps(x = p_int(upper = 1, lower = 0)), ParamSet$new(list(ParamInt$new("x", 0, 1)))) - expect_equal(ps(x = p_dbl(upper = 1, lower = 0)), ParamSet$new(list(ParamDbl$new("x", 0, 1)))) + expect_equal_ps(ps(x = p_int(upper = 1, lower = 0)), ParamSet$new(list(ParamInt$new("x", 0, 1)))) + expect_equal_ps(ps(x = p_dbl(upper = 1, lower = 0)), ParamSet$new(list(ParamDbl$new("x", 0, 1)))) - expect_equal(ps(x = p_int(special_vals = list("x"), default = 0, tags = "required")), + expect_equal_ps(ps(x = p_int(special_vals = list("x"), default = 0, tags = "required")), ParamSet$new(list(ParamInt$new("x", special_vals = list("x"), default = 0, tags = "required")))) - expect_equal(ps(x = p_dbl(special_vals = list("x"), default = 0, tags = "required")), + expect_equal_ps(ps(x = p_dbl(special_vals = list("x"), default = 0, tags = "required")), ParamSet$new(list(ParamDbl$new("x", special_vals = list("x"), default = 0, tags = "required")))) - expect_equal(ps(x = p_lgl(special_vals = list("x"), default = TRUE, tags = "required")), + expect_equal_ps(ps(x = p_lgl(special_vals = list("x"), default = TRUE, tags = "required")), ParamSet$new(list(ParamLgl$new("x", special_vals = list("x"), default = TRUE, tags = "required")))) - expect_equal(ps(x = p_fct(letters, special_vals = list(0), default = 0, tags = "required")), + expect_equal_ps(ps(x = p_fct(letters, special_vals = list(0), default = 0, tags = "required")), ParamSet$new(list(ParamFct$new("x", letters, special_vals = list(0), default = 0, tags = "required")))) - expect_equal(ps(x = p_uty(default = 1, tags = "required", custom_check = check_int)), + expect_equal_ps(ps(x = p_uty(default = 1, tags = "required", custom_check = check_int)), ParamSet$new(list(ParamUty$new("x", default = 1, tags = "required", custom_check = check_int)))) expect_error(ps(x = p_int(), x = p_int()), "unique names") - expect_equal(ps(x = p_uty(default = 1, tags = "required", custom_check = check_int)), + expect_equal_ps(ps(x = p_uty(default = 1, tags = "required", custom_check = check_int)), ps(x = ParamUty$new("y", default = 1, tags = "required", custom_check = check_int))) expect_error(p_int(id = 1), "unused argument.*id") @@ -93,14 +93,14 @@ test_that("requirements in domains", { simpleps = ParamSet$new(list(ParamInt$new("x"), ParamDbl$new("y")))$add_dep("y", "x", CondEqual$new(1)) # basic equality expression - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_dbl(depends = x == 1) ), simpleps) # quote() is accepted - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_dbl(depends = quote(x == 1)) @@ -108,7 +108,7 @@ test_that("requirements in domains", { # using a expression variable reqquote = quote(x == 1) - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_dbl(depends = reqquote) @@ -116,13 +116,13 @@ test_that("requirements in domains", { # the same for `p_fct`, which behaves slightly differently from the rest simpleps = ParamSet$new(list(ParamInt$new("x"), ParamFct$new("y", letters)))$add_dep("y", "x", CondEqual$new(1)) - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_fct(letters, depends = x == 1) ), simpleps) reqquote = quote(x == 1) - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_fct(letters, depends = reqquote) @@ -130,20 +130,20 @@ test_that("requirements in domains", { # the same for `p_fct` involving autotrafo, which behaves slightly differently from the rest simpleps = ps(x = p_int(), y = p_fct(list(a = 1, b = 2)))$add_dep("y", "x", CondEqual$new(1)) - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_fct(list(a = 1, b = 2), depends = x == 1) ), simpleps) reqquote = quote(x == 1) - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_fct(list(a = 1, b = 2), depends = reqquote) ), simpleps) # `&&` - expect_equal( + expect_equal_ps( ps( x = p_int(), y = p_dbl(depends = x == 1 && x == 3) @@ -151,7 +151,7 @@ test_that("requirements in domains", { ParamSet$new(list(ParamInt$new("x"), ParamDbl$new("y")))$add_dep("y", "x", CondEqual$new(1))$add_dep("y", "x", CondEqual$new(3))) # `&&`, `%in%` - expect_equal( + expect_equal_ps( ps( x = p_int(), z = p_fct(letters[1:3]), @@ -161,7 +161,7 @@ test_that("requirements in domains", { add_dep("y", "x", CondEqual$new(1))$add_dep("y", "z", CondAnyOf$new(c("a", "b")))) # recursive dependencies - expect_equal( + expect_equal_ps( ps( x = p_int(), z = p_fct(letters[1:3], depends = x == 2), @@ -173,7 +173,7 @@ test_that("requirements in domains", { # `fct` with complex requirements complexps = ParamSet$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamFct$new("y", letters[1:3])))$ add_dep("y", "x", CondEqual$new(1))$add_dep("y", "z", CondAnyOf$new(c("a", "b"))) - expect_equal( + expect_equal_ps( ps( x = p_int(), z = p_fct(letters[1:3]), @@ -181,7 +181,7 @@ test_that("requirements in domains", { ), complexps) # parentheses are ignored - expect_equal( + expect_equal_ps( ps( x = p_int(), z = p_fct(letters[1:3]), @@ -189,7 +189,7 @@ test_that("requirements in domains", { ), complexps) # multiple dependencies on the same value - expect_equal( + expect_equal_ps( ps( x = p_int(), z = p_fct(letters[1:3]), diff --git a/tests/testthat/test_sampler.R b/tests/testthat/test_sampler.R index 876ca01d..1cc102f6 100644 --- a/tests/testthat/test_sampler.R +++ b/tests/testthat/test_sampler.R @@ -112,7 +112,9 @@ test_that("we had a bug where creating the joint sampler changed the ps-ref of t s2_expected = ParamSet$new(list(th_param_dbl())) s2_expected$params + s2_expected$tags s2$param_set$params + s2$param_set$tags expect_equal(s2$param_set, s2_expected) }) diff --git a/tests/testthat/test_to_tune.R b/tests/testthat/test_to_tune.R index 23ee38b8..34422f78 100644 --- a/tests/testthat/test_to_tune.R +++ b/tests/testthat/test_to_tune.R @@ -103,7 +103,7 @@ test_that("Tune ParamSet is created", { setindex(pars_tune$deps, NULL) pars_tune$values = list() - expect_equal(pars, pars_tune) + expect_equal_ps(pars, pars_tune) pars_unbound = ParamSet$new(list( ParamDbl$new("x"), @@ -115,7 +115,7 @@ test_that("Tune ParamSet is created", { pars_tune = pars_unbound$search_space(list(x = to_tune(p_int(0, 10)), y = to_tune(p_dbl(0, 10, special_vals = list("x"))), fct = to_tune(c("x", "y")), lgl = to_tune(p_lgl()))) pars_tune$values = list() setindex(pars_tune$deps, NULL) - expect_equal(pars, pars_tune) + expect_equal_ps(pars, pars_tune) pars_unbound_2 = ParamSet$new(list( ParamInt$new("x"), @@ -127,7 +127,7 @@ test_that("Tune ParamSet is created", { pars_tune = pars_unbound_2$search_space(list(x = to_tune(0, 10), y = to_tune(p_dbl(0, 10, special_vals = list("x"))), fct = to_tune(c("x", "y")), lgl = to_tune(p_lgl()))) pars_tune$values = list() setindex(pars_tune$deps, NULL) - expect_equal(pars, pars_tune) + expect_equal_ps(pars, pars_tune) pars_tune = pars_unbound$search_space(list(x = to_tune(ParamInt$new("z", 0, 10)), y = to_tune(ps(z = p_dbl(0, 10, special_vals = list("x")))), fct = to_tune(c("x", "y")), lgl = to_tune(p_lgl()))) From 922d2c3848cce6ddf4f4093e348d3a06c1030293 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 02:47:50 +0100 Subject: [PATCH 09/64] avoid some instantiation of tags --- R/ParamSet.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index 5a5a715b..ea4ae831 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -81,13 +81,13 @@ ParamSet = R6Class("ParamSet", assert_multi_class(p, c("Param", "ParamSet")) if (inherits(p, "Param")) { # level-up param to set pparams = structure(list(p), names = p$id) - ptags = structure(list(p$tags), names = p$id) + if (!is.null(private$.tags)) ptags = structure(list(p$tags), names = p$id) ptrafo = NULL pvalues = NULL pdeps = NULL } else { pparams = p$params_unid - ptags = p$tags + if (!is.null(private$.tags)) ptags = p$tags ptrafo = p$trafo pvalues = p$values pdeps = p$deps From 9f13b8b3e8b1062c429865a4323094e47caa4326 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 02:56:48 +0100 Subject: [PATCH 10/64] renaming param to prevent misuse --- R/Param.R | 8 ++++---- R/ParamSet.R | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/Param.R b/R/Param.R index e9abafa2..c0446fa4 100644 --- a/R/Param.R +++ b/R/Param.R @@ -89,7 +89,7 @@ Param = R6Class("Param", assert_count(n) pid = self$id join_id = paste0(pid, "_rep") - taggedself = self$with_tags(c(self$tags, join_id)) + taggedself = self$with_tags(c(self$param_tags, join_id)) repeatedself = structure(rep(list(taggedself), n), names = sprintf("%s_%s", join_id, seq_len(n))) ParamSet$new(repeatedself, ignore_ids = TRUE) }, @@ -179,9 +179,9 @@ Param = R6Class("Param", #' Default value. default = function() private$.default, - #' @field tags (`character()`)\cr + #' @field param_tags (`character()`)\cr #' Arbitrary tags to group and subset parameters. - tags = function() private$.tags, + param_tags = function() private$.tags, #' @field class (`character(1)`)\cr #' R6 class name. Read-only. @@ -223,6 +223,6 @@ as.data.table.Param = function(x, ...) { # nolint special_vals = list(x$special_vals), default = list(x$default), storage_type = x$storage_type, - tags = list(x$tags) + tags = list(x$param_tags) ) } diff --git a/R/ParamSet.R b/R/ParamSet.R index ea4ae831..4493e54b 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -81,7 +81,7 @@ ParamSet = R6Class("ParamSet", assert_multi_class(p, c("Param", "ParamSet")) if (inherits(p, "Param")) { # level-up param to set pparams = structure(list(p), names = p$id) - if (!is.null(private$.tags)) ptags = structure(list(p$tags), names = p$id) + if (!is.null(private$.tags)) ptags = structure(list(p$param_tags), names = p$id) ptrafo = NULL pvalues = NULL pdeps = NULL @@ -535,7 +535,7 @@ ParamSet = R6Class("ParamSet", #' Named with parameter IDs. tags = function(v) { if (is.null(private$.tags)) { - private$.tags = private$get_member_with_idnames("tags", as.list) + private$.tags = private$get_member_with_idnames("param_tags", as.list) } if (!missing(v)) { assert_list(v, any.missing = FALSE, types = "character") From 21baf90e5774ce8008e8f571ab15a857976c2da8 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 11:18:27 +0100 Subject: [PATCH 11/64] trying to make R 4 tests work --- tests/testthat/helper_03_domain.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R index 26c46389..d29785ac 100644 --- a/tests/testthat/helper_03_domain.R +++ b/tests/testthat/helper_03_domain.R @@ -10,8 +10,6 @@ expect_equal_ps = function(a, b) { b = b$clone(deep = TRUE) b$params b$tags - a = as.list(a) - b = as.list(b) a$.__enclos_env__$private$.params_unid = lapply(a$.__enclos_env__$private$.params_unid, function(x) { x = x$clone(deep = TRUE) x$.__enclos_env__$private$.id = "" @@ -22,6 +20,9 @@ expect_equal_ps = function(a, b) { x$.__enclos_env__$private$.id = "" x }) + a = as.list(a) + b = as.list(b) + expect_equal(a, b) } From 7cf9ac9a41db4b66b05d23d37394048fa5b1aa4b Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 11:46:12 +0100 Subject: [PATCH 12/64] testing shadow tags --- R/ParamSetCollection.R | 32 +++++++--------- tests/testthat/test_ParamSetCollection.R | 49 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index bc62b280..6fbd94d5 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -49,10 +49,15 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, nameless_sets = split_sets$`TRUE` named_sets = split_sets$`FALSE` assert_names(names2(named_sets), type = "strict") + + private$.tags = unlist(map(sets, "tags"), recursive = FALSE) %??% named_list() + if (!ignore_ids) sets = unname(sets) # when private$.sets is unnamed, then the set_ids are used. private$.sets = sets self$set_id = "" pnames = names(self$params_unid) + + dups = duplicated(pnames) if (any(dups)) { stopf("ParamSetCollection would contain duplicated parameter names: %s", @@ -88,11 +93,11 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # ignoring the other ParamSet's set_id in favor of names(private$.sets), so add the name here as well. names(set_addition) = p$set_id } - if (!is.null(private$.tags)) { - tagsaddition = p$tags - names(tagsaddition) = sprintf("%s.%s", p$set_id, names(tagsaddition)) - private$.tags = c(private$.tags, tagsaddition) - } + + tagsaddition = p$tags + names(tagsaddition) = sprintf("%s.%s", p$set_id, names(tagsaddition)) + private$.tags = c(private$.tags, tagsaddition) + private$.sets = c(private$.sets, set_addition) invisible(self) }, @@ -105,12 +110,10 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") assert_subset(ids, setnames) - if (!is.null(private$.tags)) { - droptagnames = pmap(list(setnames[setnames %in% ids], private$.sets[setnames %in% ids]), function(n, s) { - sprintf("%s.%s", n, names(s$tags)) - }) - private$.tags = private$.tags[!names(private$.tags) %in% droptagnames] - } + droptagnames = pmap(list(setnames[setnames %in% ids], private$.sets[setnames %in% ids]), function(n, s) { + sprintf("%s.%s", n, names(s$tags)) + }) + private$.tags = private$.tags[!names(private$.tags) %in% droptagnames] private$.sets[setnames %in% ids] = NULL invisible(self) @@ -187,13 +190,6 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Can be used to group and subset parameters. #' Named with parameter IDs. tags = function(v) { - if (is.null(private$.tags)) { - sets = private$.sets - if (is.null(names(sets))) { - names(sets) = map(private$.sets, "set_id") - } - private$.tags = unlist(map(sets, "tags"), recursive = FALSE) %??% named_list() - } if (!missing(v)) { assert_list(v, any.missing = FALSE, types = "character") assert_names(names(v), identical.to = names(self$params_unid)) diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index c6548986..f46bdcc5 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -126,6 +126,11 @@ test_that("deps", { expect_true(psc$check(list(ps1.f = "a", ps1.d = 0, ps2.d = 0))) expect_string(psc$check(list(ps2.d = 0))) + ps1clone$tags + ps2clone$tags + ps1$tags + ps2$tags + # ps1 and ps2 should not be changed expect_equal(ps1clone, ps1) expect_equal(ps2clone, ps2) @@ -335,3 +340,47 @@ test_that("cloning and changing underlying params works", { expect_equal(psc2_clone$params[6], ps3$params[1]) }) + +test_that("tags shadowing works", { + ps1 = ParamSet$new(list(ParamInt$new("x", tags = c("a", "b")), ParamInt$new("y", tags = c("c")))) + ps1$set_id = "s1" + + expect_equal(ps1$tags, list(x = c("a", "b"), y = "c")) + expect_error({ps1$tags$x = 2}, "May only contain.*character.*numeric") + expect_error({ps1$tags$x = NULL}, "Must be.*identical to.*x,y") + expect_error({ps1$tags = list(y = "c", x = "a")}, "Must be.*identical to.*x,y") + + ps1$tags$x = "z" + + expect_equal(ps1$params$x$param_tags, c("a", "b")) + + ps2 = th_paramset_full() + ps2$set_id = "s2" + + ps2$tags$th_param_int = "xx" + + expect_equal(ps2$params$th_param_int$param_tags, character(0)) + + ps3 = th_paramset_dbl1() + ps3$set_id = "" + psc1 = ParamSetCollection$new(list(ps1, ps2)) + psc2 = ParamSetCollection$new(list(psc1, ps3)) + + expect_equal(psc2$tags, list(s1.x = "z", s1.y = "c", s2.th_param_int = "xx", + s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0), th_param_dbl = character(0))) + + pscc = psc1$clone(deep = TRUE) + psc1$tags$s1.y = "d" + + + expect_equal(psc2$tags, list(s1.x = "z", s1.y = "c", s2.th_param_int = "xx", + s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0), th_param_dbl = character(0))) + expect_equal(psc1$tags, list(s1.x = "z", s1.y = "d", s2.th_param_int = "xx", + s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0))) + expect_equal(pscc$tags, list(s1.x = "z", s1.y = "c", s2.th_param_int = "xx", + s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0))) + + expect_equal(ps2$params$th_param_int$param_tags, character(0)) + expect_equal(ps1$params$x$param_tags, c("a", "b")) + +}) From 7ee166e769f2b578c3f83dbc0e28bfd337853b29 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 23 Mar 2021 12:54:19 +0100 Subject: [PATCH 13/64] tag shadows work with adds / removes --- man-roxygen/field_tags.R | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 man-roxygen/field_tags.R diff --git a/man-roxygen/field_tags.R b/man-roxygen/field_tags.R new file mode 100644 index 00000000..6072a2c4 --- /dev/null +++ b/man-roxygen/field_tags.R @@ -0,0 +1,4 @@ +#' @field tags (named `list()` of `character()`)\cr +#' Can be used to group and subset parameters. +#' Named with parameter IDs. + From a1f97fb63d61b67b2e90bdec6e6a0de087af2f00 Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 25 Mar 2021 01:50:06 +0100 Subject: [PATCH 14/64] Towards S3 Params tuning sets and ParamSetCollection still missing --- .gitignore | 7 + DESCRIPTION | 1 + NAMESPACE | 71 ++- R/Design.R | 6 +- R/NoDefault.R | 17 +- R/Param.R | 293 ++++------- R/ParamDbl.R | 133 ++--- R/ParamFct.R | 102 ++-- R/ParamInt.R | 132 ++--- R/ParamLgl.R | 70 ++- R/ParamS6.R | 157 ++++++ R/ParamSet.R | 502 ++++++++++--------- R/ParamSetCollection.R | 116 +---- R/ParamUty.R | 84 +--- R/Sampler1D.R | 26 +- R/SamplerJointIndep.R | 3 +- R/SamplerUnif.R | 2 +- R/asserts.R | 32 +- R/domain.R | 149 ++---- R/generate_design_grid.R | 2 +- R/generate_design_lhs.R | 4 +- R/helper.R | 22 + R/ps.R | 49 +- attic/ParamSet_add.R | 73 +++ man/Domain.Rd | 53 +- man/NO_DEF.Rd | 5 +- man/Param.Rd | 345 ------------- man/ParamDbl.Rd | 188 ------- man/ParamFct.Rd | 151 ------ man/ParamInt.Rd | 178 ------- man/ParamLgl.Rd | 142 ------ man/ParamSet.Rd | 134 +++-- man/ParamSetCollection.Rd | 73 +-- man/ParamUty.Rd | 145 ------ man/{assert_param.Rd => assert_param_set.Rd} | 11 +- man/domain_check.Rd | 20 + man/ps.Rd | 2 +- man/to_tune.Rd | 2 +- tests/testthat/helper_compat.R | 37 ++ 39 files changed, 1207 insertions(+), 2332 deletions(-) create mode 100644 R/ParamS6.R create mode 100644 attic/ParamSet_add.R delete mode 100644 man/Param.Rd delete mode 100644 man/ParamDbl.Rd delete mode 100644 man/ParamFct.Rd delete mode 100644 man/ParamInt.Rd delete mode 100644 man/ParamLgl.Rd delete mode 100644 man/ParamUty.Rd rename man/{assert_param.Rd => assert_param_set.Rd} (80%) create mode 100644 man/domain_check.Rd diff --git a/.gitignore b/.gitignore index 2a84d970..0c7c4848 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,10 @@ $RECYCLE.BIN/ !.vscode/extensions.json *.code-workspace .vscode + +# Autosaves and Temp files +.#* +*~ +\#*# +*.swp + diff --git a/DESCRIPTION b/DESCRIPTION index bea05e9a..61a13e17 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -67,6 +67,7 @@ Collate: 'ParamFct.R' 'ParamInt.R' 'ParamLgl.R' + 'ParamS6.R' 'ParamSet.R' 'ParamSetCollection.R' 'ParamUty.R' diff --git a/NAMESPACE b/NAMESPACE index 6153cbcd..bed456bf 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,57 @@ # Generated by roxygen2: do not edit by hand -S3method(as.data.table,Param) S3method(as.data.table,ParamSet) +S3method(domain_check,ParamDbl) +S3method(domain_check,ParamFct) +S3method(domain_check,ParamInt) +S3method(domain_check,ParamLgl) +S3method(domain_check,ParamSet6) +S3method(domain_check,ParamUty) +S3method(domain_is_bounded,ParamDbl) +S3method(domain_is_bounded,ParamFct) +S3method(domain_is_bounded,ParamInt) +S3method(domain_is_bounded,ParamLgl) +S3method(domain_is_bounded,ParamSet6) +S3method(domain_is_bounded,ParamUty) +S3method(domain_is_bounded,default) +S3method(domain_is_categ,ParamDbl) +S3method(domain_is_categ,ParamFct) +S3method(domain_is_categ,ParamInt) +S3method(domain_is_categ,ParamLgl) +S3method(domain_is_categ,ParamSet6) +S3method(domain_is_categ,ParamUty) +S3method(domain_is_categ,default) +S3method(domain_is_number,ParamDbl) +S3method(domain_is_number,ParamFct) +S3method(domain_is_number,ParamInt) +S3method(domain_is_number,ParamLgl) +S3method(domain_is_number,ParamSet6) +S3method(domain_is_number,ParamUty) +S3method(domain_is_number,default) +S3method(domain_nlevels,ParamDbl) +S3method(domain_nlevels,ParamFct) +S3method(domain_nlevels,ParamInt) +S3method(domain_nlevels,ParamLgl) +S3method(domain_nlevels,ParamSet6) +S3method(domain_nlevels,ParamUty) +S3method(domain_nlevels,default) +S3method(domain_qunif,ParamDbl) +S3method(domain_qunif,ParamFct) +S3method(domain_qunif,ParamInt) +S3method(domain_qunif,ParamLgl) +S3method(domain_qunif,ParamSet6) +S3method(domain_qunif,ParamUty) +S3method(domain_qunif,default) +S3method(domain_sanitize,ParamDbl) +S3method(domain_sanitize,ParamInt) +S3method(domain_sanitize,default) +S3method(domain_storage_type,ParamDbl) +S3method(domain_storage_type,ParamFct) +S3method(domain_storage_type,ParamInt) +S3method(domain_storage_type,ParamLgl) +S3method(domain_storage_type,ParamSet6) +S3method(domain_storage_type,ParamUty) +S3method(domain_storage_type,default) S3method(print,Domain) S3method(print,FullTuneToken) S3method(print,ObjectTuneToken) @@ -12,15 +62,8 @@ export(CondEqual) export(Condition) export(Design) export(NO_DEF) -export(NoDefault) -export(Param) -export(ParamDbl) -export(ParamFct) -export(ParamInt) -export(ParamLgl) export(ParamSet) export(ParamSetCollection) -export(ParamUty) export(Sampler) export(Sampler1D) export(Sampler1DCateg) @@ -31,8 +74,17 @@ export(SamplerHierarchical) export(SamplerJointIndep) export(SamplerUnif) export(as.data.table) -export(assert_param) export(assert_param_set) +export(domain_assert) +export(domain_check) +export(domain_is_bounded) +export(domain_is_categ) +export(domain_is_number) +export(domain_nlevels) +export(domain_qunif) +export(domain_sanitize) +export(domain_storage_type) +export(domain_test) export(generate_design_grid) export(generate_design_lhs) export(generate_design_random) @@ -40,6 +92,7 @@ export(p_dbl) export(p_fct) export(p_int) export(p_lgl) +export(p_s6) export(p_uty) export(ps) export(to_tune) diff --git a/R/Design.R b/R/Design.R index 8070d6cf..e92b8ff6 100644 --- a/R/Design.R +++ b/R/Design.R @@ -81,7 +81,7 @@ Design = R6Class("Design", xs = map(xs, function(x) Filter(Negate(is_scalar_na), x)) } if (ps$has_trafo && trafo) { - xs = map(xs, function(x) ps$trafo(x, ps)) + xs = map(xs, function(x) ps$trafo(x)) } return(xs) } @@ -102,14 +102,14 @@ Design = R6Class("Design", graph = graph[, list("parents" = list(unlist(get("parents")))), by = "id"] topo = topo_sort(graph) pids_sorted = topo$id + storage_types = ps$storage_type for (param_id in pids_sorted) { - param = ps$params[[param_id]] dd = ps$deps[get("id") == param_id, ] for (j in seq_row(dd)) { pcol = self$data[[dd$on[j]]] # we are ok if parent was active and cond on parent is OK not_ok = which(is.na(pcol) | !dd$cond[[j]]$test(pcol)) - set(self$data, not_ok, j = param_id, value = as_type(NA, param$storage_type)) + set(self$data, not_ok, j = param_id, value = as_type(NA, storage_types[[param_id]])) } } } diff --git a/R/NoDefault.R b/R/NoDefault.R index 44b2e57c..39215692 100644 --- a/R/NoDefault.R +++ b/R/NoDefault.R @@ -4,22 +4,13 @@ #' Special new data type for no-default. #' Not often needed by the end-user, mainly internal. #' -#' * `NoDefault`: R6 factory. -#' * `NO_DEF`: R6 Singleton object for type, used in [Param]. -#' * `is_nodefault()`: Is an object of type 'no default'? +#' * `NO_DEF`: Singleton object for type, used in [Param]. +#' * `is_nodefault()`: Is an object the 'no default' object? #' #' @name NO_DEF #' @aliases NoDefault is_nodefault NULL #' @export -NoDefault = R6Class("NoDefault", - public = list( - initialize = function() { - } - ), -) - -#' @export -NO_DEF = NoDefault$new() # nolint -is_nodefault = function(x) inherits(x, "NoDefault") +NO_DEF = structure(list(), class = "NoDefault") # nolint +is_nodefault = function(x) identical(x, NO_DEF) diff --git a/R/Param.R b/R/Param.R index c0446fa4..67bc4d9e 100644 --- a/R/Param.R +++ b/R/Param.R @@ -1,228 +1,103 @@ -#' @title Param Class +#' @title Check value validity #' #' @description -#' This is the abstract base class for parameter objects like [ParamDbl] and -#' [ParamFct]. +#' \pkg{checkmate}-like check-function. Check whether a list of values is feasible in the domain. +#' A value is feasible if it is of the same `storage_type`, inside of the bounds or element of +#' `special_vals`. `TuneToken`s are generally *not* accepted, so they should be filtered out +#' before the call, if present. #' -#' @template param_id -#' @template param_special_vals -#' @template param_default -#' @template param_tags -#' -#' @section S3 methods: -#' * `as.data.table()`\cr -#' [Param] -> [data.table::data.table()]\cr -#' Converts param to [data.table::data.table()] with 1 row. See [ParamSet]. -#' -#' @family Params -#' @export -Param = R6Class("Param", - public = list( - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - #' - #' Note that this object is typically constructed via derived classes, - #' e.g., [ParamDbl]. - initialize = function(id, special_vals, default, tags) { - - assert_id(id) - assert_names(id, type = "strict") - assert_list(special_vals) - assert_character(tags, any.missing = FALSE, unique = TRUE) - - private$.id = id - private$.special_vals = special_vals - private$.default = default - private$.tags = tags - if (!is_nodefault(default)) { # check that default is feasible - self$assert(default) - } - }, - - #' @description - #' \pkg{checkmate}-like check-function. Take a value from the domain of the - #' parameter, and check if it is feasible. A value is feasible if it is of - #' the same `storage_type`, inside of the bounds or element of - #' `special_vals`. - #' - #' @param x (`any`). - #' @return If successful `TRUE`, if not a string with the error message. - check = function(x) { - # either we are directly feasible, or in special vals, if both are untrue return errmsg from 1st check - if (inherits(x, "TuneToken")) { - return(tryCatch({ - tunetoken_to_ps(x, self, self$id) - TRUE - }, error = function(e) paste("tune token invalid:", conditionMessage(e)))) - } - ch = private$.check(x) - ifelse(isTRUE(ch) || has_element(private$.special_vals, x), TRUE, ch) - }, - - #' @description - #' \pkg{checkmate}-like assert-function. Take a value from the domain of - #' the parameter, and assert if it is feasible. A value is feasible if it - #' is of the same `storage_type`, inside of the bounds or element of - #' `special_vals`. - #' - #' @param x (`any`). - #' @return If successful `x` invisibly, if not an exception is raised. - assert = function(x) makeAssertionFunction(self$check)(x), - - #' @description - #' \pkg{checkmate}-like test-function. Take a value from the domain of the - #' parameter, and test if it is feasible. A value is feasible if it is of - #' the same `storage_type`, inside of the bounds or element of - #' `special_vals`. - #' - #' @param x (`any`). - #' @return If successful `TRUE`, if not `FALSE`. - test = function(x) makeTestFunction(self$check)(x), - - #' @description - #' Repeats this parameter n-times (by cloning). - #' Each parameter is named "\[id\]_rep_\[k\]" and gets the additional tag "\[id\]_rep". - #' - #' @param n (`integer(1)`). - #' @return [`ParamSet`]. - rep = function(n) { - assert_count(n) - pid = self$id - join_id = paste0(pid, "_rep") - taggedself = self$with_tags(c(self$param_tags, join_id)) - repeatedself = structure(rep(list(taggedself), n), names = sprintf("%s_%s", join_id, seq_len(n))) - ParamSet$new(repeatedself, ignore_ids = TRUE) - }, - - #' @description - #' Creates a clone of this parameter with changed `$tags` slot. - #' If the tags do not change, no clone is created. - #' @param tags (`character`) tags of the clone. - #' @return [`Param`]. - with_tags = function(tags) { - origtags = private$.tags - if (identical(tags, origtags)) return(self) - assert_character(tags, any.missing = FALSE, unique = TRUE) - on.exit({private$.tags = origtags}) - private$.tags = tags - self$clone(deep = TRUE) - }, +#' @param x (`any`). +#' @return If successful `TRUE`, if not a string with the error message. +#' @export +domain_check = function(param, values, describe_error = TRUE) { + ## # either we are directly feasible, or in special vals, if both are untrue return errmsg from 1st check + ## if (inherits(value, "TuneToken")) { + ## return(tryCatch({ + ## tunetoken_to_ps(x, self, self$id) + ## TRUE + ## }, error = function(e) paste("tune token invalid:", conditionMessage(e)))) + ## } + ## has_element(private$.special_vals, x), TRUE, ch) + if (!test_list(values, len = nrow(param))) return(if (describe_error) "values must be a list" else FALSE) + if (length(values) == 0) return(TRUE) # happens when there are no params + values to check + assert_string(unique(param$grouping)) + special_vals_hit = pmap_lgl(list(param$special_vals, values), has_element) + if (any(special_vals_hit)) { + # don't annoy domain_check methods with the burdon of having to filter out + # values that match special_values + Recall(param[!special_vals_hit], values[!special_vals_hit], describe_error = describe_error) + } else { + UseMethod("domain_check") + } +} +#' @export +domain_assert = makeAssertionFunction(domain_check) +#' @export +domain_test = function(param, values) domain_check(param, values, describe_error = FALSE) - #' @description - #' Creates a clone of this parameter with changed `$id` slot. - #' If the `id` does not change, no clone is created. - #' @param id (`character(1)`) id of the clone. - #' @return [`Param`]. - with_id = function(id) { - if (identical(id, self$id)) return(self) - assert_id(id) - origid = private$.id - on.exit({private$.id = origid}) - private$.id = id - self$clone(deep = TRUE) - }, +#' @export +domain_storage_type = function(param) { + assert_string(unique(param$grouping)) + UseMethod("domain_storage_type") +} - #' @description - #' Helper for print outputs. - format = function() { - sprintf("<%s:%s>", class(self)[1L], self$id) - }, +#' @export +domain_nlevels = function(param) { + assert_string(unique(param$grouping)) + UseMethod("domain_nlevels") +} - #' @description - #' Printer. - #' - #' @param ... (ignored). - #' @param hide_cols (`character()`)\cr - #' Which fields should not be printed? Default is `"nlevels"`, - #' `"is_bounded"`, `"special_vals"`, `"tags"`, and `"storage_type"`. - print = function(..., hide_cols = c("nlevels", "is_bounded", "special_vals", "tags", "storage_type")) { - # this is bit bullshitty, but works by delegating to the printer of the PS - d = as.data.table(ParamSet$new(list(self))) - assert_subset(hide_cols, names(d)) - print(d[, setdiff(colnames(d), hide_cols), with = FALSE]) - }, +#' @export +domain_is_bounded = function(param) { + assert_string(unique(param$grouping)) + UseMethod("domain_is_bounded") +} - #' @description - #' Takes values from \[0,1\] and maps them, regularly distributed, to the - #' domain of the parameter. Think of: quantile function or the use case to - #' map a uniform-\[0,1\] random variable into a uniform sample from this - #' param. - #' - #' @param x (`numeric(1)`). - #' @return Value of the domain of the parameter. - qunif = function(x) { - assert_numeric(x, lower = 0, upper = 1) - assert_true(self$is_bounded) - private$.qunif(x) - }, +#' @export +domain_is_number = function(param) { + assert_string(unique(param$grouping)) + UseMethod("domain_is_number") +} - #' @description - #' Converts a value to the closest valid param. Only for values that - #' pass `$check()` and mostly used internally. - #' @param x (`any`). - #' @return `x` converted to a valid type for the `Param`. - convert = function(x) { - x - } - ), +#' @export +domain_is_categ = function(param) { + assert_string(unique(param$grouping)) + UseMethod("domain_is_categ") +} - active = list( - #' @field id (`character(1)`)\cr - #' Identifier of the object. - id = function() private$.id, +#' @export +domain_qunif = function(param, x) { + assert_string(unique(param$grouping)) + assert(check_number(x, lower = 0, upper = 1), check_numeric(x, lower = 0, upper = 1, any.missing = FALSE, length = nrow(param))) + UseMethod("domain_qunif") +} - #' @field special_vals (`list()`)\cr - #' Arbitrary special values this parameter is allowed to take. - special_vals = function() private$.special_vals, +#' @export +domain_sanitize = function(param, values) { + assert_string(unique(param$grouping)) + UseMethod("domain_sanitize") +} - #' @field default (`any`)\cr - #' Default value. - default = function() private$.default, +#' @export +domain_storage_type.default = function(param) rep("list", nrow(param)) - #' @field param_tags (`character()`)\cr - #' Arbitrary tags to group and subset parameters. - param_tags = function() private$.tags, +#' @export +domain_nlevels.default = function(param) rep(Inf, nrow(param)) - #' @field class (`character(1)`)\cr - #' R6 class name. Read-only. - class = function() class(self)[[1L]], +#' @export +domain_is_bounded.default = function(param) rep(FALSE, nrow(param)) - #' @field is_number (`logical(1)`)\cr - #' `TRUE` if the parameter is of type `"dbl"` or `"int"`. - is_number = function() self$class %in% c("ParamDbl", "ParamInt"), +#' @export +domain_is_number.default = function(param) rep(FALSE, nrow(param)) - #' @field is_categ (`logical(1)`)\cr - #' `TRUE` if the parameter is of type `"fct"` or `"lgl"`. - is_categ = function() self$class %in% c("ParamFct", "ParamLgl"), +#' @export +domain_is_categ.default = function(param) rep(FALSE, nrow(param)) - #' @field has_default (`logical(1)`)\cr - #' Is there a default value? - has_default = function() !is_nodefault(private$.default) - ), +#' @export +domain_is_categ.default = function(param) rep(FALSE, nrow(param)) - private = list( - .check = function(x) stop("abstract"), - .qunif = function(x) stop("abstract"), # should be implemented by subclasses, argcheck happens in Param$qunif - .special_vals = NULL, - .default = NULL, - .tags = NULL, - .id = NULL - ) -) +#' @export +domain_qunif.default = function(param) stop("undefined") #' @export -as.data.table.Param = function(x, ...) { # nolint - data.table( - id = x$id, - class = x$class, - lower = x$lower, - upper = x$upper, - levels = list(x$levels), - nlevels = x$nlevels, - is_bounded = x$is_bounded, - special_vals = list(x$special_vals), - default = list(x$default), - storage_type = x$storage_type, - tags = list(x$param_tags) - ) -} +domain_sanitize.default = function(param, values) values diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 32e23bc2..8c469265 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -1,86 +1,55 @@ -#' @title Numerical Parameter -#' -#' @description -#' A [Param] to describe real-valued parameters. -#' -#' @note -#' The upper and lower bounds in `$check()` are expanded by -#' `sqrt(.Machine$double.eps)` to prevent errors due to the precision of double -#' values. -#' -#' @template param_id -#' @template param_lower -#' @template param_upper -#' @template param_special_vals -#' @template param_default -#' @template param_tags -#' @template param_tolerance -#' -#' @family Params -#' @include Param.R +#' @rdname Domain #' @export -#' @examples -#' ParamDbl$new("ratio", lower = 0, upper = 1, default = 0.5) -ParamDbl = R6Class("ParamDbl", inherit = Param, - public = list( - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - initialize = function(id, lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps)) { - private$.lower = assert_number(lower) - private$.upper = assert_number(upper) - private$.tolerance = assert_number(tolerance, lower = 0) - assert_true(lower <= upper) - super$initialize(id, special_vals = special_vals, default = default, tags = tags) - }, +p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE) { + assert_number(tolerance, lower = 0) + assert_number(lower) + assert_number(upper) + assert_true(lower <= upper) + if (assert_flag(logscale)) { + if (!is.null(trafo)) stop("When a trafo is given then logscale must be FALSE") + if (assert_number(lower) <= 0) stop("When logscale is TRUE then lower bound must be strictly greater than 0") + trafo = exp + # at this point we don't want to overwrite 'lower' and 'upper, since they get used for the representation + real_lower = log(lower) + real_upper = log(assert_number(upper)) + } else { + real_lower = lower + real_upper = upper + } - #' @description - #' Restrict the value to within the allowed range. This works - #' in conjunction with `$tolerance`, which accepts values - #' slightly out of this range. - #' - #' @param x (`numeric(1)`)\cr - #' Value to convert. - convert = function(x) { - min(max(x, private$.lower), private$.upper) - } - ), + domain(cls = "ParamDbl", grouping = "ParamDbl", lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, + depends_expr = substitute(depends)) +} - active = list( - #' @template field_lower - lower = function() private$.lower, - #' @template field_upper - upper = function() private$.upper, - #' @field tolerance (`numeric(1)`)\cr - #' tolerance of values to accept beyond `$lower` and `$upper`. - #' Used both for relative and absolute tolerance. - tolerance = function() private$.tolerance, - #' @template field_levels - levels = function() NULL, - #' @template field_nlevels - nlevels = function() Inf, - #' @template field_is_bounded - is_bounded = function() is.finite(private$.lower) && is.finite(private$.upper), - #' @template field_storage_type - storage_type = function() "numeric" - ), +#' @export +domain_check.ParamDbl = function(param, values, describe_error = TRUE) { + lower = param$lower - param$tolerance * pmax(1, abs(param$lower)) + upper = param$upper - param$tolerance * pmax(1, abs(param$upper)) + if (qtestr(values, "N1")) { + values_num = as.numeric(values) + if (all(values_num >= lower) && all(values_num <= upper)) return(TRUE) + } + if (!describe_error) return(FALSE) + + check_domain_vectorize(param$id, values, check_number, more_args = list(lower = lower, upper = upper)) +} - private = list( - .check = function(x) { - # Accept numbers between lower and upper bound, with tolerance `$tolerance` - # Tolerance is both absolute & relative tolerance (if either tolerance is - # undercut the value is accepted: - # Values that go beyond the bound by less than `tolerance` are also - # accepted (absolute tolerance) - # Values that go beyond the bound by less than `abs() * tolerance` - # are also accepted (relative tolerance) - checkNumber(x, - lower = private$.lower - private$.tolerance * max(1, abs(private$.lower)), - upper = private$.upper + private$.tolerance * max(1, abs(private$.upper)) - ) - }, - .qunif = function(x) x * private$.upper - (x-1) * private$.lower, - .lower = NULL, - .upper = NULL, - .tolerance = NULL - ) -) +#' @export +domain_sanitize.ParamDbl = function(param, values) { + as.list(min(max(as.numeric(values), param$lower), param$upper)) +} + +#' @export +domain_storage_type.ParamDbl = function(param) rep("numeric", nrow(param)) +#' @export +domain_nlevels.ParamDbl = function(param) ifelse(param$upper == param$lower, 1, Inf) +#' @export +domain_is_bounded.ParamDbl = function(param) is.finite(param$lower) && is.finite(param$upper) +#' @export +domain_is_number.ParamDbl = function(param) rep(TRUE, nrow(param)) +#' @export +domain_is_categ.ParamDbl = function(param) rep(FALSE, nrow(param)) +#' @export +domain_qunif.ParamDbl = function(param, x) { + pmax(pmin(x * param$upper - (x-1) * param$lower, param$upper), param$lower) # extra careful here w/ rounding errors +} diff --git a/R/ParamFct.R b/R/ParamFct.R index 80ebeb60..890d1d6d 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -1,57 +1,53 @@ -#' @title Factor Parameter -#' -#' @description -#' A [Param] to describe categorical (factor) parameters. -#' -#' @template param_id -#' @template param_levels -#' @template param_special_vals -#' @template param_default -#' @template param_tags -#' -#' @family Params -#' @include Param.R -#' @export -#' @examples -#' ParamFct$new("f", levels = letters[1:3]) -ParamFct = R6Class("ParamFct", inherit = Param, - public = list( - - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - #' - #' @param levels (`character()`)\cr - #' Set of allowed levels. - initialize = function(id, levels, special_vals = list(), default = NO_DEF, tags = character()) { - assert_character(levels, any.missing = FALSE, unique = TRUE) - private$.levels = levels - super$initialize(id, special_vals = special_vals, default = default, tags = tags) +#' @rdname Domain +#' @export +p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { + constargs = as.list(match.call()[-1]) + levels = eval.parent(constargs$levels) + if (!is.character(levels)) { + # if the "levels" argument is not a character vector, then + # we add a trafo. + assert(check_atomic_vector(levels), check_list(levels)) + if (is.null(names(levels))) { + names(levels) = as.character(levels) } - ), - - active = list( - #' @template field_levels - levels = function() private$.levels, - #' @template field_lower - lower = function() NA_real_, - #' @template field_upper - upper = function() NA_real_, - #' @template field_nlevels - nlevels = function() length(private$.levels), - #' @template field_is_bounded - is_bounded = function() TRUE, - #' @template field_storage_type - storage_type = function() "character" - ), + trafo = crate(function(x) { + x = levels[[x]] + if (!is.null(trafo)) x = trafo(x) + x + }, trafo, levels) + real_levels = names(levels) + } else { + real_levels = levels + } + # group p_fct by levels, so the group can be checked in a vectorized fashion. + grouping = str_collapse(gsub("([\\\\\"])", "\\\\\\1", sort(real_levels)), quote = '"', sep = ",") + domain(cls = "ParamFct", grouping = grouping, levels = real_levels, special_vals = special_vals, default = default, tags = tags, trafo = trafo, depends_expr = substitute(depends)) +} - private = list( - .check = function(x) check_choice(x, choices = private$.levels), +#' @export +domain_check.ParamFct = function(param, values, describe_error = TRUE) { + if (testr(values, "S1")) { + values_str = as.character(values) + if (all(values_str %in% param$levels[[1]])) return(TRUE) # this works because we have the grouping -- all 'levels' are the same here. + } + if (!describe_error) return(FALSE) + check_domain_vectorized(param$ids, param, check_choice, more_args = list(choices = param$levels), describe_error = describe_error) +} - .qunif = function(x) { - z = floor(x * self$nlevels * (1 - 1e-16)) + 1 # make sure we dont map to upper+1 - private$.levels[z] - }, +#' @export +domain_storage_type.ParamFct = function(param) rep("character", nrow(param)) +#' @export +domain_nlevels.ParamFct = function(param) map_dbl(param$levels, length) +#' @export +domain_is_bounded.ParamFct = function(param) rep(TRUE, nrow(param)) +#' @export +domain_is_number.ParamFct = function(param) rep(FALSE, nrow(param)) +#' @export +domain_is_categ.ParamFct = function(param) rep(TRUE, nrow(param)) +#' @export +domain_qunif.ParamFct = function(param, x) { + nlevels = domain_nlevels(param) + z = pmin(floor(x * nlevels) + 1, nlevels) # make sure we dont map to upper+1 + as.character(pmap(list(param$levels, z), `[[`)) +} - .levels = NULL - ) -) diff --git a/R/ParamInt.R b/R/ParamInt.R index a85c70d8..1d212ddd 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -1,70 +1,72 @@ -#' @title Integer Parameter -#' -#' @description -#' A [Param] to describe integer parameters. -#' -#' @template param_id -#' @template param_lower -#' @template param_upper -#' @template param_special_vals -#' @template param_default -#' @template param_tags -#' -#' @section Methods: -#' See [Param]. -#' -#' @family Params -#' @include Param.R + +#' @rdname Domain #' @export -#' @examples -#' ParamInt$new("count", lower = 0, upper = 10, default = 1) -ParamInt = R6Class("ParamInt", inherit = Param, - public = list( - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - initialize = function(id, lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character()) { - if (isTRUE(is.infinite(lower))) { - private$.lower = lower - } else { - private$.lower = assert_int(lower) - } - if (isTRUE(is.infinite(upper))) { - private$.upper = upper - } else { - private$.upper = assert_int(upper) - } - assert_true(lower <= upper) - super$initialize(id, special_vals = special_vals, default = default, tags = tags) - }, +p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE) { + assert_number(tolerance, lower = 0, upper = 0.5) + # assert_int will stop for `Inf` values, which we explicitly allow as lower / upper bound + if (!isTRUE(is.infinite(lower))) assert_int(lower, tol = 1e-300) else assert_number(lower) + if (!isTRUE(is.infinite(upper))) assert_int(upper, tol = 1e-300) else assert_number(upper) + assert_true(lower <= upper) + if (assert_flag(logscale)) { + if (!is.null(trafo)) stop("When a trafo is given then logscale must be FALSE") + if (lower < 0) stop("When logscale is TRUE then lower bound must be greater or equal 0") + trafo = crate(function(x) as.integer(max(min(exp(x), upper), lower)), lower, upper) + # at this point we don't want to overwrite 'lower' and 'upper, since they get used for the representation + real_lower = log(max(lower, 0.5)) + real_upper = log(upper + 1) + cls = "ParamDbl" + } else { + cls = "ParamInt" + real_lower = lower + real_upper = upper + } - #' @description - #' Converts a value to an integer. - #' @param x (`numeric(1)`)\cr - #' Value to convert. - convert = function(x) { - as.integer(x) - } - ), + domain(cls = cls, grouping = cls, lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, + depends_expr = substitute(depends)) +} + +#' @export +domain_check.ParamInt = function(param, values, describe_error = TRUE) { + if (!qtestr(values, "N1()")) { + if (!describe_error) return(FALSE) + check_domain_vectorize(param$id, values, check_int, + more_args = list(lower = values$lower - 0.5, upper = values$upper + 0.5, # be lenient with bounds, because they would refer to the rounded values + tol = .51) # we don't know the tolerances of individual values, but we do know that some values are not even (non-missing, finite) numerics + ) + } - active = list( - #' @template field_lower - lower = function() private$.lower, - #' @template field_upper - upper = function() private$.upper, - #' @template field_levels - levels = function() NULL, - #' @template field_nlevels - nlevels = function() (private$.upper - private$.lower) + 1L, - #' @template field_is_bounded - is_bounded = function() is.finite(private$.lower) && is.finite(private$.upper), - #' @template field_storage_type - storage_type = function() "integer" - ), + values_num = as.numeric(values) - private = list( - .check = function(x) checkInt(x, lower = private$.lower, upper = private$.upper, tol = 1e-300), - .qunif = function(x) as.integer(floor(x * self$nlevels * (1 - 1e-16)) + private$.lower), # make sure we dont map to upper+1 - .lower = NULL, - .upper = NULL + if (all(abs(trunc(values_num + 0.5) - 0.5) <= param$tolerance)) { + values_num = round(values_num) + if (all(values_num >= param$lower) && all(values_num <= param$upper)) { + return(TRUE) + } + } + if (!describe_error) return(FALSE) + + check_domain_vectorize(param$id, values_num, check_int, + more_args = list(lower = param$lower - 0.5, upper = param$upper + 0.5, # be lenient with bounds, because they would refer to the rounded values + tol = pmax(1e-300, param$tolerance + 2 * abs(values_num) * .Machine$double.eps)) # want to have inclusive tolerance bounds. Not sure if 2* is necessary. ) -) +} + +#' @export +domain_sanitize.ParamInt = function(param, values) { + as.list(as.integer(as.numeric(values) + 0.5)) +} + +#' @export +domain_nlevels.ParamInt = function(param) (param$upper - param$lower) + 1 +#' @export +domain_storage_type.ParamInt = function(param) rep("integer", nrow(param)) +#' @export +domain_is_bounded.ParamInt = function(param) is.finite(param$lower) && is.finite(param$upper) +#' @export +domain_is_number.ParamInt = function(param) rep(TRUE, nrow(param)) +#' @export +domain_is_categ.ParamInt = function(param) rep(FALSE, nrow(param)) +#' @export +domain_qunif.ParamInt = function(param, x) { + pmax(pmin(as.integer(x * (param$upper + 1) - (x-1) * param$lower), param$upper), param$lower) # extra careful here w/ rounding errors and the x == 1 case +} diff --git a/R/ParamLgl.R b/R/ParamLgl.R index 7052165b..ab5d1839 100644 --- a/R/ParamLgl.R +++ b/R/ParamLgl.R @@ -1,44 +1,30 @@ -#' @title Logical Parameter -#' -#' @description -#' A [Param] to describe logical parameters. -#' -#' @template param_id -#' @template param_special_vals -#' @template param_default -#' @template param_tags -#' -#' @family Params -#' @include Param.R -#' @export -#' @examples -#' ParamLgl$new("flag", default = TRUE) -ParamLgl = R6Class("ParamLgl", inherit = Param, - public = list( - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - initialize = function(id, special_vals = list(), default = NO_DEF, tags = character()) { - super$initialize(id, special_vals = special_vals, default = default, tags = tags) - } - ), +#' @rdname Domain +#' @export +p_lgl = function(special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { + domain(cls = "ParamLgl", grouping = "ParamLgl", levels = c(TRUE, FALSE), special_vals = special_vals, default = default, + tags = tags, trafo = trafo, depends_expr = substitute(depends)) +} - active = list( - #' @template field_lower - lower = function() NA_real_, - #' @template field_upper - upper = function() NA_real_, - #' @template field_levels - levels = function() c(TRUE, FALSE), - #' @template field_nlevels - nlevels = function() 2L, - #' @template field_is_bounded - is_bounded = function() TRUE, - #' @template field_storage_type - storage_type = function() "logical" - ), +#' @export +domain_check.ParamLgl = function(param, values, describe_error = TRUE) { + if (qtestr(values, "B1")) { + return(TRUE) + } + if (!describe_error) return(FALSE) + check_domain_vectorized(param$id, values, check_flag) +} - private = list( - .check = function(x) check_flag(x), - .qunif = function(x) x < 0.5 - ) -) +#' @export +domain_storage_type.ParamLgl = function(param) rep("logical", nrow(param)) +#' @export +domain_nlevels.ParamLgl = function(param) rep(2, nrow(param)) +#' @export +domain_is_bounded.ParamLgl = function(param) rep(TRUE, nrow(param)) +#' @export +domain_is_number.ParamLgl = function(param) rep(FALSE, nrow(param)) +#' @export +domain_is_categ.ParamLgl = function(param) rep(TRUE, nrow(param)) +#' @export +domain_qunif.ParamLgl = function(param, x) { + x < 0.5 +} diff --git a/R/ParamS6.R b/R/ParamS6.R new file mode 100644 index 00000000..d2615340 --- /dev/null +++ b/R/ParamS6.R @@ -0,0 +1,157 @@ + +#' @rdname Domain +#' @export +p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { + support_object = set6_cache_dedup(support) + support_description = if (is.character(support)) support else set6_sane_repr(object) + + domain(cls = "ParamSet6", grouping = support_description, + lower = suppressWarnings(as.numeric(support_object$lower)), + upper = suppressWarnings(as.numeric(support_object$upper)), + levels = as.list(support_object$elements), + special_vals = special_vals, + default = default, + tags = tags, + trafo = trafo, + depends_expr = depends_expr) +} + +#' @export +domain_check.ParamSet6 = function(param, values, describe_error = TRUE) { + # we can rely on 'grouping' always giving us the same class + set = set6_cache_get(param$grouping[[1]]) + if (set$contains(values, all = TRUE)) { + return(TRUE) + } + if (!describe_error) return(FALSE) + nomatches = param$id[!set$contains(values, all = FALSE)] + sprintf("%s: not contained in %s.", str_collapse(nomatches, sep = ", "), set$strprint()) +} + +#' @export +domain_storage_type.ParamSet6 = function(param) { + set = set6_cache_get(param$grouping[[1]]) + if (set$power != 1 || set$class %nin% c("numeric", "integer")) { + storage_type = "list" + } else { + storage_type = set$class + } + rep(storage_type, nrow(param)) +} +#' @export +domain_nlevels.ParamSet6 = function(param) { + # we can rely on 'grouping' always giving us the same class + set = set6_cache_get(param$grouping[[1]]) + card = set$properties$cardinality + card = if (!is.numeric(card)) Inf + rep(card, nrow(param)) +} +#' @export +domain_is_bounded.ParamSet6 = function(param) { + # we can rely on 'grouping' always giving us the same class + set = set6_cache_get(param$grouping[[1]]) + card = set$properties$cardinality + card = if (!is.numeric(card)) Inf + boundedness = is.finite(card) || (is.finite(set$lower) && is.finite(set$upper)) + rep(boundedness, nrow(param)) +} +#' @export +domain_is_number.ParamSet6 = function(param) { + rep(domain_storage_type.ParamSet6(param[1]) != "list", nrow(param)) +} +#' @export +domain_is_categ.ParamSet6 = function(param) { + set = set6_cache_get(param$grouping[[1]]) + categness = set$power == 1 && set$class %nin% c("numeric", "integer") && is.finite(set$properties$cardinality) + + rep(categness, nrow(param)) +} +#' @export +domain_qunif.ParamSet6 = function(param, x) stop("undefined") + + + +set6_cache = new.env(parent = emptyenv()) + +set6_cache_get = function(description) { + assert_string(description) + set = set6_cache[[description]] + if (!is.null(set)) return(set) + s6ns = asNamespace("set6") + settypes = keep(names(s6ns), function(n) { + obj = s6ns[[n]] + is.environment(obj) && is.function(obj[["get_inherit"]]) && (identical(obj, set6::Set) || identical(obj$get_inherit(), set6::Set)) + }) + get_settype = function(st, error_on_not_found = TRUE) { + matches = settypes[match(tolower(st), tolower(settypes), nomatch = 0)] + if (!length(matches)) { + if (error_on_not_found) { + stopf("Unknown settype '%s' in description '%s'", st, description) + } else { + return(NULL) + } + } + if (length(matches) > 1) { + matches = settypes[match(st, settypes, nomatch = 0)] + # even error if error_on_not_found is FALSE, because this is a special message + if (length(matches) != 1) stopf("Settype '%s' in description '%s' matched multiple set6 classes, but none of them matches by case.",st, description) + } + s6ns[[matches]] + } + intervaldef = regmatches(description, regexec("^([[(])(([^,]*),([^,|]*))?(\\| *([a-zA-Z]+))? *([])])$", description))[[1]] + discretedef = regmatches(description, regexec("^\\{([^{}|]*)(\\| *([a-zA-Z]+))? *\\}$", description))[[1]] + trimdesc = trimws(description) + generated = NULL + if (length(intervaldef)) { # interval definition found + type = paste0(intervaldef[[2]], intervaldef[[8]]) + lower = if (intervaldef[[4]] == "") -Inf else suppressWarnings(as.numeric(intervaldef[[4]])) + upper = if (intervaldef[[5]] == "") Inf else suppressWarnings(as.numeric(intervaldef[[5]])) + if (is.na(lower) || is.na(upper)) stopf("Description '%s' is ill-formed interval expression.", description) + universe = if (intervaldef[[7]] == "") set6::ExtendedReals else get_settype(intervaldef[[7]]) + universe = universe$new() + generated = set6::Interval$new(lower = lower, upper = upper, type = type, class = universe$class, universe = universe) + } else if (length(discretedef)) { + if (discretedef[[2]] == "") { + entries = character(0) + } else { + entries = strsplit(discretedef[[2]], " *, *")[[1]] + if ("" %in% entries) stopf("Empty string element not allowed in description '%s'", description) + } + universe = if (intervaldef[[4]] == "") set6::Universal else get_settype(intervaldef[[4]]) + universe = universe$new() + generated = set6::Set$new(elements = entries, universe = universe) + } else if (!is.null(get_settype(trimdesc))) { + generated = get_settype(trimdesc)$new() + } else if (tolower(substr(trimdesc, 1, 1)) == "n" && !is.null(get_settype(trimdesc))) { + generated = set6::setpower(get_settype(trimdesc)$new(), "n") + } else { + stopf("Description '%s' not in set6_cache and could not be constructed.", description) # how do we automatically generate a set from a string? + } + set6_cache_enter(generated, description) + generated +} + +set6_cache_enter = function(object, description = NULL) { + set6_cache[[set6_sane_repr(object)]] = object + if (!is.null(description)) { + set6_cache[[description]] = object + } +} + +set6_cache_dedup = function(object) { + if (is.character(object)) return(set6_cache_get(object)) + assert_class(object, "Set") + repr = set6_sane_repr(object) + alternative = set6_cache[[description]] + if (!is.null(alternative)) return(alternative) + set6_cache_enter(object) + object +} + +set6_sane_repr = function(object) { + assert_class(object, "Set") + using_unicode_for_whatever_reason = set6::useUnicode() + on.exit(set6::useUnicode(using_unicode_for_whatever_reason)) + set6::useUnicode(FALSE) + object$strprint(n = 1e10) +} diff --git a/R/ParamSet.R b/R/ParamSet.R index 4493e54b..ea8f1239 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -52,94 +52,111 @@ ParamSet = R6Class("ParamSet", #' List of [Param], named with their respective ID. #' `params` are cloned on-demand, so the input does not need #' to be cloned. + #' @param set_id (`character(1)`)\cr + #' `$set_id` of the resulting `ParamSet`. This determines the + #' prefix inside [`ParamSetCollection`]. Default `""` (no prefix). #' @param ignore_ids (`logical(1)`)\cr #' Ignore `$id` slots of `params` and instead use the names instead. #' When this is `TRUE`, then `params` must be named. #' This can be used to create a `ParamSet` with certain [`Param`] `id`s #' without having to clone said [`Param`]s. #' Default `FALSE`. - initialize = function(params = named_list(), ignore_ids = FALSE) { - assert_list(params, types = "Param") - if (ignore_ids) { - ids = names(params) + initialize = function(params = named_list(), extra_trafo = NULL, allow_dangling_dependencies = FALSE) { + assert_list(params, types = "Domain") + self$extra_trafo = extra_trafo # does assert in the active binding + + assert_names(names(params), type = "strict") + assert_ids(names(params)) + + if (!length(params)) { + private$.params = data.table(cls = character(0), grouping = character(0), cargo = list(), lower = numeric(0), upper = numeric(0), tolerance = numeric(0), + levels = list(), special_vals = list(), default = list(), tags = list(), trafo = list(), requirements = list(), id = character(0)) } else { - ids = map_chr(params, "id") - params = set_names(params, ids) + private$.params = rbindlist(c(list(dt0), params))[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))][, + # put 'id' at the beginning + c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "tags", "trafo", "has_trafo"), with = FALSE] } - assert_names(ids, type = "strict", .var.name = "params") - private$.params_unid = params - self$set_id = "" + setindexv(private$.params, c("id", "cls", "grouping")) + assert_names(colnames(private$.params), identical.to = ) + + # add Dependencies + imap(params, function(p, name) { + if (is.null(p$requirements[[1]])) return(NULL) + map(p$requirements[[1]], function(req) { + if (!req$on %in% names(params) || req$on == name) { + if (allow_dangling_dependencies) { + if (name == req$on) stop("A param cannot depend on itself!") + self$deps = rbind(self$deps, data.table(id = name, on = req$on, cond = list(req$cond))) + } else { + stopf("Parameter %s can not depend on %s.", name, req$on) + } + } else { + invoke(self$add_dep, id = name, .args = req) + } + }) + }) }, - #' @description - #' Adds a single param or another set to this set, all params are cloned on-demand, - #' so the input does not need to be cloned. - #' - #' @param p ([Param] | [ParamSet]). - add = function(p) { - - assert_multi_class(p, c("Param", "ParamSet")) - if (inherits(p, "Param")) { # level-up param to set - pparams = structure(list(p), names = p$id) - if (!is.null(private$.tags)) ptags = structure(list(p$param_tags), names = p$id) - ptrafo = NULL - pvalues = NULL - pdeps = NULL - } else { - pparams = p$params_unid - if (!is.null(private$.tags)) ptags = p$tags - ptrafo = p$trafo - pvalues = p$values - pdeps = p$deps + trafo = function(x, param_set) { # param_set argument only here for compatibility + trafos = private$.params[names(x), list(id, trafo, has_trafo, value = x), on = "id", nomatch = 0][(has_trafo)] + if (nrow(trafos)) { + trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] + insert_named(x, set_names(trafos$value, trafos$id)) + } + extra_trafo = self$extra_trafo + if (!is.null(extra_trafo)) { + x = extra_trafo(x) } + x + }, - nn = c(names(private$.params_unid), names(pparams)) - assert_names(nn, type = "strict") - if (!is.null(ptrafo)) { - stop("Cannot add a param set with a trafo.") + qunif = function(x) { + assert(check_data_frame(x, types = "numeric"), check_matrix(x, mode = "numeric")) + if (is.matrix(x)) { + qassert(x, "N[0,1]") + } else { + qassertr(x, "N[0,1]") + x = as.matrix(x) } - private$.params_unid = c(private$.params_unid, pparams) - private$.values = c(private$.values, pvalues) - if (!is.null(private$.tags)) private$.tags = c(private$.tags, ptags) - private$.deps = rbind(private$.deps, pdeps) - invisible(self) + assert_names(colnames(x), type = "unique", subset.of = private$.params$id) + + x = t(x) + params = private$.params[colnames(x), on = "id"] + params$result = list() + params[, result := as.data.frame(t(matrix(domain_qunif(.SD, x[id, ]), nrow = .N))), by = c("cls", "grouping")] + as.data.table(set_names(params$result, params$id)) + }, + + get_param = function(id) { + .id = id + paramrow = private$.params[id == .id] + if (!nrow(paramrow)) stopf("No param with id '%s'", id) + set_class(paramrow, c(paramrow$cls, class(paramrow))) }, + get_levels = function(id) domain_nlevels(self$get_param(id)), + #' @description #' Retrieves IDs of contained parameters based on some filter criteria #' selections, `NULL` means no restriction. #' Only returns IDs of parameters that satisfy all conditions. #' #' @param class (`character()`). - #' @param is_bounded (`logical(1)`). #' @param tags (`character()`). #' @return `character()`. - ids = function(class = NULL, is_bounded = NULL, tags = NULL) { + ids = function(class = NULL, tags = NULL, any_tags = NULL) { assert_character(class, any.missing = FALSE, null.ok = TRUE) - assert_flag(is_bounded, null.ok = TRUE) assert_character(tags, any.missing = FALSE, null.ok = TRUE) + assert_character(any_tags, any.missing = FALSE, null.ok = TRUE) - params = self$params_unid - ids = names(params) - if (is.null(class) && is.null(is_bounded) && is.null(tags)) { - return(ids) - } - - ii = rep(TRUE, length(ids)) - - if (!is.null(class)) { - ii = ii & map_chr(params, "class") %in% class - } - - if (!is.null(is_bounded)) { - ii = ii & map_lgl(params, "is_bounded") - } - - if (!is.null(tags)) { - ii = ii & map_lgl(self$tags, function(tg) all(tags %in% tg)) + if (is.null(class) && is.null(tags) && is.null(any_tags)) { + return(private$.params$id) } - - ids[ii] + .tags = tags + ptbl[(is.null(class) | cls %in% class) & + (is.null(tags) | map_lgl(tags, function(tg) all(.tags %in% tg))) & + (is.null(any_tags) | map_lgl(tags, function(tg) any(any_tags %in% tg))), + id] }, #' @description @@ -155,12 +172,13 @@ ParamSet = R6Class("ParamSet", #' @param check_required (`logical(1)`)\cr #' Check if all required parameters are set? #' @return Named `list()`. - get_values = function(class = NULL, is_bounded = NULL, tags = NULL, - type = "with_token", check_required = TRUE) { + get_values = function(class = NULL, tags = NULL, any_tags = NULL, + type = "with_token", check_required = TRUE, remove_dependencies = TRUE) { assert_choice(type, c("with_token", "without_token", "only_token")) + assert_flag(check_required) + values = self$values - params = self$params_unid ns = names(values) if (type == "without_token") { @@ -169,39 +187,73 @@ ParamSet = R6Class("ParamSet", values = keep(values, is, "TuneToken") } - if(check_required) { - required = setdiff(names(keep(self$tags, function(tg) "required" %in% tg)), ns) + if (check_required) { + required = setdiff(self$ids(tags = "required"), nds) if (length(required) > 0L) { stop(sprintf("Missing required parameters: %s", str_collapse(required))) } } - values[intersect(names(values), self$ids(class = class, is_bounded = is_bounded, tags = tags))] + if (remove_dependencies && nrow(deps)) { + for (j in seq_row(deps)) { + p1id = deps$id[[j]] + p2id = deps$on[[j]] + cond = deps$cond[[j]] + if (p1id %in% ns && !inherits(values[[p2id]], "TuneToken") && !isTRUE(cond$test(values[[p2id]]))) { + values[p1id] = NULL + } + } + } + + values[intersect(names(values), self$ids(class = class, tags = tags, any_tags = any_tags))] }, #' @description - #' Changes the current set to the set of passed IDs. + #' Create a new `ParamSet` restricted to the passed IDs. #' #' @param ids (`character()`). - subset = function(ids) { - param_ids = names(self$params_unid) + #' @return `ParamSet`. + subset = function(ids, allow_dangling_dependencies = FALSE) { + param_ids = private$.params$id + assert_subset(ids, param_ids) deps = self$deps - if (nrow(deps)) { # check that all required / leftover parents are still in new ids - parents = unique(deps[get("id") %in% ids, "on"][[1L]]) + if (!allow_dangling_dependencies && nrow(deps)) { # check that all required / leftover parents are still in new ids + on = NULL + parents = unique(deps[id %in% ids, on]) pids_not_there = setdiff(parents, ids) if (length(pids_not_there) > 0L) { stopf(paste0("Subsetting so that dependencies on params exist which would be gone: %s.", - "\nIf you still want to do that, manipulate '$deps' yourself."), str_collapse(pids_not_there)) + "\nIf you still want to subset, set allow_dangling_dependencies to TRUE."), str_collapse(pids_not_there)) } } - private$.params_unid = private$.params_unid[ids] - private$.params = private$.params[intersect(ids, names(private$.params))] - if (!is.null(private$.tags)) private$.tags = private$.tags[intersect(ids, names(private$.tags))] + result = ParamSet$new(extra_trafo = self$extra_trafo) + result$.__enclos_env__$private$.params = private$.params[id %in% ids] + result$assert_values = FALSE + result$deps = deps # restrict to ids already in pvals - ids2 = union(intersect(ids, names(private$.values)), setdiff(names(private$.values), param_ids)) - private$.values = private$.values[ids2] - invisible(self) + values = self$values + result$values = values[intersect(names(values), ids)] + result$assert_values = TRUE + result + }, + + subspaces = function(ids = private$.params$id) { + sapply(ids, simplify = FALSE, function(get_id) { + result = ParamSet$new(extra_trafo = self$extra_trafo) + result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] + result$assert_values = FALSE + result$values = values[intersect(names(values), get_id)] + result$assert_values = TRUE + result + }) + }, + + #' @description + #' Create a `ParamSet` from this object, even if this object itself is not + #' a `ParamSet`. + flatten = function() { + self$subset(private$.params$id) }, #' @description @@ -215,7 +267,7 @@ ParamSet = R6Class("ParamSet", on = NULL # pacify static code check dangling_deps = pars$deps[!on %in% pars$ids()] if (nrow(dangling_deps)) { - stopf("Dangling dependencies not allowed: Dependencies on %s dangling", str_collapse(dangling_deps$on)) + stopf("Dangling dependencies not allowed: Dependencies on %s dangling.", str_collapse(dangling_deps$on)) } pars }, @@ -228,63 +280,89 @@ ParamSet = R6Class("ParamSet", #' #' @param xs (named `list()`). #' @return If successful `TRUE`, if not a string with the error message. - check = function(xs) { - + check = function(xs, check_strict = FALSE, describe_error = TRUE) { + assert_flag(check_strict) + assert_flag(describe_error) ok = check_list(xs, names = "unique") if (!isTRUE(ok)) { - return(ok) + return(if (!describe_error) FALSE else ok) } - params = self$params_unid + + params = private$.params ns = names(xs) - ids = names(params) + ids = private$.params$id extra = wf(ns %nin% ids) if (length(extra)) { - return(sprintf("Parameter '%s' not available.%s", ns[extra], did_you_mean(extra, ids))) + return(if (!describe_error) FALSE else sprintf("Parameter '%s' not available.%s", ns[extra], did_you_mean(extra, ids))) } - # check each parameters feasibility - for (n in ns) { - ch = params[[n]]$check(xs[[n]]) - if (test_string(ch)) { # we failed a check, return string - return(paste0(n, ": ", ch)) + # check each parameter group's feasibility + ns_nontune = names(discard(xs, inherits, "TuneToken")) + pgroups = split(params[id %in% ns_nontune][, values := xs], by = c("cls", "grouping")) + break_early = FALSE + checkresults = map(pgroups, function(x) { + if (break_early) return(FALSE) + ch = domain_check(set_class(x, c(x$cls[[1]], class(x))), x$values, describe_error = describe_error) + if (!describe_error && !ch) break_early <<- TRUE # nolint + ch + }) + if (describe_error) { + if (break_early) return(FALSE) + } else { + checkresults = discard(checkresults, isTRUE) + if (length(checkresults)) { + return(str_collapse(checkresults), sep = "\n") } } - # check dependencies - deps = self$deps - if (nrow(deps)) { - for (j in seq_row(deps)) { - p1id = deps$id[j] - p2id = deps$on[j] - if (inherits(xs[[p1id]], "TuneToken") || inherits(xs[[p2id]], "TuneToken")) { - next # be lenient with dependencies when any parameter involved is a TuneToken - } - # we are ONLY ok if: - # - if param is there, then parent must be there, then cond must be true - # - if param is not there - cond = deps$cond[[j]] - ok = (p1id %in% ns && p2id %in% ns && cond$test(xs[[p2id]])) || - (p1id %nin% ns) - if (isFALSE(ok)) { - message = sprintf("The parameter '%s' can only be set if the following condition is met '%s'.", - p1id, cond$as_string(p2id)) - val = xs[[p2id]] - if (is.null(val)) { - message = sprintf(paste("%s Instead the parameter value for '%s' is not set at all.", - "Try setting '%s' to a value that satisfies the condition"), message, p2id, p2id) - } else { - message = sprintf("%s Instead the current parameter value is: %s=%s", message, p2id, val) - } - return(message) - } + if (check_strict) { + required = setdiff(self$ids(tags = "required"), ns) + if (length(required) > 0L) { + return(if (describe_error) sprintf("Missing required parameters: %s", str_collapse(required)) else FALSE) } + return(check_dependencies(xs, describe_error = describe_error)) } return(TRUE) # we passed all checks }, + check_dependencies = function(xs, describe_error = TRUE) { + deps = self$deps + if (!nrow(deps)) return(TRUE) + params = private$.params + ns = names(xs) + break_early = FALSE + errors = pmap(deps[id %in% ns], function(id, on, cond) { + if (break_early) return(FALSE) + onval = xs[[on]] + if (inherits(xs[[id]], "TuneToken") || inherits(onval, "TuneToken")) return(NULL) + + # we are ONLY ok if: + # - if 'id' is there, then 'on' must be there, and cond must be true + # - if 'id' is not there. but that is skipped (deps[id %in% ns] filter) + if (on %in% ns && cond$test(onval)) return(NULL) + if (!describe_error) { + break_early = TRUE + return(FALSE) + } + msg = sprintf("%s: can only be set if the following condition is met '%s'.", + id, cond$as_string(on)) + if (is.null(onval)) { + msg = sprintf(paste("%s Instead the parameter value for '%s' is not set at all.", + "Try setting '%s' to a value that satisfies the condition"), msg, on, on) + } else { + msg = sprintf("%s Instead the current parameter value is: %s=%s", msg, on, as_short_string(onval)) + } + msg + }) + if (!describe_error) return(!break_early) + errors = unlist(errors) + if (!length(errors)) return(TRUE) + str_collapse(errors, sep = "\n") + }, + #' @description #' \pkg{checkmate}-like test-function. Takes a named list. #' A point x is feasible, if it configures a subset of params, @@ -293,7 +371,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xs (named `list()`). #' @return If successful `TRUE`, if not `FALSE`. - test = function(xs) makeTest(res = self$check(xs)), + test = function(xs, check_strict = FALSE) self$check(xs, check_strict = check_strict, describe_error = FALSE), #' @description #' \pkg{checkmate}-like assert-function. Takes a named list. @@ -306,7 +384,7 @@ ParamSet = R6Class("ParamSet", #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. #' @return If successful `xs` invisibly, if not an error message. - assert = function(xs, .var.name = vname(xs)) makeAssertion(xs, self$check(xs), .var.name, NULL), # nolint + assert = function(xs, check_strict = FALSE, .var.name = vname(xs)) makeAssertion(xs, self$check(xs, check_strict = check_strict), .var.name, NULL), # nolint #' @description #' \pkg{checkmate}-like check-function. Takes a [data.table::data.table] @@ -317,16 +395,18 @@ ParamSet = R6Class("ParamSet", #' #' @param xdt ([data.table::data.table] | `data.frame()`). #' @return If successful `TRUE`, if not a string with the error message. - check_dt = function(xdt) { + check_dt = function(xdt, check_strict = FALSE, describe_error = TRUE) { xss = map(transpose_list(xdt), discard, is.na) - for (xs in xss) { - ok = self$check(xs) + msgs = list() + for (is in seq_along(xss)) { + xs = xss[[i]] + ok = self$check(xs, check_strict = check_strict, describe_error = describe_error) if (!isTRUE(ok)) { - return(ok) + if (!describe_error) return(FALSE) + msgs[[length(msgs) + 1]] = ok # this is the fast way to grow a list in R } } - - return(TRUE) + if (length(msgs)) msgs else TRUE }, #' @description @@ -334,7 +414,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xdt ([data.table::data.table]). #' @return If successful `TRUE`, if not `FALSE`. - test_dt = function(xdt) makeTest(res = self$check_dt(xdt)), + test_dt = function(xdt, check_strict = FALSE) makeTest(res = self$check_dt(xdt, check_strict = check_strict, describe_error = FALSE)), #' @description #' \pkg{checkmate}-like assert-function (s. `$check_dt()`). @@ -344,7 +424,7 @@ ParamSet = R6Class("ParamSet", #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. #' @return If successful `xs` invisibly, if not an error message. - assert_dt = function(xdt, .var.name = vname(xdt)) makeAssertion(xdt, self$check_dt(xdt), .var.name, NULL), # nolint + assert_dt = function(xdt, check_strict = FALSE, .var.name = vname(xdt)) makeAssertion(xdt, self$check_dt(xdt, check_strict = check_strict), .var.name, NULL), # nolint #' @description #' Adds a dependency to this set, so that param `id` now depends on param `on`. @@ -353,15 +433,16 @@ ParamSet = R6Class("ParamSet", #' @param on (`character(1)`). #' @param cond ([Condition]). add_dep = function(id, on, cond) { - params = self$params_unid - ids = names(params) + params = private$.params + ids = params$id assert_choice(id, ids) assert_choice(on, ids) assert_r6(cond, "Condition") if (id == on) { stopf("A param cannot depend on itself!") } - feasible_on_values = map_lgl(cond$rhs, params[[on]]$test) + + feasible_on_values = map_lgl(cond$rhs, domain_check, param = self$get_param(on), describe_error = FALSE) if (any(!feasible_on_values)) { stopf("Condition has infeasible values for %s: %s", on, str_collapse(cond$rhs[!feasible_on_values])) } @@ -372,12 +453,7 @@ ParamSet = R6Class("ParamSet", #' @description #' Helper for print outputs. format = function() { - set_id = self$set_id - if (!nzchar(set_id)) { - sprintf("<%s>", class(self)[1L]) - } else { - sprintf("<%s:%s>", class(self)[1L], set_id) - } + sprintf("<%s[%s]>", class(self)[1L], nrow(private$.params)) }, #' @description @@ -397,7 +473,8 @@ ParamSet = R6Class("ParamSet", assert_subset(hide_cols, names(d)) deps = self$deps if (nrow(deps)) { # add a nice extra charvec-col to the tab, which lists all parents-ids - dd = deps[, list(parents = list(unlist(get("on")))), by = "id"] + on = NULL + dd = deps[, list(parents = list(unlist(on))), by = "id"] d = merge(d, dd, on = "id", all.x = TRUE) } v = named_list(d$id) # add values to last col of print-dt as list col @@ -412,30 +489,18 @@ ParamSet = R6Class("ParamSet", ), active = list( + data = function(v) { + if (!missing(v)) stop("v is read-only") + private$.params[, list(id, class = cls, lower, upper, nlevels = self$nlevels, + is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags)] + }, + #' @template field_params params = function(rhs) { if (!missing(rhs) && !identical(rhs, private$.params)) { stop("$params is read-only.") } - # clone on-demand - punid = self$params_unid - truenames = names(punid) - uncloned = setdiff(truenames, names(private$.params)) - if (length(uncloned)) { - private$.params = c(private$.params, - sapply(uncloned, function(u) { - punid[[u]]$with_id(u) - }, simplify = FALSE) - )[truenames] - } - private$.params - }, - #' @template field_params_unid - params_unid = function(rhs) { - if (!missing(rhs) && !identical(rhs, private$.params_unid)) { - stop("$params_unid is read-only.") - } - private$.params_unid + map(private$.params, self$param) # giga-slow }, #' @template field_deps @@ -443,54 +508,50 @@ ParamSet = R6Class("ParamSet", if (missing(v)) { private$.deps } else { - private$.deps = assert_data_table(v) - } - }, - - #' @field set_id (`character(1)`)\cr - #' ID of this param set. Default `""`. Settable. - set_id = function(v) { - if (missing(v)) { - private$.set_id - } else { - if (!identical(v, "")) { - assert_id(v) - assert_names(v, type = "strict") + assert_data_table(v) + if (nrow(v)) { + # only test for things without which things would seriously break + assert_names(colnames(v), identical.to = c("id", "on", "cond")) + assert_subset(v$id, private$.params$id) + assert_character(v$on, any.missing = FALSE) + assert_list(v$cond, types = "Condition", any.missing = FALSE) + } else { + v = data.table(id = character(0), on = character(0), cond = list()) # make sure we have the right columns } - private$.set_id = v + private$.deps = v } }, #' @field length (`integer(1)`)\cr #' Number of contained [Param]s. length = function() { - length(self$params_unid) + nrow(private$.params) }, #' @field is_empty (`logical(1)`)\cr #' Is the `ParamSet` empty? is_empty = function() { - length(self$params_unid) == 0L + nrow(private$.params) == 0L }, #' @field class (named `character()`)\cr #' Classes of contained parameters, named with parameter IDs. class = function() { - private$get_member_with_idnames("class", as.character) + set_names(private$.params$class, private$.params$id) }, #' @field lower (named `double()`)\cr #' Lower bounds of parameters (`NA` if parameter is not numeric). #' Named with parameter IDs. lower = function() { - private$get_member_with_idnames("lower", as.double) + set_names(private$.params$lower, private$.params$id) }, #' @field upper (named `double()`)\cr #' Upper bounds of parameters (`NA` if parameter is not numeric). #' Named with parameter IDs. upper = function() { - private$get_member_with_idnames("upper", as.double) + set_names(private$.params$upper, private$.params$id) }, #' @field levels (named `list()`)\cr @@ -498,28 +559,30 @@ ParamSet = R6Class("ParamSet", #' `NULL` if the parameter is not categorical. #' Named with parameter IDs. levels = function() { - private$get_member_with_idnames("levels", as.list) + set_names(private$.params$levels, private$.params$id) }, #' @field nlevels (named `integer()`)\cr #' Number of categorical levels per parameter, `Inf` for double parameters or unbounded integer parameters. #' Named with param IDs. nlevels = function() { - private$get_member_with_idnames("nlevels", as.double) + set_names(private$.params[, list(id, nlevels = domain_nlevels(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", nlevels], + private$.params$id) }, #' @field is_bounded (named `logical()`)\cr #' Do all parameters have finite bounds? #' Named with parameter IDs. is_bounded = function() { - all(map_lgl(self$params_unid, "is_bounded")) + set_names(private$.params[, list(id, is_bounded = domain_is_bounded(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", is_bounded], + private$.params$id) }, #' @field special_vals (named `list()` of `list()`)\cr #' Special values for all parameters. #' Named with parameter IDs. special_vals = function() { - private$get_member_with_idnames("special_vals", as.list) + set_names(private$.params$special_vals, private$.params$id) }, #' @field default (named `list()`)\cr @@ -527,43 +590,43 @@ ParamSet = R6Class("ParamSet", #' If no default exists, element is not present. #' Named with parameter IDs. default = function() { - discard(private$get_member_with_idnames("default", as.list), is_nodefault) + defaulthaving = private$.params[!map_lgl(default, is_nodefault), list(id, default)] + set_names(defaulthaving$default, defaulthaving$id) }, - #' @field tags (named `list()` of `character()`)\cr - #' Can be used to group and subset parameters. - #' Named with parameter IDs. + #' @template field_tags tags = function(v) { - if (is.null(private$.tags)) { - private$.tags = private$get_member_with_idnames("param_tags", as.list) - } if (!missing(v)) { assert_list(v, any.missing = FALSE, types = "character") - assert_names(names(v), identical.to = names(self$params_unid)) - private$.tags = v + assert_names(names(v), permutation.of = private$.params$id) + private$.params[names(v), tags := v] + return(v) } - private$.tags + set_names(private$.params$tags, private$.params$id) }, #' @field storage_type (`character()`)\cr #' Data types of parameters when stored in tables. #' Named with parameter IDs. storage_type = function() { - private$get_member_with_idnames("storage_type", as.character) + set_names(private$.params[, list(id, storage_type = domain_storage_type(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", storage_type], + private$.params$id) }, #' @field is_number (named `logical()`)\cr #' Position is TRUE for [ParamDbl] and [ParamInt]. #' Named with parameter IDs. is_number = function() { - private$get_member_with_idnames("is_number", as.logical) + set_names(private$.params[, list(id, is_number = domain_is_number(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], + private$.params$id) }, #' @field is_categ (named `logical()`)\cr #' Position is TRUE for [ParamFct] and [ParamLgl]. #' Named with parameter IDs. is_categ = function() { - private$get_member_with_idnames("is_categ", as.logical) + set_names(private$.params[, list(id, is_categ = domain_is_categ(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], + private$.params$id) }, #' @field all_numeric (`logical(1)`)\cr @@ -578,9 +641,9 @@ ParamSet = R6Class("ParamSet", all(self$is_categ) }, - #' @field trafo (`function(x, param_set)`)\cr + #' @field extra_trafo (`function(x, param_set)`)\cr #' Transformation function. Settable. - #' User has to pass a `function(x, param_set)`, of the form\cr + #' User has to pass a `function(x)`, of the form\cr #' (named `list()`, [ParamSet]) -> named `list()`.\cr #' The function is responsible to transform a feasible configuration into another encoding, #' before potentially evaluating the configuration with the target algorithm. @@ -588,19 +651,19 @@ ParamSet = R6Class("ParamSet", #' It needs to have unique names, and the target algorithm has to accept the configuration. #' For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). #' Is NULL by default, and you can set it to NULL to switch the transformation off. - trafo = function(f) { + extra_trafo = function(f) { if (missing(f)) { - private$.trafo + private$.extra_trafo } else { - assert_function(f, args = c("x", "param_set"), null.ok = TRUE) - private$.trafo = f + assert_function(f, args = c("x"), null.ok = TRUE) + private$.extra_trafo = f } }, #' @field has_trafo (`logical(1)`)\cr #' Has the set a `trafo` function? has_trafo = function() { - !is.null(private$.trafo) + !is.null(private$.extra_trafo) || any(private$.params$has_trafo) }, #' @template field_values @@ -610,23 +673,24 @@ ParamSet = R6Class("ParamSet", } if (self$assert_values) { self$assert(xs) - private$get_tune_ps(xs) # check that to_tune() are valid + if (test_list(xs, types = "TuneToken")) { + private$get_tune_ps(xs) # check that to_tune() are valid + } } if (length(xs) == 0L) { xs = named_list() } else if (self$assert_values) { # this only makes sense when we have asserts on # convert all integer params really to storage type int, move doubles to within bounds etc. # solves issue #293, #317 - params = self$params_unid # cache the AB - for (n in names(xs)) { - p = params[[n]] - x = xs[[n]] - if (inherits(x, "TuneToken")) next - if (has_element(p$special_vals, x)) next - xs[[n]] = p$convert(x) - } + nontt = discard(xs, inherits, "TuneToken") + sanitised = private$.params[names(nontt), on = "id"][, values := nontt][ + !pmap_lgl(list(special_vals, values), has_element), + list(id, values = domain_sanitize(.SD, values)), by = c("cls", "grouping")] + insert_named(xx, set_names(sanitised$values, sanitised$id)) } - private$.values = xs + # store with param ordering, return value with original ordering + private$.values = xs[private$.params[id %in% names(xs), id]] + xs }, #' @field has_deps (`logical(1)`)\cr @@ -637,12 +701,9 @@ ParamSet = R6Class("ParamSet", ), private = list( - .set_id = NULL, - .trafo = NULL, - .params = named_list(), - .params_unid = NULL, + .extra_trafo = NULL, + .params = NULL, .values = named_list(), - .tags = NULL, .deps = data.table(id = character(0L), on = character(0L), cond = list()), # return a slot / AB, as a named vec, named with id (and can enforce a certain vec-type) get_member_with_idnames = function(member, astype) { @@ -685,12 +746,7 @@ ParamSet = R6Class("ParamSet", deep_clone = function(name, value) { switch(name, - .params = named_list(), - .deps = { - value = copy(value) - value$cond = lapply(value$cond, function(x) x$clone(deep = TRUE)) - value - }, + .deps = copy(value), .values = map(value, function(x) { # clones R6 objects in values, leave other things as they are @@ -710,9 +766,7 @@ ParamSet = R6Class("ParamSet", #' @export as.data.table.ParamSet = function(x, ...) { # nolint - punid = x$params_unid - tg = x$tags - map_dtr(punid, as.data.table)[, `:=`(id = names(punid), tags = tg)][] + x$data } #' @export diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 6fbd94d5..1789cce3 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -7,8 +7,6 @@ #' ".". Parameters from ParamSets with empty (i.e. `""`) `$set_id` are referenced #' directly. Multiple ParamSets with `$set_id` `""` can be combined, but their parameter names #' must be unique. -#' * Operation `subset` is currently not allowed. -#' * Operation `add` currently only works when adding complete sets not single params. #' * When you either ask for 'values' or set them, the operation is delegated to the individual, #' contained param set references. The collection itself does not maintain a `values` state. #' This also implies that if you directly change `values` in one of the referenced sets, @@ -30,6 +28,9 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' #' @param sets (`list()` of [ParamSet])\cr #' ParamSet objects are not cloned. + #' @param set_id (`character(1)`)\cr + #' `$set_id` of the resulting `ParamSet`. This determines the + #' prefix inside [`ParamSetCollection`]. Default `""` (no prefix). #' @param ignore_ids (`logical(1)`)\cr #' Ignore `$set_id` slots of `sets` and instead use the names instead. #' When this is `TRUE`, then `sets` should be named when id prefixes are desired. @@ -39,8 +40,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Also note that the `$values` will have undefined behavior when `sets` contains a #' single [`ParamSet`] multiple times (by reference). #' Default `FALSE`. - initialize = function(sets, ignore_ids = FALSE) { - + initialize = function(sets, set_id = "", ignore_ids = FALSE) { assert_list(sets, types = "ParamSet") if (!ignore_ids) { names(sets) = map(sets, "set_id") @@ -51,112 +51,26 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, assert_names(names2(named_sets), type = "strict") private$.tags = unlist(map(sets, "tags"), recursive = FALSE) %??% named_list() - - if (!ignore_ids) sets = unname(sets) # when private$.sets is unnamed, then the set_ids are used. private$.sets = sets - self$set_id = "" - pnames = names(self$params_unid) + if (!identical(set_id, "")) { + assert_id(set_id) + assert_names(set_id, type = "strict") + } + private$.set_id = set_id + pnames = names(self$params_unid) dups = duplicated(pnames) if (any(dups)) { stopf("ParamSetCollection would contain duplicated parameter names: %s", str_collapse(unique(pnames[dups]))) } - }, - - #' @description - #' Adds a set to this collection. - #' - #' @param p ([ParamSet]). - add = function(p) { - assert_r6(p, "ParamSet") - setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") - if (p$set_id == "") { - unnamed_set_parnames = map(private$.sets[setnames == ""], function(x) names(x$params_unid)) - } else if (p$set_id %in% setnames) { - stopf("Setid '%s' already present in collection!", p$set_id) - } - if (p$has_trafo) { - stop("Building a collection out sets, where a ParamSet has a trafo is currently unsupported!") - } - pnames = names(p$params_unid) - nameclashes = intersect( - ifelse(p$set_id != "", sprintf("%s.%s", p$set_id, pnames), pnames), - names(self$params_unid) - ) - if (length(nameclashes)) { - stopf("Adding parameter set would lead to nameclashes: %s", str_collapse(nameclashes)) - } - set_addition = list(p) - if (!is.null(names(private$.sets))) { - # ignoring the other ParamSet's set_id in favor of names(private$.sets), so add the name here as well. - names(set_addition) = p$set_id - } - - tagsaddition = p$tags - names(tagsaddition) = sprintf("%s.%s", p$set_id, names(tagsaddition)) - private$.tags = c(private$.tags, tagsaddition) - - private$.sets = c(private$.sets, set_addition) - invisible(self) - }, - - #' @description - #' Removes sets of given ids from collection. - #' - #' @param ids (`character()`). - remove_sets = function(ids) { - setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") - assert_subset(ids, setnames) - - droptagnames = pmap(list(setnames[setnames %in% ids], private$.sets[setnames %in% ids]), function(n, s) { - sprintf("%s.%s", n, names(s$tags)) - }) - private$.tags = private$.tags[!names(private$.tags) %in% droptagnames] - - private$.sets[setnames %in% ids] = NULL - invisible(self) - }, - - #' @description - #' Only included for consistency. Not allowed to perform on [ParamSetCollection]s. - #' - #' @param ids (`character()`). - subset = function(ids) stop("not allowed") - + } ), - active = list( - #' @template field_params - params = function(rhs) { - if (!missing(rhs) && !identical(rhs, private$.params)) { - stop("$params is read-only.") - } - # clone on-demand, whenever underlying class changed - punid = self$params_unid - truenames = names(punid) - - # 'changed' can be either because an underlying ParamSet's $params changed though $add or so, - # or because we are calling $params for the first time (in which case all are 'changed' - # as the original is NULL). - changed = as.logical(imap(punid, function(x, n) !identical(private$.params_cloned[[n]], x))) - - if (any(changed) || length(punid) != length(private$.params_cloned)) { # if there was a strict subset operation we wouldn't notice it otherwise - changed_names = truenames[changed] - private$.params = c(private$.params[setdiff(names(private$.params), changed_names)], # don't regenerate Params that were not changed. - sapply(changed_names, function(u) punid[[u]]$with_id(u), simplify = FALSE) - )[truenames] - } - private$.params_cloned = punid - private$.params - }, #' @template field_params_unid params_unid = function(v) { sets = private$.sets - if (is.null(names(sets))) { - names(sets) = map(private$.sets, "set_id") - } if (length(sets) == 0L) { return(named_list()) } @@ -168,10 +82,10 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, }, #' @template field_deps deps = function(v) { - sets = private$.sets - if (is.null(names(sets))) { - names(sets) = map(private$.sets, "set_id") + if (!missing(v)) { + stop("deps is read-only in ParamSetCollection.") } + sets = private$.sets d_all = imap(sets, function(s, id) { # copy all deps and rename ids to prefixed versions dd = s$deps @@ -232,11 +146,9 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, private = list( .sets = NULL, - .params_cloned = NULL, deep_clone = function(name, value) { switch(name, .params = named_list(), - .params_cloned = NULL, .deps = { value = copy(value) value$cond = lapply(value$cond, function(x) x$clone(deep = TRUE)) diff --git a/R/ParamUty.R b/R/ParamUty.R index 2f05dd1d..f0f44200 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -1,61 +1,27 @@ -#' @title Untyped Parameter -#' -#' @description -#' A [Param] to describe untyped parameters. -#' -#' @template param_id -#' @template param_default -#' @template param_tags -#' @template param_custom_check -#' -#' @family Params -#' @include Param.R -#' @export -#' @examples -#' ParamUty$new("untyped", default = Inf) -ParamUty = R6Class("ParamUty", inherit = Param, - public = list( - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - #' - #' @param custom_check (`function()`)\cr - #' Custom function to check the feasibility. - #' Function which checks the input. - #' Must return 'TRUE' if the input is valid and a string with the error message otherwise. - #' Defaults to `NULL`, which means that no check is performed. - initialize = function(id, default = NO_DEF, tags = character(), custom_check = NULL) { - # super class calls private$.check, so this must be set BEFORE - # we initialize the super class - if (is.null(custom_check)) { - private$.custom_check = function(x) TRUE - } else { - private$.custom_check = assert_function(custom_check, "x") - } - super$initialize(id, special_vals = list(), default = default, tags = tags) - } - ), - active = list( - #' @field custom_check (`function()`)\cr - #' Custom function to check the feasibility. - custom_check = function() private$.custom_check, - #' @template field_lower - lower = function() NA_real_, - #' @template field_upper - upper = function() NA_real_, - #' @template field_levels - levels = function() NULL, - #' @template field_nlevels - nlevels = function() Inf, - #' @template field_is_bounded - is_bounded = function() FALSE, - #' @template field_storage_type - storage_type = function() "list" - ), +#' @rdname Domain +#' @export +p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { + assert_function(custom_check, nargs = 1) + custom_check_result = custom_check(1) + assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") + domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, depends_expr = substitute(depends)) +} + +#' @export +domain_check.ParamUty = function(param, values, describe_error = TRUE) { + check_domain_vectorize(param$id, values, param$cargo, describe_error = describe_error) +} - private = list( - .check = function(x) private$.custom_check(x), - .qunif = function(x) stop("undefined"), - .custom_check = NULL - ) -) +#' @export +domain_storage_type.ParamUty = function(param) rep("list", nrow(param)) +#' @export +domain_nlevels.ParamUty = function(param) rep(Inf, nrow(param)) +#' @export +domain_is_bounded.ParamUty = function(param) rep(FALSE, nrow(param)) +#' @export +domain_is_number.ParamUty = function(param) rep(FALSE, nrow(param)) +#' @export +domain_is_categ.ParamUty = function(param) rep(FALSE, nrow(param)) +#' @export +domain_qunif.ParamUty = function(param, x) stop("undefined") diff --git a/R/Sampler1D.R b/R/Sampler1D.R index dc227944..6ee96668 100644 --- a/R/Sampler1D.R +++ b/R/Sampler1D.R @@ -17,27 +17,23 @@ Sampler1D = R6Class("Sampler1D", inherit = Sampler, # abstract base class #' Note that this object is typically constructed via derived classes, #' e.g., [Sampler1DUnif]. initialize = function(param) { - assert(check_r6(param, "Param"), check_r6(param, "ParamSet")) - if (inherits(param, "Param")) { - super$initialize(ParamSet$new(list(param))) - } else { - if (param$length != 1) stopf("param must contain exactly 1 Param, but contains %s", param$length) - super$initialize(param) - } + assert_r6(param, "ParamSet") + if (param$length != 1) stopf("param must contain exactly 1 Param, but contains %s", param$length) + super$initialize(param) } ), active = list( #' @field param ([Param])\cr #' Returns the one Parameter that is sampled from. - param = function() self$param_set$params[[1]] + param = function() self$param_set ), private = list( # create a 1-col-dt, named by param-id, from a data vector (from sampling), and enforce storage type as_dt_col = function(x) { x = as_type(x, self$param$storage_type) - set_names(data.table(x), self$param$id) + set_names(data.table(x), self$param$ids()) } ) ) @@ -57,12 +53,12 @@ Sampler1DUnif = R6Class("Sampler1DUnif", inherit = Sampler1D, #' Creates a new instance of this [R6][R6::R6Class] class. initialize = function(param) { super$initialize(param) - assert_param(self$param, no_untyped = TRUE, must_bounded = TRUE) + assert_param_set(self$param, no_untyped = TRUE, must_bounded = TRUE) } ), private = list( - .sample = function(n) private$as_dt_col(self$param$qunif(runif(n))) # sample by doing qunif(u) + .sample = function(n) private$as_dt_col(self$qunif(setnames(data.table(runif(n)), self$param$ids()))) # sample by doing qunif(u) ) ) @@ -95,7 +91,7 @@ Sampler1DRfun = R6Class("Sampler1DRfun", inherit = Sampler1D, #' `TRUE` enables naive rejection sampling, so we stay inside of \[lower, upper\]. initialize = function(param, rfun, trunc = TRUE) { super$initialize(param) - assert_param(self$param, "ParamDbl") + assert_param_set(self$param, "ParamDbl") assert_function(rfun, args = "n") assert_flag(trunc) self$rfun = rfun @@ -155,7 +151,7 @@ Sampler1DCateg = R6Class("Sampler1DCateg", inherit = Sampler1D, #' Numeric vector of `param$nlevels` probabilities, which is uniform by default. initialize = function(param, prob = NULL) { super$initialize(param) - assert_multi_class(self$param, c("ParamFct", "ParamLgl")) + assert_subset(self$param$class, c("ParamFct", "ParamLgl")) k = param$nlevels if (is.null(prob)) { prob = rep(1 / k, k) @@ -168,7 +164,7 @@ Sampler1DCateg = R6Class("Sampler1DCateg", inherit = Sampler1D, private = list( .sample = function(n) { - s = sample(self$param$levels, n, replace = TRUE, prob = self$prob) + s = sample(self$param$levels[[1]], n, replace = TRUE, prob = self$prob) super$as_dt_col(s) } ) @@ -198,7 +194,7 @@ Sampler1DNormal = R6Class("Sampler1DNormal", inherit = Sampler1DRfun, super$initialize(param, trunc = TRUE, # we always trunc, this should not hurt for unbounded params rfun = function(n) rnorm(n, mean = self$mean, sd = self$sd)) param = self$param - assert_param(param, "ParamDbl") + assert_param_set(param, "ParamDbl") if ((is.null(mean) || is.null(sd)) && !param$is_bounded) { stop("If 'mean' or 'sd' are not set, param must be bounded!") } diff --git a/R/SamplerJointIndep.R b/R/SamplerJointIndep.R index c26efd6f..ed49d468 100644 --- a/R/SamplerJointIndep.R +++ b/R/SamplerJointIndep.R @@ -22,8 +22,7 @@ SamplerJointIndep = R6Class("SamplerJointIndep", inherit = Sampler, self$samplers = samplers pss = map(samplers, "param_set") # FIXME: maybe we should use a paramset collection here? - pss[[1L]] = pss[[1]]$clone() # we need to clone, add will clone later, too, otherwise we change the 1set in place - self$param_set = Reduce(function(ps1, ps2) ps1$add(ps2), pss) + self$param_set = ps_union(pss) # must_bounded and untyped should be check by the sapler, or if the sampler still works, then ok assert_param_set(self$param_set, no_deps = TRUE) } diff --git a/R/SamplerUnif.R b/R/SamplerUnif.R index f7a80c26..6399c806 100644 --- a/R/SamplerUnif.R +++ b/R/SamplerUnif.R @@ -16,7 +16,7 @@ SamplerUnif = R6Class("SamplerUnif", inherit = SamplerHierarchical, #' Creates a new instance of this [R6][R6::R6Class] class. initialize = function(param_set) { assert_param_set(param_set, must_bounded = TRUE, no_deps = FALSE, no_untyped = TRUE) - samplers = lapply(param_set$params, Sampler1DUnif$new) + samplers = lapply(param_set$subspaces(), Sampler1DUnif$new) super$initialize(param_set, samplers) } ) diff --git a/R/asserts.R b/R/asserts.R index 2a520671..6d9bcb34 100644 --- a/R/asserts.R +++ b/R/asserts.R @@ -1,35 +1,22 @@ #' @title Assertions for Params and ParamSets #' -#' @param param ([Param]). +#' @param param_set ([ParamSet]). #' @param cl (`character()`)\cr #' Allowed subclasses. #' @param no_untyped (`logical(1)`)\cr #' Are untyped [Param]s allowed? #' @param must_bounded (`logical(1)`)\cr #' Only bounded [Param]s allowed? -#' -#' @return The checked object, invisibly. -#' @export -assert_param = function(param, cl = "Param", no_untyped = FALSE, must_bounded = FALSE) { - assert_multi_class(param, cl) - if (no_untyped && (param$class == "ParamUty")) { - stop("Param is untyped!") - } - if (must_bounded && !param$is_bounded) { - stop("Param is unbounded!") - } - invisible(param) -} - -#' @param param_set ([ParamSet]). #' @param no_deps (`logical(1)`)\cr #' Are dependencies allowed? -#' @rdname assert_param +#' @return The checked object, invisibly. #' @export -assert_param_set = function(param_set, cl = "Param", no_untyped = FALSE, must_bounded = FALSE, no_deps = FALSE) { +assert_param_set = function(param_set, cl = NULL, no_untyped = FALSE, must_bounded = FALSE, no_deps = FALSE) { assert_r6(param_set, "ParamSet") - assert_list(param_set$params, types = cl) - if (no_untyped && ("ParamUty" %in% param_set$class)) { + if (!is.null(cl)) { + if (!all(param_set$class %in% cl)) stopf("Only classes %s allowed", str_collapse(cl)) + } + if (no_untyped && !all(param_set$class %in% c("ParamLgl", "ParamInt", "ParamFct", "ParamDbl"))) { stop("ParamSet contains untyped params!") } if (must_bounded && !all(param_set$is_bounded)) { @@ -45,3 +32,8 @@ assert_param_set = function(param_set, cl = "Param", no_untyped = FALSE, must_bo assert_id = function(id) { assert_string(id, pattern = "^[[:alpha:]]+[[:alnum:]_.]*$") } + +# assert that we can use the string in list, tables, formulas +assert_ids = function(id) { + assert_character(id, pattern = "^[[:alpha:]]+[[:alnum:]_.]*$", any.missing = FALSE) +} diff --git a/R/domain.R b/R/domain.R index e5907bbb..578fbccf 100644 --- a/R/domain.R +++ b/R/domain.R @@ -117,98 +117,24 @@ #' @name Domain NULL -#' @rdname Domain -#' @export -p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, logscale = FALSE) { - if (assert_flag(logscale)) { - if (!is.null(trafo)) stop("When a trafo is given then logscale must be FALSE") - # assert_int will stop for `Inf` values - if (!isTRUE(is.infinite(lower))) assert_int(lower) - if (!isTRUE(is.infinite(upper))) assert_int(upper) - if (lower < 0) stop("When logscale is TRUE then lower bound must be greater or equal 0") - trafo = crate(function(x) as.integer(max(min(exp(x), upper), lower)), lower, upper) - constargs_override = list(lower = log(max(lower, 0.5)), upper = log(upper + 1)) - constructor = ParamDbl - } else { - constructor = ParamInt - constargs_override = NULL - } - - domain(constructor = constructor, constargs = as.list(match.call()[-1]), - depends_expr = substitute(depends), trafo = trafo, constargs_override = constargs_override) -} - -#' @rdname Domain -#' @export -p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE) { - if (assert_flag(logscale)) { - if (!is.null(trafo)) stop("When a trafo is given then logscale must be FALSE") - if (assert_number(lower) <= 0) stop("When logscale is TRUE then lower bound must be strictly greater than 0") - trafo = exp - constargs_override = list(lower = log(lower), upper = log(assert_number(upper))) - } else { - constargs_override = NULL - } - - domain(constructor = ParamDbl, constargs = as.list(match.call()[-1]), - depends_expr = substitute(depends), trafo = trafo, constargs_override = constargs_override) -} - -#' @rdname Domain -#' @export -p_uty = function(default = NO_DEF, tags = character(), custom_check = NULL, depends = NULL, trafo = NULL) { - domain(constructor = ParamUty, constargs = as.list(match.call()[-1]), - depends_expr = substitute(depends), trafo = trafo) -} - -#' @rdname Domain -#' @export -p_lgl = function(special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { - domain(constructor = ParamLgl, constargs = as.list(match.call()[-1]), - depends_expr = substitute(depends), trafo = trafo) -} - -#' @rdname Domain -#' @export -p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { - constargs = as.list(match.call()[-1]) - levels = eval.parent(constargs$levels) - if (!is.character(levels)) { - # if the "levels" argument is not a character vector, then - # we add a trafo. - assert(check_atomic_vector(levels), check_list(levels)) - if (is.null(names(levels))) { - names(levels) = as.character(levels) - } - trafo = crate(function(x) { - x = levels[[x]] - if (!is.null(trafo)) x = trafo(x) - x - }, trafo, levels) - constargs$levels = names(levels) - } - domain(constructor = ParamFct, constargs = constargs, depends_expr = substitute(depends), trafo = trafo) -} # Construct the actual `Domain` object # @param Constructor: The ParamXxx to call `$new()` for. # @param constargs: arguments of constructor # @param constargs_override: replace these in `constargs`, but don't represent this in printer -domain = function(constructor, constargs, depends_expr = NULL, trafo = NULL, constargs_override = NULL) { - constargs$trafo = NULL - constargs$depends = NULL - constargs = map(constargs, eval, envir = parent.frame(2)) - reprargs = constargs - - constargs$logscale = NULL - constargs = insert_named(constargs, constargs_override) - +domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real_, levels = NULL, special_vals = list(), default = NO_DEF, tags = character(0), + tolerance = NA_real_, trafo = NULL, depends_expr = NULL) { - if ("id" %in% names(constargs)) stop("id must not be given to p_xxx") + assert_string(cls) + assert_string(grouping) + assert_number(lower, na.ok = TRUE) + assert_number(upper, na.ok = TRUE) + # assert_character(levels, null.ok = TRUE) + assert_list(special_vals) + assert_character(tags, any.missing = FALSE, unique = TRUE) + assert_number(tolerance, na.ok = TRUE) + assert_function(trafo, null.ok = TRUE) - # check that `...` are valid by constructing and making sure this doesn't error - # The object generated here is thrown away, this is only for checks. - param = invoke(constructor$new, id = "ID", .args = constargs) # depends may be an expression, but may also be quote() or expression() if (length(depends_expr) == 1) { @@ -218,28 +144,53 @@ domain = function(constructor, constargs, depends_expr = NULL, trafo = NULL, con } } - # repr: what to print - repr = sys.call(-1) - traforep = repr$trafo + param = data.table(cls = cls, grouping = grouping, cargo = list(cargo), lower = lower, upper = upper, tolerance = tolerance, levels = list(levels), + special_vals = list(special_vals), + default = list(default), tags = list(tags), trafo = list(trafo), requirements = list(parse_depends(depends_expr, parent.frame(2)))) + class(param) = c(cls, "Domain", class(param)) - repr = as.call(c(as.list(repr)[[1]], reprargs)) # use cleaned up constargs - repr$depends = depends_expr # put `depends` at the end, but only if not NULL - repr$trafo = traforep # put `trafo` at the end, but only if not NULL - if (isTRUE(repr$logscale)) repr$trafo = NULL # when the user declared logscale then the trafo stays hidden. + if (!is_nodefault(default)) { + param_assert(param, default) + } - set_class(list( - param = param, - trafo = assert_function(trafo, null.ok = TRUE), - requirements = parse_depends(depends_expr, parent.frame(2)), - repr = repr - ), "Domain") + # repr: what to print + constructorcall = match.call(sys.function(-1), sys.call(-1)) + trafoexpr = constructorcall$trafo + constructorcall$trafo = NULL + constructorcall$depends = NULL + reprargs = sapply(names(constructorcall)[-1], get, pos = parent.frame(1), simplify = FALSE) + reprargs$depends = depends_expr + reprargs$trafo = trafoexpr + if (isTRUE(reprargs$logscale)) reprargs$trafo = NULL + attr(param, "repr") = as.call(c(constructorcall[[1]], reprargs)) + param$id = repr(attr(param, "repr")) # some ID for consistency with ParamSet$params, only for error messages. + param } #' @export print.Domain = function(x, ...) { - print(x$repr) + repr = attr(x, "repr") + if (!is.null(repr)) { + print(repr) + } else { + plural_rows = + classes = class(x) + if ("Domain" %in% classes) { + domainidx = which("Domain" == classes)[[1]] + classes = first(classes, domainidx - 1) + class(x) = last(class(x), -domainidx) + } + catf("Param%s of class%s %s:\n", + if (NROW(x) > 1) "s" else "", + if (length(classes) > 1) "es" else "", + str_collapse(classes, sep = ", ", quote = '"') + ) + print(x) + } } + + # Parse the expression for requirements, as they are given to p_int, p_dbl etc. # We allow `==`, `%in%` `&&`, and `(`/`)` to occur in such expressions. # We construct a list of `Condition` objects with an additional `on` element of what diff --git a/R/generate_design_grid.R b/R/generate_design_grid.R index 8b887194..19c4c6ef 100644 --- a/R/generate_design_grid.R +++ b/R/generate_design_grid.R @@ -54,7 +54,7 @@ generate_design_grid = function(param_set, resolution = NULL, param_resolutions # generate regular grid from 0,1 then map it to the values of the param, # then do a crossproduct grid_vec = lapply(par_res, function(r) seq(0, 1, length.out = r)) - res = imap(grid_vec, function(value, id) param_set$params[[id]]$qunif(x = value)) + res = imap(grid_vec, function(value, id) param_set$qunif(setnames(data.table(value), id))) res = cross_join(res, sorted = FALSE) Design$new(param_set, res, remove_dupl = TRUE) # user wants no dupls, remove } diff --git a/R/generate_design_lhs.R b/R/generate_design_lhs.R index 1d01fd14..b8ac1f88 100644 --- a/R/generate_design_lhs.R +++ b/R/generate_design_lhs.R @@ -40,6 +40,6 @@ generate_design_lhs = function(param_set, n, lhs_fun = NULL) { d = lhs_fun(n, k = param_set$length) } colnames(d) = ids - d = map_dtc(ids, function(id) param_set$params[[id]]$qunif(d[, id])) - Design$new(param_set, set_names(d, ids), remove_dupl = FALSE) # user wants n-points, dont remove + d = param_set$qunif(d) + Design$new(param_set, d, remove_dupl = FALSE) # user wants n-points, dont remove } diff --git a/R/helper.R b/R/helper.R index e7dc1aa8..2fb87f28 100644 --- a/R/helper.R +++ b/R/helper.R @@ -187,3 +187,25 @@ as_type = function(x, type) { stopf("Invalid storage type '%s'", type) ) } + +# param: +check_domain_vectorize = function(ids, values, checker, more_args = list(), describe_error = TRUE) { + break_early = FALSE + if (length(checker) == length(values)) { + errors = imap(c(list(ids, values), more_args), function(id, value, ...) { + if (break_early) return(FALSE) + ch = checker(value, ...) + if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", ch) else break_early <<- TRUE # nolint + }) + } else { + errors = imap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { + if (break_early) return(FALSE) + ch = chck(value, ...) + if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", ch) else break_early <<- TRUE # nolint + }) + } + if (!describe_error) return(!break_early) + errors = unlist(errors) + if (!length(errors)) return(TRUE) + str_collapse(errors, sep = "\n") +} diff --git a/R/ps.R b/R/ps.R index 73a64e90..1370b21c 100644 --- a/R/ps.R +++ b/R/ps.R @@ -59,52 +59,5 @@ #' @family ParamSet construction helpers #' @export ps = function(..., .extra_trafo = NULL, .allow_dangling_dependencies = FALSE) { - args = list(...) - assert_list(args, names = "unique", types = c("Param", "Domain")) - assert_function(.extra_trafo, null.ok = TRUE) - - # Extract Param from Domain objects - params = imap(args, function(p, name) { - if (inherits(p, "Param")) p else p$param - }) - - paramset = ParamSet$new(params, ignore_ids = length(params) > 0) # if length is 0 then no names are present - - # add Dependencies - imap(args, function(p, name) { - if (inherits(p, "Param") || is.null(p$requirements)) return(NULL) - map(p$requirements, function(req) { - if (!req$on %in% names(args) || req$on == name) { - if (.allow_dangling_dependencies) { - if (name == req$on) stop("A param cannot depend on itself!") - paramset$deps = rbind(paramset$deps, data.table(id = name, on = req$on, cond = list(req$cond))) - } else { - stopf("Parameter %s can not depend on %s.", name, req$on) - } - } else { - invoke(paramset$add_dep, id = name, .args = req) - } - }) - }) - - # add trafos - trafos = map(discard(args, function(x) inherits(x, "Param") || is.null(x$trafo)), - function(p) { - assert_function(p$trafo) - }) - if (length(trafos) || !is.null(.extra_trafo)) { - # the $trafo function iterates through the trafos and applies them - # We put the $trafo in a crate() (helper.R) to avoid having a function - # with lots of things in its environment. - paramset$trafo = crate(function(x, param_set) { - for (trafoing in names(trafos)) { - if (!is.null(x[[trafoing]])) { - x[[trafoing]] = trafos[[trafoing]](x[[trafoing]]) - } - } - if (!is.null(.extra_trafo)) x = .extra_trafo(x, param_set) - x - }, trafos, .extra_trafo) - } - paramset + ParamSet$new(list(...), extra_trafo = .extra_trafo, allow_dangling_dependencies = .allow_dangling_dependencies) # if length is 0 then no names are present } diff --git a/attic/ParamSet_add.R b/attic/ParamSet_add.R new file mode 100644 index 00000000..de160f6a --- /dev/null +++ b/attic/ParamSet_add.R @@ -0,0 +1,73 @@ + + #' @description + #' Adds a single param or another set to this set, all params are cloned on-demand, + #' so the input does not need to be cloned. + #' + #' @param p ([Param] | [ParamSet]). + add = function(p) { + + assert_multi_class(p, c("Param", "ParamSet")) + if (inherits(p, "Param")) { # level-up param to set + pparams = structure(list(p), names = p$id) + if (!is.null(private$.tags)) ptags = structure(list(p$param_tags), names = p$id) + ptrafo = NULL + pvalues = NULL + pdeps = NULL + } else { + pparams = p$params_unid + if (!is.null(private$.tags)) ptags = p$tags + ptrafo = p$trafo + pvalues = p$values + pdeps = p$deps + } + + nn = c(names(private$.params_unid), names(pparams)) + assert_names(nn, type = "strict") + if (!is.null(ptrafo)) { + stop("Cannot add a param set with a trafo.") + } + private$.params_unid = c(private$.params_unid, pparams) + private$.values = c(private$.values, pvalues) + if (!is.null(private$.tags)) private$.tags = c(private$.tags, ptags) + private$.deps = rbind(private$.deps, pdeps) + invisible(self) + }, + +############## ParamSetCollection + + #' @description + #' Adds a set to this collection. + #' + #' @param p ([ParamSet]). + add = function(p) { + assert_r6(p, "ParamSet") + setnames = names(private$.sets) %??% map_chr(private$.sets, "set_id") + if (p$set_id == "") { + unnamed_set_parnames = map(private$.sets[setnames == ""], function(x) names(x$params_unid)) + } else if (p$set_id %in% setnames) { + stopf("Setid '%s' already present in collection!", p$set_id) + } + if (p$has_trafo) { + stop("Building a collection out sets, where a ParamSet has a trafo is currently unsupported!") + } + pnames = names(p$params_unid) + nameclashes = intersect( + ifelse(p$set_id != "", sprintf("%s.%s", p$set_id, pnames), pnames), + names(self$params_unid) + ) + if (length(nameclashes)) { + stopf("Adding parameter set would lead to nameclashes: %s", str_collapse(nameclashes)) + } + set_addition = list(p) + if (!is.null(names(private$.sets))) { + # ignoring the other ParamSet's set_id in favor of names(private$.sets), so add the name here as well. + names(set_addition) = p$set_id + } + + tagsaddition = p$tags + names(tagsaddition) = sprintf("%s.%s", p$set_id, names(tagsaddition)) + private$.tags = c(private$.tags, tagsaddition) + + private$.sets = c(private$.sets, set_addition) + invisible(self) + }, diff --git a/man/Domain.Rd b/man/Domain.Rd index 7a0b03ce..b22b1053 100644 --- a/man/Domain.Rd +++ b/man/Domain.Rd @@ -1,26 +1,38 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/domain.R -\name{Domain} -\alias{Domain} -\alias{p_int} +% Please edit documentation in R/ParamDbl.R, R/ParamFct.R, R/ParamInt.R, +% R/ParamLgl.R, R/ParamS6.R, R/ParamUty.R, R/domain.R +\name{p_dbl} \alias{p_dbl} -\alias{p_uty} -\alias{p_lgl} \alias{p_fct} +\alias{p_int} +\alias{p_lgl} +\alias{p_s6} +\alias{p_uty} +\alias{Domain} \title{Domain: Parameter Range without an Id} \usage{ -p_int( +p_dbl( lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), + tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE ) -p_dbl( +p_fct( + levels, + special_vals = list(), + default = NO_DEF, + tags = character(), + depends = NULL, + trafo = NULL +) + +p_int( lower = -Inf, upper = Inf, special_vals = list(), @@ -32,15 +44,16 @@ p_dbl( logscale = FALSE ) -p_uty( +p_lgl( + special_vals = list(), default = NO_DEF, tags = character(), - custom_check = NULL, depends = NULL, trafo = NULL ) -p_lgl( +p_s6( + support, special_vals = list(), default = NO_DEF, tags = character(), @@ -48,8 +61,8 @@ p_lgl( trafo = NULL ) -p_fct( - levels, +p_uty( + custom_check = NULL, special_vals = list(), default = NO_DEF, tags = character(), @@ -87,6 +100,9 @@ purpose:\cr \code{values} in \link{ParamSet}. }} +\item{tolerance}{(\code{numeric(1)})\cr +Initializes the \verb{$tolerance} field that determines the} + \item{depends}{(\code{call} | \code{expression})\cr An expression indicating a requirement for the parameter that will be constructed from this. Can be given as an expression (using \code{quote()}), or the expression can be entered directly and will be parsed using NSE (see @@ -123,8 +139,10 @@ defining domains or hyperparameter ranges of learning algorithms, because these \code{logscale} happens on a natural (\verb{e == 2.718282...}) basis. Be aware that using a different base (\code{log10()}/\verb{10^}, \code{log2()}/\verb{2^}) is completely equivalent and does not change the values being sampled after transformation.} -\item{tolerance}{(\code{numeric(1)})\cr -Initializes the \verb{$tolerance} field that determines the} +\item{levels}{(\code{character} | \code{atomic} | \code{list})\cr +Allowed categorical values of the parameter. If this is not a \code{character}, then a \code{trafo} is generated that +converts the names (if not given: \code{as.character()} of the values) of the \code{levels} argument to the values. +This trafo is then performed \emph{before} the function given as the \code{trafo} argument.} \item{custom_check}{(\verb{function()})\cr Custom function to check the feasibility. @@ -132,11 +150,6 @@ Function which checks the input. Must return 'TRUE' if the input is valid and a \code{character(1)} with the error message otherwise. This function should \emph{not} throw an error. Defaults to \code{NULL}, which means that no check is performed.} - -\item{levels}{(\code{character} | \code{atomic} | \code{list})\cr -Allowed categorical values of the parameter. If this is not a \code{character}, then a \code{trafo} is generated that -converts the names (if not given: \code{as.character()} of the values) of the \code{levels} argument to the values. -This trafo is then performed \emph{before} the function given as the \code{trafo} argument.} } \value{ A \code{Domain} object. diff --git a/man/NO_DEF.Rd b/man/NO_DEF.Rd index 3051f92c..13858315 100644 --- a/man/NO_DEF.Rd +++ b/man/NO_DEF.Rd @@ -9,8 +9,7 @@ Special new data type for no-default. Not often needed by the end-user, mainly internal. \itemize{ -\item \code{NoDefault}: R6 factory. -\item \code{NO_DEF}: R6 Singleton object for type, used in \link{Param}. -\item \code{is_nodefault()}: Is an object of type 'no default'? +\item \code{NO_DEF}: Singleton object for type, used in \link{Param}. +\item \code{is_nodefault()}: Is an object the 'no default' object? } } diff --git a/man/Param.Rd b/man/Param.Rd deleted file mode 100644 index ec653e7e..00000000 --- a/man/Param.Rd +++ /dev/null @@ -1,345 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/Param.R -\name{Param} -\alias{Param} -\title{Param Class} -\description{ -This is the abstract base class for parameter objects like \link{ParamDbl} and -\link{ParamFct}. -} -\section{S3 methods}{ - -\itemize{ -\item \code{as.data.table()}\cr -\link{Param} -> \code{\link[data.table:data.table]{data.table::data.table()}}\cr -Converts param to \code{\link[data.table:data.table]{data.table::data.table()}} with 1 row. See \link{ParamSet}. -} -} - -\seealso{ -Other Params: -\code{\link{ParamDbl}}, -\code{\link{ParamFct}}, -\code{\link{ParamInt}}, -\code{\link{ParamLgl}}, -\code{\link{ParamUty}} -} -\concept{Params} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{special_vals}}{(\code{list()})\cr -Arbitrary special values this parameter is allowed to take.} - -\item{\code{default}}{(\code{any})\cr -Default value.} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters.} - -\item{\code{class}}{(\code{character(1)})\cr -R6 class name. Read-only.} - -\item{\code{is_number}}{(\code{logical(1)})\cr -\code{TRUE} if the parameter is of type \code{"dbl"} or \code{"int"}.} - -\item{\code{is_categ}}{(\code{logical(1)})\cr -\code{TRUE} if the parameter is of type \code{"fct"} or \code{"lgl"}.} - -\item{\code{has_default}}{(\code{logical(1)})\cr -Is there a default value?} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{Param$new()}} -\item \href{#method-check}{\code{Param$check()}} -\item \href{#method-assert}{\code{Param$assert()}} -\item \href{#method-test}{\code{Param$test()}} -\item \href{#method-rep}{\code{Param$rep()}} -\item \href{#method-with_tags}{\code{Param$with_tags()}} -\item \href{#method-with_id}{\code{Param$with_id()}} -\item \href{#method-format}{\code{Param$format()}} -\item \href{#method-print}{\code{Param$print()}} -\item \href{#method-qunif}{\code{Param$qunif()}} -\item \href{#method-convert}{\code{Param$convert()}} -\item \href{#method-clone}{\code{Param$clone()}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. - -Note that this object is typically constructed via derived classes, -e.g., \link{ParamDbl}. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$new(id, special_vals, default, tags)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{special_vals}}{(\code{list()})\cr -Arbitrary special values this parameter is allowed to take, to make it -feasible. This allows extending the domain of the parameter. Note that -these values are only used in feasibility checks, neither in generating -designs nor sampling.} - -\item{\code{default}}{(\code{any})\cr -Default value. Can be from the domain of the parameter or an element of -\code{special_vals}. Has value \link{NO_DEF} if no default exists. \code{NULL} can be a -valid default. -The value has no effect on \code{ParamSet$values} or the behavior of -\code{ParamSet$check()}, \verb{$test()} or \verb{$assert()}. -The \code{default} is intended to be used for documentation purposes. -`} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters. Some tags serve a special -purpose:\cr -\itemize{ -\item \code{"required"} implies that the parameters has to be given when setting -\code{values} in \link{ParamSet}. -}} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check}{}}} -\subsection{Method \code{check()}}{ -\pkg{checkmate}-like check-function. Take a value from the domain of the -parameter, and check if it is feasible. A value is feasible if it is of -the same \code{storage_type}, inside of the bounds or element of -\code{special_vals}. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$check(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{any}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -If successful \code{TRUE}, if not a string with the error message. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-assert}{}}} -\subsection{Method \code{assert()}}{ -\pkg{checkmate}-like assert-function. Take a value from the domain of -the parameter, and assert if it is feasible. A value is feasible if it -is of the same \code{storage_type}, inside of the bounds or element of -\code{special_vals}. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$assert(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{any}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -If successful \code{x} invisibly, if not an exception is raised. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test}{}}} -\subsection{Method \code{test()}}{ -\pkg{checkmate}-like test-function. Take a value from the domain of the -parameter, and test if it is feasible. A value is feasible if it is of -the same \code{storage_type}, inside of the bounds or element of -\code{special_vals}. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$test(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{any}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -If successful \code{TRUE}, if not \code{FALSE}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-rep}{}}} -\subsection{Method \code{rep()}}{ -Repeats this parameter n-times (by cloning). -Each parameter is named "[id]\emph{rep}[k]" and gets the additional tag "[id]_rep". -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$rep(n)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{n}}{(\code{integer(1)}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -\code{\link{ParamSet}}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-with_tags}{}}} -\subsection{Method \code{with_tags()}}{ -Creates a clone of this parameter with changed \verb{$tags} slot. -If the tags do not change, no clone is created. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$with_tags(tags)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{tags}}{(\code{character}) tags of the clone.} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -\code{\link{Param}}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-with_id}{}}} -\subsection{Method \code{with_id()}}{ -Creates a clone of this parameter with changed \verb{$id} slot. -If the \code{id} does not change, no clone is created. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$with_id(id)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)}) id of the clone.} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -\code{\link{Param}}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-format}{}}} -\subsection{Method \code{format()}}{ -Helper for print outputs. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$format()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} -\subsection{Method \code{print()}}{ -Printer. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$print( - ..., - hide_cols = c("nlevels", "is_bounded", "special_vals", "tags", "storage_type") -)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{...}}{(ignored).} - -\item{\code{hide_cols}}{(\code{character()})\cr -Which fields should not be printed? Default is \code{"nlevels"}, -\code{"is_bounded"}, \code{"special_vals"}, \code{"tags"}, and \code{"storage_type"}.} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-qunif}{}}} -\subsection{Method \code{qunif()}}{ -Takes values from [0,1] and maps them, regularly distributed, to the -domain of the parameter. Think of: quantile function or the use case to -map a uniform-[0,1] random variable into a uniform sample from this -param. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$qunif(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{numeric(1)}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -Value of the domain of the parameter. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-convert}{}}} -\subsection{Method \code{convert()}}{ -Converts a value to the closest valid param. Only for values that -pass \verb{$check()} and mostly used internally. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$convert(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{any}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -\code{x} converted to a valid type for the \code{Param}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Param$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/ParamDbl.Rd b/man/ParamDbl.Rd deleted file mode 100644 index 68cb0e5a..00000000 --- a/man/ParamDbl.Rd +++ /dev/null @@ -1,188 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ParamDbl.R -\name{ParamDbl} -\alias{ParamDbl} -\title{Numerical Parameter} -\description{ -A \link{Param} to describe real-valued parameters. -} -\note{ -The upper and lower bounds in \verb{$check()} are expanded by -\code{sqrt(.Machine$double.eps)} to prevent errors due to the precision of double -values. -} -\examples{ -ParamDbl$new("ratio", lower = 0, upper = 1, default = 0.5) -} -\seealso{ -Other Params: -\code{\link{ParamFct}}, -\code{\link{ParamInt}}, -\code{\link{ParamLgl}}, -\code{\link{ParamUty}}, -\code{\link{Param}} -} -\concept{Params} -\section{Super class}{ -\code{\link[paradox:Param]{paradox::Param}} -> \code{ParamDbl} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{tolerance}}{(\code{numeric(1)})\cr -tolerance of values to accept beyond \verb{$lower} and \verb{$upper}. -Used both for relative and absolute tolerance.} - -\item{\code{levels}}{(\code{character()} | \code{NULL})\cr -Set of allowed levels. -Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. -Always \code{c(TRUE, FALSE)} for \link{ParamLgl}.} - -\item{\code{nlevels}}{(\code{integer(1)} | \code{Inf})\cr -Number of categorical levels. -Always \code{Inf} for \link{ParamDbl} and \link{ParamUty}. -The number of integers in the range \verb{[lower, upper]}, or \code{Inf} if unbounded for \link{ParamInt}. -Always \code{2} for \link{ParamLgl}.} - -\item{\code{is_bounded}}{(\code{logical(1)})\cr -Are the bounds finite? -Always \code{TRUE} for \link{ParamFct} and \link{ParamLgl}. -Always \code{FALSE} for \link{ParamUty}.} - -\item{\code{storage_type}}{(\code{character(1)})\cr -Data type when values of this parameter are stored in a data table or sampled. -Always \code{"numeric"} for \link{ParamDbl}. -Always \code{"character"} for \link{ParamFct}. -Always \code{"integer"} for \link{ParamInt}. -Always \code{"logical"} for \link{ParamLgl}. -Always \code{"list"} for \link{ParamUty}.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{ParamDbl$new()}} -\item \href{#method-convert}{\code{ParamDbl$convert()}} -\item \href{#method-clone}{\code{ParamDbl$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../paradox/html/Param.html#method-assert}{\code{paradox::Param$assert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-check}{\code{paradox::Param$check()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-format}{\code{paradox::Param$format()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-print}{\code{paradox::Param$print()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} -} -\out{
} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamDbl$new( - id, - lower = -Inf, - upper = Inf, - special_vals = list(), - default = NO_DEF, - tags = character(), - tolerance = sqrt(.Machine$double.eps) -)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound, can be \code{-Inf}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound can be \code{+Inf}.} - -\item{\code{special_vals}}{(\code{list()})\cr -Arbitrary special values this parameter is allowed to take, to make it -feasible. This allows extending the domain of the parameter. Note that -these values are only used in feasibility checks, neither in generating -designs nor sampling.} - -\item{\code{default}}{(\code{any})\cr -Default value. Can be from the domain of the parameter or an element of -\code{special_vals}. Has value \link{NO_DEF} if no default exists. \code{NULL} can be a -valid default. -The value has no effect on \code{ParamSet$values} or the behavior of -\code{ParamSet$check()}, \verb{$test()} or \verb{$assert()}. -The \code{default} is intended to be used for documentation purposes. -`} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters. Some tags serve a special -purpose:\cr -\itemize{ -\item \code{"required"} implies that the parameters has to be given when setting -\code{values} in \link{ParamSet}. -}} - -\item{\code{tolerance}}{(\code{numeric(1)})\cr -Initializes the \verb{$tolerance} field that determines the} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-convert}{}}} -\subsection{Method \code{convert()}}{ -Restrict the value to within the allowed range. This works -in conjunction with \verb{$tolerance}, which accepts values -slightly out of this range. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamDbl$convert(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{numeric(1)})\cr -Value to convert.} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamDbl$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/ParamFct.Rd b/man/ParamFct.Rd deleted file mode 100644 index e54aaf17..00000000 --- a/man/ParamFct.Rd +++ /dev/null @@ -1,151 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ParamFct.R -\name{ParamFct} -\alias{ParamFct} -\title{Factor Parameter} -\description{ -A \link{Param} to describe categorical (factor) parameters. -} -\examples{ -ParamFct$new("f", levels = letters[1:3]) -} -\seealso{ -Other Params: -\code{\link{ParamDbl}}, -\code{\link{ParamInt}}, -\code{\link{ParamLgl}}, -\code{\link{ParamUty}}, -\code{\link{Param}} -} -\concept{Params} -\section{Super class}{ -\code{\link[paradox:Param]{paradox::Param}} -> \code{ParamFct} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{levels}}{(\code{character()} | \code{NULL})\cr -Set of allowed levels. -Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. -Always \code{c(TRUE, FALSE)} for \link{ParamLgl}.} - -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{nlevels}}{(\code{integer(1)} | \code{Inf})\cr -Number of categorical levels. -Always \code{Inf} for \link{ParamDbl} and \link{ParamUty}. -The number of integers in the range \verb{[lower, upper]}, or \code{Inf} if unbounded for \link{ParamInt}. -Always \code{2} for \link{ParamLgl}.} - -\item{\code{is_bounded}}{(\code{logical(1)})\cr -Are the bounds finite? -Always \code{TRUE} for \link{ParamFct} and \link{ParamLgl}. -Always \code{FALSE} for \link{ParamUty}.} - -\item{\code{storage_type}}{(\code{character(1)})\cr -Data type when values of this parameter are stored in a data table or sampled. -Always \code{"numeric"} for \link{ParamDbl}. -Always \code{"character"} for \link{ParamFct}. -Always \code{"integer"} for \link{ParamInt}. -Always \code{"logical"} for \link{ParamLgl}. -Always \code{"list"} for \link{ParamUty}.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{ParamFct$new()}} -\item \href{#method-clone}{\code{ParamFct$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../paradox/html/Param.html#method-assert}{\code{paradox::Param$assert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-check}{\code{paradox::Param$check()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-convert}{\code{paradox::Param$convert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-format}{\code{paradox::Param$format()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-print}{\code{paradox::Param$print()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} -} -\out{
} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamFct$new( - id, - levels, - special_vals = list(), - default = NO_DEF, - tags = character() -)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{levels}}{(\code{character()})\cr -Set of allowed levels.} - -\item{\code{special_vals}}{(\code{list()})\cr -Arbitrary special values this parameter is allowed to take, to make it -feasible. This allows extending the domain of the parameter. Note that -these values are only used in feasibility checks, neither in generating -designs nor sampling.} - -\item{\code{default}}{(\code{any})\cr -Default value. Can be from the domain of the parameter or an element of -\code{special_vals}. Has value \link{NO_DEF} if no default exists. \code{NULL} can be a -valid default. -The value has no effect on \code{ParamSet$values} or the behavior of -\code{ParamSet$check()}, \verb{$test()} or \verb{$assert()}. -The \code{default} is intended to be used for documentation purposes. -`} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters. Some tags serve a special -purpose:\cr -\itemize{ -\item \code{"required"} implies that the parameters has to be given when setting -\code{values} in \link{ParamSet}. -}} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamFct$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/ParamInt.Rd b/man/ParamInt.Rd deleted file mode 100644 index c1129f8c..00000000 --- a/man/ParamInt.Rd +++ /dev/null @@ -1,178 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ParamInt.R -\name{ParamInt} -\alias{ParamInt} -\title{Integer Parameter} -\description{ -A \link{Param} to describe integer parameters. -} -\section{Methods}{ - -See \link{Param}. -} - -\examples{ -ParamInt$new("count", lower = 0, upper = 10, default = 1) -} -\seealso{ -Other Params: -\code{\link{ParamDbl}}, -\code{\link{ParamFct}}, -\code{\link{ParamLgl}}, -\code{\link{ParamUty}}, -\code{\link{Param}} -} -\concept{Params} -\section{Super class}{ -\code{\link[paradox:Param]{paradox::Param}} -> \code{ParamInt} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{levels}}{(\code{character()} | \code{NULL})\cr -Set of allowed levels. -Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. -Always \code{c(TRUE, FALSE)} for \link{ParamLgl}.} - -\item{\code{nlevels}}{(\code{integer(1)} | \code{Inf})\cr -Number of categorical levels. -Always \code{Inf} for \link{ParamDbl} and \link{ParamUty}. -The number of integers in the range \verb{[lower, upper]}, or \code{Inf} if unbounded for \link{ParamInt}. -Always \code{2} for \link{ParamLgl}.} - -\item{\code{is_bounded}}{(\code{logical(1)})\cr -Are the bounds finite? -Always \code{TRUE} for \link{ParamFct} and \link{ParamLgl}. -Always \code{FALSE} for \link{ParamUty}.} - -\item{\code{storage_type}}{(\code{character(1)})\cr -Data type when values of this parameter are stored in a data table or sampled. -Always \code{"numeric"} for \link{ParamDbl}. -Always \code{"character"} for \link{ParamFct}. -Always \code{"integer"} for \link{ParamInt}. -Always \code{"logical"} for \link{ParamLgl}. -Always \code{"list"} for \link{ParamUty}.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{ParamInt$new()}} -\item \href{#method-convert}{\code{ParamInt$convert()}} -\item \href{#method-clone}{\code{ParamInt$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../paradox/html/Param.html#method-assert}{\code{paradox::Param$assert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-check}{\code{paradox::Param$check()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-format}{\code{paradox::Param$format()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-print}{\code{paradox::Param$print()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} -} -\out{
} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamInt$new( - id, - lower = -Inf, - upper = Inf, - special_vals = list(), - default = NO_DEF, - tags = character() -)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound, can be \code{-Inf}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound can be \code{+Inf}.} - -\item{\code{special_vals}}{(\code{list()})\cr -Arbitrary special values this parameter is allowed to take, to make it -feasible. This allows extending the domain of the parameter. Note that -these values are only used in feasibility checks, neither in generating -designs nor sampling.} - -\item{\code{default}}{(\code{any})\cr -Default value. Can be from the domain of the parameter or an element of -\code{special_vals}. Has value \link{NO_DEF} if no default exists. \code{NULL} can be a -valid default. -The value has no effect on \code{ParamSet$values} or the behavior of -\code{ParamSet$check()}, \verb{$test()} or \verb{$assert()}. -The \code{default} is intended to be used for documentation purposes. -`} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters. Some tags serve a special -purpose:\cr -\itemize{ -\item \code{"required"} implies that the parameters has to be given when setting -\code{values} in \link{ParamSet}. -}} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-convert}{}}} -\subsection{Method \code{convert()}}{ -Converts a value to an integer. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamInt$convert(x)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{numeric(1)})\cr -Value to convert.} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamInt$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/ParamLgl.Rd b/man/ParamLgl.Rd deleted file mode 100644 index d122f5dc..00000000 --- a/man/ParamLgl.Rd +++ /dev/null @@ -1,142 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ParamLgl.R -\name{ParamLgl} -\alias{ParamLgl} -\title{Logical Parameter} -\description{ -A \link{Param} to describe logical parameters. -} -\examples{ -ParamLgl$new("flag", default = TRUE) -} -\seealso{ -Other Params: -\code{\link{ParamDbl}}, -\code{\link{ParamFct}}, -\code{\link{ParamInt}}, -\code{\link{ParamUty}}, -\code{\link{Param}} -} -\concept{Params} -\section{Super class}{ -\code{\link[paradox:Param]{paradox::Param}} -> \code{ParamLgl} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{levels}}{(\code{character()} | \code{NULL})\cr -Set of allowed levels. -Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. -Always \code{c(TRUE, FALSE)} for \link{ParamLgl}.} - -\item{\code{nlevels}}{(\code{integer(1)} | \code{Inf})\cr -Number of categorical levels. -Always \code{Inf} for \link{ParamDbl} and \link{ParamUty}. -The number of integers in the range \verb{[lower, upper]}, or \code{Inf} if unbounded for \link{ParamInt}. -Always \code{2} for \link{ParamLgl}.} - -\item{\code{is_bounded}}{(\code{logical(1)})\cr -Are the bounds finite? -Always \code{TRUE} for \link{ParamFct} and \link{ParamLgl}. -Always \code{FALSE} for \link{ParamUty}.} - -\item{\code{storage_type}}{(\code{character(1)})\cr -Data type when values of this parameter are stored in a data table or sampled. -Always \code{"numeric"} for \link{ParamDbl}. -Always \code{"character"} for \link{ParamFct}. -Always \code{"integer"} for \link{ParamInt}. -Always \code{"logical"} for \link{ParamLgl}. -Always \code{"list"} for \link{ParamUty}.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{ParamLgl$new()}} -\item \href{#method-clone}{\code{ParamLgl$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../paradox/html/Param.html#method-assert}{\code{paradox::Param$assert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-check}{\code{paradox::Param$check()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-convert}{\code{paradox::Param$convert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-format}{\code{paradox::Param$format()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-print}{\code{paradox::Param$print()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} -} -\out{
} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamLgl$new(id, special_vals = list(), default = NO_DEF, tags = character())}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{special_vals}}{(\code{list()})\cr -Arbitrary special values this parameter is allowed to take, to make it -feasible. This allows extending the domain of the parameter. Note that -these values are only used in feasibility checks, neither in generating -designs nor sampling.} - -\item{\code{default}}{(\code{any})\cr -Default value. Can be from the domain of the parameter or an element of -\code{special_vals}. Has value \link{NO_DEF} if no default exists. \code{NULL} can be a -valid default. -The value has no effect on \code{ParamSet$values} or the behavior of -\code{ParamSet$check()}, \verb{$test()} or \verb{$assert()}. -The \code{default} is intended to be used for documentation purposes. -`} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters. Some tags serve a special -purpose:\cr -\itemize{ -\item \code{"required"} implies that the parameters has to be given when setting -\code{values} in \link{ParamSet}. -}} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamLgl$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 5232f6fa..433d43a3 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -62,23 +62,12 @@ Default is \code{TRUE}, only switch this off if you know what you are doing.} \item{\code{params}}{(named \code{list()})\cr List of \link{Param}, named with their respective ID.} -\item{\code{params_unid}}{(named \code{list()})\cr -List of \link{Param}, named with their true ID. However, -this field has the \link{Param}'s \verb{$id} value set to a -potentially invalid value, and it is furthermore not -cloned when the \code{ParamSet} is cloned. This active -binding should be used internally, or to avoid unnecessary -cloning when that becomes a bottleneck.} - \item{\code{deps}}{(\code{\link[data.table:data.table]{data.table::data.table()}})\cr Table has cols \code{id} (\code{character(1)}) and \code{on} (\code{character(1)}) and \code{cond} (\link{Condition}). Lists all (direct) dependency parents of a param, through parameter IDs. Internally created by a call to \code{add_dep}. Settable, if you want to remove dependencies or perform other changes.} -\item{\code{set_id}}{(\code{character(1)})\cr -ID of this param set. Default \code{""}. Settable.} - \item{\code{length}}{(\code{integer(1)})\cr Number of contained \link{Param}s.} @@ -140,9 +129,9 @@ Is \code{TRUE} if all parameters are \link{ParamDbl} or \link{ParamInt}.} \item{\code{all_categorical}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \link{ParamFct} and \link{ParamLgl}.} -\item{\code{trafo}}{(\verb{function(x, param_set)})\cr +\item{\code{extra_trafo}}{(\verb{function(x, param_set)})\cr Transformation function. Settable. -User has to pass a \verb{function(x, param_set)}, of the form\cr +User has to pass a \verb{function(x)}, of the form\cr (named \code{list()}, \link{ParamSet}) -> named \code{list()}.\cr The function is responsible to transform a feasible configuration into another encoding, before potentially evaluating the configuration with the target algorithm. @@ -169,12 +158,18 @@ Has the set parameter dependencies?} \subsection{Public methods}{ \itemize{ \item \href{#method-new}{\code{ParamSet$new()}} -\item \href{#method-add}{\code{ParamSet$add()}} +\item \href{#method-trafo}{\code{ParamSet$trafo()}} +\item \href{#method-qunif}{\code{ParamSet$qunif()}} +\item \href{#method-get_param}{\code{ParamSet$get_param()}} +\item \href{#method-get_levels}{\code{ParamSet$get_levels()}} \item \href{#method-ids}{\code{ParamSet$ids()}} \item \href{#method-get_values}{\code{ParamSet$get_values()}} \item \href{#method-subset}{\code{ParamSet$subset()}} +\item \href{#method-subspaces}{\code{ParamSet$subspaces()}} +\item \href{#method-flatten}{\code{ParamSet$flatten()}} \item \href{#method-search_space}{\code{ParamSet$search_space()}} \item \href{#method-check}{\code{ParamSet$check()}} +\item \href{#method-check_dependencies}{\code{ParamSet$check_dependencies()}} \item \href{#method-test}{\code{ParamSet$test()}} \item \href{#method-assert}{\code{ParamSet$assert()}} \item \href{#method-check_dt}{\code{ParamSet$check_dt()}} @@ -192,7 +187,11 @@ Has the set parameter dependencies?} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$new(params = named_list(), ignore_ids = FALSE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$new( + params = named_list(), + extra_trafo = NULL, + allow_dangling_dependencies = FALSE +)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -203,6 +202,10 @@ List of \link{Param}, named with their respective ID. \code{params} are cloned on-demand, so the input does not need to be cloned.} +\item{\code{set_id}}{(\code{character(1)})\cr +\verb{$set_id} of the resulting \code{ParamSet}. This determines the +prefix inside \code{\link{ParamSetCollection}}. Default \code{""} (no prefix).} + \item{\code{ignore_ids}}{(\code{logical(1)})\cr Ignore \verb{$id} slots of \code{params} and instead use the names instead. When this is \code{TRUE}, then \code{params} must be named. @@ -214,22 +217,40 @@ Default \code{FALSE}.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-add}{}}} -\subsection{Method \code{add()}}{ -Adds a single param or another set to this set, all params are cloned on-demand, -so the input does not need to be cloned. +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-trafo}{}}} +\subsection{Method \code{trafo()}}{ \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$add(p)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$trafo(x, param_set)}\if{html}{\out{
}} } -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{p}}{(\link{Param} | \link{ParamSet}).} } -\if{html}{\out{
}} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-qunif}{}}} +\subsection{Method \code{qunif()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$qunif(x)}\if{html}{\out{
}} } + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_param}{}}} +\subsection{Method \code{get_param()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$get_param(id)}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_levels}{}}} +\subsection{Method \code{get_levels()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$get_levels(id)}\if{html}{\out{
}} +} + } \if{html}{\out{
}} \if{html}{\out{}} @@ -239,7 +260,7 @@ Retrieves IDs of contained parameters based on some filter criteria selections, \code{NULL} means no restriction. Only returns IDs of parameters that satisfy all conditions. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$ids(class = NULL, is_bounded = NULL, tags = NULL)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$ids(class = NULL, tags = NULL, any_tags = NULL)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -247,8 +268,6 @@ Only returns IDs of parameters that satisfy all conditions. \describe{ \item{\code{class}}{(\code{character()}).} -\item{\code{is_bounded}}{(\code{logical(1)}).} - \item{\code{tags}}{(\code{character()}).} } \if{html}{\out{
}} @@ -267,10 +286,11 @@ Only returns values of parameters that satisfy all conditions. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$get_values( class = NULL, - is_bounded = NULL, tags = NULL, + any_tags = NULL, type = "with_token", - check_required = TRUE + check_required = TRUE, + remove_dependencies = TRUE )}\if{html}{\out{
}} } @@ -279,8 +299,6 @@ Only returns values of parameters that satisfy all conditions. \describe{ \item{\code{class}}{(\code{character()}).} -\item{\code{is_bounded}}{(\code{logical(1)}).} - \item{\code{tags}}{(\code{character()}).} \item{\code{type}}{(\code{character(1)})\cr @@ -288,6 +306,8 @@ Return values \code{with_token}, \code{without_token} or \code{only_token}?} \item{\code{check_required}}{(\code{logical(1)})\cr Check if all required parameters are set?} + +\item{\code{is_bounded}}{(\code{logical(1)}).} } \if{html}{\out{
}} } @@ -299,9 +319,9 @@ Named \code{list()}. \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-subset}{}}} \subsection{Method \code{subset()}}{ -Changes the current set to the set of passed IDs. +Create a new \code{ParamSet} restricted to the passed IDs. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$subset(ids)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$subset(ids, allow_dangling_dependencies = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -311,6 +331,29 @@ Changes the current set to the set of passed IDs. } \if{html}{\out{
}} } +\subsection{Returns}{ +\code{ParamSet}. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-subspaces}{}}} +\subsection{Method \code{subspaces()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$subspaces(ids = private$.params$id)}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-flatten}{}}} +\subsection{Method \code{flatten()}}{ +Create a \code{ParamSet} from this object, even if this object itself is not +a \code{ParamSet}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$flatten()}\if{html}{\out{
}} +} + } \if{html}{\out{
}} \if{html}{\out{}} @@ -338,7 +381,7 @@ A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$check(xs)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$check(xs, check_strict = FALSE, describe_error = TRUE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -351,6 +394,15 @@ Params for which dependencies are not satisfied should not be part of \code{x}. \subsection{Returns}{ If successful \code{TRUE}, if not a string with the error message. } +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-check_dependencies}{}}} +\subsection{Method \code{check_dependencies()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$check_dependencies(xs, describe_error = TRUE)}\if{html}{\out{
}} +} + } \if{html}{\out{
}} \if{html}{\out{}} @@ -361,7 +413,7 @@ A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$test(xs)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$test(xs, check_strict = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -384,7 +436,7 @@ A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$assert(xs, .var.name = vname(xs))}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$assert(xs, check_strict = FALSE, .var.name = vname(xs))}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -412,7 +464,7 @@ if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should be set to \code{NA} in \code{xdt}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$check_dt(xdt)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$check_dt(xdt, check_strict = FALSE, describe_error = TRUE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -432,7 +484,7 @@ If successful \code{TRUE}, if not a string with the error message. \subsection{Method \code{test_dt()}}{ \pkg{checkmate}-like test-function (s. \verb{$check_dt()}). \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$test_dt(xdt)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$test_dt(xdt, check_strict = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -452,7 +504,7 @@ If successful \code{TRUE}, if not \code{FALSE}. \subsection{Method \code{assert_dt()}}{ \pkg{checkmate}-like assert-function (s. \verb{$check_dt()}). \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$assert_dt(xdt, .var.name = vname(xdt))}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$assert_dt(xdt, check_strict = FALSE, .var.name = vname(xdt))}\if{html}{\out{
}} } \subsection{Arguments}{ diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 555f303d..662dbc73 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -11,8 +11,6 @@ A collection of multiple \link{ParamSet} objects. ".". Parameters from ParamSets with empty (i.e. \code{""}) \verb{$set_id} are referenced directly. Multiple ParamSets with \verb{$set_id} \code{""} can be combined, but their parameter names must be unique. -\item Operation \code{subset} is currently not allowed. -\item Operation \code{add} currently only works when adding complete sets not single params. \item When you either ask for 'values' or set them, the operation is delegated to the individual, contained param set references. The collection itself does not maintain a \code{values} state. This also implies that if you directly change \code{values} in one of the referenced sets, @@ -34,9 +32,6 @@ If you call \code{deps} on the collection, you are returned a complete table of \section{Active bindings}{ \if{html}{\out{
}} \describe{ -\item{\code{params}}{(named \code{list()})\cr -List of \link{Param}, named with their respective ID.} - \item{\code{params_unid}}{(named \code{list()})\cr List of \link{Param}, named with their true ID. However, this field has the \link{Param}'s \verb{$id} value set to a @@ -67,9 +62,6 @@ When you set values, all previously set values will be unset / removed.} \subsection{Public methods}{ \itemize{ \item \href{#method-new}{\code{ParamSetCollection$new()}} -\item \href{#method-add}{\code{ParamSetCollection$add()}} -\item \href{#method-remove_sets}{\code{ParamSetCollection$remove_sets()}} -\item \href{#method-subset}{\code{ParamSetCollection$subset()}} \item \href{#method-clone}{\code{ParamSetCollection$clone()}} } } @@ -80,14 +72,22 @@ When you set values, all previously set values will be unset / removed.} \item \out{}\href{../../paradox/html/ParamSet.html#method-assert}{\code{paradox::ParamSet$assert()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-assert_dt}{\code{paradox::ParamSet$assert_dt()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-check}{\code{paradox::ParamSet$check()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-check_dependencies}{\code{paradox::ParamSet$check_dependencies()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-check_dt}{\code{paradox::ParamSet$check_dt()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-flatten}{\code{paradox::ParamSet$flatten()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-format}{\code{paradox::ParamSet$format()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-get_levels}{\code{paradox::ParamSet$get_levels()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-get_param}{\code{paradox::ParamSet$get_param()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-get_values}{\code{paradox::ParamSet$get_values()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-ids}{\code{paradox::ParamSet$ids()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-print}{\code{paradox::ParamSet$print()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-qunif}{\code{paradox::ParamSet$qunif()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-search_space}{\code{paradox::ParamSet$search_space()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-subset}{\code{paradox::ParamSet$subset()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-subspaces}{\code{paradox::ParamSet$subspaces()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-test}{\code{paradox::ParamSet$test()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-test_dt}{\code{paradox::ParamSet$test_dt()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-trafo}{\code{paradox::ParamSet$trafo()}}\out{} } \out{} } @@ -97,7 +97,7 @@ When you set values, all previously set values will be unset / removed.} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSetCollection$new(sets, ignore_ids = FALSE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSetCollection$new(sets, set_id = "", ignore_ids = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -106,6 +106,10 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \item{\code{sets}}{(\code{list()} of \link{ParamSet})\cr ParamSet objects are not cloned.} +\item{\code{set_id}}{(\code{character(1)})\cr +\verb{$set_id} of the resulting \code{ParamSet}. This determines the +prefix inside \code{\link{ParamSetCollection}}. Default \code{""} (no prefix).} + \item{\code{ignore_ids}}{(\code{logical(1)})\cr Ignore \verb{$set_id} slots of \code{sets} and instead use the names instead. When this is \code{TRUE}, then \code{sets} should be named when id prefixes are desired. @@ -120,57 +124,6 @@ Default \code{FALSE}.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-add}{}}} -\subsection{Method \code{add()}}{ -Adds a set to this collection. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSetCollection$add(p)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{p}}{(\link{ParamSet}).} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-remove_sets}{}}} -\subsection{Method \code{remove_sets()}}{ -Removes sets of given ids from collection. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSetCollection$remove_sets(ids)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{ids}}{(\code{character()}).} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-subset}{}}} -\subsection{Method \code{subset()}}{ -Only included for consistency. Not allowed to perform on \link{ParamSetCollection}s. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSetCollection$subset(ids)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{ids}}{(\code{character()}).} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-clone}{}}} \subsection{Method \code{clone()}}{ diff --git a/man/ParamUty.Rd b/man/ParamUty.Rd deleted file mode 100644 index 42f8c9cd..00000000 --- a/man/ParamUty.Rd +++ /dev/null @@ -1,145 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ParamUty.R -\name{ParamUty} -\alias{ParamUty} -\title{Untyped Parameter} -\description{ -A \link{Param} to describe untyped parameters. -} -\examples{ -ParamUty$new("untyped", default = Inf) -} -\seealso{ -Other Params: -\code{\link{ParamDbl}}, -\code{\link{ParamFct}}, -\code{\link{ParamInt}}, -\code{\link{ParamLgl}}, -\code{\link{Param}} -} -\concept{Params} -\section{Super class}{ -\code{\link[paradox:Param]{paradox::Param}} -> \code{ParamUty} -} -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{custom_check}}{(\verb{function()})\cr -Custom function to check the feasibility.} - -\item{\code{lower}}{(\code{numeric(1)})\cr -Lower bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{upper}}{(\code{numeric(1)})\cr -Upper bound. -Always \code{NA} for \link{ParamFct}, \link{ParamLgl} and \link{ParamUty}.} - -\item{\code{levels}}{(\code{character()} | \code{NULL})\cr -Set of allowed levels. -Always \code{NULL} for \link{ParamDbl}, \link{ParamInt} and \link{ParamUty}. -Always \code{c(TRUE, FALSE)} for \link{ParamLgl}.} - -\item{\code{nlevels}}{(\code{integer(1)} | \code{Inf})\cr -Number of categorical levels. -Always \code{Inf} for \link{ParamDbl} and \link{ParamUty}. -The number of integers in the range \verb{[lower, upper]}, or \code{Inf} if unbounded for \link{ParamInt}. -Always \code{2} for \link{ParamLgl}.} - -\item{\code{is_bounded}}{(\code{logical(1)})\cr -Are the bounds finite? -Always \code{TRUE} for \link{ParamFct} and \link{ParamLgl}. -Always \code{FALSE} for \link{ParamUty}.} - -\item{\code{storage_type}}{(\code{character(1)})\cr -Data type when values of this parameter are stored in a data table or sampled. -Always \code{"numeric"} for \link{ParamDbl}. -Always \code{"character"} for \link{ParamFct}. -Always \code{"integer"} for \link{ParamInt}. -Always \code{"logical"} for \link{ParamLgl}. -Always \code{"list"} for \link{ParamUty}.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{ParamUty$new()}} -\item \href{#method-clone}{\code{ParamUty$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../paradox/html/Param.html#method-assert}{\code{paradox::Param$assert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-check}{\code{paradox::Param$check()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-convert}{\code{paradox::Param$convert()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-format}{\code{paradox::Param$format()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-print}{\code{paradox::Param$print()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-qunif}{\code{paradox::Param$qunif()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-rep}{\code{paradox::Param$rep()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-test}{\code{paradox::Param$test()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_id}{\code{paradox::Param$with_id()}}\out{} -\item \out{}\href{../../paradox/html/Param.html#method-with_tags}{\code{paradox::Param$with_tags()}}\out{} -} -\out{
} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamUty$new(id, default = NO_DEF, tags = character(), custom_check = NULL)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{id}}{(\code{character(1)})\cr -Identifier of the object.} - -\item{\code{default}}{(\code{any})\cr -Default value. Can be from the domain of the parameter or an element of -\code{special_vals}. Has value \link{NO_DEF} if no default exists. \code{NULL} can be a -valid default. -The value has no effect on \code{ParamSet$values} or the behavior of -\code{ParamSet$check()}, \verb{$test()} or \verb{$assert()}. -The \code{default} is intended to be used for documentation purposes. -`} - -\item{\code{tags}}{(\code{character()})\cr -Arbitrary tags to group and subset parameters. Some tags serve a special -purpose:\cr -\itemize{ -\item \code{"required"} implies that the parameters has to be given when setting -\code{values} in \link{ParamSet}. -}} - -\item{\code{custom_check}}{(\verb{function()})\cr -Custom function to check the feasibility. -Function which checks the input. -Must return 'TRUE' if the input is valid and a string with the error message otherwise. -Defaults to \code{NULL}, which means that no check is performed.} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamUty$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/assert_param.Rd b/man/assert_param_set.Rd similarity index 80% rename from man/assert_param.Rd rename to man/assert_param_set.Rd index 27a28566..7fc13982 100644 --- a/man/assert_param.Rd +++ b/man/assert_param_set.Rd @@ -1,22 +1,19 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/asserts.R -\name{assert_param} -\alias{assert_param} +\name{assert_param_set} \alias{assert_param_set} \title{Assertions for Params and ParamSets} \usage{ -assert_param(param, cl = "Param", no_untyped = FALSE, must_bounded = FALSE) - assert_param_set( param_set, - cl = "Param", + cl = NULL, no_untyped = FALSE, must_bounded = FALSE, no_deps = FALSE ) } \arguments{ -\item{param}{(\link{Param}).} +\item{param_set}{(\link{ParamSet}).} \item{cl}{(\code{character()})\cr Allowed subclasses.} @@ -27,8 +24,6 @@ Are untyped \link{Param}s allowed?} \item{must_bounded}{(\code{logical(1)})\cr Only bounded \link{Param}s allowed?} -\item{param_set}{(\link{ParamSet}).} - \item{no_deps}{(\code{logical(1)})\cr Are dependencies allowed?} } diff --git a/man/domain_check.Rd b/man/domain_check.Rd new file mode 100644 index 00000000..0468a361 --- /dev/null +++ b/man/domain_check.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Param.R +\name{domain_check} +\alias{domain_check} +\title{Check value validity} +\usage{ +domain_check(param, values, describe_error = TRUE) +} +\arguments{ +\item{x}{(\code{any}).} +} +\value{ +If successful \code{TRUE}, if not a string with the error message. +} +\description{ +\pkg{checkmate}-like check-function. Check whether a list of values is feasible in the domain. +A value is feasible if it is of the same \code{storage_type}, inside of the bounds or element of +\code{special_vals}. \code{TuneToken}s are generally \emph{not} accepted, so they should be filtered out +before the call, if present. +} diff --git a/man/ps.Rd b/man/ps.Rd index 17b7b167..cf8f2c17 100644 --- a/man/ps.Rd +++ b/man/ps.Rd @@ -69,7 +69,7 @@ pars$search_space() } \seealso{ Other ParamSet construction helpers: -\code{\link{Domain}}, +\code{\link{p_dbl}()}, \code{\link{to_tune}()} } \concept{ParamSet construction helpers} diff --git a/man/to_tune.Rd b/man/to_tune.Rd index e5764143..a9a02be6 100644 --- a/man/to_tune.Rd +++ b/man/to_tune.Rd @@ -144,7 +144,7 @@ print(grid$transpose()) } \seealso{ Other ParamSet construction helpers: -\code{\link{Domain}}, +\code{\link{p_dbl}()}, \code{\link{ps}()} } \concept{ParamSet construction helpers} diff --git a/tests/testthat/helper_compat.R b/tests/testthat/helper_compat.R index 826edc6c..cf784685 100644 --- a/tests/testthat/helper_compat.R +++ b/tests/testthat/helper_compat.R @@ -9,3 +9,40 @@ context = function(...) suppressWarnings(testthat::context(...)) expect_is = function(...) suppressWarnings(testthat::expect_is(...)) expect_equivalent = function(...) suppressWarnings(testthat::expect_equivalent(...)) library("checkmate") + + +ParamInt = list( + new = function(id, ...) { + ParamSet$new(set_names(list(p_int(...)), id)) + } +) + +ParamDbl = list( + new = function(id, ...) { + ParamSet$new(set_names(list(p_dbl(...)), id)) + } +) + +ParamFct = list( + new = function(id, ...) { + ParamSet$new(set_names(list(p_fct(...)), id)) + } +) + +ParamLgl = list( + new = function(id, ...) { + ParamSet$new(set_names(list(p_lgl(...)), id)) + } +) + +ParamUty = list( + new = function(id, ...) { + ParamSet$new(set_names(list(p_uty(...)), id)) + } +) + +ParamSet_legacy = list( + new = function(params) { + ps_union(params) + } +) From 910e483f81daf13c914b3001ac8f5d50c7cd64ac Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 25 Mar 2021 02:23:40 +0100 Subject: [PATCH 15/64] some fixes --- R/ParamSet.R | 19 ++++++++++++------- R/ParamUty.R | 12 +++++++++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index ea8f1239..4f83fa92 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -72,7 +72,7 @@ ParamSet = R6Class("ParamSet", private$.params = data.table(cls = character(0), grouping = character(0), cargo = list(), lower = numeric(0), upper = numeric(0), tolerance = numeric(0), levels = list(), special_vals = list(), default = list(), tags = list(), trafo = list(), requirements = list(), id = character(0)) } else { - private$.params = rbindlist(c(list(dt0), params))[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))][, + private$.params = rbindlist(params)[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))][, # put 'id' at the beginning c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "tags", "trafo", "has_trafo"), with = FALSE] } @@ -566,7 +566,7 @@ ParamSet = R6Class("ParamSet", #' Number of categorical levels per parameter, `Inf` for double parameters or unbounded integer parameters. #' Named with param IDs. nlevels = function() { - set_names(private$.params[, list(id, nlevels = domain_nlevels(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", nlevels], + set_names(private$.params[, list(id, nlevels = domain_nlevels(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", nlevels], private$.params$id) }, @@ -574,7 +574,7 @@ ParamSet = R6Class("ParamSet", #' Do all parameters have finite bounds? #' Named with parameter IDs. is_bounded = function() { - set_names(private$.params[, list(id, is_bounded = domain_is_bounded(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", is_bounded], + set_names(private$.params[, list(id, is_bounded = domain_is_bounded(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_bounded], private$.params$id) }, @@ -609,7 +609,7 @@ ParamSet = R6Class("ParamSet", #' Data types of parameters when stored in tables. #' Named with parameter IDs. storage_type = function() { - set_names(private$.params[, list(id, storage_type = domain_storage_type(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", storage_type], + set_names(private$.params[, list(id, storage_type = domain_storage_type(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", storage_type], private$.params$id) }, @@ -617,7 +617,7 @@ ParamSet = R6Class("ParamSet", #' Position is TRUE for [ParamDbl] and [ParamInt]. #' Named with parameter IDs. is_number = function() { - set_names(private$.params[, list(id, is_number = domain_is_number(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], + set_names(private$.params[, list(id, is_number = domain_is_number(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], private$.params$id) }, @@ -625,7 +625,7 @@ ParamSet = R6Class("ParamSet", #' Position is TRUE for [ParamFct] and [ParamLgl]. #' Named with parameter IDs. is_categ = function() { - set_names(private$.params[, list(id, is_categ = domain_is_categ(.SD)), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], + set_names(private$.params[, list(id, is_categ = domain_is_categ(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], private$.params$id) }, @@ -685,7 +685,7 @@ ParamSet = R6Class("ParamSet", nontt = discard(xs, inherits, "TuneToken") sanitised = private$.params[names(nontt), on = "id"][, values := nontt][ !pmap_lgl(list(special_vals, values), has_element), - list(id, values = domain_sanitize(.SD, values)), by = c("cls", "grouping")] + list(id, values = domain_sanitize(cbind(.SD, .BY), values)), by = c("cls", "grouping")] insert_named(xx, set_names(sanitised$values, sanitised$id)) } # store with param ordering, return value with original ordering @@ -764,6 +764,11 @@ ParamSet = R6Class("ParamSet", ) ) +recover_domain = function(sd, by) { + domain = as.data.table(c(by, sd)) + class(domain) = c(domain$cls, class(domain)) +} + #' @export as.data.table.ParamSet = function(x, ...) { # nolint x$data diff --git a/R/ParamUty.R b/R/ParamUty.R index f0f44200..11a31bfb 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -2,14 +2,20 @@ #' @rdname Domain #' @export p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { - assert_function(custom_check, nargs = 1) - custom_check_result = custom_check(1) - assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") + assert_function(custom_check, nargs = 1, null.ok = TRUE) + if (!is.null(custom_check)) { + custom_check_result = custom_check(1) + assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") + } domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, depends_expr = substitute(depends)) } #' @export domain_check.ParamUty = function(param, values, describe_error = TRUE) { + subset = !map_lgl(param$cargo, is.null) + if (!any(subset)) return(TRUE) + values = values[subset] + param = param[subset] check_domain_vectorize(param$id, values, param$cargo, describe_error = describe_error) } From 7c077c1224e57db71e95175b005bc1fd3cd71e0d Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 25 Mar 2021 11:12:06 +0100 Subject: [PATCH 16/64] a few fixes --- R/ParamDbl.R | 2 +- R/ParamSet.R | 17 +++++++++-------- R/domain.R | 4 ++-- R/helper.R | 4 ++-- tests/testthat/helper_compat.R | 15 ++++++++++----- tests/testthat/test_Param.R | 26 +++++++++++++------------- tests/testthat/test_ParamDbl.R | 2 +- 7 files changed, 38 insertions(+), 32 deletions(-) diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 8c469265..fefefcf5 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -24,7 +24,7 @@ p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ #' @export domain_check.ParamDbl = function(param, values, describe_error = TRUE) { lower = param$lower - param$tolerance * pmax(1, abs(param$lower)) - upper = param$upper - param$tolerance * pmax(1, abs(param$upper)) + upper = param$upper + param$tolerance * pmax(1, abs(param$upper)) if (qtestr(values, "N1")) { values_num = as.numeric(values) if (all(values_num >= lower) && all(values_num <= upper)) return(TRUE) diff --git a/R/ParamSet.R b/R/ParamSet.R index 4f83fa92..36d8e3db 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -453,7 +453,7 @@ ParamSet = R6Class("ParamSet", #' @description #' Helper for print outputs. format = function() { - sprintf("<%s[%s]>", class(self)[1L], nrow(private$.params)) + sprintf("<%s[%s]>", class(self)[[1L]], self$length) }, #' @description @@ -491,7 +491,7 @@ ParamSet = R6Class("ParamSet", active = list( data = function(v) { if (!missing(v)) stop("v is read-only") - private$.params[, list(id, class = cls, lower, upper, nlevels = self$nlevels, + private$.params[, list(id, class = cls, lower, upper, levels, nlevels = self$nlevels, is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags)] }, @@ -566,7 +566,7 @@ ParamSet = R6Class("ParamSet", #' Number of categorical levels per parameter, `Inf` for double parameters or unbounded integer parameters. #' Named with param IDs. nlevels = function() { - set_names(private$.params[, list(id, nlevels = domain_nlevels(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", nlevels], + set_names(private$.params[, list(id, nlevels = domain_nlevels(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", nlevels], private$.params$id) }, @@ -574,7 +574,7 @@ ParamSet = R6Class("ParamSet", #' Do all parameters have finite bounds? #' Named with parameter IDs. is_bounded = function() { - set_names(private$.params[, list(id, is_bounded = domain_is_bounded(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_bounded], + set_names(private$.params[, list(id, is_bounded = domain_is_bounded(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_bounded], private$.params$id) }, @@ -609,7 +609,7 @@ ParamSet = R6Class("ParamSet", #' Data types of parameters when stored in tables. #' Named with parameter IDs. storage_type = function() { - set_names(private$.params[, list(id, storage_type = domain_storage_type(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", storage_type], + set_names(private$.params[, list(id, storage_type = domain_storage_type(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", storage_type], private$.params$id) }, @@ -617,7 +617,7 @@ ParamSet = R6Class("ParamSet", #' Position is TRUE for [ParamDbl] and [ParamInt]. #' Named with parameter IDs. is_number = function() { - set_names(private$.params[, list(id, is_number = domain_is_number(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], + set_names(private$.params[, list(id, is_number = domain_is_number(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], private$.params$id) }, @@ -625,7 +625,7 @@ ParamSet = R6Class("ParamSet", #' Position is TRUE for [ParamFct] and [ParamLgl]. #' Named with parameter IDs. is_categ = function() { - set_names(private$.params[, list(id, is_categ = domain_is_categ(cbind(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], + set_names(private$.params[, list(id, is_categ = domain_is_categ(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], private$.params$id) }, @@ -685,7 +685,7 @@ ParamSet = R6Class("ParamSet", nontt = discard(xs, inherits, "TuneToken") sanitised = private$.params[names(nontt), on = "id"][, values := nontt][ !pmap_lgl(list(special_vals, values), has_element), - list(id, values = domain_sanitize(cbind(.SD, .BY), values)), by = c("cls", "grouping")] + list(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] insert_named(xx, set_names(sanitised$values, sanitised$id)) } # store with param ordering, return value with original ordering @@ -767,6 +767,7 @@ ParamSet = R6Class("ParamSet", recover_domain = function(sd, by) { domain = as.data.table(c(by, sd)) class(domain) = c(domain$cls, class(domain)) + domain } #' @export diff --git a/R/domain.R b/R/domain.R index 578fbccf..17993f0b 100644 --- a/R/domain.R +++ b/R/domain.R @@ -150,11 +150,11 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real class(param) = c(cls, "Domain", class(param)) if (!is_nodefault(default)) { - param_assert(param, default) + domain_assert(param, list(default)) } # repr: what to print - constructorcall = match.call(sys.function(-1), sys.call(-1)) + constructorcall = match.call(sys.function(-1), sys.call(-1), envir = parent.frame(2)) trafoexpr = constructorcall$trafo constructorcall$trafo = NULL constructorcall$depends = NULL diff --git a/R/helper.R b/R/helper.R index 2fb87f28..08ee3715 100644 --- a/R/helper.R +++ b/R/helper.R @@ -195,13 +195,13 @@ check_domain_vectorize = function(ids, values, checker, more_args = list(), desc errors = imap(c(list(ids, values), more_args), function(id, value, ...) { if (break_early) return(FALSE) ch = checker(value, ...) - if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", ch) else break_early <<- TRUE # nolint + if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint }) } else { errors = imap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { if (break_early) return(FALSE) ch = chck(value, ...) - if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", ch) else break_early <<- TRUE # nolint + if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint }) } if (!describe_error) return(!break_early) diff --git a/tests/testthat/helper_compat.R b/tests/testthat/helper_compat.R index cf784685..be501bb2 100644 --- a/tests/testthat/helper_compat.R +++ b/tests/testthat/helper_compat.R @@ -14,31 +14,36 @@ library("checkmate") ParamInt = list( new = function(id, ...) { ParamSet$new(set_names(list(p_int(...)), id)) - } + }, + classname = "ParamInt" ) ParamDbl = list( new = function(id, ...) { ParamSet$new(set_names(list(p_dbl(...)), id)) - } + }, + classname = "ParamDbl" ) ParamFct = list( new = function(id, ...) { ParamSet$new(set_names(list(p_fct(...)), id)) - } + }, + classname = "ParamFct" ) ParamLgl = list( new = function(id, ...) { ParamSet$new(set_names(list(p_lgl(...)), id)) - } + }, + classname = "ParamLgl" ) ParamUty = list( new = function(id, ...) { ParamSet$new(set_names(list(p_uty(...)), id)) - } + }, + classname = "ParamUty" ) ParamSet_legacy = list( diff --git a/tests/testthat/test_Param.R b/tests/testthat/test_Param.R index 995f24dc..d0e74f33 100644 --- a/tests/testthat/test_Param.R +++ b/tests/testthat/test_Param.R @@ -4,8 +4,8 @@ context("Param") test_that("basic properties", { p1 = ParamDbl$new("x", default = 4) p2 = ParamFct$new("y", levels = c("a", "b")) - expect_true(p1$has_default) - expect_false(p2$has_default) + expect_equal(p1$default, list(x = 4)) + expect_equal(p2$default, named_list()) expect_true(p1$is_number) expect_false(p2$is_number) expect_false(p1$is_categ) @@ -16,12 +16,12 @@ test_that("check and assert work", { # test-funcion should be tested in individual test_Param files # here we briefly check all 3 to see if they work in principle p = ParamDbl$new("x", lower = 1, upper = 2) - p$assert(1) - expect_error(p$assert(3)) - expect_true(p$check(1)) - expect_string(p$check(3), fixed = "<= 2") - expect_true(p$test(1)) - expect_false(p$test(3)) + p$assert(list(x = 1)) + expect_error(p$assert(list(x = 3))) + expect_true(p$check(list(x = 1))) + expect_string(p$check(list(x = 3)), fixed = "<= 2") + expect_true(p$test(list(x = 1))) + expect_false(p$test(list(x = 3))) }) @@ -42,17 +42,17 @@ test_that("special_vals work for all Param subclasses", { p = cl$new(id = paste0("test.", cl$classname), special_vals = special_vals) } for (special_val in special_vals) { - expect_true(p$test(special_val)) - expect_false(p$test("never valid")) - expect_false(p$test(NA)) - expect_false(p$test(NULL)) + expect_true(p$test(set_names(list(special_val), paste0("test.", cl$classname)))) + expect_false(p$test(set_names(list("never valid"), paste0("test.", cl$classname)))) + expect_false(p$test(set_names(list(NA), paste0("test.", cl$classname)))) + expect_false(p$test(set_names(list(NULL), paste0("test.", cl$classname)))) } } } }) test_that("we cannot create Params with non-strict R names", { - expect_error(ParamInt$new(id = "$foo"), "Must comply") + expect_error(ParamInt$new(id = "$foo"), "does not comply") }) test_that("printer works", { diff --git a/tests/testthat/test_ParamDbl.R b/tests/testthat/test_ParamDbl.R index d88e672b..86a7a91e 100644 --- a/tests/testthat/test_ParamDbl.R +++ b/tests/testthat/test_ParamDbl.R @@ -2,7 +2,7 @@ context("ParamDbl") test_that("constructor works", { p = ParamDbl$new(id = "test", lower = 1, upper = 10) - expect_equal(p$id, "test") + expect_equal(p$ids(), "test") expect_equal(p$lower, 1) expect_equal(p$upper, 10) From d941f2b0767507fa3ddfd9e25cae4d1d4b5d3f7e Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 25 Mar 2021 11:27:03 +0100 Subject: [PATCH 17/64] a few fixes --- R/Param.R | 3 ++- R/ParamSet.R | 8 ++++---- tests/testthat/test_ParamDbl.R | 29 ++++++++++++++++------------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/R/Param.R b/R/Param.R index 67bc4d9e..60199959 100644 --- a/R/Param.R +++ b/R/Param.R @@ -68,7 +68,8 @@ domain_is_categ = function(param) { #' @export domain_qunif = function(param, x) { assert_string(unique(param$grouping)) - assert(check_number(x, lower = 0, upper = 1), check_numeric(x, lower = 0, upper = 1, any.missing = FALSE, length = nrow(param))) + assert_numeric(x, lower = 0, upper = 1, any.missing = FALSE, min.len = nrow(param)) + assert_true(length(x) %% length(nrow(param)) == 0) UseMethod("domain_qunif") } diff --git a/R/ParamSet.R b/R/ParamSet.R index 36d8e3db..04894350 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -111,7 +111,7 @@ ParamSet = R6Class("ParamSet", }, qunif = function(x) { - assert(check_data_frame(x, types = "numeric"), check_matrix(x, mode = "numeric")) + assert(check_data_frame(x, types = "numeric", min.cols = 1), check_matrix(x, mode = "numeric", min.cols = 1)) if (is.matrix(x)) { qassert(x, "N[0,1]") } else { @@ -121,9 +121,9 @@ ParamSet = R6Class("ParamSet", assert_names(colnames(x), type = "unique", subset.of = private$.params$id) x = t(x) - params = private$.params[colnames(x), on = "id"] + params = private$.params[rownames(x), on = "id"] params$result = list() - params[, result := as.data.frame(t(matrix(domain_qunif(.SD, x[id, ]), nrow = .N))), by = c("cls", "grouping")] + params[, result := list(as.list(as.data.frame(t(matrix(domain_qunif(recover_domain(.SD, .BY), x[id, ]), nrow = .N))))), by = c("cls", "grouping")] as.data.table(set_names(params$result, params$id)) }, @@ -398,7 +398,7 @@ ParamSet = R6Class("ParamSet", check_dt = function(xdt, check_strict = FALSE, describe_error = TRUE) { xss = map(transpose_list(xdt), discard, is.na) msgs = list() - for (is in seq_along(xss)) { + for (i in seq_along(xss)) { xs = xss[[i]] ok = self$check(xs, check_strict = check_strict, describe_error = describe_error) if (!isTRUE(ok)) { diff --git a/tests/testthat/test_ParamDbl.R b/tests/testthat/test_ParamDbl.R index 86a7a91e..0ceb4f26 100644 --- a/tests/testthat/test_ParamDbl.R +++ b/tests/testthat/test_ParamDbl.R @@ -3,13 +3,13 @@ context("ParamDbl") test_that("constructor works", { p = ParamDbl$new(id = "test", lower = 1, upper = 10) expect_equal(p$ids(), "test") - expect_equal(p$lower, 1) - expect_equal(p$upper, 10) + expect_equal(p$lower, c(test = 1)) + expect_equal(p$upper, c(test = 10)) # check that we can create param with Inf bounds p = ParamDbl$new(id = "test", lower = 1) - expect_equal(p$lower, 1) - expect_equal(p$upper, Inf) + expect_equal(p$lower, c(test = 1)) + expect_equal(p$upper, c(test = Inf)) # check some invalid arg settings expect_error(ParamDbl$new(id = "x", lower = NULL), "not 'NULL'") @@ -19,12 +19,12 @@ test_that("constructor works", { test_that("allowing inf as feasible value works", { p = ParamDbl$new(id = "x", lower = 1, upper = 10) - expect_true(p$test(1)) - expect_false(p$test(Inf)) + expect_true(p$test(list(x = 1))) + expect_false(p$test(list(x = Inf))) p = ParamDbl$new(id = "x", lower = 1, special_vals = list(Inf)) - expect_true(p$test(1)) - expect_true(p$test(Inf)) + expect_true(p$test(list(x = 1))) + expect_true(p$test(list(x = Inf))) }) @@ -43,10 +43,12 @@ test_that("qunif", { # then check that the estimated ecdfs from both distribs are nearly the same (L1 dist) p = ParamDbl$new("x", lower = a, upper = b) u = runif(n) - v1 = p$qunif(u) - expect_double(v1, any.missing = FALSE, len = n) + v1 = p$qunif(data.table(x = u)) + expect_data_table(v1, ncols = 1, nrow = n) + expect_equal(colnames(v1), "x") + expect_double(v1$x, any.missing = FALSE, len = n) v2 = runif(n, min = a, max = b) - e1 = ecdf(v1) + e1 = ecdf(v1$x) e2 = ecdf(v2) s = seq(a, b, by = 0.0001) d = abs(e1(s) - e2(s)) @@ -58,8 +60,9 @@ test_that("qunif", { test_that("tolerance in check allows values at the upper bound", { p = ParamDbl$new("x", lower = log(.01), upper = log(100)) - ub = p$qunif(1) - expect_true(p$check(ub)) + ub = p$qunif(data.table(x = 1)) + expect_true(p$check_dt(ub)) + expect_true(p$check(as.list(ub))) }) test_that("tolerance for setting values", { From 9acfc8ac35fc7bcf999585d41f73ff0916b3b66e Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 25 Mar 2021 23:57:32 +0100 Subject: [PATCH 18/64] quality of life stuff: set6 cache, domain class propreties, init values --- R/Param.R | 22 ++---- R/ParamDbl.R | 17 +++-- R/ParamFct.R | 14 ++-- R/ParamInt.R | 16 ++--- R/ParamLgl.R | 15 ++-- R/ParamS6.R | 133 +++++++---------------------------- R/ParamSet.R | 40 +++++++---- R/ParamUty.R | 13 ++-- R/S6ObjectCache.R | 174 ++++++++++++++++++++++++++++++++++++++++++++++ R/domain.R | 9 ++- R/helper.R | 9 ++- 11 files changed, 280 insertions(+), 182 deletions(-) create mode 100644 R/S6ObjectCache.R diff --git a/R/Param.R b/R/Param.R index 60199959..6b0e33f8 100644 --- a/R/Param.R +++ b/R/Param.R @@ -10,14 +10,6 @@ #' @return If successful `TRUE`, if not a string with the error message. #' @export domain_check = function(param, values, describe_error = TRUE) { - ## # either we are directly feasible, or in special vals, if both are untrue return errmsg from 1st check - ## if (inherits(value, "TuneToken")) { - ## return(tryCatch({ - ## tunetoken_to_ps(x, self, self$id) - ## TRUE - ## }, error = function(e) paste("tune token invalid:", conditionMessage(e)))) - ## } - ## has_element(private$.special_vals, x), TRUE, ch) if (!test_list(values, len = nrow(param))) return(if (describe_error) "values must be a list" else FALSE) if (length(values) == 0) return(TRUE) # happens when there are no params + values to check assert_string(unique(param$grouping)) @@ -79,9 +71,6 @@ domain_sanitize = function(param, values) { UseMethod("domain_sanitize") } -#' @export -domain_storage_type.default = function(param) rep("list", nrow(param)) - #' @export domain_nlevels.default = function(param) rep(Inf, nrow(param)) @@ -89,16 +78,13 @@ domain_nlevels.default = function(param) rep(Inf, nrow(param)) domain_is_bounded.default = function(param) rep(FALSE, nrow(param)) #' @export -domain_is_number.default = function(param) rep(FALSE, nrow(param)) - -#' @export -domain_is_categ.default = function(param) rep(FALSE, nrow(param)) +domain_qunif.default = function(param) stop("undefined") #' @export -domain_is_categ.default = function(param) rep(FALSE, nrow(param)) +domain_sanitize.default = function(param, values) values #' @export -domain_qunif.default = function(param) stop("undefined") +domain_is_categ.default = function(param) FALSE #' @export -domain_sanitize.default = function(param, values) values +domain_is_number.default = function(param) FALSE diff --git a/R/ParamDbl.R b/R/ParamDbl.R index fefefcf5..3aa61e93 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -1,6 +1,6 @@ #' @rdname Domain #' @export -p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE) { +p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE, init) { assert_number(tolerance, lower = 0) assert_number(lower) assert_number(upper) @@ -17,8 +17,8 @@ p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ real_upper = upper } - domain(cls = "ParamDbl", grouping = "ParamDbl", lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, - depends_expr = substitute(depends)) + domain(cls = "ParamDbl", grouping = "ParamDbl", lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, storage_type = "numeric", + depends_expr = substitute(depends), init = init) } #' @export @@ -39,17 +39,16 @@ domain_sanitize.ParamDbl = function(param, values) { as.list(min(max(as.numeric(values), param$lower), param$upper)) } -#' @export -domain_storage_type.ParamDbl = function(param) rep("numeric", nrow(param)) #' @export domain_nlevels.ParamDbl = function(param) ifelse(param$upper == param$lower, 1, Inf) #' @export domain_is_bounded.ParamDbl = function(param) is.finite(param$lower) && is.finite(param$upper) #' @export -domain_is_number.ParamDbl = function(param) rep(TRUE, nrow(param)) -#' @export -domain_is_categ.ParamDbl = function(param) rep(FALSE, nrow(param)) -#' @export domain_qunif.ParamDbl = function(param, x) { pmax(pmin(x * param$upper - (x-1) * param$lower, param$upper), param$lower) # extra careful here w/ rounding errors } + +#' @export +domain_is_number.ParamDbl = function(param) TRUE +#' @export +domain_is_categ.ParamDbl = function(param) FALSE diff --git a/R/ParamFct.R b/R/ParamFct.R index 890d1d6d..3a7177fb 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -1,6 +1,6 @@ #' @rdname Domain #' @export -p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { +p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { constargs = as.list(match.call()[-1]) levels = eval.parent(constargs$levels) if (!is.character(levels)) { @@ -21,7 +21,7 @@ p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = charact } # group p_fct by levels, so the group can be checked in a vectorized fashion. grouping = str_collapse(gsub("([\\\\\"])", "\\\\\\1", sort(real_levels)), quote = '"', sep = ",") - domain(cls = "ParamFct", grouping = grouping, levels = real_levels, special_vals = special_vals, default = default, tags = tags, trafo = trafo, depends_expr = substitute(depends)) + domain(cls = "ParamFct", grouping = grouping, levels = real_levels, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "character", depends_expr = substitute(depends), init = init) } #' @export @@ -34,20 +34,18 @@ domain_check.ParamFct = function(param, values, describe_error = TRUE) { check_domain_vectorized(param$ids, param, check_choice, more_args = list(choices = param$levels), describe_error = describe_error) } -#' @export -domain_storage_type.ParamFct = function(param) rep("character", nrow(param)) #' @export domain_nlevels.ParamFct = function(param) map_dbl(param$levels, length) #' @export domain_is_bounded.ParamFct = function(param) rep(TRUE, nrow(param)) #' @export -domain_is_number.ParamFct = function(param) rep(FALSE, nrow(param)) -#' @export -domain_is_categ.ParamFct = function(param) rep(TRUE, nrow(param)) -#' @export domain_qunif.ParamFct = function(param, x) { nlevels = domain_nlevels(param) z = pmin(floor(x * nlevels) + 1, nlevels) # make sure we dont map to upper+1 as.character(pmap(list(param$levels, z), `[[`)) } +#' @export +domain_is_number.ParamFct = function(param) FALSE +#' @export +domain_is_categ.ParamFct = function(param) TRUE diff --git a/R/ParamInt.R b/R/ParamInt.R index 1d212ddd..7beccafd 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -1,7 +1,7 @@ #' @rdname Domain #' @export -p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE) { +p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_DEF, tags = character(), tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, logscale = FALSE, init) { assert_number(tolerance, lower = 0, upper = 0.5) # assert_int will stop for `Inf` values, which we explicitly allow as lower / upper bound if (!isTRUE(is.infinite(lower))) assert_int(lower, tol = 1e-300) else assert_number(lower) @@ -22,7 +22,8 @@ p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ } domain(cls = cls, grouping = cls, lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, - depends_expr = substitute(depends)) + storage_type = "integer", + depends_expr = substitute(depends), init = init) } #' @export @@ -59,14 +60,13 @@ domain_sanitize.ParamInt = function(param, values) { #' @export domain_nlevels.ParamInt = function(param) (param$upper - param$lower) + 1 #' @export -domain_storage_type.ParamInt = function(param) rep("integer", nrow(param)) -#' @export domain_is_bounded.ParamInt = function(param) is.finite(param$lower) && is.finite(param$upper) #' @export -domain_is_number.ParamInt = function(param) rep(TRUE, nrow(param)) -#' @export -domain_is_categ.ParamInt = function(param) rep(FALSE, nrow(param)) -#' @export domain_qunif.ParamInt = function(param, x) { pmax(pmin(as.integer(x * (param$upper + 1) - (x-1) * param$lower), param$upper), param$lower) # extra careful here w/ rounding errors and the x == 1 case } + +#' @export +domain_is_number.ParamInt = function(param) TRUE +#' @export +domain_is_categ.ParamInt = function(param) FALSE diff --git a/R/ParamLgl.R b/R/ParamLgl.R index ab5d1839..8bfdc356 100644 --- a/R/ParamLgl.R +++ b/R/ParamLgl.R @@ -1,8 +1,8 @@ #' @rdname Domain #' @export -p_lgl = function(special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { +p_lgl = function(special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { domain(cls = "ParamLgl", grouping = "ParamLgl", levels = c(TRUE, FALSE), special_vals = special_vals, default = default, - tags = tags, trafo = trafo, depends_expr = substitute(depends)) + tags = tags, trafo = trafo, storage_type = "logical", depends_expr = substitute(depends), init = init) } #' @export @@ -14,17 +14,16 @@ domain_check.ParamLgl = function(param, values, describe_error = TRUE) { check_domain_vectorized(param$id, values, check_flag) } -#' @export -domain_storage_type.ParamLgl = function(param) rep("logical", nrow(param)) #' @export domain_nlevels.ParamLgl = function(param) rep(2, nrow(param)) #' @export domain_is_bounded.ParamLgl = function(param) rep(TRUE, nrow(param)) #' @export -domain_is_number.ParamLgl = function(param) rep(FALSE, nrow(param)) -#' @export -domain_is_categ.ParamLgl = function(param) rep(TRUE, nrow(param)) -#' @export domain_qunif.ParamLgl = function(param, x) { x < 0.5 } + +#' @export +domain_is_number.ParamLgl = function(param) FALSE +#' @export +domain_is_categ.ParamLgl = function(param) TRUE diff --git a/R/ParamS6.R b/R/ParamS6.R index d2615340..015849f0 100644 --- a/R/ParamS6.R +++ b/R/ParamS6.R @@ -1,19 +1,28 @@ #' @rdname Domain #' @export -p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { - support_object = set6_cache_dedup(support) - support_description = if (is.character(support)) support else set6_sane_repr(object) +p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { + set = S6Cache$canonicalize(support) + support_description = if (is.character(support)) support else S6Cache$set6_sane_repr(object) + + if ((("power" %in% names(set)) && set$power != 1) || set$class %nin% c("numeric", "integer")) { + storage_type = "list" + } else { + storage_type = set$class + } domain(cls = "ParamSet6", grouping = support_description, - lower = suppressWarnings(as.numeric(support_object$lower)), - upper = suppressWarnings(as.numeric(support_object$upper)), - levels = as.list(support_object$elements), + cargo = set, + lower = suppressWarnings(as.numeric(set$lower)), + upper = suppressWarnings(as.numeric(set$upper)), + levels = as.list(set$elements), special_vals = special_vals, default = default, tags = tags, trafo = trafo, - depends_expr = depends_expr) + storage_type = storage_type, + depends_expr = substitute(depends), + init = init) } #' @export @@ -28,16 +37,6 @@ domain_check.ParamSet6 = function(param, values, describe_error = TRUE) { sprintf("%s: not contained in %s.", str_collapse(nomatches, sep = ", "), set$strprint()) } -#' @export -domain_storage_type.ParamSet6 = function(param) { - set = set6_cache_get(param$grouping[[1]]) - if (set$power != 1 || set$class %nin% c("numeric", "integer")) { - storage_type = "list" - } else { - storage_type = set$class - } - rep(storage_type, nrow(param)) -} #' @export domain_nlevels.ParamSet6 = function(param) { # we can rely on 'grouping' always giving us the same class @@ -55,103 +54,19 @@ domain_is_bounded.ParamSet6 = function(param) { boundedness = is.finite(card) || (is.finite(set$lower) && is.finite(set$upper)) rep(boundedness, nrow(param)) } +#' @export +domain_qunif.ParamSet6 = function(param, x) stop("undefined") + + #' @export domain_is_number.ParamSet6 = function(param) { - rep(domain_storage_type.ParamSet6(param[1]) != "list", nrow(param)) + param$storage_type[[1]] != "list" } #' @export domain_is_categ.ParamSet6 = function(param) { set = set6_cache_get(param$grouping[[1]]) - categness = set$power == 1 && set$class %nin% c("numeric", "integer") && is.finite(set$properties$cardinality) - - rep(categness, nrow(param)) -} -#' @export -domain_qunif.ParamSet6 = function(param, x) stop("undefined") - - - -set6_cache = new.env(parent = emptyenv()) - -set6_cache_get = function(description) { - assert_string(description) - set = set6_cache[[description]] - if (!is.null(set)) return(set) - s6ns = asNamespace("set6") - settypes = keep(names(s6ns), function(n) { - obj = s6ns[[n]] - is.environment(obj) && is.function(obj[["get_inherit"]]) && (identical(obj, set6::Set) || identical(obj$get_inherit(), set6::Set)) - }) - get_settype = function(st, error_on_not_found = TRUE) { - matches = settypes[match(tolower(st), tolower(settypes), nomatch = 0)] - if (!length(matches)) { - if (error_on_not_found) { - stopf("Unknown settype '%s' in description '%s'", st, description) - } else { - return(NULL) - } - } - if (length(matches) > 1) { - matches = settypes[match(st, settypes, nomatch = 0)] - # even error if error_on_not_found is FALSE, because this is a special message - if (length(matches) != 1) stopf("Settype '%s' in description '%s' matched multiple set6 classes, but none of them matches by case.",st, description) - } - s6ns[[matches]] - } - intervaldef = regmatches(description, regexec("^([[(])(([^,]*),([^,|]*))?(\\| *([a-zA-Z]+))? *([])])$", description))[[1]] - discretedef = regmatches(description, regexec("^\\{([^{}|]*)(\\| *([a-zA-Z]+))? *\\}$", description))[[1]] - trimdesc = trimws(description) - generated = NULL - if (length(intervaldef)) { # interval definition found - type = paste0(intervaldef[[2]], intervaldef[[8]]) - lower = if (intervaldef[[4]] == "") -Inf else suppressWarnings(as.numeric(intervaldef[[4]])) - upper = if (intervaldef[[5]] == "") Inf else suppressWarnings(as.numeric(intervaldef[[5]])) - if (is.na(lower) || is.na(upper)) stopf("Description '%s' is ill-formed interval expression.", description) - universe = if (intervaldef[[7]] == "") set6::ExtendedReals else get_settype(intervaldef[[7]]) - universe = universe$new() - generated = set6::Interval$new(lower = lower, upper = upper, type = type, class = universe$class, universe = universe) - } else if (length(discretedef)) { - if (discretedef[[2]] == "") { - entries = character(0) - } else { - entries = strsplit(discretedef[[2]], " *, *")[[1]] - if ("" %in% entries) stopf("Empty string element not allowed in description '%s'", description) - } - universe = if (intervaldef[[4]] == "") set6::Universal else get_settype(intervaldef[[4]]) - universe = universe$new() - generated = set6::Set$new(elements = entries, universe = universe) - } else if (!is.null(get_settype(trimdesc))) { - generated = get_settype(trimdesc)$new() - } else if (tolower(substr(trimdesc, 1, 1)) == "n" && !is.null(get_settype(trimdesc))) { - generated = set6::setpower(get_settype(trimdesc)$new(), "n") - } else { - stopf("Description '%s' not in set6_cache and could not be constructed.", description) # how do we automatically generate a set from a string? - } - set6_cache_enter(generated, description) - generated -} - -set6_cache_enter = function(object, description = NULL) { - set6_cache[[set6_sane_repr(object)]] = object - if (!is.null(description)) { - set6_cache[[description]] = object - } -} - -set6_cache_dedup = function(object) { - if (is.character(object)) return(set6_cache_get(object)) - assert_class(object, "Set") - repr = set6_sane_repr(object) - alternative = set6_cache[[description]] - if (!is.null(alternative)) return(alternative) - set6_cache_enter(object) - object -} + categness = ("power" %nin% names(set) || set$power == 1) && + set$class %nin% c("numeric", "integer") && is.finite(set$properties$cardinality) -set6_sane_repr = function(object) { - assert_class(object, "Set") - using_unicode_for_whatever_reason = set6::useUnicode() - on.exit(set6::useUnicode(using_unicode_for_whatever_reason)) - set6::useUnicode(FALSE) - object$strprint(n = 1e10) + categness } diff --git a/R/ParamSet.R b/R/ParamSet.R index 04894350..f2e34510 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -71,10 +71,16 @@ ParamSet = R6Class("ParamSet", if (!length(params)) { private$.params = data.table(cls = character(0), grouping = character(0), cargo = list(), lower = numeric(0), upper = numeric(0), tolerance = numeric(0), levels = list(), special_vals = list(), default = list(), tags = list(), trafo = list(), requirements = list(), id = character(0)) + initvalues = named_list() } else { - private$.params = rbindlist(params)[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))][, + private$.params = rbindlist(params)[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))] + + initvalues = private$.params[(init_given), list(id, init)] + initvalues = set_names(initvalues$init, initvalues$id) + private$.params = private$.params[, # put 'id' at the beginning - c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "tags", "trafo", "has_trafo"), with = FALSE] + c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "tags", "trafo", "has_trafo", "storage_type"), with = FALSE] + } setindexv(private$.params, c("id", "cls", "grouping")) assert_names(colnames(private$.params), identical.to = ) @@ -95,9 +101,15 @@ ParamSet = R6Class("ParamSet", } }) }) + self$values = initvalues }, trafo = function(x, param_set) { # param_set argument only here for compatibility + if (!missing(param_set)) { + warning("Giving the `param_set` argument is deprecated!") + } else { + param_set = self # for the .extra_trafo, in case it still has a param_set argument + } trafos = private$.params[names(x), list(id, trafo, has_trafo, value = x), on = "id", nomatch = 0][(has_trafo)] if (nrow(trafos)) { trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] @@ -105,7 +117,11 @@ ParamSet = R6Class("ParamSet", } extra_trafo = self$extra_trafo if (!is.null(extra_trafo)) { - x = extra_trafo(x) + if (test_function(private$.extra_trafo, args = c("x", "param_set"))) { + x = extra_trafo(x, param_set) + } else { + x = extra_trafo(x) + } } x }, @@ -134,8 +150,6 @@ ParamSet = R6Class("ParamSet", set_class(paramrow, c(paramrow$cls, class(paramrow))) }, - get_levels = function(id) domain_nlevels(self$get_param(id)), - #' @description #' Retrieves IDs of contained parameters based on some filter criteria #' selections, `NULL` means no restriction. @@ -316,7 +330,6 @@ ParamSet = R6Class("ParamSet", } } - if (check_strict) { required = setdiff(self$ids(tags = "required"), ns) if (length(required) > 0L) { @@ -482,7 +495,7 @@ ParamSet = R6Class("ParamSet", d$value = list(v) print(d[, setdiff(colnames(d), hide_cols), with = FALSE]) } - if (!is.null(self$trafo)) { + if (self$has_trafo) { catf("Trafo is set.") } # printing the trafa functions sucks (can be very long). dont see a nother option then to suppress it for now } @@ -609,15 +622,14 @@ ParamSet = R6Class("ParamSet", #' Data types of parameters when stored in tables. #' Named with parameter IDs. storage_type = function() { - set_names(private$.params[, list(id, storage_type = domain_storage_type(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", storage_type], - private$.params$id) + set_names(private$.params$storage_type, private$.params$id) }, #' @field is_number (named `logical()`)\cr #' Position is TRUE for [ParamDbl] and [ParamInt]. #' Named with parameter IDs. is_number = function() { - set_names(private$.params[, list(id, is_number = domain_is_number(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], + set_names(private$.params[, list(id, is_number = rep(domain_is_number(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], private$.params$id) }, @@ -625,7 +637,7 @@ ParamSet = R6Class("ParamSet", #' Position is TRUE for [ParamFct] and [ParamLgl]. #' Named with parameter IDs. is_categ = function() { - set_names(private$.params[, list(id, is_categ = domain_is_categ(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], + set_names(private$.params[, list(id, is_categ = rep(domain_is_categ(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], private$.params$id) }, @@ -655,7 +667,11 @@ ParamSet = R6Class("ParamSet", if (missing(f)) { private$.extra_trafo } else { - assert_function(f, args = c("x"), null.ok = TRUE) + if (test_function(f, args = c("x", "param_set"))) { + warning("The 'param_set' argument for '.extra_trafos' is deprecated and will be removed in future versions!") + } else { + assert_function(f, args = "x", null.ok = TRUE) + } private$.extra_trafo = f } }, diff --git a/R/ParamUty.R b/R/ParamUty.R index 11a31bfb..f5543d06 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -1,13 +1,13 @@ #' @rdname Domain #' @export -p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL) { +p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { assert_function(custom_check, nargs = 1, null.ok = TRUE) if (!is.null(custom_check)) { custom_check_result = custom_check(1) assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") } - domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, depends_expr = substitute(depends)) + domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "list", depends_expr = substitute(depends), init = init) } #' @export @@ -19,15 +19,14 @@ domain_check.ParamUty = function(param, values, describe_error = TRUE) { check_domain_vectorize(param$id, values, param$cargo, describe_error = describe_error) } -#' @export -domain_storage_type.ParamUty = function(param) rep("list", nrow(param)) #' @export domain_nlevels.ParamUty = function(param) rep(Inf, nrow(param)) #' @export domain_is_bounded.ParamUty = function(param) rep(FALSE, nrow(param)) #' @export -domain_is_number.ParamUty = function(param) rep(FALSE, nrow(param)) +domain_qunif.ParamUty = function(param, x) stop("undefined") + #' @export -domain_is_categ.ParamUty = function(param) rep(FALSE, nrow(param)) +domain_is_number.ParamUty = function(param) FALSE #' @export -domain_qunif.ParamUty = function(param, x) stop("undefined") +domain_is_categ.ParamUty = function(param) FALSE diff --git a/R/S6ObjectCache.R b/R/S6ObjectCache.R new file mode 100644 index 00000000..fe329ada --- /dev/null +++ b/R/S6ObjectCache.R @@ -0,0 +1,174 @@ + +S6Cache = R6Class("S6Cache", + cloneable = FALSE, + public = list( + initialize = function(cachesize) { + private$.cache = new.env(parent = emptyenv()) + private$.evictable = list() + private$.insertpointer = 1 + private$.evictpointer = 1 + private$.size = 0 + private$.maxsize = cachesize + private$.s6ns = asNamespace("set6") + private$.settypes = keep(names(private$.s6ns), function(n) { + obj = private$.s6ns[[n]] + if (!is.environment(obj) || !is.function(obj[["get_inherit"]])) return(FALSE) + repeat { + if (identical(obj, set6::Set)) return(TRUE) + obj = obj$get_inherit() + if (is.null(obj)) return(FALSE) + } + }) + + }, + get = function(description) { + assert_string(description) + description = trimws(description) + set = private$.cache[[description]] + if (!is.null(set)) return(set) + set = + private$.infer_interval(description) %??% + private$.infer_discrete(description) %??% + private$.infer_settype(description) %??% + if (grepl("^n", description)) { + stype = private$.infer_settype(description) + if (!is.null(stype)) { + set6::setpower(stype, "n") + } + } %??% stopf("Description '%s' not in set6_cache and could not be constructed.", description) # how do we automatically generate a set from a string? + self$enter(set, description) + set + }, + canonicalize = function(object) { + if (test_string(object, pattern = "[^ ]")) return(self$get(object) %??% stopf("Could not get object from string '%s'", object)) + assert_class(object, "Set") + repr = self$set6_sane_repr(object) + alternative = private$.cache[[repr]] + if (!is.null(alternative)) return(alternative) # don't use the given object when one with the same representation is already present. + self$enter(object) + object + + }, + enter = function(object, description = NULL) { + # if we were able to get the object from the description, then we mark it as evictable + private$.enter_lowlevel(object, description %??% self$set6_sane_repr(object), evictable = !is.null(description)) + }, + set6_sane_repr = function(object) { + assert_r6(object, "Set") + using_unicode_for_whatever_reason = set6::useUnicode() + on.exit(set6::useUnicode(using_unicode_for_whatever_reason)) + set6::useUnicode(FALSE) + object$strprint(n = 1e10) + } + ), + private = list( + .cache = NULL, + .size = NULL, + .maxsize = NULL, + + .evictable = NULL, # list indicating names of objects that can be evicted + .insertpointer = NULL, + .evictpointer = NULL, + + .s6ns = NULL, # namespace of set6, from where we get the Set constructors + .settypes = NULL, # names of the set6 constructors + .enter_lowlevel = function(object, descriptions, evictable) { + while (private$.size >= private$.maxsize) { + # evict + if (private$.evictpointer == private$.insertpointer) { # can't evict + if (evictable) { + return(FALSE) # we are full, have nothing to evict, but *this object* is evictable, so don't bother. + } else { + break + } + } + if (private$.evictpointer > private$.maxsize + 1) { + private$.evictpointer = 1 + private$.evictable = private$.evictable[seq_len(min(private$.maxsize, 1.5 * (private$.insertpointer - 1)))] + } + evp = private$.evictpointer + evicting = private$.evictable[[evp]] + assert_character(evicting, min.len = 1, any.missing = FALSE) + rm(list = evicting, envir = private$.cache) + private$.size = private$.size - 1 + private$.evictpointer = evp + 1 + private$.evictable[evp] = list(NULL) # remove list element, but don't shorten list + } + + for (desc in descriptions) { + private$.cache[[desc]] = object + } + + private$.size = private$.size + 1 # even if it has multiple descriptions, the object only counts for one. + if (evictable) { + ins = private$.insertpointer + if (ins > private$.maxsize + 1) { + ins = 1 + } + private$.evictable[[ins]] = descriptions + private$.insertpointer = ins + 1 + } + }, + .get_settype = function(st, description, error_on_not_found = TRUE, construction_args = list()) { + matches = private$.settypes[match(tolower(st), tolower(private$.settypes), nomatch = 0)] + if (!length(matches)) { + if (error_on_not_found) { + stopf("Unknown settype '%s' in description '%s'", st, description) + } else { + return(NULL) + } + } + if (length(matches) > 1) { + matches = private$.settypes[match(st, private$.settypes, nomatch = 0)] + # even error if error_on_not_found is FALSE, because this is a special message + if (length(matches) != 1) stopf("Settype '%s' in description '%s' matched multiple set6 classes, but none of them matches by case.",st, description) + } + do.call(private$.s6ns[[matches]]$new, construction_args) + }, + .infer_interval = function(description) { + intervaldef = regmatches(description, regexec("^([[(])(([^,]*),([^,|]*))?(\\| *([a-zA-Z]+))? *([])]) *(^ *[0-9n]+)?$", description))[[1]] + if (!length(intervaldef)) return(NULL) + type = paste0(intervaldef[[2]], intervaldef[[8]]) + lower = if (intervaldef[[4]] == "") -Inf else suppressWarnings(as.numeric(intervaldef[[4]])) + upper = if (intervaldef[[5]] == "") Inf else suppressWarnings(as.numeric(intervaldef[[5]])) + if (is.na(lower) || is.na(upper)) stopf("Description '%s' is ill-formed interval expression.", description) + universe = if (intervaldef[[7]] == "") set6::ExtendedReals$new() else private$.get_settype(intervaldef[[7]], description) + result = set6::Interval$new(lower = lower, upper = upper, type = type, class = universe$class, universe = universe) + private$.make_power(result, intervaldef[[9]], description) + }, + .infer_discrete = function(description) { + discretedef = regmatches(description, regexec("^\\{([^{}|]*)(\\| *([a-zA-Z]+))? *\\} *(^ *[0-9n]+)?$", description))[[1]] + if (!length(discretedef)) return(NULL) + if (discretedef[[2]] == "") { + entries = character(0) + } else { + entries = strsplit(discretedef[[2]], " *, *")[[1]] + if ("" %in% entries) stopf("Empty string element not allowed in description '%s'", description) + } + universe = if (discretedef[[4]] == "") set6::Universal$new() else private$.get_settype(discretedef[[4]], description) + result = set6::Set$new(elements = entries, universe = universe) + private$.make_power(result, discretedef[[5]], description) + }, + .infer_settype = function(description) { + typedef = regmatches(description, regexec("^([^^ ]*) *(^ *[0-9n]+)?$", description))[[1]] + if (!length(typedef)) return(NULL) + result = private$.get_settype(typedef[[2]], description, error_on_not_found = FALSE) + if (is.null(result)) return(NULL) + private$.make_power(result, typedef[[3]], description) + }, + .make_power = function(set, powerexp, description) { + if (powerexp == "") { + return(set) + } + power = substr(powerexp, 2, nchar(powerexp)) + if (trimws(power) == "n") { + power = "n" + } else { + power = suppressWarnings(as.numeric(power)) + if (is.na(power)) stopf("Ill-formatted power expression in %s", description) + } + return(set6::setpower(set, power)) + } + ) +)$new(cachesize = 2^20) + diff --git a/R/domain.R b/R/domain.R index 17993f0b..1fa775d6 100644 --- a/R/domain.R +++ b/R/domain.R @@ -123,7 +123,7 @@ NULL # @param constargs: arguments of constructor # @param constargs_override: replace these in `constargs`, but don't represent this in printer domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real_, levels = NULL, special_vals = list(), default = NO_DEF, tags = character(0), - tolerance = NA_real_, trafo = NULL, depends_expr = NULL) { + tolerance = NA_real_, trafo = NULL, storage_type = "list", depends_expr = NULL, init) { assert_string(cls) assert_string(grouping) @@ -144,14 +144,19 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real } } + param = data.table(cls = cls, grouping = grouping, cargo = list(cargo), lower = lower, upper = upper, tolerance = tolerance, levels = list(levels), special_vals = list(special_vals), - default = list(default), tags = list(tags), trafo = list(trafo), requirements = list(parse_depends(depends_expr, parent.frame(2)))) + default = list(default), tags = list(tags), trafo = list(trafo), requirements = list(parse_depends(depends_expr, parent.frame(2))), + storage_type = storage_type, init_given = !missing(init), init = if (!missing(init)) init) class(param) = c(cls, "Domain", class(param)) if (!is_nodefault(default)) { domain_assert(param, list(default)) } + if (!missing(init)) { + domain_assert(param, list(init)) + } # repr: what to print constructorcall = match.call(sys.function(-1), sys.call(-1), envir = parent.frame(2)) diff --git a/R/helper.R b/R/helper.R index 08ee3715..360a4f98 100644 --- a/R/helper.R +++ b/R/helper.R @@ -31,6 +31,13 @@ transpose = function(data, ps = NULL, filter_na = TRUE, trafo = TRUE) { return(xs) } +ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_set = FALSE, tag_params = FALSE) { + assert_count(times) + assert_character(prefixes, any.missing = FALSE, unique = TRUE, len = times) + + ps_union(named_list(prefixes, set), tag_set = tag_set, tag_params = tag_params) +} + # Create a ParamSet from a list of ParamSets # This emulates `ParamSetCollection$new(sets)`, except that # - The result is a `ParamSet`, not a `ParamSetCollection` @@ -39,7 +46,7 @@ transpose = function(data, ps = NULL, filter_na = TRUE, trafo = TRUE) { # from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will # have their changed to .. This is also reflected in deps and in `$trafo`. # @param sets: list of ParamSet -ps_union = function(sets, ignore_ids = FALSE) { +ps_union = function(sets, tag_set = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") if (!ignore_ids) { From 31bbb0355f2cb4ab31c495e2c8132288f983a678 Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 26 Mar 2021 19:48:34 +0100 Subject: [PATCH 19/64] some reorg --- DESCRIPTION | 1 + NAMESPACE | 7 - R/ParamSet.R | 511 +++++++++++++++++++------------------- R/S6ObjectCache.R | 10 +- R/domain.R | 2 + man/Domain.Rd | 18 +- man/ParamSet.Rd | 33 ++- man/ParamSetCollection.Rd | 3 +- 8 files changed, 290 insertions(+), 295 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 61a13e17..7445ddbd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -71,6 +71,7 @@ Collate: 'ParamSet.R' 'ParamSetCollection.R' 'ParamUty.R' + 'S6ObjectCache.R' 'Sampler.R' 'Sampler1D.R' 'SamplerHierarchical.R' diff --git a/NAMESPACE b/NAMESPACE index bed456bf..cf9c2e05 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -45,13 +45,6 @@ S3method(domain_qunif,default) S3method(domain_sanitize,ParamDbl) S3method(domain_sanitize,ParamInt) S3method(domain_sanitize,default) -S3method(domain_storage_type,ParamDbl) -S3method(domain_storage_type,ParamFct) -S3method(domain_storage_type,ParamInt) -S3method(domain_storage_type,ParamLgl) -S3method(domain_storage_type,ParamSet6) -S3method(domain_storage_type,ParamUty) -S3method(domain_storage_type,default) S3method(print,Domain) S3method(print,FullTuneToken) S3method(print,ObjectTuneToken) diff --git a/R/ParamSet.R b/R/ParamSet.R index f2e34510..8f19ec4c 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -104,52 +104,6 @@ ParamSet = R6Class("ParamSet", self$values = initvalues }, - trafo = function(x, param_set) { # param_set argument only here for compatibility - if (!missing(param_set)) { - warning("Giving the `param_set` argument is deprecated!") - } else { - param_set = self # for the .extra_trafo, in case it still has a param_set argument - } - trafos = private$.params[names(x), list(id, trafo, has_trafo, value = x), on = "id", nomatch = 0][(has_trafo)] - if (nrow(trafos)) { - trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] - insert_named(x, set_names(trafos$value, trafos$id)) - } - extra_trafo = self$extra_trafo - if (!is.null(extra_trafo)) { - if (test_function(private$.extra_trafo, args = c("x", "param_set"))) { - x = extra_trafo(x, param_set) - } else { - x = extra_trafo(x) - } - } - x - }, - - qunif = function(x) { - assert(check_data_frame(x, types = "numeric", min.cols = 1), check_matrix(x, mode = "numeric", min.cols = 1)) - if (is.matrix(x)) { - qassert(x, "N[0,1]") - } else { - qassertr(x, "N[0,1]") - x = as.matrix(x) - } - assert_names(colnames(x), type = "unique", subset.of = private$.params$id) - - x = t(x) - params = private$.params[rownames(x), on = "id"] - params$result = list() - params[, result := list(as.list(as.data.frame(t(matrix(domain_qunif(recover_domain(.SD, .BY), x[id, ]), nrow = .N))))), by = c("cls", "grouping")] - as.data.table(set_names(params$result, params$id)) - }, - - get_param = function(id) { - .id = id - paramrow = private$.params[id == .id] - if (!nrow(paramrow)) stopf("No param with id '%s'", id) - set_class(paramrow, c(paramrow$cls, class(paramrow))) - }, - #' @description #' Retrieves IDs of contained parameters based on some filter criteria #' selections, `NULL` means no restriction. @@ -222,68 +176,37 @@ ParamSet = R6Class("ParamSet", values[intersect(names(values), self$ids(class = class, tags = tags, any_tags = any_tags))] }, - #' @description - #' Create a new `ParamSet` restricted to the passed IDs. - #' - #' @param ids (`character()`). - #' @return `ParamSet`. - subset = function(ids, allow_dangling_dependencies = FALSE) { - param_ids = private$.params$id - - assert_subset(ids, param_ids) - deps = self$deps - if (!allow_dangling_dependencies && nrow(deps)) { # check that all required / leftover parents are still in new ids - on = NULL - parents = unique(deps[id %in% ids, on]) - pids_not_there = setdiff(parents, ids) - if (length(pids_not_there) > 0L) { - stopf(paste0("Subsetting so that dependencies on params exist which would be gone: %s.", - "\nIf you still want to subset, set allow_dangling_dependencies to TRUE."), str_collapse(pids_not_there)) + trafo = function(x, param_set) { # param_set argument only here for compatibility + if (!missing(param_set)) { + warning("Giving the `param_set` argument is deprecated!") + } else { + param_set = self # for the .extra_trafo, in case it still has a param_set argument + } + trafos = private$.params[names(x), list(id, trafo, has_trafo, value = x), on = "id", nomatch = 0][(has_trafo)] + if (nrow(trafos)) { + trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] + insert_named(x, set_names(trafos$value, trafos$id)) + } + extra_trafo = self$extra_trafo + if (!is.null(extra_trafo)) { + if (test_function(private$.extra_trafo, args = c("x", "param_set"))) { + x = extra_trafo(x, param_set) + } else { + x = extra_trafo(x) } } - result = ParamSet$new(extra_trafo = self$extra_trafo) - result$.__enclos_env__$private$.params = private$.params[id %in% ids] - result$assert_values = FALSE - result$deps = deps - # restrict to ids already in pvals - values = self$values - result$values = values[intersect(names(values), ids)] - result$assert_values = TRUE - result - }, - - subspaces = function(ids = private$.params$id) { - sapply(ids, simplify = FALSE, function(get_id) { - result = ParamSet$new(extra_trafo = self$extra_trafo) - result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] - result$assert_values = FALSE - result$values = values[intersect(names(values), get_id)] - result$assert_values = TRUE - result - }) + x }, - #' @description - #' Create a `ParamSet` from this object, even if this object itself is not - #' a `ParamSet`. - flatten = function() { - self$subset(private$.params$id) + test_constraint = function(x, assert_value = TRUE) { + if (assert_value) self$assert(x) + assert_flag(private$.constraint(x)) }, - #' @description - #' Construct a [`ParamSet`] to tune over. Constructed from [`TuneToken`] in `$values`, see [`to_tune()`]. - #' - #' @param values (`named list`): optional named list of [`TuneToken`] objects to convert, in place of `$values`. - search_space = function(values = self$values) { - assert_list(values) - assert_names(names(values), subset.of = self$ids()) - pars = private$get_tune_ps(values) - on = NULL # pacify static code check - dangling_deps = pars$deps[!on %in% pars$ids()] - if (nrow(dangling_deps)) { - stopf("Dangling dependencies not allowed: Dependencies on %s dangling.", str_collapse(dangling_deps$on)) - } - pars + test_constraint_dt = function(x, assert_value = TRUE) { + assert_data_table(x) + if (assert_value) self$assert_dt(x) + map_dbl(transpose(x), self$test_constraint) }, #' @description @@ -336,9 +259,10 @@ ParamSet = R6Class("ParamSet", return(if (describe_error) sprintf("Missing required parameters: %s", str_collapse(required)) else FALSE) } return(check_dependencies(xs, describe_error = describe_error)) + if (!self$test_constraint(xs)) return(if (describe_error) sprintf("Constraint not fulfilled.") else FALSE) } - return(TRUE) # we passed all checks + TRUE # we passed all checks }, check_dependencies = function(xs, describe_error = TRUE) { @@ -357,7 +281,7 @@ ParamSet = R6Class("ParamSet", # - if 'id' is not there. but that is skipped (deps[id %in% ns] filter) if (on %in% ns && cond$test(onval)) return(NULL) if (!describe_error) { - break_early = TRUE + break_early <<- TRUE return(FALSE) } msg = sprintf("%s: can only be set if the following condition is met '%s'.", @@ -439,6 +363,92 @@ ParamSet = R6Class("ParamSet", #' @return If successful `xs` invisibly, if not an error message. assert_dt = function(xdt, check_strict = FALSE, .var.name = vname(xdt)) makeAssertion(xdt, self$check_dt(xdt, check_strict = check_strict), .var.name, NULL), # nolint + qunif = function(x) { + assert(check_data_frame(x, types = "numeric", min.cols = 1), check_matrix(x, mode = "numeric", min.cols = 1)) + if (is.matrix(x)) { + qassert(x, "N[0,1]") + } else { + qassertr(x, "N[0,1]") + x = as.matrix(x) + } + assert_names(colnames(x), type = "unique", subset.of = private$.params$id) + + x = t(x) + params = private$.params[rownames(x), on = "id"] + params$result = list() + params[, result := list(as.list(as.data.frame(t(matrix(domain_qunif(recover_domain(.SD, .BY), x[id, ]), nrow = .N))))), by = c("cls", "grouping")] + as.data.table(set_names(params$result, params$id)) + }, + + get_param = function(id) { + .id = id + paramrow = private$.params[id == .id] + if (!nrow(paramrow)) stopf("No param with id '%s'", id) + set_class(paramrow, c(paramrow$cls, class(paramrow))) + }, + + #' @description + #' Create a new `ParamSet` restricted to the passed IDs. + #' + #' @param ids (`character()`). + #' @return `ParamSet`. + subset = function(ids, allow_dangling_dependencies = FALSE) { + param_ids = private$.params$id + + assert_subset(ids, param_ids) + deps = self$deps + if (!allow_dangling_dependencies && nrow(deps)) { # check that all required / leftover parents are still in new ids + on = NULL + parents = unique(deps[id %in% ids, on]) + pids_not_there = setdiff(parents, ids) + if (length(pids_not_there) > 0L) { + stopf(paste0("Subsetting so that dependencies on params exist which would be gone: %s.", + "\nIf you still want to subset, set allow_dangling_dependencies to TRUE."), str_collapse(pids_not_there)) + } + } + result = ParamSet$new(extra_trafo = self$extra_trafo) + result$.__enclos_env__$private$.params = private$.params[id %in% ids] + result$assert_values = FALSE + result$deps = deps + # restrict to ids already in pvals + values = self$values + result$values = values[intersect(names(values), ids)] + result$assert_values = TRUE + result + }, + + subspaces = function(ids = private$.params$id) { + sapply(ids, simplify = FALSE, function(get_id) { + result = ParamSet$new(extra_trafo = self$extra_trafo) + result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] + result$assert_values = FALSE + result$values = values[intersect(names(values), get_id)] + result$assert_values = TRUE + result + }) + }, + + #' @description + #' Create a `ParamSet` from this object, even if this object itself is not + #' a `ParamSet`. + flatten = function() self$subset(private$.params$id), + + #' @description + #' Construct a [`ParamSet`] to tune over. Constructed from [`TuneToken`] in `$values`, see [`to_tune()`]. + #' + #' @param values (`named list`): optional named list of [`TuneToken`] objects to convert, in place of `$values`. + search_space = function(values = self$values) { + assert_list(values) + assert_names(names(values), subset.of = self$ids()) + pars = private$get_tune_ps(values) + on = NULL # pacify static code check + dangling_deps = pars$deps[!on %in% pars$ids()] + if (nrow(dangling_deps)) { + stopf("Dangling dependencies not allowed: Dependencies on %s dangling.", str_collapse(dangling_deps$on)) + } + pars + }, + #' @description #' Adds a dependency to this set, so that param `id` now depends on param `on`. #' @@ -502,109 +512,39 @@ ParamSet = R6Class("ParamSet", ), active = list( + + #' @field data (`data.table`) `data.table` representation of the `ParamSet`. data = function(v) { if (!missing(v)) stop("v is read-only") private$.params[, list(id, class = cls, lower, upper, levels, nlevels = self$nlevels, is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags)] }, - #' @template field_params - params = function(rhs) { - if (!missing(rhs) && !identical(rhs, private$.params)) { - stop("$params is read-only.") + #' @template field_values + values = function(xs) { + if (missing(xs)) { + return(private$.values) } - map(private$.params, self$param) # giga-slow - }, - - #' @template field_deps - deps = function(v) { - if (missing(v)) { - private$.deps - } else { - assert_data_table(v) - if (nrow(v)) { - # only test for things without which things would seriously break - assert_names(colnames(v), identical.to = c("id", "on", "cond")) - assert_subset(v$id, private$.params$id) - assert_character(v$on, any.missing = FALSE) - assert_list(v$cond, types = "Condition", any.missing = FALSE) - } else { - v = data.table(id = character(0), on = character(0), cond = list()) # make sure we have the right columns + if (self$assert_values) { + self$assert(xs) + if (test_list(xs, types = "TuneToken")) { + private$get_tune_ps(xs) # check that to_tune() are valid } - private$.deps = v } - }, - - #' @field length (`integer(1)`)\cr - #' Number of contained [Param]s. - length = function() { - nrow(private$.params) - }, - - #' @field is_empty (`logical(1)`)\cr - #' Is the `ParamSet` empty? - is_empty = function() { - nrow(private$.params) == 0L - }, - - #' @field class (named `character()`)\cr - #' Classes of contained parameters, named with parameter IDs. - class = function() { - set_names(private$.params$class, private$.params$id) - }, - - #' @field lower (named `double()`)\cr - #' Lower bounds of parameters (`NA` if parameter is not numeric). - #' Named with parameter IDs. - lower = function() { - set_names(private$.params$lower, private$.params$id) - }, - - #' @field upper (named `double()`)\cr - #' Upper bounds of parameters (`NA` if parameter is not numeric). - #' Named with parameter IDs. - upper = function() { - set_names(private$.params$upper, private$.params$id) - }, - - #' @field levels (named `list()`)\cr - #' List of character vectors of allowed categorical values of contained parameters. - #' `NULL` if the parameter is not categorical. - #' Named with parameter IDs. - levels = function() { - set_names(private$.params$levels, private$.params$id) - }, - - #' @field nlevels (named `integer()`)\cr - #' Number of categorical levels per parameter, `Inf` for double parameters or unbounded integer parameters. - #' Named with param IDs. - nlevels = function() { - set_names(private$.params[, list(id, nlevels = domain_nlevels(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", nlevels], - private$.params$id) - }, - - #' @field is_bounded (named `logical()`)\cr - #' Do all parameters have finite bounds? - #' Named with parameter IDs. - is_bounded = function() { - set_names(private$.params[, list(id, is_bounded = domain_is_bounded(recover_domain(.SD, .BY))), by = c("cls", "grouping")][private$.params$id, on = "id", is_bounded], - private$.params$id) - }, - - #' @field special_vals (named `list()` of `list()`)\cr - #' Special values for all parameters. - #' Named with parameter IDs. - special_vals = function() { - set_names(private$.params$special_vals, private$.params$id) - }, - - #' @field default (named `list()`)\cr - #' Default values of all parameters. - #' If no default exists, element is not present. - #' Named with parameter IDs. - default = function() { - defaulthaving = private$.params[!map_lgl(default, is_nodefault), list(id, default)] - set_names(defaulthaving$default, defaulthaving$id) + if (length(xs) == 0L) { + xs = named_list() + } else if (self$assert_values) { # this only makes sense when we have asserts on + # convert all integer params really to storage type int, move doubles to within bounds etc. + # solves issue #293, #317 + nontt = discard(xs, inherits, "TuneToken") + sanitised = private$.params[names(nontt), on = "id"][, values := nontt][ + !pmap_lgl(list(special_vals, values), has_element), + list(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] + insert_named(xx, set_names(sanitised$values, sanitised$id)) + } + # store with param ordering, return value with original ordering + private$.values = xs[private$.params[id %in% names(xs), id]] + xs }, #' @template field_tags @@ -618,39 +558,12 @@ ParamSet = R6Class("ParamSet", set_names(private$.params$tags, private$.params$id) }, - #' @field storage_type (`character()`)\cr - #' Data types of parameters when stored in tables. - #' Named with parameter IDs. - storage_type = function() { - set_names(private$.params$storage_type, private$.params$id) - }, - - #' @field is_number (named `logical()`)\cr - #' Position is TRUE for [ParamDbl] and [ParamInt]. - #' Named with parameter IDs. - is_number = function() { - set_names(private$.params[, list(id, is_number = rep(domain_is_number(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping")][private$.params$id, on = "id", is_number], - private$.params$id) - }, - - #' @field is_categ (named `logical()`)\cr - #' Position is TRUE for [ParamFct] and [ParamLgl]. - #' Named with parameter IDs. - is_categ = function() { - set_names(private$.params[, list(id, is_categ = rep(domain_is_categ(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping")][private$.params$id, on = "id", is_categ], - private$.params$id) - }, - - #' @field all_numeric (`logical(1)`)\cr - #' Is `TRUE` if all parameters are [ParamDbl] or [ParamInt]. - all_numeric = function() { - all(self$is_number) - }, - - #' @field all_categorical (`logical(1)`)\cr - #' Is `TRUE` if all parameters are [ParamFct] and [ParamLgl]. - all_categorical = function() { - all(self$is_categ) + #' @template field_params + params = function(rhs) { + if (!missing(rhs) && !identical(rhs, private$.params)) { + stop("$params is read-only.") + } + map(private$.params, self$param) # giga-slow }, #' @field extra_trafo (`function(x, param_set)`)\cr @@ -676,48 +589,122 @@ ParamSet = R6Class("ParamSet", } }, - #' @field has_trafo (`logical(1)`)\cr - #' Has the set a `trafo` function? - has_trafo = function() { - !is.null(private$.extra_trafo) || any(private$.params$has_trafo) + constraint = function(f) { + if (missing(f)) { + private$.constraint + } else { + assert_function(f, args = x, null.ok = TRUE) + private$.constraint = f + } }, - #' @template field_values - values = function(xs) { - if (missing(xs)) { - return(private$.values) - } - if (self$assert_values) { - self$assert(xs) - if (test_list(xs, types = "TuneToken")) { - private$get_tune_ps(xs) # check that to_tune() are valid + + #' @template field_deps + deps = function(v) { + if (missing(v)) { + private$.deps + } else { + assert_data_table(v) + if (nrow(v)) { + # only test for things without which things would seriously break + assert_names(colnames(v), identical.to = c("id", "on", "cond")) + assert_subset(v$id, private$.params$id) + assert_character(v$on, any.missing = FALSE) + assert_list(v$cond, types = "Condition", any.missing = FALSE) + } else { + v = data.table(id = character(0), on = character(0), cond = list()) # make sure we have the right columns } + private$.deps = v } - if (length(xs) == 0L) { - xs = named_list() - } else if (self$assert_values) { # this only makes sense when we have asserts on - # convert all integer params really to storage type int, move doubles to within bounds etc. - # solves issue #293, #317 - nontt = discard(xs, inherits, "TuneToken") - sanitised = private$.params[names(nontt), on = "id"][, values := nontt][ - !pmap_lgl(list(special_vals, values), has_element), - list(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] - insert_named(xx, set_names(sanitised$values, sanitised$id)) - } - # store with param ordering, return value with original ordering - private$.values = xs[private$.params[id %in% names(xs), id]] - xs }, - #' @field has_deps (`logical(1)`)\cr - #' Has the set parameter dependencies? - has_deps = function() { - nrow(self$deps) > 0L + ############################ + # ParamSet flags + + #' @field length (`integer(1)`)\cr Number of contained [Param]s. + length = function() nrow(private$.params), + #' @field is_empty (`logical(1)`)\cr Is the `ParamSet` empty? Named with parameter IDs. + is_empty = function() nrow(private$.params) == 0L, + #' @field has_trafo (`logical(1)`)\cr Whether a `trafo` function is present, in parameters or in `extra_trafo`. + has_trafo = function() !is.null(private$.extra_trafo) || any(private$.params$has_trafo), + #' @field has_deps (`logical(1)`)\cr Whether the parameter dependencies are present + has_deps = function() nrow(self$deps) > 0L, + #' @field has_constraint (`logical(1)`)\cr Whether parameter constraint is set. + has_constraint = function() !is.null(private$.constraint), + #' @field all_numeric (`logical(1)`)\cr Is `TRUE` if all parameters are [ParamDbl] or [ParamInt]. + all_numeric = function() all(self$is_number), + #' @field all_categorical (`logical(1)`)\cr Is `TRUE` if all parameters are [ParamFct] and [ParamLgl]. + all_categorical = function() all(self$is_categ), + + + ############################ + # Per-Parameter properties + + #' @field class (named `character()`)\cr Classes of contained parameters. Named with parameter IDs. + class = function() private$.params[, set_names(class, id)], + #' @field lower (named `double()`)\cr Lower bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. + lower = function() private$.params[, set_names(class, lower)], + #' @field upper (named `double()`)\cr Upper bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. + upper = function() private$.params[, set_names(class, upper)], + #' @field levels (named `list()` of `character`)\cr Allowed levels of categorical parameters (`NULL` for non-categoricals). + #' Named with parameter IDs. + levels = function() private$.params[, set_names(class, levels)], + #' @field storage_type (`character()`)\cr Data types of parameters when stored in tables. Named with parameter IDs. + storage_type = function() private$.params[, set_names(class, storage_type)], + #' @field special_vals (named `list()` of `list()`)\cr Special values for all parameters. Named with parameter IDs. + special_vals = function() private$.params[, set_names(class, special_vals)], + #' @field default (named `list()`)\cr Default values of all parameters. If no default exists, element is not present. + #' Named with parameter IDs. + default = function() private$.params[!map_lgl(default, is_nodefault), set_names(default, id)], + + ############################ + # Per-Parameter class properties (S3 method call) + + #' @field nlevels (named `integer()`)\cr Number of distinct levels of parameters. `Inf` for double parameters or unbounded integer parameters. + #' Named with param IDs. + nlevels = function() { + private$.params[, + list(id, nlevels = domain_nlevels(recover_domain(.SD, .BY))), + by = c("cls", "grouping") + ][ + private$.params$id, on = "id", set_names(nlevels, id) + ] + }, + + #' @field is_number (named `logical()`)\cr Whether parameter is [ParamDbl] or [ParamInt]. Named with parameter IDs. + is_number = function() { + private$.params[, + list(id, is_number = rep(domain_is_number(recover_domain(.SD, .BY)), .N)), + by = c("cls", "grouping") + ][ + private$.params$id, on = "id", set_names(is_number, id) + ] + }, + + #' @field is_categ (named `logical()`)\cr Whether parameter is [ParamFct] or [ParamLgl]. Named with parameter IDs. + is_categ = function() { + private$.params[, + list(id, is_categ = rep(domain_is_categ(recover_domain(.SD, .BY)), .N)), + by = c("cls", "grouping") + ][ + private$.params$id, on = "id", set_names(is_categ, id) + ] + }, + + #' @field is_bounded (named `logical()`)\cr Whether parameters have finite bounds. Named with parameter IDs. + is_bounded = function() { + private$.params[, + list(id, is_bounded = domain_is_bounded(recover_domain(.SD, .BY))), + by = c("cls", "grouping") + ][ + private$.params$id, on = "id", set_names(is_bounded, id) + ] } ), private = list( .extra_trafo = NULL, + .constraint = NULL, .params = NULL, .values = named_list(), .deps = data.table(id = character(0L), on = character(0L), cond = list()), diff --git a/R/S6ObjectCache.R b/R/S6ObjectCache.R index fe329ada..618ddebd 100644 --- a/R/S6ObjectCache.R +++ b/R/S6ObjectCache.R @@ -36,22 +36,18 @@ S6Cache = R6Class("S6Cache", set6::setpower(stype, "n") } } %??% stopf("Description '%s' not in set6_cache and could not be constructed.", description) # how do we automatically generate a set from a string? - self$enter(set, description) + self$enter(self$canonicalize(set), description) set }, canonicalize = function(object) { if (test_string(object, pattern = "[^ ]")) return(self$get(object) %??% stopf("Could not get object from string '%s'", object)) assert_class(object, "Set") repr = self$set6_sane_repr(object) - alternative = private$.cache[[repr]] - if (!is.null(alternative)) return(alternative) # don't use the given object when one with the same representation is already present. - self$enter(object) - object - + private$.cache[[repr]] %??% object # don't use the given object when one with the same representation is already present. }, enter = function(object, description = NULL) { # if we were able to get the object from the description, then we mark it as evictable - private$.enter_lowlevel(object, description %??% self$set6_sane_repr(object), evictable = !is.null(description)) + private$.enter_lowlevel(object, c(description, self$set6_sane_repr(object)), evictable = TRUE) }, set6_sane_repr = function(object) { assert_r6(object, "Set") diff --git a/R/domain.R b/R/domain.R index 1fa775d6..169741c4 100644 --- a/R/domain.R +++ b/R/domain.R @@ -153,9 +153,11 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real if (!is_nodefault(default)) { domain_assert(param, list(default)) + if ("required" %in% tags) stop("A 'required' parameter can not have a 'default'.\nWhen the method behaves the same as if the parameter value were 'X' whenever the parameter is missing, then 'X' should be a 'default', but the 'required' indicates that the parameter may not be missing.") } if (!missing(init)) { domain_assert(param, list(init)) + if (identical(init, default)) warning("Initial value and 'default' value seem to be the same, this is usually a mistake due to a misunderstanding of the meaning of 'default'.\nWhen the method behaves the same as if the parameter value were 'X' whenever the parameter is missing, then 'X' should be a 'default' (but then there is no point in setting it as initial value). 'default' should not be used to indicate the value with which values are initialized.") } # repr: what to print diff --git a/man/Domain.Rd b/man/Domain.Rd index b22b1053..c15eefb4 100644 --- a/man/Domain.Rd +++ b/man/Domain.Rd @@ -20,7 +20,8 @@ p_dbl( tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, - logscale = FALSE + logscale = FALSE, + init ) p_fct( @@ -29,7 +30,8 @@ p_fct( default = NO_DEF, tags = character(), depends = NULL, - trafo = NULL + trafo = NULL, + init ) p_int( @@ -41,7 +43,8 @@ p_int( tolerance = sqrt(.Machine$double.eps), depends = NULL, trafo = NULL, - logscale = FALSE + logscale = FALSE, + init ) p_lgl( @@ -49,7 +52,8 @@ p_lgl( default = NO_DEF, tags = character(), depends = NULL, - trafo = NULL + trafo = NULL, + init ) p_s6( @@ -58,7 +62,8 @@ p_s6( default = NO_DEF, tags = character(), depends = NULL, - trafo = NULL + trafo = NULL, + init ) p_uty( @@ -67,7 +72,8 @@ p_uty( default = NO_DEF, tags = character(), depends = NULL, - trafo = NULL + trafo = NULL, + init ) } \arguments{ diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 433d43a3..366e933e 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -68,8 +68,7 @@ Lists all (direct) dependency parents of a param, through parameter IDs. Internally created by a call to \code{add_dep}. Settable, if you want to remove dependencies or perform other changes.} -\item{\code{length}}{(\code{integer(1)})\cr -Number of contained \link{Param}s.} +\item{\code{length}}{(\code{integer(1)})\cr Number of contained \link{Param}s.} \item{\code{is_empty}}{(\code{logical(1)})\cr Is the \code{ParamSet} empty?} @@ -159,9 +158,10 @@ Has the set parameter dependencies?} \itemize{ \item \href{#method-new}{\code{ParamSet$new()}} \item \href{#method-trafo}{\code{ParamSet$trafo()}} +\item \href{#method-test_constraint}{\code{ParamSet$test_constraint()}} +\item \href{#method-test_constraint_dt}{\code{ParamSet$test_constraint_dt()}} \item \href{#method-qunif}{\code{ParamSet$qunif()}} \item \href{#method-get_param}{\code{ParamSet$get_param()}} -\item \href{#method-get_levels}{\code{ParamSet$get_levels()}} \item \href{#method-ids}{\code{ParamSet$ids()}} \item \href{#method-get_values}{\code{ParamSet$get_values()}} \item \href{#method-subset}{\code{ParamSet$subset()}} @@ -224,6 +224,24 @@ Default \code{FALSE}.} \if{html}{\out{
}}\preformatted{ParamSet$trafo(x, param_set)}\if{html}{\out{
}} } +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-test_constraint}{}}} +\subsection{Method \code{test_constraint()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$test_constraint(x, assert_value = TRUE)}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-test_constraint_dt}{}}} +\subsection{Method \code{test_constraint_dt()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$test_constraint_dt(x, assert_value = TRUE)}\if{html}{\out{
}} +} + } \if{html}{\out{
}} \if{html}{\out{}} @@ -242,15 +260,6 @@ Default \code{FALSE}.} \if{html}{\out{
}}\preformatted{ParamSet$get_param(id)}\if{html}{\out{
}} } -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_levels}{}}} -\subsection{Method \code{get_levels()}}{ -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$get_levels(id)}\if{html}{\out{
}} -} - } \if{html}{\out{
}} \if{html}{\out{}} diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 662dbc73..458c70e0 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -76,7 +76,6 @@ When you set values, all previously set values will be unset / removed.} \item \out{}\href{../../paradox/html/ParamSet.html#method-check_dt}{\code{paradox::ParamSet$check_dt()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-flatten}{\code{paradox::ParamSet$flatten()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-format}{\code{paradox::ParamSet$format()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-get_levels}{\code{paradox::ParamSet$get_levels()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-get_param}{\code{paradox::ParamSet$get_param()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-get_values}{\code{paradox::ParamSet$get_values()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-ids}{\code{paradox::ParamSet$ids()}}\out{} @@ -86,6 +85,8 @@ When you set values, all previously set values will be unset / removed.} \item \out{}\href{../../paradox/html/ParamSet.html#method-subset}{\code{paradox::ParamSet$subset()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-subspaces}{\code{paradox::ParamSet$subspaces()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-test}{\code{paradox::ParamSet$test()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-test_constraint}{\code{paradox::ParamSet$test_constraint()}}\out{} +\item \out{}\href{../../paradox/html/ParamSet.html#method-test_constraint_dt}{\code{paradox::ParamSet$test_constraint_dt()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-test_dt}{\code{paradox::ParamSet$test_dt()}}\out{} \item \out{}\href{../../paradox/html/ParamSet.html#method-trafo}{\code{paradox::ParamSet$trafo()}}\out{} } From 0c9cfc2aaa027d0f802e142c554309969151e5f7 Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 26 Mar 2021 21:33:31 +0100 Subject: [PATCH 20/64] PSC & PSC trafos --- R/ParamSet.R | 55 ++++++++++++++++---------------- R/ParamSetCollection.R | 72 ++++++++++++++++++++++++++++++------------ 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index 8f19ec4c..6f7a0f8f 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -70,20 +70,19 @@ ParamSet = R6Class("ParamSet", if (!length(params)) { private$.params = data.table(cls = character(0), grouping = character(0), cargo = list(), lower = numeric(0), upper = numeric(0), tolerance = numeric(0), - levels = list(), special_vals = list(), default = list(), tags = list(), trafo = list(), requirements = list(), id = character(0)) + levels = list(), special_vals = list(), default = list(), trafo = list(), requirements = list(), id = character(0)) initvalues = named_list() } else { private$.params = rbindlist(params)[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))] - initvalues = private$.params[(init_given), list(id, init)] - initvalues = set_names(initvalues$init, initvalues$id) - private$.params = private$.params[, - # put 'id' at the beginning - c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "tags", "trafo", "has_trafo", "storage_type"), with = FALSE] + initvalues = private$.params[(init_given), set_names(init, id)] + private$.tags = private$.params[, set_names(tags, id)] + + private$.params[, setdiff(colnames(private$.params), paramcols) := NULL] } setindexv(private$.params, c("id", "cls", "grouping")) - assert_names(colnames(private$.params), identical.to = ) + assert_names(colnames(private$.params), identical.to = paramcols) # add Dependencies imap(params, function(p, name) { @@ -120,10 +119,10 @@ ParamSet = R6Class("ParamSet", if (is.null(class) && is.null(tags) && is.null(any_tags)) { return(private$.params$id) } - .tags = tags + selftags = private$.tags ptbl[(is.null(class) | cls %in% class) & - (is.null(tags) | map_lgl(tags, function(tg) all(.tags %in% tg))) & - (is.null(any_tags) | map_lgl(tags, function(tg) any(any_tags %in% tg))), + (is.null(tags) | map_lgl(selftags, function(tg) all(tags %in% tg))) & + (is.null(any_tags) | map_lgl(selftags, function(tg) any(any_tags %in% tg))), id] }, @@ -173,7 +172,7 @@ ParamSet = R6Class("ParamSet", } } - values[intersect(names(values), self$ids(class = class, tags = tags, any_tags = any_tags))] + values[match(self$ids(class = class, tags = tags, any_tags = any_tags), names(values), nomatch = 0)] }, trafo = function(x, param_set) { # param_set argument only here for compatibility @@ -187,9 +186,9 @@ ParamSet = R6Class("ParamSet", trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] insert_named(x, set_names(trafos$value, trafos$id)) } - extra_trafo = self$extra_trafo + extra_trafo = private$.extra_trafo if (!is.null(extra_trafo)) { - if (test_function(private$.extra_trafo, args = c("x", "param_set"))) { + if (test_function(extra_trafo, args = c("x", "param_set"))) { x = extra_trafo(x, param_set) } else { x = extra_trafo(x) @@ -200,7 +199,7 @@ ParamSet = R6Class("ParamSet", test_constraint = function(x, assert_value = TRUE) { if (assert_value) self$assert(x) - assert_flag(private$.constraint(x)) + assert_flag(is.null(private$.constraint) || private$.constraint(x)) }, test_constraint_dt = function(x, assert_value = TRUE) { @@ -407,12 +406,13 @@ ParamSet = R6Class("ParamSet", } } result = ParamSet$new(extra_trafo = self$extra_trafo) - result$.__enclos_env__$private$.params = private$.params[id %in% ids] + result$.__enclos_env__$private$.params = private$.params[id %in% ids, paramcols, with = FALSE] result$assert_values = FALSE result$deps = deps + result$constraint = private$.constraint # restrict to ids already in pvals values = self$values - result$values = values[intersect(names(values), ids)] + result$values = values[match(ids, names(values), nomatch = 0)] result$assert_values = TRUE result }, @@ -420,9 +420,10 @@ ParamSet = R6Class("ParamSet", subspaces = function(ids = private$.params$id) { sapply(ids, simplify = FALSE, function(get_id) { result = ParamSet$new(extra_trafo = self$extra_trafo) + # constraint make no sense here, basically by definition result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] result$assert_values = FALSE - result$values = values[intersect(names(values), get_id)] + result$values = values[match(get_id, names(values), nomatch = 0)] result$assert_values = TRUE result }) @@ -517,7 +518,7 @@ ParamSet = R6Class("ParamSet", data = function(v) { if (!missing(v)) stop("v is read-only") private$.params[, list(id, class = cls, lower, upper, levels, nlevels = self$nlevels, - is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags)] + is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags = private$.tags)] }, #' @template field_values @@ -552,18 +553,18 @@ ParamSet = R6Class("ParamSet", if (!missing(v)) { assert_list(v, any.missing = FALSE, types = "character") assert_names(names(v), permutation.of = private$.params$id) - private$.params[names(v), tags := v] + private$.tags = v[match(private$.params$id, names(v), nomatch = 0)] return(v) } - set_names(private$.params$tags, private$.params$id) + private$.tags }, #' @template field_params params = function(rhs) { - if (!missing(rhs) && !identical(rhs, private$.params)) { + if (!missing(rhs)) { stop("$params is read-only.") } - map(private$.params, self$param) # giga-slow + private$.params[, paramcols, with = FALSE] }, #' @field extra_trafo (`function(x, param_set)`)\cr @@ -707,14 +708,10 @@ ParamSet = R6Class("ParamSet", .constraint = NULL, .params = NULL, .values = named_list(), + .tags = named_list(), .deps = data.table(id = character(0L), on = character(0L), cond = list()), - # return a slot / AB, as a named vec, named with id (and can enforce a certain vec-type) - get_member_with_idnames = function(member, astype) { - params = self$params_unid - set_names(astype(map(params, member)), names(params)) - }, get_tune_ps = function(values) { - selfparams = self$params_unid # cache to avoid performance hit in ParamSetCollection + selfparams = private$.params partsets = imap(keep(values, inherits, "TuneToken"), function(value, pn) { tunetoken_to_ps(value, selfparams[[pn]], pn) }) @@ -767,6 +764,8 @@ ParamSet = R6Class("ParamSet", ) ) +paramcols = c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "trafo", "has_trafo", "storage_type") + recover_domain = function(sd, by) { domain = as.data.table(c(by, sd)) class(domain) = c(domain$cls, class(domain)) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 1789cce3..d7b447e7 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -40,31 +40,56 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Also note that the `$values` will have undefined behavior when `sets` contains a #' single [`ParamSet`] multiple times (by reference). #' Default `FALSE`. - initialize = function(sets, set_id = "", ignore_ids = FALSE) { + initialize = function(sets) { assert_list(sets, types = "ParamSet") - if (!ignore_ids) { - names(sets) = map(sets, "set_id") - } split_sets = split(sets, names(sets) == "") nameless_sets = split_sets$`TRUE` named_sets = split_sets$`FALSE` assert_names(names2(named_sets), type = "strict") + assert_ids(names2(named_sets)) + if (is.null(names(sets))) names(sets) = rep("", length(sets)) - private$.tags = unlist(map(sets, "tags"), recursive = FALSE) %??% named_list() - private$.sets = sets - - if (!identical(set_id, "")) { - assert_id(set_id) - assert_names(set_id, type = "strict") - } - private$.set_id = set_id + params = rbindlist(map(seq_along(sets), function(i) { + s = sets[[i]] + n = names2(sets)[[n]] + params_child = cbind(s$params, orig_id = s$params$id, owner_ps = i) + if (n != "") params_child$id = sprintf("%s.%s", n, params_child$id) + params_child + }) - pnames = names(self$params_unid) - dups = duplicated(pnames) + dups = duplicated(params$id) if (any(dups)) { stopf("ParamSetCollection would contain duplicated parameter names: %s", str_collapse(unique(pnames[dups]))) } + + child_trafos = map(sets, "extra_trafo") + child_with_trafos = which(!map_lgl(child_trafos, is.null)) + + if (length(child_with_trafos)) { + child_trafos = child_trafos[child_with_trafos] + private$.extra_trafo = crate(function(x) { + unlist(imap(child_with_trafos, function(child, trafoi) { + child = child_with_trafos[[trafoi]] + trafo = child_trafos[[trafoi]] + setinfo = params[data.table(owner_ps = child), c("id", "orig_id"), with = FALSE, on = "owner_ps"] + subx = x[match(setinfo$id, names(x), nomatch = 0)] + names(subx) = params[names(subx), orig_id, on = "id"] + + transformed = trafo(subx) + + prefix = names(sets)[[child]] + if (prefix != "") { + names(transformed) = sprintf("%s.%s", prefix, names(transformed)) + } + }), recursive = FALSE) + }, params, sets, child_trafos) + } else { + private$.extra_trafo = NULL + } + + private$.params = params + private$.sets = sets } ), active = list( @@ -141,19 +166,26 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, vals = unlist(vals, recursive = FALSE) if (!length(vals)) vals = named_list() vals + }, + + extra_trafo = function(f) { + if (!missing(f)) stop("extra_trafo is read-only in ParamSetCollection.") + private$.extra_trafo + }, + + constraint = function(f) { + if (!missing(f)) stop("constraint is read-only in ParamSetCollection.") + private$.constraint } ), private = list( .sets = NULL, + .extra_trafo = NULL, + .constraint = NULL, deep_clone = function(name, value) { switch(name, - .params = named_list(), - .deps = { - value = copy(value) - value$cond = lapply(value$cond, function(x) x$clone(deep = TRUE)) - value - }, + .deps = copy(value), .sets = map(value, function(x) { x$clone(deep = TRUE) }), From 132811426dbc6207e13e8efd9c528c9b6793e687 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 27 Mar 2021 16:51:08 +0100 Subject: [PATCH 21/64] relatively done with PSC --- R/Param.R | 23 ++++++ R/ParamSet.R | 18 +++-- R/ParamSetCollection.R | 102 +++++++++++++++++------- R/helper.R | 172 +---------------------------------------- R/ps_replicate.R | 7 ++ R/ps_union.R | 78 +++++++++++++++++++ attic/helper_r6.R | 78 +++++++++++++++++++ 7 files changed, 274 insertions(+), 204 deletions(-) create mode 100644 R/ps_replicate.R create mode 100644 R/ps_union.R create mode 100644 attic/helper_r6.R diff --git a/R/Param.R b/R/Param.R index 6b0e33f8..9b497433 100644 --- a/R/Param.R +++ b/R/Param.R @@ -88,3 +88,26 @@ domain_is_categ.default = function(param) FALSE #' @export domain_is_number.default = function(param) FALSE + + +# param: +check_domain_vectorize = function(ids, values, checker, more_args = list(), describe_error = TRUE) { + break_early = FALSE + if (length(checker) == length(values)) { + errors = imap(c(list(ids, values), more_args), function(id, value, ...) { + if (break_early) return(FALSE) + ch = checker(value, ...) + if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint + }) + } else { + errors = imap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { + if (break_early) return(FALSE) + ch = chck(value, ...) + if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint + }) + } + if (!describe_error) return(!break_early) + errors = unlist(errors) + if (!length(errors)) return(TRUE) + str_collapse(errors, sep = "\n") +} diff --git a/R/ParamSet.R b/R/ParamSet.R index 6f7a0f8f..32b62cb7 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -73,12 +73,14 @@ ParamSet = R6Class("ParamSet", levels = list(), special_vals = list(), default = list(), trafo = list(), requirements = list(), id = character(0)) initvalues = named_list() } else { - private$.params = rbindlist(params)[, `:=`(id = names(params), has_trafo = !map_lgl(trafo, is.null))] + private$.params = rbindlist(params) + set(private$.params, , "id", names(params)) + set(private$.params, , "has_trafo", !map_lgl(private$.params, is.null)) initvalues = private$.params[(init_given), set_names(init, id)] private$.tags = private$.params[, set_names(tags, id)] - private$.params[, setdiff(colnames(private$.params), paramcols) := NULL] + set(private$.params, , setdiff(colnames(private$.params), paramcols), NULL) } setindexv(private$.params, c("id", "cls", "grouping")) @@ -234,8 +236,11 @@ ParamSet = R6Class("ParamSet", } # check each parameter group's feasibility - ns_nontune = names(discard(xs, inherits, "TuneToken")) - pgroups = split(params[id %in% ns_nontune][, values := xs], by = c("cls", "grouping")) + xs_nontune = discard(xs, inherits, "TuneToken") + + params = params[names(xs_nontune), on = "id"] + set(params, , "values", xs_nontune) + pgroups = split(params, by = c("cls", "grouping")) break_early = FALSE checkresults = map(pgroups, function(x) { if (break_early) return(FALSE) @@ -375,7 +380,8 @@ ParamSet = R6Class("ParamSet", x = t(x) params = private$.params[rownames(x), on = "id"] params$result = list() - params[, result := list(as.list(as.data.frame(t(matrix(domain_qunif(recover_domain(.SD, .BY), x[id, ]), nrow = .N))))), by = c("cls", "grouping")] + params[, result := list(as.list(as.data.frame(t(matrix(domain_qunif(recover_domain(.SD, .BY), x[id, ]), nrow = .N))))), + by = c("cls", "grouping")] as.data.table(set_names(params$result, params$id)) }, @@ -538,7 +544,7 @@ ParamSet = R6Class("ParamSet", # convert all integer params really to storage type int, move doubles to within bounds etc. # solves issue #293, #317 nontt = discard(xs, inherits, "TuneToken") - sanitised = private$.params[names(nontt), on = "id"][, values := nontt][ + sanitised = set(private$.params[names(nontt), on = "id"], , "values", nontt)[ !pmap_lgl(list(special_vals, values), has_element), list(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] insert_named(xx, set_names(sanitised$values, sanitised$id)) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index d7b447e7..0a28126b 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -40,8 +40,10 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Also note that the `$values` will have undefined behavior when `sets` contains a #' single [`ParamSet`] multiple times (by reference). #' Default `FALSE`. - initialize = function(sets) { + initialize = function(sets, tag_set = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") + assert_flag(tag_set) + assert_flag(tag_params) split_sets = split(sets, names(sets) == "") nameless_sets = split_sets$`TRUE` named_sets = split_sets$`FALSE` @@ -53,7 +55,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, s = sets[[i]] n = names2(sets)[[n]] params_child = cbind(s$params, orig_id = s$params$id, owner_ps = i) - if (n != "") params_child$id = sprintf("%s.%s", n, params_child$id) + if (n != "") set(params_child, , "id", sprintf("%s.%s", n, params_child$id)) params_child }) @@ -63,31 +65,9 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, str_collapse(unique(pnames[dups]))) } - child_trafos = map(sets, "extra_trafo") - child_with_trafos = which(!map_lgl(child_trafos, is.null)) - - if (length(child_with_trafos)) { - child_trafos = child_trafos[child_with_trafos] - private$.extra_trafo = crate(function(x) { - unlist(imap(child_with_trafos, function(child, trafoi) { - child = child_with_trafos[[trafoi]] - trafo = child_trafos[[trafoi]] - setinfo = params[data.table(owner_ps = child), c("id", "orig_id"), with = FALSE, on = "owner_ps"] - subx = x[match(setinfo$id, names(x), nomatch = 0)] - names(subx) = params[names(subx), orig_id, on = "id"] - - transformed = trafo(subx) - - prefix = names(sets)[[child]] - if (prefix != "") { - names(transformed) = sprintf("%s.%s", prefix, names(transformed)) - } - }), recursive = FALSE) - }, params, sets, child_trafos) - } else { - private$.extra_trafo = NULL - } - + private$.tags = combine_tags(sets, tag_sets, tag_params) + private$.extra_trafo = combine_trafos(sets, params) + private$.constraint = combine_constraints(sets, params) private$.params = params private$.sets = sets } @@ -194,3 +174,71 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, } ) ) + +combine_trafos = function(sets, params) { + child_trafos = map(sets, "extra_trafo") + child_with_trafos = which(!map_lgl(child_trafos, is.null)) + if (!length(child_with_trafos)) return(NULL) + + child_trafos = child_trafos[child_with_trafos] + crate(function(x) { + unlist(imap(child_with_trafos, function(child, trafoi) { + child = child_with_trafos[[trafoi]] + trafo = child_trafos[[trafoi]] + setinfo = params[data.table(owner_ps = child), c("id", "orig_id"), with = FALSE, on = "owner_ps"] + subx = x[match(setinfo$id, names(x), nomatch = 0)] + names(subx) = params[names(subx), orig_id, on = "id"] + + transformed = trafo(subx) + + prefix = names(sets)[[child]] + if (prefix != "") { + names(transformed) = sprintf("%s.%s", prefix, names(transformed)) + } + }), recursive = FALSE) + }, params, sets, child_trafos, child_with_trafos) +} + +combine_constraints = function(sets, params) { + child_constraints = map(sets, "constraint") + child_with_constraints = which(!map_lgl(child_constraints, is.null)) + if (!length(child_with_constraints)) return(NULL) + + child_constraints = child_constraints[child_with_constraints] + crate(function(x) { + for (constrainti in seq_along(child_with_constraints)) { + child = child_with_constraints[[constrainti]] + constraint = child_constraints[[constrainti]] + setinfo = params[data.table(owner_ps = child), c("id", "orig_id"), with = FALSE, on = "owner_ps"] + subx = x[match(setinfo$id, names(x), nomatch = 0)] + names(subx) = params[names(subx), orig_id, on = "id"] + + if (!constraint(x)) return(FALSE) + } + TRUE + }, params, sets, child_constraints, child_with_constraints) +} + +combine_tags = function(sets, tag_sets, tag_params) { + if (tag_set) { + if (tag_params) { + unlist(imap(sets, function(s, n) { + add_tags = sprintf("set_%s", n) + imap(s$tags, function(t, tn) unique(c(t, add_tags, sprintf("param_%s", tn)))) + }), recursive = FALSE) + } else { + unlist(imap(sets, function(s, n) { + add_tags = sprintf("set_%s", n) + imap(s$tags, function(t) unique(c(t, add_tags))) + }), recursive = FALSE) + } + } else { + if (tag_params) { + unlist(map(sets, function(s) { + imap(s$tags, function(t, tn) unique(c(t, sprintf("param_%s", tn)))) + }), recursive = FALSE) + } else { + unlist(map(sets, "tags"), recursive = FALSE) + } + } +} diff --git a/R/helper.R b/R/helper.R index 360a4f98..6b4e604c 100644 --- a/R/helper.R +++ b/R/helper.R @@ -31,156 +31,6 @@ transpose = function(data, ps = NULL, filter_na = TRUE, trafo = TRUE) { return(xs) } -ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_set = FALSE, tag_params = FALSE) { - assert_count(times) - assert_character(prefixes, any.missing = FALSE, unique = TRUE, len = times) - - ps_union(named_list(prefixes, set), tag_set = tag_set, tag_params = tag_params) -} - -# Create a ParamSet from a list of ParamSets -# This emulates `ParamSetCollection$new(sets)`, except that -# - The result is a `ParamSet`, not a `ParamSetCollection` -# - The ParamSets are allowed to have `$trafo`, which are collected together into a single function. -# This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting ParamSet has all the Params -# from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will -# have their changed to .. This is also reflected in deps and in `$trafo`. -# @param sets: list of ParamSet -ps_union = function(sets, tag_set = FALSE, tag_params = FALSE) { - assert_list(sets, types = "ParamSet") - - if (!ignore_ids) { - names(sets) = map(sets, "set_id") - } - - psc = ParamSetCollection$new(sets, ignore_ids = TRUE) - - newps = ParamSet$new()$add(psc) - - # This loop collects information that is needed by the trafo. - # Resulting is a list of named lists, with one element per `sets` entry. Elements of the named lists are: - # - trafo: trafo of the given ParamSet - # - set_id: set_id of the given ParamSet - # - forward_name_translation: named `character`. Names are the Param IDs of the resulting newps, - # values are the Param IDs of the original Params in the `sets` argument. - # E.g. if a single ParamSet with set_id "sid" and with one Param with id "pid" is given, - # then this is a `c(sid.pid = "pid")`. - # Why is this needed? If the $trafo() is given a value `list(sid.pid = 1)`, then - # `forward_name_translation` can be used to rename this to `list(pid = 1)`, which is what the - # original trafo expects. - setinfo = unname(imap(keep(sets, function(x) x$has_trafo), function(s, n) { - sparams = s$params_unid # avoid slow ParamSetCollection $params active binding - sinfo = list( - trafo = s$trafo, - set_id = n, - forward_name_translation = names2(sparams) - ) - psids = names2(sparams) - if (n != "") { - psids = sprintf("%s.%s", n, psids) - } - names(sinfo$forward_name_translation) = psids - sinfo - })) - - if (length(setinfo)) { - # allnames: names of all parameters, as seen from the outside - allnames = names2(unlist(map(setinfo, "forward_name_translation"))) - assert_subset(allnames, names2(newps$params_unid)) # just check, this should always be the case - - newps$trafo = crate(function(x, param_set) { - res = unlist(mlr3misc::map(setinfo, function(s) { - trafo = s$trafo - # get the parameter values that the current trafo should operate on, - # as identified by the names in forward_name_translation - pv = x[match(names(s$forward_name_translation), names(x), nomatch = 0)] - - # translate name from "." to "" - names(pv) = s$forward_name_translation[names(pv)] - pv = trafo(pv) - - # append prefix again. trafo() could have changed parameter names, so - # we can't use any cached name_translation magic here - if (s$set_id != "") { - names(pv) = sprintf("%s.%s", s$set_id, names(pv)) - } - - pv - }), recursive = FALSE) - - # add the Params that were not translated at all, because the ParamSet doesn't know about them. - res = c(mlr3misc::remove_named(x, allnames), res) - - res[c(intersect(names(res), names(x)), setdiff(names(res), names(x)))] # unchanged parameter names stay in order - }, setinfo, allnames) - } - newps -} - -# Put a function in a "lean" environment that does not carry unnecessary baggage with it (e.g. references to datasets) -# -# @param .fn: function to crate -# @param ...: the objects, which should be visible inside `.fn`. These objects should be given as is, not in a string -# or expression or anything like that. -# -# @examples -# meta_f = function(z) { -# x = 1 -# y = 2 -# crate(function() { -# c(x, y, z) -# }, x) -# } -# x = 100 -# y = 200 -# z = 300 -# f = meta_f(1) -# f() -# #> [1] 1 200 300 -# ## using `x` from the crate, `y` and `z` from global env -crate = function(.fn, ...) { - environment(.fn) = list2env(parent = .GlobalEnv, - structure(list(...), names = map_chr(substitute(list(...)), as.character)[-1])) - .fn -} - -# Get the R6ClassGenerator (constructor) by class name, even if the generator itself is not directly visible. -# This is necessary to create new instances of a `Param`, because the `Param` only contains a link to its class -# name, not its constructor. -# @param name: name of R6 class to be found -# @param env: environment (and its parents) to prefer -# Uses `getAnywhere()` to find objects named `name` with a `$new` slot that is a function. An error is thrown if nothing -# is found. -# If multiple objects are found, the order in which they are returned is: -# 1. object found in `env` (or its parents) -# 2. objects that are "visible" from the global environment -# 3. objects that are actually R6-objects (and do not just have a `$new()` function -get_r6_constructor = function(name, env = parent.frame()) { - found_in_env = dups = visible = isr6 = objs = NULL # pacify static check - # data.table with , , , , - candidates = do.call(data.table, utils::getAnywhere(name)) - # reducing to columns: , - candidates = candidates[!dups | visible, list(objs, visible)] - - # prefer object we find directly where the call expects it - candidates[, found_in_env := FALSE] - direct_found = tryCatch(get(name, envir = env), error = function(e) NULL) - if (!is.null(direct_found)) { - candidates = rbind(candidates, data.table(objs = list(direct_found), visible = TRUE, found_in_env = TRUE)) - } - - # throw away objects without a `$new()` - candidates = candidates[map_lgl(objs, function(o) "new" %in% names(o) && is.function(o$new))] - candidates[, isr6 := map_lgl(objs, is.R6Class)] - - if (!nrow(candidates)) { - stopf("Could not find R6ClassGenerator (or any object with $new() function) named %s.", name) - } - # Order of preference: - # found_in_env, then visible, then isr6 - candidates[order(found_in_env, visible, isr6, decreasing = TRUE)]$objs[[1]] -} - repr = function(x) { str_collapse(utils::capture.output(print(x)), "\n") } @@ -191,28 +41,8 @@ as_type = function(x, type) { integer = as.integer(x), numeric = as.numeric(x), character = as.character(x), + list = as.list(x), stopf("Invalid storage type '%s'", type) ) } -# param: -check_domain_vectorize = function(ids, values, checker, more_args = list(), describe_error = TRUE) { - break_early = FALSE - if (length(checker) == length(values)) { - errors = imap(c(list(ids, values), more_args), function(id, value, ...) { - if (break_early) return(FALSE) - ch = checker(value, ...) - if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint - }) - } else { - errors = imap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { - if (break_early) return(FALSE) - ch = chck(value, ...) - if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint - }) - } - if (!describe_error) return(!break_early) - errors = unlist(errors) - if (!length(errors)) return(TRUE) - str_collapse(errors, sep = "\n") -} diff --git a/R/ps_replicate.R b/R/ps_replicate.R new file mode 100644 index 00000000..98c8dd04 --- /dev/null +++ b/R/ps_replicate.R @@ -0,0 +1,7 @@ + +ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_set = FALSE, tag_params = FALSE) { + assert_count(times) + assert_character(prefixes, any.missing = FALSE, unique = TRUE, len = times) + + ps_union(named_list(prefixes, set), tag_set = tag_set, tag_params = tag_params) +} diff --git a/R/ps_union.R b/R/ps_union.R new file mode 100644 index 00000000..21b655d3 --- /dev/null +++ b/R/ps_union.R @@ -0,0 +1,78 @@ +# Create a ParamSet from a list of ParamSets +# This emulates `ParamSetCollection$new(sets)`, except that +# - The result is a `ParamSet`, not a `ParamSetCollection` +# - The ParamSets are allowed to have `$trafo`, which are collected together into a single function. +# This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting ParamSet has all the Params +# from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will +# have their changed to .. This is also reflected in deps and in `$trafo`. +# @param sets: list of ParamSet +ps_union = function(sets, tag_set = FALSE, tag_params = FALSE) { + assert_list(sets, types = "ParamSet") + + if (!ignore_ids) { + names(sets) = map(sets, "set_id") + } + + psc = ParamSetCollection$new(sets, ignore_ids = TRUE) + + newps = ParamSet$new()$add(psc) + + # This loop collects information that is needed by the trafo. + # Resulting is a list of named lists, with one element per `sets` entry. Elements of the named lists are: + # - trafo: trafo of the given ParamSet + # - set_id: set_id of the given ParamSet + # - forward_name_translation: named `character`. Names are the Param IDs of the resulting newps, + # values are the Param IDs of the original Params in the `sets` argument. + # E.g. if a single ParamSet with set_id "sid" and with one Param with id "pid" is given, + # then this is a `c(sid.pid = "pid")`. + # Why is this needed? If the $trafo() is given a value `list(sid.pid = 1)`, then + # `forward_name_translation` can be used to rename this to `list(pid = 1)`, which is what the + # original trafo expects. + setinfo = unname(imap(keep(sets, function(x) x$has_trafo), function(s, n) { + sparams = s$params_unid # avoid slow ParamSetCollection $params active binding + sinfo = list( + trafo = s$trafo, + set_id = n, + forward_name_translation = names2(sparams) + ) + psids = names2(sparams) + if (n != "") { + psids = sprintf("%s.%s", n, psids) + } + names(sinfo$forward_name_translation) = psids + sinfo + })) + + if (length(setinfo)) { + # allnames: names of all parameters, as seen from the outside + allnames = names2(unlist(map(setinfo, "forward_name_translation"))) + assert_subset(allnames, names2(newps$params_unid)) # just check, this should always be the case + + newps$trafo = crate(function(x, param_set) { + res = unlist(mlr3misc::map(setinfo, function(s) { + trafo = s$trafo + # get the parameter values that the current trafo should operate on, + # as identified by the names in forward_name_translation + pv = x[match(names(s$forward_name_translation), names(x), nomatch = 0)] + + # translate name from "." to "" + names(pv) = s$forward_name_translation[names(pv)] + pv = trafo(pv) + + # append prefix again. trafo() could have changed parameter names, so + # we can't use any cached name_translation magic here + if (s$set_id != "") { + names(pv) = sprintf("%s.%s", s$set_id, names(pv)) + } + + pv + }), recursive = FALSE) + + # add the Params that were not translated at all, because the ParamSet doesn't know about them. + res = c(mlr3misc::remove_named(x, allnames), res) + + res[c(intersect(names(res), names(x)), setdiff(names(res), names(x)))] # unchanged parameter names stay in order + }, setinfo, allnames) + } + newps +} diff --git a/attic/helper_r6.R b/attic/helper_r6.R new file mode 100644 index 00000000..21b655d3 --- /dev/null +++ b/attic/helper_r6.R @@ -0,0 +1,78 @@ +# Create a ParamSet from a list of ParamSets +# This emulates `ParamSetCollection$new(sets)`, except that +# - The result is a `ParamSet`, not a `ParamSetCollection` +# - The ParamSets are allowed to have `$trafo`, which are collected together into a single function. +# This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting ParamSet has all the Params +# from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will +# have their changed to .. This is also reflected in deps and in `$trafo`. +# @param sets: list of ParamSet +ps_union = function(sets, tag_set = FALSE, tag_params = FALSE) { + assert_list(sets, types = "ParamSet") + + if (!ignore_ids) { + names(sets) = map(sets, "set_id") + } + + psc = ParamSetCollection$new(sets, ignore_ids = TRUE) + + newps = ParamSet$new()$add(psc) + + # This loop collects information that is needed by the trafo. + # Resulting is a list of named lists, with one element per `sets` entry. Elements of the named lists are: + # - trafo: trafo of the given ParamSet + # - set_id: set_id of the given ParamSet + # - forward_name_translation: named `character`. Names are the Param IDs of the resulting newps, + # values are the Param IDs of the original Params in the `sets` argument. + # E.g. if a single ParamSet with set_id "sid" and with one Param with id "pid" is given, + # then this is a `c(sid.pid = "pid")`. + # Why is this needed? If the $trafo() is given a value `list(sid.pid = 1)`, then + # `forward_name_translation` can be used to rename this to `list(pid = 1)`, which is what the + # original trafo expects. + setinfo = unname(imap(keep(sets, function(x) x$has_trafo), function(s, n) { + sparams = s$params_unid # avoid slow ParamSetCollection $params active binding + sinfo = list( + trafo = s$trafo, + set_id = n, + forward_name_translation = names2(sparams) + ) + psids = names2(sparams) + if (n != "") { + psids = sprintf("%s.%s", n, psids) + } + names(sinfo$forward_name_translation) = psids + sinfo + })) + + if (length(setinfo)) { + # allnames: names of all parameters, as seen from the outside + allnames = names2(unlist(map(setinfo, "forward_name_translation"))) + assert_subset(allnames, names2(newps$params_unid)) # just check, this should always be the case + + newps$trafo = crate(function(x, param_set) { + res = unlist(mlr3misc::map(setinfo, function(s) { + trafo = s$trafo + # get the parameter values that the current trafo should operate on, + # as identified by the names in forward_name_translation + pv = x[match(names(s$forward_name_translation), names(x), nomatch = 0)] + + # translate name from "." to "" + names(pv) = s$forward_name_translation[names(pv)] + pv = trafo(pv) + + # append prefix again. trafo() could have changed parameter names, so + # we can't use any cached name_translation magic here + if (s$set_id != "") { + names(pv) = sprintf("%s.%s", s$set_id, names(pv)) + } + + pv + }), recursive = FALSE) + + # add the Params that were not translated at all, because the ParamSet doesn't know about them. + res = c(mlr3misc::remove_named(x, allnames), res) + + res[c(intersect(names(res), names(x)), setdiff(names(res), names(x)))] # unchanged parameter names stay in order + }, setinfo, allnames) + } + newps +} From 58eb595d59a956ae2484b7997683c16737489fe1 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 30 Mar 2021 11:02:38 +0200 Subject: [PATCH 22/64] psc should be feature complete now --- R/ParamSetCollection.R | 58 ++++++++---------------------------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 0a28126b..fe91ab8c 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -73,25 +73,12 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, } ), active = list( - #' @template field_params_unid - params_unid = function(v) { - sets = private$.sets - if (length(sets) == 0L) { - return(named_list()) - } - # clone each param into new params-list and prefix id - ps_all = lapply(sets, function(s) s$params_unid) - ps_all = unlist(ps_all, recursive = FALSE) - if (!length(ps_all)) ps_all = named_list() - ps_all - }, #' @template field_deps deps = function(v) { if (!missing(v)) { stop("deps is read-only in ParamSetCollection.") } - sets = private$.sets - d_all = imap(sets, function(s, id) { + d_all = imap(private$.sets, function(s, id) { # copy all deps and rename ids to prefixed versions dd = s$deps if (id != "" && nrow(dd)) { @@ -105,46 +92,23 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, rbindlist(c(d_all, list(private$.deps)), use.names = TRUE) }, - #' @field tags (named `list()` of `character()`)\cr - #' Can be used to group and subset parameters. - #' Named with parameter IDs. - tags = function(v) { - if (!missing(v)) { - assert_list(v, any.missing = FALSE, types = "character") - assert_names(names(v), identical.to = names(self$params_unid)) - private$.tags = v - } - private$.tags - }, - #' @template field_values values = function(xs) { sets = private$.sets - if (is.null(names(sets))) { - names(sets) = map(private$.sets, "set_id") - } if (!missing(xs)) { assert_list(xs) - self$assert(xs) # make sure everything is valid and feasible - - for (i in seq_along(sets)) { - s = sets[[i]] - sid = names(sets)[[i]] - # retrieve sublist for each set, then assign it in set (after removing prefix) - psids = names(s$params_unid) - if (sid != "") { - psids = sprintf("%s.%s", sid, psids) - } - pv = xs[intersect(psids, names(xs))] - if (sid != "") { - names(pv) = substr(names(pv), nchar(sid) + 2, nchar(names(pv))) - } - s$values = pv + # make sure everything is valid and feasible. + # We do this here because we don't want the loop to be aborted early and have half an update. + self$assert(xs) + + translate = private$.params[names(xs), list(orig_id, owner_ps), on = "id"] + set(translate, , j = "values", xs) + for (xtl in split(translate, by = "owner_ps")) { + sets[[xtl$owner_ps]]$values = set_names(xtl$values, xtl$orig_id) } } - vals = map(sets, "values") - vals = unlist(vals, recursive = FALSE) - if (!length(vals)) vals = named_list() + vals = unlist(map(sets, "values"), recursive = FALSE) + if (!length(vals)) return(named_list()) vals }, From 687abf8e041e7957f050be92c140da809fccc29e Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 30 Mar 2021 12:43:25 +0200 Subject: [PATCH 23/64] remove 'describe_error' --- R/Param.R | 18 +++++++----------- R/ParamDbl.R | 4 +--- R/ParamFct.R | 5 ++--- R/ParamInt.R | 4 +--- R/ParamLgl.R | 3 +-- R/ParamS6.R | 3 +-- R/ParamSet.R | 53 ++++++++++++++++++---------------------------------- R/ParamUty.R | 4 ++-- 8 files changed, 33 insertions(+), 61 deletions(-) diff --git a/R/Param.R b/R/Param.R index 9b497433..71c3bfcf 100644 --- a/R/Param.R +++ b/R/Param.R @@ -9,15 +9,15 @@ #' @param x (`any`). #' @return If successful `TRUE`, if not a string with the error message. #' @export -domain_check = function(param, values, describe_error = TRUE) { - if (!test_list(values, len = nrow(param))) return(if (describe_error) "values must be a list" else FALSE) +domain_check = function(param, values) { + if (!test_list(values, len = nrow(param))) return("values must be a list") if (length(values) == 0) return(TRUE) # happens when there are no params + values to check assert_string(unique(param$grouping)) special_vals_hit = pmap_lgl(list(param$special_vals, values), has_element) if (any(special_vals_hit)) { # don't annoy domain_check methods with the burdon of having to filter out # values that match special_values - Recall(param[!special_vals_hit], values[!special_vals_hit], describe_error = describe_error) + Recall(param[!special_vals_hit], values[!special_vals_hit]) } else { UseMethod("domain_check") } @@ -25,7 +25,7 @@ domain_check = function(param, values, describe_error = TRUE) { #' @export domain_assert = makeAssertionFunction(domain_check) #' @export -domain_test = function(param, values) domain_check(param, values, describe_error = FALSE) +domain_test = function(param, values) domain_check(param, values) #' @export domain_storage_type = function(param) { @@ -91,22 +91,18 @@ domain_is_number.default = function(param) FALSE # param: -check_domain_vectorize = function(ids, values, checker, more_args = list(), describe_error = TRUE) { - break_early = FALSE +check_domain_vectorize = function(ids, values, checker, more_args = list()) { if (length(checker) == length(values)) { errors = imap(c(list(ids, values), more_args), function(id, value, ...) { - if (break_early) return(FALSE) ch = checker(value, ...) - if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint + if (isTRUE(ch)) NULL else sprintf("%s: %s", id, ch) }) } else { errors = imap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { - if (break_early) return(FALSE) ch = chck(value, ...) - if (isTRUE(ch)) NULL else if (describe_error) sprintf("%s: %s", id, ch) else break_early <<- TRUE # nolint + if (isTRUE(ch)) NULL else sprintf("%s: %s", id, ch) }) } - if (!describe_error) return(!break_early) errors = unlist(errors) if (!length(errors)) return(TRUE) str_collapse(errors, sep = "\n") diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 3aa61e93..a3fe759a 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -22,15 +22,13 @@ p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ } #' @export -domain_check.ParamDbl = function(param, values, describe_error = TRUE) { +domain_check.ParamDbl = function(param, values) { lower = param$lower - param$tolerance * pmax(1, abs(param$lower)) upper = param$upper + param$tolerance * pmax(1, abs(param$upper)) if (qtestr(values, "N1")) { values_num = as.numeric(values) if (all(values_num >= lower) && all(values_num <= upper)) return(TRUE) } - if (!describe_error) return(FALSE) - check_domain_vectorize(param$id, values, check_number, more_args = list(lower = lower, upper = upper)) } diff --git a/R/ParamFct.R b/R/ParamFct.R index 3a7177fb..94c53443 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -25,13 +25,12 @@ p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = charact } #' @export -domain_check.ParamFct = function(param, values, describe_error = TRUE) { +domain_check.ParamFct = function(param, values) { if (testr(values, "S1")) { values_str = as.character(values) if (all(values_str %in% param$levels[[1]])) return(TRUE) # this works because we have the grouping -- all 'levels' are the same here. } - if (!describe_error) return(FALSE) - check_domain_vectorized(param$ids, param, check_choice, more_args = list(choices = param$levels), describe_error = describe_error) + check_domain_vectorized(param$ids, param, check_choice, more_args = list(choices = param$levels)) } #' @export diff --git a/R/ParamInt.R b/R/ParamInt.R index 7beccafd..a63748c8 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -27,9 +27,8 @@ p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ } #' @export -domain_check.ParamInt = function(param, values, describe_error = TRUE) { +domain_check.ParamInt = function(param, values) { if (!qtestr(values, "N1()")) { - if (!describe_error) return(FALSE) check_domain_vectorize(param$id, values, check_int, more_args = list(lower = values$lower - 0.5, upper = values$upper + 0.5, # be lenient with bounds, because they would refer to the rounded values tol = .51) # we don't know the tolerances of individual values, but we do know that some values are not even (non-missing, finite) numerics @@ -44,7 +43,6 @@ domain_check.ParamInt = function(param, values, describe_error = TRUE) { return(TRUE) } } - if (!describe_error) return(FALSE) check_domain_vectorize(param$id, values_num, check_int, more_args = list(lower = param$lower - 0.5, upper = param$upper + 0.5, # be lenient with bounds, because they would refer to the rounded values diff --git a/R/ParamLgl.R b/R/ParamLgl.R index 8bfdc356..164e2ff8 100644 --- a/R/ParamLgl.R +++ b/R/ParamLgl.R @@ -6,11 +6,10 @@ p_lgl = function(special_vals = list(), default = NO_DEF, tags = character(), de } #' @export -domain_check.ParamLgl = function(param, values, describe_error = TRUE) { +domain_check.ParamLgl = function(param, values) { if (qtestr(values, "B1")) { return(TRUE) } - if (!describe_error) return(FALSE) check_domain_vectorized(param$id, values, check_flag) } diff --git a/R/ParamS6.R b/R/ParamS6.R index 015849f0..6cb36936 100644 --- a/R/ParamS6.R +++ b/R/ParamS6.R @@ -26,13 +26,12 @@ p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = charact } #' @export -domain_check.ParamSet6 = function(param, values, describe_error = TRUE) { +domain_check.ParamSet6 = function(param, values) { # we can rely on 'grouping' always giving us the same class set = set6_cache_get(param$grouping[[1]]) if (set$contains(values, all = TRUE)) { return(TRUE) } - if (!describe_error) return(FALSE) nomatches = param$id[!set$contains(values, all = FALSE)] sprintf("%s: not contained in %s.", str_collapse(nomatches, sep = ", "), set$strprint()) } diff --git a/R/ParamSet.R b/R/ParamSet.R index 32b62cb7..4384afb2 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -218,12 +218,11 @@ ParamSet = R6Class("ParamSet", #' #' @param xs (named `list()`). #' @return If successful `TRUE`, if not a string with the error message. - check = function(xs, check_strict = FALSE, describe_error = TRUE) { + check = function(xs, check_strict = FALSE) { assert_flag(check_strict) - assert_flag(describe_error) ok = check_list(xs, names = "unique") if (!isTRUE(ok)) { - return(if (!describe_error) FALSE else ok) + return(ok) } params = private$.params @@ -232,7 +231,7 @@ ParamSet = R6Class("ParamSet", extra = wf(ns %nin% ids) if (length(extra)) { - return(if (!describe_error) FALSE else sprintf("Parameter '%s' not available.%s", ns[extra], did_you_mean(extra, ids))) + return(sprintf("Parameter '%s' not available.%s", ns[extra], did_you_mean(extra, ids))) } # check each parameter group's feasibility @@ -241,42 +240,32 @@ ParamSet = R6Class("ParamSet", params = params[names(xs_nontune), on = "id"] set(params, , "values", xs_nontune) pgroups = split(params, by = c("cls", "grouping")) - break_early = FALSE checkresults = map(pgroups, function(x) { - if (break_early) return(FALSE) - ch = domain_check(set_class(x, c(x$cls[[1]], class(x))), x$values, describe_error = describe_error) - if (!describe_error && !ch) break_early <<- TRUE # nolint - ch + domain_check(set_class(x, c(x$cls[[1]], class(x))), x$values) }) - if (describe_error) { - if (break_early) return(FALSE) - } else { - checkresults = discard(checkresults, isTRUE) - if (length(checkresults)) { - return(str_collapse(checkresults), sep = "\n") - } + checkresults = discard(checkresults, isTRUE) + if (length(checkresults)) { + return(str_collapse(checkresults), sep = "\n") } if (check_strict) { required = setdiff(self$ids(tags = "required"), ns) if (length(required) > 0L) { - return(if (describe_error) sprintf("Missing required parameters: %s", str_collapse(required)) else FALSE) + return(sprintf("Missing required parameters: %s", str_collapse(required))) } - return(check_dependencies(xs, describe_error = describe_error)) - if (!self$test_constraint(xs)) return(if (describe_error) sprintf("Constraint not fulfilled.") else FALSE) + return(check_dependencies(xs)) + if (!self$test_constraint(xs)) return(sprintf("Constraint not fulfilled.")) } TRUE # we passed all checks }, - check_dependencies = function(xs, describe_error = TRUE) { + check_dependencies = function(xs) { deps = self$deps if (!nrow(deps)) return(TRUE) params = private$.params ns = names(xs) - break_early = FALSE errors = pmap(deps[id %in% ns], function(id, on, cond) { - if (break_early) return(FALSE) onval = xs[[on]] if (inherits(xs[[id]], "TuneToken") || inherits(onval, "TuneToken")) return(NULL) @@ -284,10 +273,6 @@ ParamSet = R6Class("ParamSet", # - if 'id' is there, then 'on' must be there, and cond must be true # - if 'id' is not there. but that is skipped (deps[id %in% ns] filter) if (on %in% ns && cond$test(onval)) return(NULL) - if (!describe_error) { - break_early <<- TRUE - return(FALSE) - } msg = sprintf("%s: can only be set if the following condition is met '%s'.", id, cond$as_string(on)) if (is.null(onval)) { @@ -298,7 +283,6 @@ ParamSet = R6Class("ParamSet", } msg }) - if (!describe_error) return(!break_early) errors = unlist(errors) if (!length(errors)) return(TRUE) str_collapse(errors, sep = "\n") @@ -312,7 +296,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xs (named `list()`). #' @return If successful `TRUE`, if not `FALSE`. - test = function(xs, check_strict = FALSE) self$check(xs, check_strict = check_strict, describe_error = FALSE), + test = function(xs, check_strict = FALSE) makeTest(self$check(xs, check_strict = check_strict)), #' @description #' \pkg{checkmate}-like assert-function. Takes a named list. @@ -336,18 +320,17 @@ ParamSet = R6Class("ParamSet", #' #' @param xdt ([data.table::data.table] | `data.frame()`). #' @return If successful `TRUE`, if not a string with the error message. - check_dt = function(xdt, check_strict = FALSE, describe_error = TRUE) { + check_dt = function(xdt, check_strict = FALSE) { xss = map(transpose_list(xdt), discard, is.na) msgs = list() for (i in seq_along(xss)) { xs = xss[[i]] - ok = self$check(xs, check_strict = check_strict, describe_error = describe_error) + ok = self$check(xs, check_strict = check_strict) if (!isTRUE(ok)) { - if (!describe_error) return(FALSE) - msgs[[length(msgs) + 1]] = ok # this is the fast way to grow a list in R + return(ok) } } - if (length(msgs)) msgs else TRUE + TRUE }, #' @description @@ -355,7 +338,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xdt ([data.table::data.table]). #' @return If successful `TRUE`, if not `FALSE`. - test_dt = function(xdt, check_strict = FALSE) makeTest(res = self$check_dt(xdt, check_strict = check_strict, describe_error = FALSE)), + test_dt = function(xdt) makeTest(res = self$check_dt(xdt, check_strict = check_strict)), #' @description #' \pkg{checkmate}-like assert-function (s. `$check_dt()`). @@ -472,7 +455,7 @@ ParamSet = R6Class("ParamSet", stopf("A param cannot depend on itself!") } - feasible_on_values = map_lgl(cond$rhs, domain_check, param = self$get_param(on), describe_error = FALSE) + feasible_on_values = map_lgl(cond$rhs, domain_check, param = self$get_param(on)) if (any(!feasible_on_values)) { stopf("Condition has infeasible values for %s: %s", on, str_collapse(cond$rhs[!feasible_on_values])) } diff --git a/R/ParamUty.R b/R/ParamUty.R index f5543d06..356f222c 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -11,12 +11,12 @@ p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, t } #' @export -domain_check.ParamUty = function(param, values, describe_error = TRUE) { +domain_check.ParamUty = function(param, values) { subset = !map_lgl(param$cargo, is.null) if (!any(subset)) return(TRUE) values = values[subset] param = param[subset] - check_domain_vectorize(param$id, values, param$cargo, describe_error = describe_error) + check_domain_vectorize(param$id, values, param$cargo) } #' @export From 54b45035bdb8050b0faba9718956ed7616c6ff5f Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 3 Apr 2021 00:25:31 +0200 Subject: [PATCH 24/64] minor fixes --- R/ParamSet.R | 5 ++--- R/ParamSetCollection.R | 2 +- R/domain.R | 2 +- R/ps.R | 7 +++++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index 4384afb2..3898f493 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -61,9 +61,8 @@ ParamSet = R6Class("ParamSet", #' This can be used to create a `ParamSet` with certain [`Param`] `id`s #' without having to clone said [`Param`]s. #' Default `FALSE`. - initialize = function(params = named_list(), extra_trafo = NULL, allow_dangling_dependencies = FALSE) { + initialize = function(params = named_list(), allow_dangling_dependencies = FALSE) { assert_list(params, types = "Domain") - self$extra_trafo = extra_trafo # does assert in the active binding assert_names(names(params), type = "strict") assert_ids(names(params)) @@ -75,7 +74,7 @@ ParamSet = R6Class("ParamSet", } else { private$.params = rbindlist(params) set(private$.params, , "id", names(params)) - set(private$.params, , "has_trafo", !map_lgl(private$.params, is.null)) + set(private$.params, , "has_trafo", !map_lgl(private$.params$trafo, is.null)) initvalues = private$.params[(init_given), set_names(init, id)] private$.tags = private$.params[, set_names(tags, id)] diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index fe91ab8c..3614f7c2 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -57,7 +57,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, params_child = cbind(s$params, orig_id = s$params$id, owner_ps = i) if (n != "") set(params_child, , "id", sprintf("%s.%s", n, params_child$id)) params_child - }) + })) dups = duplicated(params$id) if (any(dups)) { diff --git a/R/domain.R b/R/domain.R index 169741c4..d3e77845 100644 --- a/R/domain.R +++ b/R/domain.R @@ -148,7 +148,7 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real param = data.table(cls = cls, grouping = grouping, cargo = list(cargo), lower = lower, upper = upper, tolerance = tolerance, levels = list(levels), special_vals = list(special_vals), default = list(default), tags = list(tags), trafo = list(trafo), requirements = list(parse_depends(depends_expr, parent.frame(2))), - storage_type = storage_type, init_given = !missing(init), init = if (!missing(init)) init) + storage_type = storage_type, init_given = !missing(init), init = list(if (!missing(init)) init)) class(param) = c(cls, "Domain", class(param)) if (!is_nodefault(default)) { diff --git a/R/ps.R b/R/ps.R index 1370b21c..20458e56 100644 --- a/R/ps.R +++ b/R/ps.R @@ -58,6 +58,9 @@ #' pars$search_space() #' @family ParamSet construction helpers #' @export -ps = function(..., .extra_trafo = NULL, .allow_dangling_dependencies = FALSE) { - ParamSet$new(list(...), extra_trafo = .extra_trafo, allow_dangling_dependencies = .allow_dangling_dependencies) # if length is 0 then no names are present +ps = function(..., .extra_trafo = NULL, .constraint = NULL, .allow_dangling_dependencies = FALSE) { + param_set = ParamSet$new(list(...), allow_dangling_dependencies = .allow_dangling_dependencies) # if length is 0 then no names are present + param_set$extra_trafo = .extra_trafo + param_set$constraint = .constraint + param_set } From 7126e3f2a1e310fc50c7b9617ff8a2ed7dd9e578 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 3 Apr 2021 00:25:43 +0200 Subject: [PATCH 25/64] experiments --- attic/demo.R | 9 ++ attic/vectoralgorithm.R | 131 +++++++++++++++++++++++++++ attic/vectoralgorithm_ii.R | 176 +++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 attic/demo.R create mode 100644 attic/vectoralgorithm.R create mode 100644 attic/vectoralgorithm_ii.R diff --git a/attic/demo.R b/attic/demo.R new file mode 100644 index 00000000..885fc398 --- /dev/null +++ b/attic/demo.R @@ -0,0 +1,9 @@ + + + +probability = p_dbl(0, 1) +param = p_int(0, logscale = TRUE) + + +ps(prob = probability, param = param) + diff --git a/attic/vectoralgorithm.R b/attic/vectoralgorithm.R new file mode 100644 index 00000000..ea31f23a --- /dev/null +++ b/attic/vectoralgorithm.R @@ -0,0 +1,131 @@ + +library("R6") +library("paradox") +library("checkmate") + + +Strategy = R6Class("Strategy", + public = list( + initialize = function(param_set) { + private$.param_set = param_set + }, + run = function(x) stop("abstract") + ), + active = list( + param_set = function(v) { + if (!missing(v) && !identical(v, private$.param_set)) stop("param_set is read-only") + private$.param_set + } + ), + private = list( + .param_set = NULL + ) +) + +StrategyVector = R6Class("StrategyVector", inherit = Strategy, + public = list( + components = NULL, + initialize = function(components) { + components = lapply(components, function(x) x$clone(deep = TRUE)) + self$components = components + params = lapply(seq_along(components), function(ci) { + comp = components[[ci]] + p = comp$param_set + p$set_id = paste0("element", ci) + p + }) + param_set = ParamSetCollection$new(params) + super$initialize(param_set) + }, + run = function(x) { + lapply(self$components, function(comp) comp$run(x)) + }, + run_vector = function(x) { + assert_list(x, len = length(self$components)) + Map(function(comp, xcomp) comp$run(xcomp), self$components, x) + } + ) +) + +c.Strategy = function(...) { + components = list(...) + assert_list(components, types = "Strategy") + components = unlist(lapply(unname(components), function(cmp) { + if (inherits(cmp, "StrategyVector")) cmp$components else list(cmp) + }), recursive = FALSE) + StrategyVector$new(components) +} + + +StrategyAdd = R6Class("StrategyAdd", inherit = Strategy, + public = list( + initialize = function() { + super$initialize(ps(summand = p_dbl())) + }, + run = function(x) { + self$param_set$get_values()$summand + x + } + ) +) + +StrategyMultiply = R6Class("StrategyMultiply", inherit = Strategy, + public = list( + initialize = function() { + super$initialize(ps(factor = p_dbl())) + }, + run = function(x) { + self$param_set$get_values()$factor * x + } + ) +) + +StrategyMultiplyAdd = R6Class("StrategyMultiplyAdd", inherit = Strategy, + public = list( + initialize = function() { + super$initialize(ps(factor = p_dbl(), summand = p_dbl())) + }, + run = function(x) { + self$param_set$get_values()$factor * x + self$param_set$get_values()$summand + } + ) +) + + + +sa = StrategyAdd$new() +sa$param_set$values$summand = 10 + +sm = StrategyMultiply$new() +sm$param_set$values$factor = 2 + +sam = StrategyMultiplyAdd$new() +sam$param_set$values$summand = -1 +sam$param_set$values$factor = 4 + +sa$run(100) # 110 +sm$run(100) # 200 +sam$run(100) # 399 + +svec = StrategyVector$new(list(sa, sm, sam)) +svec$run(100) # list(110, 200, 399) +svec$run_vector(list(100, 200, 300)) # list(110, 400, 1199) + +c(svec, sa, sm)$run_vector(list(100, 200, 300, 400, 500)) # list(110, 400, 1199, 410, 1000) + + + +svec_long = StrategyVector$new(c( + lapply(1:2000, function(x) { + sa = StrategyAdd$new() + sa$param_set$values$summand = x + sa + }), + list(sm, sam) +)) + +svec_long$run(100) + # as.list(c(101:2100, 200, 399)) +svec_long$run_vector(as.list(1:2002)) + # as.list(c((1:2000) * 2, 4002, 8007)) + + diff --git a/attic/vectoralgorithm_ii.R b/attic/vectoralgorithm_ii.R new file mode 100644 index 00000000..cb5e71bb --- /dev/null +++ b/attic/vectoralgorithm_ii.R @@ -0,0 +1,176 @@ + + + +Strategy = R6Class("Strategy", + public = list( + is_scalar = NULL, + multiplicity = NULL, + initialize = function(param_set, is_scalar = TRUE, multiplicity = FALSE) { + self$is_scalar = assert_flag(is_scalar) + self$multiplicity = assert_count(multiplicity, positive = TRUE) + assert_true(!is_scalar || multiplicity == 1) + private$param_set = param_set + }, + run = function(x) stop("abstract"), + run_vector = function(x) { + if (self$is_scalar) { + stop("Scalar strategy does not vectorize.") + } else { + stop("abstract") + } + } + ), + active = list( + param_set = function(v) { + if (!missing(v) && !identical(v, private$.param_set)) stop("param_set is read-only") + private$.param_set + } + ), + private = list( + .param_set = NULL + ) +) + +StrategyHybridVector = R6Class("StrategyHybridVector", inherits = Strategy, + public = list( + initialize = function(param_set, multi_init = NULL) { + assert_data_table(multi_init, min.rows = 1, null.ok = TRUE) + if (!is.null(multi_init)) { + param_set$vectorize = TRUE + param_set$multiplicity = nrow(multi_init) + param_set$values = multi_init + is_scalar = FALSE + multiplicity = param_set$multiplicity + } else { + is_scalar = TRUE + multiplicity = 1 + } + super$initialize(param_set, is_scalar = is_scalar, multiplicity = multiplicity) + } + ) +) + +######## Making use of possible multiplicities +StrategyVector = R6Class("Strategy", inherits = Strategy, + public = list( + components = NULL, + chunksizes = NULL, + initialize = function(components) { + assert_list(components, types = "Strategy") + self$components = unname(components) + self$chunksizes = sapply(self$components, `[[`, "multiplicity") + super$initialize(param_set = NULL, is_scalar = FALSE, multiplicity = sum(self$chunksizes) + }, + run = function(x) { + result_listlist = lapply(self$components, function(comp) { + if (comp$is_scalar) list(comp$run(x)) else comp$run(x) + }) + unlist(result_listlist, recursive = FALSE) + }, + run_vector = function(x) { + assert_list(x, len = self$multiplicity) + x = split(x, unname(rep(seq_along(x), self$chunksizes))) + result_listlist = Map(self$components, x, f = function(comp, x) { + if (comp$is_scalar) list(comp$run(x)) else comp$run(x) + }) + unlist(result_listlist, recursive = FALSE) + } + ) +) + +######## as before +c.Strategy = function(...) { + components = list(...) + assert_list(components, types = "Strategy") + components = unlist(lapply(unname(components), function(cmp) { + if (inherits(cmp, "StrategyVector")) cmp$components else list(cmp) + }), recursive = FALSE) + StrategyVector$new(components) +} + +StrategyAdd = R6Class("StrategyAdd", inherit = StrategyHybridVector, + public = list( + initialize = function(multi_init = NULL) { + super$initizlize(ps(summand = p_dbl()), multi_init = multi_init) + }, + run = function(x) { + result = self$get_values()$summand + x + if (!self$is_scalar) result = as.list(result) + result + }, + run_vector = function(x) { + if (self$is_scalar) stop("Scalar strategy does not vectorize.") + self$run(x) + } + ) +) + +######## quality of life +c.StrategyAdd = function(...) { + components = list(...) + if (!test_list(components, types = "StrategyAdd")) NextMethod() + StrategyAdd$new(multi_init = data.table::rbindlist(lapply(components, function(x) x$param_set$get_values(as_dt = TRUE)))) +} + +######## as before +StrategyMultiply = R6Class("StrategyMultiply", inherit = Strategy, + public = list( + initialize = function() { + super$initialize(ps(factor = p_dbl())) + }, + run = function(x) { + self$get_values()$factor * x + } + ) +) + +######## as before +StrategyMultiplyAdd = R6Class("StrategyMultiplyAdd", inherit = Strategy, + public = list( + initialize = function() { + super$initialize(ps(factor = p_dbl(), summand = p_dbl())) + }, + run = function(x) { + self$get_values()$factor * x + self$get_values()$summand + } + ) +) + + +sa = StrategyAdd$new() +sa$param_set$values$summand = 10 + +sm = StrategyMultiply$new() +sm$param_set$values$factor = 2 + +sam = StrategyMultiplyAdd$new() +sam$param_set$values$summand = -1 +sam$param_set$values$factor = 4 + +sa$run(100) # 110 +sm$run(100) # 200 +sam$run(100) # 399 + +svec = StrategyVector$new(list(sa, sm, sam)) +svec$run(100) # list(110, 200, 399) +svec$run_vector(c(100, 200, 300)) # list(110, 400, 1199) + +c(svec, sa, sm)$run_vector(c(100, 200, 300, 400, 500)) # list(110, 400, 1199, 410, 1000) + + + +svec_long = StrategyVector$new(c( + do.call(c, lapply(1:2000, function(x) { + sa = StrategyAdd$new() + sa$param_set$values$summand = x + sa + })), + list(sm, sam) +)) + +svec_long$run(100) + # as.list(c(101:2100, 200, 399)) +svec_long$run_vector(1:2002) + # as.list(c((1:2000) * 2, 4004, 8007)) + + From 4261e60b467713e0f24b1864841cb512ceac2648 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 3 Apr 2021 00:27:57 +0200 Subject: [PATCH 26/64] solution demo --- attic/vectoralgorithm_ii.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/attic/vectoralgorithm_ii.R b/attic/vectoralgorithm_ii.R index cb5e71bb..ed1698fb 100644 --- a/attic/vectoralgorithm_ii.R +++ b/attic/vectoralgorithm_ii.R @@ -1,4 +1,8 @@ +library("R6") +library("paradox") +library("checkmate") + Strategy = R6Class("Strategy", @@ -160,17 +164,13 @@ c(svec, sa, sm)$run_vector(c(100, 200, 300, 400, 500)) # list(110, 400, 1199, 4 svec_long = StrategyVector$new(c( - do.call(c, lapply(1:2000, function(x) { - sa = StrategyAdd$new() - sa$param_set$values$summand = x - sa - })), + StrategyAdd$new(multi_init = data.table(summand = 1:2000)), list(sm, sam) )) svec_long$run(100) # as.list(c(101:2100, 200, 399)) svec_long$run_vector(1:2002) - # as.list(c((1:2000) * 2, 4004, 8007)) + # as.list(c((1:2000) * 2, 4002, 8007)) From a50a2768dcb71cfa6c33d0d87e5ae691af2f4953 Mon Sep 17 00:00:00 2001 From: mb706 Date: Wed, 26 May 2021 19:33:31 +0200 Subject: [PATCH 27/64] progress --- R/Param.R | 6 +-- R/ParamFct.R | 4 +- R/ParamInt.R | 6 +-- R/ParamLgl.R | 2 +- R/ParamSet.R | 60 ++++++++++++------------- R/domain.R | 6 ++- R/helper.R | 6 +++ R/ps_union.R | 68 +---------------------------- attic/demo.R | 11 +++-- attic/vectoralgorithm.R | 1 - attic/vectoralgorithm_ii.R | 4 +- tests/testthat/helper_02_ParamSet.R | 12 ++--- 12 files changed, 63 insertions(+), 123 deletions(-) diff --git a/R/Param.R b/R/Param.R index 71c3bfcf..d3fe3a31 100644 --- a/R/Param.R +++ b/R/Param.R @@ -92,13 +92,13 @@ domain_is_number.default = function(param) FALSE # param: check_domain_vectorize = function(ids, values, checker, more_args = list()) { - if (length(checker) == length(values)) { - errors = imap(c(list(ids, values), more_args), function(id, value, ...) { + if (length(checker) == 1) { + errors = pmap(c(list(ids, values), more_args), function(id, value, ...) { ch = checker(value, ...) if (isTRUE(ch)) NULL else sprintf("%s: %s", id, ch) }) } else { - errors = imap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { + errors = pmap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { ch = chck(value, ...) if (isTRUE(ch)) NULL else sprintf("%s: %s", id, ch) }) diff --git a/R/ParamFct.R b/R/ParamFct.R index 94c53443..3af207c9 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -26,11 +26,11 @@ p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = charact #' @export domain_check.ParamFct = function(param, values) { - if (testr(values, "S1")) { + if (qtestr(values, "S1")) { values_str = as.character(values) if (all(values_str %in% param$levels[[1]])) return(TRUE) # this works because we have the grouping -- all 'levels' are the same here. } - check_domain_vectorized(param$ids, param, check_choice, more_args = list(choices = param$levels)) + check_domain_vectorize(param$id, param, check_choice, more_args = list(choices = param$levels)) } #' @export diff --git a/R/ParamInt.R b/R/ParamInt.R index a63748c8..facfaf14 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -29,10 +29,10 @@ p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ #' @export domain_check.ParamInt = function(param, values) { if (!qtestr(values, "N1()")) { - check_domain_vectorize(param$id, values, check_int, - more_args = list(lower = values$lower - 0.5, upper = values$upper + 0.5, # be lenient with bounds, because they would refer to the rounded values + return(check_domain_vectorize(param$id, values, check_int, + more_args = list(lower = param$lower - 0.5, upper = param$upper + 0.5, # be lenient with bounds, because they would refer to the rounded values tol = .51) # we don't know the tolerances of individual values, but we do know that some values are not even (non-missing, finite) numerics - ) + )) } values_num = as.numeric(values) diff --git a/R/ParamLgl.R b/R/ParamLgl.R index 164e2ff8..e66ff910 100644 --- a/R/ParamLgl.R +++ b/R/ParamLgl.R @@ -10,7 +10,7 @@ domain_check.ParamLgl = function(param, values) { if (qtestr(values, "B1")) { return(TRUE) } - check_domain_vectorized(param$id, values, check_flag) + check_domain_vectorize(param$id, values, check_flag) } #' @export diff --git a/R/ParamSet.R b/R/ParamSet.R index 3898f493..8513a3d8 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -76,14 +76,14 @@ ParamSet = R6Class("ParamSet", set(private$.params, , "id", names(params)) set(private$.params, , "has_trafo", !map_lgl(private$.params$trafo, is.null)) - initvalues = private$.params[(init_given), set_names(init, id)] - private$.tags = private$.params[, set_names(tags, id)] + initvalues = col_to_nl(private$.params[(init_given), list(init, id)]) + private$.tags = col_to_nl(private$.params, "tags", "id") set(private$.params, , setdiff(colnames(private$.params), paramcols), NULL) } setindexv(private$.params, c("id", "cls", "grouping")) - assert_names(colnames(private$.params), identical.to = paramcols) + assert_names(colnames(private$.params), permutation.of = paramcols) # add Dependencies imap(params, function(p, name) { @@ -176,12 +176,7 @@ ParamSet = R6Class("ParamSet", values[match(self$ids(class = class, tags = tags, any_tags = any_tags), names(values), nomatch = 0)] }, - trafo = function(x, param_set) { # param_set argument only here for compatibility - if (!missing(param_set)) { - warning("Giving the `param_set` argument is deprecated!") - } else { - param_set = self # for the .extra_trafo, in case it still has a param_set argument - } + trafo = function(x, param_set = self) { trafos = private$.params[names(x), list(id, trafo, has_trafo, value = x), on = "id", nomatch = 0][(has_trafo)] if (nrow(trafos)) { trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] @@ -237,14 +232,14 @@ ParamSet = R6Class("ParamSet", xs_nontune = discard(xs, inherits, "TuneToken") params = params[names(xs_nontune), on = "id"] - set(params, , "values", xs_nontune) + set(params, , "values", list(xs_nontune)) pgroups = split(params, by = c("cls", "grouping")) checkresults = map(pgroups, function(x) { domain_check(set_class(x, c(x$cls[[1]], class(x))), x$values) }) checkresults = discard(checkresults, isTRUE) if (length(checkresults)) { - return(str_collapse(checkresults), sep = "\n") + return(str_collapse(checkresults, sep = "\n")) } if (check_strict) { @@ -526,7 +521,7 @@ ParamSet = R6Class("ParamSet", # convert all integer params really to storage type int, move doubles to within bounds etc. # solves issue #293, #317 nontt = discard(xs, inherits, "TuneToken") - sanitised = set(private$.params[names(nontt), on = "id"], , "values", nontt)[ + sanitised = set(private$.params[names(nontt), on = "id"], , "values", list(nontt))[ !pmap_lgl(list(special_vals, values), has_element), list(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] insert_named(xx, set_names(sanitised$values, sanitised$id)) @@ -569,11 +564,7 @@ ParamSet = R6Class("ParamSet", if (missing(f)) { private$.extra_trafo } else { - if (test_function(f, args = c("x", "param_set"))) { - warning("The 'param_set' argument for '.extra_trafos' is deprecated and will be removed in future versions!") - } else { - assert_function(f, args = "x", null.ok = TRUE) - } + assert(check_function(f, args = c("x", "param_set"), null.ok = TRUE), check_function(f, args = "x", null.ok = TRUE)) private$.extra_trafo = f } }, @@ -630,21 +621,21 @@ ParamSet = R6Class("ParamSet", # Per-Parameter properties #' @field class (named `character()`)\cr Classes of contained parameters. Named with parameter IDs. - class = function() private$.params[, set_names(class, id)], + class = function() col_to_nl(private$.params, "class", "id"), #' @field lower (named `double()`)\cr Lower bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. - lower = function() private$.params[, set_names(class, lower)], + lower = function() col_to_nl(private$.params, "lower", "id"), #' @field upper (named `double()`)\cr Upper bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. - upper = function() private$.params[, set_names(class, upper)], + upper = function() col_to_nl(private$.params, "upper", "id"), #' @field levels (named `list()` of `character`)\cr Allowed levels of categorical parameters (`NULL` for non-categoricals). #' Named with parameter IDs. - levels = function() private$.params[, set_names(class, levels)], + levels = function() col_to_nl(private$.params, "levels", "id"), #' @field storage_type (`character()`)\cr Data types of parameters when stored in tables. Named with parameter IDs. - storage_type = function() private$.params[, set_names(class, storage_type)], + storage_type = function() col_to_nl(private$.params, "storage_type", "id"), #' @field special_vals (named `list()` of `list()`)\cr Special values for all parameters. Named with parameter IDs. - special_vals = function() private$.params[, set_names(class, special_vals)], + special_vals = function() col_to_nl(private$.params, "special_vals", "id"), #' @field default (named `list()`)\cr Default values of all parameters. If no default exists, element is not present. #' Named with parameter IDs. - default = function() private$.params[!map_lgl(default, is_nodefault), set_names(default, id)], + default = function() col_to_nl(private$.params[!map_lgl(default, is_nodefault), list(default, id)]), ############################ # Per-Parameter class properties (S3 method call) @@ -652,42 +643,46 @@ ParamSet = R6Class("ParamSet", #' @field nlevels (named `integer()`)\cr Number of distinct levels of parameters. `Inf` for double parameters or unbounded integer parameters. #' Named with param IDs. nlevels = function() { - private$.params[, + info = private$.params[, list(id, nlevels = domain_nlevels(recover_domain(.SD, .BY))), by = c("cls", "grouping") ][ - private$.params$id, on = "id", set_names(nlevels, id) + private$.params$id, on = "id", list(nlevels, id) ] + col_to_nl(info) }, #' @field is_number (named `logical()`)\cr Whether parameter is [ParamDbl] or [ParamInt]. Named with parameter IDs. is_number = function() { - private$.params[, + info = private$.params[, list(id, is_number = rep(domain_is_number(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping") ][ - private$.params$id, on = "id", set_names(is_number, id) + private$.params$id, on = "id", list(is_number, id) ] + col_to_nl(info) }, #' @field is_categ (named `logical()`)\cr Whether parameter is [ParamFct] or [ParamLgl]. Named with parameter IDs. is_categ = function() { - private$.params[, + info = private$.params[, list(id, is_categ = rep(domain_is_categ(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping") ][ - private$.params$id, on = "id", set_names(is_categ, id) + private$.params$id, on = "id", list(is_categ, id) ] + col_to_nl(info) }, #' @field is_bounded (named `logical()`)\cr Whether parameters have finite bounds. Named with parameter IDs. is_bounded = function() { - private$.params[, + info = private$.params[, list(id, is_bounded = domain_is_bounded(recover_domain(.SD, .BY))), by = c("cls", "grouping") ][ - private$.params$id, on = "id", set_names(is_bounded, id) + private$.params$id, on = "id", list(is_bounded, id) ] + col_to_nl(info) } ), @@ -699,6 +694,7 @@ ParamSet = R6Class("ParamSet", .tags = named_list(), .deps = data.table(id = character(0L), on = character(0L), cond = list()), get_tune_ps = function(values) { + return(NULL) # TODO selfparams = private$.params partsets = imap(keep(values, inherits, "TuneToken"), function(value, pn) { tunetoken_to_ps(value, selfparams[[pn]], pn) diff --git a/R/domain.R b/R/domain.R index d3e77845..b4fe28de 100644 --- a/R/domain.R +++ b/R/domain.R @@ -131,6 +131,7 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real assert_number(upper, na.ok = TRUE) # assert_character(levels, null.ok = TRUE) assert_list(special_vals) + if (length(special_vals) && !is.null(trafo)) stop("trafo and special_values can not both be given at the same time.") assert_character(tags, any.missing = FALSE, unique = TRUE) assert_number(tolerance, na.ok = TRUE) assert_function(trafo, null.ok = TRUE) @@ -145,10 +146,12 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real } + # domain is a data.table with a few classes. + # setting `id` to something preliminary so that `domain_assert()` works. param = data.table(cls = cls, grouping = grouping, cargo = list(cargo), lower = lower, upper = upper, tolerance = tolerance, levels = list(levels), special_vals = list(special_vals), default = list(default), tags = list(tags), trafo = list(trafo), requirements = list(parse_depends(depends_expr, parent.frame(2))), - storage_type = storage_type, init_given = !missing(init), init = list(if (!missing(init)) init)) + storage_type = storage_type, init_given = !missing(init), init = list(if (!missing(init)) init), id = "domain being constructed") class(param) = c(cls, "Domain", class(param)) if (!is_nodefault(default)) { @@ -156,6 +159,7 @@ domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real if ("required" %in% tags) stop("A 'required' parameter can not have a 'default'.\nWhen the method behaves the same as if the parameter value were 'X' whenever the parameter is missing, then 'X' should be a 'default', but the 'required' indicates that the parameter may not be missing.") } if (!missing(init)) { + if (!is.null(trafo)) stop("Initial value and trafo can not both be given at the same time.") domain_assert(param, list(init)) if (identical(init, default)) warning("Initial value and 'default' value seem to be the same, this is usually a mistake due to a misunderstanding of the meaning of 'default'.\nWhen the method behaves the same as if the parameter value were 'X' whenever the parameter is missing, then 'X' should be a 'default' (but then there is no point in setting it as initial value). 'default' should not be used to indicate the value with which values are initialized.") } diff --git a/R/helper.R b/R/helper.R index 6b4e604c..26df31e0 100644 --- a/R/helper.R +++ b/R/helper.R @@ -46,3 +46,9 @@ as_type = function(x, type) { ) } +# column to named list +col_to_nl = function(dt, col = 1, idcol = 2) { + data = dt[[col]] + names(data) = dt[[idcol]] + data +} diff --git a/R/ps_union.R b/R/ps_union.R index 21b655d3..91946ddd 100644 --- a/R/ps_union.R +++ b/R/ps_union.R @@ -8,71 +8,5 @@ # @param sets: list of ParamSet ps_union = function(sets, tag_set = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") - - if (!ignore_ids) { - names(sets) = map(sets, "set_id") - } - - psc = ParamSetCollection$new(sets, ignore_ids = TRUE) - - newps = ParamSet$new()$add(psc) - - # This loop collects information that is needed by the trafo. - # Resulting is a list of named lists, with one element per `sets` entry. Elements of the named lists are: - # - trafo: trafo of the given ParamSet - # - set_id: set_id of the given ParamSet - # - forward_name_translation: named `character`. Names are the Param IDs of the resulting newps, - # values are the Param IDs of the original Params in the `sets` argument. - # E.g. if a single ParamSet with set_id "sid" and with one Param with id "pid" is given, - # then this is a `c(sid.pid = "pid")`. - # Why is this needed? If the $trafo() is given a value `list(sid.pid = 1)`, then - # `forward_name_translation` can be used to rename this to `list(pid = 1)`, which is what the - # original trafo expects. - setinfo = unname(imap(keep(sets, function(x) x$has_trafo), function(s, n) { - sparams = s$params_unid # avoid slow ParamSetCollection $params active binding - sinfo = list( - trafo = s$trafo, - set_id = n, - forward_name_translation = names2(sparams) - ) - psids = names2(sparams) - if (n != "") { - psids = sprintf("%s.%s", n, psids) - } - names(sinfo$forward_name_translation) = psids - sinfo - })) - - if (length(setinfo)) { - # allnames: names of all parameters, as seen from the outside - allnames = names2(unlist(map(setinfo, "forward_name_translation"))) - assert_subset(allnames, names2(newps$params_unid)) # just check, this should always be the case - - newps$trafo = crate(function(x, param_set) { - res = unlist(mlr3misc::map(setinfo, function(s) { - trafo = s$trafo - # get the parameter values that the current trafo should operate on, - # as identified by the names in forward_name_translation - pv = x[match(names(s$forward_name_translation), names(x), nomatch = 0)] - - # translate name from "." to "" - names(pv) = s$forward_name_translation[names(pv)] - pv = trafo(pv) - - # append prefix again. trafo() could have changed parameter names, so - # we can't use any cached name_translation magic here - if (s$set_id != "") { - names(pv) = sprintf("%s.%s", s$set_id, names(pv)) - } - - pv - }), recursive = FALSE) - - # add the Params that were not translated at all, because the ParamSet doesn't know about them. - res = c(mlr3misc::remove_named(x, allnames), res) - - res[c(intersect(names(res), names(x)), setdiff(names(res), names(x)))] # unchanged parameter names stay in order - }, setinfo, allnames) - } - newps + ParamSetCollection$new(sets, tag_set = tag_set, tag_params = tag_params)$flatten() } diff --git a/attic/demo.R b/attic/demo.R index 885fc398..89d78565 100644 --- a/attic/demo.R +++ b/attic/demo.R @@ -1,9 +1,12 @@ +p_dbl(0, 1, init = 1) +p = ps(prob = p_dbl(0, 1, init = 1), param = p_int(0, logscale = TRUE)) -probability = p_dbl(0, 1) -param = p_int(0, logscale = TRUE) +p$values +p$values$prob = 0.5 +p$values +p$values$param = 0 +p$values$param = -100 -ps(prob = probability, param = param) - diff --git a/attic/vectoralgorithm.R b/attic/vectoralgorithm.R index ea31f23a..f59458bc 100644 --- a/attic/vectoralgorithm.R +++ b/attic/vectoralgorithm.R @@ -1,4 +1,3 @@ - library("R6") library("paradox") library("checkmate") diff --git a/attic/vectoralgorithm_ii.R b/attic/vectoralgorithm_ii.R index ed1698fb..9a8b83d6 100644 --- a/attic/vectoralgorithm_ii.R +++ b/attic/vectoralgorithm_ii.R @@ -1,10 +1,7 @@ - library("R6") library("paradox") library("checkmate") - - Strategy = R6Class("Strategy", public = list( is_scalar = NULL, @@ -35,6 +32,7 @@ Strategy = R6Class("Strategy", ) ) + StrategyHybridVector = R6Class("StrategyHybridVector", inherits = Strategy, public = list( initialize = function(param_set, multi_init = NULL) { diff --git a/tests/testthat/helper_02_ParamSet.R b/tests/testthat/helper_02_ParamSet.R index 1df2ee13..c5f4074b 100644 --- a/tests/testthat/helper_02_ParamSet.R +++ b/tests/testthat/helper_02_ParamSet.R @@ -1,5 +1,5 @@ th_paramset_dbl1 = function() { - ParamSet$new( + ParamSet_legacy$new( params = list( th_param_dbl() ) @@ -7,7 +7,7 @@ th_paramset_dbl1 = function() { } th_paramset_full = function() { - ParamSet$new( + ParamSet_legacy$new( params = list( th_param_int(), th_param_dbl(), @@ -18,13 +18,13 @@ th_paramset_full = function() { } th_paramset_untyped = function() { - ParamSet$new( + ParamSet_legacy$new( params = list(th_param_uty()) ) } th_paramset_numeric = function() { - ParamSet$new( + ParamSet_legacy$new( params = list( th_param_int(), th_param_dbl() @@ -33,7 +33,7 @@ th_paramset_numeric = function() { } th_paramset_categorical = function() { - ParamSet$new( + ParamSet_legacy$new( params = list( th_param_fct(), th_param_lgl() @@ -42,7 +42,7 @@ th_paramset_categorical = function() { } th_paramset_repeated = function() { - ps = ParamSet$new( + ps = ParamSet_legacy$new( params = c( list(th_param_nat(), th_param_fct()) ) From 83e1aed3b2105735ef2f7b8362e875b4a62d6d38 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 12:02:35 +0200 Subject: [PATCH 28/64] condition as S3 --- R/Condition.R | 150 +++++++++++++++++++++++++++----------------------- R/domain.R | 2 - 2 files changed, 80 insertions(+), 72 deletions(-) diff --git a/R/Condition.R b/R/Condition.R index 167b2847..24c235b1 100644 --- a/R/Condition.R +++ b/R/Condition.R @@ -1,87 +1,97 @@ +# -- class methods + +#' @describeIn Condition +#' +#' Used internally. Tests whether a value satisfies a given condition. +#' Vectorizes when `x` is atomic. +#' +#' @param cond (`Condition`)\cr +#' `Condition` to use +#' @param x (`any`)\cr +#' Value to test +condition_test = function(cond, x) { + UseMethod("conditionTest") +} + +#' @describeIn Condition +#' +#' Used internally. Returns a string that represents the condition for pretty +#' printing, in the form `" "`, e.g. `"x == 3"` or +#' `"param %in% {1, 2, 10}"`. +#' +#' @param cond (`Condition`)\cr +#' `Condition` to use +#' @param lhs_chr (`character(1)`)\cr +#' Symbolic representation to use for `` in the returned string. +condition_as_string = function(cond, lhs_chr = "x") { + assert_string(lhs_chr) + UseMethod("conditionTest") +} + +# -- Condition + #' @title Dependency Condition #' #' @description #' Condition object, to specify the condition in a dependency. #' +#' @param rhs (`any`)\cr +#' Right-hand-side of the condition. +#' @param condition_format_string (`character(1)`)\cr +#' Format-string for representing the condition when pretty-printing +#' in `condition_as_string()`. +#' Should contain two `%s`, as it is used in an `sprintf()`-call with +#' two further string values. +#' #' @section Currently implemented simple conditions: -#' * `CondEqual$new(rhs)` \cr -#' Parent must be equal to `rhs`. -#' * `CondAnyOf$new(rhs)` \cr -#' Parent must be any value of `rhs`. +#' * `CondEqual(rhs)` \cr +#' Value must be equal to `rhs`. +#' * `CondAnyOf(rhs)` \cr +#' Value must be any value of `rhs`. #' #' @aliases CondEqual CondAnyOf #' @export -Condition = R6Class("Condition", - public = list( - #' @field type (`character(1)`)\cr - #' Name / type of the condition. - type = NULL, - #' @field rhs (`any`)\cr - #' Right-hand-side of the condition. - rhs = NULL, - #' @description - #' Creates a new instance of this [R6][R6::R6Class] class. - #' - #' @param type (`character(1)`)\cr - #' Name / type of the condition. - #' @param rhs (`any`)\cr - #' Right-hand-side of the condition. - initialize = function(type, rhs) { - self$type = assert_string(type) - self$rhs = rhs - }, +Condition = function(rhs, condition_format_string) { + assert_string(condition_format_string) + structure(list(rhs = rhs, condition_format_string = condition_format_string), class = "Condition") +} + +condition_as_string.Condition = function(cond, lhs_chr = "x") { + sprintf(condition_format_string, lhs_chr, str_collapse(cond$rhs)) +} - #' @description - #' Checks if condition is satisfied. - #' Called on a vector of parent param values. - #' - #' @param x (`vector()`). - #' @return `logical(1)`. - test = function(x) stop("abstract"), +#' @export +format.Condition = function(x, ...) { + sprintf("", class(x)[[1L]]) +} - #' @description - #' Conversion helper for print outputs. - #' @param lhs_chr (`character(1)`) - as_string = function(lhs_chr = "x") { - sprintf("%s %s %s", lhs_chr, self$type, str_collapse(self$rhs)) - }, +#' @export +print.Condition = function(x, ...) { + catf("%s: %s", class(x)[[1L]], condition_as_string(x)) +} - #' @description - #' Helper for print outputs. - format = function() { - sprintf("<%s:%s>", class(self)[1L], self$type) - }, +# -- CondEqual - #' @description - #' Printer. - #' - #' @param ... (ignored). - print = function(...) { - catf("%s: %s", class(self)[1L], self$as_string()) - } - ), -) +#' @export +CondEqual = function(rhs) { + assert_atomic(rhs, any.missing = FALSE, len = 1) + cond = Condition(rhs, "%s == %s") + set_class(cond, c("CondEqual", class(cond))) +} + +#' @export +condition_test.CondEqual = function(cond, x) { + !is.na(x) & x == cond$rhs +} #' @export -CondEqual = R6Class("CondEqual", inherit = Condition, - public = list( - initialize = function(rhs) { - assert_atomic(rhs, any.missing = FALSE, len = 1) - super$initialize("equal", rhs) - }, - test = function(x) !is.na(x) & x == self$rhs, - as_string = function(lhs_chr = "x") sprintf("%s = %s", lhs_chr, as.character(self$rhs)) - ) -) +CondAnyOf = function(rhw) { + assert_atomic(rhs, any.missing = FALSE, min.len = 1, unique = TRUE) + cond = Condition(rhs, "%s %%in%% {%s}") + set_class(cond, c("CondEqual", class(cond))) +} #' @export -CondAnyOf = R6Class("CondAnyOf", inherit = Condition, - public = list( - initialize = function(rhs) { - assert_atomic(rhs, any.missing = FALSE, min.len = 1, unique = TRUE) - super$initialize("anyof", rhs) - }, - test = function(x) !is.na(x) & x %in% self$rhs, - as_string = function(lhs_chr = "x") sprintf("%s \u2208 {%s}", lhs_chr, str_collapse(self$rhs)) - ) -) +condition_test.CondAnyOf = function(cond, x) { + !is.na(x) & x %in% cond$rhs +} diff --git a/R/domain.R b/R/domain.R index d3e77845..e96b3365 100644 --- a/R/domain.R +++ b/R/domain.R @@ -1,5 +1,3 @@ -#' @include helper.R - #' @title Domain: Parameter Range without an Id #' #' @description From 803549f9cab41358ecfd38d6876a5889c93fdb35 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 12:04:10 +0200 Subject: [PATCH 29/64] Param.R -> Domain_methods.R --- R/{Param.R => Domain_methods.R} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename R/{Param.R => Domain_methods.R} (100%) diff --git a/R/Param.R b/R/Domain_methods.R similarity index 100% rename from R/Param.R rename to R/Domain_methods.R From 0fd1620f430e57b879e10e1db872a2582ee4e78b Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 12:05:33 +0200 Subject: [PATCH 30/64] S6ObjectCache is pretty cool but we don't need it --- {R => attic}/S6ObjectCache.R | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {R => attic}/S6ObjectCache.R (100%) diff --git a/R/S6ObjectCache.R b/attic/S6ObjectCache.R similarity index 100% rename from R/S6ObjectCache.R rename to attic/S6ObjectCache.R From 66ca1bdaab0df16edff08f03f67157440395ddb2 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 12:21:41 +0200 Subject: [PATCH 31/64] domain -> Domain --- R/{domain.R => Domain.R} | 2 +- R/ParamDbl.R | 2 +- R/ParamFct.R | 2 +- R/ParamInt.R | 2 +- R/ParamLgl.R | 2 +- R/ParamS6.R | 2 +- R/ParamUty.R | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename R/{domain.R => Domain.R} (99%) diff --git a/R/domain.R b/R/Domain.R similarity index 99% rename from R/domain.R rename to R/Domain.R index e96b3365..f7e47c6f 100644 --- a/R/domain.R +++ b/R/Domain.R @@ -120,7 +120,7 @@ NULL # @param Constructor: The ParamXxx to call `$new()` for. # @param constargs: arguments of constructor # @param constargs_override: replace these in `constargs`, but don't represent this in printer -domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real_, levels = NULL, special_vals = list(), default = NO_DEF, tags = character(0), +Domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real_, levels = NULL, special_vals = list(), default = NO_DEF, tags = character(0), tolerance = NA_real_, trafo = NULL, storage_type = "list", depends_expr = NULL, init) { assert_string(cls) diff --git a/R/ParamDbl.R b/R/ParamDbl.R index a3fe759a..7554ba68 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -17,7 +17,7 @@ p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ real_upper = upper } - domain(cls = "ParamDbl", grouping = "ParamDbl", lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, storage_type = "numeric", + Domain(cls = "ParamDbl", grouping = "ParamDbl", lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, storage_type = "numeric", depends_expr = substitute(depends), init = init) } diff --git a/R/ParamFct.R b/R/ParamFct.R index 94c53443..0cd58de6 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -21,7 +21,7 @@ p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = charact } # group p_fct by levels, so the group can be checked in a vectorized fashion. grouping = str_collapse(gsub("([\\\\\"])", "\\\\\\1", sort(real_levels)), quote = '"', sep = ",") - domain(cls = "ParamFct", grouping = grouping, levels = real_levels, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "character", depends_expr = substitute(depends), init = init) + Domain(cls = "ParamFct", grouping = grouping, levels = real_levels, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "character", depends_expr = substitute(depends), init = init) } #' @export diff --git a/R/ParamInt.R b/R/ParamInt.R index a63748c8..aecc5fe2 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -21,7 +21,7 @@ p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ real_upper = upper } - domain(cls = cls, grouping = cls, lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, + Domain(cls = cls, grouping = cls, lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, storage_type = "integer", depends_expr = substitute(depends), init = init) } diff --git a/R/ParamLgl.R b/R/ParamLgl.R index 164e2ff8..2548ddb2 100644 --- a/R/ParamLgl.R +++ b/R/ParamLgl.R @@ -1,7 +1,7 @@ #' @rdname Domain #' @export p_lgl = function(special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { - domain(cls = "ParamLgl", grouping = "ParamLgl", levels = c(TRUE, FALSE), special_vals = special_vals, default = default, + Domain(cls = "ParamLgl", grouping = "ParamLgl", levels = c(TRUE, FALSE), special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "logical", depends_expr = substitute(depends), init = init) } diff --git a/R/ParamS6.R b/R/ParamS6.R index 6cb36936..f37bd866 100644 --- a/R/ParamS6.R +++ b/R/ParamS6.R @@ -11,7 +11,7 @@ p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = charact storage_type = set$class } - domain(cls = "ParamSet6", grouping = support_description, + Domain(cls = "ParamSet6", grouping = support_description, cargo = set, lower = suppressWarnings(as.numeric(set$lower)), upper = suppressWarnings(as.numeric(set$upper)), diff --git a/R/ParamUty.R b/R/ParamUty.R index 356f222c..29794ee4 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -7,7 +7,7 @@ p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, t custom_check_result = custom_check(1) assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") } - domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "list", depends_expr = substitute(depends), init = init) + Domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "list", depends_expr = substitute(depends), init = init) } #' @export From 3db1fc6089286206972b51506878b39c900267c1 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 12:56:57 +0200 Subject: [PATCH 32/64] for domain methods: default -> Domain --- R/Domain_methods.R | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/R/Domain_methods.R b/R/Domain_methods.R index 71c3bfcf..23d230b8 100644 --- a/R/Domain_methods.R +++ b/R/Domain_methods.R @@ -22,10 +22,12 @@ domain_check = function(param, values) { UseMethod("domain_check") } } + #' @export domain_assert = makeAssertionFunction(domain_check) + #' @export -domain_test = function(param, values) domain_check(param, values) +domain_test = function(param, values) isTRUE(domain_check(param, values)) #' @export domain_storage_type = function(param) { @@ -72,22 +74,22 @@ domain_sanitize = function(param, values) { } #' @export -domain_nlevels.default = function(param) rep(Inf, nrow(param)) +domain_nlevels.Domain = function(param) rep(Inf, nrow(param)) #' @export -domain_is_bounded.default = function(param) rep(FALSE, nrow(param)) +domain_is_bounded.Domain = function(param) rep(FALSE, nrow(param)) #' @export -domain_qunif.default = function(param) stop("undefined") +domain_qunif.Domain = function(param) stop("undefined") #' @export -domain_sanitize.default = function(param, values) values +domain_sanitize.Domain = function(param, values) values #' @export -domain_is_categ.default = function(param) FALSE +domain_is_categ.Domain = function(param) FALSE #' @export -domain_is_number.default = function(param) FALSE +domain_is_number.Domain = function(param) FALSE # param: From 2ed8ef544d5424c68738f9e396e608a6e423911d Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 19:10:43 +0200 Subject: [PATCH 33/64] cleaning up --- R/Domain.R | 65 +++++++++++++---- R/ParamFct.R | 1 + R/ParamSet.R | 195 ++++++++++++++++++++++++++++----------------------- R/asserts.R | 10 --- R/zzz.R | 5 ++ 5 files changed, 166 insertions(+), 110 deletions(-) diff --git a/R/Domain.R b/R/Domain.R index f7e47c6f..76521561 100644 --- a/R/Domain.R +++ b/R/Domain.R @@ -36,8 +36,8 @@ #' An expression indicating a requirement for the parameter that will be constructed from this. Can be given as an #' expression (using `quote()`), or the expression can be entered directly and will be parsed using NSE (see #' examples). The expression may be of the form ` == ` or ` %in% `, which will result in -#' dependencies according to `ParamSet$add_dep(on = "", cond = CondEqual$new())` or -#' `ParamSet$add_dep(on = "", cond = CondAnyOf$new())`, respectively (see [`CondEqual`], +#' dependencies according to `ParamSet$add_dep(on = "", cond = CondEqual())` or +#' `ParamSet$add_dep(on = "", cond = CondAnyOf())`, respectively (see [`CondEqual`], #' [`CondAnyOf`]). The expression may also contain multiple conditions separated by `&&`. #' @param logscale (`logical(1)`)\cr #' Put numeric domains on a log scale. Default `FALSE`. Log-scale `Domain`s represent parameter ranges where lower and upper bounds @@ -120,17 +120,25 @@ NULL # @param Constructor: The ParamXxx to call `$new()` for. # @param constargs: arguments of constructor # @param constargs_override: replace these in `constargs`, but don't represent this in printer -Domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real_, levels = NULL, special_vals = list(), default = NO_DEF, tags = character(0), - tolerance = NA_real_, trafo = NULL, storage_type = "list", depends_expr = NULL, init) { +Domain = function(cls, grouping, + cargo = NULL, + lower = NA_real_, upper = NA_real_, tolerance = NA_real_, levels = NULL, + special_vals = list(), + default = NO_DEF, + tags = character(0), + trafo = NULL, + depends_expr = NULL, + storage_type = "list", + init) { assert_string(cls) assert_string(grouping) assert_number(lower, na.ok = TRUE) assert_number(upper, na.ok = TRUE) - # assert_character(levels, null.ok = TRUE) + assert_number(tolerance, na.ok = TRUE) + assert_character(levels, null.ok = TRUE) assert_list(special_vals) assert_character(tags, any.missing = FALSE, unique = TRUE) - assert_number(tolerance, na.ok = TRUE) assert_function(trafo, null.ok = TRUE) @@ -143,22 +151,35 @@ Domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real } - param = data.table(cls = cls, grouping = grouping, cargo = list(cargo), lower = lower, upper = upper, tolerance = tolerance, levels = list(levels), + param = data.table(id = "", cls = cls, grouping = grouping, + cargo = list(cargo), + lower = lower, upper = upper, tolerance = tolerance, levels = list(levels), special_vals = list(special_vals), - default = list(default), tags = list(tags), trafo = list(trafo), requirements = list(parse_depends(depends_expr, parent.frame(2))), - storage_type = storage_type, init_given = !missing(init), init = list(if (!missing(init)) init)) + default = list(default), + storage_type = storage_type, + .tags = list(tags), + .trafo = list(trafo), + .requirements = list(parse_depends(depends_expr, parent.frame(2))), + + .init_given = !missing(init), + .init = list(if (!missing(init)) init) + ) + class(param) = c(cls, "Domain", class(param)) if (!is_nodefault(default)) { domain_assert(param, list(default)) if ("required" %in% tags) stop("A 'required' parameter can not have a 'default'.\nWhen the method behaves the same as if the parameter value were 'X' whenever the parameter is missing, then 'X' should be a 'default', but the 'required' indicates that the parameter may not be missing.") } + if (!missing(init)) { domain_assert(param, list(init)) if (identical(init, default)) warning("Initial value and 'default' value seem to be the same, this is usually a mistake due to a misunderstanding of the meaning of 'default'.\nWhen the method behaves the same as if the parameter value were 'X' whenever the parameter is missing, then 'X' should be a 'default' (but then there is no point in setting it as initial value). 'default' should not be used to indicate the value with which values are initialized.") } # repr: what to print + # This takes the call of the shortform-constructor (such as `p_dbl()`) and inserts all the + # given values. constructorcall = match.call(sys.function(-1), sys.call(-1), envir = parent.frame(2)) trafoexpr = constructorcall$trafo constructorcall$trafo = NULL @@ -168,10 +189,28 @@ Domain = function(cls, grouping, cargo = NULL, lower = NA_real_, upper = NA_real reprargs$trafo = trafoexpr if (isTRUE(reprargs$logscale)) reprargs$trafo = NULL attr(param, "repr") = as.call(c(constructorcall[[1]], reprargs)) - param$id = repr(attr(param, "repr")) # some ID for consistency with ParamSet$params, only for error messages. + set(param, , "id", repr(attr(param, "repr"))) # some ID for consistency with ParamSet$params, only for error messages. + + assert_names(names(param), identical.to = domain_names) # If this is not true then there is either a bug in Domain(), or empty_domain was not updated. + param } +empty_domain = data.table(id = character(0), cls = character(0), grouping = character(0), + cargo = list(), + lower = numeric(0), upper = numeric(0), tolerance = numeric(0), levels = list(), + special_vals = list(), + default = list(), + storage_type = character(0), + .tags = list(), + .trafo = list(), + .requirements = list(), + .init_given = logical(0), + .init = list()) + +domain_names = names(empty_domain) +domain_names_permanent = grep("^\\.", domain_names, values = TRUE) + #' @export print.Domain = function(x, ...) { repr = attr(x, "repr") @@ -205,8 +244,8 @@ print.Domain = function(x, ...) { # parse_depends(quote(x == 1 && y %in% c("b", "c")), environment()) # # same as: # list( -# list(on = "x", CondEqual$new(1)), -# list(on = "y", CondAnyOf$new(c("b", "c"))) +# list(on = "x", CondEqual(1)), +# list(on = "y", CondAnyOf(c("b", "c"))) # ) parse_depends = function(depends_expr, evalenv) { if (is.null(depends_expr)) return(NULL) @@ -275,7 +314,7 @@ parse_depends = function(depends_expr, evalenv) { if (!is.symbol(comparand)) throw("LHS must be a parameter name") comparand = as.character(comparand) value = eval(value, envir = evalenv) - list(list(on = comparand, cond = constructor$new(value))) + list(list(on = comparand, cond = constructor(value))) } recurse_expression(depends_expr) diff --git a/R/ParamFct.R b/R/ParamFct.R index 0cd58de6..79eeeca8 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -20,6 +20,7 @@ p_fct = function(levels, special_vals = list(), default = NO_DEF, tags = charact real_levels = levels } # group p_fct by levels, so the group can be checked in a vectorized fashion. + # We escape '"' and '\' to '\"' and '\\', respectively. grouping = str_collapse(gsub("([\\\\\"])", "\\\\\\1", sort(real_levels)), quote = '"', sep = ",") Domain(cls = "ParamFct", grouping = grouping, levels = real_levels, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "character", depends_expr = substitute(depends), init = init) } diff --git a/R/ParamSet.R b/R/ParamSet.R index 3898f493..ec8a9353 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -65,42 +65,32 @@ ParamSet = R6Class("ParamSet", assert_list(params, types = "Domain") assert_names(names(params), type = "strict") - assert_ids(names(params)) if (!length(params)) { - private$.params = data.table(cls = character(0), grouping = character(0), cargo = list(), lower = numeric(0), upper = numeric(0), tolerance = numeric(0), - levels = list(), special_vals = list(), default = list(), trafo = list(), requirements = list(), id = character(0)) - initvalues = named_list() + paramtbl = empty_domain } else { - private$.params = rbindlist(params) - set(private$.params, , "id", names(params)) - set(private$.params, , "has_trafo", !map_lgl(private$.params$trafo, is.null)) - - initvalues = private$.params[(init_given), set_names(init, id)] - private$.tags = private$.params[, set_names(tags, id)] + paramtbl = rbindlist(params) + set(paramtbl, , "id", names(params)) + } - set(private$.params, , setdiff(colnames(private$.params), paramcols), NULL) + private$.tags = with(paramtbl, set_names(tags, id)) + initvalues = with(paramtbl[(.init_given), .(init, id)], set_names(.init, id)) + private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") + requirements = paramtbl$.requirements + for (row in seq_len(nrow(paramtbl))) { + for (req in .requirements[[row]]) { + invoke(self$add_dep, id = paramtbl$id[[row]], .args = rl) + } } - setindexv(private$.params, c("id", "cls", "grouping")) - assert_names(colnames(private$.params), identical.to = paramcols) - - # add Dependencies - imap(params, function(p, name) { - if (is.null(p$requirements[[1]])) return(NULL) - map(p$requirements[[1]], function(req) { - if (!req$on %in% names(params) || req$on == name) { - if (allow_dangling_dependencies) { - if (name == req$on) stop("A param cannot depend on itself!") - self$deps = rbind(self$deps, data.table(id = name, on = req$on, cond = list(req$cond))) - } else { - stopf("Parameter %s can not depend on %s.", name, req$on) - } - } else { - invoke(self$add_dep, id = name, .args = req) - } - }) - }) + + set(paramtbl, , setdiff(colnames(paramtbl), domain_names_permanent), NULL) + assert_names(colnames(paramtbl), identical.to = domain_names_permanent) + + setindexv(paramtbl, c("id", "cls", "grouping")) + + private$.params = paramtbl + self$values = initvalues }, @@ -121,10 +111,11 @@ ParamSet = R6Class("ParamSet", return(private$.params$id) } selftags = private$.tags - ptbl[(is.null(class) | cls %in% class) & - (is.null(tags) | map_lgl(selftags, function(tg) all(tags %in% tg))) & - (is.null(any_tags) | map_lgl(selftags, function(tg) any(any_tags %in% tg))), - id] + ptbl = private$.params + clsmatch = if (is.null(class)) TRUE else ptbl$cls %in% class + alltagmatch = if (is.null(tags)) TRUE else map_lgl(selftags, function(tg) all(tags %in% tg)) + anytagmatch = if (is.null(any_tags)) TRUE else map_lgl(selftags, function(tg) any(any_tags %in% tg)) + ptbl[clsmatch & tagmatch & anytagmatch, id] }, #' @description @@ -182,15 +173,15 @@ ParamSet = R6Class("ParamSet", } else { param_set = self # for the .extra_trafo, in case it still has a param_set argument } - trafos = private$.params[names(x), list(id, trafo, has_trafo, value = x), on = "id", nomatch = 0][(has_trafo)] + trafos = private$.params[names(x), .(id, trafo, value = x), nomatch = 0] if (nrow(trafos)) { - trafos = trafos[, list(value = trafo[[1]](value[[1]])), by = "id"] - insert_named(x, set_names(trafos$value, trafos$id)) + transformed = pmap(trafos, function(id, trafo, value) trafo(value)) + insert_named(x, set_names(transformed, trafos$id)) } extra_trafo = private$.extra_trafo if (!is.null(extra_trafo)) { if (test_function(extra_trafo, args = c("x", "param_set"))) { - x = extra_trafo(x, param_set) + x = extra_trafo(x = x, param_set = param_set) } else { x = extra_trafo(x) } @@ -198,6 +189,7 @@ ParamSet = R6Class("ParamSet", x }, + # assert_value: used internally, to avoid touble-asserts test_constraint = function(x, assert_value = TRUE) { if (assert_value) self$assert(x) assert_flag(is.null(private$.constraint) || private$.constraint(x)) @@ -206,7 +198,7 @@ ParamSet = R6Class("ParamSet", test_constraint_dt = function(x, assert_value = TRUE) { assert_data_table(x) if (assert_value) self$assert_dt(x) - map_dbl(transpose(x), self$test_constraint) + map_lgl(transpose(x), self$test_constraint, assert_value = FALSE) }, #' @description @@ -237,6 +229,7 @@ ParamSet = R6Class("ParamSet", xs_nontune = discard(xs, inherits, "TuneToken") params = params[names(xs_nontune), on = "id"] + set(params, , "values", xs_nontune) pgroups = split(params, by = c("cls", "grouping")) checkresults = map(pgroups, function(x) { @@ -253,7 +246,7 @@ ParamSet = R6Class("ParamSet", return(sprintf("Missing required parameters: %s", str_collapse(required))) } return(check_dependencies(xs)) - if (!self$test_constraint(xs)) return(sprintf("Constraint not fulfilled.")) + if (!self$test_constraint(xs, assert_value = FALSE)) return(sprintf("Constraint not fulfilled.")) } TRUE # we passed all checks @@ -308,7 +301,7 @@ ParamSet = R6Class("ParamSet", #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. #' @return If successful `xs` invisibly, if not an error message. - assert = function(xs, check_strict = FALSE, .var.name = vname(xs)) makeAssertion(xs, self$check(xs, check_strict = check_strict), .var.name, NULL), # nolint + assert = function(xs, check_strict = FALSE, .var.name = vname(xs)) makeAssertion(xs, self$check(xs, check_strict = check_strict), .var.name, NULL), # nolint #' @description #' \pkg{checkmate}-like check-function. Takes a [data.table::data.table] @@ -368,9 +361,20 @@ ParamSet = R6Class("ParamSet", }, get_param = function(id) { - .id = id - paramrow = private$.params[id == .id] + assert_string(id) + paramrow = private$.params[id, on = "id", nomatch = NULL] + if (!nrow(paramrow)) stopf("No param with id '%s'", id) + + vals = self$values + paramrow[, `:=`( + .tags = private$.tags[[id]], + .trafo = private$.trafos[id, trafo], + .requirements = list(transpose_list(private$.deps[id, .(on, cond), on = "id"])), + .init_given = id %in% names(values), + .init = unname(vals[id])) + ] + set_class(paramrow, c(paramrow$cls, class(paramrow))) }, @@ -379,25 +383,29 @@ ParamSet = R6Class("ParamSet", #' #' @param ids (`character()`). #' @return `ParamSet`. - subset = function(ids, allow_dangling_dependencies = FALSE) { + subset = function(ids, allow_dangling_dependencies = FALSE, keep_constraint = TRUE) { param_ids = private$.params$id assert_subset(ids, param_ids) deps = self$deps if (!allow_dangling_dependencies && nrow(deps)) { # check that all required / leftover parents are still in new ids on = NULL - parents = unique(deps[id %in% ids, on]) + parents = unique(deps[ids, on, on = "id", nomatch = NULL]) pids_not_there = setdiff(parents, ids) if (length(pids_not_there) > 0L) { stopf(paste0("Subsetting so that dependencies on params exist which would be gone: %s.", "\nIf you still want to subset, set allow_dangling_dependencies to TRUE."), str_collapse(pids_not_there)) } } - result = ParamSet$new(extra_trafo = self$extra_trafo) - result$.__enclos_env__$private$.params = private$.params[id %in% ids, paramcols, with = FALSE] + result = ParamSet$new() + + + result$.__enclos_env__$private$.params = private$.params[ids, on = "id"] + result$.__enclos_env__$private$.trafos = private$.trafos[ids, on = "id", nomatch = NULL] result$assert_values = FALSE result$deps = deps - result$constraint = private$.constraint + if (keep_constraint) result$constraint = self$constraint + result$extra_trafo = self$extra_trafo # restrict to ids already in pvals values = self$values result$values = values[match(ids, names(values), nomatch = 0)] @@ -407,9 +415,11 @@ ParamSet = R6Class("ParamSet", subspaces = function(ids = private$.params$id) { sapply(ids, simplify = FALSE, function(get_id) { - result = ParamSet$new(extra_trafo = self$extra_trafo) + result = ParamSet$new() + result$extra_trafo = self$extra_trafo # constraint make no sense here, basically by definition result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] + result$.__enclos_env__$private$.trafos = private$.trafos[get_id, on = "id", nomatch = NULL] result$assert_values = FALSE result$values = values[match(get_id, names(values), nomatch = 0)] result$assert_values = TRUE @@ -431,7 +441,7 @@ ParamSet = R6Class("ParamSet", assert_names(names(values), subset.of = self$ids()) pars = private$get_tune_ps(values) on = NULL # pacify static code check - dangling_deps = pars$deps[!on %in% pars$ids()] + dangling_deps = pars$deps[!pars$ids(), on = "on"] if (nrow(dangling_deps)) { stopf("Dangling dependencies not allowed: Dependencies on %s dangling.", str_collapse(dangling_deps$on)) } @@ -443,20 +453,23 @@ ParamSet = R6Class("ParamSet", #' #' @param id (`character(1)`). #' @param on (`character(1)`). + #' @param allow_dangling_dependencies (`logical(1)`): Whether to allow dependencies on parameters that are not present. #' @param cond ([Condition]). - add_dep = function(id, on, cond) { + add_dep = function(id, on, cond, allow_dangling_dependencies = FALSE) { params = private$.params ids = params$id assert_choice(id, ids) - assert_choice(on, ids) - assert_r6(cond, "Condition") + if (allow_dangling_dependencies) assert_choice(on, ids) else assert_string(on) + assert_class(cond, "Condition") if (id == on) { stopf("A param cannot depend on itself!") } - feasible_on_values = map_lgl(cond$rhs, domain_check, param = self$get_param(on)) - if (any(!feasible_on_values)) { - stopf("Condition has infeasible values for %s: %s", on, str_collapse(cond$rhs[!feasible_on_values])) + if (on %in% ids) { # not necessarily true when allow_dangling_dependencies + feasible_on_values = map_lgl(cond$rhs, domain_check, param = self$get_param(on)) + if (any(!feasible_on_values)) { + stopf("Condition has infeasible values for %s: %s", on, str_collapse(cond$rhs[!feasible_on_values])) + } } private$.deps = rbind(private$.deps, data.table(id = id, on = on, cond = list(cond))) invisible(self) @@ -465,7 +478,7 @@ ParamSet = R6Class("ParamSet", #' @description #' Helper for print outputs. format = function() { - sprintf("<%s[%s]>", class(self)[[1L]], self$length) + sprintf("<%s(%s)>", class(self)[[1L]], self$length) }, #' @description @@ -504,7 +517,7 @@ ParamSet = R6Class("ParamSet", #' @field data (`data.table`) `data.table` representation of the `ParamSet`. data = function(v) { - if (!missing(v)) stop("v is read-only") + if (!missing(v)) stop("data is read-only") private$.params[, list(id, class = cls, lower, upper, levels, nlevels = self$nlevels, is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags = private$.tags)] }, @@ -526,21 +539,22 @@ ParamSet = R6Class("ParamSet", # convert all integer params really to storage type int, move doubles to within bounds etc. # solves issue #293, #317 nontt = discard(xs, inherits, "TuneToken") - sanitised = set(private$.params[names(nontt), on = "id"], , "values", nontt)[ + sanitized = set(private$.params[names(nontt), on = "id"], , "values", nontt)[ !pmap_lgl(list(special_vals, values), has_element), - list(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] - insert_named(xx, set_names(sanitised$values, sanitised$id)) + .(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] + insert_named(xx, with(sanitized, set_names(values, id))) } # store with param ordering, return value with original ordering - private$.values = xs[private$.params[id %in% names(xs), id]] + private$.values = xs[match(private$.params$id, names(xs), nomatch = 0)] xs }, #' @template field_tags tags = function(v) { if (!missing(v)) { - assert_list(v, any.missing = FALSE, types = "character") assert_names(names(v), permutation.of = private$.params$id) + assert_list(v, any.missing = FALSE, types = "character") + # store with param ordering, return value with original ordering private$.tags = v[match(private$.params$id, names(v), nomatch = 0)] return(v) } @@ -550,9 +564,20 @@ ParamSet = R6Class("ParamSet", #' @template field_params params = function(rhs) { if (!missing(rhs)) { - stop("$params is read-only.") + stop("params is read-only.") } - private$.params[, paramcols, with = FALSE] + + result = copy(private$.params) + result[, .tags = private$.tags] + result[private$.trafos, .trafo := trafo, on = "id"] + result[private$.deps, .requirements := transpose_list(.(on, cond)), on = "id"] + vals = self$values + result[, `:=`( + .init_given = id %in% names(vals), + .init := unname(vals[id]) + )] + + result }, #' @field extra_trafo (`function(x, param_set)`)\cr @@ -615,7 +640,7 @@ ParamSet = R6Class("ParamSet", #' @field is_empty (`logical(1)`)\cr Is the `ParamSet` empty? Named with parameter IDs. is_empty = function() nrow(private$.params) == 0L, #' @field has_trafo (`logical(1)`)\cr Whether a `trafo` function is present, in parameters or in `extra_trafo`. - has_trafo = function() !is.null(private$.extra_trafo) || any(private$.params$has_trafo), + has_trafo = function() !is.null(private$.extra_trafo) || nrow(private$.trafos), #' @field has_deps (`logical(1)`)\cr Whether the parameter dependencies are present has_deps = function() nrow(self$deps) > 0L, #' @field has_constraint (`logical(1)`)\cr Whether parameter constraint is set. @@ -630,21 +655,21 @@ ParamSet = R6Class("ParamSet", # Per-Parameter properties #' @field class (named `character()`)\cr Classes of contained parameters. Named with parameter IDs. - class = function() private$.params[, set_names(class, id)], + class = function() with(private$.params, set_names(class, id)), #' @field lower (named `double()`)\cr Lower bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. - lower = function() private$.params[, set_names(class, lower)], + lower = function() with(private$.params, set_names(lower, id)), #' @field upper (named `double()`)\cr Upper bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. - upper = function() private$.params[, set_names(class, upper)], + upper = function() with(private$.params, set_names(upper, id)), #' @field levels (named `list()` of `character`)\cr Allowed levels of categorical parameters (`NULL` for non-categoricals). #' Named with parameter IDs. - levels = function() private$.params[, set_names(class, levels)], + levels = function() with(private$.params, set_names(levels, id)), #' @field storage_type (`character()`)\cr Data types of parameters when stored in tables. Named with parameter IDs. - storage_type = function() private$.params[, set_names(class, storage_type)], + storage_type = function() with(private$.params, set_names(storage_type, id)), #' @field special_vals (named `list()` of `list()`)\cr Special values for all parameters. Named with parameter IDs. - special_vals = function() private$.params[, set_names(class, special_vals)], + special_vals = function() with(private$.params, set_names(special_vals, id)), #' @field default (named `list()`)\cr Default values of all parameters. If no default exists, element is not present. #' Named with parameter IDs. - default = function() private$.params[!map_lgl(default, is_nodefault), set_names(default, id)], + default = function() with(private$.params[!map_lgl(default, is_nodefault), .(default, id)], set_names(default, id)), ############################ # Per-Parameter class properties (S3 method call) @@ -652,42 +677,38 @@ ParamSet = R6Class("ParamSet", #' @field nlevels (named `integer()`)\cr Number of distinct levels of parameters. `Inf` for double parameters or unbounded integer parameters. #' Named with param IDs. nlevels = function() { - private$.params[, + tmp = private$.params[, list(id, nlevels = domain_nlevels(recover_domain(.SD, .BY))), by = c("cls", "grouping") - ][ - private$.params$id, on = "id", set_names(nlevels, id) ] + with(tmp[private$.params$id, on = "id"], set_names(nlevels, id)) }, #' @field is_number (named `logical()`)\cr Whether parameter is [ParamDbl] or [ParamInt]. Named with parameter IDs. is_number = function() { - private$.params[, + tmp = private$.params[, list(id, is_number = rep(domain_is_number(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping") - ][ - private$.params$id, on = "id", set_names(is_number, id) ] + with(tmp[private$.params$id, on = "id"], set_names(is_number, id)) }, #' @field is_categ (named `logical()`)\cr Whether parameter is [ParamFct] or [ParamLgl]. Named with parameter IDs. is_categ = function() { - private$.params[, + tmp = private$.params[, list(id, is_categ = rep(domain_is_categ(recover_domain(.SD, .BY)), .N)), by = c("cls", "grouping") - ][ - private$.params$id, on = "id", set_names(is_categ, id) ] + with(tmp[private$.params$id, on = "id"], set_names(is_categ, id)) }, #' @field is_bounded (named `logical()`)\cr Whether parameters have finite bounds. Named with parameter IDs. is_bounded = function() { - private$.params[, + tmp = private$.params[, list(id, is_bounded = domain_is_bounded(recover_domain(.SD, .BY))), by = c("cls", "grouping") - ][ - private$.params$id, on = "id", set_names(is_bounded, id) ] + with(tmp[private$.params$id, on = "id"], set_names(is_bounded, id)) } ), @@ -698,6 +719,8 @@ ParamSet = R6Class("ParamSet", .values = named_list(), .tags = named_list(), .deps = data.table(id = character(0L), on = character(0L), cond = list()), + .trafos = data.table(id = character(0L), trafo = list(), key = "id"), + get_tune_ps = function(values) { selfparams = private$.params partsets = imap(keep(values, inherits, "TuneToken"), function(value, pn) { @@ -752,8 +775,6 @@ ParamSet = R6Class("ParamSet", ) ) -paramcols = c("id", "cls", "grouping", "cargo", "lower", "upper", "tolerance", "levels", "special_vals", "default", "trafo", "has_trafo", "storage_type") - recover_domain = function(sd, by) { domain = as.data.table(c(by, sd)) class(domain) = c(domain$cls, class(domain)) diff --git a/R/asserts.R b/R/asserts.R index 6d9bcb34..5356e35f 100644 --- a/R/asserts.R +++ b/R/asserts.R @@ -27,13 +27,3 @@ assert_param_set = function(param_set, cl = NULL, no_untyped = FALSE, must_bound } invisible(param_set) } - -# assert that we can use the string in list, tables, formulas -assert_id = function(id) { - assert_string(id, pattern = "^[[:alpha:]]+[[:alnum:]_.]*$") -} - -# assert that we can use the string in list, tables, formulas -assert_ids = function(id) { - assert_character(id, pattern = "^[[:alpha:]]+[[:alnum:]_.]*$", any.missing = FALSE) -} diff --git a/R/zzz.R b/R/zzz.R index 4771bc1c..be5b5640 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -6,6 +6,11 @@ #' @importFrom methods is "_PACKAGE" + + +# data.table-variables to announce: +# .init_given + .onLoad = function(libname, pkgname) { # nolint # nocov start backports::import(pkgname) From c5c555b35cf6b6fa9190b8c2640b4830d7f81958 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 28 Sep 2021 21:00:28 +0200 Subject: [PATCH 34/64] "feature complete" --- R/ParamSet.R | 10 +-- R/ParamSetCollection.R | 158 ++++++++++++++++++----------------------- 2 files changed, 74 insertions(+), 94 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index ec8a9353..35be607d 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -73,7 +73,7 @@ ParamSet = R6Class("ParamSet", set(paramtbl, , "id", names(params)) } - private$.tags = with(paramtbl, set_names(tags, id)) + private$.tags = with(paramtbl, set_names(.tags, id)) initvalues = with(paramtbl[(.init_given), .(init, id)], set_names(.init, id)) private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") @@ -178,7 +178,7 @@ ParamSet = R6Class("ParamSet", transformed = pmap(trafos, function(id, trafo, value) trafo(value)) insert_named(x, set_names(transformed, trafos$id)) } - extra_trafo = private$.extra_trafo + extra_trafo = self$extra_trafo if (!is.null(extra_trafo)) { if (test_function(extra_trafo, args = c("x", "param_set"))) { x = extra_trafo(x = x, param_set = param_set) @@ -370,7 +370,7 @@ ParamSet = R6Class("ParamSet", paramrow[, `:=`( .tags = private$.tags[[id]], .trafo = private$.trafos[id, trafo], - .requirements = list(transpose_list(private$.deps[id, .(on, cond), on = "id"])), + .requirements = list(transpose_list(self$deps[id, .(on, cond), on = "id"])), .init_given = id %in% names(values), .init = unname(vals[id])) ] @@ -570,7 +570,7 @@ ParamSet = R6Class("ParamSet", result = copy(private$.params) result[, .tags = private$.tags] result[private$.trafos, .trafo := trafo, on = "id"] - result[private$.deps, .requirements := transpose_list(.(on, cond)), on = "id"] + result[self$deps, .requirements := transpose_list(.(on, cond)), on = "id"] vals = self$values result[, `:=`( .init_given = id %in% names(vals), @@ -640,7 +640,7 @@ ParamSet = R6Class("ParamSet", #' @field is_empty (`logical(1)`)\cr Is the `ParamSet` empty? Named with parameter IDs. is_empty = function() nrow(private$.params) == 0L, #' @field has_trafo (`logical(1)`)\cr Whether a `trafo` function is present, in parameters or in `extra_trafo`. - has_trafo = function() !is.null(private$.extra_trafo) || nrow(private$.trafos), + has_trafo = function() !is.null(self$extra_trafo) || nrow(private$.trafos), #' @field has_deps (`logical(1)`)\cr Whether the parameter dependencies are present has_deps = function() nrow(self$deps) > 0L, #' @field has_constraint (`logical(1)`)\cr Whether parameter constraint is set. diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 3614f7c2..43c2eaa4 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -40,38 +40,51 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Also note that the `$values` will have undefined behavior when `sets` contains a #' single [`ParamSet`] multiple times (by reference). #' Default `FALSE`. - initialize = function(sets, tag_set = FALSE, tag_params = FALSE) { + initialize = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") assert_flag(tag_set) assert_flag(tag_params) - split_sets = split(sets, names(sets) == "") - nameless_sets = split_sets$`TRUE` - named_sets = split_sets$`FALSE` - assert_names(names2(named_sets), type = "strict") - assert_ids(names2(named_sets)) + if (is.null(names(sets))) names(sets) = rep("", length(sets)) - params = rbindlist(map(seq_along(sets), function(i) { + assert_names(names(named_sets)[names(named_sets) != ""], type = "strict") + + paramtbl = rbindlist(map(seq_along(sets), function(i) { s = sets[[i]] - n = names2(sets)[[n]] - params_child = cbind(s$params, orig_id = s$params$id, owner_ps = i) + n = names(sets)[[n]] + params_child = s$params[, `:=`(original_id = id, owner_ps_index = i, owner_name = n)] if (n != "") set(params_child, , "id", sprintf("%s.%s", n, params_child$id)) params_child })) - dups = duplicated(params$id) + dups = duplicated(paramtbl$id) if (any(dups)) { stopf("ParamSetCollection would contain duplicated parameter names: %s", str_collapse(unique(pnames[dups]))) } - private$.tags = combine_tags(sets, tag_sets, tag_params) - private$.extra_trafo = combine_trafos(sets, params) - private$.constraint = combine_constraints(sets, params) - private$.params = params + if (tag_sets) paramtbl[owner_name != "", , .tags := pmap(list(.tags, owner_name), function(x, n) c(x, sprintf("set_%s", n)))] + if (tag_params) paramtbl[, .tags := pmap(list(.tags, original_id), function(x, n) c(x, sprintf("param_%s", n)))] + private$.tags = with(paramtbl, set_names(tags, id)) + + private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") + + private$.translation = paramtbl[, c("id", "original_id", "owner_ps_index", "owner_name"), with = FALSE] + setkeyv(private$.translation, "id") + setindexv(private$.translation, "original_id") + + set(paramtbl, , setdiff(colnames(paramtbl), domain_names_permanent), NULL) + assert_names(colnames(paramtbl), identical.to = domain_names_permanent) + setindexv(paramtbl, c("id", "cls", "grouping")) + private$.params = paramtbl + + private$.children_with_trafos = which(map_lgl(map(sets, "extra_trafo"), is.null)) + private$.children_with_constraints = which(map_lgl(map(sets, "constraint"), is.null)) + private$.sets = sets } ), + active = list( #' @template field_deps deps = function(v) { @@ -101,10 +114,10 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # We do this here because we don't want the loop to be aborted early and have half an update. self$assert(xs) - translate = private$.params[names(xs), list(orig_id, owner_ps), on = "id"] + translate = private$.translation[names(xs), list(orig_id, owner_ps_index), on = "id"] set(translate, , j = "values", xs) - for (xtl in split(translate, by = "owner_ps")) { - sets[[xtl$owner_ps]]$values = set_names(xtl$values, xtl$orig_id) + for (xtl in split(translate, by = "owner_ps_index")) { + sets[[xtl$owner_ps_index]]$values = set_names(xtl$values, xtl$orig_id) } } vals = unlist(map(sets, "values"), recursive = FALSE) @@ -114,19 +127,54 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, extra_trafo = function(f) { if (!missing(f)) stop("extra_trafo is read-only in ParamSetCollection.") - private$.extra_trafo + if (!length(private$.children_with_trafos)) return(NULL) + private$.extra_trafo_explicit + }, constraint = function(f) { if (!missing(f)) stop("constraint is read-only in ParamSetCollection.") - private$.constraint + if (!length(private$.children_with_constraints)) return(NULL) + private$.constraint_explicit + }, + + sets = function(v) { + if (!missing(v)) stop("sets is read-only") + private$.sets } ), private = list( .sets = NULL, - .extra_trafo = NULL, - .constraint = NULL, + .translation = data.table(id = character(0), original_id = character(0), owner_ps_index = integer(0), owner_name = character(0), key = id), + .children_with_trafos = NULL, + .extra_trafo_explicit = function(x) { + changed = unlist(lapply(private$.children_with_trafos, function(set_index) { + changing_ids = private$.translation[J(set_index), id, on = "owner_ps_index"] + trafo = private$.sets[[set_index]]$extra_trafo + changing_values = x[names(x) %in% changing_ids] + names(changing_values) = private$.translation[names(changing_values), original_id] + changing_values = trafo(changing_values) + prefix = names(sets)[[set_index]] + if (prefix != "") { + names(changing_values) = sprintf("%s.%s", prefix, names(changing_values)) + } + changing_values + }), recursive = FALSE) + unchanged_ids = private$.translation[!J(set_index), id, on = "owner_ps_index"] + unchanged = x[names(x) %in% unchanged_ids] + c(unchanged, changed) + }, + .constraint_explicit = function(x) { + for (set_index in private$.children_with_constraints) { + constraining_ids = private$.translation[J(set_index), id, on = "owner_ps_index"] + constraint = private$.sets[[set_index]]$constraint + constraining_values = x[names(x) %in% changing_ids] + names(constraining_values) = private$.translation[names(constraining_values), original_id] + if (!constraint(x)) return(FALSE) + } + TRUE + }, deep_clone = function(name, value) { switch(name, .deps = copy(value), @@ -138,71 +186,3 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, } ) ) - -combine_trafos = function(sets, params) { - child_trafos = map(sets, "extra_trafo") - child_with_trafos = which(!map_lgl(child_trafos, is.null)) - if (!length(child_with_trafos)) return(NULL) - - child_trafos = child_trafos[child_with_trafos] - crate(function(x) { - unlist(imap(child_with_trafos, function(child, trafoi) { - child = child_with_trafos[[trafoi]] - trafo = child_trafos[[trafoi]] - setinfo = params[data.table(owner_ps = child), c("id", "orig_id"), with = FALSE, on = "owner_ps"] - subx = x[match(setinfo$id, names(x), nomatch = 0)] - names(subx) = params[names(subx), orig_id, on = "id"] - - transformed = trafo(subx) - - prefix = names(sets)[[child]] - if (prefix != "") { - names(transformed) = sprintf("%s.%s", prefix, names(transformed)) - } - }), recursive = FALSE) - }, params, sets, child_trafos, child_with_trafos) -} - -combine_constraints = function(sets, params) { - child_constraints = map(sets, "constraint") - child_with_constraints = which(!map_lgl(child_constraints, is.null)) - if (!length(child_with_constraints)) return(NULL) - - child_constraints = child_constraints[child_with_constraints] - crate(function(x) { - for (constrainti in seq_along(child_with_constraints)) { - child = child_with_constraints[[constrainti]] - constraint = child_constraints[[constrainti]] - setinfo = params[data.table(owner_ps = child), c("id", "orig_id"), with = FALSE, on = "owner_ps"] - subx = x[match(setinfo$id, names(x), nomatch = 0)] - names(subx) = params[names(subx), orig_id, on = "id"] - - if (!constraint(x)) return(FALSE) - } - TRUE - }, params, sets, child_constraints, child_with_constraints) -} - -combine_tags = function(sets, tag_sets, tag_params) { - if (tag_set) { - if (tag_params) { - unlist(imap(sets, function(s, n) { - add_tags = sprintf("set_%s", n) - imap(s$tags, function(t, tn) unique(c(t, add_tags, sprintf("param_%s", tn)))) - }), recursive = FALSE) - } else { - unlist(imap(sets, function(s, n) { - add_tags = sprintf("set_%s", n) - imap(s$tags, function(t) unique(c(t, add_tags))) - }), recursive = FALSE) - } - } else { - if (tag_params) { - unlist(map(sets, function(s) { - imap(s$tags, function(t, tn) unique(c(t, sprintf("param_%s", tn)))) - }), recursive = FALSE) - } else { - unlist(map(sets, "tags"), recursive = FALSE) - } - } -} From 56e3ed7f3f8e73f525c60a4d6d703e3929982f42 Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 8 Oct 2021 13:51:43 +0200 Subject: [PATCH 35/64] more efficient tags --- R/Domain.R | 2 +- R/ParamSet.R | 32 ++++++++++++++++++-------------- R/ParamSetCollection.R | 4 ++-- R/zzz.R | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/R/Domain.R b/R/Domain.R index e960531e..f14b70fb 100644 --- a/R/Domain.R +++ b/R/Domain.R @@ -213,7 +213,7 @@ empty_domain = data.table(id = character(0), cls = character(0), grouping = char .init = list()) domain_names = names(empty_domain) -domain_names_permanent = grep("^\\.", domain_names, values = TRUE) +domain_names_permanent = grep("^\\.", domain_names, value = TRUE) #' @export print.Domain = function(x, ...) { diff --git a/R/ParamSet.R b/R/ParamSet.R index df1f5541..6e97d424 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -73,7 +73,8 @@ ParamSet = R6Class("ParamSet", set(paramtbl, , "id", names(params)) } - private$.tags = with(paramtbl, set_names(.tags, id)) + private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] + setindexv(private$.tags, "tags") initvalues = with(paramtbl[(.init_given), .(init, id)], set_names(.init, id)) private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") @@ -110,12 +111,14 @@ ParamSet = R6Class("ParamSet", if (is.null(class) && is.null(tags) && is.null(any_tags)) { return(private$.params$id) } - selftags = private$.tags - ptbl = private$.params - clsmatch = if (is.null(class)) TRUE else ptbl$cls %in% class - alltagmatch = if (is.null(tags)) TRUE else map_lgl(selftags, function(tg) all(tags %in% tg)) - anytagmatch = if (is.null(any_tags)) TRUE else map_lgl(selftags, function(tg) any(any_tags %in% tg)) - ptbl[clsmatch & tagmatch & anytagmatch, id] + ptbl = if (is.null(class)) private$.params else private$.params[cls %in% class, .(id)] + if (is.null(tags) && is.null(any_tags)) { + return(ptbl$id) + } + tagtbl = private$.tags[ptbl] + idpool = if (is.null(any_tags)) list() else list(tagtbl[tag %in% any_tags, id]) + idpool = c(idpool, lapply(tags, function(t) tagtbl[t, id, on = "tags"])) + Reduce(idpool, intersect) }, #' @description @@ -363,7 +366,7 @@ ParamSet = R6Class("ParamSet", vals = self$values paramrow[, `:=`( - .tags = private$.tags[[id]], + .tags = private$.tags[id, tag], .trafo = private$.trafos[id, trafo], .requirements = list(transpose_list(self$deps[id, .(on, cond), on = "id"])), .init_given = id %in% names(values), @@ -514,7 +517,7 @@ ParamSet = R6Class("ParamSet", data = function(v) { if (!missing(v)) stop("data is read-only") private$.params[, list(id, class = cls, lower, upper, levels, nlevels = self$nlevels, - is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags = private$.tags)] + is_bounded = self$is_bounded, special_vals, default, storage_type = self$storage_type, tags = self$tags)] }, #' @template field_values @@ -550,11 +553,12 @@ ParamSet = R6Class("ParamSet", if (!missing(v)) { assert_names(names(v), permutation.of = private$.params$id) assert_list(v, any.missing = FALSE, types = "character") - # store with param ordering, return value with original ordering - private$.tags = v[match(private$.params$id, names(v), nomatch = 0)] + private$.tags = data.table(id = rep(names(v), map_int(v, length)), tag = unlist(v), key = "id") + setindexv(private$.tags, "tag") + # return value with original ordering return(v) } - private$.tags + insert_named(named_list(private$.params$id), with(private$.tags[, list(list(tag)), by = "id"], set_names(tag, id))) }, #' @template field_params @@ -564,7 +568,7 @@ ParamSet = R6Class("ParamSet", } result = copy(private$.params) - result[, .tags = private$.tags] + result[, .tags = self$tags] result[private$.trafos, .trafo := trafo, on = "id"] result[self$deps, .requirements := transpose_list(.(on, cond)), on = "id"] vals = self$values @@ -709,7 +713,7 @@ ParamSet = R6Class("ParamSet", .constraint = NULL, .params = NULL, .values = named_list(), - .tags = named_list(), + .tags = data.table(id = character(0L), tag = character(0), key = "id"), .deps = data.table(id = character(0L), on = character(0L), cond = list()), .trafos = data.table(id = character(0L), trafo = list(), key = "id"), diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 43c2eaa4..a83207b1 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -65,7 +65,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, if (tag_sets) paramtbl[owner_name != "", , .tags := pmap(list(.tags, owner_name), function(x, n) c(x, sprintf("set_%s", n)))] if (tag_params) paramtbl[, .tags := pmap(list(.tags, original_id), function(x, n) c(x, sprintf("param_%s", n)))] - private$.tags = with(paramtbl, set_names(tags, id)) + private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") @@ -146,7 +146,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, private = list( .sets = NULL, - .translation = data.table(id = character(0), original_id = character(0), owner_ps_index = integer(0), owner_name = character(0), key = id), + .translation = data.table(id = character(0), original_id = character(0), owner_ps_index = integer(0), owner_name = character(0), key = "id"), .children_with_trafos = NULL, .extra_trafo_explicit = function(x) { changed = unlist(lapply(private$.children_with_trafos, function(set_index) { diff --git a/R/zzz.R b/R/zzz.R index be5b5640..1ff378f9 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -9,7 +9,7 @@ # data.table-variables to announce: -# .init_given +# .init_given, .trafo .onLoad = function(libname, pkgname) { # nolint # nocov start From 3657f30a58573586ca7814aca244148163691b2d Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 8 Oct 2021 13:51:49 +0200 Subject: [PATCH 36/64] rename package --- DESCRIPTION | 13 +- NAMESPACE | 16 +- man/Condition.Rd | 162 +++++-------------- man/Domain.Rd | 12 +- man/ParamSet.Rd | 315 +++++++++++++++++-------------------- man/ParamSetCollection.Rd | 58 +++---- man/Sampler1D.Rd | 8 +- man/Sampler1DCateg.Rd | 8 +- man/Sampler1DNormal.Rd | 8 +- man/Sampler1DRfun.Rd | 8 +- man/Sampler1DUnif.Rd | 8 +- man/SamplerHierarchical.Rd | 8 +- man/SamplerJointIndep.Rd | 8 +- man/SamplerUnif.Rd | 8 +- man/domain_check.Rd | 4 +- man/paradox-package.Rd | 40 ----- man/ps.Rd | 9 +- man/to_tune.Rd | 2 +- 18 files changed, 273 insertions(+), 422 deletions(-) delete mode 100644 man/paradox-package.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 7445ddbd..581c9885 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Type: Package -Package: paradox +Package: tautology Title: Define and Work with Parameter Spaces for Complex Algorithms Version: 0.7.1.9000 @@ -57,12 +57,13 @@ Config/testthat/edition: 3 Config/testthat/parallel: false NeedsCompilation: no Roxygen: list(markdown = TRUE, r6 = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.1.2 Collate: 'Condition.R' 'Design.R' + 'Domain.R' + 'Domain_methods.R' 'NoDefault.R' - 'Param.R' 'ParamDbl.R' 'ParamFct.R' 'ParamInt.R' @@ -71,19 +72,19 @@ Collate: 'ParamSet.R' 'ParamSetCollection.R' 'ParamUty.R' - 'S6ObjectCache.R' 'Sampler.R' 'Sampler1D.R' 'SamplerHierarchical.R' 'SamplerJointIndep.R' 'SamplerUnif.R' 'asserts.R' - 'helper.R' - 'domain.R' 'generate_design_grid.R' 'generate_design_lhs.R' 'generate_design_random.R' + 'helper.R' 'ps.R' + 'ps_replicate.R' + 'ps_union.R' 'reexports.R' 'to_tune.R' 'zzz.R' diff --git a/NAMESPACE b/NAMESPACE index cf9c2e05..8748a83d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,50 +1,54 @@ # Generated by roxygen2: do not edit by hand S3method(as.data.table,ParamSet) +S3method(condition_test,CondAnyOf) +S3method(condition_test,CondEqual) S3method(domain_check,ParamDbl) S3method(domain_check,ParamFct) S3method(domain_check,ParamInt) S3method(domain_check,ParamLgl) S3method(domain_check,ParamSet6) S3method(domain_check,ParamUty) +S3method(domain_is_bounded,Domain) S3method(domain_is_bounded,ParamDbl) S3method(domain_is_bounded,ParamFct) S3method(domain_is_bounded,ParamInt) S3method(domain_is_bounded,ParamLgl) S3method(domain_is_bounded,ParamSet6) S3method(domain_is_bounded,ParamUty) -S3method(domain_is_bounded,default) +S3method(domain_is_categ,Domain) S3method(domain_is_categ,ParamDbl) S3method(domain_is_categ,ParamFct) S3method(domain_is_categ,ParamInt) S3method(domain_is_categ,ParamLgl) S3method(domain_is_categ,ParamSet6) S3method(domain_is_categ,ParamUty) -S3method(domain_is_categ,default) +S3method(domain_is_number,Domain) S3method(domain_is_number,ParamDbl) S3method(domain_is_number,ParamFct) S3method(domain_is_number,ParamInt) S3method(domain_is_number,ParamLgl) S3method(domain_is_number,ParamSet6) S3method(domain_is_number,ParamUty) -S3method(domain_is_number,default) +S3method(domain_nlevels,Domain) S3method(domain_nlevels,ParamDbl) S3method(domain_nlevels,ParamFct) S3method(domain_nlevels,ParamInt) S3method(domain_nlevels,ParamLgl) S3method(domain_nlevels,ParamSet6) S3method(domain_nlevels,ParamUty) -S3method(domain_nlevels,default) +S3method(domain_qunif,Domain) S3method(domain_qunif,ParamDbl) S3method(domain_qunif,ParamFct) S3method(domain_qunif,ParamInt) S3method(domain_qunif,ParamLgl) S3method(domain_qunif,ParamSet6) S3method(domain_qunif,ParamUty) -S3method(domain_qunif,default) +S3method(domain_sanitize,Domain) S3method(domain_sanitize,ParamDbl) S3method(domain_sanitize,ParamInt) -S3method(domain_sanitize,default) +S3method(format,Condition) +S3method(print,Condition) S3method(print,Domain) S3method(print,FullTuneToken) S3method(print,ObjectTuneToken) diff --git a/man/Condition.Rd b/man/Condition.Rd index e6a456e5..bc770547 100644 --- a/man/Condition.Rd +++ b/man/Condition.Rd @@ -1,146 +1,58 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Condition.R -\name{Condition} +\name{condition_test} +\alias{condition_test} +\alias{condition_as_string} \alias{Condition} \alias{CondEqual} \alias{CondAnyOf} \title{Dependency Condition} -\description{ -Condition object, to specify the condition in a dependency. -} -\section{Currently implemented simple conditions}{ - -\itemize{ -\item \code{CondEqual$new(rhs)} \cr -Parent must be equal to \code{rhs}. -\item \code{CondAnyOf$new(rhs)} \cr -Parent must be any value of \code{rhs}. -} -} +\usage{ +condition_test(cond, x) -\section{Public fields}{ -\if{html}{\out{
}} -\describe{ -\item{\code{type}}{(\code{character(1)})\cr -Name / type of the condition.} +condition_as_string(cond, lhs_chr = "x") -\item{\code{rhs}}{(\code{any})\cr -Right-hand-side of the condition.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-new}{\code{Condition$new()}} -\item \href{#method-test}{\code{Condition$test()}} -\item \href{#method-as_string}{\code{Condition$as_string()}} -\item \href{#method-format}{\code{Condition$format()}} -\item \href{#method-print}{\code{Condition$print()}} -\item \href{#method-clone}{\code{Condition$clone()}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} -\subsection{Method \code{new()}}{ -Creates a new instance of this \link[R6:R6Class]{R6} class. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Condition$new(type, rhs)}\if{html}{\out{
}} +Condition(rhs, condition_format_string) } +\arguments{ +\item{cond}{(\code{Condition})\cr +\code{Condition} to use} + +\item{x}{(\code{any})\cr +Value to test} -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{type}}{(\code{character(1)})\cr -Name / type of the condition.} +\item{lhs_chr}{(\code{character(1)})\cr +Symbolic representation to use for \verb{} in the returned string.} -\item{\code{rhs}}{(\code{any})\cr +\item{rhs}{(\code{any})\cr Right-hand-side of the condition.} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test}{}}} -\subsection{Method \code{test()}}{ -Checks if condition is satisfied. -Called on a vector of parent param values. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Condition$test(x)}\if{html}{\out{
}} -} -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{x}}{(\code{vector()}).} -} -\if{html}{\out{
}} +\item{condition_format_string}{(\code{character(1)})\cr +Format-string for representing the condition when pretty-printing +in \code{condition_as_string()}. +Should contain two \verb{\%s}, as it is used in an \code{sprintf()}-call with +two further string values.} } -\subsection{Returns}{ -\code{logical(1)}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-as_string}{}}} -\subsection{Method \code{as_string()}}{ -Conversion helper for print outputs. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Condition$as_string(lhs_chr = "x")}\if{html}{\out{
}} +\description{ +Condition object, to specify the condition in a dependency. } +\section{Functions}{ +\itemize{ +\item \code{condition_test}: Used internally. Tests whether a value satisfies a given condition. +Vectorizes when \code{x} is atomic. -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{lhs_chr}}{(\code{character(1)})} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-format}{}}} -\subsection{Method \code{format()}}{ -Helper for print outputs. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Condition$format()}\if{html}{\out{
}} -} +\item \code{condition_as_string}: Used internally. Returns a string that represents the condition for pretty +printing, in the form \code{" "}, e.g. \code{"x == 3"} or +\code{"param \%in\% {1, 2, 10}"}. +}} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} -\subsection{Method \code{print()}}{ -Printer. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Condition$print(...)}\if{html}{\out{
}} -} +\section{Currently implemented simple conditions}{ -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{...}}{(ignored).} -} -\if{html}{\out{
}} -} +\itemize{ +\item \code{CondEqual(rhs)} \cr +Value must be equal to \code{rhs}. +\item \code{CondAnyOf(rhs)} \cr +Value must be any value of \code{rhs}. } -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Condition$clone(deep = FALSE)}\if{html}{\out{
}} } -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/man/Domain.Rd b/man/Domain.Rd index c15eefb4..266dd35c 100644 --- a/man/Domain.Rd +++ b/man/Domain.Rd @@ -1,14 +1,14 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ParamDbl.R, R/ParamFct.R, R/ParamInt.R, -% R/ParamLgl.R, R/ParamS6.R, R/ParamUty.R, R/domain.R -\name{p_dbl} +% Please edit documentation in R/Domain.R, R/ParamDbl.R, R/ParamFct.R, +% R/ParamInt.R, R/ParamLgl.R, R/ParamS6.R, R/ParamUty.R +\name{Domain} +\alias{Domain} \alias{p_dbl} \alias{p_fct} \alias{p_int} \alias{p_lgl} \alias{p_s6} \alias{p_uty} -\alias{Domain} \title{Domain: Parameter Range without an Id} \usage{ p_dbl( @@ -113,8 +113,8 @@ Initializes the \verb{$tolerance} field that determines the} An expression indicating a requirement for the parameter that will be constructed from this. Can be given as an expression (using \code{quote()}), or the expression can be entered directly and will be parsed using NSE (see examples). The expression may be of the form \verb{ == } or \verb{ \%in\% }, which will result in -dependencies according to \verb{ParamSet$add_dep(on = "", cond = CondEqual$new())} or -\verb{ParamSet$add_dep(on = "", cond = CondAnyOf$new())}, respectively (see \code{\link{CondEqual}}, +dependencies according to \verb{ParamSet$add_dep(on = "", cond = CondEqual())} or +\verb{ParamSet$add_dep(on = "", cond = CondAnyOf())}, respectively (see \code{\link{CondEqual}}, \code{\link{CondAnyOf}}). The expression may also contain multiple conditions separated by \code{&&}.} \item{trafo}{(\code{function})\cr diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 366e933e..6b5abea5 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -59,9 +59,32 @@ Default is \code{TRUE}, only switch this off if you know what you are doing.} \section{Active bindings}{ \if{html}{\out{
}} \describe{ +\item{\code{data}}{(\code{data.table}) \code{data.table} representation of the \code{ParamSet}.} + +\item{\code{values}}{(named \code{list()})\cr +Currently set / fixed parameter values. +Settable, and feasibility of values will be checked when you set them. +You do not have to set values for all parameters, but only for a subset. +When you set values, all previously set values will be unset / removed.} + +\item{\code{tags}}{(named \code{list()} of \code{character()})\cr +Can be used to group and subset parameters. +Named with parameter IDs.} + \item{\code{params}}{(named \code{list()})\cr List of \link{Param}, named with their respective ID.} +\item{\code{extra_trafo}}{(\verb{function(x, param_set)})\cr +Transformation function. Settable. +User has to pass a \verb{function(x)}, of the form\cr +(named \code{list()}, \link{ParamSet}) -> named \code{list()}.\cr +The function is responsible to transform a feasible configuration into another encoding, +before potentially evaluating the configuration with the target algorithm. +For the output, not many things have to hold. +It needs to have unique names, and the target algorithm has to accept the configuration. +For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). +Is NULL by default, and you can set it to NULL to switch the transformation off.} + \item{\code{deps}}{(\code{\link[data.table:data.table]{data.table::data.table()}})\cr Table has cols \code{id} (\code{character(1)}) and \code{on} (\code{character(1)}) and \code{cond} (\link{Condition}). Lists all (direct) dependency parents of a param, through parameter IDs. @@ -70,86 +93,42 @@ Settable, if you want to remove dependencies or perform other changes.} \item{\code{length}}{(\code{integer(1)})\cr Number of contained \link{Param}s.} -\item{\code{is_empty}}{(\code{logical(1)})\cr -Is the \code{ParamSet} empty?} +\item{\code{is_empty}}{(\code{logical(1)})\cr Is the \code{ParamSet} empty? Named with parameter IDs.} -\item{\code{class}}{(named \code{character()})\cr -Classes of contained parameters, named with parameter IDs.} +\item{\code{has_trafo}}{(\code{logical(1)})\cr Whether a \code{trafo} function is present, in parameters or in \code{extra_trafo}.} -\item{\code{lower}}{(named \code{double()})\cr -Lower bounds of parameters (\code{NA} if parameter is not numeric). -Named with parameter IDs.} +\item{\code{has_deps}}{(\code{logical(1)})\cr Whether the parameter dependencies are present} -\item{\code{upper}}{(named \code{double()})\cr -Upper bounds of parameters (\code{NA} if parameter is not numeric). -Named with parameter IDs.} +\item{\code{has_constraint}}{(\code{logical(1)})\cr Whether parameter constraint is set.} -\item{\code{levels}}{(named \code{list()})\cr -List of character vectors of allowed categorical values of contained parameters. -\code{NULL} if the parameter is not categorical. -Named with parameter IDs.} +\item{\code{all_numeric}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \link{ParamDbl} or \link{ParamInt}.} -\item{\code{nlevels}}{(named \code{integer()})\cr -Number of categorical levels per parameter, \code{Inf} for double parameters or unbounded integer parameters. -Named with param IDs.} +\item{\code{all_categorical}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \link{ParamFct} and \link{ParamLgl}.} -\item{\code{is_bounded}}{(named \code{logical()})\cr -Do all parameters have finite bounds? -Named with parameter IDs.} +\item{\code{class}}{(named \code{character()})\cr Classes of contained parameters. Named with parameter IDs.} -\item{\code{special_vals}}{(named \code{list()} of \code{list()})\cr -Special values for all parameters. -Named with parameter IDs.} +\item{\code{lower}}{(named \code{double()})\cr Lower bounds of numeric parameters (\code{NA} for non-numerics). Named with parameter IDs.} -\item{\code{default}}{(named \code{list()})\cr -Default values of all parameters. -If no default exists, element is not present. -Named with parameter IDs.} +\item{\code{upper}}{(named \code{double()})\cr Upper bounds of numeric parameters (\code{NA} for non-numerics). Named with parameter IDs.} -\item{\code{tags}}{(named \code{list()} of \code{character()})\cr -Can be used to group and subset parameters. +\item{\code{levels}}{(named \code{list()} of \code{character})\cr Allowed levels of categorical parameters (\code{NULL} for non-categoricals). Named with parameter IDs.} -\item{\code{storage_type}}{(\code{character()})\cr -Data types of parameters when stored in tables. -Named with parameter IDs.} +\item{\code{storage_type}}{(\code{character()})\cr Data types of parameters when stored in tables. Named with parameter IDs.} -\item{\code{is_number}}{(named \code{logical()})\cr -Position is TRUE for \link{ParamDbl} and \link{ParamInt}. -Named with parameter IDs.} +\item{\code{special_vals}}{(named \code{list()} of \code{list()})\cr Special values for all parameters. Named with parameter IDs.} -\item{\code{is_categ}}{(named \code{logical()})\cr -Position is TRUE for \link{ParamFct} and \link{ParamLgl}. +\item{\code{default}}{(named \code{list()})\cr Default values of all parameters. If no default exists, element is not present. Named with parameter IDs.} -\item{\code{all_numeric}}{(\code{logical(1)})\cr -Is \code{TRUE} if all parameters are \link{ParamDbl} or \link{ParamInt}.} - -\item{\code{all_categorical}}{(\code{logical(1)})\cr -Is \code{TRUE} if all parameters are \link{ParamFct} and \link{ParamLgl}.} - -\item{\code{extra_trafo}}{(\verb{function(x, param_set)})\cr -Transformation function. Settable. -User has to pass a \verb{function(x)}, of the form\cr -(named \code{list()}, \link{ParamSet}) -> named \code{list()}.\cr -The function is responsible to transform a feasible configuration into another encoding, -before potentially evaluating the configuration with the target algorithm. -For the output, not many things have to hold. -It needs to have unique names, and the target algorithm has to accept the configuration. -For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). -Is NULL by default, and you can set it to NULL to switch the transformation off.} +\item{\code{nlevels}}{(named \code{integer()})\cr Number of distinct levels of parameters. \code{Inf} for double parameters or unbounded integer parameters. +Named with param IDs.} -\item{\code{has_trafo}}{(\code{logical(1)})\cr -Has the set a \code{trafo} function?} +\item{\code{is_number}}{(named \code{logical()})\cr Whether parameter is \link{ParamDbl} or \link{ParamInt}. Named with parameter IDs.} -\item{\code{values}}{(named \code{list()})\cr -Currently set / fixed parameter values. -Settable, and feasibility of values will be checked when you set them. -You do not have to set values for all parameters, but only for a subset. -When you set values, all previously set values will be unset / removed.} +\item{\code{is_categ}}{(named \code{logical()})\cr Whether parameter is \link{ParamFct} or \link{ParamLgl}. Named with parameter IDs.} -\item{\code{has_deps}}{(\code{logical(1)})\cr -Has the set parameter dependencies?} +\item{\code{is_bounded}}{(named \code{logical()})\cr Whether parameters have finite bounds. Named with parameter IDs.} } \if{html}{\out{
}} } @@ -157,17 +136,11 @@ Has the set parameter dependencies?} \subsection{Public methods}{ \itemize{ \item \href{#method-new}{\code{ParamSet$new()}} +\item \href{#method-ids}{\code{ParamSet$ids()}} +\item \href{#method-get_values}{\code{ParamSet$get_values()}} \item \href{#method-trafo}{\code{ParamSet$trafo()}} \item \href{#method-test_constraint}{\code{ParamSet$test_constraint()}} \item \href{#method-test_constraint_dt}{\code{ParamSet$test_constraint_dt()}} -\item \href{#method-qunif}{\code{ParamSet$qunif()}} -\item \href{#method-get_param}{\code{ParamSet$get_param()}} -\item \href{#method-ids}{\code{ParamSet$ids()}} -\item \href{#method-get_values}{\code{ParamSet$get_values()}} -\item \href{#method-subset}{\code{ParamSet$subset()}} -\item \href{#method-subspaces}{\code{ParamSet$subspaces()}} -\item \href{#method-flatten}{\code{ParamSet$flatten()}} -\item \href{#method-search_space}{\code{ParamSet$search_space()}} \item \href{#method-check}{\code{ParamSet$check()}} \item \href{#method-check_dependencies}{\code{ParamSet$check_dependencies()}} \item \href{#method-test}{\code{ParamSet$test()}} @@ -175,6 +148,12 @@ Has the set parameter dependencies?} \item \href{#method-check_dt}{\code{ParamSet$check_dt()}} \item \href{#method-test_dt}{\code{ParamSet$test_dt()}} \item \href{#method-assert_dt}{\code{ParamSet$assert_dt()}} +\item \href{#method-qunif}{\code{ParamSet$qunif()}} +\item \href{#method-get_param}{\code{ParamSet$get_param()}} +\item \href{#method-subset}{\code{ParamSet$subset()}} +\item \href{#method-subspaces}{\code{ParamSet$subspaces()}} +\item \href{#method-flatten}{\code{ParamSet$flatten()}} +\item \href{#method-search_space}{\code{ParamSet$search_space()}} \item \href{#method-add_dep}{\code{ParamSet$add_dep()}} \item \href{#method-format}{\code{ParamSet$format()}} \item \href{#method-print}{\code{ParamSet$print()}} @@ -187,11 +166,7 @@ Has the set parameter dependencies?} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$new( - params = named_list(), - extra_trafo = NULL, - allow_dangling_dependencies = FALSE -)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$new(params = named_list(), allow_dangling_dependencies = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -215,51 +190,6 @@ Default \code{FALSE}.} } \if{html}{\out{
}} } -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-trafo}{}}} -\subsection{Method \code{trafo()}}{ -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$trafo(x, param_set)}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test_constraint}{}}} -\subsection{Method \code{test_constraint()}}{ -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$test_constraint(x, assert_value = TRUE)}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test_constraint_dt}{}}} -\subsection{Method \code{test_constraint_dt()}}{ -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$test_constraint_dt(x, assert_value = TRUE)}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-qunif}{}}} -\subsection{Method \code{qunif()}}{ -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$qunif(x)}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_param}{}}} -\subsection{Method \code{get_param()}}{ -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$get_param(id)}\if{html}{\out{
}} -} - } \if{html}{\out{
}} \if{html}{\out{}} @@ -325,61 +255,31 @@ Named \code{list()}. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-subset}{}}} -\subsection{Method \code{subset()}}{ -Create a new \code{ParamSet} restricted to the passed IDs. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$subset(ids, allow_dangling_dependencies = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{ids}}{(\code{character()}).} -} -\if{html}{\out{
}} -} -\subsection{Returns}{ -\code{ParamSet}. -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-subspaces}{}}} -\subsection{Method \code{subspaces()}}{ +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-trafo}{}}} +\subsection{Method \code{trafo()}}{ \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$subspaces(ids = private$.params$id)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$trafo(x, param_set = self)}\if{html}{\out{
}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-flatten}{}}} -\subsection{Method \code{flatten()}}{ -Create a \code{ParamSet} from this object, even if this object itself is not -a \code{ParamSet}. +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-test_constraint}{}}} +\subsection{Method \code{test_constraint()}}{ \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$flatten()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$test_constraint(x, assert_value = TRUE)}\if{html}{\out{
}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-search_space}{}}} -\subsection{Method \code{search_space()}}{ -Construct a \code{\link{ParamSet}} to tune over. Constructed from \code{\link{TuneToken}} in \verb{$values}, see \code{\link[=to_tune]{to_tune()}}. +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-test_constraint_dt}{}}} +\subsection{Method \code{test_constraint_dt()}}{ \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$search_space(values = self$values)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$test_constraint_dt(x, assert_value = TRUE)}\if{html}{\out{
}} } -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{values}}{(\verb{named list}): optional named list of \code{\link{TuneToken}} objects to convert, in place of \verb{$values}.} -} -\if{html}{\out{
}} -} } \if{html}{\out{
}} \if{html}{\out{}} @@ -390,7 +290,7 @@ A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$check(xs, check_strict = FALSE, describe_error = TRUE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$check(xs, check_strict = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -409,7 +309,7 @@ If successful \code{TRUE}, if not a string with the error message. \if{latex}{\out{\hypertarget{method-check_dependencies}{}}} \subsection{Method \code{check_dependencies()}}{ \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$check_dependencies(xs, describe_error = TRUE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$check_dependencies(xs)}\if{html}{\out{
}} } } @@ -473,7 +373,7 @@ if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should be set to \code{NA} in \code{xdt}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$check_dt(xdt, check_strict = FALSE, describe_error = TRUE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$check_dt(xdt, check_strict = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -493,7 +393,7 @@ If successful \code{TRUE}, if not a string with the error message. \subsection{Method \code{test_dt()}}{ \pkg{checkmate}-like test-function (s. \verb{$check_dt()}). \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$test_dt(xdt, check_strict = FALSE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$test_dt(xdt)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -532,12 +432,91 @@ If successful \code{xs} invisibly, if not an error message. } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-qunif}{}}} +\subsection{Method \code{qunif()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$qunif(x)}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_param}{}}} +\subsection{Method \code{get_param()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$get_param(id)}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-subset}{}}} +\subsection{Method \code{subset()}}{ +Create a new \code{ParamSet} restricted to the passed IDs. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$subset( + ids, + allow_dangling_dependencies = FALSE, + keep_constraint = TRUE +)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{ids}}{(\code{character()}).} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +\code{ParamSet}. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-subspaces}{}}} +\subsection{Method \code{subspaces()}}{ +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$subspaces(ids = private$.params$id)}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-flatten}{}}} +\subsection{Method \code{flatten()}}{ +Create a \code{ParamSet} from this object, even if this object itself is not +a \code{ParamSet}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$flatten()}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-search_space}{}}} +\subsection{Method \code{search_space()}}{ +Construct a \code{\link{ParamSet}} to tune over. Constructed from \code{\link{TuneToken}} in \verb{$values}, see \code{\link[=to_tune]{to_tune()}}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ParamSet$search_space(values = self$values)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{values}}{(\verb{named list}): optional named list of \code{\link{TuneToken}} objects to convert, in place of \verb{$values}.} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-add_dep}{}}} \subsection{Method \code{add_dep()}}{ Adds a dependency to this set, so that param \code{id} now depends on param \code{on}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$add_dep(id, on, cond)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$add_dep(id, on, cond, allow_dangling_dependencies = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -548,6 +527,8 @@ Adds a dependency to this set, so that param \code{id} now depends on param \cod \item{\code{on}}{(\code{character(1)}).} \item{\code{cond}}{(\link{Condition}).} + +\item{\code{allow_dangling_dependencies}}{(\code{logical(1)}): Whether to allow dependencies on parameters that are not present.} } \if{html}{\out{
}} } diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 458c70e0..63837fd2 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -27,29 +27,17 @@ If you call \code{deps} on the collection, you are returned a complete table of } } \section{Super class}{ -\code{\link[paradox:ParamSet]{paradox::ParamSet}} -> \code{ParamSetCollection} +\code{\link[tautology:ParamSet]{tautology::ParamSet}} -> \code{ParamSetCollection} } \section{Active bindings}{ \if{html}{\out{
}} \describe{ -\item{\code{params_unid}}{(named \code{list()})\cr -List of \link{Param}, named with their true ID. However, -this field has the \link{Param}'s \verb{$id} value set to a -potentially invalid value, and it is furthermore not -cloned when the \code{ParamSet} is cloned. This active -binding should be used internally, or to avoid unnecessary -cloning when that becomes a bottleneck.} - \item{\code{deps}}{(\code{\link[data.table:data.table]{data.table::data.table()}})\cr Table has cols \code{id} (\code{character(1)}) and \code{on} (\code{character(1)}) and \code{cond} (\link{Condition}). Lists all (direct) dependency parents of a param, through parameter IDs. Internally created by a call to \code{add_dep}. Settable, if you want to remove dependencies or perform other changes.} -\item{\code{tags}}{(named \code{list()} of \code{character()})\cr -Can be used to group and subset parameters. -Named with parameter IDs.} - \item{\code{values}}{(named \code{list()})\cr Currently set / fixed parameter values. Settable, and feasibility of values will be checked when you set them. @@ -68,27 +56,27 @@ When you set values, all previously set values will be unset / removed.} \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/ParamSet.html#method-add_dep}{\code{paradox::ParamSet$add_dep()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-assert}{\code{paradox::ParamSet$assert()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-assert_dt}{\code{paradox::ParamSet$assert_dt()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-check}{\code{paradox::ParamSet$check()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-check_dependencies}{\code{paradox::ParamSet$check_dependencies()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-check_dt}{\code{paradox::ParamSet$check_dt()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-flatten}{\code{paradox::ParamSet$flatten()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-format}{\code{paradox::ParamSet$format()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-get_param}{\code{paradox::ParamSet$get_param()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-get_values}{\code{paradox::ParamSet$get_values()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-ids}{\code{paradox::ParamSet$ids()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-print}{\code{paradox::ParamSet$print()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-qunif}{\code{paradox::ParamSet$qunif()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-search_space}{\code{paradox::ParamSet$search_space()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-subset}{\code{paradox::ParamSet$subset()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-subspaces}{\code{paradox::ParamSet$subspaces()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-test}{\code{paradox::ParamSet$test()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-test_constraint}{\code{paradox::ParamSet$test_constraint()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-test_constraint_dt}{\code{paradox::ParamSet$test_constraint_dt()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-test_dt}{\code{paradox::ParamSet$test_dt()}}\out{} -\item \out{}\href{../../paradox/html/ParamSet.html#method-trafo}{\code{paradox::ParamSet$trafo()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-add_dep}{\code{tautology::ParamSet$add_dep()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-assert}{\code{tautology::ParamSet$assert()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-assert_dt}{\code{tautology::ParamSet$assert_dt()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-check}{\code{tautology::ParamSet$check()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-check_dependencies}{\code{tautology::ParamSet$check_dependencies()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-check_dt}{\code{tautology::ParamSet$check_dt()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-flatten}{\code{tautology::ParamSet$flatten()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-format}{\code{tautology::ParamSet$format()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-get_param}{\code{tautology::ParamSet$get_param()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-get_values}{\code{tautology::ParamSet$get_values()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-ids}{\code{tautology::ParamSet$ids()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-print}{\code{tautology::ParamSet$print()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-qunif}{\code{tautology::ParamSet$qunif()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-search_space}{\code{tautology::ParamSet$search_space()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-subset}{\code{tautology::ParamSet$subset()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-subspaces}{\code{tautology::ParamSet$subspaces()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-test}{\code{tautology::ParamSet$test()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-test_constraint}{\code{tautology::ParamSet$test_constraint()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-test_constraint_dt}{\code{tautology::ParamSet$test_constraint_dt()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-test_dt}{\code{tautology::ParamSet$test_dt()}}\out{} +\item \out{}\href{../../tautology/html/ParamSet.html#method-trafo}{\code{tautology::ParamSet$trafo()}}\out{} } \out{
} } @@ -98,7 +86,7 @@ When you set values, all previously set values will be unset / removed.} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSetCollection$new(sets, set_id = "", ignore_ids = FALSE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSetCollection$new(sets, tag_sets = FALSE, tag_params = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ diff --git a/man/Sampler1D.Rd b/man/Sampler1D.Rd index a96a10f3..63c897d1 100644 --- a/man/Sampler1D.Rd +++ b/man/Sampler1D.Rd @@ -20,7 +20,7 @@ Other Sampler: } \concept{Sampler} \section{Super class}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{Sampler1D} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{Sampler1D} } \section{Active bindings}{ \if{html}{\out{
}} @@ -40,9 +40,9 @@ Returns the one Parameter that is sampled from.} \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/Sampler1DCateg.Rd b/man/Sampler1DCateg.Rd index b2fa7d56..a398299c 100644 --- a/man/Sampler1DCateg.Rd +++ b/man/Sampler1DCateg.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{Sampler1DCateg} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{Sampler1DCateg} } \section{Public fields}{ \if{html}{\out{
}} @@ -39,9 +39,9 @@ Numeric vector of \code{param$nlevels} probabilities.} \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/Sampler1DNormal.Rd b/man/Sampler1DNormal.Rd index 6e5bc89b..82443aa0 100644 --- a/man/Sampler1DNormal.Rd +++ b/man/Sampler1DNormal.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{\link[paradox:Sampler1DRfun]{paradox::Sampler1DRfun}} -> \code{Sampler1DNormal} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{\link[tautology:Sampler1DRfun]{tautology::Sampler1DRfun}} -> \code{Sampler1DNormal} } \section{Active bindings}{ \if{html}{\out{
}} @@ -42,9 +42,9 @@ SD parameter of the normal distribution.} \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/Sampler1DRfun.Rd b/man/Sampler1DRfun.Rd index bc1ae8cb..8d5696ce 100644 --- a/man/Sampler1DRfun.Rd +++ b/man/Sampler1DRfun.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{Sampler1DRfun} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{Sampler1DRfun} } \section{Public fields}{ \if{html}{\out{
}} @@ -42,9 +42,9 @@ Random number generator function.} \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/Sampler1DUnif.Rd b/man/Sampler1DUnif.Rd index a3c6fb02..3ec0f659 100644 --- a/man/Sampler1DUnif.Rd +++ b/man/Sampler1DUnif.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{Sampler1DUnif} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{Sampler1DUnif} } \section{Methods}{ \subsection{Public methods}{ @@ -31,9 +31,9 @@ Other Sampler: \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/SamplerHierarchical.Rd b/man/SamplerHierarchical.Rd index 429feb5a..45a90a8b 100644 --- a/man/SamplerHierarchical.Rd +++ b/man/SamplerHierarchical.Rd @@ -21,7 +21,7 @@ Other Sampler: } \concept{Sampler} \section{Super class}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{SamplerHierarchical} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{SamplerHierarchical} } \section{Public fields}{ \if{html}{\out{
}} @@ -41,9 +41,9 @@ List of \link{Sampler1D} objects that gives a Sampler for each \link{Param} in t \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/SamplerJointIndep.Rd b/man/SamplerJointIndep.Rd index db5bc8b1..2b6219a4 100644 --- a/man/SamplerJointIndep.Rd +++ b/man/SamplerJointIndep.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super class}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{SamplerJointIndep} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{SamplerJointIndep} } \section{Public fields}{ \if{html}{\out{
}} @@ -39,9 +39,9 @@ List of \link{Sampler} objects.} \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/SamplerUnif.Rd b/man/SamplerUnif.Rd index c6309a7a..be5a2d47 100644 --- a/man/SamplerUnif.Rd +++ b/man/SamplerUnif.Rd @@ -21,7 +21,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:SamplerHierarchical]{paradox::SamplerHierarchical}} -> \code{SamplerUnif} +\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:SamplerHierarchical]{tautology::SamplerHierarchical}} -> \code{SamplerUnif} } \section{Methods}{ \subsection{Public methods}{ @@ -33,9 +33,9 @@ Other Sampler: \if{html}{ \out{
Inherited methods} \itemize{ -\item \out{}\href{../../paradox/html/Sampler.html#method-format}{\code{paradox::Sampler$format()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-print}{\code{paradox::Sampler$print()}}\out{} -\item \out{}\href{../../paradox/html/Sampler.html#method-sample}{\code{paradox::Sampler$sample()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} +\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} } \out{
} } diff --git a/man/domain_check.Rd b/man/domain_check.Rd index 0468a361..1158d456 100644 --- a/man/domain_check.Rd +++ b/man/domain_check.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/Param.R +% Please edit documentation in R/Domain_methods.R \name{domain_check} \alias{domain_check} \title{Check value validity} \usage{ -domain_check(param, values, describe_error = TRUE) +domain_check(param, values) } \arguments{ \item{x}{(\code{any}).} diff --git a/man/paradox-package.Rd b/man/paradox-package.Rd deleted file mode 100644 index 3cbae10c..00000000 --- a/man/paradox-package.Rd +++ /dev/null @@ -1,40 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/zzz.R -\docType{package} -\name{paradox-package} -\alias{paradox} -\alias{paradox-package} -\title{paradox: Define and Work with Parameter Spaces for Complex - Algorithms} -\description{ -Define parameter spaces, constraints and - dependencies for arbitrary algorithms, to program on such spaces. Also - includes statistical designs and random samplers. Objects are - implemented as 'R6' classes. -} -\seealso{ -Useful links: -\itemize{ - \item \url{https://paradox.mlr-org.com} - \item \url{https://github.com/mlr-org/paradox} - \item Report bugs at \url{https://github.com/mlr-org/paradox/issues} -} - -} -\author{ -\strong{Maintainer}: Michel Lang \email{michellang@gmail.com} (\href{https://orcid.org/0000-0001-9754-0393}{ORCID}) - -Authors: -\itemize{ - \item Bernd Bischl \email{bernd_bischl@gmx.net} (\href{https://orcid.org/0000-0001-6002-6980}{ORCID}) - \item Jakob Richter \email{jakob1richter@gmail.com} (\href{https://orcid.org/0000-0003-4481-5554}{ORCID}) - \item Xudong Sun \email{smilesun.east@gmail.com} (\href{https://orcid.org/0000-0003-3269-2307}{ORCID}) - \item Martin Binder \email{mlr.developer@mb706.com} -} - -Other contributors: -\itemize{ - \item Marc Becker \email{marcbecker@posteo.de} (\href{https://orcid.org/0000-0002-8115-0400}{ORCID}) [contributor] -} - -} diff --git a/man/ps.Rd b/man/ps.Rd index cf8f2c17..3c6a3635 100644 --- a/man/ps.Rd +++ b/man/ps.Rd @@ -4,7 +4,12 @@ \alias{ps} \title{Construct a ParamSet using Short Forms} \usage{ -ps(..., .extra_trafo = NULL, .allow_dangling_dependencies = FALSE) +ps( + ..., + .extra_trafo = NULL, + .constraint = NULL, + .allow_dangling_dependencies = FALSE +) } \arguments{ \item{...}{(\code{\link{Domain}} | \code{\link{Param}})\cr @@ -69,7 +74,7 @@ pars$search_space() } \seealso{ Other ParamSet construction helpers: -\code{\link{p_dbl}()}, +\code{\link{Domain}()}, \code{\link{to_tune}()} } \concept{ParamSet construction helpers} diff --git a/man/to_tune.Rd b/man/to_tune.Rd index a9a02be6..8a2af329 100644 --- a/man/to_tune.Rd +++ b/man/to_tune.Rd @@ -144,7 +144,7 @@ print(grid$transpose()) } \seealso{ Other ParamSet construction helpers: -\code{\link{p_dbl}()}, +\code{\link{Domain}()}, \code{\link{ps}()} } \concept{ParamSet construction helpers} From 5d2f3c487bdb1002bbe34859aad741caf4fda217 Mon Sep 17 00:00:00 2001 From: mb706 Date: Tue, 22 Aug 2023 14:52:19 +0200 Subject: [PATCH 37/64] some bugfixes --- R/Domain.R | 4 ++-- R/ParamSet.R | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/Domain.R b/R/Domain.R index f14b70fb..e8114ec0 100644 --- a/R/Domain.R +++ b/R/Domain.R @@ -136,7 +136,7 @@ Domain = function(cls, grouping, assert_number(lower, na.ok = TRUE) assert_number(upper, na.ok = TRUE) assert_number(tolerance, na.ok = TRUE) - assert_character(levels, null.ok = TRUE) + if (!is.logical(levels)) assert_character(levels, null.ok = TRUE) assert_list(special_vals) if (length(special_vals) && !is.null(trafo)) stop("trafo and special_values can not both be given at the same time.") assert_character(tags, any.missing = FALSE, unique = TRUE) @@ -213,7 +213,7 @@ empty_domain = data.table(id = character(0), cls = character(0), grouping = char .init = list()) domain_names = names(empty_domain) -domain_names_permanent = grep("^\\.", domain_names, value = TRUE) +domain_names_permanent = grep("^\\.", domain_names, value = TRUE, invert = TRUE) #' @export print.Domain = function(x, ...) { diff --git a/R/ParamSet.R b/R/ParamSet.R index 6e97d424..15c5ed23 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -74,13 +74,13 @@ ParamSet = R6Class("ParamSet", } private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] - setindexv(private$.tags, "tags") - initvalues = with(paramtbl[(.init_given), .(init, id)], set_names(.init, id)) + setindexv(private$.tags, "tag") + initvalues = with(paramtbl[(.init_given), .(.init, id)], set_names(.init, id)) private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") requirements = paramtbl$.requirements for (row in seq_len(nrow(paramtbl))) { - for (req in .requirements[[row]]) { + for (req in requirements[[row]]) { invoke(self$add_dep, id = paramtbl$id[[row]], .args = rl) } } @@ -558,7 +558,7 @@ ParamSet = R6Class("ParamSet", # return value with original ordering return(v) } - insert_named(named_list(private$.params$id), with(private$.tags[, list(list(tag)), by = "id"], set_names(tag, id))) + insert_named(named_list(private$.params$id), with(private$.tags[, list(tag = list(tag)), by = "id"], set_names(tag, id))) }, #' @template field_params From 2022397287fa96545bc6b75879c46ea9b3035ecf Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 24 Aug 2023 00:27:46 +0200 Subject: [PATCH 38/64] qiute some progress --- DESCRIPTION | 3 +- NAMESPACE | 9 +- R/Condition.R | 10 +- R/Domain.R | 7 +- R/Domain_methods.R | 16 +- R/ParamDbl.R | 2 +- R/ParamFct.R | 2 +- R/ParamInt.R | 6 +- R/ParamS6.R | 71 ------- R/ParamSet.R | 68 +++--- R/ParamSetCollection.R | 25 ++- R/Sampler1D.R | 2 +- R/SamplerHierarchical.R | 2 +- R/ps.R | 2 +- R/ps_replicate.R | 4 +- R/ps_union.R | 10 +- man/Condition.Rd | 6 +- man/Design.Rd | 30 +-- man/Domain.Rd | 13 +- man/ParamSet.Rd | 145 ++++++------- man/ParamSetCollection.Rd | 70 +++---- man/Sampler.Rd | 30 +-- man/Sampler1D.Rd | 34 +-- man/Sampler1DCateg.Rd | 34 +-- man/Sampler1DNormal.Rd | 34 +-- man/Sampler1DRfun.Rd | 34 +-- man/Sampler1DUnif.Rd | 34 +-- man/SamplerHierarchical.Rd | 34 +-- man/SamplerJointIndep.Rd | 34 +-- man/SamplerUnif.Rd | 34 +-- tests/testthat/helper_02_ParamSet.R | 53 ++--- tests/testthat/helper_03_domain.R | 31 ++- tests/testthat/helper_compat.R | 2 +- tests/testthat/test_Condition.R | 18 +- tests/testthat/test_Param.R | 19 +- tests/testthat/test_ParamDbl.R | 4 +- tests/testthat/test_ParamFct.R | 8 +- tests/testthat/test_ParamInt.R | 20 +- tests/testthat/test_ParamLgl.R | 10 +- tests/testthat/test_ParamSet.R | 152 +++++++------- tests/testthat/test_ParamSetCollection.R | 252 +++++------------------ tests/testthat/test_ParamUty.R | 14 +- tests/testthat/test_Param_rep.R | 10 +- tests/testthat/test_deps.R | 82 ++++---- 44 files changed, 623 insertions(+), 857 deletions(-) delete mode 100644 R/ParamS6.R diff --git a/DESCRIPTION b/DESCRIPTION index 581c9885..fafe532e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -57,7 +57,7 @@ Config/testthat/edition: 3 Config/testthat/parallel: false NeedsCompilation: no Roxygen: list(markdown = TRUE, r6 = TRUE) -RoxygenNote: 7.1.2 +RoxygenNote: 7.2.3 Collate: 'Condition.R' 'Design.R' @@ -68,7 +68,6 @@ Collate: 'ParamFct.R' 'ParamInt.R' 'ParamLgl.R' - 'ParamS6.R' 'ParamSet.R' 'ParamSetCollection.R' 'ParamUty.R' diff --git a/NAMESPACE b/NAMESPACE index 8748a83d..8ece4877 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,48 +1,43 @@ # Generated by roxygen2: do not edit by hand S3method(as.data.table,ParamSet) +S3method(c,ParamSet) S3method(condition_test,CondAnyOf) S3method(condition_test,CondEqual) S3method(domain_check,ParamDbl) S3method(domain_check,ParamFct) S3method(domain_check,ParamInt) S3method(domain_check,ParamLgl) -S3method(domain_check,ParamSet6) S3method(domain_check,ParamUty) S3method(domain_is_bounded,Domain) S3method(domain_is_bounded,ParamDbl) S3method(domain_is_bounded,ParamFct) S3method(domain_is_bounded,ParamInt) S3method(domain_is_bounded,ParamLgl) -S3method(domain_is_bounded,ParamSet6) S3method(domain_is_bounded,ParamUty) S3method(domain_is_categ,Domain) S3method(domain_is_categ,ParamDbl) S3method(domain_is_categ,ParamFct) S3method(domain_is_categ,ParamInt) S3method(domain_is_categ,ParamLgl) -S3method(domain_is_categ,ParamSet6) S3method(domain_is_categ,ParamUty) S3method(domain_is_number,Domain) S3method(domain_is_number,ParamDbl) S3method(domain_is_number,ParamFct) S3method(domain_is_number,ParamInt) S3method(domain_is_number,ParamLgl) -S3method(domain_is_number,ParamSet6) S3method(domain_is_number,ParamUty) S3method(domain_nlevels,Domain) S3method(domain_nlevels,ParamDbl) S3method(domain_nlevels,ParamFct) S3method(domain_nlevels,ParamInt) S3method(domain_nlevels,ParamLgl) -S3method(domain_nlevels,ParamSet6) S3method(domain_nlevels,ParamUty) S3method(domain_qunif,Domain) S3method(domain_qunif,ParamDbl) S3method(domain_qunif,ParamFct) S3method(domain_qunif,ParamInt) S3method(domain_qunif,ParamLgl) -S3method(domain_qunif,ParamSet6) S3method(domain_qunif,ParamUty) S3method(domain_sanitize,Domain) S3method(domain_sanitize,ParamDbl) @@ -79,7 +74,6 @@ export(domain_is_categ) export(domain_is_number) export(domain_nlevels) export(domain_qunif) -export(domain_sanitize) export(domain_storage_type) export(domain_test) export(generate_design_grid) @@ -89,7 +83,6 @@ export(p_dbl) export(p_fct) export(p_int) export(p_lgl) -export(p_s6) export(p_uty) export(ps) export(to_tune) diff --git a/R/Condition.R b/R/Condition.R index 24c235b1..44e10f40 100644 --- a/R/Condition.R +++ b/R/Condition.R @@ -10,7 +10,7 @@ #' @param x (`any`)\cr #' Value to test condition_test = function(cond, x) { - UseMethod("conditionTest") + UseMethod("condition_test") } #' @describeIn Condition @@ -25,7 +25,7 @@ condition_test = function(cond, x) { #' Symbolic representation to use for `` in the returned string. condition_as_string = function(cond, lhs_chr = "x") { assert_string(lhs_chr) - UseMethod("conditionTest") + UseMethod("condition_as_string") } # -- Condition @@ -57,7 +57,7 @@ Condition = function(rhs, condition_format_string) { } condition_as_string.Condition = function(cond, lhs_chr = "x") { - sprintf(condition_format_string, lhs_chr, str_collapse(cond$rhs)) + sprintf(cond$condition_format_string, lhs_chr, str_collapse(cond$rhs)) } #' @export @@ -85,10 +85,10 @@ condition_test.CondEqual = function(cond, x) { } #' @export -CondAnyOf = function(rhw) { +CondAnyOf = function(rhs) { assert_atomic(rhs, any.missing = FALSE, min.len = 1, unique = TRUE) cond = Condition(rhs, "%s %%in%% {%s}") - set_class(cond, c("CondEqual", class(cond))) + set_class(cond, c("CondAnyOf", class(cond))) } #' @export diff --git a/R/Domain.R b/R/Domain.R index e8114ec0..a46ad4ff 100644 --- a/R/Domain.R +++ b/R/Domain.R @@ -136,7 +136,7 @@ Domain = function(cls, grouping, assert_number(lower, na.ok = TRUE) assert_number(upper, na.ok = TRUE) assert_number(tolerance, na.ok = TRUE) - if (!is.logical(levels)) assert_character(levels, null.ok = TRUE) + if (!is.logical(levels)) assert_character(levels, any.missing = FALSE, unique = TRUE, null.ok = TRUE) assert_list(special_vals) if (length(special_vals) && !is.null(trafo)) stop("trafo and special_values can not both be given at the same time.") assert_character(tags, any.missing = FALSE, unique = TRUE) @@ -206,11 +206,12 @@ empty_domain = data.table(id = character(0), cls = character(0), grouping = char special_vals = list(), default = list(), storage_type = character(0), - .tags = list(), + .tags = character(0), # should be list(), strictly speaking, but that would lose the 'character' type information .trafo = list(), .requirements = list(), .init_given = logical(0), - .init = list()) + .init = list() +) domain_names = names(empty_domain) domain_names_permanent = grep("^\\.", domain_names, value = TRUE, invert = TRUE) diff --git a/R/Domain_methods.R b/R/Domain_methods.R index 01313c7c..e61fc6b7 100644 --- a/R/Domain_methods.R +++ b/R/Domain_methods.R @@ -31,44 +31,51 @@ domain_test = function(param, values) isTRUE(domain_check(param, values)) #' @export domain_storage_type = function(param) { + if (!nrow(param)) return(character(0)) assert_string(unique(param$grouping)) UseMethod("domain_storage_type") } #' @export domain_nlevels = function(param) { + if (!nrow(param)) return(integer(0)) assert_string(unique(param$grouping)) UseMethod("domain_nlevels") } #' @export domain_is_bounded = function(param) { + if (!nrow(param)) return(logical(0)) assert_string(unique(param$grouping)) UseMethod("domain_is_bounded") } #' @export domain_is_number = function(param) { + if (!nrow(param)) return(logical(0)) assert_string(unique(param$grouping)) UseMethod("domain_is_number") } #' @export domain_is_categ = function(param) { + if (!nrow(param)) return(logical(0)) assert_string(unique(param$grouping)) UseMethod("domain_is_categ") } #' @export domain_qunif = function(param, x) { + if (!nrow(param)) return(logical(0)) assert_string(unique(param$grouping)) assert_numeric(x, lower = 0, upper = 1, any.missing = FALSE, min.len = nrow(param)) assert_true(length(x) %% length(nrow(param)) == 0) UseMethod("domain_qunif") } -#' @export +#' export domain_sanitize = function(param, values) { + if (!nrow(param)) return(values) assert_string(unique(param$grouping)) UseMethod("domain_sanitize") } @@ -86,20 +93,21 @@ domain_qunif.Domain = function(param) stop("undefined") domain_sanitize.Domain = function(param, values) values #' @export -domain_is_categ.Domain = function(param) FALSE +domain_is_categ.Domain = function(param) rep(FALSE, nrow(param)) #' @export -domain_is_number.Domain = function(param) FALSE +domain_is_number.Domain = function(param) rep(FALSE, nrow(param)) # param: check_domain_vectorize = function(ids, values, checker, more_args = list()) { - if (length(checker) == 1) { + if (is.function(checker)) { errors = pmap(c(list(ids, values), more_args), function(id, value, ...) { ch = checker(value, ...) if (isTRUE(ch)) NULL else sprintf("%s: %s", id, ch) }) } else { + # `checker` is a list of functions with the same length as `values` errors = pmap(c(list(ids, values, checker), more_args), function(id, value, chck, ...) { ch = chck(value, ...) if (isTRUE(ch)) NULL else sprintf("%s: %s", id, ch) diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 7554ba68..8932f149 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -40,7 +40,7 @@ domain_sanitize.ParamDbl = function(param, values) { #' @export domain_nlevels.ParamDbl = function(param) ifelse(param$upper == param$lower, 1, Inf) #' @export -domain_is_bounded.ParamDbl = function(param) is.finite(param$lower) && is.finite(param$upper) +domain_is_bounded.ParamDbl = function(param) is.finite(param$lower) & is.finite(param$upper) #' @export domain_qunif.ParamDbl = function(param, x) { pmax(pmin(x * param$upper - (x-1) * param$lower, param$upper), param$lower) # extra careful here w/ rounding errors diff --git a/R/ParamFct.R b/R/ParamFct.R index e79fe3d2..cad930db 100644 --- a/R/ParamFct.R +++ b/R/ParamFct.R @@ -31,7 +31,7 @@ domain_check.ParamFct = function(param, values) { values_str = as.character(values) if (all(values_str %in% param$levels[[1]])) return(TRUE) # this works because we have the grouping -- all 'levels' are the same here. } - check_domain_vectorize(param$id, param, check_choice, more_args = list(choices = param$levels)) + check_domain_vectorize(param$id, values, check_choice, more_args = list(choices = param$levels)) } #' @export diff --git a/R/ParamInt.R b/R/ParamInt.R index 8be68532..c669fba6 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -58,10 +58,12 @@ domain_sanitize.ParamInt = function(param, values) { #' @export domain_nlevels.ParamInt = function(param) (param$upper - param$lower) + 1 #' @export -domain_is_bounded.ParamInt = function(param) is.finite(param$lower) && is.finite(param$upper) +domain_is_bounded.ParamInt = function(param) is.finite(param$lower) & is.finite(param$upper) #' @export domain_qunif.ParamInt = function(param, x) { - pmax(pmin(as.integer(x * (param$upper + 1) - (x-1) * param$lower), param$upper), param$lower) # extra careful here w/ rounding errors and the x == 1 case + # extra careful here w/ rounding errors and the x == 1 case + # note that as.integer alone rounds towards 0 and can not be used without 'floor' here + as.integer(floor(pmax(pmin(x * (param$upper + 1) - (x - 1) * param$lower, param$upper), param$lower))) } #' @export diff --git a/R/ParamS6.R b/R/ParamS6.R deleted file mode 100644 index f37bd866..00000000 --- a/R/ParamS6.R +++ /dev/null @@ -1,71 +0,0 @@ - -#' @rdname Domain -#' @export -p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { - set = S6Cache$canonicalize(support) - support_description = if (is.character(support)) support else S6Cache$set6_sane_repr(object) - - if ((("power" %in% names(set)) && set$power != 1) || set$class %nin% c("numeric", "integer")) { - storage_type = "list" - } else { - storage_type = set$class - } - - Domain(cls = "ParamSet6", grouping = support_description, - cargo = set, - lower = suppressWarnings(as.numeric(set$lower)), - upper = suppressWarnings(as.numeric(set$upper)), - levels = as.list(set$elements), - special_vals = special_vals, - default = default, - tags = tags, - trafo = trafo, - storage_type = storage_type, - depends_expr = substitute(depends), - init = init) -} - -#' @export -domain_check.ParamSet6 = function(param, values) { - # we can rely on 'grouping' always giving us the same class - set = set6_cache_get(param$grouping[[1]]) - if (set$contains(values, all = TRUE)) { - return(TRUE) - } - nomatches = param$id[!set$contains(values, all = FALSE)] - sprintf("%s: not contained in %s.", str_collapse(nomatches, sep = ", "), set$strprint()) -} - -#' @export -domain_nlevels.ParamSet6 = function(param) { - # we can rely on 'grouping' always giving us the same class - set = set6_cache_get(param$grouping[[1]]) - card = set$properties$cardinality - card = if (!is.numeric(card)) Inf - rep(card, nrow(param)) -} -#' @export -domain_is_bounded.ParamSet6 = function(param) { - # we can rely on 'grouping' always giving us the same class - set = set6_cache_get(param$grouping[[1]]) - card = set$properties$cardinality - card = if (!is.numeric(card)) Inf - boundedness = is.finite(card) || (is.finite(set$lower) && is.finite(set$upper)) - rep(boundedness, nrow(param)) -} -#' @export -domain_qunif.ParamSet6 = function(param, x) stop("undefined") - - -#' @export -domain_is_number.ParamSet6 = function(param) { - param$storage_type[[1]] != "list" -} -#' @export -domain_is_categ.ParamSet6 = function(param) { - set = set6_cache_get(param$grouping[[1]]) - categness = ("power" %nin% names(set) || set$power == 1) && - set$class %nin% c("numeric", "integer") && is.finite(set$properties$cardinality) - - categness -} diff --git a/R/ParamSet.R b/R/ParamSet.R index 15c5ed23..fcd175a9 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -58,23 +58,23 @@ ParamSet = R6Class("ParamSet", #' @param ignore_ids (`logical(1)`)\cr #' Ignore `$id` slots of `params` and instead use the names instead. #' When this is `TRUE`, then `params` must be named. - #' This can be used to create a `ParamSet` with certain [`Param`] `id`s + #' Thisdo can be used to create a `ParamSet` with certain [`Param`] `id`s #' without having to clone said [`Param`]s. #' Default `FALSE`. initialize = function(params = named_list(), allow_dangling_dependencies = FALSE) { assert_list(params, types = "Domain") - assert_names(names(params), type = "strict") + if (length(params)) assert_names(names(params), type = "strict") if (!length(params)) { - paramtbl = empty_domain + paramtbl = copy(empty_domain) } else { paramtbl = rbindlist(params) set(paramtbl, , "id", names(params)) + private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] + setindexv(private$.tags, "tag") } - private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] - setindexv(private$.tags, "tag") initvalues = with(paramtbl[(.init_given), .(.init, id)], set_names(.init, id)) private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") @@ -115,10 +115,10 @@ ParamSet = R6Class("ParamSet", if (is.null(tags) && is.null(any_tags)) { return(ptbl$id) } - tagtbl = private$.tags[ptbl] + tagtbl = private$.tags[ptbl, nomatch = 0] idpool = if (is.null(any_tags)) list() else list(tagtbl[tag %in% any_tags, id]) - idpool = c(idpool, lapply(tags, function(t) tagtbl[t, id, on = "tags"])) - Reduce(idpool, intersect) + idpool = c(idpool, lapply(tags, function(t) tagtbl[t, id, on = "tag", nomatch = 0])) + Reduce(intersect, idpool) }, #' @description @@ -150,18 +150,19 @@ ParamSet = R6Class("ParamSet", } if (check_required) { - required = setdiff(self$ids(tags = "required"), nds) + required = setdiff(self$ids(tags = "required"), ns) if (length(required) > 0L) { stop(sprintf("Missing required parameters: %s", str_collapse(required))) } } + deps = self$deps if (remove_dependencies && nrow(deps)) { for (j in seq_row(deps)) { p1id = deps$id[[j]] p2id = deps$on[[j]] cond = deps$cond[[j]] - if (p1id %in% ns && !inherits(values[[p2id]], "TuneToken") && !isTRUE(cond$test(values[[p2id]]))) { + if (p1id %in% ns && !inherits(values[[p2id]], "TuneToken") && !isTRUE(condition_test(cond, values[[p2id]]))) { values[p1id] = NULL } } @@ -171,7 +172,7 @@ ParamSet = R6Class("ParamSet", }, trafo = function(x, param_set = self) { - trafos = private$.params[names(x), .(id, trafo, value = x), nomatch = 0] + trafos = private$.trafos[names(x), .(id, trafo, value = x), nomatch = 0] if (nrow(trafos)) { transformed = pmap(trafos, function(id, trafo, value) trafo(value)) insert_named(x, set_names(transformed, trafos$id)) @@ -226,12 +227,13 @@ ParamSet = R6Class("ParamSet", # check each parameter group's feasibility xs_nontune = discard(xs, inherits, "TuneToken") - params = params[names(xs_nontune), on = "id"] + # need to make sure we index w/ empty character instead of NULL + params = params[names(xs_nontune) %??% character(0), on = "id"] set(params, , "values", list(xs_nontune)) pgroups = split(params, by = c("cls", "grouping")) checkresults = map(pgroups, function(x) { - domain_check(set_class(x, c(x$cls[[1]], class(x))), x$values) + domain_check(set_class(x, c(x$cls[[1]], "Domain", class(x))), x$values) }) checkresults = discard(checkresults, isTRUE) if (length(checkresults)) { @@ -243,7 +245,7 @@ ParamSet = R6Class("ParamSet", if (length(required) > 0L) { return(sprintf("Missing required parameters: %s", str_collapse(required))) } - return(check_dependencies(xs)) + return(self$check_dependencies(xs)) if (!self$test_constraint(xs, assert_value = FALSE)) return(sprintf("Constraint not fulfilled.")) } @@ -262,14 +264,14 @@ ParamSet = R6Class("ParamSet", # we are ONLY ok if: # - if 'id' is there, then 'on' must be there, and cond must be true # - if 'id' is not there. but that is skipped (deps[id %in% ns] filter) - if (on %in% ns && cond$test(onval)) return(NULL) + if (on %in% ns && condition_test(cond, onval)) return(NULL) msg = sprintf("%s: can only be set if the following condition is met '%s'.", - id, cond$as_string(on)) + id, condition_as_string(cond, on)) if (is.null(onval)) { msg = sprintf(paste("%s Instead the parameter value for '%s' is not set at all.", "Try setting '%s' to a value that satisfies the condition"), msg, on, on) } else { - msg = sprintf("%s Instead the current parameter value is: %s=%s", msg, on, as_short_string(onval)) + msg = sprintf("%s Instead the current parameter value is: %s == %s", msg, on, as_short_string(onval)) } msg }) @@ -369,7 +371,7 @@ ParamSet = R6Class("ParamSet", .tags = private$.tags[id, tag], .trafo = private$.trafos[id, trafo], .requirements = list(transpose_list(self$deps[id, .(on, cond), on = "id"])), - .init_given = id %in% names(values), + .init_given = id %in% names(vals), .init = unname(vals[id])) ] @@ -378,7 +380,7 @@ ParamSet = R6Class("ParamSet", #' @description #' Create a new `ParamSet` restricted to the passed IDs. - #' + #' # TODO: ParamSetCollection trafo #' @param ids (`character()`). #' @return `ParamSet`. subset = function(ids, allow_dangling_dependencies = FALSE, keep_constraint = TRUE) { @@ -400,8 +402,9 @@ ParamSet = R6Class("ParamSet", result$.__enclos_env__$private$.params = private$.params[ids, on = "id"] result$.__enclos_env__$private$.trafos = private$.trafos[ids, on = "id", nomatch = NULL] + result$.__enclos_env__$private$.tags = private$.tags[ids, on = "id", nomatch = NULL] result$assert_values = FALSE - result$deps = deps + result$deps = deps[ids, on = "id", nomatch = NULL] if (keep_constraint) result$constraint = self$constraint result$extra_trafo = self$extra_trafo # restrict to ids already in pvals @@ -412,12 +415,14 @@ ParamSet = R6Class("ParamSet", }, subspaces = function(ids = private$.params$id) { + values = self$values sapply(ids, simplify = FALSE, function(get_id) { result = ParamSet$new() result$extra_trafo = self$extra_trafo # constraint make no sense here, basically by definition result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] result$.__enclos_env__$private$.trafos = private$.trafos[get_id, on = "id", nomatch = NULL] + result$.__enclos_env__$private$.tags = private$.tags[get_id, on = "id", nomatch = NULL] result$assert_values = FALSE result$values = values[match(get_id, names(values), nomatch = 0)] result$assert_values = TRUE @@ -464,7 +469,7 @@ ParamSet = R6Class("ParamSet", } if (on %in% ids) { # not necessarily true when allow_dangling_dependencies - feasible_on_values = map_lgl(cond$rhs, domain_check, param = self$get_param(on)) + feasible_on_values = map_lgl(cond$rhs, function(x) domain_test(self$get_param(on), list(x))) if (any(!feasible_on_values)) { stopf("Condition has infeasible values for %s: %s", on, str_collapse(cond$rhs[!feasible_on_values])) } @@ -538,10 +543,10 @@ ParamSet = R6Class("ParamSet", # solves issue #293, #317 nontt = discard(xs, inherits, "TuneToken") - sanitised = set(private$.params[names(nontt), on = "id"], , "values", list(nontt))[ + sanitized = set(private$.params[names(nontt), on = "id"], , "values", list(nontt))[ !pmap_lgl(list(special_vals, values), has_element), .(id, values = domain_sanitize(recover_domain(.SD, .BY), values)), by = c("cls", "grouping")] - insert_named(xx, with(sanitized, set_names(values, id))) + xs = insert_named(xs, with(sanitized, set_names(values, id))) } # store with param ordering, return value with original ordering private$.values = xs[match(private$.params$id, names(xs), nomatch = 0)] @@ -558,7 +563,7 @@ ParamSet = R6Class("ParamSet", # return value with original ordering return(v) } - insert_named(named_list(private$.params$id), with(private$.tags[, list(tag = list(tag)), by = "id"], set_names(tag, id))) + insert_named(named_list(private$.params$id, character(0)), with(private$.tags[, list(tag = list(tag)), by = "id"], set_names(tag, id))) }, #' @template field_params @@ -568,16 +573,16 @@ ParamSet = R6Class("ParamSet", } result = copy(private$.params) - result[, .tags = self$tags] + result[, .tags := list(self$tags)] result[private$.trafos, .trafo := trafo, on = "id"] result[self$deps, .requirements := transpose_list(.(on, cond)), on = "id"] vals = self$values result[, `:=`( .init_given = id %in% names(vals), - .init := unname(vals[id]) + .init = unname(vals[id]) )] - result + result[] }, #' @field extra_trafo (`function(x, param_set)`)\cr @@ -603,7 +608,7 @@ ParamSet = R6Class("ParamSet", if (missing(f)) { private$.constraint } else { - assert_function(f, args = x, null.ok = TRUE) + assert_function(f, args = "x", null.ok = TRUE) private$.constraint = f } }, @@ -645,13 +650,14 @@ ParamSet = R6Class("ParamSet", all_numeric = function() all(self$is_number), #' @field all_categorical (`logical(1)`)\cr Is `TRUE` if all parameters are [ParamFct] and [ParamLgl]. all_categorical = function() all(self$is_categ), - + #' @field all_bounded (`logical(1)`)\cr Is `TRUE` if all parameters are bounded. + all_bounded = function() all(self$is_bounded), ############################ # Per-Parameter properties #' @field class (named `character()`)\cr Classes of contained parameters. Named with parameter IDs. - class = function() with(private$.params, set_names(class, id)), + class = function() with(private$.params, set_names(cls, id)), #' @field lower (named `double()`)\cr Lower bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. lower = function() with(private$.params, set_names(lower, id)), #' @field upper (named `double()`)\cr Upper bounds of numeric parameters (`NA` for non-numerics). Named with parameter IDs. @@ -774,7 +780,7 @@ ParamSet = R6Class("ParamSet", recover_domain = function(sd, by) { domain = as.data.table(c(by, sd)) - class(domain) = c(domain$cls, class(domain)) + class(domain) = c(domain$cls, "Domain", class(domain)) domain } diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index a83207b1..adcd3fa7 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -42,16 +42,16 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' Default `FALSE`. initialize = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") - assert_flag(tag_set) + assert_flag(tag_sets) assert_flag(tag_params) if (is.null(names(sets))) names(sets) = rep("", length(sets)) - assert_names(names(named_sets)[names(named_sets) != ""], type = "strict") + assert_names(names(sets)[names(sets) != ""], type = "strict") paramtbl = rbindlist(map(seq_along(sets), function(i) { s = sets[[i]] - n = names(sets)[[n]] + n = names(sets)[[i]] params_child = s$params[, `:=`(original_id = id, owner_ps_index = i, owner_name = n)] if (n != "") set(params_child, , "id", sprintf("%s.%s", n, params_child$id)) params_child @@ -60,9 +60,13 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, dups = duplicated(paramtbl$id) if (any(dups)) { stopf("ParamSetCollection would contain duplicated parameter names: %s", - str_collapse(unique(pnames[dups]))) + str_collapse(unique(paramtbl$id[dups]))) } + if (!nrow(paramtbl)) { + # when paramtbl is empty, use special setup to make sure information about the `.tags` column is present. + paramtbl = copy(empty_domain)[, `:=`(original_id = character(0), owner_ps_index = integer(0), owner_name = character(0))] + } if (tag_sets) paramtbl[owner_name != "", , .tags := pmap(list(.tags, owner_name), function(x, n) c(x, sprintf("set_%s", n)))] if (tag_params) paramtbl[, .tags := pmap(list(.tags, original_id), function(x, n) c(x, sprintf("param_%s", n)))] private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] @@ -78,7 +82,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, setindexv(paramtbl, c("id", "cls", "grouping")) private$.params = paramtbl - private$.children_with_trafos = which(map_lgl(map(sets, "extra_trafo"), is.null)) + private$.children_with_trafos = which(!map_lgl(map(sets, "extra_trafo"), is.null)) private$.children_with_constraints = which(map_lgl(map(sets, "constraint"), is.null)) private$.sets = sets @@ -114,10 +118,14 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # We do this here because we don't want the loop to be aborted early and have half an update. self$assert(xs) - translate = private$.translation[names(xs), list(orig_id, owner_ps_index), on = "id"] - set(translate, , j = "values", xs) + translate = private$.translation[names(xs), list(original_id, owner_ps_index), on = "id"] + set(translate, , j = "values", list(xs)) for (xtl in split(translate, by = "owner_ps_index")) { - sets[[xtl$owner_ps_index]]$values = set_names(xtl$values, xtl$orig_id) + sets[[xtl$owner_ps_index[[1]]]]$values = set_names(xtl$values, xtl$original_id) + } + # clear the values of all sets that are not touched by xs + for (clearing in setdiff(seq_along(sets), translate$owner_ps_index)) { + sets[[clearing]]$values = named_list() } } vals = unlist(map(sets, "values"), recursive = FALSE) @@ -148,6 +156,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, .sets = NULL, .translation = data.table(id = character(0), original_id = character(0), owner_ps_index = integer(0), owner_name = character(0), key = "id"), .children_with_trafos = NULL, + .children_with_constraints = NULL, .extra_trafo_explicit = function(x) { changed = unlist(lapply(private$.children_with_trafos, function(set_index) { changing_ids = private$.translation[J(set_index), id, on = "owner_ps_index"] diff --git a/R/Sampler1D.R b/R/Sampler1D.R index 6ee96668..a53c98e6 100644 --- a/R/Sampler1D.R +++ b/R/Sampler1D.R @@ -58,7 +58,7 @@ Sampler1DUnif = R6Class("Sampler1DUnif", inherit = Sampler1D, ), private = list( - .sample = function(n) private$as_dt_col(self$qunif(setnames(data.table(runif(n)), self$param$ids()))) # sample by doing qunif(u) + .sample = function(n) self$param_set$qunif(setnames(data.table(runif(n)), self$param$ids())) # sample by doing qunif(u) ) ) diff --git a/R/SamplerHierarchical.R b/R/SamplerHierarchical.R index f5c9887e..136b2037 100644 --- a/R/SamplerHierarchical.R +++ b/R/SamplerHierarchical.R @@ -25,7 +25,7 @@ SamplerHierarchical = R6Class("SamplerHierarchical", inherit = Sampler, assert_param_set(param_set, no_untyped = TRUE) assert_list(samplers, types = "Sampler1D") ids1 = param_set$ids() - ids2 = map_chr(samplers, function(s) s$param$id) + ids2 = map_chr(samplers, function(s) s$param$ids()) if (!setequal(ids1, ids2)) { stop("IDs of params in samplers to not correspond to IDs of params in set!") } diff --git a/R/ps.R b/R/ps.R index 20458e56..5370c5ff 100644 --- a/R/ps.R +++ b/R/ps.R @@ -59,7 +59,7 @@ #' @family ParamSet construction helpers #' @export ps = function(..., .extra_trafo = NULL, .constraint = NULL, .allow_dangling_dependencies = FALSE) { - param_set = ParamSet$new(list(...), allow_dangling_dependencies = .allow_dangling_dependencies) # if length is 0 then no names are present + param_set = ParamSet$new(list(...), allow_dangling_dependencies = .allow_dangling_dependencies) param_set$extra_trafo = .extra_trafo param_set$constraint = .constraint param_set diff --git a/R/ps_replicate.R b/R/ps_replicate.R index 98c8dd04..dc390a33 100644 --- a/R/ps_replicate.R +++ b/R/ps_replicate.R @@ -1,7 +1,7 @@ -ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_set = FALSE, tag_params = FALSE) { +ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_sets = FALSE, tag_params = FALSE) { assert_count(times) assert_character(prefixes, any.missing = FALSE, unique = TRUE, len = times) - ps_union(named_list(prefixes, set), tag_set = tag_set, tag_params = tag_params) + ps_union(named_list(prefixes, set), tag_sets = tag_sets, tag_params = tag_params) } diff --git a/R/ps_union.R b/R/ps_union.R index 91946ddd..0fae3d74 100644 --- a/R/ps_union.R +++ b/R/ps_union.R @@ -6,7 +6,13 @@ # from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will # have their changed to .. This is also reflected in deps and in `$trafo`. # @param sets: list of ParamSet -ps_union = function(sets, tag_set = FALSE, tag_params = FALSE) { +ps_union = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") - ParamSetCollection$new(sets, tag_set = tag_set, tag_params = tag_params)$flatten() + if (!length(sets)) return(ParamSet$new()) + ParamSetCollection$new(sets, tag_sets = tag_sets, tag_params = tag_params)$flatten() +} + +#' @export +c.ParamSet = function(..., .tag_sets = FALSE, .tag_params = FALSE) { + ps_union(list(...), tag_sets = .tag_sets, tag_params = .tag_params) } diff --git a/man/Condition.Rd b/man/Condition.Rd index bc770547..eaaea35a 100644 --- a/man/Condition.Rd +++ b/man/Condition.Rd @@ -38,14 +38,14 @@ Condition object, to specify the condition in a dependency. } \section{Functions}{ \itemize{ -\item \code{condition_test}: Used internally. Tests whether a value satisfies a given condition. +\item \code{condition_test()}: Used internally. Tests whether a value satisfies a given condition. Vectorizes when \code{x} is atomic. -\item \code{condition_as_string}: Used internally. Returns a string that represents the condition for pretty +\item \code{condition_as_string()}: Used internally. Returns a string that represents the condition for pretty printing, in the form \code{" "}, e.g. \code{"x == 3"} or \code{"param \%in\% {1, 2, 10}"}. -}} +}} \section{Currently implemented simple conditions}{ \itemize{ diff --git a/man/Design.Rd b/man/Design.Rd index 16c7a5bb..98eccce7 100644 --- a/man/Design.Rd +++ b/man/Design.Rd @@ -21,16 +21,16 @@ Stored \code{data}.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Design$new()}} -\item \href{#method-format}{\code{Design$format()}} -\item \href{#method-print}{\code{Design$print()}} -\item \href{#method-transpose}{\code{Design$transpose()}} -\item \href{#method-clone}{\code{Design$clone()}} +\item \href{#method-Design-new}{\code{Design$new()}} +\item \href{#method-Design-format}{\code{Design$format()}} +\item \href{#method-Design-print}{\code{Design$print()}} +\item \href{#method-Design-transpose}{\code{Design$transpose()}} +\item \href{#method-Design-clone}{\code{Design$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Design-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -52,8 +52,8 @@ Remove duplicates?} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-format}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Design-format}{}}} \subsection{Method \code{format()}}{ Helper for print outputs. \subsection{Usage}{ @@ -62,8 +62,8 @@ Helper for print outputs. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Design-print}{}}} \subsection{Method \code{print()}}{ Printer. \subsection{Usage}{ @@ -79,8 +79,8 @@ Printer. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-transpose}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Design-transpose}{}}} \subsection{Method \code{transpose()}}{ Converts \code{data} into a list of lists of row-configurations, possibly removes \code{NA} entries of inactive parameter values due to unsatisfied dependencies, @@ -103,8 +103,8 @@ Should the \code{trafo} function of the \link{ParamSet} be called?} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Design-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Domain.Rd b/man/Domain.Rd index 266dd35c..5f8ccc5c 100644 --- a/man/Domain.Rd +++ b/man/Domain.Rd @@ -1,13 +1,12 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Domain.R, R/ParamDbl.R, R/ParamFct.R, -% R/ParamInt.R, R/ParamLgl.R, R/ParamS6.R, R/ParamUty.R +% R/ParamInt.R, R/ParamLgl.R, R/ParamUty.R \name{Domain} \alias{Domain} \alias{p_dbl} \alias{p_fct} \alias{p_int} \alias{p_lgl} -\alias{p_s6} \alias{p_uty} \title{Domain: Parameter Range without an Id} \usage{ @@ -56,16 +55,6 @@ p_lgl( init ) -p_s6( - support, - special_vals = list(), - default = NO_DEF, - tags = character(), - depends = NULL, - trafo = NULL, - init -) - p_uty( custom_check = NULL, special_vals = list(), diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 6b5abea5..a1b00157 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -105,6 +105,8 @@ Settable, if you want to remove dependencies or perform other changes.} \item{\code{all_categorical}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \link{ParamFct} and \link{ParamLgl}.} +\item{\code{all_bounded}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are bounded.} + \item{\code{class}}{(named \code{character()})\cr Classes of contained parameters. Named with parameter IDs.} \item{\code{lower}}{(named \code{double()})\cr Lower bounds of numeric parameters (\code{NA} for non-numerics). Named with parameter IDs.} @@ -135,34 +137,34 @@ Named with param IDs.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{ParamSet$new()}} -\item \href{#method-ids}{\code{ParamSet$ids()}} -\item \href{#method-get_values}{\code{ParamSet$get_values()}} -\item \href{#method-trafo}{\code{ParamSet$trafo()}} -\item \href{#method-test_constraint}{\code{ParamSet$test_constraint()}} -\item \href{#method-test_constraint_dt}{\code{ParamSet$test_constraint_dt()}} -\item \href{#method-check}{\code{ParamSet$check()}} -\item \href{#method-check_dependencies}{\code{ParamSet$check_dependencies()}} -\item \href{#method-test}{\code{ParamSet$test()}} -\item \href{#method-assert}{\code{ParamSet$assert()}} -\item \href{#method-check_dt}{\code{ParamSet$check_dt()}} -\item \href{#method-test_dt}{\code{ParamSet$test_dt()}} -\item \href{#method-assert_dt}{\code{ParamSet$assert_dt()}} -\item \href{#method-qunif}{\code{ParamSet$qunif()}} -\item \href{#method-get_param}{\code{ParamSet$get_param()}} -\item \href{#method-subset}{\code{ParamSet$subset()}} -\item \href{#method-subspaces}{\code{ParamSet$subspaces()}} -\item \href{#method-flatten}{\code{ParamSet$flatten()}} -\item \href{#method-search_space}{\code{ParamSet$search_space()}} -\item \href{#method-add_dep}{\code{ParamSet$add_dep()}} -\item \href{#method-format}{\code{ParamSet$format()}} -\item \href{#method-print}{\code{ParamSet$print()}} -\item \href{#method-clone}{\code{ParamSet$clone()}} +\item \href{#method-ParamSet-new}{\code{ParamSet$new()}} +\item \href{#method-ParamSet-ids}{\code{ParamSet$ids()}} +\item \href{#method-ParamSet-get_values}{\code{ParamSet$get_values()}} +\item \href{#method-ParamSet-trafo}{\code{ParamSet$trafo()}} +\item \href{#method-ParamSet-test_constraint}{\code{ParamSet$test_constraint()}} +\item \href{#method-ParamSet-test_constraint_dt}{\code{ParamSet$test_constraint_dt()}} +\item \href{#method-ParamSet-check}{\code{ParamSet$check()}} +\item \href{#method-ParamSet-check_dependencies}{\code{ParamSet$check_dependencies()}} +\item \href{#method-ParamSet-test}{\code{ParamSet$test()}} +\item \href{#method-ParamSet-assert}{\code{ParamSet$assert()}} +\item \href{#method-ParamSet-check_dt}{\code{ParamSet$check_dt()}} +\item \href{#method-ParamSet-test_dt}{\code{ParamSet$test_dt()}} +\item \href{#method-ParamSet-assert_dt}{\code{ParamSet$assert_dt()}} +\item \href{#method-ParamSet-qunif}{\code{ParamSet$qunif()}} +\item \href{#method-ParamSet-get_param}{\code{ParamSet$get_param()}} +\item \href{#method-ParamSet-subset}{\code{ParamSet$subset()}} +\item \href{#method-ParamSet-subspaces}{\code{ParamSet$subspaces()}} +\item \href{#method-ParamSet-flatten}{\code{ParamSet$flatten()}} +\item \href{#method-ParamSet-search_space}{\code{ParamSet$search_space()}} +\item \href{#method-ParamSet-add_dep}{\code{ParamSet$add_dep()}} +\item \href{#method-ParamSet-format}{\code{ParamSet$format()}} +\item \href{#method-ParamSet-print}{\code{ParamSet$print()}} +\item \href{#method-ParamSet-clone}{\code{ParamSet$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -184,7 +186,7 @@ prefix inside \code{\link{ParamSetCollection}}. Default \code{""} (no prefix).} \item{\code{ignore_ids}}{(\code{logical(1)})\cr Ignore \verb{$id} slots of \code{params} and instead use the names instead. When this is \code{TRUE}, then \code{params} must be named. -This can be used to create a \code{ParamSet} with certain \code{\link{Param}} \code{id}s +Thisdo can be used to create a \code{ParamSet} with certain \code{\link{Param}} \code{id}s without having to clone said \code{\link{Param}}s. Default \code{FALSE}.} } @@ -192,8 +194,8 @@ Default \code{FALSE}.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-ids}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-ids}{}}} \subsection{Method \code{ids()}}{ Retrieves IDs of contained parameters based on some filter criteria selections, \code{NULL} means no restriction. @@ -216,8 +218,8 @@ Only returns IDs of parameters that satisfy all conditions. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_values}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-get_values}{}}} \subsection{Method \code{get_values()}}{ Retrieves parameter values based on some selections, \code{NULL} means no restriction and is equivalent to \verb{$values}. @@ -255,8 +257,8 @@ Named \code{list()}. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-trafo}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-trafo}{}}} \subsection{Method \code{trafo()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$trafo(x, param_set = self)}\if{html}{\out{
}} @@ -264,8 +266,8 @@ Named \code{list()}. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test_constraint}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-test_constraint}{}}} \subsection{Method \code{test_constraint()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$test_constraint(x, assert_value = TRUE)}\if{html}{\out{
}} @@ -273,8 +275,8 @@ Named \code{list()}. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test_constraint_dt}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-test_constraint_dt}{}}} \subsection{Method \code{test_constraint_dt()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$test_constraint_dt(x, assert_value = TRUE)}\if{html}{\out{
}} @@ -282,8 +284,8 @@ Named \code{list()}. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-check}{}}} \subsection{Method \code{check()}}{ \pkg{checkmate}-like check-function. Takes a named list. A point x is feasible, if it configures a subset of params, @@ -305,8 +307,8 @@ If successful \code{TRUE}, if not a string with the error message. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_dependencies}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-check_dependencies}{}}} \subsection{Method \code{check_dependencies()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$check_dependencies(xs)}\if{html}{\out{
}} @@ -314,8 +316,8 @@ If successful \code{TRUE}, if not a string with the error message. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-test}{}}} \subsection{Method \code{test()}}{ \pkg{checkmate}-like test-function. Takes a named list. A point x is feasible, if it configures a subset of params, @@ -337,8 +339,8 @@ If successful \code{TRUE}, if not \code{FALSE}. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-assert}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-assert}{}}} \subsection{Method \code{assert()}}{ \pkg{checkmate}-like assert-function. Takes a named list. A point x is feasible, if it configures a subset of params, @@ -364,8 +366,8 @@ If successful \code{xs} invisibly, if not an error message. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_dt}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-check_dt}{}}} \subsection{Method \code{check_dt()}}{ \pkg{checkmate}-like check-function. Takes a \link[data.table:data.table]{data.table::data.table} where rows are points and columns are parameters. A point x is feasible, @@ -388,8 +390,8 @@ If successful \code{TRUE}, if not a string with the error message. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-test_dt}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-test_dt}{}}} \subsection{Method \code{test_dt()}}{ \pkg{checkmate}-like test-function (s. \verb{$check_dt()}). \subsection{Usage}{ @@ -408,8 +410,8 @@ If successful \code{TRUE}, if not \code{FALSE}. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-assert_dt}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-assert_dt}{}}} \subsection{Method \code{assert_dt()}}{ \pkg{checkmate}-like assert-function (s. \verb{$check_dt()}). \subsection{Usage}{ @@ -432,8 +434,8 @@ If successful \code{xs} invisibly, if not an error message. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-qunif}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-qunif}{}}} \subsection{Method \code{qunif()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$qunif(x)}\if{html}{\out{
}} @@ -441,8 +443,8 @@ If successful \code{xs} invisibly, if not an error message. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_param}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-get_param}{}}} \subsection{Method \code{get_param()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$get_param(id)}\if{html}{\out{
}} @@ -450,10 +452,11 @@ If successful \code{xs} invisibly, if not an error message. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-subset}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-subset}{}}} \subsection{Method \code{subset()}}{ -Create a new \code{ParamSet} restricted to the passed IDs. +Create a new `ParamSet` restricted to the passed IDs. + # TODO: ParamSetCollection trafo \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$subset( ids, @@ -474,8 +477,8 @@ Create a new \code{ParamSet} restricted to the passed IDs. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-subspaces}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-subspaces}{}}} \subsection{Method \code{subspaces()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$subspaces(ids = private$.params$id)}\if{html}{\out{
}} @@ -483,8 +486,8 @@ Create a new \code{ParamSet} restricted to the passed IDs. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-flatten}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-flatten}{}}} \subsection{Method \code{flatten()}}{ Create a \code{ParamSet} from this object, even if this object itself is not a \code{ParamSet}. @@ -494,8 +497,8 @@ a \code{ParamSet}. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-search_space}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-search_space}{}}} \subsection{Method \code{search_space()}}{ Construct a \code{\link{ParamSet}} to tune over. Constructed from \code{\link{TuneToken}} in \verb{$values}, see \code{\link[=to_tune]{to_tune()}}. \subsection{Usage}{ @@ -511,8 +514,8 @@ Construct a \code{\link{ParamSet}} to tune over. Constructed from \code{\link{Tu } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-add_dep}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-add_dep}{}}} \subsection{Method \code{add_dep()}}{ Adds a dependency to this set, so that param \code{id} now depends on param \code{on}. \subsection{Usage}{ @@ -534,8 +537,8 @@ Adds a dependency to this set, so that param \code{id} now depends on param \cod } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-format}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-format}{}}} \subsection{Method \code{format()}}{ Helper for print outputs. \subsection{Usage}{ @@ -544,8 +547,8 @@ Helper for print outputs. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-print}{}}} \subsection{Method \code{print()}}{ Printer. \subsection{Usage}{ @@ -568,8 +571,8 @@ Which fields should not be printed? Default is \code{"levels"}, } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 63837fd2..e6a7ad78 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -49,40 +49,40 @@ When you set values, all previously set values will be unset / removed.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{ParamSetCollection$new()}} -\item \href{#method-clone}{\code{ParamSetCollection$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/ParamSet.html#method-add_dep}{\code{tautology::ParamSet$add_dep()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-assert}{\code{tautology::ParamSet$assert()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-assert_dt}{\code{tautology::ParamSet$assert_dt()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-check}{\code{tautology::ParamSet$check()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-check_dependencies}{\code{tautology::ParamSet$check_dependencies()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-check_dt}{\code{tautology::ParamSet$check_dt()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-flatten}{\code{tautology::ParamSet$flatten()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-format}{\code{tautology::ParamSet$format()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-get_param}{\code{tautology::ParamSet$get_param()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-get_values}{\code{tautology::ParamSet$get_values()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-ids}{\code{tautology::ParamSet$ids()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-print}{\code{tautology::ParamSet$print()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-qunif}{\code{tautology::ParamSet$qunif()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-search_space}{\code{tautology::ParamSet$search_space()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-subset}{\code{tautology::ParamSet$subset()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-subspaces}{\code{tautology::ParamSet$subspaces()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-test}{\code{tautology::ParamSet$test()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-test_constraint}{\code{tautology::ParamSet$test_constraint()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-test_constraint_dt}{\code{tautology::ParamSet$test_constraint_dt()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-test_dt}{\code{tautology::ParamSet$test_dt()}}\out{} -\item \out{}\href{../../tautology/html/ParamSet.html#method-trafo}{\code{tautology::ParamSet$trafo()}}\out{} -} -\out{
} -} +\item \href{#method-ParamSetCollection-new}{\code{ParamSetCollection$new()}} +\item \href{#method-ParamSetCollection-clone}{\code{ParamSetCollection$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSetCollection-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -113,8 +113,8 @@ Default \code{FALSE}.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSetCollection-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Sampler.Rd b/man/Sampler.Rd index 3004597d..08b3a4ca 100644 --- a/man/Sampler.Rd +++ b/man/Sampler.Rd @@ -29,16 +29,16 @@ Domain / support of the distribution we want to sample from.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Sampler$new()}} -\item \href{#method-sample}{\code{Sampler$sample()}} -\item \href{#method-format}{\code{Sampler$format()}} -\item \href{#method-print}{\code{Sampler$print()}} -\item \href{#method-clone}{\code{Sampler$clone()}} +\item \href{#method-Sampler-new}{\code{Sampler$new()}} +\item \href{#method-Sampler-sample}{\code{Sampler$sample()}} +\item \href{#method-Sampler-format}{\code{Sampler$format()}} +\item \href{#method-Sampler-print}{\code{Sampler$print()}} +\item \href{#method-Sampler-clone}{\code{Sampler$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. @@ -59,8 +59,8 @@ ParamSet is cloned on construction.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-sample}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler-sample}{}}} \subsection{Method \code{sample()}}{ Sample \code{n} values from the distribution. \subsection{Usage}{ @@ -79,8 +79,8 @@ Sample \code{n} values from the distribution. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-format}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler-format}{}}} \subsection{Method \code{format()}}{ Helper for print outputs. \subsection{Usage}{ @@ -89,8 +89,8 @@ Helper for print outputs. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler-print}{}}} \subsection{Method \code{print()}}{ Printer. \subsection{Usage}{ @@ -106,8 +106,8 @@ Printer. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Sampler1D.Rd b/man/Sampler1D.Rd index 63c897d1..77f561ff 100644 --- a/man/Sampler1D.Rd +++ b/man/Sampler1D.Rd @@ -33,22 +33,22 @@ Returns the one Parameter that is sampled from.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Sampler1D$new()}} -\item \href{#method-clone}{\code{Sampler1D$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-Sampler1D-new}{\code{Sampler1D$new()}} +\item \href{#method-Sampler1D-clone}{\code{Sampler1D$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1D-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. @@ -68,8 +68,8 @@ Domain / support of the distribution we want to sample from.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1D-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Sampler1DCateg.Rd b/man/Sampler1DCateg.Rd index a398299c..12f561b5 100644 --- a/man/Sampler1DCateg.Rd +++ b/man/Sampler1DCateg.Rd @@ -32,22 +32,22 @@ Numeric vector of \code{param$nlevels} probabilities.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Sampler1DCateg$new()}} -\item \href{#method-clone}{\code{Sampler1DCateg$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-Sampler1DCateg-new}{\code{Sampler1DCateg$new()}} +\item \href{#method-Sampler1DCateg-clone}{\code{Sampler1DCateg$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DCateg-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -67,8 +67,8 @@ Numeric vector of \code{param$nlevels} probabilities, which is uniform by defaul } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DCateg-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Sampler1DNormal.Rd b/man/Sampler1DNormal.Rd index 82443aa0..51e43646 100644 --- a/man/Sampler1DNormal.Rd +++ b/man/Sampler1DNormal.Rd @@ -35,22 +35,22 @@ SD parameter of the normal distribution.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Sampler1DNormal$new()}} -\item \href{#method-clone}{\code{Sampler1DNormal$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-Sampler1DNormal-new}{\code{Sampler1DNormal$new()}} +\item \href{#method-Sampler1DNormal-clone}{\code{Sampler1DNormal$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DNormal-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -75,8 +75,8 @@ Default is \code{(param$upper - param$lower)/4}.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DNormal-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Sampler1DRfun.Rd b/man/Sampler1DRfun.Rd index 8d5696ce..5f33d932 100644 --- a/man/Sampler1DRfun.Rd +++ b/man/Sampler1DRfun.Rd @@ -35,22 +35,22 @@ Random number generator function.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Sampler1DRfun$new()}} -\item \href{#method-clone}{\code{Sampler1DRfun$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-Sampler1DRfun-new}{\code{Sampler1DRfun$new()}} +\item \href{#method-Sampler1DRfun-clone}{\code{Sampler1DRfun$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DRfun-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -73,8 +73,8 @@ Random number generator function, e.g. \code{rexp} to sample from exponential di } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DRfun-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Sampler1DUnif.Rd b/man/Sampler1DUnif.Rd index 3ec0f659..08ef3bce 100644 --- a/man/Sampler1DUnif.Rd +++ b/man/Sampler1DUnif.Rd @@ -24,22 +24,22 @@ Other Sampler: \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Sampler1DUnif$new()}} -\item \href{#method-clone}{\code{Sampler1DUnif$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-Sampler1DUnif-new}{\code{Sampler1DUnif$new()}} +\item \href{#method-Sampler1DUnif-clone}{\code{Sampler1DUnif$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DUnif-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -56,8 +56,8 @@ Domain / support of the distribution we want to sample from.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Sampler1DUnif-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/SamplerHierarchical.Rd b/man/SamplerHierarchical.Rd index 45a90a8b..8e47d4a4 100644 --- a/man/SamplerHierarchical.Rd +++ b/man/SamplerHierarchical.Rd @@ -34,22 +34,22 @@ List of \link{Sampler1D} objects that gives a Sampler for each \link{Param} in t \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{SamplerHierarchical$new()}} -\item \href{#method-clone}{\code{SamplerHierarchical$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-SamplerHierarchical-new}{\code{SamplerHierarchical$new()}} +\item \href{#method-SamplerHierarchical-clone}{\code{SamplerHierarchical$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-SamplerHierarchical-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -70,8 +70,8 @@ List of \link{Sampler1D} objects that gives a Sampler for each \link{Param} in t } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-SamplerHierarchical-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/SamplerJointIndep.Rd b/man/SamplerJointIndep.Rd index 2b6219a4..650f701f 100644 --- a/man/SamplerJointIndep.Rd +++ b/man/SamplerJointIndep.Rd @@ -32,22 +32,22 @@ List of \link{Sampler} objects.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{SamplerJointIndep$new()}} -\item \href{#method-clone}{\code{SamplerJointIndep$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-SamplerJointIndep-new}{\code{SamplerJointIndep$new()}} +\item \href{#method-SamplerJointIndep-clone}{\code{SamplerJointIndep$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-SamplerJointIndep-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -64,8 +64,8 @@ List of \link{Sampler} objects.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-SamplerJointIndep-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/SamplerUnif.Rd b/man/SamplerUnif.Rd index be5a2d47..27d80ad9 100644 --- a/man/SamplerUnif.Rd +++ b/man/SamplerUnif.Rd @@ -26,22 +26,22 @@ Other Sampler: \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{SamplerUnif$new()}} -\item \href{#method-clone}{\code{SamplerUnif$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../tautology/html/Sampler.html#method-format}{\code{tautology::Sampler$format()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-print}{\code{tautology::Sampler$print()}}\out{} -\item \out{}\href{../../tautology/html/Sampler.html#method-sample}{\code{tautology::Sampler$sample()}}\out{} -} -\out{
} -} +\item \href{#method-SamplerUnif-new}{\code{SamplerUnif$new()}} +\item \href{#method-SamplerUnif-clone}{\code{SamplerUnif$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-SamplerUnif-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Usage}{ @@ -59,8 +59,8 @@ ParamSet is cloned on construction.} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-SamplerUnif-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/tests/testthat/helper_02_ParamSet.R b/tests/testthat/helper_02_ParamSet.R index c5f4074b..1bddec3a 100644 --- a/tests/testthat/helper_02_ParamSet.R +++ b/tests/testthat/helper_02_ParamSet.R @@ -1,58 +1,45 @@ th_paramset_dbl1 = function() { - ParamSet_legacy$new( - params = list( - th_param_dbl() - ) - ) + th_param_dbl() } th_paramset_full = function() { - ParamSet_legacy$new( - params = list( - th_param_int(), - th_param_dbl(), - th_param_fct(), - th_param_lgl() - ) + c( + th_param_int(), + th_param_dbl(), + th_param_fct(), + th_param_lgl() ) } th_paramset_untyped = function() { - ParamSet_legacy$new( - params = list(th_param_uty()) - ) + th_param_uty() } th_paramset_numeric = function() { - ParamSet_legacy$new( - params = list( - th_param_int(), - th_param_dbl() - ) + c( + th_param_int(), + th_param_dbl() ) } th_paramset_categorical = function() { - ParamSet_legacy$new( - params = list( - th_param_fct(), - th_param_lgl() - ) + c( + th_param_fct(), + th_param_lgl() ) } th_paramset_repeated = function() { - ps = ParamSet_legacy$new( - params = c( - list(th_param_nat(), th_param_fct()) - ) + c( + th_param_nat(), + th_param_fct(), + ps_replicate(th_param_dbl_na(), 4) ) - ps$add(th_param_dbl_na()$rep(4L)) } th_paramset_deps = function() { ps = th_paramset_full() - ps$add_dep("th_param_fct", on = "th_param_lgl", CondEqual$new(TRUE)) - ps$add_dep("th_param_dbl", on = "th_param_fct", CondAnyOf$new(c("a", "b"))) - return(ps) + ps$add_dep("th_param_fct", on = "th_param_lgl", CondEqual(TRUE)) + ps$add_dep("th_param_dbl", on = "th_param_fct", CondAnyOf(c("a", "b"))) + ps } diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R index d29785ac..115d7889 100644 --- a/tests/testthat/helper_03_domain.R +++ b/tests/testthat/helper_03_domain.R @@ -4,25 +4,18 @@ expect_equal_ps = function(a, b) { assert_class(a, "ParamSet") assert_class(b, "ParamSet") - a = a$clone(deep = TRUE) - a$params - a$tags - b = b$clone(deep = TRUE) - b$params - b$tags - a$.__enclos_env__$private$.params_unid = lapply(a$.__enclos_env__$private$.params_unid, function(x) { - x = x$clone(deep = TRUE) - x$.__enclos_env__$private$.id = "" - x - }) - b$.__enclos_env__$private$.params_unid = lapply(b$.__enclos_env__$private$.params_unid, function(x) { - x = x$clone(deep = TRUE) - x$.__enclos_env__$private$.id = "" - x - }) - a = as.list(a) - b = as.list(b) - expect_equal(a, b) + normalize_ids = function(original) { + acl = original$clone(deep = TRUE) + acp = acl$.__enclos_env__$private + acp$.params$id = sprintf("x%s", seq_len(original$length)) + names(acp$.values) = sprintf("x%s", match(names(original$values), original$ids())) + acp$.tags = copy(acp$.tags)[, id := sprintf("x%s", match(id, original$ids()))] + acp$.trafos = copy(acp$.trafos)[, id := sprintf("x%s", match(id, original$ids()))] + acp$.deps[, id := sprintf("x%s", match(id, original$ids()))] + acl + } + + expect_equal(normalize_ids(a), normalize_ids(b)) } diff --git a/tests/testthat/helper_compat.R b/tests/testthat/helper_compat.R index be501bb2..3a9149da 100644 --- a/tests/testthat/helper_compat.R +++ b/tests/testthat/helper_compat.R @@ -47,7 +47,7 @@ ParamUty = list( ) ParamSet_legacy = list( - new = function(params) { + new = function(params = list()) { ps_union(params) } ) diff --git a/tests/testthat/test_Condition.R b/tests/testthat/test_Condition.R index 952f61fe..ec011afe 100644 --- a/tests/testthat/test_Condition.R +++ b/tests/testthat/test_Condition.R @@ -1,20 +1,20 @@ context("Condition") test_that("Condition", { - cond = CondEqual$new("a") - y = cond$test(c("a", "b", "c", NA_character_)) + cond = CondEqual("a") + y = condition_test(cond, c("a", "b", "c", NA_character_)) expect_equal(y, c(TRUE, FALSE, FALSE, FALSE)) expect_output(print(cond), fixed = "CondEqual") - expect_error(CondEqual$new(c("a","b")), "Assertion on 'rhs' failed") - expect_error(CondEqual$new(NA), "Assertion on 'rhs' failed") + expect_error(CondEqual(c("a","b")), "Assertion on 'rhs' failed") + expect_error(CondEqual(NA), "Assertion on 'rhs' failed") - cond = CondAnyOf$new(c("a", "b")) - y = cond$test(c("a", "b", "c", NA_character_)) + cond = CondAnyOf(c("a", "b")) + y = condition_test(cond, c("a", "b", "c", NA_character_)) expect_equal(y, c(TRUE, TRUE, FALSE, FALSE)) expect_output(print(cond), fixed = "CondAnyOf") - expect_error(CondAnyOf$new(list("a","b")), "Assertion on 'rhs' failed") - expect_error(CondAnyOf$new(c("a", "b", NA_character_)), "Assertion on 'rhs' failed") - expect_error(CondAnyOf$new(character()), "Assertion on 'rhs' failed") + expect_error(CondAnyOf(list("a","b")), "Assertion on 'rhs' failed") + expect_error(CondAnyOf(c("a", "b", NA_character_)), "Assertion on 'rhs' failed") + expect_error(CondAnyOf(character()), "Assertion on 'rhs' failed") }) diff --git a/tests/testthat/test_Param.R b/tests/testthat/test_Param.R index d0e74f33..68bcf90d 100644 --- a/tests/testthat/test_Param.R +++ b/tests/testthat/test_Param.R @@ -6,10 +6,10 @@ test_that("basic properties", { p2 = ParamFct$new("y", levels = c("a", "b")) expect_equal(p1$default, list(x = 4)) expect_equal(p2$default, named_list()) - expect_true(p1$is_number) - expect_false(p2$is_number) - expect_false(p1$is_categ) - expect_true(p2$is_categ) + expect_true(p1$is_number[["x"]]) + expect_false(p2$is_number[["y"]]) + expect_false(p1$is_categ[["x"]]) + expect_true(p2$is_categ[["y"]]) }) test_that("check and assert work", { @@ -17,7 +17,7 @@ test_that("check and assert work", { # here we briefly check all 3 to see if they work in principle p = ParamDbl$new("x", lower = 1, upper = 2) p$assert(list(x = 1)) - expect_error(p$assert(list(x = 3))) + expect_error(p$assert(list(x = 3)), "Assertion .* failed") expect_true(p$check(list(x = 1))) expect_string(p$check(list(x = 3)), fixed = "<= 2") expect_true(p$test(list(x = 1))) @@ -54,12 +54,3 @@ test_that("special_vals work for all Param subclasses", { test_that("we cannot create Params with non-strict R names", { expect_error(ParamInt$new(id = "$foo"), "does not comply") }) - -test_that("printer works", { - for (p in th_paramset_full()$params) { - info = p$id - s = capture_output(print(p)) - expect_true(grepl(p$id, s, fixed = TRUE), info = info) - expect_true(grepl(class(p)[1L], s, fixed = TRUE), info = info) - } -}) diff --git a/tests/testthat/test_ParamDbl.R b/tests/testthat/test_ParamDbl.R index 0ceb4f26..d9c3a005 100644 --- a/tests/testthat/test_ParamDbl.R +++ b/tests/testthat/test_ParamDbl.R @@ -44,7 +44,7 @@ test_that("qunif", { p = ParamDbl$new("x", lower = a, upper = b) u = runif(n) v1 = p$qunif(data.table(x = u)) - expect_data_table(v1, ncols = 1, nrow = n) + expect_data_table(v1, ncols = 1, nrows = n) expect_equal(colnames(v1), "x") expect_double(v1$x, any.missing = FALSE, len = n) v2 = runif(n, min = a, max = b) @@ -66,7 +66,7 @@ test_that("tolerance in check allows values at the upper bound", { }) test_that("tolerance for setting values", { - p = ParamSet$new(list(ParamDbl$new("x", lower = 0, upper = 1))) + p = ParamSet_legacy$new(list(ParamDbl$new("x", lower = 0, upper = 1))) p$values$x = -1e-8 expect_equal(p$values$x, 0) expect_error({p$values$x = -1e-6}, "Element 1 is not >=") diff --git a/tests/testthat/test_ParamFct.R b/tests/testthat/test_ParamFct.R index d75d2f6c..d855f3eb 100644 --- a/tests/testthat/test_ParamFct.R +++ b/tests/testthat/test_ParamFct.R @@ -2,8 +2,8 @@ context("ParamFct") test_that("test if ParamFct constructor works", { p = ParamFct$new(id = "test", levels = c("a", "b")) - expect_equal(p$levels, c("a", "b")) - expect_equal(p$nlevels, 2L) + expect_equal(p$levels$test, c("a", "b")) + expect_equal(p$nlevels[["test"]], 2L) # we dont allow NAs as levels expect_error(ParamFct$new(id = "test", levels = c("a", NA))) @@ -15,9 +15,9 @@ test_that("qunif", { p = ParamFct$new("x", levels = vals) k = p$nlevels u = runif(n) - v1 = p$qunif(u) + v1 = p$qunif(data.frame(x = u))$x expect_character(v1, any.missing = FALSE, len = n) - expect_setequal(unique(v1), p$levels) # check we see all levels + expect_setequal(unique(v1), p$levels$x) # check we see all levels # check that empirical frequencies are pretty much uniform freqs = prop.table(table(v1)) p = rep(1 / k, k) diff --git a/tests/testthat/test_ParamInt.R b/tests/testthat/test_ParamInt.R index f2890842..1d5b28f8 100644 --- a/tests/testthat/test_ParamInt.R +++ b/tests/testthat/test_ParamInt.R @@ -2,15 +2,15 @@ context("ParamInt") test_that("constructor works", { p = ParamInt$new(id = "test", lower = 1L, upper = 10L) - expect_equal(p$id, "test") - expect_equal(p$lower, 1L) - expect_equal(p$upper, 10L) - expect_equal(p$nlevels, 10L) + expect_equal(p$ids(), "test") + expect_equal(p$lower[["test"]], 1L) + expect_equal(p$upper[["test"]], 10L) + expect_equal(p$nlevels[["test"]], 10L) # check that we can create param with Inf bounds p = ParamInt$new(id = "test", lower = 1L) - expect_equal(p$lower, 1L) - expect_equal(p$upper, Inf) + expect_equal(p$lower[["test"]], 1L) + expect_equal(p$upper[["test"]], Inf) # check some invalid arg settings expect_error(ParamInt$new(id = "x", lower = NULL), "not 'NULL'") @@ -32,10 +32,10 @@ test_that("qunif", { testit = function(a, b) { p = ParamInt$new("x", lower = a, upper = b) - k = p$nlevels + k = p$nlevels[["x"]] expect_equal(k, b - a + 1) u = runif(n) - v1 = p$qunif(u) + v1 = p$qunif(data.frame(x = u))$x expect_integer(v1, any.missing = FALSE, len = n) expect_setequal(unique(v1), a:b) # check we see all levels # check that empirical frequencies are pretty much uniform @@ -49,9 +49,9 @@ test_that("qunif", { test_that("assigning integer value results in int", { - p = ParamSet$new(list(ParamInt$new("x"))) + p = ParamSet_legacy$new(list(ParamInt$new("x"))) p$values$x = 0 expect_equal(typeof(p$values$x), "integer") - expect_error({p$values$x = 1e-10}, "Must be of type.*integerish") + expect_error({p$values$x = 1e-2}, "Must be of type.*integerish") }) diff --git a/tests/testthat/test_ParamLgl.R b/tests/testthat/test_ParamLgl.R index 78106d5c..0bcd225c 100644 --- a/tests/testthat/test_ParamLgl.R +++ b/tests/testthat/test_ParamLgl.R @@ -2,9 +2,9 @@ context("ParamLgl") test_that("constructor works", { p = ParamLgl$new(id = "test") - expect_equal(p$id, "test") - expect_equal(p$nlevels, 2L) - expect_equal(p$levels, c(TRUE, FALSE)) + expect_equal(p$ids(), "test") + expect_equal(p$nlevels[["test"]], 2L) + expect_equal(p$levels[["test"]], c(TRUE, FALSE)) }) test_that("qunif", { @@ -12,9 +12,9 @@ test_that("qunif", { testit = function() { p = ParamLgl$new("x") u = runif(n) - v1 = p$qunif(u) + v1 = p$qunif(data.frame(x = u))$x expect_logical(v1, any.missing = FALSE, len = n) - expect_setequal(unique(v1), p$levels) # check we see all levels + expect_setequal(unique(v1), p$levels[["x"]]) # check we see all levels # check that empirical frequencies are pretty much uniform freqs = prop.table(table(v1)) p = c(1 / 2, 1 / 2) diff --git a/tests/testthat/test_ParamSet.R b/tests/testthat/test_ParamSet.R index 03c41913..31c52887 100644 --- a/tests/testthat/test_ParamSet.R +++ b/tests/testthat/test_ParamSet.R @@ -9,7 +9,7 @@ test_that("simple active bindings work", { th_paramset_numeric() ) for (ps in ps_list) { - info = ps$set_id + info = str_collapse(ps$class) expect_class(ps, "ParamSet", info = info) expect_int(ps$length, lower = 0L, info = info) expect_character(ps$ids(), info = info) @@ -23,7 +23,9 @@ test_that("simple active bindings work", { expect_names(names(ps$upper), identical.to = ps$ids(), info = info) expect_list(ps$levels, info = info) expect_names(names(ps$levels), identical.to = ps$ids(), info = info) - expect_flag(ps$is_bounded, info = info) + expect_logical(ps$is_bounded, any.missing = FALSE, info = info) + expect_names(names(ps$is_bounded), identical.to = ps$ids(), info = info) + expect_flag(ps$all_bounded, info = info) expect_numeric(ps$nlevels, any.missing = FALSE, lower = 1, info = info) expect_list(ps$tags, types = "character", info = info) expect_names(names(ps$tags), identical.to = ps$ids(), info = info) @@ -31,7 +33,7 @@ test_that("simple active bindings work", { expect_names(names(ps$default), subset.of = ps$ids(), info = info) } ps = th_paramset_full() - expect_output(print(ps), "") + expect_output(print(ps), fixed = "") expect_equal(ps$ids(), c("th_param_int", "th_param_dbl", "th_param_fct", "th_param_lgl")) expect_equal(ps$lower, c(th_param_int = -10, th_param_dbl = -10, th_param_fct = NA_real_, th_param_lgl = NA_real_)) expect_equal(ps$upper, c(th_param_int = 10, th_param_dbl = 10, th_param_fct = NA_real_, th_param_lgl = NA_real_)) @@ -42,20 +44,20 @@ test_that("ParamSet$subset", { getps = function() th_paramset_full()$clone(deep = TRUE) # give us a fresh clone of the fullset # we can subset to an empty set ps = getps() - ps$subset(character(0L)) + ps = ps$subset(character(0L)) expect_true(ps$is_empty) ps = getps() # subsetting to 2 params make the set smaller - ps$subset(ids[2:3]) + ps = ps$subset(ids[2:3]) expect_equal(ps$ids(), ids[2:3]) expect_equal(ps$length, 2) # subsetting to all ids does not change anything ps = getps() - ps$subset(ids) + ps = ps$subset(ids) expect_equal(as.data.table(ps), as.data.table(getps())) # subset full set to 2 numeric params ps = getps() - ps$subset(c("th_param_int", "th_param_dbl")) + ps = ps$subset(c("th_param_int", "th_param_dbl")) expect_equal(as.data.table(ps), as.data.table(th_paramset_numeric())) }) @@ -64,20 +66,20 @@ test_that("ParamSet$add_param_set", { ps1 = ParamSet$new() n1 = ps1$length ps2 = ParamSet$new() - ps1$add(ps2) + ps1 = ps_union(list(ps1, ps2)) expect_equal(ps1$length, n1) - ps2$add(ps1) + ps2 = ps_union(list(ps2, ps1)) expect_equal(ps2$length, n1) # adding 2 sets, numeric and untyped, makes them larger ps1 = th_paramset_numeric()$clone(deep = TRUE) ps2 = th_paramset_untyped()$clone(deep = TRUE) - ps1$add(ps2) + ps1 = ps_union(list(ps1, ps2)) expect_equal(ps2$length, 1L) expect_equal(ps1$ids(), c("th_param_int", "th_param_dbl", "th_param_uty")) ps1 = th_paramset_numeric()$clone(deep = TRUE) ps2 = th_paramset_untyped()$clone(deep = TRUE) - ps2$add(ps1) + ps2 = ps_union(list(ps2, ps1)) expect_equal(ps2$ids(), c("th_param_uty", "th_param_int", "th_param_dbl")) expect_equal(ps1$length, 2L) }) @@ -102,32 +104,30 @@ test_that("ParamSet$check", { expect_true(ps$check(list(th_param_dbl = 5))) expect_true(ps$check(list(th_param_int = 5))) - ps = ParamLgl$new("x")$rep(2) - ps$add_dep("x_rep_1", "x_rep_2", CondEqual$new(TRUE)) - expect_string(ps$check(list(x_rep_1 = FALSE, x_rep_2 = FALSE)), fixed = "x_rep_2 = TRUE") + ps = ps_replicate(ParamLgl$new("x"), 2) + ps$add_dep("rep1.x", "rep2.x", CondEqual(TRUE)) + expect_string(ps$check(list(rep1.x = FALSE, rep2.x = FALSE), check_strict = TRUE), fixed = "rep2.x == TRUE") }) test_that("we cannot create ParamSet with non-strict R names", { - ps = ParamSet$new() - expect_error(ps$set_id <- "$foo", "Must comply") + expect_error(ParamDbl$new("$foo"), "does not comply") }) test_that("ParamSets cannot have duplicated ids", { p1 = ParamDbl$new("x1") p2 = ParamDbl$new("x1") - expect_error(ParamSet$new(list(p1, p2)), "duplicated") - ps = ParamSet$new(list(p1)) - expect_error(ps$add(p2), "duplicated") - expect_error(ps$add(ParamSet$new(list(p2))), "duplicated") + expect_error(ParamSet_legacy$new(list(p1, p2)), "duplicated") + ps = ParamSet_legacy$new(list(p1)) + expect_error(ps_union(list(ps, p2)), "duplicated") + expect_error(ps_union(list(ps, ParamSet_legacy$new(list(p2)))), "duplicated") }) test_that("ParamSet$print", { - ps = ParamSet$new() - ps$set_id = "foo" - expect_output(print(ps), "") + ps = ParamSet_legacy$new() + expect_output(print(ps), fixed = "") expect_output(print(ps), "Empty") ps = th_paramset_numeric() - expect_output(print(ps), "") + expect_output(print(ps), fixed = sprintf("", ps$length)) s = capture_output(print(ps)) expect_true(grepl("ParamInt", s, fixed = TRUE)) expect_true(grepl("ParamDbl", s, fixed = TRUE)) @@ -141,80 +141,83 @@ test_that("ParamSet$print", { th_paramset_untyped() ) for (ps in ps_list) { - expect_output(print(ps), "") + expect_output(print(ps), fixed = sprintf("", ps$length)) } }) -test_that("ParamSet does a delayed deep copy of params on construction", { +test_that("ParamSet does a deep copy of params on construction", { p = ParamDbl$new("x", lower = 1, upper = 3) - ps = ParamSet$new(list(y = p), ignore_ids = TRUE) - expect_equal(ps$params_unid, list(y = p)) - expect_equal(ps$params, list(y = ParamDbl$new("y", lower = 1, upper = 3))) + ps = ParamSet_legacy$new(list(y = p)) + p$values = list(x = 1) + ps$values = list(y.x = 2) + expect_equal(p$values, list(x = 1)) + expect_equal(ps$values, list(y.x = 2)) }) -test_that("ParamSet does a delayed deep copy of param on add", { +test_that("ParamSet does a deep copy of param on add", { p = ParamDbl$new("x", lower = 1, upper = 3) - ps = ParamSet$new(list())$add(ParamSet$new(list(y = p), ignore_ids = TRUE)) - expect_equal(ps$params_unid, list(y = p)) - expect_equal(ps$params, list(y = ParamDbl$new("y", lower = 1, upper = 3))) - + ps = ps_union(list(ParamSet_legacy$new(list()), ParamSet_legacy$new(list(y = p)))) + p$values = list(x = 1) + ps$values = list(y.x = 2) + expect_equal(p$values, list(x = 1)) + expect_equal(ps$values, list(y.x = 2)) }) test_that("ParamSet$clone can be deep", { - p1 = ParamDbl$new("x", lower = 1, upper = 3) + p1 = c(ParamDbl$new("x", lower = 1, upper = 3), ParamDbl$new("foo", lower = -10, upper = 10)) p2 = ParamFct$new("y", levels = c("a", "b")) - ps1 = ParamSet$new(list(p1, p2)) + ps1 = ParamSet_legacy$new(list(p1, p2)) ps2 = ps1$clone(deep = TRUE) p3 = ParamFct$new("z", levels = c("a", "b")) - ps2$add(p3) + ps2 = ps_union(list(ps2, p3)) - expect_equal(ps2$params, list(x = p1, y = p2, z = p3)) - expect_equal(ps1$params, list(x = p1, y = p2)) + expect_equal(ps2$params, ps_union(list(p1, p2, p3))$params) + expect_equal(ps1$params, ps_union(list(p1, p2))$params) # now lets add a dep, see if that gets clones properly - ps1$add_dep("x", on = "y", CondEqual$new("a")) + ps1$add_dep("x", on = "y", CondEqual("a")) ps2 = ps1$clone(deep = TRUE) d = ps2$deps$id[1] = "foo" expect_equal(ps2$deps$id[1], "foo") expect_equal(ps1$deps$id[1], "x") - ps = ParamSet$new() + ps = ParamSet_legacy$new() expect_equal(ps, ps$clone(deep = TRUE)) }) test_that("ParamSet$is_bounded", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1, upper = 3) )) - expect_true(ps$is_bounded) - ps = ParamSet$new(list( + expect_true(ps$all_bounded) + ps = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1, upper = 3), ParamLgl$new("y") )) - expect_true(ps$is_bounded) - ps = ParamSet$new(list( + expect_true(ps$all_bounded) + ps = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1), ParamLgl$new("y") )) - expect_false(ps$is_bounded) + expect_false(ps$all_bounded) }) test_that("ParamSet$add_param", { - ps = ParamSet$new(list()) - ps$add(ParamDbl$new("x", lower = 1)) + ps = ParamSet_legacy$new(list()) + ps = ps_union(list(ps, ParamDbl$new("x", lower = 1))) expect_equal(ps$length, 1L) expect_equal(ps$ids(), "x") expect_equal(ps$lower, c(x = 1)) - ps$add(ParamFct$new("y", levels = c("a"))) + ps = ps_union(list(ps, ParamFct$new("y", levels = c("a")))) expect_equal(ps$length, 2L) expect_equal(ps$ids(), c("x", "y")) expect_equal(ps$lower, c(x = 1, y = NA)) }) test_that("as.data.table", { - d = as.data.table(ParamSet$new()) + d = as.data.table(ParamSet_legacy$new()) expect_data_table(d, nrows = 0) ps = th_paramset_full() d = as.data.table(ps) @@ -225,14 +228,14 @@ test_that("as.data.table", { }) test_that("ParamSet$default", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1, upper = 3, default = 2), ParamInt$new("y", lower = 1, upper = 3) )) expect_equal(ps$default, list(x = 2)) expect_error(ParamDbl$new("x", lower = 1, upper = 3, default = 4)) expect_error(ParamDbl$new("x", lower = 1, upper = 3, default = NULL)) - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1, upper = 3, special_vals = list(NULL), default = NULL), ParamInt$new("y", lower = 1, upper = 3) )) @@ -251,7 +254,7 @@ test_that("is_number / is_categ / all_numeric / all_categoric", { }) test_that("ParamSet$ids", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new(id = "x", lower = 1, tags = c("t1")), ParamInt$new(id = "y", lower = 1, upper = 2), ParamFct$new(id = "z", levels = letters[1:3], tags = c("t1")) @@ -259,11 +262,12 @@ test_that("ParamSet$ids", { expect_equal(ps$ids(), c("x", "y", "z")) expect_equal(ps$ids(class = c("ParamInt", "ParamFct")), c("y", "z")) expect_equal(ps$ids(class = c("ParamInt", "ParamFct"), tags = "t1"), c("z")) - expect_equal(ps$ids(is_bounded = TRUE), c("y", "z")) + expect_equal(ps$ids(class = c("ParamInt", "ParamFct"), any_tags = c("t1", "t2")), c("z")) + expect_equal(ps$ids(class = c("ParamInt", "ParamFct"), tags = c("t1", "t2")), character(0)) }) test_that("ParamSet$get_values", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new(id = "x", lower = 1, tags = c("t1")), ParamInt$new(id = "y", lower = 1, upper = 2), ParamFct$new(id = "z", levels = letters[1:3], tags = c("t1")) @@ -272,15 +276,13 @@ test_that("ParamSet$get_values", { expect_equal(ps$get_values(class = c("ParamInt", "ParamFct")), named_list()) ps$values$x = 1 expect_equal(ps$get_values(class = c("ParamInt", "ParamFct")), named_list()) - expect_equal(ps$get_values(is_bounded = TRUE), named_list()) ps$values$y = 2 expect_equal(ps$get_values(), list(x = 1, y = 2)) expect_equal(ps$get_values(class = c("ParamInt", "ParamFct")), list(y = 2)) - expect_equal(ps$get_values(is_bounded = TRUE), list(y = 2)) }) test_that("required tag", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new(id = "x", tags = c("required")), ParamInt$new(id = "y") )) @@ -298,16 +300,16 @@ test_that("required tag", { }) test_that("required tag, empty param set (#219)", { - ps = ParamSet$new() + ps = ParamSet_legacy$new() ps$ids() expect_identical(ps$ids(tags = "required"), character(0)) }) test_that("paramset clones properly", { - ps = ParamSet$new() - ps$add(ParamFct$new("a", levels = letters[1:3])) - ps$add(ParamFct$new("b", levels = letters[1:3])) - ps$add_dep("a", "b", CondAnyOf$new(letters[1:2])) + ps = ParamSet_legacy$new() + ps = ps_union(list(ps, ParamFct$new("a", levels = letters[1:3]))) + ps = ps_union(list(ps, ParamFct$new("b", levels = letters[1:3]))) + ps$add_dep("a", "b", CondAnyOf(letters[1:2])) ps2 = ps$clone(deep = TRUE) expect_equal(ps, ps2) @@ -332,27 +334,27 @@ test_that("ParamSet$check_dt", { expect_character(ps$check_dt(xdt), fixed = "th_param_int: Element 1 is not <= 10") xdt = data.table(th_param_dbl = c(1, 1), new_param = c(1, 20)) expect_character(ps$check_dt(xdt), fixed = "not available") - ps = ParamLgl$new("x")$rep(2) - ps$add_dep("x_rep_2", "x_rep_1", CondEqual$new(TRUE)) - xdt = data.table(x_rep_1 = c(TRUE, TRUE), x_rep_2 = c(FALSE, TRUE)) - expect_true(ps$check_dt(xdt)) - xdt = data.table(x_rep_1 = c(TRUE, TRUE, FALSE), x_rep_2 = c(FALSE, TRUE, FALSE)) - expect_character(ps$check_dt(xdt), fixed = "x_rep_1 = TRUE") - xdt = data.table(x_rep_1 = c(TRUE, TRUE, FALSE), x_rep_2 = c(FALSE, TRUE, NA)) - expect_true(ps$check_dt(xdt)) + ps = ps_replicate(ParamLgl$new("x"), 2) + ps$add_dep("rep2.x", "rep1.x", CondEqual(TRUE)) + xdt = data.table(rep1.x = c(TRUE, TRUE), rep2.x = c(FALSE, TRUE)) + expect_true(ps$check_dt(xdt, check_strict = TRUE)) + xdt = data.table(rep1.x = c(TRUE, TRUE, FALSE), rep2.x = c(FALSE, TRUE, FALSE)) + expect_character(ps$check_dt(xdt, check_strict = TRUE), fixed = "rep1.x == TRUE") + xdt = data.table(rep1.x = c(TRUE, TRUE, FALSE), rep2.x = c(FALSE, TRUE, NA)) + expect_true(ps$check_dt(xdt, check_strict = TRUE)) }) test_that("rd_info.ParamSet", { - ps = ParamSet$new() + ps = ParamSet_legacy$new() expect_character(rd_info(ps)) - ps$add(ParamFct$new("a", levels = letters[1:3])) + ps = ps_union(list(ps, ParamFct$new("a", levels = letters[1:3]))) expect_character(rd_info(ps)) }) test_that("ParamSet$values convert nums to ints for ParamInt", { pp = ParamInt$new("x") - ps = ParamSet$new(list(pp)) + ps = ParamSet_legacy$new(list(pp)) ps$values$x = 2 expect_class(ps$values$x, "integer") }) diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index f46bdcc5..5812f25b 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -2,12 +2,9 @@ context("ParamSetCollection") test_that("ParamSet basic stuff works", { ps1 = th_paramset_dbl1() - ps1$set_id = "s1" ps2 = th_paramset_full() - ps2$set_id = "s2" ps3 = th_paramset_dbl1() - ps3$set_id = "" - psc = ParamSetCollection$new(list(ps1, ps2, ps3)) + psc = ParamSetCollection$new(list(s1 = ps1, s2 = ps2, ps3)) ps1clone = ps1$clone(deep = TRUE) ps2clone = ps2$clone(deep = TRUE) @@ -21,9 +18,10 @@ test_that("ParamSet basic stuff works", { expect_class(psc, "ParamSetCollection") expect_equal(psc$length, ps1$length + ps2$length + ps3$length) # check that param internally in collection is constructed correctly - p = psc$params[[2L]] - p = p$with_id("th_param_int") - expect_equal(p, ps2$params[[1L]]) + p = psc$params[2L] + p$id = "th_param_int" + + expect_equal(p, ps2$params[1L]) expect_equal(psc$ids(), c(paste0("s1.", ps1$ids()), paste0("s2.", ps2$ids()), ps3$ids())) expect_equal(psc$lower, my_c(ps1$lower, ps2$lower, ps3$lower)) d = as.data.table(psc) @@ -41,12 +39,13 @@ test_that("ParamSet basic stuff works", { d = generate_design_random(psc, n = 10L) expect_data_table(d$data, nrows = 10, ncols = 6L) - psc$trafo = function(x, param_set) { + psflat = psc$flatten() + psflat$extra_trafo = function(x, param_set) { x$s2.th_param_int = 99 # nolint return(x) } - expect_true(psc$has_trafo) - d = generate_design_random(psc, n = 10L) + expect_true(psflat$has_trafo) + d = generate_design_random(psflat, n = 10L) expect_data_table(d$data, nrows = 10, ncols = 6L) xs = d$transpose(trafo = TRUE) for (i in 1:10) { @@ -56,16 +55,6 @@ test_that("ParamSet basic stuff works", { expect_equal(x$s2.th_param_int, 99) } - # Generate cached values for comparisons - ps1$params - ps2$params - ps1clone$params - ps2clone$params - ps1$tags - ps2$tags - ps1clone$tags - ps2clone$tags - # ps1 and ps2 should not be changed expect_equal(ps1, ps1clone) expect_equal(ps2, ps2clone) @@ -76,60 +65,46 @@ test_that("ParamSet basic stuff works", { expect_equal(ps1, ps1clone) expect_equal(ps2, ps2clone) - # adding a set - ps4 = ParamSet$new(list(ParamDbl$new("x"))) - ps4$set_id = "s4" - psc$add(ps4) + # adding a set (not really supported any more) + ps4 = ParamSet_legacy$new(list(ParamDbl$new("x"))) + psc = ps_union(list(psc, s4 = ps4)) expect_equal(psc$length, ps1$length + ps2$length + ps3$length + ps4$length) expect_equal(psc$ids(), c(paste0("s1.", ps1$ids()), paste0("s2.", ps2$ids()), ps3$ids(), paste0("s4.", ps4$ids()))) - psc$remove_sets("s1") - expect_equal(psc$length, ps2$length + ps3$length + ps4$length) - expect_equal(psc$ids(), c(paste0("s2.", ps2$ids()), ps3$ids(), paste0("s4.", ps4$ids()))) }) test_that("some operations are not allowed", { ps1 = th_paramset_dbl1() - ps1$set_id = "s1" ps2 = th_paramset_full() - ps2$set_id = "s2" - psc = ParamSetCollection$new(list(ps1, ps2)) + psc = ParamSetCollection$new(list(s1 = ps1, s2 = ps2)) - expect_error(psc$subset("foo"), "not allowed") - expect_error(psc$add(th_param_dbl()), "ParamSet") + expect_error(psc$subset("foo"), "Must be a subset of") }) test_that("deps", { - ps1 = ParamSet$new(list( + ps1 = ParamSet_legacy$new(list( ParamFct$new("f", levels = c("a", "b")), ParamDbl$new("d") )) - ps1$set_id = "ps1" - ps1$add_dep("d", on = "f", CondEqual$new("a")) + ps1$add_dep("d", on = "f", CondEqual("a")) - ps2 = ParamSet$new(list( + ps2 = ParamSet_legacy$new(list( ParamFct$new("f", levels = c("a", "b")), ParamDbl$new("d") )) - ps2$set_id = "ps2" ps1clone = ps1$clone(deep = TRUE) ps2clone = ps2$clone(deep = TRUE) - psc = ParamSetCollection$new(list(ps1, ps2)) + psc = ParamSetCollection$new(list(ps1 = ps1, ps2 = ps2)) d = psc$deps expect_data_table(d, nrows = 1, ncols = 3) expect_equal(d$id, c("ps1.d")) # check deps across sets - psc$add_dep("ps2.d", on = "ps1.f", CondEqual$new("a")) + psc$add_dep("ps2.d", on = "ps1.f", CondEqual("a")) expect_data_table(psc$deps, nrows = 2, ncols = 3) expect_true(psc$check(list(ps1.f = "a", ps1.d = 0, ps2.d = 0))) - expect_string(psc$check(list(ps2.d = 0))) - - ps1clone$tags - ps2clone$tags - ps1$tags - ps2$tags + expect_string(psc$check(list(ps2.d = 0), check_strict = TRUE)) # ps1 and ps2 should not be changed expect_equal(ps1clone, ps1) @@ -137,27 +112,25 @@ test_that("deps", { }) test_that("values", { - ps1 = ParamSet$new(list( + ps1 = ParamSet_legacy$new(list( ParamFct$new("f", levels = c("a", "b")), ParamDbl$new("d", lower = 1, upper = 8) )) - ps1$set_id = "foo" - ps2 = ParamSet$new(list( + ps2 = ParamSet_legacy$new(list( ParamFct$new("f", levels = c("a", "b")), ParamDbl$new("d", lower = 1, upper = 8) )) - ps2$set_id = "bar" - ps3 = ParamSet$new(list( + ps3 = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1, upper = 8) )) - ps4 = ParamSet$new(list( + ps4 = ParamSet_legacy$new(list( ParamDbl$new("y", lower = 1, upper = 8) )) ps1clone = ps1$clone(deep = TRUE) ps2clone = ps2$clone(deep = TRUE) - pcs = ParamSetCollection$new(list(ps1, ps2, ps3, ps4)) + pcs = ParamSetCollection$new(list(foo = ps1, bar = ps2, ps3, ps4)) expect_equal(pcs$values, named_list()) ps2$values = list(d = 3) expect_equal(pcs$values, list(bar.d = 3)) @@ -179,15 +152,6 @@ test_that("values", { setindex(ps1$deps, NULL) setindex(ps2$deps, NULL) - ps1$params - ps2$params - ps1clone$params - ps2clone$params - ps1$tags - ps2$tags - ps1clone$tags - ps2clone$tags - expect_equal(ps1clone, ps1) expect_equal(ps2clone, ps2) }) @@ -196,60 +160,39 @@ test_that("empty collections", { # no paramsets psc = ParamSetCollection$new(list()) expect_equal(psc$length, 0L) - expect_equal(psc$params, named_list()) + expect_equal(psc$subspaces(), named_list()) expect_equal(psc$ids(), character(0L)) expect_data_table(as.data.table(psc), nrows = 0L) # 1 empty paramset - psc = ParamSetCollection$new(list(ParamSet$new())) + psc = ParamSetCollection$new(list(ParamSet_legacy$new())) expect_equal(psc$length, 0L) - expect_equal(psc$params, named_list()) + expect_equal(psc$subspaces(), named_list()) expect_equal(psc$ids(), character(0L)) expect_data_table(as.data.table(psc), nrows = 0L) }) test_that("no problems if we name the list of sets", { - ps = ParamSet$new(list(ParamDbl$new("test1"))) - ps$set_id = "paramset" - psc = ParamSetCollection$new(list(prefix = ps)) - expect_equal(names(psc$params), "paramset.test1") + ps = ParamSet_legacy$new(list(ParamDbl$new("test1"))) + psc = ParamSetCollection$new(list(paramset = ps)) + expect_equal(names(psc$subspaces()), "paramset.test1") }) test_that("no warning in printer, see issue 208", { - ps = ParamSet$new(list(ParamDbl$new("test1"))) - ps$set_id = "paramset" - psc = ParamSetCollection$new(list(ps)) + ps = ParamSet_legacy$new(list(ParamDbl$new("test1"))) + + psc = ParamSetCollection$new(list(paramset = ps)) psc$values = list(paramset.test1 = 1) expect_warning(capture_output(print(ps)), NA) }) - -test_that("collection reflects direct paramset$set_id change", { - ps = ParamSet$new(list(ParamDbl$new("d"))) - ps$set_id = "paramset" - psc = ParamSetCollection$new(list(ps)) - ps$values = list(d = 1) - expect_equal(psc$values, list(paramset.d = 1)) - ps$set_id = "foo" - expect_equal(psc$values, list(foo.d = 1)) - expect_equal(psc$params, list(foo.d = ParamDbl$new("foo.d"))) - - ps$set_id = "" - expect_equal(psc$values, list(d = 1)) - expect_equal(psc$params, list(d = ParamDbl$new("d"))) -}) - - test_that("collection allows state-change setting of paramvals, see issue 205", { - ps1 = ParamSet$new(list(ParamDbl$new("d1"))) - ps1$set_id = "s1" - ps2 = ParamSet$new(list(ParamDbl$new("d2"))) - ps2$set_id = "s2" - ps3 = ParamSet$new(list(ParamDbl$new("d3"))) - ps3$set_id = "" - - psc = ParamSetCollection$new(list(ps1, ps2, ps3)) + ps1 = ParamSet_legacy$new(list(ParamDbl$new("d1"))) + ps2 = ParamSet_legacy$new(list(ParamDbl$new("d2"))) + ps3 = ParamSet_legacy$new(list(ParamDbl$new("d3"))) + + psc = ParamSetCollection$new(list(s1 = ps1, s2 = ps2, ps3)) expect_equal(psc$values, named_list()) psc$values$s1.d1 = 1 # nolint expect_equal(psc$values, list(s1.d1 = 1)) @@ -260,28 +203,23 @@ test_that("collection allows state-change setting of paramvals, see issue 205", }) test_that("set_id inference in values assignment works now", { - psa = ParamSet$new(list(ParamDbl$new("parama"))) - psa$set_id = "a.b" + psa = ParamSet_legacy$new(list(ParamDbl$new("parama"))) - psb = ParamSet$new(list(ParamDbl$new("paramb"))) - psb$set_id = "b" + psb = ParamSet_legacy$new(list(ParamDbl$new("paramb"))) - psc = ParamSet$new(list(ParamDbl$new("paramc"))) - psc$set_id = "c" + psc = ParamSet_legacy$new(list(ParamDbl$new("paramc"))) - pscol1 = ParamSetCollection$new(list(psb, psc)) - pscol1$set_id = "a" + pscol1 = ParamSetCollection$new(list(b = psb, c = psc)) - pscol2 = ParamSetCollection$new(list(psa, pscol1)) + pscol2 = ParamSetCollection$new(list(a.b = psa, a = pscol1)) - pstest = ParamSet$new(list(ParamDbl$new("paramc"))) - pstest$set_id = "a.c" + pstest = ParamSet_legacy$new(list(ParamDbl$new("paramc"))) - expect_error(pscol2$add(pstest), "nameclashes.* a\\.c\\.paramc") + expect_error(ps_union(list(pscol2, a.c = pstest)), "duplicated parameter names.* a\\.c\\.paramc") - pstest = ParamSet$new(list(ParamDbl$new("a.c.paramc"))) - pstest$set_id = "" - expect_error(pscol2$add(pstest), "nameclashes.* a\\.c\\.paramc") + pstest = ParamSet_legacy$new(list(ParamDbl$new("a.c.paramc"))) + + expect_error(ps_union(list(pscol2, pstest)), "duplicated parameter names.* a\\.c\\.paramc") pscol2$values = list(a.c.paramc = 3, a.b.parama = 1, a.b.paramb = 2) @@ -291,96 +229,6 @@ test_that("set_id inference in values assignment works now", { expect_equal(pscol1$values, list(b.paramb = 2, c.paramc = 3)) expect_equal(pscol2$values, list(a.b.parama = 1, a.b.paramb = 2, a.c.paramc = 3)) - expect_error(ParamSetCollection$new(list(pscol1, pstest)), + expect_error(ParamSetCollection$new(list(a = pscol1, pstest)), "duplicated parameter.* a\\.c\\.paramc") }) - -test_that("cloning and changing underlying params works", { - ps1 = th_paramset_dbl1() - ps1$set_id = "s1" - ps2 = th_paramset_full() - ps2$set_id = "s2" - ps3 = th_paramset_dbl1() - ps3$set_id = "" - psc1 = ParamSetCollection$new(list(ps1, ps2)) - psc2 = ParamSetCollection$new(list(psc1, ps3)) - - expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in any psc - expect_length(psc2$.__enclos_env__$private$.params, 0) - - expect_equal(psc2$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) - expect_equal(psc2$params_unid[6], ps3$params[1]) - expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in psc1 - expect_length(psc2$.__enclos_env__$private$.params, 0) - - pe = th_paramset_dbl1()$params[[1]]$with_id("s1.th_param_dbl") - expect_equal(psc2$params[1], list(s1.th_param_dbl = pe)) - - expect_length(psc1$.__enclos_env__$private$.params, 0) # has not created .params in psc1 - expect_length(psc2$.__enclos_env__$private$.params, 6) # but psc2 has .params now - - ps1$params[[1]]$.__enclos_env__$private$.id = "test" - expect_equal(psc2$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) - expect_equal(psc2$params[1], list(s1.th_param_dbl = pe)) - - psc2_clone = psc2$clone(deep = TRUE) - - psc1$remove_sets("s2") - expect_equal(psc2$params_unid, list(s1.th_param_dbl = ps1$params[[1]], th_param_dbl = ps3$params[[1]])) - expect_equal(psc2$params, list(s1.th_param_dbl = pe, th_param_dbl = ps3$params[[1]])) - - ps1$add(ParamInt$new("x")) - - expect_equal(psc2$params_unid, list(s1.th_param_dbl = ps1$params[[1]], s1.x = ps1$params[[2]], th_param_dbl = ps3$params[[1]])) - expect_equal(psc2$params, list(s1.th_param_dbl = pe, s1.x = ParamInt$new("s1.x"), th_param_dbl = ps3$params[[1]])) - - expect_equal(psc2_clone$params_unid[1], list(s1.th_param_dbl = ps1$params[[1]])) - expect_equal(psc2_clone$params_unid[6], ps3$params[1]) - expect_equal(psc2_clone$params[1], list(s1.th_param_dbl = pe)) - expect_equal(psc2_clone$params[6], ps3$params[1]) - -}) - -test_that("tags shadowing works", { - ps1 = ParamSet$new(list(ParamInt$new("x", tags = c("a", "b")), ParamInt$new("y", tags = c("c")))) - ps1$set_id = "s1" - - expect_equal(ps1$tags, list(x = c("a", "b"), y = "c")) - expect_error({ps1$tags$x = 2}, "May only contain.*character.*numeric") - expect_error({ps1$tags$x = NULL}, "Must be.*identical to.*x,y") - expect_error({ps1$tags = list(y = "c", x = "a")}, "Must be.*identical to.*x,y") - - ps1$tags$x = "z" - - expect_equal(ps1$params$x$param_tags, c("a", "b")) - - ps2 = th_paramset_full() - ps2$set_id = "s2" - - ps2$tags$th_param_int = "xx" - - expect_equal(ps2$params$th_param_int$param_tags, character(0)) - - ps3 = th_paramset_dbl1() - ps3$set_id = "" - psc1 = ParamSetCollection$new(list(ps1, ps2)) - psc2 = ParamSetCollection$new(list(psc1, ps3)) - - expect_equal(psc2$tags, list(s1.x = "z", s1.y = "c", s2.th_param_int = "xx", - s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0), th_param_dbl = character(0))) - - pscc = psc1$clone(deep = TRUE) - psc1$tags$s1.y = "d" - - - expect_equal(psc2$tags, list(s1.x = "z", s1.y = "c", s2.th_param_int = "xx", - s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0), th_param_dbl = character(0))) - expect_equal(psc1$tags, list(s1.x = "z", s1.y = "d", s2.th_param_int = "xx", - s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0))) - expect_equal(pscc$tags, list(s1.x = "z", s1.y = "c", s2.th_param_int = "xx", - s2.th_param_dbl = character(0), s2.th_param_fct = character(0), s2.th_param_lgl = character(0))) - - expect_equal(ps2$params$th_param_int$param_tags, character(0)) - expect_equal(ps1$params$x$param_tags, c("a", "b")) - -}) diff --git a/tests/testthat/test_ParamUty.R b/tests/testthat/test_ParamUty.R index 77a9af3f..40432793 100644 --- a/tests/testthat/test_ParamUty.R +++ b/tests/testthat/test_ParamUty.R @@ -2,21 +2,21 @@ context("ParamUty") test_that("ParamUty", { p = ParamUty$new(id = "x") - expect_true(p$check(FALSE)) - expect_true(p$check(NULL)) - expect_true(p$check(NA)) + expect_true(p$check(list(x = FALSE))) + expect_true(p$check(list(x = NULL))) + expect_true(p$check(list(x = NA))) p = ParamUty$new(id = "x", custom_check = function(x) if (is.null(x)) "foo" else TRUE) - expect_true(p$check(FALSE)) - expect_string(p$check(NULL), fixed = "foo") - expect_true(p$check(NA)) + expect_true(p$check(list(x = FALSE))) + expect_string(p$check(list(x = NULL)), fixed = "foo") + expect_true(p$check(list(x = NA))) p = ParamUty$new(id = "x", default = Inf) }) test_that("R6 values of ParamUty are cloned", { - ps = ParamSet$new(list(ParamUty$new("x"))) + ps = ParamSet_legacy$new(list(ParamUty$new("x"))) ps$values$x = R6Class("testclass", public = list(x = NULL))$new() psclone = ps$clone(deep = TRUE) diff --git a/tests/testthat/test_Param_rep.R b/tests/testthat/test_Param_rep.R index 4b4fe859..91e76a73 100644 --- a/tests/testthat/test_Param_rep.R +++ b/tests/testthat/test_Param_rep.R @@ -2,22 +2,22 @@ context("Repeated params") test_that("rep params work", { p = ParamDbl$new(id = "x", lower = 1, upper = 3) - ps = p$rep(2L) + ps = ps_replicate(p, 2) expect_r6(ps, "ParamSet") expect_equal(ps$length, 2L) expect_subset(ps$class, "ParamDbl") - expect_equal(ps$ids(), c("x_rep_1", "x_rep_2")) + expect_equal(ps$ids(), c("rep1.x", "rep2.x")) expect_subset(ps$lower, 1) expect_subset(ps$upper, 3) p = ParamFct$new(id = "kk", levels = c("a", "b")) - ps = p$rep(3L) + ps = ps_replicate(p, 3) expect_r6(ps, "ParamSet") expect_equal(ps$length, 3L) expect_subset(ps$class, "ParamFct") - expect_equal(ps$ids(), c("kk_rep_1", "kk_rep_2", "kk_rep_3")) + expect_equal(ps$ids(), c("rep1.kk", "rep2.kk", "rep3.kk")) for (id in ps$ids()) { - expect_equal(ps$params[[id]]$levels, c("a", "b")) + expect_equal(ps$levels[[id]], c("a", "b")) } }) diff --git a/tests/testthat/test_deps.R b/tests/testthat/test_deps.R index 0d3ab275..0762ca04 100644 --- a/tests/testthat/test_deps.R +++ b/tests/testthat/test_deps.R @@ -3,27 +3,27 @@ context("Dependencies") test_that("basic example works", { ps = th_paramset_full() expect_false(ps$has_deps) - ps$add_dep("th_param_int", on = "th_param_fct", CondEqual$new("a")) + ps$add_dep("th_param_int", on = "th_param_fct", CondEqual("a")) expect_true(ps$has_deps) x = list(th_param_int = 1) - expect_string(ps$check(x), fixed = "The parameter 'th_param_int' can only be set") + expect_string(ps$check(x, check_strict = TRUE), fixed = "th_param_int: can only be set") x = list(th_param_int = 1, th_param_fct = "a") - expect_true(ps$check(x)) + expect_true(ps$check(x, check_strict = TRUE)) x = list(th_param_int = 1, th_param_fct = "b") - expect_string(ps$check(x), fixed = "The parameter 'th_param_int' can only be set") + expect_string(ps$check(x, check_strict = TRUE), fixed = "th_param_int: can only be set") x = list(th_param_int = NA, th_param_fct = "b") - expect_string(ps$check(x), fixed = "May not be NA") + expect_string(ps$check(x, check_strict = TRUE), fixed = "May not be NA") x = list(th_param_fct = "a") - expect_true(ps$check(x)) + expect_true(ps$check(x, check_strict = TRUE)) x = list(th_param_fct = "b") - expect_true(ps$check(x)) + expect_true(ps$check(x, check_strict = TRUE)) x = list(th_param_dbl = 1.3) - expect_true(ps$check(x)) + expect_true(ps$check(x, check_strict = TRUE)) # test printer, with 2 deps ps = th_paramset_full() - ps$add_dep("th_param_int", on = "th_param_fct", CondEqual$new("a")) - ps$add_dep("th_param_int", on = "th_param_lgl", CondEqual$new(TRUE)) + ps$add_dep("th_param_int", on = "th_param_fct", CondEqual("a")) + ps$add_dep("th_param_int", on = "th_param_lgl", CondEqual(TRUE)) expect_output(print(ps), "th_param_fct,th_param_lgl") # test that we can remove deps @@ -35,60 +35,60 @@ test_that("basic example works", { test_that("nested deps work", { ps = th_paramset_full() - ps$add_dep("th_param_int", on = "th_param_fct", CondAnyOf$new(c("a", "b"))) - ps$add_dep("th_param_dbl", on = "th_param_lgl", CondEqual$new(TRUE)) - ps$add_dep("th_param_lgl", on = "th_param_fct", CondEqual$new("c")) + ps$add_dep("th_param_int", on = "th_param_fct", CondAnyOf(c("a", "b"))) + ps$add_dep("th_param_dbl", on = "th_param_lgl", CondEqual(TRUE)) + ps$add_dep("th_param_lgl", on = "th_param_fct", CondEqual("c")) x1 = list(th_param_int = 1) - expect_string(ps$check(x1), fixed = "The parameter 'th_param_int' can only be set") + expect_string(ps$check(x1, check_strict = TRUE), fixed = "th_param_int: can only be set") x2 = list(th_param_int = 1, th_param_fct = "b") - expect_true(ps$check(x2)) + expect_true(ps$check(x2, check_strict = TRUE)) x3 = list(th_param_int = 1, th_param_fct = "c") - expect_string(ps$check(x3), fixed = "The parameter 'th_param_int' can only be set") + expect_string(ps$check(x3, check_strict = TRUE), fixed = "th_param_int: can only be set") x4 = list(th_param_fct = "a") - expect_true(ps$check(x4)) + expect_true(ps$check(x4, check_strict = TRUE)) x5 = list(th_param_dbl = 1.3) - expect_string(ps$check(x5), fixed = "The parameter 'th_param_dbl' can only be set") + expect_string(ps$check(x5, check_strict = TRUE), fixed = "th_param_dbl: can only be set") x6 = list(th_param_fct = "c", th_param_lgl = TRUE, th_param_dbl = 3) - expect_true(ps$check(x6)) + expect_true(ps$check(x6, check_strict = TRUE)) }) test_that("adding 2 sets with deps works", { - ps1 = ParamSet$new(list( + ps1 = ParamSet_legacy$new(list( ParamFct$new("x1", levels = c("a", "b")), ParamDbl$new("y1") )) - ps1$add_dep("y1", on = "x1", CondEqual$new("a")) + ps1$add_dep("y1", on = "x1", CondEqual("a")) - ps2 = ParamSet$new(list( + ps2 = ParamSet_legacy$new(list( ParamFct$new("x2", levels = c("a", "b")), ParamDbl$new("y2") )) - ps2$add_dep("y2", on = "x2", CondEqual$new("a")) + ps2$add_dep("y2", on = "x2", CondEqual("a")) - ps1$add(ps2) + ps1 = ps_union(list(ps1, ps2)) expect_equal(ps1$length, 4L) expect_true(ps1$has_deps) expect_data_table(ps1$deps, nrows = 2) # do a few feasibility checks on larger set - expect_true(ps1$test(list(x1 = "a", y1 = 1, x2 = "a", y2 = 1))) - expect_true(ps1$test(list(x1 = "a", y1 = 1))) - expect_false(ps1$test(list(x1 = "b", y1 = 1))) - expect_true(ps1$test(list(x2 = "a", y2 = 1))) - expect_false(ps1$test(list(x2 = "b", y2 = 1))) + expect_true(ps1$test(list(x1 = "a", y1 = 1, x2 = "a", y2 = 1), check_strict = TRUE)) + expect_true(ps1$test(list(x1 = "a", y1 = 1), check_strict = TRUE)) + expect_false(ps1$test(list(x1 = "b", y1 = 1), check_strict = TRUE)) + expect_true(ps1$test(list(x2 = "a", y2 = 1), check_strict = TRUE)) + expect_false(ps1$test(list(x2 = "b", y2 = 1), check_strict = TRUE)) }) test_that("subsetting with deps works", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamFct$new("a", levels = c("a", "b")), ParamFct$new("b", levels = c("a", "b")), ParamFct$new("c", levels = c("a", "b")), ParamFct$new("d", levels = c("a", "b")) )) - ps$add_dep("a", on = "b", CondEqual$new("a")) - ps$add_dep("a", on = "c", CondEqual$new("a")) - ps$add_dep("b", on = "c", CondEqual$new("a")) + ps$add_dep("a", on = "b", CondEqual("a")) + ps$add_dep("a", on = "c", CondEqual("a")) + ps$add_dep("b", on = "c", CondEqual("a")) ps$clone(deep = TRUE)$subset("d") ps$clone(deep = TRUE)$subset(c("a", "b", "c")) @@ -97,26 +97,26 @@ test_that("subsetting with deps works", { }) test_that("cannot add a dep on yourself", { - ps = ParamSet$new(list(ParamFct$new("x", levels = c("a")))) - expect_error(ps$add_dep("x", on = "x", CondEqual$new("a")), "depend on itself") + ps = ParamSet_legacy$new(list(ParamFct$new("x", levels = c("a")))) + expect_error(ps$add_dep("x", on = "x", CondEqual("a")), "depend on itself") }) test_that("we can also dep on integer", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamInt$new("i", lower = 0, upper = 9), ParamDbl$new("d", lower = 0, upper = 9) )) - ps$add_dep("d", on = "i", CondAnyOf$new(1:3)) + ps$add_dep("d", on = "i", CondAnyOf(1:3)) - expect_true(ps$check(list(i = 2, d = 5))) - expect_string(ps$check(list(i = 5, d = 5))) + expect_true(ps$check(list(i = 2, d = 5), check_strict = TRUE)) + expect_string(ps$check(list(i = 5, d = 5), check_strict = TRUE)) }) test_that("deps make sense", { ps = th_paramset_full() - expect_error(ps$add_dep("th_param_lgl", "th_param_fct", CondEqual$new("d")), + expect_error(ps$add_dep("th_param_lgl", "th_param_fct", CondEqual("d")), "Condition has infeasible values for th_param_fct") - expect_error(ps$add_dep("th_param_lgl", "th_param_int", CondAnyOf$new(5:15)), + expect_error(ps$add_dep("th_param_lgl", "th_param_int", CondAnyOf(5:15)), "Condition has infeasible values for th_param_int") }) From 5e304d0b96f45d0141390bcb0b1176d3d141af14 Mon Sep 17 00:00:00 2001 From: mb706 Date: Thu, 24 Aug 2023 02:34:20 +0200 Subject: [PATCH 39/64] commit message --- R/Design.R | 2 +- R/Domain_methods.R | 2 +- R/ParamSet.R | 20 ++++---- R/ParamSetCollection.R | 2 +- R/ParamUty.R | 2 +- R/generate_design_grid.R | 2 +- R/generate_design_lhs.R | 2 +- R/to_tune.R | 23 +++------ attic/ParamS6.R | 71 +++++++++++++++++++++++++++ man/ParamSet.Rd | 3 +- man/domain_sanitize.Rd | 11 +++++ man/tautology-package.Rd | 36 ++++++++++++++ tests/testthat/helper_03_domain.R | 3 ++ tests/testthat/test_Design.R | 6 +-- tests/testthat/test_domain.R | 71 +++++++++++++-------------- tests/testthat/test_generate_design.R | 52 ++++++++++---------- tests/testthat/test_param_vals.R | 18 +++---- tests/testthat/test_sampler.R | 48 ++++++++++-------- tests/testthat/test_to_tune.R | 38 +++++++------- tests/testthat/test_trafo.R | 4 +- 20 files changed, 266 insertions(+), 150 deletions(-) create mode 100644 attic/ParamS6.R create mode 100644 man/domain_sanitize.Rd create mode 100644 man/tautology-package.Rd diff --git a/R/Design.R b/R/Design.R index e92b8ff6..afdb5acc 100644 --- a/R/Design.R +++ b/R/Design.R @@ -108,7 +108,7 @@ Design = R6Class("Design", for (j in seq_row(dd)) { pcol = self$data[[dd$on[j]]] # we are ok if parent was active and cond on parent is OK - not_ok = which(is.na(pcol) | !dd$cond[[j]]$test(pcol)) + not_ok = which(is.na(pcol) | !condition_test(dd$cond[[j]], pcol)) set(self$data, not_ok, j = param_id, value = as_type(NA, storage_types[[param_id]])) } } diff --git a/R/Domain_methods.R b/R/Domain_methods.R index e61fc6b7..4c4f44bb 100644 --- a/R/Domain_methods.R +++ b/R/Domain_methods.R @@ -68,7 +68,7 @@ domain_is_categ = function(param) { domain_qunif = function(param, x) { if (!nrow(param)) return(logical(0)) assert_string(unique(param$grouping)) - assert_numeric(x, lower = 0, upper = 1, any.missing = FALSE, min.len = nrow(param)) + assert_numeric(x, lower = 0, upper = 1, any.missing = FALSE) assert_true(length(x) %% length(nrow(param)) == 0) UseMethod("domain_qunif") } diff --git a/R/ParamSet.R b/R/ParamSet.R index fcd175a9..a3326939 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -79,9 +79,10 @@ ParamSet = R6Class("ParamSet", private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") requirements = paramtbl$.requirements + private$.params = paramtbl # self$add_dep needs this for (row in seq_len(nrow(paramtbl))) { for (req in requirements[[row]]) { - invoke(self$add_dep, id = paramtbl$id[[row]], .args = rl) + invoke(self$add_dep, id = paramtbl$id[[row]], allow_dangling_dependencies = allow_dangling_dependencies, .args = req) } } @@ -90,7 +91,7 @@ ParamSet = R6Class("ParamSet", setindexv(paramtbl, c("id", "cls", "grouping")) - private$.params = paramtbl + private$.params = paramtbl # I am 99% sure this is not necessary, but maybe set() creates a copy when deleting too many cols? self$values = initvalues }, @@ -172,10 +173,11 @@ ParamSet = R6Class("ParamSet", }, trafo = function(x, param_set = self) { - trafos = private$.trafos[names(x), .(id, trafo, value = x), nomatch = 0] + trafos = private$.trafos[names(x), .(id, trafo), nomatch = 0] + trafos[, value := x[id]] if (nrow(trafos)) { transformed = pmap(trafos, function(id, trafo, value) trafo(value)) - insert_named(x, set_names(transformed, trafos$id)) + x = insert_named(x, set_names(transformed, trafos$id)) } extra_trafo = self$extra_trafo if (!is.null(extra_trafo)) { @@ -380,7 +382,6 @@ ParamSet = R6Class("ParamSet", #' @description #' Create a new `ParamSet` restricted to the passed IDs. - #' # TODO: ParamSetCollection trafo #' @param ids (`character()`). #' @return `ParamSet`. subset = function(ids, allow_dangling_dependencies = FALSE, keep_constraint = TRUE) { @@ -406,6 +407,7 @@ ParamSet = R6Class("ParamSet", result$assert_values = FALSE result$deps = deps[ids, on = "id", nomatch = NULL] if (keep_constraint) result$constraint = self$constraint + # TODO: ParamSetCollection trafo currently drags along the entire original paramset in its environment result$extra_trafo = self$extra_trafo # restrict to ids already in pvals values = self$values @@ -532,7 +534,7 @@ ParamSet = R6Class("ParamSet", } if (self$assert_values) { self$assert(xs) - if (test_list(xs, types = "TuneToken")) { + if (length(xs) && test_list(xs, types = "TuneToken")) { private$get_tune_ps(xs) # check that to_tune() are valid } } @@ -574,7 +576,7 @@ ParamSet = R6Class("ParamSet", result = copy(private$.params) result[, .tags := list(self$tags)] - result[private$.trafos, .trafo := trafo, on = "id"] + result[private$.trafos, .trafo := list(trafo), on = "id"] result[self$deps, .requirements := transpose_list(.(on, cond)), on = "id"] vals = self$values result[, `:=`( @@ -724,8 +726,7 @@ ParamSet = R6Class("ParamSet", .trafos = data.table(id = character(0L), trafo = list(), key = "id"), get_tune_ps = function(values) { - return(NULL) # TODO - selfparams = private$.params + selfparams = self$subspaces() partsets = imap(keep(values, inherits, "TuneToken"), function(value, pn) { tunetoken_to_ps(value, selfparams[[pn]], pn) }) @@ -744,7 +745,6 @@ ParamSet = R6Class("ParamSet", return(NULL) } # remove infeasible values from condition - cond = cond$clone(deep = TRUE) cond$rhs = keep(cond$rhs, parsparams[[on]]$test) if (!length(cond$rhs)) { # no value is feasible, but there may be a trafo that fixes this diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index adcd3fa7..e242aee5 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -83,7 +83,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, private$.params = paramtbl private$.children_with_trafos = which(!map_lgl(map(sets, "extra_trafo"), is.null)) - private$.children_with_constraints = which(map_lgl(map(sets, "constraint"), is.null)) + private$.children_with_constraints = which(!map_lgl(map(sets, "constraint"), is.null)) private$.sets = sets } diff --git a/R/ParamUty.R b/R/ParamUty.R index 29794ee4..0a338f41 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -2,7 +2,7 @@ #' @rdname Domain #' @export p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { - assert_function(custom_check, nargs = 1, null.ok = TRUE) + assert_function(custom_check, null.ok = TRUE) if (!is.null(custom_check)) { custom_check_result = custom_check(1) assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") diff --git a/R/generate_design_grid.R b/R/generate_design_grid.R index 19c4c6ef..c4213d33 100644 --- a/R/generate_design_grid.R +++ b/R/generate_design_grid.R @@ -54,7 +54,7 @@ generate_design_grid = function(param_set, resolution = NULL, param_resolutions # generate regular grid from 0,1 then map it to the values of the param, # then do a crossproduct grid_vec = lapply(par_res, function(r) seq(0, 1, length.out = r)) - res = imap(grid_vec, function(value, id) param_set$qunif(setnames(data.table(value), id))) + res = imap(grid_vec, function(value, id) param_set$qunif(setnames(data.table(value), id))[[1]]) res = cross_join(res, sorted = FALSE) Design$new(param_set, res, remove_dupl = TRUE) # user wants no dupls, remove } diff --git a/R/generate_design_lhs.R b/R/generate_design_lhs.R index b8ac1f88..9c5bd870 100644 --- a/R/generate_design_lhs.R +++ b/R/generate_design_lhs.R @@ -35,7 +35,7 @@ generate_design_lhs = function(param_set, n, lhs_fun = NULL) { ids = param_set$ids() if (n == 0) { - d = matrix(nrow = 0, ncol = param_set$length) + d = matrix(numeric(0), nrow = 0, ncol = param_set$length) } else { d = lhs_fun(n, k = param_set$length) } diff --git a/R/to_tune.R b/R/to_tune.R index 2cd6f910..7e6cd5e6 100644 --- a/R/to_tune.R +++ b/R/to_tune.R @@ -165,9 +165,9 @@ to_tune = function(...) { content = p_fct(levels = content) } else { if (inherits(content, "Domain")) { - bounded = ps(x = content, .allow_dangling_dependencies = TRUE)$is_bounded + bounded = ps(x = content, .allow_dangling_dependencies = TRUE)$all_bounded } else { - bounded = content$is_bounded + bounded = content$all_bounded } if (!bounded) { stop("tuning range must be bounded.") @@ -213,7 +213,7 @@ tunetoken_to_ps = function(tt, param, id) { } tunetoken_to_ps.FullTuneToken = function(tt, param, id) { - if (!param$is_bounded) { + if (!param$all_bounded) { stopf("%s must give a range for unbounded parameter %s.", tt$call, id) } if (isTRUE(tt$content$logscale)) { @@ -228,7 +228,7 @@ tunetoken_to_ps.RangeTuneToken = function(tt, param, id) { if (!param$is_number) { stopf("%s for non-numeric param must have zero or one argument.", tt$call) } - invalidpoints = discard(tt$content, function(x) is.null(x) || param$test(x)) + invalidpoints = discard(tt$content, function(x) is.null(x) || param$test(set_names(list(x, param$ids()))) invalidpoints$logscale = NULL if (length(invalidpoints)) { stopf("%s range not compatible with param %s.\nBad value(s):\n%s\nParameter:\n%s", @@ -274,14 +274,8 @@ pslike_to_ps.Domain = function(pslike, call, param, id, usersupplied = TRUE) { pslike_to_ps(pslike, call, param, id, usersupplied = FALSE) } -pslike_to_ps.Param = function(pslike, call, param, id, usersupplied = TRUE) { - pslike = pslike$with_id(id) - pslike = ParamSet$new(list(pslike)) - pslike_to_ps(pslike, call, param, id, usersupplied = FALSE) -} - pslike_to_ps.ParamSet = function(pslike, call, param, id, usersupplied = TRUE) { - pslike = pslike$clone(deep = TRUE) + pslike = pslike$flatten() alldeps = pslike$deps # temporarily hide dangling deps on = NULL # pacify static code check @@ -293,21 +287,20 @@ pslike_to_ps.ParamSet = function(pslike, call, param, id, usersupplied = TRUE) { stopf("%s for param %s does not have a trafo that reduces output to one dimension.\nExample:\n%s", call, id, repr(invalidpoints[[1]])) } - invalidpoints = discard(testpoints, function(x) param$test(x[[1]])) + invalidpoints = discard(testpoints, function(x) param$test(x)) if (length(invalidpoints)) { stopf("%s generates points that are not compatible with param %s.\nBad value:\n%s\nParameter:\n%s", call, id, repr(invalidpoints[[1]][[1]]), repr(param)) } - if (usersupplied) { + if (usersupplied && pslike$has_trafo) { trafo = pslike$trafo %??% identity pname = id - pslike$trafo = crate(function(x, param_set) { + pslike$extra_trafo = crate(function(x, param_set) { mlr3misc::set_names( checkmate::assert_list(trafo(x), len = 1, .var.name = sprintf("Trafo for tuning ParamSet for parameter %s", pname)), pname ) }, trafo, pname) } - pslike$set_id = "" pslike } diff --git a/attic/ParamS6.R b/attic/ParamS6.R new file mode 100644 index 00000000..f37bd866 --- /dev/null +++ b/attic/ParamS6.R @@ -0,0 +1,71 @@ + +#' @rdname Domain +#' @export +p_s6 = function(support, special_vals = list(), default = NO_DEF, tags = character(), depends = NULL, trafo = NULL, init) { + set = S6Cache$canonicalize(support) + support_description = if (is.character(support)) support else S6Cache$set6_sane_repr(object) + + if ((("power" %in% names(set)) && set$power != 1) || set$class %nin% c("numeric", "integer")) { + storage_type = "list" + } else { + storage_type = set$class + } + + Domain(cls = "ParamSet6", grouping = support_description, + cargo = set, + lower = suppressWarnings(as.numeric(set$lower)), + upper = suppressWarnings(as.numeric(set$upper)), + levels = as.list(set$elements), + special_vals = special_vals, + default = default, + tags = tags, + trafo = trafo, + storage_type = storage_type, + depends_expr = substitute(depends), + init = init) +} + +#' @export +domain_check.ParamSet6 = function(param, values) { + # we can rely on 'grouping' always giving us the same class + set = set6_cache_get(param$grouping[[1]]) + if (set$contains(values, all = TRUE)) { + return(TRUE) + } + nomatches = param$id[!set$contains(values, all = FALSE)] + sprintf("%s: not contained in %s.", str_collapse(nomatches, sep = ", "), set$strprint()) +} + +#' @export +domain_nlevels.ParamSet6 = function(param) { + # we can rely on 'grouping' always giving us the same class + set = set6_cache_get(param$grouping[[1]]) + card = set$properties$cardinality + card = if (!is.numeric(card)) Inf + rep(card, nrow(param)) +} +#' @export +domain_is_bounded.ParamSet6 = function(param) { + # we can rely on 'grouping' always giving us the same class + set = set6_cache_get(param$grouping[[1]]) + card = set$properties$cardinality + card = if (!is.numeric(card)) Inf + boundedness = is.finite(card) || (is.finite(set$lower) && is.finite(set$upper)) + rep(boundedness, nrow(param)) +} +#' @export +domain_qunif.ParamSet6 = function(param, x) stop("undefined") + + +#' @export +domain_is_number.ParamSet6 = function(param) { + param$storage_type[[1]] != "list" +} +#' @export +domain_is_categ.ParamSet6 = function(param) { + set = set6_cache_get(param$grouping[[1]]) + categness = ("power" %nin% names(set) || set$power == 1) && + set$class %nin% c("numeric", "integer") && is.finite(set$properties$cardinality) + + categness +} diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index a1b00157..7b2da7d6 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -455,8 +455,7 @@ If successful \code{xs} invisibly, if not an error message. \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-subset}{}}} \subsection{Method \code{subset()}}{ -Create a new `ParamSet` restricted to the passed IDs. - # TODO: ParamSetCollection trafo +Create a new \code{ParamSet} restricted to the passed IDs. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ParamSet$subset( ids, diff --git a/man/domain_sanitize.Rd b/man/domain_sanitize.Rd new file mode 100644 index 00000000..4bdc9612 --- /dev/null +++ b/man/domain_sanitize.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Domain_methods.R +\name{domain_sanitize} +\alias{domain_sanitize} +\title{export} +\usage{ +domain_sanitize(param, values) +} +\description{ +export +} diff --git a/man/tautology-package.Rd b/man/tautology-package.Rd new file mode 100644 index 00000000..58e7ee20 --- /dev/null +++ b/man/tautology-package.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/zzz.R +\docType{package} +\name{tautology-package} +\alias{tautology} +\alias{tautology-package} +\title{tautology: Define and Work with Parameter Spaces for Complex Algorithms} +\description{ +Define parameter spaces, constraints and dependencies for arbitrary algorithms, to program on such spaces. Also includes statistical designs and random samplers. Objects are implemented as 'R6' classes. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://paradox.mlr-org.com} + \item \url{https://github.com/mlr-org/paradox} + \item Report bugs at \url{https://github.com/mlr-org/paradox/issues} +} + +} +\author{ +\strong{Maintainer}: Michel Lang \email{michellang@gmail.com} (\href{https://orcid.org/0000-0001-9754-0393}{ORCID}) + +Authors: +\itemize{ + \item Bernd Bischl \email{bernd_bischl@gmx.net} (\href{https://orcid.org/0000-0001-6002-6980}{ORCID}) + \item Jakob Richter \email{jakob1richter@gmail.com} (\href{https://orcid.org/0000-0003-4481-5554}{ORCID}) + \item Xudong Sun \email{smilesun.east@gmail.com} (\href{https://orcid.org/0000-0003-3269-2307}{ORCID}) + \item Martin Binder \email{mlr.developer@mb706.com} +} + +Other contributors: +\itemize{ + \item Marc Becker \email{marcbecker@posteo.de} (\href{https://orcid.org/0000-0002-8115-0400}{ORCID}) [contributor] +} + +} diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R index 115d7889..9c6b0fdd 100644 --- a/tests/testthat/helper_03_domain.R +++ b/tests/testthat/helper_03_domain.R @@ -13,6 +13,9 @@ expect_equal_ps = function(a, b) { acp$.tags = copy(acp$.tags)[, id := sprintf("x%s", match(id, original$ids()))] acp$.trafos = copy(acp$.trafos)[, id := sprintf("x%s", match(id, original$ids()))] acp$.deps[, id := sprintf("x%s", match(id, original$ids()))] + setindexv(acp$.params, NULL) + setindexv(acp$.trafos, NULL) + setindexv(acp$.tags, NULL) acl } diff --git a/tests/testthat/test_Design.R b/tests/testthat/test_Design.R index d23e32e5..b359fe5e 100644 --- a/tests/testthat/test_Design.R +++ b/tests/testthat/test_Design.R @@ -1,11 +1,11 @@ context("Design") test_that("transpose works", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamFct$new("f", levels = c("a", "b")), ParamInt$new("i", lower = 1, upper = 5) )) - ps$add_dep("i", on = "f", CondEqual$new("a")) + ps$add_dep("i", on = "f", CondEqual("a")) data = data.table(f = c("a", "b"), i = c(1L, NA)) d = Design$new(ps, data, remove_dupl = FALSE) xs = d$transpose(filter_na = FALSE) @@ -16,7 +16,7 @@ test_that("transpose works", { expect_equal(xs, xs2) # now a trafo, with a dep - ps$trafo = function(x, param_set) { + ps$extra_trafo = function(x, param_set) { if (!is.null(x$i)) { x$i = x$i + 10 } diff --git a/tests/testthat/test_domain.R b/tests/testthat/test_domain.R index 60edb5a9..4cb3064f 100644 --- a/tests/testthat/test_domain.R +++ b/tests/testthat/test_domain.R @@ -13,44 +13,41 @@ test_that("p_xxx printers", { expect_output(print(p_uty()), "p_uty\\(\\)") expect_output(print(p_fct("a")), "p_fct\\(levels = \"a\"\\)") - expect_output(print(p_fct(1)), "p_fct\\(levels = \"1\"\\)") - expect_output(print(p_fct(list(x = 1))), "p_fct\\(levels = \"x\"\\)") + expect_output(print(p_fct("1")), "p_fct\\(levels = \"1\"\\)") + expect_output(print(p_fct(list(x = 1))), "p_fct\\(levels = list\\(x = 1\\)\\)") - expect_output(print(p_fct(list(x = 1), depends = x == 1)), "p_fct\\(levels = \"x\"\\, depends = x == 1)") + expect_output(print(p_fct(list(x = 1), depends = x == 1)), "p_fct\\(levels = list\\(x = 1\\)\\, depends = x == 1)") reqquote = quote(x == 1) - expect_output(print(p_fct(list(x = 1), depends = reqquote)), "p_fct\\(levels = \"x\"\\, depends = x == 1)") + expect_output(print(p_fct(list(x = 1), depends = reqquote)), "p_fct\\(levels = list\\(x = 1\\)\\, depends = x == 1)") }) test_that("ps(p_xxx(...)) creates ParamSets", { - expect_equal_ps(ps(x = p_int()), ParamSet$new(list(ParamInt$new("x")))) - expect_equal_ps(ps(x = p_dbl()), ParamSet$new(list(ParamDbl$new("x")))) - expect_equal_ps(ps(x = p_uty()), ParamSet$new(list(ParamUty$new("x")))) - expect_equal_ps(ps(x = p_lgl()), ParamSet$new(list(ParamLgl$new("x")))) - expect_equal_ps(ps(x = p_fct(letters)), ParamSet$new(list(ParamFct$new("x", letters)))) + expect_equal_ps(ps(x = p_int()), ParamSet_legacy$new(list(ParamInt$new("x")))) + expect_equal_ps(ps(x = p_dbl()), ParamSet_legacy$new(list(ParamDbl$new("x")))) + expect_equal_ps(ps(x = p_uty()), ParamSet_legacy$new(list(ParamUty$new("x")))) + expect_equal_ps(ps(x = p_lgl()), ParamSet_legacy$new(list(ParamLgl$new("x")))) + expect_equal_ps(ps(x = p_fct(letters)), ParamSet_legacy$new(list(ParamFct$new("x", letters)))) - expect_equal_ps(ps(x = p_int(upper = 1, lower = 0)), ParamSet$new(list(ParamInt$new("x", 0, 1)))) - expect_equal_ps(ps(x = p_dbl(upper = 1, lower = 0)), ParamSet$new(list(ParamDbl$new("x", 0, 1)))) + expect_equal_ps(ps(x = p_int(upper = 1, lower = 0)), ParamSet_legacy$new(list(ParamInt$new("x", 0, 1)))) + expect_equal_ps(ps(x = p_dbl(upper = 1, lower = 0)), ParamSet_legacy$new(list(ParamDbl$new("x", 0, 1)))) - expect_equal_ps(ps(x = p_int(special_vals = list("x"), default = 0, tags = "required")), - ParamSet$new(list(ParamInt$new("x", special_vals = list("x"), default = 0, tags = "required")))) - expect_equal_ps(ps(x = p_dbl(special_vals = list("x"), default = 0, tags = "required")), - ParamSet$new(list(ParamDbl$new("x", special_vals = list("x"), default = 0, tags = "required")))) + expect_equal_ps(ps(x = p_int(special_vals = list("x"), default = 0, tags = "xx")), + ParamSet_legacy$new(list(ParamInt$new("x", special_vals = list("x"), default = 0, tags = "xx")))) + expect_equal_ps(ps(x = p_dbl(special_vals = list("x"), default = 0, tags = "xx")), + ParamSet_legacy$new(list(ParamDbl$new("x", special_vals = list("x"), default = 0, tags = "xx")))) - expect_equal_ps(ps(x = p_lgl(special_vals = list("x"), default = TRUE, tags = "required")), - ParamSet$new(list(ParamLgl$new("x", special_vals = list("x"), default = TRUE, tags = "required")))) + expect_equal_ps(ps(x = p_lgl(special_vals = list("x"), default = TRUE, tags = "xx")), + ParamSet_legacy$new(list(ParamLgl$new("x", special_vals = list("x"), default = TRUE, tags = "xx")))) - expect_equal_ps(ps(x = p_fct(letters, special_vals = list(0), default = 0, tags = "required")), - ParamSet$new(list(ParamFct$new("x", letters, special_vals = list(0), default = 0, tags = "required")))) + expect_equal_ps(ps(x = p_fct(letters, special_vals = list(0), default = 0, tags = "xx")), + ParamSet_legacy$new(list(ParamFct$new("x", letters, special_vals = list(0), default = 0, tags = "xx")))) - expect_equal_ps(ps(x = p_uty(default = 1, tags = "required", custom_check = check_int)), - ParamSet$new(list(ParamUty$new("x", default = 1, tags = "required", custom_check = check_int)))) + expect_equal_ps(ps(x = p_uty(default = 1, tags = "xx", custom_check = check_int)), + ParamSet_legacy$new(list(ParamUty$new("x", default = 1, tags = "xx", custom_check = check_int)))) expect_error(ps(x = p_int(), x = p_int()), "unique names") - expect_equal_ps(ps(x = p_uty(default = 1, tags = "required", custom_check = check_int)), - ps(x = ParamUty$new("y", default = 1, tags = "required", custom_check = check_int))) - expect_error(p_int(id = 1), "unused argument.*id") }) @@ -90,7 +87,7 @@ test_that("p_fct autotrafo", { test_that("requirements in domains", { - simpleps = ParamSet$new(list(ParamInt$new("x"), ParamDbl$new("y")))$add_dep("y", "x", CondEqual$new(1)) + simpleps = ParamSet_legacy$new(list(ParamInt$new("x"), ParamDbl$new("y")))$add_dep("y", "x", CondEqual(1)) # basic equality expression expect_equal_ps( @@ -115,7 +112,7 @@ test_that("requirements in domains", { ), simpleps) # the same for `p_fct`, which behaves slightly differently from the rest - simpleps = ParamSet$new(list(ParamInt$new("x"), ParamFct$new("y", letters)))$add_dep("y", "x", CondEqual$new(1)) + simpleps = ParamSet_legacy$new(list(ParamInt$new("x"), ParamFct$new("y", letters)))$add_dep("y", "x", CondEqual(1)) expect_equal_ps( ps( x = p_int(), @@ -129,7 +126,7 @@ test_that("requirements in domains", { ), simpleps) # the same for `p_fct` involving autotrafo, which behaves slightly differently from the rest - simpleps = ps(x = p_int(), y = p_fct(list(a = 1, b = 2)))$add_dep("y", "x", CondEqual$new(1)) + simpleps = ps(x = p_int(), y = p_fct(list(a = 1, b = 2)))$add_dep("y", "x", CondEqual(1)) expect_equal_ps( ps( x = p_int(), @@ -148,7 +145,7 @@ test_that("requirements in domains", { x = p_int(), y = p_dbl(depends = x == 1 && x == 3) ), - ParamSet$new(list(ParamInt$new("x"), ParamDbl$new("y")))$add_dep("y", "x", CondEqual$new(1))$add_dep("y", "x", CondEqual$new(3))) + ParamSet_legacy$new(list(ParamInt$new("x"), ParamDbl$new("y")))$add_dep("y", "x", CondEqual(1))$add_dep("y", "x", CondEqual(3))) # `&&`, `%in%` expect_equal_ps( @@ -157,8 +154,8 @@ test_that("requirements in domains", { z = p_fct(letters[1:3]), y = p_dbl(depends = x == 1 && z %in% c("a", "b")) ), - ParamSet$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamDbl$new("y")))$ - add_dep("y", "x", CondEqual$new(1))$add_dep("y", "z", CondAnyOf$new(c("a", "b")))) + ParamSet_legacy$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamDbl$new("y")))$ + add_dep("y", "x", CondEqual(1))$add_dep("y", "z", CondAnyOf(c("a", "b")))) # recursive dependencies expect_equal_ps( @@ -167,12 +164,12 @@ test_that("requirements in domains", { z = p_fct(letters[1:3], depends = x == 2), y = p_dbl(depends = x == 1 && z %in% c("a", "b")) ), - ParamSet$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamDbl$new("y")))$ - add_dep("z", "x", CondEqual$new(2))$add_dep("y", "x", CondEqual$new(1))$add_dep("y", "z", CondAnyOf$new(c("a", "b")))) + ParamSet_legacy$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamDbl$new("y")))$ + add_dep("z", "x", CondEqual(2))$add_dep("y", "x", CondEqual(1))$add_dep("y", "z", CondAnyOf(c("a", "b")))) # `fct` with complex requirements - complexps = ParamSet$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamFct$new("y", letters[1:3])))$ - add_dep("y", "x", CondEqual$new(1))$add_dep("y", "z", CondAnyOf$new(c("a", "b"))) + complexps = ParamSet_legacy$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamFct$new("y", letters[1:3])))$ + add_dep("y", "x", CondEqual(1))$add_dep("y", "z", CondAnyOf(c("a", "b"))) expect_equal_ps( ps( x = p_int(), @@ -195,9 +192,9 @@ test_that("requirements in domains", { z = p_fct(letters[1:3]), y = p_fct(letters[1:3], depends = ((((x == 1)) && (z %in% c("a", "b") && z == "a")))) ), - ParamSet$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamFct$new("y", letters[1:3])))$ - add_dep("y", "x", CondEqual$new(1))$add_dep("y", "z", CondAnyOf$new(c("a", "b")))$ - add_dep("y", "z", CondEqual$new("a"))) + ParamSet_legacy$new(list(ParamInt$new("x"), ParamFct$new("z", letters[1:3]), ParamFct$new("y", letters[1:3])))$ + add_dep("y", "x", CondEqual(1))$add_dep("y", "z", CondAnyOf(c("a", "b")))$ + add_dep("y", "z", CondEqual("a"))) expect_error(p_int(depends = 1 == x), "must be a parameter name") expect_error(p_int(depends = 1), "must be an expression") diff --git a/tests/testthat/test_generate_design.R b/tests/testthat/test_generate_design.R index 30b7498e..cab24cd0 100644 --- a/tests/testthat/test_generate_design.R +++ b/tests/testthat/test_generate_design.R @@ -2,14 +2,14 @@ context("generate_design") test_that("generate_design_random", { ps_list = list( - th_paramset_dbl1(), - th_paramset_full(), - th_paramset_repeated(), - th_paramset_numeric() + dbl = th_paramset_dbl1(), + full = th_paramset_full(), + repeated = th_paramset_repeated(), + numeric = th_paramset_numeric() ) - for (ps in ps_list) { - info = ps$set_id + for (info in names(ps_list)) { + ps = ps_list[[info]] d = generate_design_random(ps, n = 5L) dd = d$data expect_data_table(dd, any.missing = FALSE, nrows = 5L, ncols = ps$length, info = info) @@ -19,14 +19,14 @@ test_that("generate_design_random", { test_that("generate_design_grid", { ps_list = list( - th_paramset_dbl1(), - th_paramset_full(), - th_paramset_repeated(), - th_paramset_numeric() + dbl = th_paramset_dbl1(), + full = th_paramset_full(), + repeated = th_paramset_repeated(), + numeric = th_paramset_numeric() ) - for (ps in ps_list) { - info = ps$set_id + for (info in names(ps_list)) { + ps = ps_list[[info]] reso = 3L d = generate_design_grid(ps, resolution = reso) dd = d$data @@ -41,18 +41,18 @@ test_that("generate_design_grid", { }) test_that("generate_design_grid with different resolutions and egde cases", { - ps = ParamSet$new(list(ParamFct$new("f", levels = letters[1:2]))) + ps = ParamSet_legacy$new(list(ParamFct$new("f", levels = letters[1:2]))) d = generate_design_grid(ps) expect_data_table(d$data, any.missing = FALSE, nrows = 2, ncols = 1) - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamFct$new("f", levels = letters[1:2]), ParamDbl$new("d", lower = 0, upper = 1) )) d = generate_design_grid(ps, param_resolutions = c(d = 3)) expect_data_table(d$data, any.missing = FALSE, nrows = 6, ncols = 2) - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamInt$new("x", lower = 0, upper = 10), ParamInt$new("y", lower = 0, upper = 10) )) @@ -68,7 +68,7 @@ test_that("generate_design_grid with different resolutions and egde cases", { expect_equal(length(unique(dd$x)), 4) expect_equal(length(unique(dd$y)), 3) - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamInt$new("x", lower = 0, upper = 10), ParamInt$new("y", lower = 0, upper = 10), ParamLgl$new("z") @@ -82,7 +82,7 @@ test_that("generate_design_grid with different resolutions and egde cases", { }) test_that("check generate_design_grid against concrete expectation", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new("x", lower = 1, upper = 3), ParamFct$new("y", levels = c("a", "b")) )) @@ -95,14 +95,14 @@ test_that("generate_design_lhs", { skip_if_not_installed("lhs") ps_list = list( - th_paramset_dbl1(), - th_paramset_full(), - th_paramset_repeated(), - th_paramset_numeric() + dbl = th_paramset_dbl1(), + full = th_paramset_full(), + repeated = th_paramset_repeated(), + numeric = th_paramset_numeric() ) - for (ps in ps_list) { - info = ps$set_id + for (info in names(ps_list)) { + ps = ps_list[[info]] d = generate_design_lhs(ps, 10) dd = d$data expect_data_table(d$data, nrows = 10, any.missing = FALSE, info = info) @@ -143,7 +143,7 @@ test_that("generate_design_random and grid works with deps", { test_that("generate_design_random with zero rows", { ps = th_paramset_full() d = generate_design_random(ps, n = 0) - expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length, info = ps$set_id) + expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length) }) test_that("generate_design_lhs with zero rows", { @@ -151,11 +151,11 @@ test_that("generate_design_lhs with zero rows", { ps = th_paramset_full() d = generate_design_lhs(ps, n = 0) - expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length, info = ps$set_id) + expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length) }) test_that("generate_design_grid with zero rows", { ps = th_paramset_full() d = generate_design_grid(ps, resolution = 0) - expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length, info = ps$set_id) + expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length) }) diff --git a/tests/testthat/test_param_vals.R b/tests/testthat/test_param_vals.R index 13ed72dc..7a36b68c 100644 --- a/tests/testthat/test_param_vals.R +++ b/tests/testthat/test_param_vals.R @@ -1,7 +1,7 @@ context("values") test_that("values", { - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new(id = "d", lower = 0, upper = 1), ParamInt$new(id = "i", lower = 1, upper = 3), ParamFct$new(id = "f", levels = letters[1:3]) @@ -12,24 +12,24 @@ test_that("values", { ps$values = list(d = 1, f = "a") expect_true(ps$check(list(d = 0, f = "a"))) ps2 = ps$clone() - ps2$subset(ids = c("d", "i")) + ps2 = ps2$subset(ids = c("d", "i")) expect_equal(ps2$values, list(d = 1)) ps2$values = list(d = 0.5) expect_true(ps$check(list(d = 1, f = "a"))) expect_equal(ps2$values, list(d = 0.5)) # check printer - expect_output(print(ps2), "d.*.*0.5") + expect_output(print(ps2), "d.*.*0.5") ps2 = ps$clone() - ps2$subset(ids = c("i")) + ps2 = ps2$subset(ids = c("i")) expect_equal(ps2$values, set_names(list(), character(0))) - ps3 = ParamSet$new(list( + ps3 = ParamSet_legacy$new(list( ParamDbl$new(id = "x", lower = 0, upper = 9) )) ps3$values = list(x = 7) ps2 = ps$clone() - ps2$add(ps3) + ps2 = ps_union(list(ps2, ps3)) expect_equal(ps2$values, list(d = 1, f = "a", x = 7)) # designs @@ -62,7 +62,7 @@ test_that("values", { test_that("values calls assert", { # most of the tests should be done for ParamSet$check, so we simply # check here, that paramvals calls assert - ps = ParamSet$new(list( + ps = ParamSet_legacy$new(list( ParamDbl$new(id = "d", lower = 0, upper = 1), ParamInt$new(id = "i", lower = 1, upper = 3), ParamFct$new(id = "f", levels = letters[1:3]) @@ -72,8 +72,8 @@ test_that("values calls assert", { # now check that we can disable assert ps$assert_values = FALSE - ps$values = list(xxx = 1) - expect_equal(ps$values, list(xxx = 1)) ps$values = list(d = 9) expect_equal(ps$values, list(d = 9)) }) + + diff --git a/tests/testthat/test_sampler.R b/tests/testthat/test_sampler.R index 1cc102f6..7cec709a 100644 --- a/tests/testthat/test_sampler.R +++ b/tests/testthat/test_sampler.R @@ -8,14 +8,14 @@ test_that("1d samplers: basic tests", { ParamLgl = list(Sampler1DUnif, Sampler1DCateg) ) ps = th_paramset_full() - for (p in ps$params) { + for (p in ps$subspaces()) { ss = samplers[[p$class]] for (s in ss) { expect_error(s$new(ps()), "exactly 1 Param, but contains 0") - expect_error(s$new(ps(x = p, y = p)), "exactly 1 Param, but contains 2") - expect_class(s$new(ps(x = p)), "Sampler1D") + expect_error(s$new(ps_union(list(x = p, y = p))), "exactly 1 Param, but contains 2") + expect_class(s$new(p), "Sampler1D") s = s$new(p) - info = paste(p$id, "-", class(s)[[1L]]) + info = paste(p$ids(), "-", class(s)[[1L]]) n = 5L x = s$sample(n) d = x$data @@ -26,7 +26,7 @@ test_that("1d samplers: basic tests", { expect_true(all(d1 >= p$lower & d1 <= p$upper), info = info) } if (p$class %in% c("ParamFct")) { - expect_true(all(d1 %in% p$levels), info = info) + expect_true(all(d1 %in% p$levels[[1]]), info = info) } expect_output(print(s), " Date: Fri, 25 Aug 2023 01:15:07 +0200 Subject: [PATCH 40/64] tests pass --- NEWS.md | 10 ++++ R/ParamInt.R | 4 +- R/ParamSet.R | 83 ++++++++++++++++++++----------- R/ParamSetCollection.R | 4 +- R/to_tune.R | 82 +++++++++++++++--------------- tests/testthat/helper_03_domain.R | 8 +++ tests/testthat/test_to_tune.R | 61 +++++++++++------------ tests/testthat/test_trafo.R | 2 +- 8 files changed, 152 insertions(+), 102 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3b844e1b..49a9faf5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,13 @@ + +# paradox 0.8.0 +* Removed `Param` objects. `ParamSet` now uses a `data.table` internally; individual parameters are more like `Domain` objects now. `ParamSets` should be constructed using the `ps()` shorthand and `Domain` objects. This entails the following major changes: + * `ParamSet` now supports `extra_trafo` natively; it behaves like `.extra_trafo` of the `ps()` call. + * `ParamSet` has `$constraint` + * `ParamSet` objects are now less mutable. The only properties that can be changed are `values`, `tags`, `deps`, `constraint` and `extra_trafo`. + * `ParamSet$is_bounded` is a vector with an entry for each parameter. Use `$all_bounded` for the previous behavior. + * `Condition` objects are now S3 objects and can be constructed with `CondEqual()` and `CondAnyOf()`, instead of `CondXyz$new()`. (It is recommended to use the `Domain` interface for conditions, which has not changed) + + # paradox 0.7.1.9000 - Same as previous version. diff --git a/R/ParamInt.R b/R/ParamInt.R index c669fba6..84299e3e 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -15,14 +15,16 @@ p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ real_lower = log(max(lower, 0.5)) real_upper = log(upper + 1) cls = "ParamDbl" + storage_type = "numeric" } else { cls = "ParamInt" + storage_type = "integer" real_lower = lower real_upper = upper } Domain(cls = cls, grouping = cls, lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, - storage_type = "integer", + storage_type = storage_type, depends_expr = substitute(depends), init = init) } diff --git a/R/ParamSet.R b/R/ParamSet.R index a3326939..7e2bc025 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -71,29 +71,40 @@ ParamSet = R6Class("ParamSet", } else { paramtbl = rbindlist(params) set(paramtbl, , "id", names(params)) - private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] - setindexv(private$.tags, "tag") + if (".tags" %in% colnames(paramtbl)) { + private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] + setindexv(private$.tags, "tag") + } } - initvalues = with(paramtbl[(.init_given), .(.init, id)], set_names(.init, id)) - private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") + # get initvalues here, so we can delete the relevant column. + # we only assign it later, so checks can run normally. + initvalues = if (".init" %in% names(paramtbl)) with(paramtbl[(.init_given), .(.init, id)], set_names(.init, id)) + + if (".trafo" %in% names(paramtbl)) { + private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") + } - requirements = paramtbl$.requirements - private$.params = paramtbl # self$add_dep needs this - for (row in seq_len(nrow(paramtbl))) { - for (req in requirements[[row]]) { - invoke(self$add_dep, id = paramtbl$id[[row]], allow_dangling_dependencies = allow_dangling_dependencies, .args = req) + if (".requirements" %in% names(paramtbl)) { + requirements = paramtbl$.requirements + private$.params = paramtbl # self$add_dep needs this + for (row in seq_len(nrow(paramtbl))) { + for (req in requirements[[row]]) { + invoke(self$add_dep, id = paramtbl$id[[row]], allow_dangling_dependencies = allow_dangling_dependencies, + .args = req) + } } } - set(paramtbl, , setdiff(colnames(paramtbl), domain_names_permanent), NULL) + delendum_cols = setdiff(colnames(paramtbl), domain_names_permanent) + if (length(delendum_cols)) set(paramtbl, , delendum_cols, NULL) assert_names(colnames(paramtbl), identical.to = domain_names_permanent) setindexv(paramtbl, c("id", "cls", "grouping")) private$.params = paramtbl # I am 99% sure this is not necessary, but maybe set() creates a copy when deleting too many cols? - self$values = initvalues + if (!is.null(initvalues)) self$values = initvalues }, #' @description @@ -226,6 +237,14 @@ ParamSet = R6Class("ParamSet", return(sprintf("Parameter '%s' not available.%s", ns[extra], did_you_mean(extra, ids))) } + if (length(xs) && test_list(xs, types = "TuneToken")) { + tunecheck = tryCatch({ + private$get_tune_ps(xs) + TRUE + }, error = function(e) paste("tune token invalid:", conditionMessage(e))) + if (!isTRUE(tunecheck)) return(tunecheck) + } + # check each parameter group's feasibility xs_nontune = discard(xs, inherits, "TuneToken") @@ -362,6 +381,11 @@ ParamSet = R6Class("ParamSet", as.data.table(set_names(params$result, params$id)) }, + #' @description + #' get the [`Domain`] object that could be used to create a given parameter. + #' + #' @param id (`character(1)`). + #' @return [`Domain`]. get_param = function(id) { assert_string(id) paramrow = private$.params[id, on = "id", nomatch = NULL] @@ -369,15 +393,16 @@ ParamSet = R6Class("ParamSet", if (!nrow(paramrow)) stopf("No param with id '%s'", id) vals = self$values + depstbl = self$deps[id, .(on, cond), on = "id", nomatch = 0] paramrow[, `:=`( - .tags = private$.tags[id, tag], + .tags = list(private$.tags[id, tag, nomatch = 0]), .trafo = private$.trafos[id, trafo], - .requirements = list(transpose_list(self$deps[id, .(on, cond), on = "id"])), + .requirements = list(if (nrow(depstbl)) transpose_list(depstbl)), # NULL if no deps .init_given = id %in% names(vals), .init = unname(vals[id])) ] - set_class(paramrow, c(paramrow$cls, class(paramrow))) + set_class(paramrow, c(paramrow$cls, "Domain", class(paramrow))) }, #' @description @@ -435,7 +460,7 @@ ParamSet = R6Class("ParamSet", #' @description #' Create a `ParamSet` from this object, even if this object itself is not #' a `ParamSet`. - flatten = function() self$subset(private$.params$id), + flatten = function() self$subset(private$.params$id, allow_dangling_dependencies = TRUE), #' @description #' Construct a [`ParamSet`] to tune over. Constructed from [`TuneToken`] in `$values`, see [`to_tune()`]. @@ -464,7 +489,7 @@ ParamSet = R6Class("ParamSet", params = private$.params ids = params$id assert_choice(id, ids) - if (allow_dangling_dependencies) assert_choice(on, ids) else assert_string(on) + if (allow_dangling_dependencies) assert_string(on) else assert_choice(on, ids) assert_class(cond, "Condition") if (id == on) { stopf("A param cannot depend on itself!") @@ -534,9 +559,6 @@ ParamSet = R6Class("ParamSet", } if (self$assert_values) { self$assert(xs) - if (length(xs) && test_list(xs, types = "TuneToken")) { - private$get_tune_ps(xs) # check that to_tune() are valid - } } if (length(xs) == 0L) { xs = named_list() @@ -726,16 +748,21 @@ ParamSet = R6Class("ParamSet", .trafos = data.table(id = character(0L), trafo = list(), key = "id"), get_tune_ps = function(values) { - selfparams = self$subspaces() - partsets = imap(keep(values, inherits, "TuneToken"), function(value, pn) { - tunetoken_to_ps(value, selfparams[[pn]], pn) + values = keep(values, inherits, "TuneToken") + if (!length(values)) return(ParamSet$new()) + params = map(names(values), function(pn) { + domain = private$.params[pn, on = "id"] + set_class(domain, c(domain$cls, "Domain", class(domain))) }) - if (!length(partsets)) return(ParamSet$new()) + names(params) = names(values) + + # package-internal S3 fails if we don't call the function indirectly here + partsets = pmap(list(values, params), function(...) tunetoken_to_ps(...)) + pars = ps_union(partsets) # partsets does not have names here, wihch is what we want. + + names(partsets) = names(values) idmapping = map(partsets, function(x) x$ids()) - pars = ps_union(partsets) - pars$set_id = self$set_id - parsparams = pars$params_unid - parsnames = names(parsparams) + # only add the dependencies that are also in the tuning PS on = id = NULL # pacify static code check pmap(self$deps[id %in% names(idmapping) & on %in% names(partsets), c("on", "id", "cond")], function(on, id, cond) { @@ -745,7 +772,7 @@ ParamSet = R6Class("ParamSet", return(NULL) } # remove infeasible values from condition - cond$rhs = keep(cond$rhs, parsparams[[on]]$test) + cond$rhs = keep(cond$rhs, function(x) partsets[[on]]$test(set_names(list(x), on))) if (!length(cond$rhs)) { # no value is feasible, but there may be a trafo that fixes this # so we are forgiving here. diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index e242aee5..bdb71f62 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -164,13 +164,13 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, changing_values = x[names(x) %in% changing_ids] names(changing_values) = private$.translation[names(changing_values), original_id] changing_values = trafo(changing_values) - prefix = names(sets)[[set_index]] + prefix = names(private$.sets)[[set_index]] if (prefix != "") { names(changing_values) = sprintf("%s.%s", prefix, names(changing_values)) } changing_values }), recursive = FALSE) - unchanged_ids = private$.translation[!J(set_index), id, on = "owner_ps_index"] + unchanged_ids = private$.translation[!J(private$.children_with_trafos), id, on = "owner_ps_index"] unchanged = x[names(x) %in% unchanged_ids] c(unchanged, changed) }, diff --git a/R/to_tune.R b/R/to_tune.R index 7e6cd5e6..4a92beac 100644 --- a/R/to_tune.R +++ b/R/to_tune.R @@ -165,7 +165,7 @@ to_tune = function(...) { content = p_fct(levels = content) } else { if (inherits(content, "Domain")) { - bounded = ps(x = content, .allow_dangling_dependencies = TRUE)$all_bounded + bounded = domain_is_bounded(content) } else { bounded = content$all_bounded } @@ -208,52 +208,50 @@ print.ObjectTuneToken = function(x, ...) { # does not go out of range. # # Makes liberal use to `pslike_to_ps` (converting Param, ParamSet, Domain to ParamSet) -tunetoken_to_ps = function(tt, param, id) { +# param is a data.table that is potentially modified by reference using data.table set() methods. +tunetoken_to_ps = function(tt, param) { UseMethod("tunetoken_to_ps") } -tunetoken_to_ps.FullTuneToken = function(tt, param, id) { - if (!param$all_bounded) { - stopf("%s must give a range for unbounded parameter %s.", tt$call, id) +tunetoken_to_ps.FullTuneToken = function(tt, param) { + if (!domain_is_bounded(param)) { + stopf("%s must give a range for unbounded parameter %s.", tt$call, param$id) } if (isTRUE(tt$content$logscale)) { - if (!param$is_number) stop("%s (%s): logscale only valid for numeric / integer parameters.", tt$call, id) - tunetoken_to_ps.RangeTuneToken(list(content = list(logscale = tt$content$logscale), tt$call), param, id) + if (!domain_is_number(param)) stop("%s (%s): logscale only valid for numeric / integer parameters.", tt$call, param$id) + tunetoken_to_ps.RangeTuneToken(list(content = list(logscale = tt$content$logscale), tt$call), param) } else { - pslike_to_ps(param, tt$call, param, id) + pslike_to_ps(param, tt$call, param) } } -tunetoken_to_ps.RangeTuneToken = function(tt, param, id) { - if (!param$is_number) { +tunetoken_to_ps.RangeTuneToken = function(tt, param) { + if (!domain_is_number(param)) { stopf("%s for non-numeric param must have zero or one argument.", tt$call) } - invalidpoints = discard(tt$content, function(x) is.null(x) || param$test(set_names(list(x, param$ids()))) + invalidpoints = discard(tt$content, function(x) is.null(x) || domain_test(param, set_names(list(x), param$id))) invalidpoints$logscale = NULL if (length(invalidpoints)) { stopf("%s range not compatible with param %s.\nBad value(s):\n%s\nParameter:\n%s", - tt$call, id, repr(invalidpoints), repr(param)) + tt$call, param$id, repr(invalidpoints), repr(param)) } - lower = tt$content$lower %??% param$lower - upper = tt$content$upper %??% param$upper + bound_lower = tt$content$lower %??% param$lower + bound_upper = tt$content$upper %??% param$upper - if (!is.finite(lower) || !is.finite(upper)) stopf("%s range must be bounded, but is [%s, %s]", id, lower, upper) - - if (isTRUE(tt$content$logscale)) { - # for logscale: create p_int / p_dbl object. Doesn't work if there is a numeric param class that we don't know about. - constructor = switch(param$class, ParamInt = p_int, ParamDbl = p_dbl, - stopf("%s: logscale for parameter %s of class %s not supported", tt$call, id, param$class)) - content = constructor(lower = lower, upper = upper, logscale = tt$content$logscale) - } else { - # general approach: create Param object - content = get_r6_constructor(param$class)$new(id = id, lower = lower, upper = upper) + if (!is.finite(bound_lower) || !is.finite(bound_upper)) { + stopf("%s range must be bounded, but is [%s, %s]", param$id, bound_lower, bound_upper) } - pslike_to_ps(content, tt$call, param, id) + + # create p_int / p_dbl object. Doesn't work if there is a numeric param class that we don't know about :-/ + constructor = switch(param$cls, ParamInt = p_int, ParamDbl = p_dbl, + stopf("%s: logscale for parameter %s of class %s not supported", tt$call, param$id, param$class)) + content = constructor(lower = bound_lower, upper = bound_upper, logscale = tt$content$logscale) + pslike_to_ps(content, tt$call, param) } -tunetoken_to_ps.ObjectTuneToken = function(tt, param, id) { - pslike_to_ps(tt$content, tt$call, param, id) +tunetoken_to_ps.ObjectTuneToken = function(tt, param) { + pslike_to_ps(tt$content, tt$call, param) } # Convert something that is `ParamSet`-like (ParamSet, Param, or Domain) to a `ParamSet`. @@ -265,36 +263,42 @@ tunetoken_to_ps.ObjectTuneToken = function(tt, param, id) { # @param param: `Param`, that the `pslike` refers to, and therefore needs to be compatible to # @param usersupplied: whether the `pslike` is supplied by the user (and should therefore be checked more thoroughly) # This is currently used for user-supplied ParamSets, for which the trafo must be adjusted. -pslike_to_ps = function(pslike, call, param, id, usersupplied = TRUE) { +pslike_to_ps = function(pslike, call, param, usersupplied = TRUE) { UseMethod("pslike_to_ps") } -pslike_to_ps.Domain = function(pslike, call, param, id, usersupplied = TRUE) { - pslike = invoke(ps, .allow_dangling_dependencies = TRUE, .args = set_names(list(pslike), id)) - pslike_to_ps(pslike, call, param, id, usersupplied = FALSE) +pslike_to_ps.Domain = function(pslike, call, param, usersupplied = TRUE) { + # 'pslike' could be the same as 'param', i.e. a Domain with some cols missing. + # We could consider allowing construction of ParamSet from these unfinished domains instead. + pslike = ParamSet$new(structure(list(pslike), names = param$id), allow_dangling_dependencies = TRUE) + pslike_to_ps(pslike, call, param, usersupplied = FALSE) } -pslike_to_ps.ParamSet = function(pslike, call, param, id, usersupplied = TRUE) { +pslike_to_ps.ParamSet = function(pslike, call, param, usersupplied = TRUE) { pslike = pslike$flatten() alldeps = pslike$deps # temporarily hide dangling deps on = NULL # pacify static code check pslike$deps = pslike$deps[on %in% pslike$ids()] - testpoints = generate_design_grid(pslike, 2)$transpose() + testpoints = generate_design_random(pslike, 10)$transpose() pslike$deps = alldeps invalidpoints = discard(testpoints, function(x) length(x) == 1) if (length(invalidpoints)) { stopf("%s for param %s does not have a trafo that reduces output to one dimension.\nExample:\n%s", - call, id, repr(invalidpoints[[1]])) + call, param$id, repr(invalidpoints[[1]])) } - invalidpoints = discard(testpoints, function(x) param$test(x)) + + # do set_names because we ignore the name generated by the trafo + invalidpoints = discard(testpoints, function(x) domain_test(param, set_names(x, param$id))) if (length(invalidpoints)) { stopf("%s generates points that are not compatible with param %s.\nBad value:\n%s\nParameter:\n%s", - call, id, repr(invalidpoints[[1]][[1]]), repr(param)) + call, param$id, repr(invalidpoints[[1]][[1]]), repr(param)) } - if (usersupplied && pslike$has_trafo) { - trafo = pslike$trafo %??% identity - pname = id + if (usersupplied) { + # if the user gave us a paramset, then we need to make sure the resulting name is correct. + # we therefore always add a trafo here, even if the user-supplied ParamSet does not have a trafo itself + trafo = pslike$extra_trafo %??% identity + pname = param$id pslike$extra_trafo = crate(function(x, param_set) { mlr3misc::set_names( checkmate::assert_list(trafo(x), len = 1, .var.name = sprintf("Trafo for tuning ParamSet for parameter %s", pname)), diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R index 9c6b0fdd..29bb8a62 100644 --- a/tests/testthat/helper_03_domain.R +++ b/tests/testthat/helper_03_domain.R @@ -22,3 +22,11 @@ expect_equal_ps = function(a, b) { expect_equal(normalize_ids(a), normalize_ids(b)) } +reset_indices = function(p) { + + setindexv(p$.__enclos_env__$private$.tags, NULL) + setindexv(p$.__enclos_env__$private$.trafos, NULL) + setindexv(p$.__enclos_env__$private$.params, NULL) + setindexv(p$.__enclos_env__$private$.deps, NULL) + p +} diff --git a/tests/testthat/test_to_tune.R b/tests/testthat/test_to_tune.R index 0bb1f7e3..31c12241 100644 --- a/tests/testthat/test_to_tune.R +++ b/tests/testthat/test_to_tune.R @@ -82,7 +82,7 @@ test_that("$check() works on TuneToken", { expect_string(pars$check(list(lgl = to_tune(0, 1))), "must have zero or one argument") expect_error(pars$search_space(list(lgl = to_tune(0, 1))), "must have zero or one argument") - expect_error(pars$search_space(list(xxx = to_tune())), "Must be a subset of .*x,xub,y,uty,uty1,fct,lgl") + expect_error(pars$search_space(list(xxx = to_tune())), "ust be a subset of .*x.*xub.*y.*uty.*uty1.*fct.*lgl") }) @@ -98,11 +98,10 @@ test_that("Tune ParamSet is created", { pars_tune = pars$search_space(list(x = to_tune(), y = to_tune(), fct = to_tune(), lgl = to_tune())) # the following is necessary because $add modifies index of the .deps table, and one of the `$values` is not named. - pars$add(ParamSet_legacy$new()) - pars$values = list() - setindex(pars_tune$deps, NULL) pars_tune$values = list() + reset_indices(pars) + reset_indices(pars_tune) expect_equal_ps(pars, pars_tune) pars_unbound = ParamSet_legacy$new(list( @@ -129,7 +128,7 @@ test_that("Tune ParamSet is created", { setindex(pars_tune$deps, NULL) expect_equal_ps(pars, pars_tune) - pars_tune = pars_unbound$search_space(list(x = to_tune(ParamInt$new("z", 0, 10)), y = to_tune(ps(z = p_dbl(0, 10, special_vals = list("x")))), + pars_tune = pars_unbound$search_space(list(x = to_tune(ParamInt$new("y", 0, 10)), y = to_tune(ps(z = p_dbl(0, 10, special_vals = list("x")))), fct = to_tune(c("x", "y")), lgl = to_tune(p_lgl()))) # to_tune from ps() generates messed up value order @@ -164,7 +163,7 @@ test_that("Trafo works as expected", { ParamFct$new("x", c("a", "b")), ParamFct$new("y", c("a", "b")) )) - inpars$trafo = function(x, param_set) list(x$x != x$y) + inpars$extra_trafo = function(x, param_set) list(x$x != x$y) indesign = generate_design_grid(inpars) outdesign = generate_design_grid(pars$search_space(list(lgl = to_tune(inpars)))) @@ -184,9 +183,9 @@ test_that("Dependencies work", { ParamInt$new("z2", lower = 0, upper = 1) )) - pars$add_dep("x", "y", CondEqual$new(1)) - pars$add_dep("x", "z", CondEqual$new(1)) - pars$add_dep("z2", "x", CondEqual$new(1)) + pars$add_dep("x", "y", CondEqual(1)) + pars$add_dep("x", "z", CondEqual(1)) + pars$add_dep("z2", "x", CondEqual(1)) # if the tune paramset contains a "z2" in place of "y2", then the x->"z2" dependency is actually kept. This may be useful. # if some parameter depends on y, that dependency is lost. nothing we can do here. @@ -202,24 +201,24 @@ test_that("Dependencies work", { ParamInt$new("z", lower = 0, upper = 1), ParamInt$new("z2", lower = 0, upper = 1) )) - pars$add_dep("y", "x", CondEqual$new(1)) - pars$add_dep("x", "z", CondEqual$new(1)) - pars$add_dep("z2", "x", CondEqual$new(1)) + pars$add_dep("y", "x", CondEqual(1)) + pars$add_dep("x", "z", CondEqual(1)) + pars$add_dep("z2", "x", CondEqual(1)) # only the relevant dependency is kept tuneps = pars$search_space(list(x = to_tune(), y = to_tune())) - expect_equal(setindex(tuneps$deps, NULL), data.table(id = "y", on = "x", cond = list(CondEqual$new(1)))) + expect_equal(setindex(tuneps$deps, NULL), data.table(id = "y", on = "x", cond = list(CondEqual(1)))) #dependencies are kept between params, even if the dependor is trafo'd from other params tuneps = pars$search_space(list(x = to_tune(), y = to_tune(ps(y1 = p_int(0, 1), y2 = p_int(0, 1), .extra_trafo = function(x, param_set) list(abs(x$y1 - x$y2)))))) - expect_equal(setindex(tuneps$deps, NULL), data.table(id = c("y1", "y2"), on = c("x", "x"), cond = list(CondEqual$new(1)))) + expect_equal(setindex(tuneps$deps, NULL), data.table(id = c("y1", "y2"), on = c("x", "x"), cond = list(CondEqual(1)))) tuneps = pars$search_space(list(x = to_tune(), y = to_tune(ps(y1 = p_int(0, 1), y2 = p_int(0, 1, depends = y1 == 1), .extra_trafo = function(x, param_set) list(min(x$y1, x$y2, na.rm = TRUE)))))) # mixing dependencies from inside and outside - expect_equal(setindex(tuneps$deps, NULL), data.table(id = c("y2", "y1", "y2"), on = c("y1", "x", "x"), cond = list(CondEqual$new(1)))) + expect_equal(setindex(tuneps$deps, NULL), data.table(id = c("y2", "y1", "y2"), on = c("y1", "x", "x"), cond = list(CondEqual(1)))) parsnodep = ParamSet_legacy$new(list( @@ -232,14 +231,14 @@ test_that("Dependencies work", { # amazing: dependencies across to_tune tuneps = parsnodep$search_space(list(x = to_tune(p_int(0, 1, depends = y == 0)), y = to_tune())) - expect_equal(setindex(tuneps$deps, NULL), data.table(id = "x", on = "y", cond = list(CondEqual$new(0)))) + expect_equal(setindex(tuneps$deps, NULL), data.table(id = "x", on = "y", cond = list(CondEqual(0)))) tuneps = pars$search_space(list(x = to_tune(), y = to_tune(ps(y1 = p_int(0, 1), y2 = p_int(0, 1, depends = x == 1), .extra_trafo = function(x, param_set) list(min(x$y1, x$y2, na.rm = TRUE)), .allow_dangling_dependencies = TRUE)))) # mixing dependencies from inside and across to_tune. I am the only person in this building capable of coding this. - expect_equal(setindex(tuneps$deps, NULL), data.table(id = c("y2", "y1", "y2"), on = c("x", "x", "x"), cond = list(CondEqual$new(1)))) + expect_equal(setindex(tuneps$deps, NULL), data.table(id = c("y2", "y1", "y2"), on = c("x", "x", "x"), cond = list(CondEqual(1)))) expect_error(pars$search_space(list(x = to_tune(), y = to_tune(ps(y1 = p_int(0, 1), y2 = p_int(0, 1, depends = z == 1), .extra_trafo = function(x, param_set) list(min(x$y1, x$y2, na.rm = TRUE)), .allow_dangling_dependencies = TRUE)))), @@ -266,7 +265,7 @@ test_that("Dependencies work", { ParamFct$new("x", c("a", "b", "c")), ParamLgl$new("y") )) - largeps$add_dep("y", "x", CondAnyOf$new(c("a", "b"))) + largeps$add_dep("y", "x", CondAnyOf(c("a", "b"))) res = largeps$search_space(list(x = to_tune(c("a", "b")), y = to_tune())) expect_equal(res$deps$cond[[1]]$rhs, c("a", "b")) @@ -284,9 +283,7 @@ test_that("ParamSetCollection works", { ps1 = ParamSet_legacy$new(list(ParamInt$new("x"), ParamInt$new("y"))) ps2 = ParamSet_legacy$new(list(ParamInt$new("a"))) - ps1$set_id = "prefix" - - psc = ParamSetCollection$new(list(ps1, ps2)) + psc = ParamSetCollection$new(list(prefix = ps1, ps2)) ps1$values$x = to_tune(0, 10) ps1$values$y = to_tune(ps(y1 = p_int(0, 1), y2 = p_int(0, 1), .extra_trafo = function(x, param_set) list(y = x$y1 * x$y2))) @@ -346,19 +343,19 @@ test_that("partial bounds in tunetoken", { ParamDbl$new("z", upper = 10) )) - expect_equal(pars$search_space(list(x = to_tune()))$params[[1]], ParamInt$new("x", lower = 0, upper = 10)) + expect_equal_ps(pars$search_space(list(x = to_tune())), ParamInt$new("x", lower = 0, upper = 10)) - expect_equal(pars$search_space(list(x = to_tune(lower = 1)))$params[[1]], ParamInt$new("x", lower = 1, upper = 10)) - expect_equal(pars$search_space(list(x = to_tune(upper = 1)))$params[[1]], ParamInt$new("x", lower = 0, upper = 1)) - expect_equal(pars$search_space(list(x = to_tune(lower = 1, upper = 2)))$params[[1]], ParamInt$new("x", lower = 1, upper = 2)) + expect_equal_ps(pars$search_space(list(x = to_tune(lower = 1))), ParamInt$new("x", lower = 1, upper = 10)) + expect_equal_ps(pars$search_space(list(x = to_tune(upper = 1))), ParamInt$new("x", lower = 0, upper = 1)) + expect_equal_ps(pars$search_space(list(x = to_tune(lower = 1, upper = 2))), ParamInt$new("x", lower = 1, upper = 2)) expect_error(pars$search_space(list(y = to_tune(lower = 1))), "y range must be bounded, but is \\[1, Inf\\]") - expect_equal(pars$search_space(list(y = to_tune(upper = 1)))$params[[1]], ParamDbl$new("y", lower = 0, upper = 1)) - expect_equal(pars$search_space(list(y = to_tune(lower = 1, upper = 2)))$params[[1]], ParamDbl$new("y", lower = 1, upper = 2)) + expect_equal_ps(pars$search_space(list(y = to_tune(upper = 1))), ParamDbl$new("y", lower = 0, upper = 1)) + expect_equal_ps(pars$search_space(list(y = to_tune(lower = 1, upper = 2))), ParamDbl$new("y", lower = 1, upper = 2)) expect_error(pars$search_space(list(z = to_tune(upper = 1))), "z range must be bounded, but is \\[-Inf, 1\\]") - expect_equal(pars$search_space(list(z = to_tune(lower = 1)))$params[[1]], ParamDbl$new("z", lower = 1, upper = 10)) - expect_equal(pars$search_space(list(z = to_tune(lower = 1, upper = 2)))$params[[1]], ParamDbl$new("z", lower = 1, upper = 2)) + expect_equal_ps(pars$search_space(list(z = to_tune(lower = 1))), ParamDbl$new("z", lower = 1, upper = 10)) + expect_equal_ps(pars$search_space(list(z = to_tune(lower = 1, upper = 2))), ParamDbl$new("z", lower = 1, upper = 2)) expect_output(print(to_tune()), "entire parameter range") expect_output(print(to_tune(lower = 1)), "range \\[1, \\.\\.\\.]") @@ -379,8 +376,10 @@ test_that("logscale in tunetoken", { ParamDbl$new("y", lower = 0) )) - expect_equal(pars$search_space(list(x = to_tune(logscale = TRUE)))$params[[1]], ParamDbl$new("x", lower = log(.5), upper = log(11))) - expect_equal(pars$search_space(list(y = to_tune(lower = 1, upper = 10, logscale = TRUE)))$params[[1]], ParamDbl$new("y", lower = log(1), upper = log(10))) + expect_equal(reset_indices(pars$search_space(list(x = to_tune(logscale = TRUE))))$.__enclos_env__$private$.params, + reset_indices(ParamDbl$new("x", lower = log(.5), upper = log(11)))$.__enclos_env__$private$.params) + expect_equal(reset_indices(pars$search_space(list(y = to_tune(lower = 1, upper = 10, logscale = TRUE))))$.__enclos_env__$private$.params, + reset_indices(ParamDbl$new("y", lower = log(1), upper = log(10)))$.__enclos_env__$private$.params) expect_error(pars$search_space(list(y = to_tune(upper = 10, logscale = TRUE))), "When logscale is TRUE then lower bound must be strictly greater than 0") diff --git a/tests/testthat/test_trafo.R b/tests/testthat/test_trafo.R index 22aec3df..febd6a4d 100644 --- a/tests/testthat/test_trafo.R +++ b/tests/testthat/test_trafo.R @@ -19,7 +19,7 @@ test_that("trafo", { expect_output(print(ps), "Trafo is set") d1 = generate_design_grid(ps, resolution = 4) dd1 = d1$data - d2 = ps$trafo(dd1) + d2 = map_dtr(seq_len(nrow(dd1)), function(i) ps$trafo(as.list(dd1[i]))) expect_numeric(d2$x, lower = 0) expect_numeric(d2$w1, lower = 0, upper = 1) expect_numeric(d2$w2, lower = 0, upper = 1) From 361c50516cbb4c5aac3137c10139ce22e810ce9e Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 25 Aug 2023 02:21:26 +0200 Subject: [PATCH 41/64] S3 consistency --- R/Domain_methods.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Domain_methods.R b/R/Domain_methods.R index 4c4f44bb..4947f120 100644 --- a/R/Domain_methods.R +++ b/R/Domain_methods.R @@ -87,7 +87,7 @@ domain_nlevels.Domain = function(param) rep(Inf, nrow(param)) domain_is_bounded.Domain = function(param) rep(FALSE, nrow(param)) #' @export -domain_qunif.Domain = function(param) stop("undefined") +domain_qunif.Domain = function(param, x) stop("undefined") #' @export domain_sanitize.Domain = function(param, values) values From d23b1e9edf8c3329d9c04f49450221997af2d1bc Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 25 Aug 2023 16:18:07 +0200 Subject: [PATCH 42/64] tests pass --- R/ParamDbl.R | 5 +- R/ParamSet.R | 2 +- R/generate_design_sobol.R | 4 +- tests/testthat/test_ParamSet.R | 16 ++-- tests/testthat/test_domain.R | 102 +++++++++++++------------- tests/testthat/test_generate_design.R | 14 ++-- 6 files changed, 75 insertions(+), 68 deletions(-) diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 8932f149..68a1560c 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -34,7 +34,10 @@ domain_check.ParamDbl = function(param, values) { #' @export domain_sanitize.ParamDbl = function(param, values) { - as.list(min(max(as.numeric(values), param$lower), param$upper)) + values = as.numeric(values) + values[values < param$lower] = param$lower + values[values > param$upper] = param$upper + as.list(values) } #' @export diff --git a/R/ParamSet.R b/R/ParamSet.R index e8e27b80..ab3f9ab2 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -846,7 +846,7 @@ as.data.table.ParamSet = function(x, ...) { # nolint #' @export rd_info.ParamSet = function(obj, descriptions = character(), ...) { # nolint - if (length(obj$params) == 0L) { + if (obj$length == 0L) { return("Empty ParamSet") } diff --git a/R/generate_design_sobol.R b/R/generate_design_sobol.R index 01b86353..e9b8fc2e 100644 --- a/R/generate_design_sobol.R +++ b/R/generate_design_sobol.R @@ -33,12 +33,12 @@ generate_design_sobol = function(param_set, n) { ids = param_set$ids() if (n == 0) { - d = matrix(nrow = 0, ncol = param_set$length) + d = matrix(numeric(0), nrow = 0, ncol = param_set$length) } else { seed = sample(.Machine$integer.max, size = 1L) d = spacefillr::generate_sobol_set(n, dim = param_set$length, seed = seed) } colnames(d) = ids - d = map_dtc(ids, function(id) param_set$params[[id]]$qunif(d[, id])) + d = param_set$qunif(d) Design$new(param_set, set_names(d, ids), remove_dupl = FALSE) # user wants n-points, dont remove } diff --git a/tests/testthat/test_ParamSet.R b/tests/testthat/test_ParamSet.R index af93ec97..260e5538 100644 --- a/tests/testthat/test_ParamSet.R +++ b/tests/testthat/test_ParamSet.R @@ -360,20 +360,20 @@ test_that("ParamSet$values convert nums to ints for ParamInt", { }) test_that("Empty ParamSets are named (#351)", { - ps = ps()$add(ps(x = p_lgl())) + ps = ps_union(list(ps(), ps(x = p_lgl()))) expect_names(names(ps$values), type = "strict") expect_is(ps$search_space(), "ParamSet") }) test_that("set_values checks inputs correctly", { - param_set = ps(a = paradox::p_dbl(), b = paradox::p_dbl()) + param_set = ps(a = p_dbl(), b = p_dbl()) expect_error(param_set$set_values(a = 2, .values = list(a = 1))) expect_error(param_set$set_values(2)) expect_error(param_set$set_values(.values = list(1))) }) test_that("set_values works for ... with correct inputs", { - param_set = ps(a = paradox::p_dbl(), b = paradox::p_dbl()) + param_set = ps(a = p_dbl(), b = p_dbl()) param_set$values$a = 1 param_set$set_values(b = 2, .insert = FALSE) expect_true(is.null(param_set$values$a)) @@ -385,7 +385,7 @@ test_that("set_values works for ... with correct inputs", { }) test_that("set_values works for .values with correct inputs", { - param_set = ps(a = paradox::p_dbl(), b = paradox::p_dbl()) + param_set = ps(a = p_dbl(), b = p_dbl()) param_set$values$a = 1 param_set$set_values(.values = list(b = 2), .insert = FALSE) expect_true(is.null(param_set$values$a)) @@ -397,7 +397,7 @@ test_that("set_values works for .values with correct inputs", { }) test_that("set_values works for .values and ... with correct inputs", { - param_set = ps(a = paradox::p_dbl(), b = paradox::p_dbl(), c = paradox::p_dbl()) + param_set = ps(a = p_dbl(), b = p_dbl(), c = p_dbl()) param_set$values$a = 1 param_set$set_values(b = 2, .values = list(c = 3), .insert = TRUE) expect_true(param_set$values$a == 1) @@ -424,6 +424,10 @@ test_that("set_values allows to unset parameters by setting them to NULL", { param_set = ps(a = p_int()) param_set$set_values(a = 1) + # .insert = FALSE can also set values to NULL + expect_error(param_set$set_values(.values = list(a = NULL), .insert = FALSE), "not 'NULL'") + param_set = ps(a = p_int(special_vals = list(NULL))) + param_set$set_values(a = 1) param_set$set_values(.values = list(a = NULL), .insert = FALSE) - expect_true(length(param_set$values) == 0) + expect_identical(param_set$values, list(a = NULL)) }) diff --git a/tests/testthat/test_domain.R b/tests/testthat/test_domain.R index 67e8ae27..ae78293c 100644 --- a/tests/testthat/test_domain.R +++ b/tests/testthat/test_domain.R @@ -296,54 +296,54 @@ test_that("logscale in domains", { }) -test_that("$.has_logscale flag works", { - pps = ps(x = p_int(1, 2, logscale = TRUE)) - expect_true(get_private(pps$params[["x"]])$.has_logscale) - - pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20)) - expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_logscale), c(x = TRUE, y = FALSE)) - - pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20), z = p_int(1, 2, trafo = function(x) 2^x)) - expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_logscale), c(x = TRUE, y = FALSE, z = FALSE)) -}) - -test_that("$.has_trafo flag works", { - pps = ps(x = p_int(1, 2, trafo = function(x) 2^x)) - expect_true(get_private(pps$params[["x"]])$.has_trafo) - - pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20)) - expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_trafo), c(x = TRUE, y = FALSE)) - - pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20), z = p_int(1, 2, logscale = TRUE)) - expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_trafo), c(x = TRUE, y = FALSE, z = FALSE)) -}) - -test_that("$.extra_trafo flag works", { - pps = ps(x = p_int(1, 2), .extra_trafo = function(x, param_set) { - x = 1 - x - }) - expect_true(get_private(pps)$.has_extra_trafo) - - pps = ps(x = p_int(1, 2, logscale = TRUE)) - expect_false(get_private(pps)$.has_extra_trafo) - - pps$trafo = function(x, param_set) { - x = 1 - x - } - expect_true(get_private(pps)$.has_extra_trafo) - - pps$trafo = NULL - expect_false(get_private(pps)$.has_extra_trafo) - - pps = ps(x = p_int(1, 10)) - pps$values$x = to_tune() - search_space = pps$search_space() - expect_false(get_private(search_space)$.has_extra_trafo) - - pps = ps(x = p_int(1, 10)) - pps$values$x = to_tune(logscale = TRUE) - search_space = pps$search_space() - expect_false(get_private(search_space)$.has_extra_trafo) -}) +# test_that("$.has_logscale flag works", { +# pps = ps(x = p_int(1, 2, logscale = TRUE)) +# expect_true(get_private(pps$params[["x"]])$.has_logscale) + +# pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20)) +# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_logscale), c(x = TRUE, y = FALSE)) + +# pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20), z = p_int(1, 2, trafo = function(x) 2^x)) +# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_logscale), c(x = TRUE, y = FALSE, z = FALSE)) +# }) + +# test_that("$.has_trafo flag works", { +# pps = ps(x = p_int(1, 2, trafo = function(x) 2^x)) +# expect_true(get_private(pps$params[["x"]])$.has_trafo) + +# pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20)) +# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_trafo), c(x = TRUE, y = FALSE)) + +# pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20), z = p_int(1, 2, logscale = TRUE)) +# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_trafo), c(x = TRUE, y = FALSE, z = FALSE)) +# }) + +# test_that("$.extra_trafo flag works", { +# pps = ps(x = p_int(1, 2), .extra_trafo = function(x, param_set) { +# x = 1 +# x +# }) +# expect_true(get_private(pps)$.has_extra_trafo) + +# pps = ps(x = p_int(1, 2, logscale = TRUE)) +# expect_false(get_private(pps)$.has_extra_trafo) + +# pps$trafo = function(x, param_set) { +# x = 1 +# x +# } +# expect_true(get_private(pps)$.has_extra_trafo) + +# pps$trafo = NULL +# expect_false(get_private(pps)$.has_extra_trafo) + +# pps = ps(x = p_int(1, 10)) +# pps$values$x = to_tune() +# search_space = pps$search_space() +# expect_false(get_private(search_space)$.has_extra_trafo) + +# pps = ps(x = p_int(1, 10)) +# pps$values$x = to_tune(logscale = TRUE) +# search_space = pps$search_space() +# expect_false(get_private(search_space)$.has_extra_trafo) +# }) diff --git a/tests/testthat/test_generate_design.R b/tests/testthat/test_generate_design.R index fffcdce1..3f7b71ff 100644 --- a/tests/testthat/test_generate_design.R +++ b/tests/testthat/test_generate_design.R @@ -164,14 +164,14 @@ test_that("generate_design_sobol", { skip_if_not_installed("spacefillr") ps_list = list( - th_paramset_dbl1(), - th_paramset_full(), - th_paramset_repeated(), - th_paramset_numeric() + dbl = th_paramset_dbl1(), + full = th_paramset_full(), + repeated = th_paramset_repeated(), + numeric = th_paramset_numeric() ) - for (ps in ps_list) { - info = ps$set_id + for (info in names(ps_list)) { + ps = ps_list[[info]] d = generate_design_sobol(ps, 10) dd = d$data expect_data_table(d$data, nrows = 10, any.missing = FALSE, info = info) @@ -195,6 +195,6 @@ test_that("generate_design_sobol with zero rows", { ps = th_paramset_full() d = generate_design_sobol(ps, n = 0) - expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length, info = ps$set_id) + expect_data_table(d$data, any.missing = FALSE, nrows = 0, ncols = ps$length) }) From fc21a54317554fd5cb504cbd8b3bef034238e16f Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 25 Aug 2023 17:13:18 +0200 Subject: [PATCH 43/64] trafo info flags --- R/ParamDbl.R | 2 +- R/ParamInt.R | 2 +- R/ParamSet.R | 9 +++ tests/testthat/helper_03_domain.R | 1 + tests/testthat/test_domain.R | 102 +++++++++++++++--------------- tests/testthat/test_sampler.R | 4 ++ tests/testthat/test_to_tune.R | 4 +- 7 files changed, 69 insertions(+), 55 deletions(-) diff --git a/R/ParamDbl.R b/R/ParamDbl.R index 68a1560c..73000591 100644 --- a/R/ParamDbl.R +++ b/R/ParamDbl.R @@ -18,7 +18,7 @@ p_dbl = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ } Domain(cls = "ParamDbl", grouping = "ParamDbl", lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, storage_type = "numeric", - depends_expr = substitute(depends), init = init) + depends_expr = substitute(depends), init = init, cargo = if (logscale) "logscale") } #' @export diff --git a/R/ParamInt.R b/R/ParamInt.R index 84299e3e..495d4d86 100644 --- a/R/ParamInt.R +++ b/R/ParamInt.R @@ -25,7 +25,7 @@ p_int = function(lower = -Inf, upper = Inf, special_vals = list(), default = NO_ Domain(cls = cls, grouping = cls, lower = real_lower, upper = real_upper, special_vals = special_vals, default = default, tags = tags, tolerance = tolerance, trafo = trafo, storage_type = storage_type, - depends_expr = substitute(depends), init = init) + depends_expr = substitute(depends), init = init, cargo = if (logscale) "logscale") } #' @export diff --git a/R/ParamSet.R b/R/ParamSet.R index ab3f9ab2..ae8d186f 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -694,6 +694,8 @@ ParamSet = R6Class("ParamSet", is_empty = function() nrow(private$.params) == 0L, #' @field has_trafo (`logical(1)`)\cr Whether a `trafo` function is present, in parameters or in `extra_trafo`. has_trafo = function() !is.null(self$extra_trafo) || nrow(private$.trafos), + #' @field has_trafo (`logical(1)`)\cr Whether `extra_trafo` is set. + has_extra_trafo = function() !is.null(self$extra_trafo), #' @field has_deps (`logical(1)`)\cr Whether the parameter dependencies are present has_deps = function() nrow(self$deps) > 0L, #' @field has_constraint (`logical(1)`)\cr Whether parameter constraint is set. @@ -724,6 +726,13 @@ ParamSet = R6Class("ParamSet", #' @field default (named `list()`)\cr Default values of all parameters. If no default exists, element is not present. #' Named with parameter IDs. default = function() with(private$.params[!map_lgl(default, is_nodefault), .(default, id)], set_names(default, id)), + #' @field has_trafo_param (`logical()`)\cr Whether `trafo` is set for any parameter. + has_trafo_param = function() with(private$.params, set_names(id %in% private$.trafos$id, id)), + #' @field is_logscale (`logical()`)\cr Whether `trafo` was set to `logscale` during construction.\cr + #' Note that this only refers to the `logscale` flag set during construction, e.g. `p_dbl(logscale = TRUE)`. + #' If the parameter was set to logscale manually, e.g. through `p_dbl(trafo = exp)`, + #' this `is_logscale` will be `FALSE`. + is_logscale = function() with(private$.params, set_names(cls %in% c("ParamDbl", "ParamInt") & cargo == "logscale", id)), ############################ # Per-Parameter class properties (S3 method call) diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R index 29bb8a62..6ab3c278 100644 --- a/tests/testthat/helper_03_domain.R +++ b/tests/testthat/helper_03_domain.R @@ -16,6 +16,7 @@ expect_equal_ps = function(a, b) { setindexv(acp$.params, NULL) setindexv(acp$.trafos, NULL) setindexv(acp$.tags, NULL) + setindexv(acp$.deps, NULL) acl } diff --git a/tests/testthat/test_domain.R b/tests/testthat/test_domain.R index ae78293c..1a29fe1d 100644 --- a/tests/testthat/test_domain.R +++ b/tests/testthat/test_domain.R @@ -296,54 +296,54 @@ test_that("logscale in domains", { }) -# test_that("$.has_logscale flag works", { -# pps = ps(x = p_int(1, 2, logscale = TRUE)) -# expect_true(get_private(pps$params[["x"]])$.has_logscale) - -# pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20)) -# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_logscale), c(x = TRUE, y = FALSE)) - -# pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20), z = p_int(1, 2, trafo = function(x) 2^x)) -# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_logscale), c(x = TRUE, y = FALSE, z = FALSE)) -# }) - -# test_that("$.has_trafo flag works", { -# pps = ps(x = p_int(1, 2, trafo = function(x) 2^x)) -# expect_true(get_private(pps$params[["x"]])$.has_trafo) - -# pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20)) -# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_trafo), c(x = TRUE, y = FALSE)) - -# pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20), z = p_int(1, 2, logscale = TRUE)) -# expect_equal(map_lgl(pps$params, function(param) get_private(param)$.has_trafo), c(x = TRUE, y = FALSE, z = FALSE)) -# }) - -# test_that("$.extra_trafo flag works", { -# pps = ps(x = p_int(1, 2), .extra_trafo = function(x, param_set) { -# x = 1 -# x -# }) -# expect_true(get_private(pps)$.has_extra_trafo) - -# pps = ps(x = p_int(1, 2, logscale = TRUE)) -# expect_false(get_private(pps)$.has_extra_trafo) - -# pps$trafo = function(x, param_set) { -# x = 1 -# x -# } -# expect_true(get_private(pps)$.has_extra_trafo) - -# pps$trafo = NULL -# expect_false(get_private(pps)$.has_extra_trafo) - -# pps = ps(x = p_int(1, 10)) -# pps$values$x = to_tune() -# search_space = pps$search_space() -# expect_false(get_private(search_space)$.has_extra_trafo) - -# pps = ps(x = p_int(1, 10)) -# pps$values$x = to_tune(logscale = TRUE) -# search_space = pps$search_space() -# expect_false(get_private(search_space)$.has_extra_trafo) -# }) +test_that("$is_logscale flags work", { + pps = ps(x = p_int(1, 2, logscale = TRUE)) + expect_equal(pps$is_logscale, c(x = TRUE)) + + pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20)) + expect_equal(pps$is_logscale, c(x = TRUE, y = FALSE)) + + pps = ps(x = p_int(1, 2, logscale = TRUE), y = p_int(10, 20), z = p_int(1, 2, trafo = function(x) 2^x)) + expect_equal(pps$is_logscale, c(x = TRUE, y = FALSE, z = FALSE)) +}) + +test_that("$has_trafo_param flags work", { + pps = ps(x = p_int(1, 2, trafo = function(x) 2^x)) + expect_equal(pps$has_trafo_param, c(x = TRUE)) + + pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20)) + expect_equal(pps$has_trafo_param, c(x = TRUE, y = FALSE)) + + pps = ps(x = p_int(1, 2, trafo = function(x) 2^x), y = p_int(10, 20), z = p_int(1, 2, logscale = TRUE)) + expect_equal(pps$has_trafo_param, c(x = TRUE, y = FALSE, z = TRUE)) +}) + +test_that("$extra_trafo flag works", { + pps = ps(x = p_int(1, 2), .extra_trafo = function(x, param_set) { + x = 1 + x + }) + expect_true(pps$has_extra_trafo) + + pps = ps(x = p_int(1, 2, logscale = TRUE)) + expect_false(pps$has_extra_trafo) + + pps$extra_trafo = function(x, param_set) { + x = 1 + x + } + expect_true(pps$has_extra_trafo) + + pps$extra_trafo = NULL + expect_false(pps$has_extra_trafo) + + pps = ps(x = p_int(1, 10)) + pps$values$x = to_tune() + search_space = pps$search_space() + expect_false(search_space$has_extra_trafo) + + pps = ps(x = p_int(1, 10)) + pps$values$x = to_tune(logscale = TRUE) + search_space = pps$search_space() + expect_false(search_space$has_extra_trafo) +}) diff --git a/tests/testthat/test_sampler.R b/tests/testthat/test_sampler.R index 7cec709a..ee856759 100644 --- a/tests/testthat/test_sampler.R +++ b/tests/testthat/test_sampler.R @@ -112,6 +112,8 @@ test_that("we had a bug where creating the joint sampler changed the ps-ref of t setindexv(s1$param_set$.__enclos_env__$private$.params, NULL) setindexv(s1_expected$.__enclos_env__$private$.tags, NULL) setindexv(s1$param_set$.__enclos_env__$private$.tags, NULL) + setindexv(s1_expected$.__enclos_env__$private$.deps, NULL) + setindexv(s1$param_set$.__enclos_env__$private$.deps, NULL) expect_equal(s1$param_set, s1_expected) s2_expected = ParamSet_legacy$new(list(th_param_dbl())) @@ -120,6 +122,8 @@ test_that("we had a bug where creating the joint sampler changed the ps-ref of t setindexv(s2$param_set$.__enclos_env__$private$.params, NULL) setindexv(s2_expected$.__enclos_env__$private$.tags, NULL) setindexv(s2$param_set$.__enclos_env__$private$.tags, NULL) + setindexv(s2_expected$.__enclos_env__$private$.deps, NULL) + setindexv(s2$param_set$.__enclos_env__$private$.deps, NULL) expect_equal(s2$param_set, s2_expected) }) diff --git a/tests/testthat/test_to_tune.R b/tests/testthat/test_to_tune.R index 0dfc2413..339b989c 100644 --- a/tests/testthat/test_to_tune.R +++ b/tests/testthat/test_to_tune.R @@ -375,9 +375,9 @@ test_that("logscale in tunetoken", { ParamDbl$new("y", lower = 0) )) - expect_equal(reset_indices(pars$search_space(list(x = to_tune(logscale = TRUE))))$.__enclos_env__$private$.params, + expect_equal(reset_indices(pars$search_space(list(x = to_tune(logscale = TRUE))))$.__enclos_env__$private$.params[, cargo := list(list(NULL))], reset_indices(ParamDbl$new("x", lower = log(.5), upper = log(11)))$.__enclos_env__$private$.params) - expect_equal(reset_indices(pars$search_space(list(y = to_tune(lower = 1, upper = 10, logscale = TRUE))))$.__enclos_env__$private$.params, + expect_equal(reset_indices(pars$search_space(list(y = to_tune(lower = 1, upper = 10, logscale = TRUE))))$.__enclos_env__$private$.params[, cargo := list(list(NULL))], reset_indices(ParamDbl$new("y", lower = log(1), upper = log(10)))$.__enclos_env__$private$.params) expect_error(pars$search_space(list(y = to_tune(upper = 10, logscale = TRUE))), "When logscale is TRUE then lower bound must be strictly greater than 0") From fc6fdbcbe2d99ebf4f8cadd9777d0e37947c0e05 Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 25 Aug 2023 17:15:26 +0200 Subject: [PATCH 44/64] news and docs --- NEWS.md | 1 + R/ParamSet.R | 2 +- man/ParamSet.Rd | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index f236fa7e..79a119c3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,7 @@ * `ParamSet` objects are now less mutable. The only properties that can be changed are `values`, `tags`, `deps`, `constraint` and `extra_trafo`. * `ParamSet$is_bounded` is a vector with an entry for each parameter. Use `$all_bounded` for the previous behavior. * `Condition` objects are now S3 objects and can be constructed with `CondEqual()` and `CondAnyOf()`, instead of `CondXyz$new()`. (It is recommended to use the `Domain` interface for conditions, which has not changed) + * `ParamSet` has new fields `$is_logscale`, `$has_trafo_param` (per-param), and `$has_trafo_param` (scalar for the whole set). * Added a vignette which was previously a chapter in the `mlr3book` # paradox 0.11.1 diff --git a/R/ParamSet.R b/R/ParamSet.R index ae8d186f..06de955c 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -694,7 +694,7 @@ ParamSet = R6Class("ParamSet", is_empty = function() nrow(private$.params) == 0L, #' @field has_trafo (`logical(1)`)\cr Whether a `trafo` function is present, in parameters or in `extra_trafo`. has_trafo = function() !is.null(self$extra_trafo) || nrow(private$.trafos), - #' @field has_trafo (`logical(1)`)\cr Whether `extra_trafo` is set. + #' @field has_extra_trafo (`logical(1)`)\cr Whether `extra_trafo` is set. has_extra_trafo = function() !is.null(self$extra_trafo), #' @field has_deps (`logical(1)`)\cr Whether the parameter dependencies are present has_deps = function() nrow(self$deps) > 0L, diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 55cb9c24..71807d4f 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -97,6 +97,8 @@ Settable, if you want to remove dependencies or perform other changes.} \item{\code{has_trafo}}{(\code{logical(1)})\cr Whether a \code{trafo} function is present, in parameters or in \code{extra_trafo}.} +\item{\code{has_extra_trafo}}{(\code{logical(1)})\cr Whether \code{extra_trafo} is set.} + \item{\code{has_deps}}{(\code{logical(1)})\cr Whether the parameter dependencies are present} \item{\code{has_constraint}}{(\code{logical(1)})\cr Whether parameter constraint is set.} @@ -123,6 +125,13 @@ Named with parameter IDs.} \item{\code{default}}{(named \code{list()})\cr Default values of all parameters. If no default exists, element is not present. Named with parameter IDs.} +\item{\code{has_trafo_param}}{(\code{logical()})\cr Whether \code{trafo} is set for any parameter.} + +\item{\code{is_logscale}}{(\code{logical()})\cr Whether \code{trafo} was set to \code{logscale} during construction.\cr +Note that this only refers to the \code{logscale} flag set during construction, e.g. \code{p_dbl(logscale = TRUE)}. +If the parameter was set to logscale manually, e.g. through \code{p_dbl(trafo = exp)}, +this \code{is_logscale} will be \code{FALSE}.} + \item{\code{nlevels}}{(named \code{integer()})\cr Number of distinct levels of parameters. \code{Inf} for double parameters or unbounded integer parameters. Named with param IDs.} From 4430ea82fc7831b925adf0170916d050ad9f3fa1 Mon Sep 17 00:00:00 2001 From: mb706 Date: Fri, 25 Aug 2023 17:16:49 +0200 Subject: [PATCH 45/64] rename back to 'paradox' --- DESCRIPTION | 2 +- man/ParamSetCollection.Rd | 46 +++++++++---------- man/Sampler1D.Rd | 8 ++-- man/Sampler1DCateg.Rd | 8 ++-- man/Sampler1DNormal.Rd | 8 ++-- man/Sampler1DRfun.Rd | 8 ++-- man/Sampler1DUnif.Rd | 8 ++-- man/SamplerHierarchical.Rd | 8 ++-- man/SamplerJointIndep.Rd | 8 ++-- man/SamplerUnif.Rd | 8 ++-- ...autology-package.Rd => paradox-package.Rd} | 8 ++-- 11 files changed, 60 insertions(+), 60 deletions(-) rename man/{tautology-package.Rd => paradox-package.Rd} (88%) diff --git a/DESCRIPTION b/DESCRIPTION index 5cb51eb6..90ba5d7e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Type: Package -Package: tautology +Package: paradox Title: Define and Work with Parameter Spaces for Complex Algorithms Version: 0.12.0 diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index a0ad8193..a5a83524 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -27,7 +27,7 @@ If you call \code{deps} on the collection, you are returned a complete table of } } \section{Super class}{ -\code{\link[tautology:ParamSet]{tautology::ParamSet}} -> \code{ParamSetCollection} +\code{\link[paradox:ParamSet]{paradox::ParamSet}} -> \code{ParamSetCollection} } \section{Active bindings}{ \if{html}{\out{
}} @@ -56,28 +56,28 @@ When you set values, all previously set values will be unset / removed.} \if{html}{\out{
Inherited methods
}} diff --git a/man/Sampler1D.Rd b/man/Sampler1D.Rd index 77f561ff..12cf81b3 100644 --- a/man/Sampler1D.Rd +++ b/man/Sampler1D.Rd @@ -20,7 +20,7 @@ Other Sampler: } \concept{Sampler} \section{Super class}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{Sampler1D} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{Sampler1D} } \section{Active bindings}{ \if{html}{\out{
}} @@ -40,9 +40,9 @@ Returns the one Parameter that is sampled from.} \if{html}{\out{
Inherited methods
}} diff --git a/man/Sampler1DCateg.Rd b/man/Sampler1DCateg.Rd index 12f561b5..e8f1b966 100644 --- a/man/Sampler1DCateg.Rd +++ b/man/Sampler1DCateg.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{Sampler1DCateg} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{Sampler1DCateg} } \section{Public fields}{ \if{html}{\out{
}} @@ -39,9 +39,9 @@ Numeric vector of \code{param$nlevels} probabilities.} \if{html}{\out{
Inherited methods
}} diff --git a/man/Sampler1DNormal.Rd b/man/Sampler1DNormal.Rd index 51e43646..e4e7cfa6 100644 --- a/man/Sampler1DNormal.Rd +++ b/man/Sampler1DNormal.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{\link[tautology:Sampler1DRfun]{tautology::Sampler1DRfun}} -> \code{Sampler1DNormal} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{\link[paradox:Sampler1DRfun]{paradox::Sampler1DRfun}} -> \code{Sampler1DNormal} } \section{Active bindings}{ \if{html}{\out{
}} @@ -42,9 +42,9 @@ SD parameter of the normal distribution.} \if{html}{\out{
Inherited methods
}} diff --git a/man/Sampler1DRfun.Rd b/man/Sampler1DRfun.Rd index 5f33d932..22f6a87f 100644 --- a/man/Sampler1DRfun.Rd +++ b/man/Sampler1DRfun.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{Sampler1DRfun} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{Sampler1DRfun} } \section{Public fields}{ \if{html}{\out{
}} @@ -42,9 +42,9 @@ Random number generator function.} \if{html}{\out{
Inherited methods
}} diff --git a/man/Sampler1DUnif.Rd b/man/Sampler1DUnif.Rd index 08ef3bce..09cf10f0 100644 --- a/man/Sampler1DUnif.Rd +++ b/man/Sampler1DUnif.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:Sampler1D]{tautology::Sampler1D}} -> \code{Sampler1DUnif} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:Sampler1D]{paradox::Sampler1D}} -> \code{Sampler1DUnif} } \section{Methods}{ \subsection{Public methods}{ @@ -31,9 +31,9 @@ Other Sampler: \if{html}{\out{
Inherited methods
}} diff --git a/man/SamplerHierarchical.Rd b/man/SamplerHierarchical.Rd index 8e47d4a4..6f221be7 100644 --- a/man/SamplerHierarchical.Rd +++ b/man/SamplerHierarchical.Rd @@ -21,7 +21,7 @@ Other Sampler: } \concept{Sampler} \section{Super class}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{SamplerHierarchical} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{SamplerHierarchical} } \section{Public fields}{ \if{html}{\out{
}} @@ -41,9 +41,9 @@ List of \link{Sampler1D} objects that gives a Sampler for each \link{Param} in t \if{html}{\out{
Inherited methods
}} diff --git a/man/SamplerJointIndep.Rd b/man/SamplerJointIndep.Rd index 650f701f..25477667 100644 --- a/man/SamplerJointIndep.Rd +++ b/man/SamplerJointIndep.Rd @@ -19,7 +19,7 @@ Other Sampler: } \concept{Sampler} \section{Super class}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{SamplerJointIndep} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{SamplerJointIndep} } \section{Public fields}{ \if{html}{\out{
}} @@ -39,9 +39,9 @@ List of \link{Sampler} objects.} \if{html}{\out{
Inherited methods
}} diff --git a/man/SamplerUnif.Rd b/man/SamplerUnif.Rd index 27d80ad9..a405c822 100644 --- a/man/SamplerUnif.Rd +++ b/man/SamplerUnif.Rd @@ -21,7 +21,7 @@ Other Sampler: } \concept{Sampler} \section{Super classes}{ -\code{\link[tautology:Sampler]{tautology::Sampler}} -> \code{\link[tautology:SamplerHierarchical]{tautology::SamplerHierarchical}} -> \code{SamplerUnif} +\code{\link[paradox:Sampler]{paradox::Sampler}} -> \code{\link[paradox:SamplerHierarchical]{paradox::SamplerHierarchical}} -> \code{SamplerUnif} } \section{Methods}{ \subsection{Public methods}{ @@ -33,9 +33,9 @@ Other Sampler: \if{html}{\out{
Inherited methods
}} diff --git a/man/tautology-package.Rd b/man/paradox-package.Rd similarity index 88% rename from man/tautology-package.Rd rename to man/paradox-package.Rd index 58e7ee20..7e7ef662 100644 --- a/man/tautology-package.Rd +++ b/man/paradox-package.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/zzz.R \docType{package} -\name{tautology-package} -\alias{tautology} -\alias{tautology-package} -\title{tautology: Define and Work with Parameter Spaces for Complex Algorithms} +\name{paradox-package} +\alias{paradox} +\alias{paradox-package} +\title{paradox: Define and Work with Parameter Spaces for Complex Algorithms} \description{ Define parameter spaces, constraints and dependencies for arbitrary algorithms, to program on such spaces. Also includes statistical designs and random samplers. Objects are implemented as 'R6' classes. } From 26fb1f3246177540d320758a854c198fe9bf953c Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 26 Aug 2023 11:41:27 +0200 Subject: [PATCH 46/64] vignette progress --- R/ParamSet.R | 7 +- R/ParamSetCollection.R | 8 +- vignettes/indepth.Rmd | 359 +++++++++++++++++++++-------------------- 3 files changed, 194 insertions(+), 180 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index 06de955c..15e7cb10 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -219,10 +219,13 @@ ParamSet = R6Class("ParamSet", } extra_trafo = self$extra_trafo if (!is.null(extra_trafo)) { + # need to give the input of extra_trafo a different name than the output; otherwise the user would have to + # "force()" the x-argument of extra_trafo. + xin = x if (test_function(extra_trafo, args = c("x", "param_set"))) { - x = extra_trafo(x = x, param_set = param_set) + x = extra_trafo(x = xin, param_set = param_set) } else { - x = extra_trafo(x) + x = extra_trafo(xin) } } x diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index bdb71f62..0215162b 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -161,9 +161,11 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, changed = unlist(lapply(private$.children_with_trafos, function(set_index) { changing_ids = private$.translation[J(set_index), id, on = "owner_ps_index"] trafo = private$.sets[[set_index]]$extra_trafo - changing_values = x[names(x) %in% changing_ids] - names(changing_values) = private$.translation[names(changing_values), original_id] - changing_values = trafo(changing_values) + changing_values_in = x[names(x) %in% changing_ids] + names(changing_values_in) = private$.translation[names(changing_values_in), original_id] + # input of trafo() must not be changed after the call; otherwise the trafo would have to `force()` it in + # some circumstances. + changing_values = trafo(changing_values_in) prefix = names(private$.sets)[[set_index]] if (prefix != "") { names(changing_values) = sprintf("%s.%s", prefix, names(changing_values)) diff --git a/vignettes/indepth.Rmd b/vignettes/indepth.Rmd index 9b1d6a16..97defc87 100644 --- a/vignettes/indepth.Rmd +++ b/vignettes/indepth.Rmd @@ -7,14 +7,8 @@ vignette: > %\VignetteEncoding{UTF-8} --- - - -**Note:** Many of the concepts explained here, are also covered in the [mlr3book](https://github.com/mlr-org/mlr3book/). - ## Parameters (using paradox) - - The `paradox` package offers a language for the description of *parameter spaces*, as well as tools for useful operations on these parameter spaces. A parameter space is often useful when describing: @@ -28,152 +22,141 @@ The tools provided by `paradox` therefore relate to: * **Parameter sampling**: Generating parameter values that lie in the parameter space for systematic exploration of program behavior depending on these parameters `paradox` is, by nature, an auxiliary package that derives its usefulness from other packages that make use of it. -It is heavily utilized in other [mlr-org](https://github.com/mlr-org) packages such as `mlr3`, `mlr3pipelines`, and `mlr3tuning`. +It is heavily utilized in other [mlr-org](https://github.com/mlr-org) packages such as `mlr3`, `mlr3pipelines`, `mlr3tuning` and `miesmuschel`. ### Reference Based Objects -`paradox` is the spiritual successor to the `ParamHelpers` package and was written from scratch using the `R6` class system. -The most important consequence of this is that all objects created in `paradox` are "reference-based", unlike most other objects in R. -When a change is made to a `ParamSet` object, for example by adding a parameter using the `$add()` function, all variables that point to this `ParamSet` will contain the changed object. -To create an independent copy of a `ParamSet`, the `$clone()` method needs to be used: +`paradox` is the spiritual successor to the `ParamHelpers` package and was written from scratch. +The most important consequence of this is that some objects created in `paradox` are "reference-based", unlike most other objects in R. +When a change is made to a `ParamSet` object, for example by changing the `$values` field, all variables that point to this `ParamSet` will contain the changed object. +To create an independent copy of a `ParamSet`, the `$clone(deep = TRUE)` method needs to be used: ```{r} library("paradox") -ps = ParamSet$new() -ps2 = ps -ps3 = ps$clone(deep = TRUE) -print(ps) # the same for ps2 and ps3 +ps1 = ps(a = p_int(init = 1)) +ps2 = ps1 +ps3 = ps1$clone(deep = TRUE) +print(ps1) # the same for ps2 and ps3 ``` ```{r} -ps$add(ParamLgl$new("a")) +ps1$values$a = 2 ``` ```{r} -print(ps) # ps was changed -print(ps2) # contains the same reference as ps -print(ps3) # is a "clone" of the old (empty) ps +print(ps1) # ps1 value of 'a' was changed +print(ps2) # contains the same reference as ps1, so also changed +print(ps3) # is a "clone" of the old ps1 with 'a' == 1 ``` ### Defining a Parameter Space -#### Single Parameters +#### `Domain` Representing Single Parameters -The basic building block for describing parameter spaces is the **`Param`** class. -It represents a single parameter, which usually can take a single atomic value. +Parameter spaces are made up of individual parameters, which usually can take a single atomic value. Consider, for example, trying to configure the `rpart` package's `rpart.control` object. -It has various components (`minsplit`, `cp`, ...) that all take a single value, and that would all be represented by a different instance of a `Param` object. +It has various components (`minsplit`, `cp`, ...) that all take a single value. -The `Param` class has various subclasses that represent different value types: +These components are represented by `Domain` objects, which are constructed by calls of the form `p_xxx()`: -* `ParamInt`: Integer numbers -* `ParamDbl`: Real numbers -* `ParamFct`: String values from a set of possible values, similar to R `factor`s -* `ParamLgl`: Truth values (`TRUE` / `FALSE`), as `logical`s in R -* `ParamUty`: Parameter that can take any value +* `p_int()` for integer numbers +* `p_dbl()` for real numbers +* `p_fct()` for categorical values, similar to R `factor`s +* `p_lgl()` for truth values (`TRUE` / `FALSE`), as `logical`s in R +* `p_uty()` for parameters that can take any value -A particular instance of a parameter is created by calling the attached `$new()` function: +A `ParamSet` that represent a given set of parameters is created by calling `ps()` with named arguments that are `Domain` objects. +While `Domain` themselves are R objects that can in principle be handled and manipulated, they should not be changed after construction. ```{r} library("paradox") -parA = ParamLgl$new(id = "A") -parB = ParamInt$new(id = "B", lower = 0, upper = 10, tags = c("tag1", "tag2")) -parC = ParamDbl$new(id = "C", lower = 0, upper = 4, special_vals = list(NULL)) -parD = ParamFct$new(id = "D", levels = c("x", "y", "z"), default = "y") -parE = ParamUty$new(id = "E", custom_check = function(x) checkmate::checkFunction(x)) +param_set = ps( + parA = p_lgl(init = FALSE), + parB = p_int(lower = 0, upper = 10, tags = c("tag1", "tag2")), + parC = p_dbl(lower = 0, upper = 4, special_vals = list(NULL)), + parD = p_fct(levels = c("x", "y", "z"), default = "y"), + parE = p_uty(custom_check = function(x) checkmate::checkFunction(x)) +) +param_set ``` -Every parameter must have: +Every parameter can have: -* **id** - A name for the parameter within the parameter set -* **default** - A default value +* **default** - A default value, indicating the behaviour of something if the specific value is not given. +* **init** - An initial value, which is set in `$values` when the `ParamSet` is created. + Note that this is not the same as `default`: `default` is used when a parameter is not present in `$values`, while `init` is the value that is set upon creation. * **special_vals** - A list of values that are accepted even if they do not conform to the type * **tags** - Tags that can be used to organize parameters +* **trafo** - A transformation function that is applied to the parameter value after it has been sampled. + It is for example used through the `Design$transpose()` function after a `Design` was created by `generate_design_random()` or similar functions. -The numeric (`Int` and `Dbl`) parameters furthermore allow for specification of a **lower** and **upper** bound. -Meanwhile, the `Fct` parameter must be given a vector of **levels** that define the possible states its parameter can take. -The `Uty` parameter can also have a **`custom_check`** function that must return `TRUE` when a value is acceptable and may return a `character(1)` error description otherwise. +The numeric (`p_int()` and `p_dbl()`) parameters furthermore allow for specification of a **lower** and **upper** bound. +Meanwhile, the `p_fct()` parameter must be given a vector of **levels** that define the possible states its parameter can take. +The `p_uty` parameter can also have a **`custom_check`** function that must return `TRUE` when a value is acceptable and may return a `character(1)` error description otherwise. The example above defines `parE` as a parameter that only accepts functions. -All values which are given to the constructor are then accessible from the object for inspection using `$`. -Although all these values can be changed for a parameter after construction, this can be a bad idea and should be avoided when possible. +All values which are given to the constructor are then accessible from the `ParamSet` for inspection using `$`. +The `ParamSet` should be considered immutable, except for some fields such as `$values`, `$deps`, `$tags`. +Bounds and levels should not be changed after construction. +Instead, a new `ParamSet` should be constructed. -Instead, a new parameter should be constructed. Besides the possible values that can be given to a constructor, there are also the `$class`, `$nlevels`, `$is_bounded`, `$has_default`, `$storage_type`, `$is_number` and `$is_categ` slots that give information about a parameter. A list of all slots can be found in `?Param`. ```{r} -parB$lower -parA$levels -parE$class +param_set$lower +param_set$parD$levels +param_set$class ``` -It is also possible to get all information of a `Param` as `data.table` by calling `as.data.table()`. +It is also possible to get all information of a `ParamSet` as `data.table` by calling `as.data.table()`. ```{r} -as.data.table(parA) +as.data.table(param_set) ``` ##### Type / Range Checking -A `Param` object offers the possibility to check whether a value satisfies its condition, i.e. is of the right type, and also falls within the range of allowed values, using the `$test()`, `$check()`, and `$assert()` functions. +The `ParamSet` object offers the possibility to check whether a value satisfies its condition, i.e. is of the right type, and also falls within the range of allowed values, using the `$test()`, `$check()`, and `$assert()` functions. +Their argument must be a named list with values that are checked against the respective parameters, and it is possible to check only a subset of parameters. `test()` should be used within conditional checks and returns `TRUE` or `FALSE`, while `check()` returns an error description when a value does not conform to the parameter (and thus plays well with the `"checkmate::assert()"` function). `assert()` will throw an error whenever a value does not fit. ```{r} -parA$test(FALSE) -parA$test("FALSE") -parA$check("FALSE") +param_set$test(list(parA = FALSE, parB = 0)) +param_set$test(list(parA = "FALSE")) +param_set$check(list(parA = "FALSE")) ``` -Instead of testing single parameters, it is often more convenient to check a whole set of parameters using a `ParamSet`. - #### Parameter Sets -The ordered collection of parameters is handled in a `ParamSet`^[Although the name is suggestive of a "Set"-valued `Param`, this is unrelated to the other objects that follow the `ParamXxx` naming scheme.]. -It is initialized using the `$new()` function and optionally takes a list of `Param`s as argument. -Parameters can also be added to the constructed `ParamSet` using the `$add()` function. -It is even possible to add whole `ParamSet`s to other `ParamSet`s. +The ordered collection of parameters is handled in a `ParamSet`. +It is typically created by calling `ps()`, but can also be initialized using the `ParamSet$new()` function. +The main difference is that `ps()` takes named arguments, whereas `ParamSet$new()` takes a named list. +The latter makes it easier to construct a `ParamSet` programmatically, but is slightly more verbose. -```{r} -ps = ParamSet$new(list(parA, parB)) -ps$add(parC) -ps$add(ParamSet$new(list(parD, parE))) -print(ps) -``` - -The individual parameters can be accessed through the `$params` slot. -It is also possible to get information about all parameters in a vectorized fashion using mostly the same slots as for individual `Param`s (i.e. `$class`, `$levels` etc.), see `?ParamSet` for details. - -It is possible to reduce `ParamSet`s using the **`$subset`** method. -Be aware that it modifies a ParamSet in-place, so a "clone" must be created first if the original `ParamSet` should not be modified. +`ParamSet`s can be combined using `c()` or `ps_union` (the latter of which takes a list), and they have a `$subset()` method that allows for subsetting. +All of these functions return a new, cloned `ParamSet` object, and do not modify the original `ParamSet`. ```{r} -psSmall = ps$clone() -psSmall$subset(c("A", "B", "C")) -print(psSmall) +ps1 = ParamSet$new(list(x = p_int(), y = p_dbl())) +ps2 = ParamSet$new(list(z = p_fct(levels = c("a", "b", "c")))) +ps_all = c(ps1, ps2) +print(ps_all) +ps_all$subset(c("x", "z")) ``` -Just as for `Param`s, and much more useful, it is possible to get the `ParamSet` as a `data.table` using `as.data.table()`. +`ParamSet`s of each individual parameters can be accessed through the `$subspaces()` function. + +It is possible to get the `ParamSet` as a `data.table` using `as.data.table()`. This makes it easy to subset parameters on certain conditions and aggregate information about them, using the variety of methods provided by `data.table`. ```{r} -as.data.table(ps) +as.data.table(ps_all) ``` -##### Type / Range Checking - -Similar to individual `Param`s, the `ParamSet` provides `$test()`, `$check()` and `$assert()` functions that allow for type and range checking of parameters. -Their argument must be a named list with values that are checked against the respective parameters. -It is possible to check only a subset of parameters. - -```{r} -ps$check(list(A = TRUE, B = 0, E = identity)) -ps$check(list(A = 1)) -ps$check(list(Z = 1)) -``` ##### Values in a `ParamSet` @@ -183,15 +166,15 @@ The `$values` slot contains a named list that is always checked against paramete When trying to set parameter values, e.g. for `mlr3` `Learner`s, it is the `$values` slot of its `$param_set` that needs to be used. ```{r} -ps$values = list(A = TRUE, B = 0) -ps$values$B = 1 -print(ps$values) +ps1$values = list(x = 1, y = 1.5) +ps1$values$y = 2.5 +print(ps1$values) ``` The parameter constraints are automatically checked: ```{r, error = TRUE} -ps$values$B = 100 +ps1$values$x = 1.5 ``` ##### Dependencies @@ -202,29 +185,37 @@ The second parameter would be said to *depend* on the first parameter having the A dependency can be added using the `$add_dep` method, which takes both the ids of the "depender" and "dependee" parameters as well as a `Condition` object. The `Condition` object represents the check to be performed on the "dependee". -Currently it can be created using `CondEqual$new()` and `CondAnyOf$new()`. +Currently it can be created using `CondEqual()` and `CondAnyOf()`. Multiple dependencies can be added, and parameters that depend on others can again be depended on, as long as no cyclic dependencies are introduced. The consequence of dependencies are twofold: -For one, the `$check()`, `$test()` and `$assert()` tests will not accept the presence of a parameter if its dependency is not met. +For one, the `$check()`, `$test()` and `$assert()` tests will not accept the presence of a parameter if its dependency is not met, when the `check_strict` argument is given as `TRUE`. Furthermore, when sampling or creating grid designs from a `ParamSet`, the dependencies will be respected. +The easiest way to set dependencies is to give the `depends` argument to the `Domain` constructor. + The following example makes parameter `D` depend on parameter `A` being `FALSE`, and parameter `B` depend on parameter `D` being one of `"x"` or `"y"`. This introduces an implicit dependency of `B` on `A` being `FALSE` as well, because `D` does not take any value if `A` is `TRUE`. ```{r} -ps$add_dep("D", "A", CondEqual$new(FALSE)) -ps$add_dep("B", "D", CondAnyOf$new(c("x", "y"))) +p = ps( + A = p_lgl(init = FALSE), + B = p_int(lower = 0, upper = 10, depends = D %in% c("x", "y")), + C = p_dbl(lower = 0, upper = 4), + D = p_fct(levels = c("x", "y", "z"), depends = A == FALSE) +) ``` +Note that the `depends` argument is limited to operators `==` and `%in%`, so `D = p_fct(..., depends = !A)` would not work. + ```{r} -ps$check(list(A = FALSE, D = "x", B = 1)) # OK: all dependencies met -ps$check(list(A = FALSE, D = "z", B = 1)) # B's dependency is not met -ps$check(list(A = FALSE, B = 1)) # B's dependency is not met -ps$check(list(A = FALSE, D = "z")) # OK: B is absent -ps$check(list(A = TRUE)) # OK: neither B nor D present -ps$check(list(A = TRUE, D = "x", B = 1)) # D's dependency is not met -ps$check(list(A = TRUE, B = 1)) # B's dependency is not met +p$check(list(A = FALSE, D = "x", B = 1), check_strict = TRUE) # OK: all dependencies met +p$check(list(A = FALSE, D = "z", B = 1), check_strict = TRUE) # B's dependency is not met +p$check(list(A = FALSE, B = 1), check_strict = TRUE) # B's dependency is not met +p$check(list(A = FALSE, D = "z"), check_strict = TRUE) # OK: B is absent +p$check(list(A = TRUE), check_strict = TRUE) # OK: neither B nor D present +p$check(list(A = TRUE, D = "x", B = 1), check_strict = TRUE) # D's dependency is not met +p$check(list(A = TRUE, B = 1), check_strict = TRUE) # B's dependency is not met ``` Internally, the dependencies are represented as a `data.table`, which can be accessed listed in the **`$deps`** slot. @@ -233,33 +224,31 @@ There are no sanity checks done when the `$deps` slot is changed this way. Therefore it is advised to be cautious. ```{r} -ps$deps +p$deps ``` #### Vector Parameters Unlike in the old `ParamHelpers` package, there are no more vectorial parameters in `paradox`. -Instead, it is now possible to create multiple copies of a single parameter using the `$rep` function. +Instead, it is now possible to create multiple copies of a single parameter using the `ps_replicate` function. This creates a `ParamSet` consisting of multiple copies of the parameter, which can then (optionally) be added to another `ParamSet`. ```{r} -ps2d = ParamDbl$new("x", lower = 0, upper = 1)$rep(2) +ps2d = ps_replicate(ps(x = p_dbl(lower = 0, upper = 1)), 2) print(ps2d) ``` -```{r} -ps$add(ps2d) -print(ps) -``` - It is also possible to use a `ParamUty` to accept vectorial parameters, which also works for parameters of variable length. A `ParamSet` containing a `ParamUty` can be used for parameter checking, but not for sampling. -To sample values for a method that needs a vectorial parameter, it is advised to use a parameter transformation function that creates a vector from atomic values. +To sample values for a method that needs a vectorial parameter, it is advised to use an `$extra_trafo` transformation function that creates a vector from atomic values. -Assembling a vector from repeated parameters is aided by the parameter's `$tags`: Parameters that were generated by the `$rep()` command automatically get tagged as belonging to a group of repeated parameters. +Assembling a vector from repeated parameters is aided by the parameter's `$tags`: Parameters that were generated by the `pr_replicate()` command can be tagged as belonging to a group of repeated parameters. ```{r} -ps$tags +ps2d = ps_replicate(ps(x = p_dbl(0, 1), y = p_int(0, 10)), 2, tag_params = TRUE) +ps2d$values = list(rep1.x = 0.2, rep2.x = 0.4, rep1.y = 3, rep2.y = 4) +ps2d$tags +ps2d$get_values(tags = "param_x") ``` ### Parameter Sampling @@ -269,7 +258,7 @@ It is often useful to have a list of possible parameter values that can be syste In the latter case, it is possible to influence the sampling distribution in more or less fine detail. A point to always keep in mind while sampling is that only numerical and factorial parameters that are bounded can be sampled from, i.e. not `ParamUty`. -Furthermore, for most samplers `ParamInt` and `ParamDbl` must have finite lower and upper bounds. +Furthermore, for most samplers `p_int()` and `p_dbl()` must have finite lower and upper bounds. #### Parameter Designs @@ -282,12 +271,13 @@ The `generate_design_grid()` function is used to create grid designs that contai The resolution can be given for all numeric parameters, or for specific named parameters through the `param_resolutions` parameter. ```{r} -design = generate_design_grid(psSmall, 2) +ps_small = ps(A = p_dbl(0, 1), B = p_dbl(0, 1)) +design = generate_design_grid(ps_small, 2) print(design) ``` ```{r} -generate_design_grid(psSmall, param_resolutions = c(B = 1, C = 2)) +generate_design_grid(ps_small, param_resolutions = c(A = 3, B = 2)) ``` #### Random Sampling @@ -296,10 +286,12 @@ generate_design_grid(psSmall, param_resolutions = c(B = 1, C = 2)) The easiest way to get a uniformly random sample of parameters is `generate_design_random()`. It is also possible to create "[latin hypercube](https://en.wikipedia.org/wiki/Latin_hypercube_sampling)" sampled parameter values using `generate_design_lhs()`, which utilizes the `lhs` package. LHS-sampling creates low-discrepancy sampled values that cover the parameter space more evenly than purely random values. +[Sobol sampling](https://en.wikipedia.org/wiki/Latin_hypercube_sampling) is also possible, using `generate_design_sobol()`. ```{r} -pvrand = generate_design_random(ps2d, 500) -pvlhs = generate_design_lhs(ps2d, 500) +pvrand = generate_design_random(ps_small, 500) +pvlhs = generate_design_lhs(ps_small, 500) +pvsobol = generate_design_sobol(ps_small, 500) ``` ```{r, echo = FALSE, out.width="45%", fig.show = "hold", fig.width = 4, fig.height = 4} @@ -307,6 +299,7 @@ pvlhs = generate_design_lhs(ps2d, 500) par(mar=c(4, 4, 2, 1)) plot(pvrand$data, main = "'random' design", xlim = c(0, 1), ylim=c(0, 1)) plot(pvlhs$data, main = "'lhs' design", xlim = c(0, 1), ylim=c(0, 1)) +plot(pvsobol$data, main = "'sobol' design", xlim = c(0, 1), ylim=c(0, 1)) ``` #### Generalized Sampling: The `Sampler` Class @@ -321,10 +314,10 @@ Every `Sampler` object has a `sample()` function, which takes one argument, the There is a variety of samplers that sample values for a single parameter. These are `Sampler1DUnif` (uniform sampling), `Sampler1DCateg` (sampling for categorical parameters), `Sampler1DNormal` (normally distributed sampling, truncated at parameter bounds), and `Sampler1DRfun` (arbitrary 1D sampling, given a random-function). -These are initialized with a single `Param`, and can then be used to sample values. +These are initialized with a one-dimensional `ParamSet`, and can then be used to sample values. ```{r} -sampA = Sampler1DCateg$new(parA) +sampA = Sampler1DCateg$new(ps(x = p_fct(letters))) sampA$sample(5) ``` @@ -339,13 +332,19 @@ The following example shows how this works: The `Int` parameter `B` depends on t In the cases where `A` is `FALSE`, `B` is set to `NA`. ```{r} -psSmall$add_dep("B", "A", CondEqual$new(TRUE)) -sampH = SamplerHierarchical$new(psSmall, - list(Sampler1DCateg$new(parA), - Sampler1DUnif$new(parB), - Sampler1DUnif$new(parC)) +p = ps( + A = p_lgl(), + B = p_int(0, 10, depends = A == TRUE) +) + +p_subspaces = p$subspaces() + +sampH = SamplerHierarchical$new(p, + list(Sampler1DCateg$new(p_subspaces$A), + Sampler1DUnif$new(p_subspaces$B)) ) sampled = sampH$sample(1000) +head(sampled$data) table(sampled$data[, c("A", "B")], useNA = "ifany") ``` @@ -357,8 +356,8 @@ However, `SamplerJointIndep` currently can not handle `ParamSet`s with dependenc ```{r} sampJ = SamplerJointIndep$new( - list(Sampler1DUnif$new(ParamDbl$new("x", 0, 1)), - Sampler1DUnif$new(ParamDbl$new("y", 0, 1))) + list(Sampler1DUnif$new(ps(x = p_dbl(0, 1))), + Sampler1DUnif$new(ps(y = p_dbl(0, 1)))) ) sampJ$sample(5) ``` @@ -370,25 +369,28 @@ The `Sampler` used in `generate_design_random()` is the `SamplerUnif` sampler, w ### Parameter Transformation While the different `Sampler`s allow for a wide specification of parameter distributions, there are cases where the simplest way of getting a desired distribution is to sample parameters from a simple distribution (such as the uniform distribution) and then transform them. -This can be done by assigning a function to the `$trafo` slot of a `ParamSet`. -The `$trafo` function is called with two parameters: +This can be done by constructing a `Domain` with a `trafo` argument, or assigning a function to the `$extra_trafo` field of a `ParamSet`. +The latter can also be done by passing an `.extra_trafo` argument to the `ps()` shorthand constructor. -* The list of parameter values to be transformed as `x` +A `trafo` function in a `Domain` is called with a single parameter, the value to be transformed. +It can only operate on the dimension of a single parameter. + +The `$extra_trafo` function is called with two parameters: + +* The list of parameter values to be transformed as `x`. + Unlike the `Domain`'s `trafo`, the `$extra_trafo` handles the whole parameter set and can even model "interactions" between parameters. * The `ParamSet` itself as `param_set` -The `$trafo` function must return a list of transformed parameter values. +The `$extra_trafo` function must return a list of transformed parameter values. The transformation is performed when calling the `$transpose` function of the `Design` object returned by a `Sampler` with the `trafo` ParamSet to `TRUE` (the default). The following, for example, creates a parameter that is exponentially distributed: ```{r} -psexp = ParamSet$new(list(ParamDbl$new("par", 0, 1))) -psexp$trafo = function(x, param_set) { - x$par = -log(x$par) - x -} -design = generate_design_random(psexp, 2) -print(design) +psexp = ps(par = p_dbl(0, 1, trafo = function(x) -log(x))) + +design = generate_design_random(psexp, 3) +print(design) # not transformed: between 0 and 1 design$transpose() # trafo is TRUE ``` @@ -398,6 +400,18 @@ Compare this to `$transpose()` without transformation: design$transpose(trafo = FALSE) ``` +Another way to get tihs effect, using `$extra_trafo`, would be: +```{r} +psexp = ps(par = p_dbl(0, 1)) +psexp$extra_trafo = function(x, param_set) { + x$par = -log(x$par) + x +} +``` +However, the `trafo` way is more recommended when transforming parameters independently. +`$extra_trafo` is more useful when transforming parameters that interact in some way, or when new parameters should be generated. + + #### Transformation between Types Usually the design created with one `ParamSet` is then used to configure other objects that themselves have a `ParamSet` which defines the values they take. @@ -413,31 +427,18 @@ The user can pass functions like `median()` or `mean()`, but could also pass qua This method would probably use the following `ParamSet`: ```{r} -methodPS = ParamSet$new( - list( - ParamUty$new("fun", - custom_check = function(x) checkmate::checkFunction(x, nargs = 1)) - ) -) +methodPS = ps(fun = p_uty(custom_check = function(x) checkmate::checkFunction(x, nargs = 1))) + print(methodPS) ``` If one wanted to sample this method, using one of four functions, a way to do this would be: ```{r} -samplingPS = ParamSet$new( - list( - ParamFct$new("fun", c("mean", "median", "min", "max")) - ) +samplingPS = ps( + fun = p_fct(c("mean", "median", "min", "max"), + trafo = function(x) get(x, mode = "function")) ) - -samplingPS$trafo = function(x, param_set) { - # x$fun is a `character(1)`, - # in particular one of 'mean', 'median', 'min', 'max'. - # We want to turn it into a function! - x$fun = get(x$fun, mode = "function") - x -} ``` ```{r} @@ -460,23 +461,32 @@ methodPS$check(xvals[[1]]) xvals[[1]]$fun(1:10) ``` +`p_fct()` has a shortcut for this kind of transformation, where a `character` is transformed into a specific set of (typically non-scalar) values. +When its `levels` argument is given as a named `list` (or named non-`character` vector), it constructs a `Domain` that does the trafo automatically. +A way to perform the above would therefore be: +```{r} +samplingPS = ps( + fun = p_fct(list("mean" = mean, "median" = median, "min" = min, "max" = max)) +) + +generate_design_random(samplingPS, 1)$transpose() +``` + Imagine now that a different kind of parametrization of the function is desired: The user wants to give a function that selects a certain quantile, where the quantile is set by a parameter. In that case the `$transpose` function could generate a function in a different way. -For interpretability, the parameter is called "`quantile`" before transformation, and the "`fun`" parameter is generated on the fly. + +For interpretability, the parameter should be called "`quantile`" before transformation, and the "`fun`" parameter is generated on the fly. +We therefore use an `extra_trafo` here, given as a function to the `ps()` call. ```{r} -samplingPS2 = ParamSet$new( - list( - ParamDbl$new("quantile", 0, 1) - ) +samplingPS2 = ps(quantile = p_dbl(0, 1), + .extra_trafo = function(x, param_set) { + # x$quantile is a `numeric(1)` between 0 and 1. + # We want to turn it into a function! + list(fun = function(input) quantile(input, x$quantile)) + } ) - -samplingPS2$trafo = function(x, param_set) { - # x$quantile is a `numeric(1)` between 0 and 1. - # We want to turn it into a function! - list(fun = function(input) quantile(input, x$quantile)) -} ``` ```{r} @@ -499,7 +509,6 @@ xvals[[1]]$fun(1:10) When running an optimization, it is important to inform the tuning algorithm about what hyperparameters are valid. Here the names, types, and valid ranges of each hyperparameter are important. All this information is communicated with objects of the class `ParamSet`, which is defined in `paradox`. -While it is possible to create `ParamSet`-objects using its `$new`-constructor, it is much shorter and readable to use the `ps`-shortcut, which will be presented here. Note, that `ParamSet` objects exist in two contexts. First, `ParamSet`-objects are used to define the space of valid parameter settings for a learner (and other objects). @@ -533,13 +542,13 @@ print(search_space) There are five domain constructors that produce a parameters when given to `ps`: -| Constructor | Description | Is bounded? | Underlying Class | -| :-----------------------: | :----------------------------------: | :--------------------------------: | :-----------------: | -| `p_dbl` | Real valued parameter ("double") | When `upper` and `lower` are given | `ParamDbl` | -| `p_int` | Integer parameter | When `upper` and `lower` are given | `ParamInt` | -| `p_fct` | Discrete valued parameter ("factor") | Always | `ParamFct` | -| `p_lgl` | Logical / Boolean parameter | Always | `ParamLgl` | -| `p_uty` | Untyped parameter | Never | `ParamUty` | +| Constructor | Description | Is bounded? | +| :-----------------------: | :----------------------------------: | :--------------------------------: | +| `p_dbl` | Real valued parameter ("double") | When `upper` and `lower` are given | +| `p_int` | Integer parameter | When `upper` and `lower` are given | +| `p_fct` | Discrete valued parameter ("factor") | Always | +| `p_lgl` | Logical / Boolean parameter | Always | +| `p_uty` | Untyped parameter | Never | These domain constructors each take some of the following arguments: From e9d37e949a13aa5023d9e12d9d2053d2dbc21b9a Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 26 Aug 2023 12:53:49 +0200 Subject: [PATCH 47/64] vignette builds --- NAMESPACE | 2 ++ R/ps_replicate.R | 2 +- R/ps_union.R | 19 +++++++++++-------- vignettes/indepth.Rmd | 18 ++++++++++-------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index de6c5d43..e45067d0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -90,6 +90,8 @@ export(p_int) export(p_lgl) export(p_uty) export(ps) +export(ps_replicate) +export(ps_union) export(psc) export(to_tune) import(checkmate) diff --git a/R/ps_replicate.R b/R/ps_replicate.R index dc390a33..c84e9c84 100644 --- a/R/ps_replicate.R +++ b/R/ps_replicate.R @@ -1,4 +1,4 @@ - +#' @export ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_sets = FALSE, tag_params = FALSE) { assert_count(times) assert_character(prefixes, any.missing = FALSE, unique = TRUE, len = times) diff --git a/R/ps_union.R b/R/ps_union.R index 0fae3d74..1650b05a 100644 --- a/R/ps_union.R +++ b/R/ps_union.R @@ -1,11 +1,14 @@ -# Create a ParamSet from a list of ParamSets -# This emulates `ParamSetCollection$new(sets)`, except that -# - The result is a `ParamSet`, not a `ParamSetCollection` -# - The ParamSets are allowed to have `$trafo`, which are collected together into a single function. -# This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting ParamSet has all the Params -# from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will -# have their changed to .. This is also reflected in deps and in `$trafo`. -# @param sets: list of ParamSet +#' @title Create a ParamSet from a list of ParamSets +#' +#' @description +#' This emulates `ParamSetCollection$new(sets)`, except that +#' - The result is a `ParamSet`, not a `ParamSetCollection` +#' - The ParamSets are allowed to have `$trafo`, which are collected together into a single function. +#' This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting ParamSet has all the Params +#' from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will +#' have their changed to .. This is also reflected in deps and in `$trafo`. +#' @param sets: list of ParamSet +#' @export ps_union = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") if (!length(sets)) return(ParamSet$new()) diff --git a/vignettes/indepth.Rmd b/vignettes/indepth.Rmd index 97defc87..8b05185a 100644 --- a/vignettes/indepth.Rmd +++ b/vignettes/indepth.Rmd @@ -286,7 +286,7 @@ generate_design_grid(ps_small, param_resolutions = c(A = 3, B = 2)) The easiest way to get a uniformly random sample of parameters is `generate_design_random()`. It is also possible to create "[latin hypercube](https://en.wikipedia.org/wiki/Latin_hypercube_sampling)" sampled parameter values using `generate_design_lhs()`, which utilizes the `lhs` package. LHS-sampling creates low-discrepancy sampled values that cover the parameter space more evenly than purely random values. -[Sobol sampling](https://en.wikipedia.org/wiki/Latin_hypercube_sampling) is also possible, using `generate_design_sobol()`. +`generate_design_sobol()` can be used to sample using the [Sobol sequence](https://en.wikipedia.org/wiki/Sobol_sequence). ```{r} pvrand = generate_design_random(ps_small, 500) @@ -559,6 +559,8 @@ These domain constructors each take some of the following arguments: * **`trafo`**: transformation function, see below. * **`depends`**: dependencies, see below. * **`tags`**: Further information about a parameter, used for example by the `hyperband` tuner. +* **`init`**: . + Not used for tuning search spaces. * **`default`**: Value corresponding to default behavior when the parameter is not given. Not used for tuning search spaces. * **`special_vals`**: Valid values besides the normally accepted values for a parameter. @@ -567,7 +569,7 @@ These domain constructors each take some of the following arguments: Not used for tuning search spaces. The `lower` and `upper` parameters are always in the first and second position respectively, except for `p_fct` where `levels` is in the first position. -It is preferred to omit the labels (ex: upper = 0.1 becomes just 0.1). This way of defining a `ParamSet` is more concise than the equivalent definition above. +It is preferred to omit the labels (ex: `upper = 0.1` becomes just `0.1`). This way of defining a `ParamSet` is more concise than the equivalent definition above. Preferred: ```{r} @@ -623,7 +625,7 @@ rbindlist(generate_design_grid(search_space, 3)$transpose()) The available types of search space parameters are limited: continuous, integer, discrete, and logical scalars. There are many machine learning algorithms, however, that take parameters of other types, for example vectors or functions. -These can not be defined in a search space `ParamSet`, and they are often given as `ParamUty` in the `Learner`'s `ParamSet`. +These can not be defined in a search space `ParamSet`, and they are often given as `p_uty()` in the `Learner`'s `ParamSet`. When trying to tune over these hyperparameters, it is necessary to perform a Transformation that changes the type of a parameter. An example is the `class.weights` parameter of the [Support Vector Machine](https://machinelearningmastery.com/cost-sensitive-svm-for-imbalanced-classification/) (SVM), which takes a named vector of class weights with one entry for each target class. @@ -721,7 +723,7 @@ This can be done in the same way that other hyperparameters are set to specific It can be understood as the hyperparameters being tagged for later tuning. The resulting `ParamSet` used for tuning can be retrieved using the `$search_space()` method. -```{r} +```{r, eval = FALSE} library("mlr3learners") learner = lrn("classif.svm") learner$param_set$values$kernel = "polynomial" # for example @@ -737,7 +739,7 @@ rbindlist(generate_design_grid( It is possible to omit `lower` here, because it can be inferred from the lower bound of the `degree` parameter itself. For other parameters, that are already bounded, it is possible to not give any bounds at all, because their ranges are already bounded. An example is the logical `shrinking` hyperparameter: -```{r} +```{r, eval = FALSE} learner$param_set$values$shrinking = to_tune() print(learner$param_set$search_space()) @@ -753,7 +755,7 @@ One could, for example, tune the `cost` as above on three given special values, Notice that a short form for `to_tune()` is a short form of `to_tune(p_fct())`. When introducing the dependency, we need to use the `degree` value from *before* the implicit trafo, which is the name or `as.character()` of the respective value, here `"val2"`! -```{r} +```{r, eval = FALSE} learner$param_set$values$type = "C-classification" # needs to be set because of a bug in paradox learner$param_set$values$cost = to_tune(c(val1 = 0.3, val2 = 0.7)) learner$param_set$values$shrinking = to_tune(p_lgl(depends = cost == "val2")) @@ -767,7 +769,7 @@ The `search_space()` picks up dependencies from the underlying `ParamSet` automa So if the `kernel` is tuned, then `degree` automatically gets the dependency on it, without us having to specify that. (Here we reset `cost` and `shrinking` to `NULL` for the sake of clarity of the generated output.) -```{r} +```{r, eval = FALSE} learner$param_set$values$cost = NULL learner$param_set$values$shrinking = NULL learner$param_set$values$kernel = to_tune(c("polynomial", "radial")) @@ -782,7 +784,7 @@ This may be especially useful for vector hyperparameters that should be searched This `ParamSet` must, however, have an `.extra_trafo` that returns a list with a single element, because it corresponds to a single hyperparameter that is being tuned. Suppose the `class.weights` hyperparameter should be tuned along two dimensions: -```{r} +```{r, eval = FALSE} learner$param_set$values$class.weights = to_tune( ps(spam = p_dbl(0.1, 0.9), nonspam = p_dbl(0.1, 0.9), .extra_trafo = function(x, param_set) list(c(spam = x$spam, nonspam = x$nonspam)) From 6124d68ebb45cfdf4766a00ebfe7ae8de0f1640c Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 26 Aug 2023 13:02:19 +0200 Subject: [PATCH 48/64] rd_info repair --- R/ParamSet.R | 6 +++--- R/ParamUty.R | 10 +++++----- man/ps_union.Rd | 21 +++++++++++++++++++++ tests/testthat/test_ParamUty.R | 4 ++++ 4 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 man/ps_union.Rd diff --git a/R/ParamSet.R b/R/ParamSet.R index 15e7cb10..cd833405 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -863,6 +863,7 @@ rd_info.ParamSet = function(obj, descriptions = character(), ...) { # nolint } params = as.data.table(obj)[, c("id", "storage_type", "default", "lower", "upper", "levels"), with = FALSE] + cargo = obj$params$cargo if (length(descriptions)) { params = merge(params, enframe(descriptions, name = "id", value = "description"), all.x = TRUE, by = "id") @@ -872,9 +873,8 @@ rd_info.ParamSet = function(obj, descriptions = character(), ...) { # nolint } is_default = map_lgl(params$default, inherits, "NoDefault") is_uty = params$storage_type == "list" - # TODO - #set(params, i = which(is_uty & !is_default), j = "default", - # value = map(obj$params[!is_default & is_uty], function(x) x$repr)) + set(params, i = which(is_uty & !is_default), j = "default", + value = map(cargo[!is_default & is_uty], function(x) x$repr)) set(params, i = which(is_uty), j = "storage_type", value = list("untyped")) set(params, i = which(is_default), j = "default", value = list("-")) diff --git a/R/ParamUty.R b/R/ParamUty.R index f4f99c70..61cbd143 100644 --- a/R/ParamUty.R +++ b/R/ParamUty.R @@ -8,20 +8,20 @@ p_uty = function(custom_check = NULL, special_vals = list(), default = NO_DEF, t assert(check_true(custom_check_result), check_string(custom_check_result), .var.name = "The result of 'custom_check()'") } repr = if (!is_nodefault(default)) { - as.character(repr)[[1]] + deparse(repr)[[1]] } else { "NoDefault" } - Domain(cls = "ParamUty", grouping = "ParamUty", cargo = custom_check, special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "list", depends_expr = substitute(depends), init = init) + Domain(cls = "ParamUty", grouping = "ParamUty", cargo = list(custom_check = custom_check, repr = repr), special_vals = special_vals, default = default, tags = tags, trafo = trafo, storage_type = "list", depends_expr = substitute(depends), init = init) } #' @export domain_check.ParamUty = function(param, values) { - subset = !map_lgl(param$cargo, is.null) + cargo = map(param$cargo, "custom_check") + subset = !map_lgl(cargo, is.null) if (!any(subset)) return(TRUE) values = values[subset] - param = param[subset] - check_domain_vectorize(param$id, values, param$cargo) + check_domain_vectorize(param$id[subset], values, cargo[subset]) } #' @export diff --git a/man/ps_union.Rd b/man/ps_union.Rd new file mode 100644 index 00000000..18b808f5 --- /dev/null +++ b/man/ps_union.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ps_union.R +\name{ps_union} +\alias{ps_union} +\title{Create a ParamSet from a list of ParamSets} +\usage{ +ps_union(sets, tag_sets = FALSE, tag_params = FALSE) +} +\arguments{ +\item{sets:}{list of ParamSet} +} +\description{ +This emulates \code{ParamSetCollection$new(sets)}, except that +\itemize{ +\item The result is a \code{ParamSet}, not a \code{ParamSetCollection} +\item The ParamSets are allowed to have \verb{$trafo}, which are collected together into a single function. +This emulates \code{ParamSetCollection$new(sets)}, which in particular means that the resulting ParamSet has all the Params +from the input \code{sets}, but some \verb{$id}s are changed: If the ParamSet has a non-empty \code{set_id}, then the Params will +have their \if{html}{\out{}} changed to .\if{html}{\out{}}. This is also reflected in deps and in \verb{$trafo}. +} +} diff --git a/tests/testthat/test_ParamUty.R b/tests/testthat/test_ParamUty.R index 40432793..589b3060 100644 --- a/tests/testthat/test_ParamUty.R +++ b/tests/testthat/test_ParamUty.R @@ -28,3 +28,7 @@ test_that("R6 values of ParamUty are cloned", { expect_true(psunclone$values$x$x) # reference check: value was not cloned expect_null(psclone$values$x$x) # was cloned before change --> should still be null }) + +test_that("default NULL works", { + expect_equal(p_uty(default = NULL)$cargo[[1]]$repr, "NULL") +}) From 8162aa2dfef847d4484a347f698585ed7fd849ce Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 13 Jan 2024 19:30:17 +0100 Subject: [PATCH 49/64] work with bbotk --- DESCRIPTION | 6 +++--- NAMESPACE | 1 + R/Condition.R | 14 ++++++++++++++ R/ParamSet.R | 17 +++++++++++++++-- R/ps_replicate.R | 1 + R/ps_union.R | 1 + man/ParamSet.Rd | 13 ++++++++----- man/ParamSetCollection.Rd | 2 +- man/paradox-package.Rd | 4 ++-- 9 files changed, 46 insertions(+), 13 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 90ba5d7e..6c79cb3a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,11 +2,11 @@ Type: Package Package: paradox Title: Define and Work with Parameter Spaces for Complex Algorithms -Version: 0.12.0 +Version: 1.0.0 Authors@R: c(person(given = "Michel", family = "Lang", - role = c("cre", "aut"), + role = "aut", email = "michellang@gmail.com", comment = c(ORCID = "0000-0001-9754-0393")), person(given = "Bernd", @@ -26,7 +26,7 @@ Authors@R: comment = c(ORCID = "0000-0003-3269-2307")), person(given = "Martin", family = "Binder", - role = "aut", + role = c("aut", "cre"), email = "mlr.developer@mb706.com"), person(given = "Marc", family = "Becker", diff --git a/NAMESPACE b/NAMESPACE index e45067d0..e74951d6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method("$",Constructor) S3method(as.data.table,ParamSet) S3method(c,ParamSet) S3method(condition_as_string,Condition) diff --git a/R/Condition.R b/R/Condition.R index 0ef8b275..3d58e8fc 100644 --- a/R/Condition.R +++ b/R/Condition.R @@ -97,3 +97,17 @@ CondAnyOf = function(rhs) { condition_test.CondAnyOf = function(cond, x) { !is.na(x) & x %in% cond$rhs } + +# FIXME: the following makes `condition$new()` possible for paradox transition +# should give a deprecated warning at some point. +#' @export +`$.Constructor` = function(e1, e2) { + if (!identical(e2, "new")) { + stop("only 'new' element can be accessed.") + } else { + e1 + } +} + +CondEqual = structure(CondEqual, class = c("Constructor", "function")) +CondAnyOf = structure(CondAnyOf, class = c("Constructor", "function")) diff --git a/R/ParamSet.R b/R/ParamSet.R index cd833405..bcbb4921 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -211,6 +211,8 @@ ParamSet = R6Class("ParamSet", }, trafo = function(x, param_set = self) { + if (is.data.frame(x)) x = as.list(x) + assert_list(x, names = "unique") trafos = private$.trafos[names(x), .(id, trafo), nomatch = 0] trafos[, value := x[id]] if (nrow(trafos)) { @@ -416,7 +418,7 @@ ParamSet = R6Class("ParamSet", #' #' @param id (`character(1)`). #' @return [`Domain`]. - get_param = function(id) { + get_domain = function(id) { assert_string(id) paramrow = private$.params[id, on = "id", nomatch = NULL] @@ -526,7 +528,7 @@ ParamSet = R6Class("ParamSet", } if (on %in% ids) { # not necessarily true when allow_dangling_dependencies - feasible_on_values = map_lgl(cond$rhs, function(x) domain_test(self$get_param(on), list(x))) + feasible_on_values = map_lgl(cond$rhs, function(x) domain_test(self$get_domain(on), list(x))) if (any(!feasible_on_values)) { stopf("Condition has infeasible values for %s: %s", on, str_collapse(cond$rhs[!feasible_on_values])) } @@ -640,6 +642,17 @@ ParamSet = R6Class("ParamSet", result[] }, + #' @field domains (named `list` of [`Domain`]) + #' List of [`Domain`] objects that could be used to initialize this `ParamSet`. + domains = function(rhs) { + if (!missing(rhs)) { + stop("domains is read-only.") + } + nm = self$ids() + set_names(map(nm, self$get_domain), nm) + }, + + #' @field extra_trafo (`function(x, param_set)`)\cr #' Transformation function. Settable. #' User has to pass a `function(x)`, of the form\cr diff --git a/R/ps_replicate.R b/R/ps_replicate.R index c84e9c84..1830c125 100644 --- a/R/ps_replicate.R +++ b/R/ps_replicate.R @@ -1,3 +1,4 @@ +#' @title #' @export ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_sets = FALSE, tag_params = FALSE) { assert_count(times) diff --git a/R/ps_union.R b/R/ps_union.R index 1650b05a..41b0ffc9 100644 --- a/R/ps_union.R +++ b/R/ps_union.R @@ -8,6 +8,7 @@ #' from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will #' have their changed to .. This is also reflected in deps and in `$trafo`. #' @param sets: list of ParamSet +#' @param tag_sets #' @export ps_union = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 71807d4f..250aedda 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -74,6 +74,9 @@ Named with parameter IDs.} \item{\code{params}}{(named \code{list()})\cr List of \link{Param}, named with their respective ID.} +\item{\code{domains}}{(named \code{list} of \code{\link{Domain}}) +List of \code{\link{Domain}} objects that could be used to initialize this \code{ParamSet}.} + \item{\code{extra_trafo}}{(\verb{function(x, param_set)})\cr Transformation function. Settable. User has to pass a \verb{function(x)}, of the form\cr @@ -161,7 +164,7 @@ Named with param IDs.} \item \href{#method-ParamSet-test_dt}{\code{ParamSet$test_dt()}} \item \href{#method-ParamSet-assert_dt}{\code{ParamSet$assert_dt()}} \item \href{#method-ParamSet-qunif}{\code{ParamSet$qunif()}} -\item \href{#method-ParamSet-get_param}{\code{ParamSet$get_param()}} +\item \href{#method-ParamSet-get_domain}{\code{ParamSet$get_domain()}} \item \href{#method-ParamSet-subset}{\code{ParamSet$subset()}} \item \href{#method-ParamSet-subspaces}{\code{ParamSet$subspaces()}} \item \href{#method-ParamSet-flatten}{\code{ParamSet$flatten()}} @@ -479,12 +482,12 @@ If successful \code{xs} invisibly, if not an error message. } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-ParamSet-get_param}{}}} -\subsection{Method \code{get_param()}}{ +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSet-get_domain}{}}} +\subsection{Method \code{get_domain()}}{ get the \code{\link{Domain}} object that could be used to create a given parameter. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ParamSet$get_param(id)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ParamSet$get_domain(id)}\if{html}{\out{
}} } \subsection{Arguments}{ diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index a5a83524..8e3d906c 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -64,7 +64,7 @@ When you set values, all previously set values will be unset / removed.}
  • paradox::ParamSet$check_dt()
  • paradox::ParamSet$flatten()
  • paradox::ParamSet$format()
  • -
  • paradox::ParamSet$get_param()
  • +
  • paradox::ParamSet$get_domain()
  • paradox::ParamSet$get_values()
  • paradox::ParamSet$ids()
  • paradox::ParamSet$print()
  • diff --git a/man/paradox-package.Rd b/man/paradox-package.Rd index 7e7ef662..5168d963 100644 --- a/man/paradox-package.Rd +++ b/man/paradox-package.Rd @@ -18,14 +18,14 @@ Useful links: } \author{ -\strong{Maintainer}: Michel Lang \email{michellang@gmail.com} (\href{https://orcid.org/0000-0001-9754-0393}{ORCID}) +\strong{Maintainer}: Martin Binder \email{mlr.developer@mb706.com} Authors: \itemize{ + \item Michel Lang \email{michellang@gmail.com} (\href{https://orcid.org/0000-0001-9754-0393}{ORCID}) \item Bernd Bischl \email{bernd_bischl@gmx.net} (\href{https://orcid.org/0000-0001-6002-6980}{ORCID}) \item Jakob Richter \email{jakob1richter@gmail.com} (\href{https://orcid.org/0000-0003-4481-5554}{ORCID}) \item Xudong Sun \email{smilesun.east@gmail.com} (\href{https://orcid.org/0000-0003-3269-2307}{ORCID}) - \item Martin Binder \email{mlr.developer@mb706.com} } Other contributors: From cab9912bbd356f6d4930b30ab62724dbcd9ef4d2 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sat, 13 Jan 2024 22:50:42 +0100 Subject: [PATCH 50/64] PSC --- R/ParamSet.R | 13 ++--- R/ParamSetCollection.R | 68 +++++++++++++++++++++++- R/zzz.R | 2 +- man/ParamSetCollection.Rd | 21 ++++++++ tests/testthat/helper_03_domain.R | 4 +- tests/testthat/test_ParamSetCollection.R | 8 +-- 6 files changed, 101 insertions(+), 15 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index bcbb4921..9e080f50 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -458,9 +458,9 @@ ParamSet = R6Class("ParamSet", result = ParamSet$new() - result$.__enclos_env__$private$.params = private$.params[ids, on = "id"] - result$.__enclos_env__$private$.trafos = private$.trafos[ids, on = "id", nomatch = NULL] - result$.__enclos_env__$private$.tags = private$.tags[ids, on = "id", nomatch = NULL] + result$.__enclos_env__$private$.params = setindexv(private$.params[ids, on = "id"], c("id", "cls", "grouping")) + result$.__enclos_env__$private$.trafos = setkeyv(private$.trafos[ids, on = "id", nomatch = NULL], "id") + result$.__enclos_env__$private$.tags = setkeyv(private$.tags[ids, on = "id", nomatch = NULL], "id") result$assert_values = FALSE result$deps = deps[ids, on = "id", nomatch = NULL] if (keep_constraint) result$constraint = self$constraint @@ -479,9 +479,10 @@ ParamSet = R6Class("ParamSet", result = ParamSet$new() result$extra_trafo = self$extra_trafo # constraint make no sense here, basically by definition - result$.__enclos_env__$private$.params = private$.params[get_id, on = "id"] - result$.__enclos_env__$private$.trafos = private$.trafos[get_id, on = "id", nomatch = NULL] - result$.__enclos_env__$private$.tags = private$.tags[get_id, on = "id", nomatch = NULL] + result$.__enclos_env__$private$.params = setindexv(private$.params[get_id, on = "id"], c("id", "cls", "grouping")) + # setkeyv not strictly necessary since get_id is scalar, but we do it for consistency + result$.__enclos_env__$private$.trafos = setkeyv(private$.trafos[get_id, on = "id", nomatch = NULL], "id") + result$.__enclos_env__$private$.tags = setkeyv(private$.tags[get_id, on = "id", nomatch = NULL], "id") result$assert_values = FALSE result$values = values[match(get_id, names(values), nomatch = 0)] result$assert_values = TRUE diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 0215162b..ecee4f40 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -67,9 +67,9 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # when paramtbl is empty, use special setup to make sure information about the `.tags` column is present. paramtbl = copy(empty_domain)[, `:=`(original_id = character(0), owner_ps_index = integer(0), owner_name = character(0))] } - if (tag_sets) paramtbl[owner_name != "", , .tags := pmap(list(.tags, owner_name), function(x, n) c(x, sprintf("set_%s", n)))] + if (tag_sets) paramtbl[owner_name != "", .tags := pmap(list(.tags, owner_name), function(x, n) c(x, sprintf("set_%s", n)))] if (tag_params) paramtbl[, .tags := pmap(list(.tags, original_id), function(x, n) c(x, sprintf("param_%s", n)))] - private$.tags = paramtbl[, .(tag = unlist(.tags)), keyby = "id"] + private$.tags = paramtbl[, .(tag = unique(unlist(.tags))), keyby = "id"] private$.trafos = setkeyv(paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)], "id") @@ -86,6 +86,70 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, private$.children_with_constraints = which(!map_lgl(map(sets, "constraint"), is.null)) private$.sets = sets + }, + + #' @description + #' Adds a set to this collection. + #' + #' @param p ([ParamSet]). + #' @param n (`character(1)`)\cr + #' Name to use. Default `""`. + add = function(p, n = "", tag_sets = FALSE, tag_params = FALSE) { + assert_r6(p, "ParamSet") + if (n != "" && n %in% names(private$.sets)) { + stopf("Set name '%s' already present in collection!", n) + } + pnames = p$ids() + nameclashes = intersect( + ifelse(n != "", sprintf("%s.%s", n, pnames), pnames), + self$ids() + ) + if (length(nameclashes)) { + stopf("Adding parameter set would lead to nameclashes: %s", str_collapse(nameclashes)) + } + + new_index = length(private$.sets) + 1 + paramtbl = p$params[, `:=`(original_id = id, owner_ps_index = new_index, owner_name = n)] + if (n != "") set(paramtbl, , "id", sprintf("%s.%s", n, paramtbl$id)) + + if (!nrow(paramtbl)) { + # when paramtbl is empty, use special setup to make sure information about the `.tags` column is present. + paramtbl = copy(empty_domain)[, `:=`(original_id = character(0), owner_ps_index = integer(0), owner_name = character(0))] + } + if (tag_sets && n != "") paramtbl[, .tags := map(.tags, function(x) c(x, sprintf("set_%s", n)))] + if (tag_params) paramtbl[, .tags := pmap(list(.tags, original_id), function(x, n) c(x, sprintf("param_%s", n)))] + newtags = paramtbl[, .(tag = unique(unlist(.tags))), by = "id"] + if (nrow(newtags)) { + private$.tags = setkeyv(rbind(private$.tags, newtags), "id") + } + + newtrafos = paramtbl[!map_lgl(.trafo, is.null), .(id, trafo = .trafo)] + if (nrow(newtrafos)) { + private$.trafos = setkeyv(rbind(private$.trafos, newtrafos), "id") + } + + private$.translation = rbind(private$.translation, paramtbl[, c("id", "original_id", "owner_ps_index", "owner_name"), with = FALSE]) + setkeyv(private$.translation, "id") + setindexv(private$.translation, "original_id") + + set(paramtbl, , setdiff(colnames(paramtbl), domain_names_permanent), NULL) + assert_names(colnames(paramtbl), identical.to = domain_names_permanent) + private$.params = rbind(private$.params, paramtbl) + setindexv(private$.params, c("id", "cls", "grouping")) + + if (!is.null(p$extra_trafo)) { + entry = if (n == "") length(private$.children_with_trafos) + 1 else n + private$.children_with_trafos[[entry]] = new_index + } + + if (!is.null(p$constraint)) { + entry = if (n == "") length(private$.children_with_constraints) + 1 else n + private$.children_with_constraints[[entry]] = new_index + } + + entry = if (n == "") length(private$.sets) + 1 else n + private$.sets[[n]] = p + invisible(self) } ), diff --git a/R/zzz.R b/R/zzz.R index a5c089d1..660cc995 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -20,4 +20,4 @@ }) } # nocov end -leanify_package() +# leanify_package() diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 8e3d906c..76bbc369 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -50,6 +50,7 @@ When you set values, all previously set values will be unset / removed.} \subsection{Public methods}{ \itemize{ \item \href{#method-ParamSetCollection-new}{\code{ParamSetCollection$new()}} +\item \href{#method-ParamSetCollection-add}{\code{ParamSetCollection$add()}} \item \href{#method-ParamSetCollection-clone}{\code{ParamSetCollection$clone()}} } } @@ -114,6 +115,26 @@ Default \code{FALSE}.} } } \if{html}{\out{
    }} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ParamSetCollection-add}{}}} +\subsection{Method \code{add()}}{ +Adds a set to this collection. +\subsection{Usage}{ +\if{html}{\out{
    }}\preformatted{ParamSetCollection$add(p, n = "", tag_sets = FALSE, tag_params = FALSE)}\if{html}{\out{
    }} +} + +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{p}}{(\link{ParamSet}).} + +\item{\code{n}}{(\code{character(1)})\cr +Name to use. Default \code{""}.} +} +\if{html}{\out{
    }} +} +} +\if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSetCollection-clone}{}}} \subsection{Method \code{clone()}}{ diff --git a/tests/testthat/helper_03_domain.R b/tests/testthat/helper_03_domain.R index 6ab3c278..964a32ad 100644 --- a/tests/testthat/helper_03_domain.R +++ b/tests/testthat/helper_03_domain.R @@ -10,8 +10,8 @@ expect_equal_ps = function(a, b) { acp = acl$.__enclos_env__$private acp$.params$id = sprintf("x%s", seq_len(original$length)) names(acp$.values) = sprintf("x%s", match(names(original$values), original$ids())) - acp$.tags = copy(acp$.tags)[, id := sprintf("x%s", match(id, original$ids()))] - acp$.trafos = copy(acp$.trafos)[, id := sprintf("x%s", match(id, original$ids()))] + acp$.tags = setkeyv(copy(acp$.tags)[, id := sprintf("x%s", match(id, original$ids()))], key(acp$.tags)) + acp$.trafos = setkeyv(copy(acp$.trafos)[, id := sprintf("x%s", match(id, original$ids()))], key(acp$.trafos)) acp$.deps[, id := sprintf("x%s", match(id, original$ids()))] setindexv(acp$.params, NULL) setindexv(acp$.trafos, NULL) diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index 5812f25b..4cd8dc98 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -65,9 +65,9 @@ test_that("ParamSet basic stuff works", { expect_equal(ps1, ps1clone) expect_equal(ps2, ps2clone) - # adding a set (not really supported any more) + # adding a set ps4 = ParamSet_legacy$new(list(ParamDbl$new("x"))) - psc = ps_union(list(psc, s4 = ps4)) + psc = psc$add(ps4, n = "s4") expect_equal(psc$length, ps1$length + ps2$length + ps3$length + ps4$length) expect_equal(psc$ids(), c(paste0("s1.", ps1$ids()), paste0("s2.", ps2$ids()), ps3$ids(), paste0("s4.", ps4$ids()))) }) @@ -215,11 +215,11 @@ test_that("set_id inference in values assignment works now", { pstest = ParamSet_legacy$new(list(ParamDbl$new("paramc"))) - expect_error(ps_union(list(pscol2, a.c = pstest)), "duplicated parameter names.* a\\.c\\.paramc") + expect_error(pscol2$add(pstest, n = "a.c"), "would lead to nameclashes.*a\\.c\\.paramc") pstest = ParamSet_legacy$new(list(ParamDbl$new("a.c.paramc"))) - expect_error(ps_union(list(pscol2, pstest)), "duplicated parameter names.* a\\.c\\.paramc") + expect_error(pscol2$add(pstest), "would lead to nameclashes.*a\\.c\\.paramc") pscol2$values = list(a.c.paramc = 3, a.b.parama = 1, a.b.paramb = 2) From 8228e0f66904c3df182810eed93fd90d5332e1af Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 14 Jan 2024 17:19:14 +0100 Subject: [PATCH 51/64] bugfix --- R/ParamSetCollection.R | 3 ++- tests/testthat/test_ParamSetCollection.R | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index ecee4f40..c5a79dc1 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -182,7 +182,8 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, # We do this here because we don't want the loop to be aborted early and have half an update. self$assert(xs) - translate = private$.translation[names(xs), list(original_id, owner_ps_index), on = "id"] + # %??% character(0) in case xs is an empty unnamed list + translate = private$.translation[names(xs) %??% character(0), list(original_id, owner_ps_index), on = "id"] set(translate, , j = "values", list(xs)) for (xtl in split(translate, by = "owner_ps_index")) { sets[[xtl$owner_ps_index[[1]]]]$values = set_names(xtl$values, xtl$original_id) diff --git a/tests/testthat/test_ParamSetCollection.R b/tests/testthat/test_ParamSetCollection.R index 4cd8dc98..91325115 100644 --- a/tests/testthat/test_ParamSetCollection.R +++ b/tests/testthat/test_ParamSetCollection.R @@ -154,6 +154,10 @@ test_that("values", { expect_equal(ps1clone, ps1) expect_equal(ps2clone, ps2) + + # resetting pcs values + pcs$values = list() + expect_list(pcs$values, len = 0) }) test_that("empty collections", { From df23a385d175de34eb391940038b1f7a644c2209 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 14 Jan 2024 17:54:56 +0100 Subject: [PATCH 52/64] checking strict now --- R/ParamSet.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index 9e080f50..a204d9f7 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -253,7 +253,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xs (named `list()`). #' @return If successful `TRUE`, if not a string with the error message. - check = function(xs, check_strict = FALSE) { + check = function(xs, check_strict = TRUE) { assert_flag(check_strict) ok = check_list(xs, names = "unique") if (!isTRUE(ok)) { @@ -341,7 +341,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xs (named `list()`). #' @return If successful `TRUE`, if not `FALSE`. - test = function(xs, check_strict = FALSE) makeTest(self$check(xs, check_strict = check_strict)), + test = function(xs, check_strict = TRUE) makeTest(self$check(xs, check_strict = check_strict)), #' @description #' \pkg{checkmate}-like assert-function. Takes a named list. @@ -354,7 +354,7 @@ ParamSet = R6Class("ParamSet", #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. #' @return If successful `xs` invisibly, if not an error message. - assert = function(xs, check_strict = FALSE, .var.name = vname(xs)) makeAssertion(xs, self$check(xs, check_strict = check_strict), .var.name, NULL), # nolint + assert = function(xs, check_strict = TRUE, .var.name = vname(xs)) makeAssertion(xs, self$check(xs, check_strict = check_strict), .var.name, NULL), # nolint #' @description #' \pkg{checkmate}-like check-function. Takes a [data.table::data.table] @@ -365,7 +365,7 @@ ParamSet = R6Class("ParamSet", #' #' @param xdt ([data.table::data.table] | `data.frame()`). #' @return If successful `TRUE`, if not a string with the error message. - check_dt = function(xdt, check_strict = FALSE) { + check_dt = function(xdt, check_strict = TRUE) { xss = map(transpose_list(xdt), discard, is.na) msgs = list() for (i in seq_along(xss)) { @@ -393,7 +393,7 @@ ParamSet = R6Class("ParamSet", #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. #' @return If successful `xs` invisibly, if not an error message. - assert_dt = function(xdt, check_strict = FALSE, .var.name = vname(xdt)) makeAssertion(xdt, self$check_dt(xdt, check_strict = check_strict), .var.name, NULL), # nolint + assert_dt = function(xdt, check_strict = TRUE, .var.name = vname(xdt)) makeAssertion(xdt, self$check_dt(xdt, check_strict = check_strict), .var.name, NULL), # nolint qunif = function(x) { assert(check_data_frame(x, types = "numeric", min.cols = 1), check_matrix(x, mode = "numeric", min.cols = 1)) From bf78002431398b5be81892d553e956a24993a378 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 14 Jan 2024 18:20:04 +0100 Subject: [PATCH 53/64] handle requirements as in current paradox --- R/ParamSet.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index a204d9f7..7fdb1038 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -294,12 +294,12 @@ ParamSet = R6Class("ParamSet", } if (check_strict) { - required = setdiff(self$ids(tags = "required"), ns) - if (length(required) > 0L) { - return(sprintf("Missing required parameters: %s", str_collapse(required))) - } - return(self$check_dependencies(xs)) + ## required = setdiff(self$ids(tags = "required"), ns) + ## if (length(required) > 0L) { + ## return(sprintf("Missing required parameters: %s", str_collapse(required))) + ## } if (!self$test_constraint(xs, assert_value = FALSE)) return(sprintf("Constraint not fulfilled.")) + return(self$check_dependencies(xs)) } TRUE # we passed all checks From 3ab4d31218bb291cee5946b3a29a768ba1257dca Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 14:43:49 +0100 Subject: [PATCH 54/64] documentation --- R/ParamSet.R | 192 ++++++++++++++++++++++---------- R/ParamSetCollection.R | 39 ++++--- R/Sampler.R | 6 +- R/Sampler1D.R | 6 +- R/SamplerHierarchical.R | 6 +- R/SamplerJointIndep.R | 4 +- R/SamplerUnif.R | 2 + R/asserts.R | 6 +- R/generate_design_grid.R | 14 +-- R/generate_design_lhs.R | 12 +- R/generate_design_random.R | 14 +-- R/generate_design_sobol.R | 12 +- R/helper.R | 2 +- R/ps.R | 7 +- R/ps_replicate.R | 54 ++++++++- R/ps_union.R | 50 +++++++-- R/to_tune.R | 48 ++++---- man-roxygen/field_constraint.R | 4 + man-roxygen/field_extra_trafo.R | 10 ++ man-roxygen/field_params.R | 4 +- man-roxygen/param_param.R | 3 +- 21 files changed, 335 insertions(+), 160 deletions(-) create mode 100644 man-roxygen/field_constraint.R create mode 100644 man-roxygen/field_extra_trafo.R diff --git a/R/ParamSet.R b/R/ParamSet.R index 7fdb1038..df020eff 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -1,41 +1,50 @@ #' @title ParamSet #' #' @description -#' A set of [Param] objects. -#' Please note that when creating a set or adding to it, the parameters of the -#' resulting set have to be uniquely named with IDs with valid R names. -#' The set also contains a member variable `values` which can be used to store an active configuration / -#' or to partially fix -#' some parameters to constant values (regarding subsequent sampling or generation of designs). +#' An object representing the space of possible parametrizations of a function or another object. +#' `ParamSet`s are used on the side of objects being parameterized, where they function as a configuration space determining the set of possible configurations accepted by these objects. +#' They can also be used to specify search spaces for optimization, indicating the set of legal configurations to try out. +#' It is often convenient to generate search spaces from configuration spaces, which can be done using the `$search_space()` method in combination with `to_tune()` / [`TuneToken`] objects. +#' +#' Individual dimensions of a `ParamSet` are specified by [`Domain`] objects, created as [`p_dbl()`], [`p_lgl()`] etc. +#' The field `$values` can be used to store an active configuration or to partially fix +#' some parameters to constant values -- the precise effect can be determined by the object being parameterized. +#' +#' Constructing a `ParamSet` can be done using `ParamSet$new()` in combination with a named list of [`Domain`] objects. +#' This route is recommended when the set of dimensions (i.e. the members of this named list) is dynamically created, such as when the number of parameters is variable. +#' `ParamSet`s can also be created using the [`ps()`] shorthand, which is the recommended way when the set of parameters is fixed. +#' In practice, the majority of cases where a `ParamSet` is created, the [`ps()`] should be used. #' #' @section S3 methods and type converters: #' * `as.data.table()`\cr -#' [ParamSet] -> [data.table::data.table()]\cr +#' `ParamSet` -> [data.table::data.table()]\cr #' Compact representation as datatable. Col types are:\cr #' - id: character -#' - lower, upper: double +#' - class: character +#' - lower, upper: numeric #' - levels: list col, with NULL elements -#' - special_vals: list col of list +#' - nlevels: integer valued numeric #' - is_bounded: logical -#' - default: list col, with NULL elements +#' - special_vals: list col of list +#' - default: list col #' - storage_type: character #' - tags: list col of character vectors #' @examples -#' ps = ParamSet$new( +#' pset = ParamSet$new( #' params = list( -#' ParamDbl$new("d", lower = -5, upper = 5, default = 0), -#' ParamFct$new("f", levels = letters[1:3]) +#' d = p_dbl(lower = -5, upper = 5, default = 0, trafo = function(x) 2^x), +#' f = p_fct(levels = letters[1:3]) #' ) #' ) #' -#' ps$trafo = function(x, param_set) { -#' x$d = 2^x$d -#' return(x) -#' } -#' -#' ps$add(ParamInt$new("i", lower = 0L, upper = 16L)) +#' # alternative, recommended way of construction in this case since the +#' # parameter list is not dynamic: +#' pset = ps( +#' d = p_dbl(lower = -5, upper = 5, default = 0, trafo = function(x) 2^x), +#' f = p_fct(levels = letters[1:3]) +#' ) #' -#' ps$check(list(d = 2.1, f = "a", i = 3L)) +#' ps$check(list(d = 2.1, f = "a")) #' @export ParamSet = R6Class("ParamSet", public = list( @@ -48,19 +57,13 @@ ParamSet = R6Class("ParamSet", #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' - #' @param params (`list()`)\cr - #' List of [Param], named with their respective ID. - #' `params` are cloned on-demand, so the input does not need - #' to be cloned. - #' @param set_id (`character(1)`)\cr - #' `$set_id` of the resulting `ParamSet`. This determines the - #' prefix inside [`ParamSetCollection`]. Default `""` (no prefix). - #' @param ignore_ids (`logical(1)`)\cr - #' Ignore `$id` slots of `params` and instead use the names instead. - #' When this is `TRUE`, then `params` must be named. - #' Thisdo can be used to create a `ParamSet` with certain [`Param`] `id`s - #' without having to clone said [`Param`]s. - #' Default `FALSE`. + #' @param params (named `list()`)\cr + #' List of [`Domain`], named with their respective ID. + #' @param allow_dangling_dependencies (`character(1)`)\cr + #' Whether dependencies depending on parameters that are not present should be allowed. A parameter `x` having + #' `depends = y == 0` if `y` is not present would usually throw an error, but if dangling + #' dependencies are allowed, the dependency is added regardless. This is mainly for internal + #' use. initialize = function(params = named_list(), allow_dangling_dependencies = FALSE) { assert_list(params, types = "Domain") @@ -112,8 +115,14 @@ ParamSet = R6Class("ParamSet", #' selections, `NULL` means no restriction. #' Only returns IDs of parameters that satisfy all conditions. #' - #' @param class (`character()`). + #' @param class (`character()`)\cr + #' Typically a subset of `"ParamDbl"`, `"ParamInt"`, `"ParamFct"`, `"ParamLgl"`, `"ParamUty"`. + #' Other classes are possible if implemented by 3rd party packages. + #' Return only IDs of dimensions with the given class. #' @param tags (`character()`). + #' Return only IDs of dimensions that have *all* tags given in this argument. + #' @param any_tags (`character()`). + #' Return only IDs of dimensions that have at least one of the tags given in this argument. #' @return `character()`. ids = function(class = NULL, tags = NULL, any_tags = NULL) { assert_character(class, any.missing = FALSE, null.ok = TRUE) @@ -138,13 +147,16 @@ ParamSet = R6Class("ParamSet", #' restriction and is equivalent to `$values`. #' Only returns values of parameters that satisfy all conditions. #' - #' @param class (`character()`). - #' @param is_bounded (`logical(1)`). - #' @param tags (`character()`). + #' @param class (`character()`). See `$ids()`. + #' @param tags (`character()`). See `$ids()`. + #' @param any_tags (`character()`). See `$ids()`. #' @param type (`character(1)`)\cr - #' Return values `with_token`, `without_token` or `only_token`? + #' Return values `"with_token"` (i.e. all values), + # `"without_token"` (all values that are not [`TuneToken`] objects) or `"only_token"` (only [`TuneToken`] objects)? #' @param check_required (`logical(1)`)\cr - #' Check if all required parameters are set? + #' Check if all required parameters are set? + #' @param remove_dependencies (`logical(1)`)\cr + #' If `TRUE`, set values with dependencies that are not fulfilled to `NULL`. #' @return Named `list()`. get_values = function(class = NULL, tags = NULL, any_tags = NULL, type = "with_token", check_required = TRUE, remove_dependencies = TRUE) { @@ -210,6 +222,14 @@ ParamSet = R6Class("ParamSet", invisible(self) }, + #' @description + #' Perform transformation specified by the `trafo` of [`Domain`] objects, as well as the `$extra_trafo` field. + #' @param x (named `list()` | `data.frame`)\cr + #' The value(s) to be transformed. + #' @param param_set (`ParamSet`)\cr + #' Passed to `extra_trafo()`. Note that the `extra_trafo` of `self` is used, not the `extra_trafo` of the + #' `ParamSet` given in the `param_set` argument. + #' In almost all cases, the default `param_set = self` should be used. trafo = function(x, param_set = self) { if (is.data.frame(x)) x = as.list(x) assert_list(x, names = "unique") @@ -233,15 +253,36 @@ ParamSet = R6Class("ParamSet", x }, - # assert_value: used internally, to avoid touble-asserts + #' @description + #' \pkg{checkmate}-like test-function. Takes a named list. + #' Return `FALSE` if the given `$constraint` is not satisfied, `TRUE` otherwise. + #' Note this is different from satisfying the bounds or types given by the `ParamSet` itself: + #' If `x` does not satisfy these, an error will be thrown, given that `assert_value` is `TRUE`. + #' @param x (named `list()`)\cr + #' The value to test. + #' @param assert_value (`logical(1)`)\cr + #' Whether to verify that `x` satisfies the bounds and types given by this `ParamSet`. + #' Should be `TRUE` unless this was already checked before. + #' @return `logical(1)`: Whether `x` satisfies the `$constraint`. test_constraint = function(x, assert_value = TRUE) { - if (assert_value) self$assert(x) + if (assert_value) self$assert(x, check_strict = FALSE) assert_flag(is.null(private$.constraint) || private$.constraint(x)) }, + #' @description + #' \pkg{checkmate}-like test-function. Takes a [`data.table`]. + #' For each row, return `FALSE` if the given `$constraint` is not satisfied, `TRUE` otherwise. + #' Note this is different from satisfying the bounds or types given by the `ParamSet` itself: + #' If `x` does not satisfy these, an error will be thrown, given that `assert_value` is `TRUE`. + #' @param x (`data.table`)\cr + #' The values to test. + #' @param assert_value (`logical(1)`)\cr + #' Whether to verify that `x` satisfies the bounds and types given by this `ParamSet`. + #' Should be `TRUE` unless this was already checked before. + #' @return `logical`: For each row in `x`, whether it satisfies the `$constraint`. test_constraint_dt = function(x, assert_value = TRUE) { assert_data_table(x) - if (assert_value) self$assert_dt(x) + if (assert_value) self$assert_dt(x, check_strict = FALSE) map_lgl(transpose(x), self$test_constraint, assert_value = FALSE) }, @@ -250,9 +291,12 @@ ParamSet = R6Class("ParamSet", #' A point x is feasible, if it configures a subset of params, #' all individual param constraints are satisfied and all dependencies are satisfied. #' Params for which dependencies are not satisfied should not be part of `x`. + #' Constraints and dependencies are not checked when `check_strict` is `FALSE`. #' #' @param xs (named `list()`). - #' @return If successful `TRUE`, if not a string with the error message. + #' @param check_strict (`logical(1)`)\cr + #' Whether to check that constraints and dependencies are satisfied. + #' @return If successful `TRUE`, if not a string with an error message. check = function(xs, check_strict = TRUE) { assert_flag(check_strict) ok = check_list(xs, names = "unique") @@ -305,6 +349,12 @@ ParamSet = R6Class("ParamSet", TRUE # we passed all checks }, + #' @description + #' \pkg{checkmate}-like check-function. Takes a named list. + #' Checks that all individual param dependencies are satisfied. + #' + #' @param xs (named `list()`). + #' @return If successful `TRUE`, if not a string with an error message. check_dependencies = function(xs) { deps = self$deps if (!nrow(deps)) return(TRUE) @@ -338,8 +388,11 @@ ParamSet = R6Class("ParamSet", #' A point x is feasible, if it configures a subset of params, #' all individual param constraints are satisfied and all dependencies are satisfied. #' Params for which dependencies are not satisfied should not be part of `x`. + #' Constraints and dependencies are not checked when `check_strict` is `FALSE`. #' #' @param xs (named `list()`). + #' @param check_strict (`logical(1)`)\cr + #' Whether to check that constraints and dependencies are satisfied. #' @return If successful `TRUE`, if not `FALSE`. test = function(xs, check_strict = TRUE) makeTest(self$check(xs, check_strict = check_strict)), @@ -348,8 +401,11 @@ ParamSet = R6Class("ParamSet", #' A point x is feasible, if it configures a subset of params, #' all individual param constraints are satisfied and all dependencies are satisfied. #' Params for which dependencies are not satisfied should not be part of `x`. + #' Constraints and dependencies are not checked when `check_strict` is `FALSE`. #' #' @param xs (named `list()`). + #' @param check_strict (`logical(1)`)\cr + #' Whether to check that constraints and dependencies are satisfied. #' @param .var.name (`character(1)`)\cr #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. @@ -358,12 +414,15 @@ ParamSet = R6Class("ParamSet", #' @description #' \pkg{checkmate}-like check-function. Takes a [data.table::data.table] - #' where rows are points and columns are parameters. A point x is feasible, - #' if it configures a subset of params, all individual param constraints are - #' satisfied and all dependencies are satisfied. Params for which - #' dependencies are not satisfied should be set to `NA` in `xdt`. + #' where rows are points and columns are parameters. + #' A point x is feasible, if it configures a subset of params, + #' all individual param constraints are satisfied and all dependencies are satisfied. + #' Params for which dependencies are not satisfied should not be part of `x`. + #' Constraints and dependencies are not checked when `check_strict` is `FALSE`. #' #' @param xdt ([data.table::data.table] | `data.frame()`). + #' @param check_strict (`logical(1)`)\cr + #' Whether to check that constraints and dependencies are satisfied. #' @return If successful `TRUE`, if not a string with the error message. check_dt = function(xdt, check_strict = TRUE) { xss = map(transpose_list(xdt), discard, is.na) @@ -382,19 +441,29 @@ ParamSet = R6Class("ParamSet", #' \pkg{checkmate}-like test-function (s. `$check_dt()`). #' #' @param xdt ([data.table::data.table]). + #' @param check_strict (`logical(1)`)\cr + #' Whether to check that constraints and dependencies are satisfied. #' @return If successful `TRUE`, if not `FALSE`. - test_dt = function(xdt) makeTest(res = self$check_dt(xdt, check_strict = check_strict)), + test_dt = function(xdt, check_strict = TRUE) makeTest(res = self$check_dt(xdt, check_strict = check_strict)), #' @description #' \pkg{checkmate}-like assert-function (s. `$check_dt()`). #' #' @param xdt ([data.table::data.table]). + #' @param check_strict (`logical(1)`)\cr + #' Whether to check that constraints and dependencies are satisfied. #' @param .var.name (`character(1)`)\cr #' Name of the checked object to print in error messages.\cr #' Defaults to the heuristic implemented in [vname][checkmate::vname]. - #' @return If successful `xs` invisibly, if not an error message. + #' @return If successful `xs` invisibly, if not, an error is generated. assert_dt = function(xdt, check_strict = TRUE, .var.name = vname(xdt)) makeAssertion(xdt, self$check_dt(xdt, check_strict = check_strict), .var.name, NULL), # nolint + #' @description + #' Map a `matrix` or `data.frame` of values between 0 and 1 to proportional values inside the feasible intervals of individual parameters. + #' + #' @param x (`matrix` | `data.frame`)\cr + #' Values to map. Column names must be a subset of the names of parameters. + #' @return `data.table`. qunif = function(x) { assert(check_data_frame(x, types = "numeric", min.cols = 1), check_matrix(x, mode = "numeric", min.cols = 1)) if (is.matrix(x)) { @@ -440,6 +509,11 @@ ParamSet = R6Class("ParamSet", #' @description #' Create a new `ParamSet` restricted to the passed IDs. #' @param ids (`character()`). + #' @param allow_dangling_dependencies (`logical(1)`)\cr + #' Whether to allow subsets that cut across parameter dependencies. + #' Dependencies that point to dropped parameters are kept (but will be "dangling", i.e. their `"on"` will not be present). + #' @param keep_constraint (`logical(1)`)\cr + #' Whether to keep the `$constraint` function. #' @return `ParamSet`. subset = function(ids, allow_dangling_dependencies = FALSE, keep_constraint = TRUE) { param_ids = private$.params$id @@ -473,6 +547,11 @@ ParamSet = R6Class("ParamSet", result }, + #' @description + #' Create new one-dimensional `ParamSet`s for each dimension. + #' @param ids (`character()`)\cr + #' IDs for which to create `ParamSet`s. Defaults to all IDs. + #' @return named `list()` of `ParamSet`. subspaces = function(ids = private$.params$id) { values = self$values sapply(ids, simplify = FALSE, function(get_id) { @@ -492,7 +571,7 @@ ParamSet = R6Class("ParamSet", #' @description #' Create a `ParamSet` from this object, even if this object itself is not - #' a `ParamSet`. + #' a `ParamSet` but e.g. a [`ParamSetCollection`]. flatten = function() self$subset(private$.params$id, allow_dangling_dependencies = TRUE), #' @description @@ -653,17 +732,7 @@ ParamSet = R6Class("ParamSet", set_names(map(nm, self$get_domain), nm) }, - - #' @field extra_trafo (`function(x, param_set)`)\cr - #' Transformation function. Settable. - #' User has to pass a `function(x)`, of the form\cr - #' (named `list()`, [ParamSet]) -> named `list()`.\cr - #' The function is responsible to transform a feasible configuration into another encoding, - #' before potentially evaluating the configuration with the target algorithm. - #' For the output, not many things have to hold. - #' It needs to have unique names, and the target algorithm has to accept the configuration. - #' For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). - #' Is NULL by default, and you can set it to NULL to switch the transformation off. + #' @template field_extra_trafo extra_trafo = function(f) { if (missing(f)) { private$.extra_trafo @@ -673,6 +742,7 @@ ParamSet = R6Class("ParamSet", } }, + #' @template field_constraint constraint = function(f) { if (missing(f)) { private$.constraint diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index c5a79dc1..4048e322 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -1,14 +1,15 @@ #' @title ParamSetCollection #' #' @description -#' A collection of multiple [ParamSet] objects. +#' A collection of multiple [`ParamSet`] objects. #' * The collection is basically a light-weight wrapper / container around references to multiple sets. #' * In order to ensure unique param names, every param in the collection is referred to with -#' ".". Parameters from ParamSets with empty (i.e. `""`) `$set_id` are referenced -#' directly. Multiple ParamSets with `$set_id` `""` can be combined, but their parameter names -#' must be unique. +#' ".", where `` is the name of the entry a given [`ParamSet`] in the named list given during construction. +#' Parameters from [`ParamSet`] with empty (i.e. `""`) `set_id` are referenced +#' directly. Multiple [`ParamSet`]s with `set_id` `""` can be combined, but their parameter names +#' may not overlap to avoid name clashes. #' * When you either ask for 'values' or set them, the operation is delegated to the individual, -#' contained param set references. The collection itself does not maintain a `values` state. +#' contained [`ParamSet`] references. The collection itself does not maintain a `values` state. #' This also implies that if you directly change `values` in one of the referenced sets, #' this change is reflected in the collection. #' * Dependencies: It is possible to currently handle dependencies @@ -26,20 +27,13 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' - #' @param sets (`list()` of [ParamSet])\cr + #' @param sets (named `list()` of [ParamSet])\cr #' ParamSet objects are not cloned. - #' @param set_id (`character(1)`)\cr - #' `$set_id` of the resulting `ParamSet`. This determines the - #' prefix inside [`ParamSetCollection`]. Default `""` (no prefix). - #' @param ignore_ids (`logical(1)`)\cr - #' Ignore `$set_id` slots of `sets` and instead use the names instead. - #' When this is `TRUE`, then `sets` should be named when id prefixes are desired. - #' This can be used to create a `ParamSetCollection` with changed [`ParamSet`] `set_id`s - #' without having to clone said [`ParamSet`]s. However, when underlying [`ParamSet`]s' - #' `$set_id`s change, then this change is no longer reflected in the ParamSetCollection. - #' Also note that the `$values` will have undefined behavior when `sets` contains a - #' single [`ParamSet`] multiple times (by reference). - #' Default `FALSE`. + #' Names are used as "set_id" for the naming scheme of delegated parameters. + #' @param tag_sets (`logical(1)`)\cr + #' Whether to add tags of the form `"set_"` to each parameter originating from a given `ParamSet` given with name ``. + #' @param tag_params (`logical(1)`)\cr + #' Whether to add tags of the form `"param_"` to each parameter with original ID ``. initialize = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") assert_flag(tag_sets) @@ -89,7 +83,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, }, #' @description - #' Adds a set to this collection. + #' Adds a [`ParamSet`] to this collection. #' #' @param p ([ParamSet]). #' @param n (`character(1)`)\cr @@ -198,6 +192,7 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, vals }, + #' @template field_extra_trafo extra_trafo = function(f) { if (!missing(f)) stop("extra_trafo is read-only in ParamSetCollection.") if (!length(private$.children_with_trafos)) return(NULL) @@ -205,14 +200,18 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, }, + #' @template field_constraint constraint = function(f) { if (!missing(f)) stop("constraint is read-only in ParamSetCollection.") if (!length(private$.children_with_constraints)) return(NULL) private$.constraint_explicit }, + #' @field sets (named `list()`)\cr + #' Read-only `list` of of [`ParamSet`]s contained in this `ParamSetCollection`. + #' This field provides direct references to the [`ParamSet`] objects. sets = function(v) { - if (!missing(v)) stop("sets is read-only") + if (!missing(v) && !identical(v, private$.sets)) stop("sets is read-only") private$.sets } ), diff --git a/R/Sampler.R b/R/Sampler.R index 74b42a09..80bf2f9e 100644 --- a/R/Sampler.R +++ b/R/Sampler.R @@ -9,7 +9,7 @@ #' @export Sampler = R6Class("Sampler", public = list( - #' @field param_set ([ParamSet])\cr + #' @field param_set ([`ParamSet`])\cr #' Domain / support of the distribution we want to sample from. param_set = NULL, @@ -18,6 +18,8 @@ Sampler = R6Class("Sampler", #' #' Note that this object is typically constructed via derived classes, #' e.g., [Sampler1D]. + #' @param param_set ([`ParamSet`])\cr + #' The [`ParamSet`] to associated with this `Sampler`. initialize = function(param_set) { assert_param_set(param_set, no_untyped = TRUE) self$param_set = param_set$clone(deep = TRUE) @@ -27,7 +29,7 @@ Sampler = R6Class("Sampler", #' Sample `n` values from the distribution. #' #' @param n (`integer(1)`). - #' @return [Design]. + #' @return [`Design`]. sample = function(n) { assert_count(n) # we do argcheck on toplevel Design$new(self$param_set, private$.sample(n), remove_dupl = FALSE) # user wants n points, dont remove diff --git a/R/Sampler1D.R b/R/Sampler1D.R index a53c98e6..e535c0d4 100644 --- a/R/Sampler1D.R +++ b/R/Sampler1D.R @@ -15,7 +15,7 @@ Sampler1D = R6Class("Sampler1D", inherit = Sampler, # abstract base class #' Creates a new instance of this [R6][R6::R6Class] class. #' #' Note that this object is typically constructed via derived classes, - #' e.g., [Sampler1DUnif]. + #' e.g., [`Sampler1DUnif`]. initialize = function(param) { assert_r6(param, "ParamSet") if (param$length != 1) stopf("param must contain exactly 1 Param, but contains %s", param$length) @@ -24,8 +24,8 @@ Sampler1D = R6Class("Sampler1D", inherit = Sampler, # abstract base class ), active = list( - #' @field param ([Param])\cr - #' Returns the one Parameter that is sampled from. + #' @field param ([`ParamSet`])\cr + #' Returns the one-dimensional [`ParamSet`] that is sampled from. param = function() self$param_set ), diff --git a/R/SamplerHierarchical.R b/R/SamplerHierarchical.R index 136b2037..f166f679 100644 --- a/R/SamplerHierarchical.R +++ b/R/SamplerHierarchical.R @@ -13,14 +13,16 @@ SamplerHierarchical = R6Class("SamplerHierarchical", inherit = Sampler, public = list( #' @field samplers (`list()`)\cr - #' List of [Sampler1D] objects that gives a Sampler for each [Param] in the `param_set`. + #' List of [`Sampler1D`] objects that gives a Sampler for each dimension in the `param_set`. samplers = NULL, #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' + #' @param param_set ([`ParamSet`])\cr + #' The [`ParamSet`] to associated with this `SamplerHierarchical`. #' @param samplers (`list()`)\cr - #' List of [Sampler1D] objects that gives a Sampler for each [Param] in the `param_set`. + #' List of [`Sampler1D`] objects that gives a Sampler for each [Param] in the `param_set`. initialize = function(param_set, samplers) { assert_param_set(param_set, no_untyped = TRUE) assert_list(samplers, types = "Sampler1D") diff --git a/R/SamplerJointIndep.R b/R/SamplerJointIndep.R index ed49d468..c5216f29 100644 --- a/R/SamplerJointIndep.R +++ b/R/SamplerJointIndep.R @@ -9,14 +9,14 @@ SamplerJointIndep = R6Class("SamplerJointIndep", inherit = Sampler, public = list( #' @field samplers (`list()`)\cr - #' List of [Sampler] objects. + #' List of [`Sampler`] objects. samplers = NULL, #' @description #' Creates a new instance of this [R6][R6::R6Class] class. #' #' @param samplers (`list()`)\cr - #' List of [Sampler] objects. + #' List of [`Sampler`] objects. initialize = function(samplers) { assert_list(samplers, types = "Sampler") self$samplers = samplers diff --git a/R/SamplerUnif.R b/R/SamplerUnif.R index 6399c806..06bfa70e 100644 --- a/R/SamplerUnif.R +++ b/R/SamplerUnif.R @@ -14,6 +14,8 @@ SamplerUnif = R6Class("SamplerUnif", inherit = SamplerHierarchical, public = list( #' @description #' Creates a new instance of this [R6][R6::R6Class] class. + #' @param param_set ([`ParamSet`])\cr + #' The [`ParamSet`] to associated with this `SamplerUnif`. initialize = function(param_set) { assert_param_set(param_set, must_bounded = TRUE, no_deps = FALSE, no_untyped = TRUE) samplers = lapply(param_set$subspaces(), Sampler1DUnif$new) diff --git a/R/asserts.R b/R/asserts.R index 5356e35f..b5bf6468 100644 --- a/R/asserts.R +++ b/R/asserts.R @@ -1,12 +1,12 @@ #' @title Assertions for Params and ParamSets #' -#' @param param_set ([ParamSet]). +#' @param param_set ([`ParamSet`]). #' @param cl (`character()`)\cr #' Allowed subclasses. #' @param no_untyped (`logical(1)`)\cr -#' Are untyped [Param]s allowed? +#' Are untyped [`Domain`]s allowed? #' @param must_bounded (`logical(1)`)\cr -#' Only bounded [Param]s allowed? +#' Only bounded [`Domain`]s allowed? #' @param no_deps (`logical(1)`)\cr #' Are dependencies allowed? #' @return The checked object, invisibly. diff --git a/R/generate_design_grid.R b/R/generate_design_grid.R index c4213d33..f26de66c 100644 --- a/R/generate_design_grid.R +++ b/R/generate_design_grid.R @@ -6,21 +6,21 @@ #' always produce a grid over all their valid levels. #' For number params the endpoints of the params are always included in the grid. #' -#' @param param_set ([ParamSet]). +#' @param param_set ([`ParamSet`]). #' @param resolution (`integer(1)`)\cr #' Global resolution for all [Param]s. #' @param param_resolutions (named `integer()`)\cr -#' Resolution per [Param], named by parameter ID. -#' @return [Design]. +#' Resolution per [`Domain`], named by parameter ID. +#' @return [`Design`]. #' #' @family generate_design #' @export #' @examples -#' ps = ParamSet$new(list( -#' ParamDbl$new("ratio", lower = 0, upper = 1), -#' ParamFct$new("letters", levels = letters[1:3]) +#' pset = ps( +#' ratio = p_dbl(lower = 0, upper = 1), +#' letters = p_fct(levels = letters[1:3]) #' )) -#' generate_design_grid(ps, 10) +#' generate_design_grid(pset, 10) generate_design_grid = function(param_set, resolution = NULL, param_resolutions = NULL) { assert_param_set(param_set, no_untyped = TRUE) diff --git a/R/generate_design_lhs.R b/R/generate_design_lhs.R index 9c5bd870..0bfa2375 100644 --- a/R/generate_design_lhs.R +++ b/R/generate_design_lhs.R @@ -5,24 +5,24 @@ #' parameters whose constraints are unsatisfied generate `NA` entries in #' their respective columns. #' -#' @param param_set ([ParamSet]). +#' @param param_set ([`ParamSet`]). #' @param n (`integer(1)`) \cr #' Number of points to sample. #' @param lhs_fun (`function(n, k)`)\cr #' Function to use to generate a LHS sample, with n samples and k values per param. #' LHS functions are implemented in package \pkg{lhs}, default is to use [lhs::maximinLHS()]. -#' @return [Design]. +#' @return [`Design`]. #' #' @family generate_design #' @export #' @examples -#' ps = ParamSet$new(list( -#' ParamDbl$new("ratio", lower = 0, upper = 1), -#' ParamFct$new("letters", levels = letters[1:3]) +#' pset = ps( +#' ratio = p_dbl(lower = 0, upper = 1), +#' letters = p_fct(levels = letters[1:3]) #' )) #' #' if (requireNamespace("lhs", quietly = TRUE)) { -#' generate_design_lhs(ps, 10) +#' generate_design_lhs(pset, 10) #' } generate_design_lhs = function(param_set, n, lhs_fun = NULL) { if (is.null(lhs_fun)) { diff --git a/R/generate_design_random.R b/R/generate_design_random.R index 8b1305cd..456cd0bd 100644 --- a/R/generate_design_random.R +++ b/R/generate_design_random.R @@ -2,22 +2,22 @@ #' #' @description #' Generates a design with randomly drawn points. -#' Internally uses [SamplerUnif], hence, also works for [ParamSet]s with dependencies. +#' Internally uses [`SamplerUnif`], hence, also works for [ParamSet]s with dependencies. #' If dependencies do not hold, values are set to `NA` in the resulting data.table. #' -#' @param param_set ([ParamSet]). +#' @param param_set ([`ParamSet`]). #' @param n (`integer(1)`)\cr #' Number of points to draw randomly. -#' @return [Design]. +#' @return [`Design`]. #' #' @family generate_design #' @export #' @examples -#' ps = ParamSet$new(list( -#' ParamDbl$new("ratio", lower = 0, upper = 1), -#' ParamFct$new("letters", levels = letters[1:3]) +#' pset = ps( +#' ratio = p_dbl(lower = 0, upper = 1), +#' letters = p_fct(levels = letters[1:3]) #' )) -#' generate_design_random(ps, 10) +#' generate_design_random(pset, 10) generate_design_random = function(param_set, n) { # arg checks done by SamplerUnif and sample SamplerUnif$new(param_set)$sample(n) diff --git a/R/generate_design_sobol.R b/R/generate_design_sobol.R index e9b8fc2e..26057339 100644 --- a/R/generate_design_sobol.R +++ b/R/generate_design_sobol.R @@ -10,21 +10,21 @@ #' Note that non determinism is achieved by sampling the seed argument via #' `sample(.Machine$integer.max, size = 1L)`. #' -#' @param param_set ([ParamSet]). +#' @param param_set ([`ParamSet`]). #' @param n (`integer(1)`) \cr #' Number of points to sample. -#' @return [Design]. +#' @return [`Design`]. #' #' @family generate_design #' @export #' @examples -#' ps = ParamSet$new(list( -#' ParamDbl$new("ratio", lower = 0, upper = 1), -#' ParamFct$new("letters", levels = letters[1:3]) +#' pset = ps( +#' ratio = p_dbl(lower = 0, upper = 1), +#' letters = p_fct(levels = letters[1:3]) #' )) #' #' if (requireNamespace("spacefillr", quietly = TRUE)) { -#' generate_design_sobol(ps, 10) +#' generate_design_sobol(pset, 10) #' } generate_design_sobol = function(param_set, n) { require_namespaces("spacefillr") diff --git a/R/helper.R b/R/helper.R index c0ebc0d2..53ca3f44 100644 --- a/R/helper.R +++ b/R/helper.R @@ -8,7 +8,7 @@ #' @param data ([data.table::data.table])\cr #' Rows are points and columns are parameters. #' -#' @param ps ([ParamSet])\cr +#' @param ps ([`ParamSet`])\cr #' If `trafo = TRUE`, used to call trafo function. #' #' @param filter_na (`logical(1)`)\cr diff --git a/R/ps.R b/R/ps.R index 09687cd1..a111a68e 100644 --- a/R/ps.R +++ b/R/ps.R @@ -8,10 +8,9 @@ #' #' For more specifics also see the documentation of [`Domain`]. #' -#' @param ... ([`Domain`] | [`Param`])\cr -#' Named arguments of [`Domain`] or [`Param`] objects. The [`ParamSet`] will be constructed of the given [`Param`]s, -#' or of [`Param`]s constructed from the given domains. The names of the arguments will be used as `$id` -#' (the `$id` of [`Param`] arguments are ignored). +#' @param ... ([`Domain`])\cr +#' Named arguments of [`Domain`] objects. The [`ParamSet`] will be constructed of the given [`Domain`]s, +#' The names of the arguments will be used as `$id()` in the resulting [`ParamSet`]. #' @param .extra_trafo (`function(x, param_set)`)\cr #' Transformation to set the resulting [`ParamSet`]'s `$trafo` value to. This is in addition to any `trafo` of #' [`Domain`] objects given in `...`, and will be run *after* transformations of individual parameters were performed. diff --git a/R/ps_replicate.R b/R/ps_replicate.R index 1830c125..cf79919e 100644 --- a/R/ps_replicate.R +++ b/R/ps_replicate.R @@ -1,4 +1,56 @@ -#' @title +#' @title Create a ParamSet by Repeating a Given ParamSet +#' +#' @description +#' Repeat a [`ParamSet`] a given number of times and thus create a larger [`ParamSet`]. +#' By default, the resulting parameters are prefixed with the string `"repX.", where `X` counts up from 1. +#' It is also possible to tag parameters by their original name and by their prefix, making grouped retrieval e.g. using `$get_values()` easier. +#' +#' @param set ([`ParamSet`])\cr +#' [`ParamSet`] to use as template. +#' @param times (`integer(1)`)\cr +#' Number of times to repeat `set`. +#' Should not be given if `prefixes` is provided. +#' @param prefixes (`character`)\cr +#' A `character` vector indicating the prefixes to use for each repetition of `set`. +#' If this is given, `times` is inferred from `length(prefixes)` and should not be given separately. +#' If `times` is given, this defaults to `"repX"`, with `X` counting up from 1. +#' @param tag_sets (`logical(1)`)\cr +#' Whether to add a tag of the form `"set_"` to each parameter in the result, indicating the repetition each parameter belongs to. +#' @param tag_params (`logical(1)`)\cr +#' Whether to add a tag of the form `"param_"` to each parameter in the result, indicating the original parameter ID inside `set`. +#' @examples +#' pset = ps( +#' i = p_int(), +#' z = p_lgl() +#' ) +#' +#' ps_replicate(pset, 3) +#' +#' ps_replicate(pset, prefixes = c("first", "last")) +#' +#' pset$values = list(i = 1, z = FALSE) +#' +#' psr = ps_replicate(pset, 2, tag_sets = TRUE, tag_params = TRUE) +#' +#' # observe the effect of tag_sets, tag_params: +#' psr$tags +#' +#' # note that values are repeated as well +#' psr$values +#' +#' psr$set_values(rep1.i = 10, rep2.z = TRUE) +#' psr$values +#' +#' # use `any_tags` to get subset of values. +#' # `any_tags = ` is preferable to `tags = `, since parameters +#' # could also have other tags. `tags = ` would require the +#' # selected params to have the given tags exclusively. +#' +#' # get all values associated with the original parameter `i` +#' psr$get_values(any_tags = "param_i") +#' +#' # get all values associated with the first repetition "rep1" +#' psr$get_values(any_tags = "set_rep1") #' @export ps_replicate = function(set, times = length(prefixes), prefixes = sprintf("rep%s", seq_len(times)), tag_sets = FALSE, tag_params = FALSE) { assert_count(times) diff --git a/R/ps_union.R b/R/ps_union.R index 41b0ffc9..a751d6f1 100644 --- a/R/ps_union.R +++ b/R/ps_union.R @@ -1,14 +1,48 @@ #' @title Create a ParamSet from a list of ParamSets #' #' @description -#' This emulates `ParamSetCollection$new(sets)`, except that -#' - The result is a `ParamSet`, not a `ParamSetCollection` -#' - The ParamSets are allowed to have `$trafo`, which are collected together into a single function. -#' This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting ParamSet has all the Params -#' from the input `sets`, but some `$id`s are changed: If the ParamSet has a non-empty `set_id`, then the Params will -#' have their changed to .. This is also reflected in deps and in `$trafo`. -#' @param sets: list of ParamSet -#' @param tag_sets +#' This emulates `ParamSetCollection$new(sets)`, except that the result is a flat [`ParamSet`], not a [`ParamSetCollection`]. +#' The resulting object is decoupled from the input [`ParamSet`] objects: Unlike [`ParamSetCollection`], changing `$values` of +#' the resulting object will not change the input [`ParamSet`] `$values` by reference. +#' +#' This emulates `ParamSetCollection$new(sets)`, which in particular means that the resulting [`ParamSet`] has all the [`Domain`]s +#' from the input `sets`, but some `$id`s are changed: If the [`ParamSet`] is given in `sets` with a name, then the [`Domain`]s will +#' have their `` changed to `.`. This is also reflected in deps. +#' +#' The `c()` operator, applied to [`ParamSet`]s, is a synony for `ps_union()`. +#' @param sets (`list` of [`ParamSet`])\cr +#' This may be a named list, in which case non-empty names are prefixed to parameters in the corresponding [`ParamSet`]. +#' @param tag_sets (`logical(1)`)\cr +#' Whether to add tags of the form `"set_"` to each parameter originating from a given `ParamSet` given with name ``. +#' @param tag_params (`logical(1)`)\cr +#' Whether to add tags of the form `"param_"` to each parameter with original ID ``. +#' @examples +#' ps1 = ps(x = p_dbl()) +#' ps1$values = list(x = 1) +#' +#' ps2 = ps(y = p_lgl()) +#' +#' pu = ps_union(list(ps1, ps2)) +#' # same as: +#' pu = c(ps1, ps2) +#' +#' pu +#' +#' pu$values +#' +#' pu$values$x = 2 +#' pu$values +#' +#' # p1 is unchanged: +#' ps1$values +#' +#' # Prefixes automatically created for named elements. +#' # This allows repeating components. +#' pu2 = c(one = ps1, two = ps1, ps2) +#' pu2 +#' +#' pu2$values +#' #' @export ps_union = function(sets, tag_sets = FALSE, tag_params = FALSE) { assert_list(sets, types = "ParamSet") diff --git a/R/to_tune.R b/R/to_tune.R index 4a92beac..0771179a 100644 --- a/R/to_tune.R +++ b/R/to_tune.R @@ -26,37 +26,36 @@ #' it must be named to disambiguate from the following cases.\cr #' When `logscale` is `TRUE`, then a `trafo` is generated automatically that transforms to the given bounds. The #' bounds are log()'d pre-trafo (see examples). See the `logscale` argument of [`Domain`] functions for more info.\cr -#' Note that "logscale" is *not* inherited from the [`Param`] that the `TuneToken` belongs to! Defining a parameter +#' Note that "logscale" is *not* inherited from the [`Domain`] that the `TuneToken` belongs to! Defining a parameter #' with `p_dbl(... logscale = TRUE)` will *not* automatically give the `to_tune()` assigned to it log-scale. #' * **`to_tune(levels)`**: Indicates a parameter should be tuned through the given discrete values. `levels` can be any #' named or unnamed atomic vector or list (although in the unnamed case it must be possible to construct a #' corresponding `character` vector with distinct values using `as.character`). #' * **`to_tune()`**: The given [`Domain`] object (constructed e.g. with [`p_int()`] or [`p_fct()`]) indicates #' the range which should be tuned over. The supplied `trafo` function is used for parameter transformation. -#' * **`to_tune()`**: The given [`Param`] object indicates the range which should be tuned over. -#' * **`to_tune()`**: The given [`ParamSet`] is used to tune over a single `Param`. This is useful for cases -#' where a single evaluation-time parameter value (e.g. [`ParamUty`]) is constructed from multiple tuner-visible -#' parameters (which may not be `ParamUty`). The supplied [`ParamSet`] should always contain a `$trafo` function, -#' which must always return a `list` with a single entry. +#' * **`to_tune()`**: The given [`ParamSet`] is used to tune over a single dimension. This is useful for cases +#' where a single evaluation-time parameter value (e.g. [`p_uty()`]) is constructed from multiple tuner-visible +#' parameters (which may not be [`p_uty()`]). If not one-dimensional, the supplied [`ParamSet`] should always contain a `$extra_trafo` function, +#' which must then always return a `list` with a single entry. #' #' The `TuneToken` object's internals are subject to change and should not be relied upon. `TuneToken` objects should #' only be constructed via `to_tune()`, and should only be used by giving them to `$values` of a [`ParamSet`]. #' @param ... if given, restricts the range to be tuning over, as described above. #' @return A `TuneToken` object. #' @examples -#' params = ParamSet$new(list( -#' ParamInt$new("int", 0, 10), -#' ParamInt$new("int_unbounded"), -#' ParamDbl$new("dbl", 0, 10), -#' ParamDbl$new("dbl_unbounded"), -#' ParamDbl$new("dbl_bounded_below", lower = 1), -#' ParamFct$new("fct", c("a", "b", "c")), -#' ParamUty$new("uty1"), -#' ParamUty$new("uty2"), -#' ParamUty$new("uty3"), -#' ParamUty$new("uty4"), -#' ParamUty$new("uty5") -#' )) +#' params = ps( +#' int = p_int(0, 10), +#' int_unbounded = p_int(), +#' dbl = p_dbl(0, 10), +#' dbl_unbounded = p_dbl(), +#' dbl_bounded_below = p_dbl(lower = 1), +#' fct = p_fct(c("a", "b", "c")), +#' uty1 = p_uty(), +#' uty2 = p_uty(), +#' uty3 = p_uty(), +#' uty4 = p_uty(), +#' uty5 = p_uty() +#' ) #' #' params$values = list( #' @@ -116,7 +115,7 @@ #' print(params$search_space()) #' #' # Notice how `logscale` applies `log()` to lower and upper bound pre-trafo: -#' params = ParamSet$new(list(ParamDbl$new("x"))) +#' params = ps(x = p_dbl()) #' #' params$values$x = to_tune(1, 100, logscale = TRUE) #' @@ -203,7 +202,7 @@ print.ObjectTuneToken = function(x, ...) { } # tunetoken_to_ps: Convert a `TuneToken` to a `ParamSet` that tunes over this. -# Needs the corresponding `Param` to which the `TuneToken` refers, both to +# Needs the corresponding `Domain` to which the `TuneToken` refers, both to # get the range (e.g. if `to_tune()` was used) and to verify that the `TuneToken` # does not go out of range. # @@ -254,13 +253,12 @@ tunetoken_to_ps.ObjectTuneToken = function(tt, param) { pslike_to_ps(tt$content, tt$call, param) } -# Convert something that is `ParamSet`-like (ParamSet, Param, or Domain) to a `ParamSet`. -# * content is ParamSet --> verify that it is compatible with given `Param` -# * content is Param --> Wrap in ParamSet +# Convert something that is `ParamSet`-like (ParamSet or Domain) to a `ParamSet`. +# * content is ParamSet --> verify that it is compatible with given `Domain` # * content is Domain --> Wrap in ParamSet, using ps() # @param pslike: thing to convert # @param call: to_tune()-call, for better debug message -# @param param: `Param`, that the `pslike` refers to, and therefore needs to be compatible to +# @param param: `Domain`, that the `pslike` refers to, and therefore needs to be compatible to # @param usersupplied: whether the `pslike` is supplied by the user (and should therefore be checked more thoroughly) # This is currently used for user-supplied ParamSets, for which the trafo must be adjusted. pslike_to_ps = function(pslike, call, param, usersupplied = TRUE) { diff --git a/man-roxygen/field_constraint.R b/man-roxygen/field_constraint.R new file mode 100644 index 00000000..6eb19870 --- /dev/null +++ b/man-roxygen/field_constraint.R @@ -0,0 +1,4 @@ +#' @field constraint (`function(x)`)\cr +#' Constraint function. Settable. +#' This function must evaluate a named `list()` of values and determine whether it satisfies +#' constraints, returning a scalar `logical(1)` value. diff --git a/man-roxygen/field_extra_trafo.R b/man-roxygen/field_extra_trafo.R new file mode 100644 index 00000000..13adc367 --- /dev/null +++ b/man-roxygen/field_extra_trafo.R @@ -0,0 +1,10 @@ +#' @field extra_trafo (`function(x, param_set)`)\cr +#' Transformation function. Settable. +#' User has to pass a `function(x)`, of the form\cr +#' (named `list()`, [ParamSet]) -> named `list()`.\cr +#' The function is responsible to transform a feasible configuration into another encoding, +#' before potentially evaluating the configuration with the target algorithm. +#' For the output, not many things have to hold. +#' It needs to have unique names, and the target algorithm has to accept the configuration. +#' For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). +#' Is NULL by default, and you can set it to NULL to switch the transformation off. diff --git a/man-roxygen/field_params.R b/man-roxygen/field_params.R index 1e95c510..062b43cb 100644 --- a/man-roxygen/field_params.R +++ b/man-roxygen/field_params.R @@ -1,2 +1,4 @@ #' @field params (named `list()`)\cr -#' List of [Param], named with their respective ID. +#' `data.table` representing the combined [`Domain`] objects used to construct the [`ParamSet`]. +#' Used for internal purpuses. +#' Its use by external code is deprecated. diff --git a/man-roxygen/param_param.R b/man-roxygen/param_param.R index c5add0b7..287fe08d 100644 --- a/man-roxygen/param_param.R +++ b/man-roxygen/param_param.R @@ -1,2 +1,3 @@ -#' @param param ([Param])\cr +#' @param param ([`ParamSet`])\cr #' Domain / support of the distribution we want to sample from. +#' Must be one-dimensional. From abba1e97405bc50fb9870275eae9d66e3af6f496 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 14:45:18 +0100 Subject: [PATCH 55/64] small correction in doc --- R/ParamSetCollection.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/R/ParamSetCollection.R b/R/ParamSetCollection.R index 4048e322..2d604245 100644 --- a/R/ParamSetCollection.R +++ b/R/ParamSetCollection.R @@ -88,6 +88,10 @@ ParamSetCollection = R6Class("ParamSetCollection", inherit = ParamSet, #' @param p ([ParamSet]). #' @param n (`character(1)`)\cr #' Name to use. Default `""`. + #' @param tag_sets (`logical(1)`)\cr + #' Whether to add tags of the form `"set_"` to the newly added parameters. + #' @param tag_params (`logical(1)`)\cr + #' Whether to add tags of the form `"param_"` to each parameter with original ID ``. add = function(p, n = "", tag_sets = FALSE, tag_params = FALSE) { assert_r6(p, "ParamSet") if (n != "" && n %in% names(private$.sets)) { From edd22fc7f31e908df592200463a8a710ee49fbb0 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 14:45:26 +0100 Subject: [PATCH 56/64] document --- man/ParamSet.Rd | 246 ++++++++++++++++++++++++++-------- man/ParamSetCollection.Rd | 60 ++++++--- man/Sampler.Rd | 9 +- man/Sampler1D.Rd | 11 +- man/Sampler1DCateg.Rd | 5 +- man/Sampler1DNormal.Rd | 5 +- man/Sampler1DRfun.Rd | 5 +- man/Sampler1DUnif.Rd | 5 +- man/SamplerHierarchical.Rd | 9 +- man/SamplerJointIndep.Rd | 4 +- man/SamplerUnif.Rd | 5 +- man/assert_param_set.Rd | 6 +- man/generate_design_grid.Rd | 14 +- man/generate_design_lhs.Rd | 12 +- man/generate_design_random.Rd | 14 +- man/generate_design_sobol.Rd | 12 +- man/ps.Rd | 7 +- man/ps_replicate.Rd | 71 ++++++++++ man/ps_union.Rd | 52 +++++-- man/to_tune.Rd | 39 +++--- 20 files changed, 427 insertions(+), 164 deletions(-) create mode 100644 man/ps_replicate.Rd diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 250aedda..2aaadda6 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -4,26 +4,35 @@ \alias{ParamSet} \title{ParamSet} \description{ -A set of \link{Param} objects. -Please note that when creating a set or adding to it, the parameters of the -resulting set have to be uniquely named with IDs with valid R names. -The set also contains a member variable \code{values} which can be used to store an active configuration / -or to partially fix -some parameters to constant values (regarding subsequent sampling or generation of designs). +An object representing the space of possible parametrizations of a function or another object. +\code{ParamSet}s are used on the side of objects being parameterized, where they function as a configuration space determining the set of possible configurations accepted by these objects. +They can also be used to specify search spaces for optimization, indicating the set of legal configurations to try out. +It is often convenient to generate search spaces from configuration spaces, which can be done using the \verb{$search_space()} method in combination with \code{to_tune()} / \code{\link{TuneToken}} objects. + +Individual dimensions of a \code{ParamSet} are specified by \code{\link{Domain}} objects, created as \code{\link[=p_dbl]{p_dbl()}}, \code{\link[=p_lgl]{p_lgl()}} etc. +The field \verb{$values} can be used to store an active configuration or to partially fix +some parameters to constant values -- the precise effect can be determined by the object being parameterized. + +Constructing a \code{ParamSet} can be done using \code{ParamSet$new()} in combination with a named list of \code{\link{Domain}} objects. +This route is recommended when the set of dimensions (i.e. the members of this named list) is dynamically created, such as when the number of parameters is variable. +\code{ParamSet}s can also be created using the \code{\link[=ps]{ps()}} shorthand, which is the recommended way when the set of parameters is fixed. +In practice, the majority of cases where a \code{ParamSet} is created, the \code{\link[=ps]{ps()}} should be used. } \section{S3 methods and type converters}{ \itemize{ \item \code{as.data.table()}\cr -\link{ParamSet} -> \code{\link[data.table:data.table]{data.table::data.table()}}\cr +\code{ParamSet} -> \code{\link[data.table:data.table]{data.table::data.table()}}\cr Compact representation as datatable. Col types are:\cr \itemize{ \item id: character -\item lower, upper: double +\item class: character +\item lower, upper: numeric \item levels: list col, with NULL elements -\item special_vals: list col of list +\item nlevels: integer valued numeric \item is_bounded: logical -\item default: list col, with NULL elements +\item special_vals: list col of list +\item default: list col \item storage_type: character \item tags: list col of character vectors } @@ -31,21 +40,21 @@ Compact representation as datatable. Col types are:\cr } \examples{ -ps = ParamSet$new( +pset = ParamSet$new( params = list( - ParamDbl$new("d", lower = -5, upper = 5, default = 0), - ParamFct$new("f", levels = letters[1:3]) + d = p_dbl(lower = -5, upper = 5, default = 0, trafo = function(x) 2^x), + f = p_fct(levels = letters[1:3]) ) ) -ps$trafo = function(x, param_set) { - x$d = 2^x$d - return(x) -} - -ps$add(ParamInt$new("i", lower = 0L, upper = 16L)) +# alternative, recommended way of construction in this case since the +# parameter list is not dynamic: +pset = ps( + d = p_dbl(lower = -5, upper = 5, default = 0, trafo = function(x) 2^x), + f = p_fct(levels = letters[1:3]) +) -ps$check(list(d = 2.1, f = "a", i = 3L)) +ps$check(list(d = 2.1, f = "a")) } \section{Public fields}{ \if{html}{\out{
    }} @@ -72,7 +81,9 @@ Can be used to group and subset parameters. Named with parameter IDs.} \item{\code{params}}{(named \code{list()})\cr -List of \link{Param}, named with their respective ID.} +\code{data.table} representing the combined \code{\link{Domain}} objects used to construct the \code{\link{ParamSet}}. +Used for internal purpuses. +Its use by external code is deprecated.} \item{\code{domains}}{(named \code{list} of \code{\link{Domain}}) List of \code{\link{Domain}} objects that could be used to initialize this \code{ParamSet}.} @@ -88,6 +99,11 @@ It needs to have unique names, and the target algorithm has to accept the config For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). Is NULL by default, and you can set it to NULL to switch the transformation off.} +\item{\code{constraint}}{(\verb{function(x)})\cr +Constraint function. Settable. +This function must evaluate a named \code{list()} of values and determine whether it satisfies +constraints, returning a scalar \code{logical(1)} value.} + \item{\code{deps}}{(\code{\link[data.table:data.table]{data.table::data.table()}})\cr Table has cols \code{id} (\code{character(1)}) and \code{on} (\code{character(1)}) and \code{cond} (\link{Condition}). Lists all (direct) dependency parents of a param, through parameter IDs. @@ -187,21 +203,14 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{params}}{(\code{list()})\cr -List of \link{Param}, named with their respective ID. -\code{params} are cloned on-demand, so the input does not need -to be cloned.} - -\item{\code{set_id}}{(\code{character(1)})\cr -\verb{$set_id} of the resulting \code{ParamSet}. This determines the -prefix inside \code{\link{ParamSetCollection}}. Default \code{""} (no prefix).} - -\item{\code{ignore_ids}}{(\code{logical(1)})\cr -Ignore \verb{$id} slots of \code{params} and instead use the names instead. -When this is \code{TRUE}, then \code{params} must be named. -Thisdo can be used to create a \code{ParamSet} with certain \code{\link{Param}} \code{id}s -without having to clone said \code{\link{Param}}s. -Default \code{FALSE}.} +\item{\code{params}}{(named \code{list()})\cr +List of \code{\link{Domain}}, named with their respective ID.} + +\item{\code{allow_dangling_dependencies}}{(\code{character(1)})\cr +Whether dependencies depending on parameters that are not present should be allowed. A parameter \code{x} having +\code{depends = y == 0} if \code{y} is not present would usually throw an error, but if dangling +dependencies are allowed, the dependency is added regardless. This is mainly for internal +use.} } \if{html}{\out{
    }} } @@ -220,9 +229,16 @@ Only returns IDs of parameters that satisfy all conditions. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{class}}{(\code{character()}).} +\item{\code{class}}{(\code{character()})\cr +Typically a subset of \code{"ParamDbl"}, \code{"ParamInt"}, \code{"ParamFct"}, \code{"ParamLgl"}, \code{"ParamUty"}. +Other classes are possible if implemented by 3rd party packages. +Return only IDs of dimensions with the given class.} -\item{\code{tags}}{(\code{character()}).} +\item{\code{tags}}{(\code{character()}). +Return only IDs of dimensions that have \emph{all} tags given in this argument.} + +\item{\code{any_tags}}{(\code{character()}). +Return only IDs of dimensions that have at least one of the tags given in this argument.} } \if{html}{\out{
    }} } @@ -251,17 +267,20 @@ Only returns values of parameters that satisfy all conditions. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{class}}{(\code{character()}).} +\item{\code{class}}{(\code{character()}). See \verb{$ids()}.} + +\item{\code{tags}}{(\code{character()}). See \verb{$ids()}.} -\item{\code{tags}}{(\code{character()}).} +\item{\code{any_tags}}{(\code{character()}). See \verb{$ids()}.} \item{\code{type}}{(\code{character(1)})\cr -Return values \code{with_token}, \code{without_token} or \code{only_token}?} +Return values \code{"with_token"} (i.e. all values),} \item{\code{check_required}}{(\code{logical(1)})\cr Check if all required parameters are set?} -\item{\code{is_bounded}}{(\code{logical(1)}).} +\item{\code{remove_dependencies}}{(\code{logical(1)})\cr +If \code{TRUE}, set values with dependencies that are not fulfilled to \code{NULL}.} } \if{html}{\out{
    }} } @@ -299,28 +318,80 @@ replace all values. Default is TRUE.} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-trafo}{}}} \subsection{Method \code{trafo()}}{ +Perform transformation specified by the \code{trafo} of \code{\link{Domain}} objects, as well as the \verb{$extra_trafo} field. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$trafo(x, param_set = self)}\if{html}{\out{
    }} } +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{x}}{(named \code{list()} | \code{data.frame})\cr +The value(s) to be transformed.} + +\item{\code{param_set}}{(\code{ParamSet})\cr +Passed to \code{extra_trafo()}. Note that the \code{extra_trafo} of \code{self} is used, not the \code{extra_trafo} of the +\code{ParamSet} given in the \code{param_set} argument. +In almost all cases, the default \code{param_set = self} should be used.} +} +\if{html}{\out{
    }} +} } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-test_constraint}{}}} \subsection{Method \code{test_constraint()}}{ +\pkg{checkmate}-like test-function. Takes a named list. +Return \code{FALSE} if the given \verb{$constraint} is not satisfied, \code{TRUE} otherwise. +Note this is different from satisfying the bounds or types given by the \code{ParamSet} itself: +If \code{x} does not satisfy these, an error will be thrown, given that \code{assert_value} is \code{TRUE}. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$test_constraint(x, assert_value = TRUE)}\if{html}{\out{
    }} } +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{x}}{(named \code{list()})\cr +The value to test.} + +\item{\code{assert_value}}{(\code{logical(1)})\cr +Whether to verify that \code{x} satisfies the bounds and types given by this \code{ParamSet}. +Should be \code{TRUE} unless this was already checked before.} +} +\if{html}{\out{
    }} +} +\subsection{Returns}{ +\code{logical(1)}: Whether \code{x} satisfies the \verb{$constraint}. +} } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-test_constraint_dt}{}}} \subsection{Method \code{test_constraint_dt()}}{ +\pkg{checkmate}-like test-function. Takes a \code{\link{data.table}}. +For each row, return \code{FALSE} if the given \verb{$constraint} is not satisfied, \code{TRUE} otherwise. +Note this is different from satisfying the bounds or types given by the \code{ParamSet} itself: +If \code{x} does not satisfy these, an error will be thrown, given that \code{assert_value} is \code{TRUE}. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$test_constraint_dt(x, assert_value = TRUE)}\if{html}{\out{
    }} } +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{x}}{(\code{data.table})\cr +The values to test.} + +\item{\code{assert_value}}{(\code{logical(1)})\cr +Whether to verify that \code{x} satisfies the bounds and types given by this \code{ParamSet}. +Should be \code{TRUE} unless this was already checked before.} +} +\if{html}{\out{
    }} +} +\subsection{Returns}{ +\code{logical}: For each row in \code{x}, whether it satisfies the \verb{$constraint}. +} } \if{html}{\out{
    }} \if{html}{\out{}} @@ -330,29 +401,45 @@ replace all values. Default is TRUE.} A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. +Constraints and dependencies are not checked when \code{check_strict} is \code{FALSE}. \subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{ParamSet$check(xs, check_strict = FALSE)}\if{html}{\out{
    }} +\if{html}{\out{
    }}\preformatted{ParamSet$check(xs, check_strict = TRUE)}\if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{xs}}{(named \code{list()}).} + +\item{\code{check_strict}}{(\code{logical(1)})\cr +Whether to check that constraints and dependencies are satisfied.} } \if{html}{\out{
    }} } \subsection{Returns}{ -If successful \code{TRUE}, if not a string with the error message. +If successful \code{TRUE}, if not a string with an error message. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-check_dependencies}{}}} \subsection{Method \code{check_dependencies()}}{ +\pkg{checkmate}-like check-function. Takes a named list. +Checks that all individual param dependencies are satisfied. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$check_dependencies(xs)}\if{html}{\out{
    }} } +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{xs}}{(named \code{list()}).} +} +\if{html}{\out{
    }} +} +\subsection{Returns}{ +If successful \code{TRUE}, if not a string with an error message. +} } \if{html}{\out{
    }} \if{html}{\out{}} @@ -362,14 +449,18 @@ If successful \code{TRUE}, if not a string with the error message. A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. +Constraints and dependencies are not checked when \code{check_strict} is \code{FALSE}. \subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{ParamSet$test(xs, check_strict = FALSE)}\if{html}{\out{
    }} +\if{html}{\out{
    }}\preformatted{ParamSet$test(xs, check_strict = TRUE)}\if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{xs}}{(named \code{list()}).} + +\item{\code{check_strict}}{(\code{logical(1)})\cr +Whether to check that constraints and dependencies are satisfied.} } \if{html}{\out{
    }} } @@ -385,8 +476,9 @@ If successful \code{TRUE}, if not \code{FALSE}. A point x is feasible, if it configures a subset of params, all individual param constraints are satisfied and all dependencies are satisfied. Params for which dependencies are not satisfied should not be part of \code{x}. +Constraints and dependencies are not checked when \code{check_strict} is \code{FALSE}. \subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{ParamSet$assert(xs, check_strict = FALSE, .var.name = vname(xs))}\if{html}{\out{
    }} +\if{html}{\out{
    }}\preformatted{ParamSet$assert(xs, check_strict = TRUE, .var.name = vname(xs))}\if{html}{\out{
    }} } \subsection{Arguments}{ @@ -394,6 +486,9 @@ Params for which dependencies are not satisfied should not be part of \code{x}. \describe{ \item{\code{xs}}{(named \code{list()}).} +\item{\code{check_strict}}{(\code{logical(1)})\cr +Whether to check that constraints and dependencies are satisfied.} + \item{\code{.var.name}}{(\code{character(1)})\cr Name of the checked object to print in error messages.\cr Defaults to the heuristic implemented in \link[checkmate:vname]{vname}.} @@ -409,18 +504,22 @@ If successful \code{xs} invisibly, if not an error message. \if{latex}{\out{\hypertarget{method-ParamSet-check_dt}{}}} \subsection{Method \code{check_dt()}}{ \pkg{checkmate}-like check-function. Takes a \link[data.table:data.table]{data.table::data.table} -where rows are points and columns are parameters. A point x is feasible, -if it configures a subset of params, all individual param constraints are -satisfied and all dependencies are satisfied. Params for which -dependencies are not satisfied should be set to \code{NA} in \code{xdt}. +where rows are points and columns are parameters. +A point x is feasible, if it configures a subset of params, +all individual param constraints are satisfied and all dependencies are satisfied. +Params for which dependencies are not satisfied should not be part of \code{x}. +Constraints and dependencies are not checked when \code{check_strict} is \code{FALSE}. \subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{ParamSet$check_dt(xdt, check_strict = FALSE)}\if{html}{\out{
    }} +\if{html}{\out{
    }}\preformatted{ParamSet$check_dt(xdt, check_strict = TRUE)}\if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{xdt}}{(\link[data.table:data.table]{data.table::data.table} | \code{data.frame()}).} + +\item{\code{check_strict}}{(\code{logical(1)})\cr +Whether to check that constraints and dependencies are satisfied.} } \if{html}{\out{
    }} } @@ -434,13 +533,16 @@ If successful \code{TRUE}, if not a string with the error message. \subsection{Method \code{test_dt()}}{ \pkg{checkmate}-like test-function (s. \verb{$check_dt()}). \subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{ParamSet$test_dt(xdt)}\if{html}{\out{
    }} +\if{html}{\out{
    }}\preformatted{ParamSet$test_dt(xdt, check_strict = TRUE)}\if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{xdt}}{(\link[data.table:data.table]{data.table::data.table}).} + +\item{\code{check_strict}}{(\code{logical(1)})\cr +Whether to check that constraints and dependencies are satisfied.} } \if{html}{\out{
    }} } @@ -454,7 +556,7 @@ If successful \code{TRUE}, if not \code{FALSE}. \subsection{Method \code{assert_dt()}}{ \pkg{checkmate}-like assert-function (s. \verb{$check_dt()}). \subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{ParamSet$assert_dt(xdt, check_strict = FALSE, .var.name = vname(xdt))}\if{html}{\out{
    }} +\if{html}{\out{
    }}\preformatted{ParamSet$assert_dt(xdt, check_strict = TRUE, .var.name = vname(xdt))}\if{html}{\out{
    }} } \subsection{Arguments}{ @@ -462,6 +564,9 @@ If successful \code{TRUE}, if not \code{FALSE}. \describe{ \item{\code{xdt}}{(\link[data.table:data.table]{data.table::data.table}).} +\item{\code{check_strict}}{(\code{logical(1)})\cr +Whether to check that constraints and dependencies are satisfied.} + \item{\code{.var.name}}{(\code{character(1)})\cr Name of the checked object to print in error messages.\cr Defaults to the heuristic implemented in \link[checkmate:vname]{vname}.} @@ -469,17 +574,29 @@ Defaults to the heuristic implemented in \link[checkmate:vname]{vname}.} \if{html}{\out{
    }} } \subsection{Returns}{ -If successful \code{xs} invisibly, if not an error message. +If successful \code{xs} invisibly, if not, an error is generated. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-qunif}{}}} \subsection{Method \code{qunif()}}{ +Map a \code{matrix} or \code{data.frame} of values between 0 and 1 to proportional values inside the feasible intervals of individual parameters. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$qunif(x)}\if{html}{\out{
    }} } +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{x}}{(\code{matrix} | \code{data.frame})\cr +Values to map. Column names must be a subset of the names of parameters.} +} +\if{html}{\out{
    }} +} +\subsection{Returns}{ +\code{data.table}. +} } \if{html}{\out{
    }} \if{html}{\out{}} @@ -518,6 +635,13 @@ Create a new \code{ParamSet} restricted to the passed IDs. \if{html}{\out{
    }} \describe{ \item{\code{ids}}{(\code{character()}).} + +\item{\code{allow_dangling_dependencies}}{(\code{logical(1)})\cr +Whether to allow subsets that cut across parameter dependencies. +Dependencies that point to dropped parameters are kept (but will be "dangling", i.e. their \code{"on"} will not be present).} + +\item{\code{keep_constraint}}{(\code{logical(1)})\cr +Whether to keep the \verb{$constraint} function.} } \if{html}{\out{
    }} } @@ -529,17 +653,29 @@ Create a new \code{ParamSet} restricted to the passed IDs. \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-subspaces}{}}} \subsection{Method \code{subspaces()}}{ +Create new one-dimensional \code{ParamSet}s for each dimension. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$subspaces(ids = private$.params$id)}\if{html}{\out{
    }} } +\subsection{Arguments}{ +\if{html}{\out{
    }} +\describe{ +\item{\code{ids}}{(\code{character()})\cr +IDs for which to create \code{ParamSet}s. Defaults to all IDs.} +} +\if{html}{\out{
    }} +} +\subsection{Returns}{ +named \code{list()} of \code{ParamSet}. +} } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSet-flatten}{}}} \subsection{Method \code{flatten()}}{ Create a \code{ParamSet} from this object, even if this object itself is not -a \code{ParamSet}. +a \code{ParamSet} but e.g. a \code{\link{ParamSetCollection}}. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSet$flatten()}\if{html}{\out{
    }} } diff --git a/man/ParamSetCollection.Rd b/man/ParamSetCollection.Rd index 76bbc369..93e4c93e 100644 --- a/man/ParamSetCollection.Rd +++ b/man/ParamSetCollection.Rd @@ -4,15 +4,16 @@ \alias{ParamSetCollection} \title{ParamSetCollection} \description{ -A collection of multiple \link{ParamSet} objects. +A collection of multiple \code{\link{ParamSet}} objects. \itemize{ \item The collection is basically a light-weight wrapper / container around references to multiple sets. \item In order to ensure unique param names, every param in the collection is referred to with -".". Parameters from ParamSets with empty (i.e. \code{""}) \verb{$set_id} are referenced -directly. Multiple ParamSets with \verb{$set_id} \code{""} can be combined, but their parameter names -must be unique. +".", where \verb{} is the name of the entry a given \code{\link{ParamSet}} in the named list given during construction. +Parameters from \code{\link{ParamSet}} with empty (i.e. \code{""}) \code{set_id} are referenced +directly. Multiple \code{\link{ParamSet}}s with \code{set_id} \code{""} can be combined, but their parameter names +may not overlap to avoid name clashes. \item When you either ask for 'values' or set them, the operation is delegated to the individual, -contained param set references. The collection itself does not maintain a \code{values} state. +contained \code{\link{ParamSet}} references. The collection itself does not maintain a \code{values} state. This also implies that if you directly change \code{values} in one of the referenced sets, this change is reflected in the collection. \item Dependencies: It is possible to currently handle dependencies @@ -43,6 +44,26 @@ Currently set / fixed parameter values. Settable, and feasibility of values will be checked when you set them. You do not have to set values for all parameters, but only for a subset. When you set values, all previously set values will be unset / removed.} + +\item{\code{extra_trafo}}{(\verb{function(x, param_set)})\cr +Transformation function. Settable. +User has to pass a \verb{function(x)}, of the form\cr +(named \code{list()}, \link{ParamSet}) -> named \code{list()}.\cr +The function is responsible to transform a feasible configuration into another encoding, +before potentially evaluating the configuration with the target algorithm. +For the output, not many things have to hold. +It needs to have unique names, and the target algorithm has to accept the configuration. +For convenience, the self-paramset is also passed in, if you need some info from it (e.g. tags). +Is NULL by default, and you can set it to NULL to switch the transformation off.} + +\item{\code{constraint}}{(\verb{function(x)})\cr +Constraint function. Settable. +This function must evaluate a named \code{list()} of values and determine whether it satisfies +constraints, returning a scalar \code{logical(1)} value.} + +\item{\code{sets}}{(named \code{list()})\cr +Read-only \code{list} of of \code{\link{ParamSet}}s contained in this \code{ParamSetCollection}. +This field provides direct references to the \code{\link{ParamSet}} objects.} } \if{html}{\out{
    }} } @@ -94,22 +115,15 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{sets}}{(\code{list()} of \link{ParamSet})\cr -ParamSet objects are not cloned.} +\item{\code{sets}}{(named \code{list()} of \link{ParamSet})\cr +ParamSet objects are not cloned. +Names are used as "set_id" for the naming scheme of delegated parameters.} -\item{\code{set_id}}{(\code{character(1)})\cr -\verb{$set_id} of the resulting \code{ParamSet}. This determines the -prefix inside \code{\link{ParamSetCollection}}. Default \code{""} (no prefix).} +\item{\code{tag_sets}}{(\code{logical(1)})\cr +Whether to add tags of the form \code{"set_"} to each parameter originating from a given \code{ParamSet} given with name \verb{}.} -\item{\code{ignore_ids}}{(\code{logical(1)})\cr -Ignore \verb{$set_id} slots of \code{sets} and instead use the names instead. -When this is \code{TRUE}, then \code{sets} should be named when id prefixes are desired. -This can be used to create a \code{ParamSetCollection} with changed \code{\link{ParamSet}} \code{set_id}s -without having to clone said \code{\link{ParamSet}}s. However, when underlying \code{\link{ParamSet}}s' -\verb{$set_id}s change, then this change is no longer reflected in the ParamSetCollection. -Also note that the \verb{$values} will have undefined behavior when \code{sets} contains a -single \code{\link{ParamSet}} multiple times (by reference). -Default \code{FALSE}.} +\item{\code{tag_params}}{(\code{logical(1)})\cr +Whether to add tags of the form \code{"param_"} to each parameter with original ID \verb{}.} } \if{html}{\out{
    }} } @@ -118,7 +132,7 @@ Default \code{FALSE}.} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ParamSetCollection-add}{}}} \subsection{Method \code{add()}}{ -Adds a set to this collection. +Adds a \code{\link{ParamSet}} to this collection. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{ParamSetCollection$add(p, n = "", tag_sets = FALSE, tag_params = FALSE)}\if{html}{\out{
    }} } @@ -130,6 +144,12 @@ Adds a set to this collection. \item{\code{n}}{(\code{character(1)})\cr Name to use. Default \code{""}.} + +\item{\code{tag_sets}}{(\code{logical(1)})\cr +Whether to add tags of the form \code{"set_"} to the newly added parameters.} + +\item{\code{tag_params}}{(\code{logical(1)})\cr +Whether to add tags of the form \code{"param_"} to each parameter with original ID \verb{}.} } \if{html}{\out{
    }} } diff --git a/man/Sampler.Rd b/man/Sampler.Rd index 5bf0185b..f66fc8d4 100644 --- a/man/Sampler.Rd +++ b/man/Sampler.Rd @@ -21,7 +21,7 @@ Other Sampler: \section{Public fields}{ \if{html}{\out{
    }} \describe{ -\item{\code{param_set}}{(\link{ParamSet})\cr +\item{\code{param_set}}{(\code{\link{ParamSet}})\cr Domain / support of the distribution we want to sample from.} } \if{html}{\out{
    }} @@ -51,9 +51,8 @@ e.g., \link{Sampler1D}. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param_set}}{(\link{ParamSet})\cr -Domain / support of the distribution we want to sample from. -ParamSet is cloned on construction.} +\item{\code{param_set}}{(\code{\link{ParamSet}})\cr +The \code{\link{ParamSet}} to associated with this \code{Sampler}.} } \if{html}{\out{
    }} } @@ -75,7 +74,7 @@ Sample \code{n} values from the distribution. \if{html}{\out{
    }} } \subsection{Returns}{ -\link{Design}. +\code{\link{Design}}. } } \if{html}{\out{
    }} diff --git a/man/Sampler1D.Rd b/man/Sampler1D.Rd index 12cf81b3..b2e6a4d2 100644 --- a/man/Sampler1D.Rd +++ b/man/Sampler1D.Rd @@ -25,8 +25,8 @@ Other Sampler: \section{Active bindings}{ \if{html}{\out{
    }} \describe{ -\item{\code{param}}{(\link{Param})\cr -Returns the one Parameter that is sampled from.} +\item{\code{param}}{(\code{\link{ParamSet}})\cr +Returns the one-dimensional \code{\link{ParamSet}} that is sampled from.} } \if{html}{\out{
    }} } @@ -53,7 +53,7 @@ Returns the one Parameter that is sampled from.} Creates a new instance of this \link[R6:R6Class]{R6} class. Note that this object is typically constructed via derived classes, -e.g., \link{Sampler1DUnif}. +e.g., \code{\link{Sampler1DUnif}}. \subsection{Usage}{ \if{html}{\out{
    }}\preformatted{Sampler1D$new(param)}\if{html}{\out{
    }} } @@ -61,8 +61,9 @@ e.g., \link{Sampler1DUnif}. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param}}{(\link{Param})\cr -Domain / support of the distribution we want to sample from.} +\item{\code{param}}{(\code{\link{ParamSet}})\cr +Domain / support of the distribution we want to sample from. +Must be one-dimensional.} } \if{html}{\out{
    }} } diff --git a/man/Sampler1DCateg.Rd b/man/Sampler1DCateg.Rd index e8f1b966..22726357 100644 --- a/man/Sampler1DCateg.Rd +++ b/man/Sampler1DCateg.Rd @@ -57,8 +57,9 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param}}{(\link{Param})\cr -Domain / support of the distribution we want to sample from.} +\item{\code{param}}{(\code{\link{ParamSet}})\cr +Domain / support of the distribution we want to sample from. +Must be one-dimensional.} \item{\code{prob}}{(\code{numeric()} | NULL)\cr Numeric vector of \code{param$nlevels} probabilities, which is uniform by default.} diff --git a/man/Sampler1DNormal.Rd b/man/Sampler1DNormal.Rd index e4e7cfa6..25c3cf65 100644 --- a/man/Sampler1DNormal.Rd +++ b/man/Sampler1DNormal.Rd @@ -60,8 +60,9 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param}}{(\link{Param})\cr -Domain / support of the distribution we want to sample from.} +\item{\code{param}}{(\code{\link{ParamSet}})\cr +Domain / support of the distribution we want to sample from. +Must be one-dimensional.} \item{\code{mean}}{(\code{numeric(1)})\cr Mean parameter of the normal distribution. diff --git a/man/Sampler1DRfun.Rd b/man/Sampler1DRfun.Rd index 22f6a87f..dd479245 100644 --- a/man/Sampler1DRfun.Rd +++ b/man/Sampler1DRfun.Rd @@ -60,8 +60,9 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param}}{(\link{Param})\cr -Domain / support of the distribution we want to sample from.} +\item{\code{param}}{(\code{\link{ParamSet}})\cr +Domain / support of the distribution we want to sample from. +Must be one-dimensional.} \item{\code{rfun}}{(\verb{function()})\cr Random number generator function, e.g. \code{rexp} to sample from exponential distribution.} diff --git a/man/Sampler1DUnif.Rd b/man/Sampler1DUnif.Rd index 09cf10f0..0254bf28 100644 --- a/man/Sampler1DUnif.Rd +++ b/man/Sampler1DUnif.Rd @@ -49,8 +49,9 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param}}{(\link{Param})\cr -Domain / support of the distribution we want to sample from.} +\item{\code{param}}{(\code{\link{ParamSet}})\cr +Domain / support of the distribution we want to sample from. +Must be one-dimensional.} } \if{html}{\out{
    }} } diff --git a/man/SamplerHierarchical.Rd b/man/SamplerHierarchical.Rd index 6f221be7..60b6c4d5 100644 --- a/man/SamplerHierarchical.Rd +++ b/man/SamplerHierarchical.Rd @@ -27,7 +27,7 @@ Other Sampler: \if{html}{\out{
    }} \describe{ \item{\code{samplers}}{(\code{list()})\cr -List of \link{Sampler1D} objects that gives a Sampler for each \link{Param} in the \code{param_set}.} +List of \code{\link{Sampler1D}} objects that gives a Sampler for each dimension in the \code{param_set}.} } \if{html}{\out{
    }} } @@ -59,12 +59,11 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param_set}}{(\link{ParamSet})\cr -Domain / support of the distribution we want to sample from. -ParamSet is cloned on construction.} +\item{\code{param_set}}{(\code{\link{ParamSet}})\cr +The \code{\link{ParamSet}} to associated with this \code{SamplerHierarchical}.} \item{\code{samplers}}{(\code{list()})\cr -List of \link{Sampler1D} objects that gives a Sampler for each \link{Param} in the \code{param_set}.} +List of \code{\link{Sampler1D}} objects that gives a Sampler for each \link{Param} in the \code{param_set}.} } \if{html}{\out{
    }} } diff --git a/man/SamplerJointIndep.Rd b/man/SamplerJointIndep.Rd index 25477667..ee4a3349 100644 --- a/man/SamplerJointIndep.Rd +++ b/man/SamplerJointIndep.Rd @@ -25,7 +25,7 @@ Other Sampler: \if{html}{\out{
    }} \describe{ \item{\code{samplers}}{(\code{list()})\cr -List of \link{Sampler} objects.} +List of \code{\link{Sampler}} objects.} } \if{html}{\out{
    }} } @@ -58,7 +58,7 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \if{html}{\out{
    }} \describe{ \item{\code{samplers}}{(\code{list()})\cr -List of \link{Sampler} objects.} +List of \code{\link{Sampler}} objects.} } \if{html}{\out{
    }} } diff --git a/man/SamplerUnif.Rd b/man/SamplerUnif.Rd index a405c822..0ac607ad 100644 --- a/man/SamplerUnif.Rd +++ b/man/SamplerUnif.Rd @@ -51,9 +51,8 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ -\item{\code{param_set}}{(\link{ParamSet})\cr -Domain / support of the distribution we want to sample from. -ParamSet is cloned on construction.} +\item{\code{param_set}}{(\code{\link{ParamSet}})\cr +The \code{\link{ParamSet}} to associated with this \code{SamplerUnif}.} } \if{html}{\out{
    }} } diff --git a/man/assert_param_set.Rd b/man/assert_param_set.Rd index 7fc13982..ce12917d 100644 --- a/man/assert_param_set.Rd +++ b/man/assert_param_set.Rd @@ -13,16 +13,16 @@ assert_param_set( ) } \arguments{ -\item{param_set}{(\link{ParamSet}).} +\item{param_set}{(\code{\link{ParamSet}}).} \item{cl}{(\code{character()})\cr Allowed subclasses.} \item{no_untyped}{(\code{logical(1)})\cr -Are untyped \link{Param}s allowed?} +Are untyped \code{\link{Domain}}s allowed?} \item{must_bounded}{(\code{logical(1)})\cr -Only bounded \link{Param}s allowed?} +Only bounded \code{\link{Domain}}s allowed?} \item{no_deps}{(\code{logical(1)})\cr Are dependencies allowed?} diff --git a/man/generate_design_grid.Rd b/man/generate_design_grid.Rd index 41f25513..82ff8e5c 100644 --- a/man/generate_design_grid.Rd +++ b/man/generate_design_grid.Rd @@ -7,16 +7,16 @@ generate_design_grid(param_set, resolution = NULL, param_resolutions = NULL) } \arguments{ -\item{param_set}{(\link{ParamSet}).} +\item{param_set}{(\code{\link{ParamSet}}).} \item{resolution}{(\code{integer(1)})\cr Global resolution for all \link{Param}s.} \item{param_resolutions}{(named \code{integer()})\cr -Resolution per \link{Param}, named by parameter ID.} +Resolution per \code{\link{Domain}}, named by parameter ID.} } \value{ -\link{Design}. +\code{\link{Design}}. } \description{ Generate a grid with a specified resolution in the parameter space. @@ -25,11 +25,11 @@ always produce a grid over all their valid levels. For number params the endpoints of the params are always included in the grid. } \examples{ -ps = ParamSet$new(list( - ParamDbl$new("ratio", lower = 0, upper = 1), - ParamFct$new("letters", levels = letters[1:3]) +pset = ps( + ratio = p_dbl(lower = 0, upper = 1), + letters = p_fct(levels = letters[1:3]) )) -generate_design_grid(ps, 10) +generate_design_grid(pset, 10) } \seealso{ Other generate_design: diff --git a/man/generate_design_lhs.Rd b/man/generate_design_lhs.Rd index 836fa03d..417e6548 100644 --- a/man/generate_design_lhs.Rd +++ b/man/generate_design_lhs.Rd @@ -7,7 +7,7 @@ generate_design_lhs(param_set, n, lhs_fun = NULL) } \arguments{ -\item{param_set}{(\link{ParamSet}).} +\item{param_set}{(\code{\link{ParamSet}}).} \item{n}{(\code{integer(1)}) \cr Number of points to sample.} @@ -17,7 +17,7 @@ Function to use to generate a LHS sample, with n samples and k values per param. LHS functions are implemented in package \pkg{lhs}, default is to use \code{\link[lhs:maximinLHS]{lhs::maximinLHS()}}.} } \value{ -\link{Design}. +\code{\link{Design}}. } \description{ Generate a space-filling design using Latin hypercube sampling. Dependent @@ -25,13 +25,13 @@ parameters whose constraints are unsatisfied generate \code{NA} entries in their respective columns. } \examples{ -ps = ParamSet$new(list( - ParamDbl$new("ratio", lower = 0, upper = 1), - ParamFct$new("letters", levels = letters[1:3]) +pset = ps( + ratio = p_dbl(lower = 0, upper = 1), + letters = p_fct(levels = letters[1:3]) )) if (requireNamespace("lhs", quietly = TRUE)) { - generate_design_lhs(ps, 10) + generate_design_lhs(pset, 10) } } \seealso{ diff --git a/man/generate_design_random.Rd b/man/generate_design_random.Rd index 9c3fc7b6..aa908082 100644 --- a/man/generate_design_random.Rd +++ b/man/generate_design_random.Rd @@ -7,25 +7,25 @@ generate_design_random(param_set, n) } \arguments{ -\item{param_set}{(\link{ParamSet}).} +\item{param_set}{(\code{\link{ParamSet}}).} \item{n}{(\code{integer(1)})\cr Number of points to draw randomly.} } \value{ -\link{Design}. +\code{\link{Design}}. } \description{ Generates a design with randomly drawn points. -Internally uses \link{SamplerUnif}, hence, also works for \link{ParamSet}s with dependencies. +Internally uses \code{\link{SamplerUnif}}, hence, also works for \link{ParamSet}s with dependencies. If dependencies do not hold, values are set to \code{NA} in the resulting data.table. } \examples{ -ps = ParamSet$new(list( - ParamDbl$new("ratio", lower = 0, upper = 1), - ParamFct$new("letters", levels = letters[1:3]) +pset = ps( + ratio = p_dbl(lower = 0, upper = 1), + letters = p_fct(levels = letters[1:3]) )) -generate_design_random(ps, 10) +generate_design_random(pset, 10) } \seealso{ Other generate_design: diff --git a/man/generate_design_sobol.Rd b/man/generate_design_sobol.Rd index ce3e6bc8..dc97a0f2 100644 --- a/man/generate_design_sobol.Rd +++ b/man/generate_design_sobol.Rd @@ -7,13 +7,13 @@ generate_design_sobol(param_set, n) } \arguments{ -\item{param_set}{(\link{ParamSet}).} +\item{param_set}{(\code{\link{ParamSet}}).} \item{n}{(\code{integer(1)}) \cr Number of points to sample.} } \value{ -\link{Design}. +\code{\link{Design}}. } \description{ Generate a space-filling design using a Sobol sequence. Dependent @@ -26,13 +26,13 @@ Note that non determinism is achieved by sampling the seed argument via \code{sample(.Machine$integer.max, size = 1L)}. } \examples{ -ps = ParamSet$new(list( - ParamDbl$new("ratio", lower = 0, upper = 1), - ParamFct$new("letters", levels = letters[1:3]) +pset = ps( + ratio = p_dbl(lower = 0, upper = 1), + letters = p_fct(levels = letters[1:3]) )) if (requireNamespace("spacefillr", quietly = TRUE)) { - generate_design_sobol(ps, 10) + generate_design_sobol(pset, 10) } } \seealso{ diff --git a/man/ps.Rd b/man/ps.Rd index 3c6a3635..fdd3fe3d 100644 --- a/man/ps.Rd +++ b/man/ps.Rd @@ -12,10 +12,9 @@ ps( ) } \arguments{ -\item{...}{(\code{\link{Domain}} | \code{\link{Param}})\cr -Named arguments of \code{\link{Domain}} or \code{\link{Param}} objects. The \code{\link{ParamSet}} will be constructed of the given \code{\link{Param}}s, -or of \code{\link{Param}}s constructed from the given domains. The names of the arguments will be used as \verb{$id} -(the \verb{$id} of \code{\link{Param}} arguments are ignored).} +\item{...}{(\code{\link{Domain}})\cr +Named arguments of \code{\link{Domain}} objects. The \code{\link{ParamSet}} will be constructed of the given \code{\link{Domain}}s, +The names of the arguments will be used as \verb{$id()} in the resulting \code{\link{ParamSet}}.} \item{.extra_trafo}{(\verb{function(x, param_set)})\cr Transformation to set the resulting \code{\link{ParamSet}}'s \verb{$trafo} value to. This is in addition to any \code{trafo} of diff --git a/man/ps_replicate.Rd b/man/ps_replicate.Rd new file mode 100644 index 00000000..ef5aeb92 --- /dev/null +++ b/man/ps_replicate.Rd @@ -0,0 +1,71 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ps_replicate.R +\name{ps_replicate} +\alias{ps_replicate} +\title{Create a ParamSet by Repeating a Given ParamSet} +\usage{ +ps_replicate( + set, + times = length(prefixes), + prefixes = sprintf("rep\%s", seq_len(times)), + tag_sets = FALSE, + tag_params = FALSE +) +} +\arguments{ +\item{set}{(\code{\link{ParamSet}})\cr +\code{\link{ParamSet}} to use as template.} + +\item{times}{(\code{integer(1)})\cr +Number of times to repeat \code{set}. +Should not be given if \code{prefixes} is provided.} + +\item{prefixes}{(\code{character})\cr +A \code{character} vector indicating the prefixes to use for each repetition of \code{set}. +If this is given, \code{times} is inferred from \code{length(prefixes)} and should not be given separately. +If \code{times} is given, this defaults to \code{"repX"}, with \code{X} counting up from 1.} + +\item{tag_sets}{(\code{logical(1)})\cr +Whether to add a tag of the form \code{"set_"} to each parameter in the result, indicating the repetition each parameter belongs to.} + +\item{tag_params}{(\code{logical(1)})\cr +Whether to add a tag of the form \code{"param_"} to each parameter in the result, indicating the original parameter ID inside \code{set}.} +} +\description{ +Repeat a \code{\link{ParamSet}} a given number of times and thus create a larger \code{\link{ParamSet}}. +By default, the resulting parameters are prefixed with the string \verb{"repX.", where }X\verb{counts up from 1. It is also possible to tag parameters by their original name and by their prefix, making grouped retrieval e.g. using}$get_values()` easier. +} +\examples{ +pset = ps( + i = p_int(), + z = p_lgl() +) + +ps_replicate(pset, 3) + +ps_replicate(pset, prefixes = c("first", "last")) + +pset$values = list(i = 1, z = FALSE) + +psr = ps_replicate(pset, 2, tag_sets = TRUE, tag_params = TRUE) + +# observe the effect of tag_sets, tag_params: +psr$tags + +# note that values are repeated as well +psr$values + +psr$set_values(rep1.i = 10, rep2.z = TRUE) +psr$values + +# use `any_tags` to get subset of values. +# `any_tags = ` is preferable to `tags = `, since parameters +# could also have other tags. `tags = ` would require the +# selected params to have the given tags exclusively. + +# get all values associated with the original parameter `i` +psr$get_values(any_tags = "param_i") + +# get all values associated with the first repetition "rep1" +psr$get_values(any_tags = "set_rep1") +} diff --git a/man/ps_union.Rd b/man/ps_union.Rd index 18b808f5..732603e5 100644 --- a/man/ps_union.Rd +++ b/man/ps_union.Rd @@ -7,15 +7,51 @@ ps_union(sets, tag_sets = FALSE, tag_params = FALSE) } \arguments{ -\item{sets:}{list of ParamSet} +\item{sets}{(\code{list} of \code{\link{ParamSet}})\cr +This may be a named list, in which case non-empty names are prefixed to parameters in the corresponding \code{\link{ParamSet}}.} + +\item{tag_sets}{(\code{logical(1)})\cr +Whether to add tags of the form \code{"set_"} to each parameter originating from a given \code{ParamSet} given with name \verb{}.} + +\item{tag_params}{(\code{logical(1)})\cr +Whether to add tags of the form \code{"param_"} to each parameter with original ID \verb{}.} } \description{ -This emulates \code{ParamSetCollection$new(sets)}, except that -\itemize{ -\item The result is a \code{ParamSet}, not a \code{ParamSetCollection} -\item The ParamSets are allowed to have \verb{$trafo}, which are collected together into a single function. -This emulates \code{ParamSetCollection$new(sets)}, which in particular means that the resulting ParamSet has all the Params -from the input \code{sets}, but some \verb{$id}s are changed: If the ParamSet has a non-empty \code{set_id}, then the Params will -have their \if{html}{\out{}} changed to .\if{html}{\out{}}. This is also reflected in deps and in \verb{$trafo}. +This emulates \code{ParamSetCollection$new(sets)}, except that the result is a flat \code{\link{ParamSet}}, not a \code{\link{ParamSetCollection}}. +The resulting object is decoupled from the input \code{\link{ParamSet}} objects: Unlike \code{\link{ParamSetCollection}}, changing \verb{$values} of +the resulting object will not change the input \code{\link{ParamSet}} \verb{$values} by reference. + +This emulates \code{ParamSetCollection$new(sets)}, which in particular means that the resulting \code{\link{ParamSet}} has all the \code{\link{Domain}}s +from the input \code{sets}, but some \verb{$id}s are changed: If the \code{\link{ParamSet}} is given in \code{sets} with a name, then the \code{\link{Domain}}s will +have their \verb{} changed to \verb{.}. This is also reflected in deps. + +The \code{c()} operator, applied to \code{\link{ParamSet}}s, is a synony for \code{ps_union()}. } +\examples{ +ps1 = ps(x = p_dbl()) +ps1$values = list(x = 1) + +ps2 = ps(y = p_lgl()) + +pu = ps_union(list(ps1, ps2)) +# same as: +pu = c(ps1, ps2) + +pu + +pu$values + +pu$values$x = 2 +pu$values + +# p1 is unchanged: +ps1$values + +# Prefixes automatically created for named elements. +# This allows repeating components. +pu2 = c(one = ps1, two = ps1, ps2) +pu2 + +pu2$values + } diff --git a/man/to_tune.Rd b/man/to_tune.Rd index 8a2af329..82622528 100644 --- a/man/to_tune.Rd +++ b/man/to_tune.Rd @@ -37,37 +37,36 @@ integer (if it is a \code{\link{ParamInt}}) or real values (if it is a \code{\li it must be named to disambiguate from the following cases.\cr When \code{logscale} is \code{TRUE}, then a \code{trafo} is generated automatically that transforms to the given bounds. The bounds are log()'d pre-trafo (see examples). See the \code{logscale} argument of \code{\link{Domain}} functions for more info.\cr -Note that "logscale" is \emph{not} inherited from the \code{\link{Param}} that the \code{TuneToken} belongs to! Defining a parameter +Note that "logscale" is \emph{not} inherited from the \code{\link{Domain}} that the \code{TuneToken} belongs to! Defining a parameter with \verb{p_dbl(... logscale = TRUE)} will \emph{not} automatically give the \code{to_tune()} assigned to it log-scale. \item \strong{\code{to_tune(levels)}}: Indicates a parameter should be tuned through the given discrete values. \code{levels} can be any named or unnamed atomic vector or list (although in the unnamed case it must be possible to construct a corresponding \code{character} vector with distinct values using \code{as.character}). \item \strong{\verb{to_tune()}}: The given \code{\link{Domain}} object (constructed e.g. with \code{\link[=p_int]{p_int()}} or \code{\link[=p_fct]{p_fct()}}) indicates the range which should be tuned over. The supplied \code{trafo} function is used for parameter transformation. -\item \strong{\verb{to_tune()}}: The given \code{\link{Param}} object indicates the range which should be tuned over. -\item \strong{\verb{to_tune()}}: The given \code{\link{ParamSet}} is used to tune over a single \code{Param}. This is useful for cases -where a single evaluation-time parameter value (e.g. \code{\link{ParamUty}}) is constructed from multiple tuner-visible -parameters (which may not be \code{ParamUty}). The supplied \code{\link{ParamSet}} should always contain a \verb{$trafo} function, -which must always return a \code{list} with a single entry. +\item \strong{\verb{to_tune()}}: The given \code{\link{ParamSet}} is used to tune over a single dimension. This is useful for cases +where a single evaluation-time parameter value (e.g. \code{\link[=p_uty]{p_uty()}}) is constructed from multiple tuner-visible +parameters (which may not be \code{\link[=p_uty]{p_uty()}}). If not one-dimensional, the supplied \code{\link{ParamSet}} should always contain a \verb{$extra_trafo} function, +which must then always return a \code{list} with a single entry. } The \code{TuneToken} object's internals are subject to change and should not be relied upon. \code{TuneToken} objects should only be constructed via \code{to_tune()}, and should only be used by giving them to \verb{$values} of a \code{\link{ParamSet}}. } \examples{ -params = ParamSet$new(list( - ParamInt$new("int", 0, 10), - ParamInt$new("int_unbounded"), - ParamDbl$new("dbl", 0, 10), - ParamDbl$new("dbl_unbounded"), - ParamDbl$new("dbl_bounded_below", lower = 1), - ParamFct$new("fct", c("a", "b", "c")), - ParamUty$new("uty1"), - ParamUty$new("uty2"), - ParamUty$new("uty3"), - ParamUty$new("uty4"), - ParamUty$new("uty5") -)) +params = ps( + int = p_int(0, 10), + int_unbounded = p_int(), + dbl = p_dbl(0, 10), + dbl_unbounded = p_dbl(), + dbl_bounded_below = p_dbl(lower = 1), + fct = p_fct(c("a", "b", "c")), + uty1 = p_uty(), + uty2 = p_uty(), + uty3 = p_uty(), + uty4 = p_uty(), + uty5 = p_uty() +) params$values = list( @@ -127,7 +126,7 @@ params$values$uty2 = to_tune(c(2, 4, 8)) print(params$search_space()) # Notice how `logscale` applies `log()` to lower and upper bound pre-trafo: -params = ParamSet$new(list(ParamDbl$new("x"))) +params = ps(x = p_dbl()) params$values$x = to_tune(1, 100, logscale = TRUE) From 2567533008ec825317e46c4ac1ce8302b48075bc Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 14:47:35 +0100 Subject: [PATCH 57/64] fix in examples --- R/generate_design_grid.R | 2 +- R/generate_design_lhs.R | 2 +- R/generate_design_random.R | 2 +- R/generate_design_sobol.R | 2 +- man/generate_design_grid.Rd | 2 +- man/generate_design_lhs.Rd | 2 +- man/generate_design_random.Rd | 2 +- man/generate_design_sobol.Rd | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/R/generate_design_grid.R b/R/generate_design_grid.R index f26de66c..2c85fa04 100644 --- a/R/generate_design_grid.R +++ b/R/generate_design_grid.R @@ -19,7 +19,7 @@ #' pset = ps( #' ratio = p_dbl(lower = 0, upper = 1), #' letters = p_fct(levels = letters[1:3]) -#' )) +#' ) #' generate_design_grid(pset, 10) generate_design_grid = function(param_set, resolution = NULL, param_resolutions = NULL) { diff --git a/R/generate_design_lhs.R b/R/generate_design_lhs.R index 0bfa2375..80d65df0 100644 --- a/R/generate_design_lhs.R +++ b/R/generate_design_lhs.R @@ -19,7 +19,7 @@ #' pset = ps( #' ratio = p_dbl(lower = 0, upper = 1), #' letters = p_fct(levels = letters[1:3]) -#' )) +#' ) #' #' if (requireNamespace("lhs", quietly = TRUE)) { #' generate_design_lhs(pset, 10) diff --git a/R/generate_design_random.R b/R/generate_design_random.R index 456cd0bd..862d531f 100644 --- a/R/generate_design_random.R +++ b/R/generate_design_random.R @@ -16,7 +16,7 @@ #' pset = ps( #' ratio = p_dbl(lower = 0, upper = 1), #' letters = p_fct(levels = letters[1:3]) -#' )) +#' ) #' generate_design_random(pset, 10) generate_design_random = function(param_set, n) { # arg checks done by SamplerUnif and sample diff --git a/R/generate_design_sobol.R b/R/generate_design_sobol.R index 26057339..de67c98a 100644 --- a/R/generate_design_sobol.R +++ b/R/generate_design_sobol.R @@ -21,7 +21,7 @@ #' pset = ps( #' ratio = p_dbl(lower = 0, upper = 1), #' letters = p_fct(levels = letters[1:3]) -#' )) +#' ) #' #' if (requireNamespace("spacefillr", quietly = TRUE)) { #' generate_design_sobol(pset, 10) diff --git a/man/generate_design_grid.Rd b/man/generate_design_grid.Rd index 82ff8e5c..4f41eb36 100644 --- a/man/generate_design_grid.Rd +++ b/man/generate_design_grid.Rd @@ -28,7 +28,7 @@ For number params the endpoints of the params are always included in the grid. pset = ps( ratio = p_dbl(lower = 0, upper = 1), letters = p_fct(levels = letters[1:3]) -)) +) generate_design_grid(pset, 10) } \seealso{ diff --git a/man/generate_design_lhs.Rd b/man/generate_design_lhs.Rd index 417e6548..cdefe012 100644 --- a/man/generate_design_lhs.Rd +++ b/man/generate_design_lhs.Rd @@ -28,7 +28,7 @@ their respective columns. pset = ps( ratio = p_dbl(lower = 0, upper = 1), letters = p_fct(levels = letters[1:3]) -)) +) if (requireNamespace("lhs", quietly = TRUE)) { generate_design_lhs(pset, 10) diff --git a/man/generate_design_random.Rd b/man/generate_design_random.Rd index aa908082..cb492567 100644 --- a/man/generate_design_random.Rd +++ b/man/generate_design_random.Rd @@ -24,7 +24,7 @@ If dependencies do not hold, values are set to \code{NA} in the resulting data.t pset = ps( ratio = p_dbl(lower = 0, upper = 1), letters = p_fct(levels = letters[1:3]) -)) +) generate_design_random(pset, 10) } \seealso{ diff --git a/man/generate_design_sobol.Rd b/man/generate_design_sobol.Rd index dc97a0f2..9b440222 100644 --- a/man/generate_design_sobol.Rd +++ b/man/generate_design_sobol.Rd @@ -29,7 +29,7 @@ Note that non determinism is achieved by sampling the seed argument via pset = ps( ratio = p_dbl(lower = 0, upper = 1), letters = p_fct(levels = letters[1:3]) -)) +) if (requireNamespace("spacefillr", quietly = TRUE)) { generate_design_sobol(pset, 10) From 0ed5dc38ada9e7407823b24d6f68c2b5345de22a Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 14:49:16 +0100 Subject: [PATCH 58/64] fix in examples II --- R/ParamSet.R | 4 +++- man/ParamSet.Rd | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/R/ParamSet.R b/R/ParamSet.R index df020eff..28f37bb6 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -44,7 +44,9 @@ #' f = p_fct(levels = letters[1:3]) #' ) #' -#' ps$check(list(d = 2.1, f = "a")) +#' pset$check(list(d = 2.1, f = "a")) +#' +#' pset$check(list(d = 2.1, f = "d")) #' @export ParamSet = R6Class("ParamSet", public = list( diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index 2aaadda6..e172fc3d 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -54,7 +54,9 @@ pset = ps( f = p_fct(levels = letters[1:3]) ) -ps$check(list(d = 2.1, f = "a")) +pset$check(list(d = 2.1, f = "a")) + +pset$check(list(d = 2.1, f = "d")) } \section{Public fields}{ \if{html}{\out{
    }} From 9a8fd5cfdee898b3ce4aa4d7a2d5db238851c6fe Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 15:12:42 +0100 Subject: [PATCH 59/64] document domain_-functions --- NAMESPACE | 2 +- R/Domain.R | 26 +++++++-------- R/Domain_methods.R | 73 +++++++++++++++++++++++++++++++++++++----- man/Domain.Rd | 26 +++++++-------- man/domain_check.Rd | 15 ++++++++- man/domain_sanitize.Rd | 16 +++++++-- 6 files changed, 120 insertions(+), 38 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index e74951d6..b3eefa14 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -79,7 +79,7 @@ export(domain_is_categ) export(domain_is_number) export(domain_nlevels) export(domain_qunif) -export(domain_storage_type) +export(domain_sanitize) export(domain_test) export(generate_design_grid) export(generate_design_lhs) diff --git a/R/Domain.R b/R/Domain.R index ae038901..7d1d475c 100644 --- a/R/Domain.R +++ b/R/Domain.R @@ -2,16 +2,15 @@ #' #' @description #' A `Domain` object is a representation of a single dimension of a [`ParamSet`]. `Domain` objects are used to construct -#' [`ParamSet`]s, either through the [`ps()`] short form, or through the [`ParamSet`]`$search_space()` mechanism (see -#' [`to_tune()`]). `Domain` corresponds to a [`Param`] object, except it does not have an `$id`, and it *does* have a -#' `trafo` and dependencies (`depends`) associated with it. For each of the basic [`Param`] classes ([`ParamInt`], -#' [`ParamDbl`], [`ParamLgl`], [`ParamFct`], and [`ParamUty`]) there is a function constructing a `Domain` object -#' (`p_int()`, `p_dbl()`, `p_lgl()`, `p_fct()`, `p_uty()`). They each have the same arguments as the corresponding -#' [`Param`] `$new()` function, except without the `id` argument, and with the the additional parameters `trafo`, and -#' `depends`. +#' [`ParamSet`]s, either through the [`ps()`] short form, through the [`ParamSet`] constructor itself, +#' or through the [`ParamSet`]`$search_space()` mechanism (see +#' [`to_tune()`]). +#' For each of the basic parameter classes (`"ParamInt"`, `"ParamDbl"`, `"ParamLgl"`, `"ParamFct"`, and `"ParamUty"`) there is a function constructing a `Domain` object +#' (`p_int()`, `p_dbl()`, `p_lgl()`, `p_fct()`, `p_uty()`). They each have fitting construction arguments that control their +#' bounds and behavior. #' #' `Domain` objects are representations of parameter ranges and are intermediate objects to be used in short form -#' constructions in [`to_tune()`] and [`ps()`]. Because of their nature, they should not be modified by the user. +#' constructions in [`to_tune()`] and [`ps()`]. Because of their nature, they should not be modified by the user, once constructed. #' The `Domain` object's internals are subject to change and should not be relied upon. #' #' @template param_lower @@ -43,10 +42,10 @@ #' Put numeric domains on a log scale. Default `FALSE`. Log-scale `Domain`s represent parameter ranges where lower and upper bounds #' are logarithmized, and where a `trafo` is added that exponentiates sampled values to the original scale. This is #' *not* the same as setting `trafo = exp`, because `logscale = TRUE` will handle parameter bounds internally: -#' a `p_dbl(1, 10, logscale = TRUE)` results in a [`ParamDbl`] that has lower bound `0`, upper bound `log(10)`, +#' a `p_dbl(1, 10, logscale = TRUE)` results in a parameter that has lower bound `0`, upper bound `log(10)`, #' and uses `exp` transformation on these. Therefore, the given bounds represent the bounds *after* the transformation. #' (see examples).\cr -#' `p_int()` with `logscale = TRUE` results in a [`ParamDbl`], not a [`ParamInt`], but with bounds `log(max(lower, 0.5))` ... +#' `p_int()` with `logscale = TRUE` results in a continuous parameter similar to `p_dbl()`, not an integer-valued parameter, with bounds `log(max(lower, 0.5))` ... #' `log(upper + 1)` and a trafo similar to "`as.integer(exp(x))`" (with additional bounds correction). The lower bound #' is lifted to `0.5` if `lower` 0 to handle the `lower == 0` case. The upper bound is increased to `log(upper + 1)` #' because the trafo would otherwise almost never generate a value of `upper`.\cr @@ -62,8 +61,9 @@ #' @return A `Domain` object. #' #' @details -#' The `p_fct` function admits a `levels` argument that goes beyond the `levels` accepted by [`ParamFct`]`$new()`. -#' Instead of a `character` vector, any atomic vector or list (optionally named) may be given. (If the value is a list +#' Although the `levels` values of a constructed `p_fct()` will always be `character`-valued, the `p_fct` function admits +#' a `levels` argument that goes beyond this: +#' Besides a `character` vector, any atomic vector or list (optionally named) may be given. (If the value is a list #' that is not named, the names are inferred using `as.character()` on the values.) The resulting `Domain` will #' correspond to a range of values given by the names of the `levels` argument with a `trafo` that maps the `character` #' names to the arbitrary values of the `levels` argument. @@ -101,7 +101,7 @@ #' # But the values are on a log scale with desired bounds after trafo #' print(grid$transpose()) #' -#' # Integer parameters with logscale are `ParamDbl`s pre-trafo +#' # Integer parameters with logscale are `p_dbl()`s pre-trafo #' params = ps(x = p_int(0, 10, logscale = TRUE)) #' print(params) #' diff --git a/R/Domain_methods.R b/R/Domain_methods.R index 4947f120..4ca7530d 100644 --- a/R/Domain_methods.R +++ b/R/Domain_methods.R @@ -1,4 +1,4 @@ -#' @title Check value validity +#' @title Check Value Validity #' #' @description #' \pkg{checkmate}-like check-function. Check whether a list of values is feasible in the domain. @@ -6,8 +6,15 @@ #' `special_vals`. `TuneToken`s are generally *not* accepted, so they should be filtered out #' before the call, if present. #' +#' `domain_check` will return `TRUE` for accepted values, a `character(1)` error message otherwise. +#' +#' `domain_test` will return `TRUE` for accepted values, `FALSE` otherwise. +#' +#' `domain_assert` will return the `param` argument silently for accepted values, and throw an error message otherwise. +#' #' @param x (`any`). #' @return If successful `TRUE`, if not a string with the error message. +#' @keywords internal #' @export domain_check = function(param, values) { if (!test_list(values, len = nrow(param))) return("values must be a list") @@ -24,18 +31,23 @@ domain_check = function(param, values) { } #' @export +#' @rdname domain_check domain_assert = makeAssertionFunction(domain_check) #' @export +#' @rdname domain_check domain_test = function(param, values) isTRUE(domain_check(param, values)) -#' @export -domain_storage_type = function(param) { - if (!nrow(param)) return(character(0)) - assert_string(unique(param$grouping)) - UseMethod("domain_storage_type") -} +#' @title The Number of Levels of a Given Domain +#' +#' @description +#' This should be the number of discrete possible levels for discrete type [`Domain`]s such as [`p_int()`] or [`p_fct()`], and +#' `Inf` for continuous or untyped parameters. +#' +#' @param x (`Domain`). +#' @return `numeric`. +#' @keywords internal #' @export domain_nlevels = function(param) { if (!nrow(param)) return(integer(0)) @@ -43,6 +55,14 @@ domain_nlevels = function(param) { UseMethod("domain_nlevels") } +#' @title Whether a Given Domain is Bounded +#' +#' @description +#' This should generally be `TRUE` when `lower` and `upper` are given and finite, or when the `nlevels` is finite, and `FALSE` otherwise. +#' +#' @param x (`Domain`). +#' @return `logical`. +#' @keywords internal #' @export domain_is_bounded = function(param) { if (!nrow(param)) return(logical(0)) @@ -50,6 +70,14 @@ domain_is_bounded = function(param) { UseMethod("domain_is_bounded") } +#' @title Whether a Given Domain is Numeric +#' +#' @description +#' This should generally be `TRUE` for discrete or continuous numeric [`Domain`]s, and `FALSE` otherwise. +#' +#' @param x (`Domain`). +#' @return `logical`. +#' @keywords internal #' @export domain_is_number = function(param) { if (!nrow(param)) return(logical(0)) @@ -57,6 +85,14 @@ domain_is_number = function(param) { UseMethod("domain_is_number") } +#' @title Whether a Given Domain is Categorical +#' +#' @description +#' This should generally be `TRUE` for categorical [`Domain`]s, such as [`p_fct()`] or [`p_lgl()`], and `FALSE` otherwise. +#' +#' @param x (`Domain`). +#' @return `logical`. +#' @keywords internal #' @export domain_is_categ = function(param) { if (!nrow(param)) return(logical(0)) @@ -64,6 +100,15 @@ domain_is_categ = function(param) { UseMethod("domain_is_categ") } +#' @title Transform a Numeric Value to a Sample +#' +#' @description +#' Return a valid sample from the given [`Domain`], given a value from the interval `[0, 1]`. +#' +#' @param param (`Domain`). +#' @param x `numeric` between 0 and 1. +#' @return `any` -- format depending on the `Domain`. +#' @keywords internal #' @export domain_qunif = function(param, x) { if (!nrow(param)) return(logical(0)) @@ -73,7 +118,19 @@ domain_qunif = function(param, x) { UseMethod("domain_qunif") } -#' export +#' @title Map to Acceptable Value +#' +#' @description +#' Map values that are close enough to the given [`Domain`] to values that are truly acceptable. +#' +#' This is used to map `numeric()` values that are close to but outside the acceptable interval to the interval bounds. +#' It is also used to convert integer-valued `numeric` values to `integer` values for [`p_int()`]. +#' +#' @param param (`Domain`). +#' @param values (`any`) -- format depending on the `Domain`. +#' @return `any` -- format depending on the `Domain`. +#' @keywords internal +#' @export domain_sanitize = function(param, values) { if (!nrow(param)) return(values) assert_string(unique(param$grouping)) diff --git a/man/Domain.Rd b/man/Domain.Rd index 83a31c1f..26f5f41a 100644 --- a/man/Domain.Rd +++ b/man/Domain.Rd @@ -119,10 +119,10 @@ defining domains or hyperparameter ranges of learning algorithms, because these Put numeric domains on a log scale. Default \code{FALSE}. Log-scale \code{Domain}s represent parameter ranges where lower and upper bounds are logarithmized, and where a \code{trafo} is added that exponentiates sampled values to the original scale. This is \emph{not} the same as setting \code{trafo = exp}, because \code{logscale = TRUE} will handle parameter bounds internally: -a \code{p_dbl(1, 10, logscale = TRUE)} results in a \code{\link{ParamDbl}} that has lower bound \code{0}, upper bound \code{log(10)}, +a \code{p_dbl(1, 10, logscale = TRUE)} results in a parameter that has lower bound \code{0}, upper bound \code{log(10)}, and uses \code{exp} transformation on these. Therefore, the given bounds represent the bounds \emph{after} the transformation. (see examples).\cr -\code{p_int()} with \code{logscale = TRUE} results in a \code{\link{ParamDbl}}, not a \code{\link{ParamInt}}, but with bounds \code{log(max(lower, 0.5))} ... +\code{p_int()} with \code{logscale = TRUE} results in a continuous parameter similar to \code{p_dbl()}, not an integer-valued parameter, with bounds \code{log(max(lower, 0.5))} ... \code{log(upper + 1)} and a trafo similar to "\code{as.integer(exp(x))}" (with additional bounds correction). The lower bound is lifted to \code{0.5} if \code{lower} 0 to handle the \code{lower == 0} case. The upper bound is increased to \code{log(upper + 1)} because the trafo would otherwise almost never generate a value of \code{upper}.\cr @@ -152,21 +152,21 @@ A \code{Domain} object. } \description{ A \code{Domain} object is a representation of a single dimension of a \code{\link{ParamSet}}. \code{Domain} objects are used to construct -\code{\link{ParamSet}}s, either through the \code{\link[=ps]{ps()}} short form, or through the \code{\link{ParamSet}}\verb{$search_space()} mechanism (see -\code{\link[=to_tune]{to_tune()}}). \code{Domain} corresponds to a \code{\link{Param}} object, except it does not have an \verb{$id}, and it \emph{does} have a -\code{trafo} and dependencies (\code{depends}) associated with it. For each of the basic \code{\link{Param}} classes (\code{\link{ParamInt}}, -\code{\link{ParamDbl}}, \code{\link{ParamLgl}}, \code{\link{ParamFct}}, and \code{\link{ParamUty}}) there is a function constructing a \code{Domain} object -(\code{p_int()}, \code{p_dbl()}, \code{p_lgl()}, \code{p_fct()}, \code{p_uty()}). They each have the same arguments as the corresponding -\code{\link{Param}} \verb{$new()} function, except without the \code{id} argument, and with the the additional parameters \code{trafo}, and -\code{depends}. +\code{\link{ParamSet}}s, either through the \code{\link[=ps]{ps()}} short form, through the \code{\link{ParamSet}} constructor itself, +or through the \code{\link{ParamSet}}\verb{$search_space()} mechanism (see +\code{\link[=to_tune]{to_tune()}}). +For each of the basic parameter classes (\code{"ParamInt"}, \code{"ParamDbl"}, \code{"ParamLgl"}, \code{"ParamFct"}, and \code{"ParamUty"}) there is a function constructing a \code{Domain} object +(\code{p_int()}, \code{p_dbl()}, \code{p_lgl()}, \code{p_fct()}, \code{p_uty()}). They each have fitting construction arguments that control their +bounds and behavior. \code{Domain} objects are representations of parameter ranges and are intermediate objects to be used in short form -constructions in \code{\link[=to_tune]{to_tune()}} and \code{\link[=ps]{ps()}}. Because of their nature, they should not be modified by the user. +constructions in \code{\link[=to_tune]{to_tune()}} and \code{\link[=ps]{ps()}}. Because of their nature, they should not be modified by the user, once constructed. The \code{Domain} object's internals are subject to change and should not be relied upon. } \details{ -The \code{p_fct} function admits a \code{levels} argument that goes beyond the \code{levels} accepted by \code{\link{ParamFct}}\verb{$new()}. -Instead of a \code{character} vector, any atomic vector or list (optionally named) may be given. (If the value is a list +Although the \code{levels} values of a constructed \code{p_fct()} will always be \code{character}-valued, the \code{p_fct} function admits +a \code{levels} argument that goes beyond this: +Besides a \code{character} vector, any atomic vector or list (optionally named) may be given. (If the value is a list that is not named, the names are inferred using \code{as.character()} on the values.) The resulting \code{Domain} will correspond to a range of values given by the names of the \code{levels} argument with a \code{trafo} that maps the \code{character} names to the arbitrary values of the \code{levels} argument. @@ -204,7 +204,7 @@ print(grid) # But the values are on a log scale with desired bounds after trafo print(grid$transpose()) -# Integer parameters with logscale are `ParamDbl`s pre-trafo +# Integer parameters with logscale are `p_dbl()`s pre-trafo params = ps(x = p_int(0, 10, logscale = TRUE)) print(params) diff --git a/man/domain_check.Rd b/man/domain_check.Rd index 1158d456..56b797b6 100644 --- a/man/domain_check.Rd +++ b/man/domain_check.Rd @@ -2,9 +2,15 @@ % Please edit documentation in R/Domain_methods.R \name{domain_check} \alias{domain_check} -\title{Check value validity} +\alias{domain_assert} +\alias{domain_test} +\title{Check Value Validity} \usage{ domain_check(param, values) + +domain_assert(param, values, .var.name = checkmate::vname(param), add = NULL) + +domain_test(param, values) } \arguments{ \item{x}{(\code{any}).} @@ -17,4 +23,11 @@ If successful \code{TRUE}, if not a string with the error message. A value is feasible if it is of the same \code{storage_type}, inside of the bounds or element of \code{special_vals}. \code{TuneToken}s are generally \emph{not} accepted, so they should be filtered out before the call, if present. + +\code{domain_check} will return \code{TRUE} for accepted values, a \code{character(1)} error message otherwise. + +\code{domain_test} will return \code{TRUE} for accepted values, \code{FALSE} otherwise. + +\code{domain_assert} will return the \code{param} argument silently for accepted values, and throw an error message otherwise. } +\keyword{internal} diff --git a/man/domain_sanitize.Rd b/man/domain_sanitize.Rd index 4bdc9612..7af1174e 100644 --- a/man/domain_sanitize.Rd +++ b/man/domain_sanitize.Rd @@ -2,10 +2,22 @@ % Please edit documentation in R/Domain_methods.R \name{domain_sanitize} \alias{domain_sanitize} -\title{export} +\title{Map to Acceptable Value} \usage{ domain_sanitize(param, values) } +\arguments{ +\item{param}{(\code{Domain}).} + +\item{values}{(\code{any}) -- format depending on the \code{Domain}.} +} +\value{ +\code{any} -- format depending on the \code{Domain}. +} \description{ -export +Map values that are close enough to the given \code{\link{Domain}} to values that are truly acceptable. + +This is used to map \code{numeric()} values that are close to but outside the acceptable interval to the interval bounds. +It is also used to convert integer-valued \code{numeric} values to \code{integer} values for \code{\link[=p_int]{p_int()}}. } +\keyword{internal} From fefd4e6fc903429aaf78eb1ba722c1afc2ccec9a Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 15:12:48 +0100 Subject: [PATCH 60/64] document() --- man/domain_is_bounded.Rd | 18 ++++++++++++++++++ man/domain_is_categ.Rd | 18 ++++++++++++++++++ man/domain_is_number.Rd | 18 ++++++++++++++++++ man/domain_nlevels.Rd | 19 +++++++++++++++++++ man/domain_qunif.Rd | 20 ++++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 man/domain_is_bounded.Rd create mode 100644 man/domain_is_categ.Rd create mode 100644 man/domain_is_number.Rd create mode 100644 man/domain_nlevels.Rd create mode 100644 man/domain_qunif.Rd diff --git a/man/domain_is_bounded.Rd b/man/domain_is_bounded.Rd new file mode 100644 index 00000000..5be27f82 --- /dev/null +++ b/man/domain_is_bounded.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Domain_methods.R +\name{domain_is_bounded} +\alias{domain_is_bounded} +\title{Whether a Given Domain is Bounded} +\usage{ +domain_is_bounded(param) +} +\arguments{ +\item{x}{(\code{Domain}).} +} +\value{ +\code{logical}. +} +\description{ +This should generally be \code{TRUE} when \code{lower} and \code{upper} are given and finite, or when the \code{nlevels} is finite, and \code{FALSE} otherwise. +} +\keyword{internal} diff --git a/man/domain_is_categ.Rd b/man/domain_is_categ.Rd new file mode 100644 index 00000000..ca99e8b3 --- /dev/null +++ b/man/domain_is_categ.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Domain_methods.R +\name{domain_is_categ} +\alias{domain_is_categ} +\title{Whether a Given Domain is Categorical} +\usage{ +domain_is_categ(param) +} +\arguments{ +\item{x}{(\code{Domain}).} +} +\value{ +\code{logical}. +} +\description{ +This should generally be \code{TRUE} for categorical \code{\link{Domain}}s, such as \code{\link[=p_fct]{p_fct()}} or \code{\link[=p_lgl]{p_lgl()}}, and \code{FALSE} otherwise. +} +\keyword{internal} diff --git a/man/domain_is_number.Rd b/man/domain_is_number.Rd new file mode 100644 index 00000000..62e757c3 --- /dev/null +++ b/man/domain_is_number.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Domain_methods.R +\name{domain_is_number} +\alias{domain_is_number} +\title{Whether a Given Domain is Numeric} +\usage{ +domain_is_number(param) +} +\arguments{ +\item{x}{(\code{Domain}).} +} +\value{ +\code{logical}. +} +\description{ +This should generally be \code{TRUE} for discrete or continuous numeric \code{\link{Domain}}s, and \code{FALSE} otherwise. +} +\keyword{internal} diff --git a/man/domain_nlevels.Rd b/man/domain_nlevels.Rd new file mode 100644 index 00000000..a74c2cf6 --- /dev/null +++ b/man/domain_nlevels.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Domain_methods.R +\name{domain_nlevels} +\alias{domain_nlevels} +\title{The Number of Levels of a Given Domain} +\usage{ +domain_nlevels(param) +} +\arguments{ +\item{x}{(\code{Domain}).} +} +\value{ +\code{numeric}. +} +\description{ +This should be the number of discrete possible levels for discrete type \code{\link{Domain}}s such as \code{\link[=p_int]{p_int()}} or \code{\link[=p_fct]{p_fct()}}, and +\code{Inf} for continuous or untyped parameters. +} +\keyword{internal} diff --git a/man/domain_qunif.Rd b/man/domain_qunif.Rd new file mode 100644 index 00000000..f8c046df --- /dev/null +++ b/man/domain_qunif.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Domain_methods.R +\name{domain_qunif} +\alias{domain_qunif} +\title{Transform a Numeric Value to a Sample} +\usage{ +domain_qunif(param, x) +} +\arguments{ +\item{param}{(\code{Domain}).} + +\item{x}{\code{numeric} between 0 and 1.} +} +\value{ +\code{any} -- format depending on the \code{Domain}. +} +\description{ +Return a valid sample from the given \code{\link{Domain}}, given a value from the interval \verb{[0, 1]}. +} +\keyword{internal} From 98500f01a7383fd54a5f9671292f805669b6ada5 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 15:24:32 +0100 Subject: [PATCH 61/64] clean up documentation --- R/NoDefault.R | 2 +- R/ParamSet.R | 10 +++++----- R/Sampler1D.R | 4 ++-- R/SamplerHierarchical.R | 2 +- R/SamplerUnif.R | 2 +- R/generate_design_grid.R | 2 +- man-roxygen/field_is_bounded.R | 4 ++-- man-roxygen/field_levels.R | 4 ++-- man-roxygen/field_lower.R | 2 +- man-roxygen/field_nlevels.R | 6 +++--- man-roxygen/field_params_unid.R | 7 ------- man-roxygen/field_storage_type.R | 10 +++++----- man-roxygen/field_upper.R | 2 +- 13 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 man-roxygen/field_params_unid.R diff --git a/R/NoDefault.R b/R/NoDefault.R index 39215692..d2a38c29 100644 --- a/R/NoDefault.R +++ b/R/NoDefault.R @@ -4,7 +4,7 @@ #' Special new data type for no-default. #' Not often needed by the end-user, mainly internal. #' -#' * `NO_DEF`: Singleton object for type, used in [Param]. +#' * `NO_DEF`: Singleton object for type, used in [`Domain`] when no default is given. #' * `is_nodefault()`: Is an object the 'no default' object? #' #' @name NO_DEF diff --git a/R/ParamSet.R b/R/ParamSet.R index 28f37bb6..e389c211 100644 --- a/R/ParamSet.R +++ b/R/ParamSet.R @@ -777,7 +777,7 @@ ParamSet = R6Class("ParamSet", ############################ # ParamSet flags - #' @field length (`integer(1)`)\cr Number of contained [Param]s. + #' @field length (`integer(1)`)\cr Number of contained parameters. length = function() nrow(private$.params), #' @field is_empty (`logical(1)`)\cr Is the `ParamSet` empty? Named with parameter IDs. is_empty = function() nrow(private$.params) == 0L, @@ -789,9 +789,9 @@ ParamSet = R6Class("ParamSet", has_deps = function() nrow(self$deps) > 0L, #' @field has_constraint (`logical(1)`)\cr Whether parameter constraint is set. has_constraint = function() !is.null(private$.constraint), - #' @field all_numeric (`logical(1)`)\cr Is `TRUE` if all parameters are [ParamDbl] or [ParamInt]. + #' @field all_numeric (`logical(1)`)\cr Is `TRUE` if all parameters are [`p_dbl()`] or [`p_int()`]. all_numeric = function() all(self$is_number), - #' @field all_categorical (`logical(1)`)\cr Is `TRUE` if all parameters are [ParamFct] and [ParamLgl]. + #' @field all_categorical (`logical(1)`)\cr Is `TRUE` if all parameters are [`p_fct()`] and [`p_lgl()`]. all_categorical = function() all(self$is_categ), #' @field all_bounded (`logical(1)`)\cr Is `TRUE` if all parameters are bounded. all_bounded = function() all(self$is_bounded), @@ -836,7 +836,7 @@ ParamSet = R6Class("ParamSet", with(tmp[private$.params$id, on = "id"], set_names(nlevels, id)) }, - #' @field is_number (named `logical()`)\cr Whether parameter is [ParamDbl] or [ParamInt]. Named with parameter IDs. + #' @field is_number (named `logical()`)\cr Whether parameter is [`p_dbl()`] or [`p_int()`]. Named with parameter IDs. is_number = function() { tmp = private$.params[, list(id, is_number = rep(domain_is_number(recover_domain(.SD, .BY)), .N)), @@ -845,7 +845,7 @@ ParamSet = R6Class("ParamSet", with(tmp[private$.params$id, on = "id"], set_names(is_number, id)) }, - #' @field is_categ (named `logical()`)\cr Whether parameter is [ParamFct] or [ParamLgl]. Named with parameter IDs. + #' @field is_categ (named `logical()`)\cr Whether parameter is [`p_fct()`] or [`p_lgl()`]. Named with parameter IDs. is_categ = function() { tmp = private$.params[, list(id, is_categ = rep(domain_is_categ(recover_domain(.SD, .BY)), .N)), diff --git a/R/Sampler1D.R b/R/Sampler1D.R index e535c0d4..5a69d1cd 100644 --- a/R/Sampler1D.R +++ b/R/Sampler1D.R @@ -132,7 +132,7 @@ Sampler1DRfun = R6Class("Sampler1DRfun", inherit = Sampler1D, #' @title Sampler1DCateg Class #' #' @description -#' Sampling from a discrete distribution, for a [ParamFct] or [ParamLgl]. +#' Sampling from a discrete distribution, for a [`ParamSet`] containing a single [`p_fct()`] or [`p_lgl()`]. #' #' @template param_param #' @@ -173,7 +173,7 @@ Sampler1DCateg = R6Class("Sampler1DCateg", inherit = Sampler1D, #' @title Sampler1DNormal Class #' #' @description -#' Normal sampling (potentially truncated) for [ParamDbl]. +#' Normal sampling (potentially truncated) for [`p_dbl()`]. #' #' @template param_param #' diff --git a/R/SamplerHierarchical.R b/R/SamplerHierarchical.R index f166f679..668c1374 100644 --- a/R/SamplerHierarchical.R +++ b/R/SamplerHierarchical.R @@ -22,7 +22,7 @@ SamplerHierarchical = R6Class("SamplerHierarchical", inherit = Sampler, #' @param param_set ([`ParamSet`])\cr #' The [`ParamSet`] to associated with this `SamplerHierarchical`. #' @param samplers (`list()`)\cr - #' List of [`Sampler1D`] objects that gives a Sampler for each [Param] in the `param_set`. + #' List of [`Sampler1D`] objects that gives a Sampler for each dimension in the `param_set`. initialize = function(param_set, samplers) { assert_param_set(param_set, no_untyped = TRUE) assert_list(samplers, types = "Sampler1D") diff --git a/R/SamplerUnif.R b/R/SamplerUnif.R index 06bfa70e..5f7f902a 100644 --- a/R/SamplerUnif.R +++ b/R/SamplerUnif.R @@ -2,7 +2,7 @@ #' #' @description #' Uniform random sampling for an arbitrary (bounded) [ParamSet]. -#' Constructs 1 uniform sampler per [Param], then passes them to [SamplerHierarchical]. +#' Constructs 1 uniform sampler per parameter, then passes them to [SamplerHierarchical]. #' Hence, also works for [ParamSet]s sets with dependencies. #' #' @template param_param_set diff --git a/R/generate_design_grid.R b/R/generate_design_grid.R index 2c85fa04..e8c783d7 100644 --- a/R/generate_design_grid.R +++ b/R/generate_design_grid.R @@ -8,7 +8,7 @@ #' #' @param param_set ([`ParamSet`]). #' @param resolution (`integer(1)`)\cr -#' Global resolution for all [Param]s. +#' Global resolution for all parameters. #' @param param_resolutions (named `integer()`)\cr #' Resolution per [`Domain`], named by parameter ID. #' @return [`Design`]. diff --git a/man-roxygen/field_is_bounded.R b/man-roxygen/field_is_bounded.R index 3a98a97b..135a2cd7 100644 --- a/man-roxygen/field_is_bounded.R +++ b/man-roxygen/field_is_bounded.R @@ -1,4 +1,4 @@ #' @field is_bounded (`logical(1)`)\cr #' Are the bounds finite? -#' Always `TRUE` for [ParamFct] and [ParamLgl]. -#' Always `FALSE` for [ParamUty]. +#' Always `TRUE` for [`p_fct()`] and [`p_lgl()`]. +#' Always `FALSE` for [`p_uty()`]. diff --git a/man-roxygen/field_levels.R b/man-roxygen/field_levels.R index ff983042..ab48188b 100644 --- a/man-roxygen/field_levels.R +++ b/man-roxygen/field_levels.R @@ -1,4 +1,4 @@ #' @field levels (`character()` | `NULL`)\cr #' Set of allowed levels. -#' Always `NULL` for [ParamDbl], [ParamInt] and [ParamUty]. -#' Always `c(TRUE, FALSE)` for [ParamLgl]. +#' Always `NULL` for [`p_dbl()`], [`p_int()`] and [`p_uty()`]. +#' Always `c(TRUE, FALSE)` for [`p_lgl()`]. diff --git a/man-roxygen/field_lower.R b/man-roxygen/field_lower.R index 23fa0795..a68a7929 100644 --- a/man-roxygen/field_lower.R +++ b/man-roxygen/field_lower.R @@ -1,3 +1,3 @@ #' @field lower (`numeric(1)`)\cr #' Lower bound. -#' Always `NA` for [ParamFct], [ParamLgl] and [ParamUty]. +#' Always `NA` for [`p_fct()`], [`p_lgl()`] and [`p_uty()`]. diff --git a/man-roxygen/field_nlevels.R b/man-roxygen/field_nlevels.R index d41c0b6a..8fd24f81 100644 --- a/man-roxygen/field_nlevels.R +++ b/man-roxygen/field_nlevels.R @@ -1,5 +1,5 @@ #' @field nlevels (`integer(1)` | `Inf`)\cr #' Number of categorical levels. -#' Always `Inf` for [ParamDbl] and [ParamUty]. -#' The number of integers in the range `[lower, upper]`, or `Inf` if unbounded for [ParamInt]. -#' Always `2` for [ParamLgl]. +#' Always `Inf` for [`p_dbl()`] and [`p_uty()`]. +#' The number of integers in the range `[lower, upper]`, or `Inf` if unbounded for [`p_int()`]. +#' Always `2` for [`p_lgl()`]. diff --git a/man-roxygen/field_params_unid.R b/man-roxygen/field_params_unid.R deleted file mode 100644 index 62f22723..00000000 --- a/man-roxygen/field_params_unid.R +++ /dev/null @@ -1,7 +0,0 @@ -#' @field params_unid (named `list()`)\cr -#' List of [Param], named with their true ID. However, -#' this field has the [Param]'s `$id` value set to a -#' potentially invalid value, and it is furthermore not -#' cloned when the `ParamSet` is cloned. This active -#' binding should be used internally, or to avoid unnecessary -#' cloning when that becomes a bottleneck. diff --git a/man-roxygen/field_storage_type.R b/man-roxygen/field_storage_type.R index 216fe15e..9e4b0552 100644 --- a/man-roxygen/field_storage_type.R +++ b/man-roxygen/field_storage_type.R @@ -1,7 +1,7 @@ #' @field storage_type (`character(1)`)\cr #' Data type when values of this parameter are stored in a data table or sampled. -#' Always `"numeric"` for [ParamDbl]. -#' Always `"character"` for [ParamFct]. -#' Always `"integer"` for [ParamInt]. -#' Always `"logical"` for [ParamLgl]. -#' Always `"list"` for [ParamUty]. +#' Always `"numeric"` for [`p_dbl()`]. +#' Always `"character"` for [`p_fct()`]. +#' Always `"integer"` for [`p_int()`]. +#' Always `"logical"` for [`p_lgl()`]. +#' Always `"list"` for [`p_uty()`]. diff --git a/man-roxygen/field_upper.R b/man-roxygen/field_upper.R index 482503fe..41d61008 100644 --- a/man-roxygen/field_upper.R +++ b/man-roxygen/field_upper.R @@ -1,3 +1,3 @@ #' @field upper (`numeric(1)`)\cr #' Upper bound. -#' Always `NA` for [ParamFct], [ParamLgl] and [ParamUty]. +#' Always `NA` for [`p_fct()`], [`p_lgl()`] and [`p_uty()`]. From c83c25fec79b4484be1629e28eab94fb3d7e7903 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 15:24:37 +0100 Subject: [PATCH 62/64] document() --- man/NO_DEF.Rd | 2 +- man/ParamSet.Rd | 10 +++++----- man/Sampler1DCateg.Rd | 2 +- man/Sampler1DNormal.Rd | 2 +- man/SamplerHierarchical.Rd | 2 +- man/SamplerUnif.Rd | 2 +- man/generate_design_grid.Rd | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/man/NO_DEF.Rd b/man/NO_DEF.Rd index 13858315..e5308b7c 100644 --- a/man/NO_DEF.Rd +++ b/man/NO_DEF.Rd @@ -9,7 +9,7 @@ Special new data type for no-default. Not often needed by the end-user, mainly internal. \itemize{ -\item \code{NO_DEF}: Singleton object for type, used in \link{Param}. +\item \code{NO_DEF}: Singleton object for type, used in \code{\link{Domain}} when no default is given. \item \code{is_nodefault()}: Is an object the 'no default' object? } } diff --git a/man/ParamSet.Rd b/man/ParamSet.Rd index e172fc3d..b74daebc 100644 --- a/man/ParamSet.Rd +++ b/man/ParamSet.Rd @@ -112,7 +112,7 @@ Lists all (direct) dependency parents of a param, through parameter IDs. Internally created by a call to \code{add_dep}. Settable, if you want to remove dependencies or perform other changes.} -\item{\code{length}}{(\code{integer(1)})\cr Number of contained \link{Param}s.} +\item{\code{length}}{(\code{integer(1)})\cr Number of contained parameters.} \item{\code{is_empty}}{(\code{logical(1)})\cr Is the \code{ParamSet} empty? Named with parameter IDs.} @@ -124,9 +124,9 @@ Settable, if you want to remove dependencies or perform other changes.} \item{\code{has_constraint}}{(\code{logical(1)})\cr Whether parameter constraint is set.} -\item{\code{all_numeric}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \link{ParamDbl} or \link{ParamInt}.} +\item{\code{all_numeric}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \code{\link[=p_dbl]{p_dbl()}} or \code{\link[=p_int]{p_int()}}.} -\item{\code{all_categorical}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \link{ParamFct} and \link{ParamLgl}.} +\item{\code{all_categorical}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are \code{\link[=p_fct]{p_fct()}} and \code{\link[=p_lgl]{p_lgl()}}.} \item{\code{all_bounded}}{(\code{logical(1)})\cr Is \code{TRUE} if all parameters are bounded.} @@ -156,9 +156,9 @@ this \code{is_logscale} will be \code{FALSE}.} \item{\code{nlevels}}{(named \code{integer()})\cr Number of distinct levels of parameters. \code{Inf} for double parameters or unbounded integer parameters. Named with param IDs.} -\item{\code{is_number}}{(named \code{logical()})\cr Whether parameter is \link{ParamDbl} or \link{ParamInt}. Named with parameter IDs.} +\item{\code{is_number}}{(named \code{logical()})\cr Whether parameter is \code{\link[=p_dbl]{p_dbl()}} or \code{\link[=p_int]{p_int()}}. Named with parameter IDs.} -\item{\code{is_categ}}{(named \code{logical()})\cr Whether parameter is \link{ParamFct} or \link{ParamLgl}. Named with parameter IDs.} +\item{\code{is_categ}}{(named \code{logical()})\cr Whether parameter is \code{\link[=p_fct]{p_fct()}} or \code{\link[=p_lgl]{p_lgl()}}. Named with parameter IDs.} \item{\code{is_bounded}}{(named \code{logical()})\cr Whether parameters have finite bounds. Named with parameter IDs.} } diff --git a/man/Sampler1DCateg.Rd b/man/Sampler1DCateg.Rd index 22726357..8ebbf668 100644 --- a/man/Sampler1DCateg.Rd +++ b/man/Sampler1DCateg.Rd @@ -4,7 +4,7 @@ \alias{Sampler1DCateg} \title{Sampler1DCateg Class} \description{ -Sampling from a discrete distribution, for a \link{ParamFct} or \link{ParamLgl}. +Sampling from a discrete distribution, for a \code{\link{ParamSet}} containing a single \code{\link[=p_fct]{p_fct()}} or \code{\link[=p_lgl]{p_lgl()}}. } \seealso{ Other Sampler: diff --git a/man/Sampler1DNormal.Rd b/man/Sampler1DNormal.Rd index 25c3cf65..c043e228 100644 --- a/man/Sampler1DNormal.Rd +++ b/man/Sampler1DNormal.Rd @@ -4,7 +4,7 @@ \alias{Sampler1DNormal} \title{Sampler1DNormal Class} \description{ -Normal sampling (potentially truncated) for \link{ParamDbl}. +Normal sampling (potentially truncated) for \code{\link[=p_dbl]{p_dbl()}}. } \seealso{ Other Sampler: diff --git a/man/SamplerHierarchical.Rd b/man/SamplerHierarchical.Rd index 60b6c4d5..e0b6f9fd 100644 --- a/man/SamplerHierarchical.Rd +++ b/man/SamplerHierarchical.Rd @@ -63,7 +63,7 @@ Creates a new instance of this \link[R6:R6Class]{R6} class. The \code{\link{ParamSet}} to associated with this \code{SamplerHierarchical}.} \item{\code{samplers}}{(\code{list()})\cr -List of \code{\link{Sampler1D}} objects that gives a Sampler for each \link{Param} in the \code{param_set}.} +List of \code{\link{Sampler1D}} objects that gives a Sampler for each dimension in the \code{param_set}.} } \if{html}{\out{
    }} } diff --git a/man/SamplerUnif.Rd b/man/SamplerUnif.Rd index 0ac607ad..77a038b3 100644 --- a/man/SamplerUnif.Rd +++ b/man/SamplerUnif.Rd @@ -5,7 +5,7 @@ \title{SamplerUnif Class} \description{ Uniform random sampling for an arbitrary (bounded) \link{ParamSet}. -Constructs 1 uniform sampler per \link{Param}, then passes them to \link{SamplerHierarchical}. +Constructs 1 uniform sampler per parameter, then passes them to \link{SamplerHierarchical}. Hence, also works for \link{ParamSet}s sets with dependencies. } \seealso{ diff --git a/man/generate_design_grid.Rd b/man/generate_design_grid.Rd index 4f41eb36..1dffab7d 100644 --- a/man/generate_design_grid.Rd +++ b/man/generate_design_grid.Rd @@ -10,7 +10,7 @@ generate_design_grid(param_set, resolution = NULL, param_resolutions = NULL) \item{param_set}{(\code{\link{ParamSet}}).} \item{resolution}{(\code{integer(1)})\cr -Global resolution for all \link{Param}s.} +Global resolution for all parameters.} \item{param_resolutions}{(named \code{integer()})\cr Resolution per \code{\link{Domain}}, named by parameter ID.} From 002e1cab53acedcc986e73be1b5de10113da9526 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 16:04:30 +0100 Subject: [PATCH 63/64] some more small doc fixes --- R/Domain.R | 6 ++++++ R/ps.R | 4 ++++ R/to_tune.R | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/R/Domain.R b/R/Domain.R index 7d1d475c..56b98a55 100644 --- a/R/Domain.R +++ b/R/Domain.R @@ -57,6 +57,12 @@ #' defining domains or hyperparameter ranges of learning algorithms, because these do not use trafos.\cr #' `logscale` happens on a natural (`e == 2.718282...`) basis. Be aware that using a different base (`log10()`/`10^`, #' `log2()`/`2^`) is completely equivalent and does not change the values being sampled after transformation. +#' @param repr (`language`)\cr +#' Symbol to use to represent the value given in `default`. +#' The `deparse()` of this object is used when printing the domain, in some cases. +#' @param init (`any`)\cr +#' Initial value. When this is given, then the corresponding entry in `ParamSet$values` is initialized with this +#' value upon construction. #' #' @return A `Domain` object. #' diff --git a/R/ps.R b/R/ps.R index a111a68e..1901ffd9 100644 --- a/R/ps.R +++ b/R/ps.R @@ -14,6 +14,10 @@ #' @param .extra_trafo (`function(x, param_set)`)\cr #' Transformation to set the resulting [`ParamSet`]'s `$trafo` value to. This is in addition to any `trafo` of #' [`Domain`] objects given in `...`, and will be run *after* transformations of individual parameters were performed. +#' @param .constraint (`function(x)`)\cr +#' Constraint function. +#' When given, this function must evaluate a named `list()` of values and determine whether it satisfies +#' constraints, returning a scalar `logical(1)` value. #' @param .allow_dangling_dependencies (`logical`)\cr #' Whether dependencies depending on parameters that are not present should be allowed. A parameter `x` having #' `depends = y == 0` if `y` is not present in the `ps()` call would usually throw an error, but if dangling diff --git a/R/to_tune.R b/R/to_tune.R index 0771179a..9c41e6ea 100644 --- a/R/to_tune.R +++ b/R/to_tune.R @@ -21,7 +21,7 @@ #' * **`to_tune(lower, upper, logscale)`**: Indicates a numeric parameter should be tuned in the inclusive interval spanning #' `lower` to `upper`, possibly on a log scale if `logscale` is se to `TRUE`. All parameters are optional, and the #' parameter's own lower / upper bounds are used without log scale, by default. Depending on the parameter, -#' integer (if it is a [`ParamInt`]) or real values (if it is a [`ParamDbl`]) are used.\cr +#' integer (if it is a [`p_int()`]) or real values (if it is a [`p_dbl()`]) are used.\cr #' `lower`, `upper`, and `logscale` can be given by position, except when only one of them is given, in which case #' it must be named to disambiguate from the following cases.\cr #' When `logscale` is `TRUE`, then a `trafo` is generated automatically that transforms to the given bounds. The From fc43931c75422c7d2a8499a2722a3c0d80ce01b6 Mon Sep 17 00:00:00 2001 From: mb706 Date: Sun, 21 Jan 2024 16:04:40 +0100 Subject: [PATCH 64/64] document() --- man/Domain.Rd | 8 ++++++++ man/ps.Rd | 5 +++++ man/to_tune.Rd | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/man/Domain.Rd b/man/Domain.Rd index 26f5f41a..d0c25a03 100644 --- a/man/Domain.Rd +++ b/man/Domain.Rd @@ -135,6 +135,10 @@ defining domains or hyperparameter ranges of learning algorithms, because these \code{logscale} happens on a natural (\verb{e == 2.718282...}) basis. Be aware that using a different base (\code{log10()}/\verb{10^}, \code{log2()}/\verb{2^}) is completely equivalent and does not change the values being sampled after transformation.} +\item{init}{(\code{any})\cr +Initial value. When this is given, then the corresponding entry in \code{ParamSet$values} is initialized with this +value upon construction.} + \item{levels}{(\code{character} | \code{atomic} | \code{list})\cr Allowed categorical values of the parameter. If this is not a \code{character}, then a \code{trafo} is generated that converts the names (if not given: \code{as.character()} of the values) of the \code{levels} argument to the values. @@ -146,6 +150,10 @@ Function which checks the input. Must return 'TRUE' if the input is valid and a \code{character(1)} with the error message otherwise. This function should \emph{not} throw an error. Defaults to \code{NULL}, which means that no check is performed.} + +\item{repr}{(\code{language})\cr +Symbol to use to represent the value given in \code{default}. +The \code{deparse()} of this object is used when printing the domain, in some cases.} } \value{ A \code{Domain} object. diff --git a/man/ps.Rd b/man/ps.Rd index fdd3fe3d..c8ab62b2 100644 --- a/man/ps.Rd +++ b/man/ps.Rd @@ -20,6 +20,11 @@ The names of the arguments will be used as \verb{$id()} in the resulting \code{\ Transformation to set the resulting \code{\link{ParamSet}}'s \verb{$trafo} value to. This is in addition to any \code{trafo} of \code{\link{Domain}} objects given in \code{...}, and will be run \emph{after} transformations of individual parameters were performed.} +\item{.constraint}{(\verb{function(x)})\cr +Constraint function. +When given, this function must evaluate a named \code{list()} of values and determine whether it satisfies +constraints, returning a scalar \code{logical(1)} value.} + \item{.allow_dangling_dependencies}{(\code{logical})\cr Whether dependencies depending on parameters that are not present should be allowed. A parameter \code{x} having \code{depends = y == 0} if \code{y} is not present in the \code{ps()} call would usually throw an error, but if dangling diff --git a/man/to_tune.Rd b/man/to_tune.Rd index 82622528..0b74e7d0 100644 --- a/man/to_tune.Rd +++ b/man/to_tune.Rd @@ -32,7 +32,7 @@ can be constructed via the \code{to_tune()} function in one of several ways: \item \strong{\code{to_tune(lower, upper, logscale)}}: Indicates a numeric parameter should be tuned in the inclusive interval spanning \code{lower} to \code{upper}, possibly on a log scale if \code{logscale} is se to \code{TRUE}. All parameters are optional, and the parameter's own lower / upper bounds are used without log scale, by default. Depending on the parameter, -integer (if it is a \code{\link{ParamInt}}) or real values (if it is a \code{\link{ParamDbl}}) are used.\cr +integer (if it is a \code{\link[=p_int]{p_int()}}) or real values (if it is a \code{\link[=p_dbl]{p_dbl()}}) are used.\cr \code{lower}, \code{upper}, and \code{logscale} can be given by position, except when only one of them is given, in which case it must be named to disambiguate from the following cases.\cr When \code{logscale} is \code{TRUE}, then a \code{trafo} is generated automatically that transforms to the given bounds. The