randomsearch

The package randomsearch optimizes a given smoof function. Use makeSingleObjectiveFunction() to wrap any function into a smoof function. It can make use of parallel resources through prallelMap. randomsearch has three internal operating modes:

Usage

Simple usage with functions that use a vector as input:

fun = function(x) x[1]^2 + sin(x[2])
res = randomsearch(fun, lower = c(-1, -1), upper = c(2,2), minimize = TRUE, max.evals = 30)
rs = summary(res)
rs$best.x
## $x
## [1] -0.2617368 -0.8738214
rs$best.y
##          y 
## -0.6982813
tail(as.data.frame(res))
##            x1         x2          y dob eol exec.time
## 25 -0.2020821  1.5734831 1.04083356  25  NA         0
## 26 -0.8625065  0.3266002 1.06474229  26  NA         0
## 27  1.3967745 -0.6343022 1.35836350  27  NA         0
## 28  0.6828440 -0.3804058 0.09497854  28  NA         0
## 29 -0.6174050  1.2599236 1.33325597  29  NA         0
## 30  1.6851361  0.1233883 2.96275907  30  NA         0

Discrete and hierarchical search spaces

For usage with more complicated search spaces you have to define a smoof function. This allows you to tune over discrete parameters and even hierarchical parameter sets like in this example. Here the parameter y is only active if a==a2.

fun = function(x) {
  if (x$a == "a1") {
    x$x^2
  } else if (x$a == "a2") {
    sin(x$x * x$z) + 1
  }
}
obj.fun = makeSingleObjectiveFunction(
  fn = fun,
  par.set = makeParamSet(
    makeDiscreteParam("a", values = c("a1", "a2")),
    makeNumericParam("x", lower = -3, upper = 3),
    makeNumericParam("z", lower = 0, upper = 2, requires = quote(a == "a2"))
  ),
  minimize = TRUE,
  has.simple.signature = FALSE
)
res = randomsearch(obj.fun, max.evals = 30)
rs = summary(res)
rs$best.x
## $a
## [1] "a2"
## 
## $x
## [1] -2.430956
## 
## $z
## [1] 0.7679393
rs$best.y
##          y 
## 0.04349791
tail(as.data.frame(res))
##     a          x         z         y dob eol exec.time
## 25 a2  1.4224664 1.0422715 1.9961129  25  NA         0
## 26 a2  1.9308328 1.5725631 1.1050422  26  NA         0
## 27 a2 -0.3634108 0.6234044 0.7753811  27  NA         0
## 28 a1 -2.9371973        NA 8.6271281  28  NA         0
## 29 a2 -1.6130293 0.4781999 0.3028959  29  NA         0
## 30 a1 -1.5256579        NA 2.3276321  30  NA         0

Parallel Usage

Note: For Windows use parallelStartSocket().

fun = function(x) {
  Sys.sleep(runif(1))
  x[1]^2 + sin(x[2])
}

parallelMap::parallelStartMulticore(cpus = 2, level = "randomsearch.feval")
res = randomsearch(fun, lower = c(-1, -1), upper = c(2,2), minimize = TRUE, max.execbudget = 2, max.evals = 1000)
parallelMap::parallelStop()
summary(res)

Multi-objective optimization

Note: randomsearch() detects a multi-objective optimization by the length of the minimize vector.

obj.fun = function(x) c(x[1]^2 + sin(x[2]), cos(x[1]))
res = randomsearch(obj.fun, lower = c(-1, -1), upper = c(2,2), minimize = c(TRUE, TRUE), max.evals = 30)
summary(res)
## Randomsearch Result: 
## Multiobjective Search Pareto Front 
##           y_1         y_2         x1          x2
## 1  -0.2266606  0.88324220 -0.4880643 -0.48348476
## 3  -0.2274047  0.94463890  0.3343040 -0.34602799
## 4  -0.2309050  0.98662663  0.1637271 -0.26065302
## 6   0.1701176  0.75456461  0.7158059 -0.34932171
## 8   0.3583286  0.62947484  0.8899192 -0.44851453
## 10  1.0262387  0.58171931  0.9499555  0.12414187
## 14  1.0618198  0.43129294  1.1248710 -0.20494658
## 15  1.1604827  0.34270358  1.2210031 -0.33669119
## 16  2.2456680  0.05147028  1.5193033 -0.06265551
## 21  3.3156845 -0.05322554  1.6240470  0.74525030
## 22  3.6721829 -0.23881557  1.8119423  0.39959811
## 29  4.5873757 -0.37660278  1.9569227  0.85997993

Of course multi-objective optimization works with smoof functions as well. A multi-objective smoof function is constructed as follows:

obj.fun = makeMultiObjectiveFunction(
  fn = function(x) c(x[1]^2 + sin(x[2]), cos(x[1])),
  par.set = makeNumericParamSet(len = 2, lower = -1, upper = 2),
  minimize = c(TRUE, TRUE)
)
res = randomsearch(obj.fun, max.evals = 10)

Augment random search

You can simply augment the results of a random search with additional random search results by converting the old result to a data.frame and use it as an input for a new random search.

fun = function(x) x[1]^2 + sin(x[2])
res = randomsearch(fun, lower = c(-1, -1), upper = c(2,2), minimize = TRUE, max.evals = 10)
summary(res)
## Randomsearch Result: 
## Eval no.: 9
## x: x=-0.228,-0.335
## y: -0.276282
des = as.data.frame(res)
res = randomsearch(fun, lower = c(-1, -1), upper = c(2,2), minimize = TRUE, max.evals = 1000, target.fun.value = -0.4)
summary(res)
## Randomsearch Result: 
## Eval no.: 243
## x: x=-0.0328,-0.996
## y: -0.8384554