Skip to content

Commit 6fa8107

Browse files
authored
introduce adaptive infill (#388)
* introduce adaptive infill (WIP) * improve adative infill * better progress handling * fix progress and tests * progress not always necessary * better fixation of adaptive infill * documentation [ci skip] * fix * add random cb * infill update * remove rcb * remove rcb (2) * design was too complicated
1 parent 3ecf866 commit 6fa8107

18 files changed

+185
-36
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
S3method(initCrit,InfillCritAdaCB)
34
S3method(initCrit,InfillCritCB)
45
S3method(initCrit,default)
56
S3method(plot,MBOMultiObjResult)
@@ -39,6 +40,7 @@ export(initSMBO)
3940
export(makeMBOControl)
4041
export(makeMBOInfillCrit)
4142
export(makeMBOInfillCritAEI)
43+
export(makeMBOInfillCritAdaCB)
4244
export(makeMBOInfillCritCB)
4345
export(makeMBOInfillCritDIB)
4446
export(makeMBOInfillCritEI)

R/OptState.R

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ NULL
3939
# @param time.used \code{integer(1)} \cr
4040
# The time in seconds we are already used for optimization since the very start.
4141
# This counts all iterations together and is necessary for continuation with a given time budget.
42+
# @param progress \code{numeric(1)} \cr
43+
# The progress in percent determined by the termination criterion.
4244

4345
# IMPORTANT NOTE:
4446
# See this as a constructor and it's variables as member variables.
@@ -48,7 +50,7 @@ NULL
4850

4951
makeOptState = function(opt.problem, loop = 0L, tasks = NULL, models = NULL,
5052
time.model = NULL, opt.result = NULL, state = "init", opt.path = NULL,
51-
time.last.saved = Sys.time(), loop.starttime = Sys.time(), time.used = 0L, time.created = Sys.time()) {
53+
time.last.saved = Sys.time(), loop.starttime = Sys.time(), time.used = 0L, progress = 0, time.created = Sys.time()) {
5254

5355
opt.state = new.env()
5456

@@ -65,6 +67,7 @@ makeOptState = function(opt.problem, loop = 0L, tasks = NULL, models = NULL,
6567
opt.state$time.last.saved = time.last.saved
6668
opt.state$loop.starttime = loop.starttime
6769
opt.state$time.used = time.used
70+
opt.state$progress = progress
6871

6972
opt.state$random.seed = getRandomSeed()
7073
opt.state$time.created = time.created

R/OptState_getter.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,23 @@ getOptStateState = function(opt.state) {
119119

120120
getOptStateTermination = function(opt.state) {
121121
terminate = shouldTerminate.OptState(opt.state)
122+
setOptStateProgress(opt.state, terminate$progress)
122123
# update only if termination condition is met
123124
if (terminate$term) {
124125
setOptStateState(opt.state, terminate$code)
125126
}
126127
terminate
127128
}
128129

130+
getOptStateProgress = function(opt.state) {
131+
opt.state$progress
132+
}
133+
129134
getOptStateValidStates = function() {
130135
c("init", "iter", getOptStateValidTerminationStates())
131136
}
132137

133138
getOptStateValidTerminationStates = function() {
134139
c("term.iter", "term.time", "term.exectime", "term.yval", "term.feval", "term.custom")
135140
}
141+

R/OptState_setter.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,9 @@ setOptStateState = function(opt.state, state) {
5858
opt.state$state = state
5959
invisible()
6060
}
61+
62+
setOptStateProgress = function(opt.state, progress) {
63+
assertNumber(progress, null.ok = TRUE)
64+
opt.state$progress = progress
65+
invisible()
66+
}

R/infillOptFocus.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#FIXME should we shrink if a local value is NA (dependent param)
77
#
88
# See infillOptCMAES.R for interface explanation.
9-
infillOptFocus = function(infill.crit, models, control, par.set, opt.path, design, iter, ...) {
9+
infillOptFocus = function(infill.crit, models, control, par.set, opt.path, ...) {
1010
global.y = Inf
1111

1212
# restart the whole crap some times
@@ -22,7 +22,7 @@ infillOptFocus = function(infill.crit, models, control, par.set, opt.path, desig
2222
# convert to param encoding our model was trained on and can use
2323
newdesign = convertDataFrameCols(newdesign, ints.as.num = TRUE, logicals.as.factor = TRUE)
2424

25-
y = infill.crit(newdesign, models, control, ps.local, design, iter, ...)
25+
y = infill.crit(newdesign, models, control, ps.local, ...)
2626

2727

2828
# get current best value

R/infill_crits.R

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
# Calculates the range of the mean and standard error and multiplies the standard error
2525
# with the quotient of theses ranges.
2626
# Default is \code{FALSE}.
27+
#' @param cb.lambda.start [\code{numeric(1)} | \code{NULL}]\cr
28+
#' The value of \code{cb.lambda} at the beginning of the optimization.
29+
#' The \code{makeMBOInfillCritAdaCB} crit takes the progress of the optimization determined by the termination criterion to linearly move from \code{cb.lambda.start} to \code{cb.lambda.end}.
30+
#' The initial desgin does not account for the progress of the optimization.
31+
#' Eexcept for \code{makeMBOTerminationMaxExecBudget}) if you dont pass a precalculated initial design.
32+
#' @param cb.lambda.end [\code{numeric(1)} | \code{NULL}]\cr
33+
#' The value of \code{cb.lambda} at the end of the optimization.
2734
#' @param aei.use.nugget [\code{logical(1)}]\cr
2835
#' Should the nugget effect be used for the pure variance estimation for augmented
2936
#' expected improvement?
@@ -47,7 +54,7 @@ NULL
4754
#' @rdname infillcrits
4855
makeMBOInfillCritMeanResponse = function() {
4956
makeMBOInfillCrit(
50-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
57+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
5158
ifelse(control$minimize, 1, -1) * predict(models[[1L]], newdata = points)$data$response
5259
},
5360
name = "Mean response",
@@ -60,7 +67,7 @@ makeMBOInfillCritMeanResponse = function() {
6067
#' @rdname infillcrits
6168
makeMBOInfillCritStandardError = function() {
6269
makeMBOInfillCrit(
63-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
70+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
6471
-predict(models[[1L]], newdata = points)$data$se
6572
},
6673
name = "Standard error",
@@ -76,7 +83,7 @@ makeMBOInfillCritEI = function(se.threshold = 1e-6) {
7683
assertNumber(se.threshold, lower = 1e-20)
7784
force(se.threshold)
7885
makeMBOInfillCrit(
79-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
86+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
8087
model = models[[1L]]
8188
maximize.mult = ifelse(control$minimize, 1, -1)
8289
y = maximize.mult * design[, control$y.name]
@@ -110,7 +117,7 @@ makeMBOInfillCritCB = function(cb.lambda = NULL) {
110117
assertNumber(cb.lambda, lower = 0, null.ok = TRUE)
111118
force(cb.lambda)
112119
makeMBOInfillCrit(
113-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
120+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
114121
model = models[[1L]]
115122
maximize.mult = ifelse(control$minimize, 1, -1)
116123
p = predict(model, newdata = points)$data
@@ -152,7 +159,7 @@ makeMBOInfillCritAEI = function(aei.use.nugget = FALSE, se.threshold = 1e-6) {
152159
force(se.threshold)
153160

154161
makeMBOInfillCrit(
155-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
162+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
156163
model = models[[1L]]
157164
maximize.mult = ifelse(control$minimize, 1, -1)
158165
p = predict(model, newdata = points)$data
@@ -198,7 +205,7 @@ makeMBOInfillCritEQI = function(eqi.beta = 0.75, se.threshold = 1e-6) {
198205
force(se.threshold)
199206

200207
makeMBOInfillCrit(
201-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
208+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
202209
model = models[[1L]]
203210
maximize.mult = ifelse(control$minimize, 1, -1)
204211
# compute q.min
@@ -251,7 +258,7 @@ makeMBOInfillCritDIB = function(cb.lambda = 1, sms.eps = NULL) {
251258
if (!is.null(sms.eps))
252259
assertNumber(sms.eps, lower = 0, finite = TRUE)
253260
makeMBOInfillCrit(
254-
fun = function(points, models, control, par.set, design, iter, attributes = FALSE) {
261+
fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
255262
# get ys and cb-value-matrix for new points, minimize version
256263
maximize.mult = ifelse(control$minimize, 1, -1)
257264
ys = as.matrix(design[, control$y.name]) %*% diag(maximize.mult)
@@ -292,3 +299,28 @@ makeMBOInfillCritDIB = function(cb.lambda = 1, sms.eps = NULL) {
292299
requires.se = TRUE
293300
)
294301
}
302+
303+
# ============================
304+
# Experimental Infill Criteria
305+
# ============================
306+
307+
#' @export
308+
#' @rdname infillcrits
309+
makeMBOInfillCritAdaCB = function(cb.lambda.start = NULL, cb.lambda.end = NULL) {
310+
assertNumber(cb.lambda.start, lower = 0, null.ok = TRUE)
311+
assertNumber(cb.lambda.end, lower = 0, null.ok = TRUE)
312+
force(cb.lambda.start)
313+
force(cb.lambda.end)
314+
crit = makeMBOInfillCritCB()
315+
orig.fun = crit$fun
316+
crit$fun = function(points, models, control, par.set, design, iter, progress, attributes = FALSE) {
317+
assertNumber(progress)
318+
cb.lambda = (1-progress) * cb.lambda.start + progress * cb.lambda.end
319+
assign("cb.lambda", cb.lambda, envir = environment(orig.fun))
320+
orig.fun(points, models, control, par.set, design, iter, progress, attributes)
321+
}
322+
crit$name = "Adaptive Confidence bound"
323+
crit$id = "adacb"
324+
crit$params = list(cb.lambda.start = cb.lambda.start, cb.lambda.end = cb.lambda.end)
325+
return(addClasses(crit, "InfillCritAdaCB"))
326+
}

R/initCrit.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ initCrit.InfillCritCB = function(crit, fun, design, learner, control) {
4040
initCritOptDirection(crit, fun)
4141
}
4242

43+
#' @export
44+
initCrit.InfillCritAdaCB = function(crit, fun, design, learner, control) {
45+
cb.lambda = crit$params$cb.lambda
46+
if (is.null(cb.lambda))
47+
cb.lambda = ifelse(isSimpleNumeric(getParamSet(fun)), 1, 2)
48+
crit = makeMBOInfillCritAdaCB(2 * cb.lambda, 0.5 * cb.lambda)
49+
initCritOptDirection(crit, fun)
50+
}
51+
4352
# sets the opt.direction to minimize or maximize depending on the object function.
4453
initCritOptDirection = function(crit, fun) {
4554
if (crit$opt.direction == "objective") {

R/makeMBOInfillCrit.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#' \item{par.set [\code{ParamSet}]}{Parameter set.}
1616
#' \item{design [\code{data.frame}]}{Design of already visited points.}
1717
#' \item{iter [\code{integer(1)}]}{Current iteration.}
18+
#' \item{progress [\code{numeric{1}}]}{A value between 0 and 1 indicating the progress of the optimization.}
1819
#' \item{attributes [\code{logical{1}}]}{Are there attributes appended to the return
1920
#' value that should be added to the \code{OptPath}?}
2021
#' }
@@ -51,8 +52,7 @@ makeMBOInfillCrit = function(fun, name, id,
5152
requires.se = FALSE) {
5253
assertFunction(
5354
fun,
54-
args = c("points", "models", "control",
55-
"par.set", "design", "iter", "attributes"),
55+
args = c("points", "models", "control", "par.set", "design", "iter", "progress", "attributes"),
5656
ordered = TRUE)
5757

5858
assertString(name)

R/proposePointsByInfillOptimization.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ proposePointsByInfillOptimization = function(opt.state, par.set = NULL, control
2020
opt.path = coalesce(opt.path, getOptStateOptPath(opt.state))
2121
iter = getOptStateLoop(opt.state)
2222
infill.crit.id = getMBOInfillCritId(control$infill.crit)
23-
23+
progress = getOptStateProgress(opt.state)
2424
#FIXME: maybe better do this in setMBOControlMultifid?
2525
if (control$multifid) {
2626
infill.crit.id = "multifid"
@@ -40,12 +40,12 @@ proposePointsByInfillOptimization = function(opt.state, par.set = NULL, control
4040
secs = measureTime({
4141
prop.points = infill.opt.fun(infill.crit.fun, models = models,
4242
control = control, par.set = par.set, opt.path = opt.path,
43-
design = design, iter = iter, ...)
43+
design = design, iter = iter, progress = progress, ...)
4444
})
4545
prop.points.converted = convertDataFrameCols(prop.points, ints.as.num = TRUE,
4646
logicals.as.factor = TRUE)
4747
crit.vals = infill.crit.fun(prop.points.converted, models, control, par.set,
48-
design, iter, attributes = TRUE, ...)
48+
design, iter, progress = progress, attributes = TRUE, ...)
4949
crit.components = attr(crit.vals, "crit.components")
5050
crit.vals = matrix(crit.vals, ncol = 1L)
5151

R/setMBOControlTermination.R

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
#' termination condition is met.}
3232
#' \item{message [\code{character(1)}]}{Termination message. At the moment we just allow \code{term.custom}.}
3333
#' }
34+
#' @param use.for.adaptive.infill [\code{character(1)}|NULL]\cr
35+
#' Which termination criterion should determine the progress that is used for adaptive infill criteria like [\code{\link{makeMBOInfillCritAdaCB}}].
36+
#' The default is \code{NULL} which means, that the first supplied argument is taken, following the order of the function signature.
37+
#' Other values can be \code{"iters"}, \code{"time.budget"}, etc.\cr
38+
#' If you want to to use it together with a criterion you supplied in \code{more.termination.conds}, \code{more.termination.conds} has to be a named list and the function further has to return a list element \code{progress} with values between 0 and 1.
3439
#' @return [\code{\link{MBOControl}}].
3540
#' @family MBOControl
3641
#' @export
@@ -56,9 +61,10 @@
5661
#' res = mbo(fn, control = ctrl)
5762
#' print(res)
5863
setMBOControlTermination = function(control,
59-
iters = NULL, time.budget = NULL, exec.time.budget = NULL, target.fun.value = NULL, max.evals = NULL, more.termination.conds = list()) {
64+
iters = NULL, time.budget = NULL, exec.time.budget = NULL, target.fun.value = NULL, max.evals = NULL, more.termination.conds = list(), use.for.adaptive.infill = NULL) {
6065

6166
assertList(more.termination.conds)
67+
assertCharacter(use.for.adaptive.infill, null.ok = TRUE)
6268

6369
stop.conds = more.termination.conds
6470

@@ -68,39 +74,48 @@ setMBOControlTermination = function(control,
6874
}
6975

7076
if (!is.null(iters)) {
71-
stop.conds = c(stop.conds, makeMBOTerminationMaxIter(iters))
77+
stop.conds = c(stop.conds, iters = makeMBOTerminationMaxIter(iters))
7278
}
7379

7480
if (!is.null(time.budget)) {
75-
stop.conds = c(stop.conds, makeMBOTerminationMaxBudget(time.budget))
81+
stop.conds = c(stop.conds, time.budget = makeMBOTerminationMaxBudget(time.budget))
7682
}
7783

7884
if (!is.null(exec.time.budget)) {
79-
stop.conds = c(stop.conds, makeMBOTerminationMaxExecBudget(exec.time.budget))
80-
}
81-
82-
if (!is.null(max.evals)) {
83-
stop.conds = c(stop.conds, makeMBOTerminationMaxEvals(max.evals))
85+
stop.conds = c(stop.conds, exec.time.budget = makeMBOTerminationMaxExecBudget(exec.time.budget))
8486
}
8587

8688
if (!is.null(target.fun.value)) {
8789
if (control$n.objectives > 1L)
8890
stop("Specifying target.fun.value is only useful in single-objective optimization.")
89-
stop.conds = c(stop.conds, makeMBOTerminationTargetFunValue(target.fun.value))
91+
stop.conds = c(stop.conds, target.fun.value = makeMBOTerminationTargetFunValue(target.fun.value))
9092
}
9193

94+
if (!is.null(max.evals)) {
95+
stop.conds = c(stop.conds, max.evals = makeMBOTerminationMaxEvals(max.evals))
96+
}
97+
98+
9299
# sanity check termination conditions
93100
lapply(stop.conds, function(stop.on) {
94101
assertFunction(stop.on, args = "opt.state")
95102
})
96103

104+
# sanity check, whether use.for.adaptive.infill was set to an active criterion
105+
if (is.null(use.for.adaptive.infill)) {
106+
use.for.adaptive.infill = names(stop.conds)[1]
107+
} else {
108+
assertSubset(use.for.adaptive.infill, names(stop.conds))
109+
}
110+
97111
control$stop.conds = stop.conds
98112

99113
# store stuff in control object since it is needed internally
100114
control$iters = coalesce(iters, control$iters, Inf)
101115
control$time.budget = coalesce(time.budget, control$time.budget, Inf)
102116
control$exec.time.budget = coalesce(exec.time.budget, control$exec.time.budget, Inf)
103117
control$max.evals = coalesce(max.evals, Inf)
118+
control$use.for.adaptive.infill = use.for.adaptive.infill
104119

105120
return(control)
106121
}

0 commit comments

Comments
 (0)