SPOTVignetteNutshell

SPOT in a Nutshell

Setup

## install.packages("devtools")
## devtools::install_github("r-lib/devtools")
url <- "http://owos.gm.fh-koeln.de:8055/bartz/spot.git"
devtools::install_git(url = url)
library("SPOT")
packageVersion("SPOT")
#> [1] '2.1.10'

Introduction

install.packages("SPOT")

library("SPOT")

Levels During Tuning and Optimization

Algorithm Tuning

  • Algorithm tuning is also referred to as off-line parameter tuning in contrast to on-line parameter tuning~.
  • Algorithm tuning involves the following three levels, which describe the experimental setup.
  • Consider the following Figure:
    • The three levels that occur when tuning an optimization algorithm with SPOT. This setting will be referred to as algorithm tuning.
    • SPOT can be applied as an optimizer. In this case, SPOT tries to find arguments of the objective function that result in an optimal function value. Following the taxonomy introduced in~, this setting will be referred to as surrogate model based optimization.

  • (L1) The real-world system. This system allows the specification of an objective function, say \(f\). As an example, we will use the sphere function in the following.

  • (L2) The optimization algorithm, here SANN. It requires the specification of algorithm parameters.

  • (L3) The tuning algorithm, here SPOT.

  • An optimization algorithm (L2) requires parameters, e.g., the initial temperature of SANN or the mutation rate of evolution strategies.

  • These parameters determine the performance of the optimization algorithm.

  • Therefore, they should be tuned. The algorithm is in turn used to determine optimal values of the objective function \(f\) from level (L1).

  • The term algorithm design summarizes factors that influence the behavior (performance) of an algorithm, whereas problem design refers to factors from the optimization (simulation) problem.

  • The initial temperature in SANN is one typical factor which belongs to the algorithm design, the search space dimension belongs to the problem design.

Surrogate Model Based Optimization

  • Instead of tuning an optimization algorithm, SPOT itself can be used as a surrogate model based optimization algorithm.
  • Then, SPOT has the same role as SANN in the algorithm tuning scenario.
  • In this the surrogate model based optimization setting, only two levels exist:
    1. the objective function (L1) and
    2. the optimization algorithm (L2).
  • This situation is seen on the right hand side of Figure~.

The SPOT Algorithm

  • SPOT finds improved solutions in the following way (see the following pseudo code):
    • Initially, a population of (random) solutions is created. The initialization step is shown in State 1 in the following pseudo code of the SPOT algorithm.
    • A set of surrogate models is specified (Step 2).
    • Then, the solutions are evaluated on the objective function (level L1). This is State 3.
    • Next, surrogate models are built (State 4).
    • A global search is performed to generate new candidate solutions (State 5).
    • The new solutions are evaluated on the objective function (level L1) (State 6).
  • These steps are repeated, until a satisfying solution has been found.

Sequential parameter optimization

  • State 1: \(t=0\). \(P(t) =\) SetInitialPopulation().
  • State 2: Select one or several surrogate models \(\mathfrak{M}\).
  • State 3: Evaluate(\(P(t)\)) on \(f\).
  • While{not TerminationCriterion()}
    • State 4: Use \(P(t)\) to build a model \(M(t)\) using \(\mathfrak{M}\).
    • State 5: $P’(t+1) = $ GlobalSearch(\(M(t)\)).
    • State 6: Evaluate(\(P'(t+1)\)) on \(f\).
    • State 7: \(P(t+1) = P(t) \cup P'(t+1)\).
    • State 8: \(t = t+1\).
  • EndWhile

Level L1: Objective Function

sphere <- function (x){
  sum(x^2)
  }
sphere( c(1,2) )
#> [1] 5
funSphere
#> function (x) 
#> {
#>     matrix(apply(x, 1, function(x) {
#>         sum(x^2)
#>     }), , 1)
#> }
#> <bytecode: 0x7fd801e7c370>
#> <environment: namespace:SPOT>
function (x) 
{
    matrix(apply(x, 1, function(x) {
        sum(x^2)
    }), , 1)
}
plotFunction(funSphere)

Level L2: Simulated Annealing SANN

optim(par, fn, gr = NULL, ..., method = c("Nelder-Mead", "BFGS", "CG", "L-BFGS-B", "SANN", "Brent"), lower = -Inf, upper = Inf, control = list(), hessian = FALSE)

set.seed(123)
resSANN <- optim(c(10,10), sphere, method="SANN",
                control=list(maxit=100, temp=10, tmax = 10))
resSANN
#> $par
#> [1] 4.835178 4.664964
#> 
#> $value
#> [1] 45.14084
#> 
#> $counts
#> function gradient 
#>      100       NA 
#> 
#> $convergence
#> [1] 0
#> 
#> $message
#> NULL
set.seed(123)
resSANN <- optim(par = c(10,10), fn = sphere, method="SANN",
                control = list(maxit = 100, temp = 20, tmax = 5))
resSANN
#> $par
#> [1] 6.163905 6.657100
#> 
#> $value
#> [1] 82.3107
#> 
#> $counts
#> function gradient 
#>      100       NA 
#> 
#> $convergence
#> [1] 0
#> 
#> $message
#> NULL

Level L3: SPOT

Example: Using Random Forest

  • First, the problem setup for level L1 has to be specified.
    • To keep the situation as simple as possible, we will use the sphere() test function, which was introduced above.
    • The problem design requires the specification of the starting point x0 for the search.
x0 =  c(-1,1,-1) 
* Since `x0` has three elements, we are facing a three dimensional optimization problem.
* `SANN` will be used to determine its minimum function value.
  • Secondly, the problem setup for level L2 has to be defined.
    • Again, several settings have to be specified for the SANN algorithm to be tuned.
    • The budget, i.e., the maximum number of function evaluations that can be used by SANN is specified via maxit:
maxit = 100 
  • As above, the R implementation of SANN will be used via the optim() function.
  • We will consider two parameters:
    1. the initial temperature (temp) and
    2. the number of function evaluations at each temperature (tmax).
  • Both are integer values.
  • All parameters and settings of SANN, which were used for this simple example are summarized in the following table.

SANN parameters

  • The first two parameters belong to the algorithm design, whereas the remaining parameters are from the problem design.
  • Note, the starting point defines the problem dimension, i.e., by specifying a three dimensional starting point the problem dimension is set to three.
  • The initial seed is the value that the RNG is initialized with.
Name Symbol Factor name
Initial temperature \(t\) temp
Number of function evaluations at each temperature $t_{} $ tmax
Starting point \(\vec{x_0} = (-1,1,-1)\) x0
Problem dimension \(n=3\)
Objective function sphere sphere()
Quality measure Expected performance, e.g., \(E(y)\) y
Initial seed \(s\) 1
Budget \(\textrm{maxit} = 100\) maxit
  • Thirdly, the tuning procedure at level L3 has to be specified.

An interface to SANN+Sphere for SPOT

  • To interface SANN with SPOT, the wrapper function sann2spot() is used.
  • Note, SPOT uses matrices as the basic data structure.
    • The matrix format was chosen as a compromise between speed and flexibility.
  • The matrix() command can be used as follows:

matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)

  • The interface function receives a matrix where each row is proposed parameter setting (temp, tmax), and each column specifies the parameters.
  • It generates a \((n,1)\)-matrix as output, where \(n\) is the number of (temp, tmax) parameter settings.
sann2spot <- function(algpar){
  performance <- NULL
  for (i in 1:nrow(algpar)){
    resultList <- optim(par = c(10,10),
                    fn = sphere,
                    method = "SANN",
                    control = list(maxit = 100,
                                  temp = algpar[i,1],
                                  tmax = algpar[i,2]))
    performance <- c(performance,resultList$value)
    }
return(matrix(performance,,1))
}
  • Now we can test the interface. First, we run SANN with temp = 10 and tmax = 10.
  • A second SANN run is performed using temp = 20 and tmax = 5.
set.seed(123)
sann2spot(algpar = matrix(c(10,10),1))
#>          [,1]
#> [1,] 45.14084
set.seed(123)
sann2spot(algpar=matrix(c(5,20),1))
#>          [,1]
#> [1,] 4.469163

The Configuration List

  • SPOT itself has various parameters that need to be configured so that it can solve the tuning problem efficiently.
  • A configuration or control list can be defined for SPOT.
  • If no configuration list is specified, the default configuration that works fine for many problems, is used.
  • In the following, some elements of the configuration list that are important for our example (tuning SANN + sphere()) will be explained.

types:

  • Since SANN’s parameters temp and tmax are integers, we provide this type information via types = c("integer", "integer").

funEvals:

  • The number of algorithm runs, i.e., runs of the SANN algorithm, is specified via funEvals.

noise:

  • SANN is a stochastic optimizer, so we specify noise = TRUE.

seedFun:

  • Also due to the stochasticity of the optimizer, a random number generator seed has to be specified, seedFun = 1.
  • Every time an algorithm configuration is tested, the RNG seed will be set.
  • For the first evaluation, the seed will be seedFun, subsequent evaluations will increment this value.

replicates:

  • Since our evaluations are subject to noise, we can make replicates to mitigate it.
  • In our example, each algorithm configuration will be evaluated twice, so the setting replicates = 2 is used.

seedSPOT:

  • An additional seed for SPOT can be specified using seedSPOT = 1.
  • This second seed is only set once in the beginning. This ensures that the SPOT run is reproducible, since SPOT itself may also be of a stochastic nature (depending on the configuration).

design:

  • design parameter defines the method to be used to generate an initial design (a set of initial algorithm settings (here: a number of pairs of temp and tmax).
  • A Latin hyper cube design (LHD) is specified via design = designLHD.

model:

  • Based on the initial design and subsequent evaluations, a model can be trained to learn the relation between algorithm parameters (temp,tmax) and algorithm performance.
  • To generate the meta model, we use a random forest implementation~.
  • This can be specified via model = buildRandomForest.
  • Random forest was chosen, because it is a robust method which can handle categorical and numerical variables.

optimizer:

  • Once a meta modelis trained, we need an optimizer to find the best potential algorithm configuration, based on the model.
  • Here, we choose a very simple optimizer, which creates a large LHD and evaluates it on the model: optimizer = optimLHD.

optimizerControl:

  • The specified optimizer may have options that need to be set.

  • Here, we only specify the number of model evaluations to be performed by the optimizer with optimizerControl = list(funEvals=1000).

  • Overall, we obtain the following configuration:

spotConfig <- list(
  types = c("integer", "integer"), #data type of tuned parameters
  funEvals = 50, #maximum number of SANN runs
  noise = TRUE, #problem is noisy (SANN is non-deterministic)
  seedFun = 1, #RNG start seed for algorithm calls (iterated)
  replicates = 2, #2 replicates for each SANN parameterization
  seedSPOT = 1, #main RNG
  design = designLHD, #initial design: Latin Hypercube
  model = buildRandomForest, # model = buildKriging Kriging surrogate model
  optimizer = optimLHD, #Use LHD to optimize on model 
  optimizerControl = list(funEvals=100) #100 model evals in each iteration  
)

The Region of Interest

tempLo = 1
tempHi = 100
tmaxLo = 1
tmaxHi = 100
lower=c(tempLo,tmaxLo)
upper=c(tempHi,tmaxHi)

Calling spot()

# library(SPOT)
# source('~/workspace/SPOT/R/spot.R')
# source('~/workspace/SPOT/R/initialInputCheck.R')
resRf <- spot(x=NULL,
              fun=sann2spot,
              lower=lower,
              upper=upper,
              control=spotConfig)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
is.null(spotConfig$optimizerControl$eval_g_ineq)
#> [1] TRUE
str(resRf)
#> List of 7
#>  $ xbest   : num [1, 1:2] 3 82
#>  $ ybest   : num [1, 1] 0.00123
#>  $ x       : num [1:50, 1:2] 9 72 45 15 26 57 70 93 33 88 ...
#>  $ y       : num [1:50, 1] 0.226 28.891 59.309 0.192 2.864 ...
#>  $ count   : int 50
#>  $ msg     : chr "budget exhausted"
#>  $ modelFit:List of 3
#>   ..$ rfFit:List of 17
#>   .. ..$ call           : language randomForest(x = x, y = y)
#>   .. ..$ type           : chr "regression"
#>   .. ..$ predicted      : num [1:49] 0.458 29.661 24.833 31.003 23.679 ...
#>   .. ..$ mse            : num [1:500] 360 410 327 188 133 ...
#>   .. ..$ rsq            : num [1:500] -0.00606 -0.14405 0.08703 0.47442 0.62732 ...
#>   .. ..$ oob.times      : int [1:49] 182 193 179 191 188 201 188 176 174 182 ...
#>   .. ..$ importance     : num [1:2, 1] 7925 7835
#>   .. .. ..- attr(*, "dimnames")=List of 2
#>   .. .. .. ..$ : chr [1:2] "1" "2"
#>   .. .. .. ..$ : chr "IncNodePurity"
#>   .. ..$ importanceSD   : NULL
#>   .. ..$ localImportance: NULL
#>   .. ..$ proximity      : NULL
#>   .. ..$ ntree          : num 500
#>   .. ..$ mtry           : num 1
#>   .. ..$ forest         :List of 11
#>   .. .. ..$ ndbigtree    : int [1:500] 7 11 9 9 9 11 7 7 7 5 ...
#>   .. .. ..$ nodestatus   : int [1:17, 1:500] -3 -3 -1 -3 -1 -1 -1 0 0 0 ...
#>   .. .. ..$ leftDaughter : int [1:17, 1:500] 2 4 0 6 0 0 0 0 0 0 ...
#>   .. .. ..$ rightDaughter: int [1:17, 1:500] 3 5 0 7 0 0 0 0 0 0 ...
#>   .. .. ..$ nodepred     : num [1:17, 1:500] 5.288 0.241 49.701 0.18 2.864 ...
#>   .. .. ..$ bestvar      : int [1:17, 1:500] 1 1 0 2 0 0 0 0 0 0 ...
#>   .. .. ..$ xbestsplit   : num [1:17, 1:500] 48 19 0 82.5 0 0 0 0 0 0 ...
#>   .. .. ..$ ncat         : num [1:2] 1 1
#>   .. .. ..$ nrnodes      : int 17
#>   .. .. ..$ ntree        : num 500
#>   .. .. ..$ xlevels      :List of 2
#>   .. .. .. ..$ : num 0
#>   .. .. .. ..$ : num 0
#>   .. ..$ coefs          : NULL
#>   .. ..$ y              : num [1:49, 1] 0.226 28.891 59.309 0.192 2.864 ...
#>   .. ..$ test           : NULL
#>   .. ..$ inbag          : NULL
#>   .. ..- attr(*, "class")= chr "randomForest"
#>   ..$ x    : num [1:49, 1:2] 9 72 45 15 26 57 70 93 33 88 ...
#>   ..$ y    : num [1:49, 1] 0.226 28.891 59.309 0.192 2.864 ...
#>   ..- attr(*, "class")= chr "spotRandomForest"
cbind(resRf$xbest, resRf$ybest)
#>      [,1] [,2]        [,3]
#> [1,]    3   82 0.001232935
resRf$xbest[1]
#> [1] 3

and tmax =

resRf$xbest[2] 
#> [1] 82

Details 1: The spot() Interface

subsubsection{Arguments}

  • SPOT uses the same interface as R’s standard optim() function, which uses the arguments reported in the following Table:
name description
x Optional start point (or set of start points), specified as a matrix. One row for each point, and one column for each optimized parameter.
fun Objective function. It should receive a matrix x and return a matrix y. In case the function uses external code and is noisy, an additional seed parameter may be used, see the control$seedFun argument in the function documentation for details.
lower Vector that defines the lower boundary of search space
upper Vector that defines the upper boundary of search space
control List of additional settings
  • Note, take care of consistency of upper, lower and, if specified, x.
    • In cases of inconsistency, the dimension of the variable lower will be taken into account to establish the dimension of the problem.

Return Values

  • The function spot() returns a list with the values shown in the following Table.
name description type
xbest Parameters of the best found solution matrix
ybest Objective function value of the best found solution matrix
x Archive of all evaluation parameters matrix
y Archive of the respective objective function values matrix
count Number of performed objective function evaluations integer
msg Message specifying the reason of termination character
modelFit The fit of the model from the last SPOT iteration, i.e., an object returned by the last call to the function specified by control$model list

Details 2: SPOT Configuration

name description default
funEvals Budget of function evaluations (spot uses no more than funEvals evaluations of fun). 20
types Vector of data type of each variable as a string. "numeric"
design A function that creates an initial design of experiment. Functions that accept the same parameters, and return a matrix like designLHD or designUniformRandom can be used. designLHD
designControl List of controls passed to the control list of the design function. empty list
model Function that builds a model of the observed data. Functions that accept the same parameters, and return a matrix like buildKriging or buildRandomForest can be used. buildKriging
modelControl List of controls passed to the control list of the model function. empty list
optimizer Function that is used to optimize the model, finding the most promising candidate solutions. Functions that accept the same parameters, and return a matrix like optimLHD or optimLBFGSB can be used. optimLHD
optimizerControl List of controls passed to the control list of the optimizer function. empty list
noise Boolean, whether the objective function has noise. FALSE
OCBA Boolean, indicating whether Optimal Computing Budget Allocation (OCBA) should be used in case of a noisy objective function. OCBA controls the number of replications for each candidate solution. Note, that replicates should be larger than one in that case, and that the initial experimental design (see design) should also have replicates larger than one. FALSE
OCBAbudget Number of objective function evaluations that OCBA can distribute in each iteration. 3
replicates Number of times a candidate solution is initially evaluated, that is, in the initial design, or when created by the optimizer. 1
seedFun Initial seed for the objective function in case of noise. The default means that no seed is set. The user should be very careful with this setting. It is intended to generate reproducible experiments for each objective function evaluation, e.g., when tuning non-deterministic algorithms. If the objective function uses a constant number of random number generations, this may be undesirable. Note, that this seed is by default set prior to each evaluation. A replicated evaluation will receive an incremented value of the seed. Sometimes, the user may want to call external code using random numbers. To allow for that case, the user can specify an objective function (fun), which has a second parameter seed, in addition to first parameter (matrix x). This seed can then be passed to the external code, for random number generator initialization. See end of examples section in the documentation of SPOT for a demonstration. NA
seedSPOT Value used to initialize the random number generator. It ensures that experiments are reproducible. 1
duplicate In case of a deterministic (non-noisy) objective function, this handles duplicated candidate solutions. By default (duplicate = "EXPLORE"), duplicates are replaced by new candidate solutions, generated by random sampling with uniform distribution. If desired, the user can set this to "STOP", which means that the optimization stops and results are returned to the user (with a warning). This may be desirable, as duplicates can be a indicator of convergence, or problems with the configuration. In case of noise, duplicates are allowed regardless of this parameter. "EXPLORE"
plots Logical. Should the progress be tracked by a line plot? FALSE

Details 3: Initial Designs

Example: Modifying the number of function evaluations

  • For example,
    • the total number of SANN algorithm runs, i.e., the available budget, can be set to 10 using `funEvals = 10},
    • the size of the initial design can be modified via designControl$size,
    • the number of repeated evaluations of the initial design can be modified via designControl$replicates, and
    • the number of replicates used for each new algorithm design point suggested by SPOT can be modified via replicates.
spotConfig10 <- list(
  funEvals = 10,
  designControl = list(
    size = 6,
    replicates = 1
  ),
  noise = TRUE,
  seedFun = 1,
  seedSPOT = 1,
  replicates = 2,
  model = buildRandomForest 
)
  • Using this configuration, the budget will be spent as follows:
    • Six initial algorithm design points (parameter sets) will be created, and each will be evaluated just once.
    • Afterwards, SPOT will have a remaining budget of four evaluations. These can be spent on sequentially testing two additional design points. Each of those will be evaluated twice.
res10 <- spot( ,fun=sann2spot
              ,lower=lower
              ,upper=upper
              ,control=spotConfig10)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
  • Results from these runs can be displayed as follows.
    • The first two columns show the settings of the algorithm parameters temp and tmax, respectively.
    • The third row shows the corresponding function values:
cbind(res10$x, res10$y)
#>            [,1]      [,2]         [,3]
#>  [1,]  8.954322 63.418391 2.935315e-01
#>  [2,] 93.392836 43.125099 1.073285e+02
#>  [3,] 25.643432 26.240373 1.614929e+01
#>  [4,] 70.072590 96.524378 1.129012e+01
#>  [5,] 47.651660 67.384965 3.933353e+00
#>  [6,] 61.529701  8.874296 7.312226e+01
#>  [7,] 12.121097 80.404416 8.496108e-03
#>  [8,] 12.121097 80.404416 4.645919e-01
#>  [9,]  8.156284 79.499694 3.392544e-01
#> [10,]  8.156284 79.499694 2.126833e-02
  • The results show the desired outcome: the first six configurations are unique, the following four contain replications (two identical settings, but with different stochastic result).

Initial Designs

designLHD() is the default setting to generate designs. A simple one-dimensional design with values from the interval \([-1, 1]\) can be generated as follows:

designLHD(,-1,1) 
#>              [,1]
#>  [1,]  0.33411001
#>  [2,]  0.46001819
#>  [3,] -0.75700111
#>  [4,]  0.69957356
#>  [5,] -0.06232477
#>  [6,]  0.92854939
#>  [7,] -0.32472347
#>  [8,] -0.97695432
#>  [9,]  0.09826695
#> [10,] -0.54409864
  • A more complicated design, which consists of numeric values for the first variable in the range from -1 to +1, of integer values for the second variable in the range from -2 to 4, of integer values for the third variable in the range from 1 to 9, and with two factors 0 and 1 for the fourth variable, can be generated as follows:
designLHD(, c(-1,-2,1,0),c(1,4,9,1)
          , control=list(size=5, retries=100, types=c("numeric","integer","factor","factor")))
#>             [,1] [,2] [,3] [,4]
#> [1,]  0.99895818    0    4    1
#> [2,] -0.83535083   -1    7    1
#> [3,] -0.30061528    4    2    1
#> [4,]  0.56058677    2    9    0
#> [5,] -0.05172332    0    3    0
  • Designs can also be combined as illustrated in the following example.
set.seed(123)
x1 <- designLHD(,c(-1,-1),c(1,1),control=list(size=50,retries=100))
x2 <- designLHD(x1,c(-2,-2),c(2,2),control=list(size=50,retries=100))
  • The corresponding plot shows the resulting design.
plot(x2,pch=1)
points(x1, pch=4)
  • Designs based on uniform random sampling can be generated with the function designUniformRandom() as follows:
designUniformRandom(,c(-1,0),c(1,10),control=list(size=5))
#>            [,1]     [,2]
#> [1,]  0.3480606 3.447254
#> [2,] -0.3917176 2.413147
#> [3,]  0.7143919 8.768452
#> [4,] -0.4602907 1.681719
#> [5,] -0.5792664 9.799514

Details 4: Models

Kriging Models

  • As default, the buildKriging() function is used for modeling.
  • This function builds a Kriging model (also known as Gaussian process regression) loosely based on code by .
  • Kriging models are based on measures of similarity, or kernels.
  • Here, a Gaussian kernel is used: \[ k(x,x') = \exp \left(-\sum^n_{i=1} \theta_i |x_i-x'_i|^{p_i} \right). \]
  • By default exponents are fixed at a value of two, \(p_i = 2\), (\(i=1,\ldots,n\)), and the nugget effect (or regularization constant) is used.
  • To correct the uncertainty estimates in case of using the nugget effect, re-interpolation is also by default turned on (see for more details on these features of the model).

Example: SPOT as a surrogate model based algorithm

  • The following code exemplifies how a Kriging model is built with an artificial data set.
  • Note, this example exemplifies how SPOT can be used as a surrogate model based optimization algorithm, i.e., no algorithm is tuned.
# Objective function
braninFunction <- function (x) {
    (x[2]  - 5.1/(4 * pi^2) * (x[1] ^2) + 5/pi * x[1]  - 6)^2 + 10 * (1 - 1/(8 * pi)) * cos(x[1] ) + 10
}
## Create 20 design points
set.seed(1)
x <- cbind(runif(20)*15-5, runif(20)*15)
## Compute observations at design points (for Branin function)
y <- as.matrix(apply(x,1,braninFunction))
## Create model with default settings
fit <- buildKriging(x,y,control = list(algTheta=optimLHD))
## Print model parameters
print(fit)
#> ------------------------
#> Forrester Kriging model.
#> ------------------------
#> Estimated activity parameters (theta) sorted 
#> from most to least important variable 
#> x1  x2 
#> 7.575502 1.361329
#>  
#> exponent(s) p:
#> 2
#>  
#> Estimated regularization constant (or nugget) lambda:
#> 5.239774e-06
#>  
#> Number of Likelihood evaluations during MLE:
#> 600
#> ------------------------
##Define a new location
newloc <- matrix(c(1,2),nrow =1 )
##Predict at new location
predict(fit,newloc)
#> $y
#> [1] 21.08753
## True value at location
braninFunction(newloc)
#> [1] 21.62764
## 

Handling Factor Variables in the Kriging Model

braninFunctionFactor <- function (x) {  
y <- (x[2] - 5.1 / (4 * pi^2) * (x[1]^2) + 5 / pi * x[1] - 6)^2 + 10 * (1 - 1 / (8 * pi)) * cos(x[1]) + 10
    if(x[3] == 1)
        y <- y + 1
    else if(x[3]==2)
        y <- y - 1
    y  
}
set.seed(1)
## Replace x with new data
x <- cbind(runif(50)*15-5,runif(50)*15,sample(1:3,50,replace=TRUE))
##
y <- as.matrix(apply(x,1,braninFunctionFactor))
fitDefault <- buildKriging(x,y,control = list(algTheta=optimLBFGSB))
fitFactor <- buildKriging(x,y,control = list(algTheta=optimLBFGSB,types=c("numeric","numeric","factor")))
##Replace xtest with new data
xtest <- cbind(runif(200)*15-5,runif(200)*15,sample(1:3,200,replace=TRUE))
##
ytest <- as.matrix(apply(xtest,1,braninFunctionFactor))
## Predict test data with both models, and compute error
ypredDef <- predict(fitDefault,xtest)$y
ypredFact <- predict(fitFactor,xtest)$y
mean((ypredDef-ytest)^2)
#> [1] 4.099175
mean((ypredFact-ytest)^2)
#> [1] 2.094953

Details 5: Optimization on the Meta Model

resOptimumLHD <- optimLHD(,fun = funSphere,lower = c(-10,-20),upper=c(20,8))
str(resOptimumLHD)
#> List of 6
#>  $ x    : num [1:100, 1:2] -5.528 -6.509 0.718 -4.327 15.608 ...
#>  $ y    : num [1:100, 1] 48.8 62.5 295.3 260.7 366.2 ...
#>  $ xbest: num [1, 1:2] 0.136 -0.558
#>  $ ybest: num [1, 1] 0.33
#>  $ count: num 100
#>  $ msg  : chr "success"
resOptimumLHD$ybest
#>         [,1]
#> [1,] 0.32992
resOptimBFGS <- optimLBFGSB(,fun = funSphere,lower = c(-10,-20),upper=c(20,8))
resOptimBFGS$ybest
#> [1] 2.098584e-40

Details 6: SPOT Loop: Continued Evaluation

Example: SPOT with continued evaluation

  • The surrogate model based optimization setting will be used to exemplify the continued evaluation.
  • The two dimensional sphere function is used as an objective function (level L1) and SPOT will be used at level L2.
  • SPOT uses 5 function evaluations.
control01 <- list(
    designControl = list(size = 5, 
                       replicates = 1),    
    funEvals = 5)
res1 <- spot(,funSphere,
             lower = c(-2,-3),
             upper = c(1,2),
             control01)
cbind(res1$x, res1$y)
#>            [,1]       [,2]     [,3]
#> [1,] -0.8963358  0.2026923 0.844502
#> [2,] -1.7919899 -1.2888788 4.872436
#> [3,]  0.6002650 -0.8783081 1.131743
#> [4,] -0.5141893 -2.7545115 7.851724
#> [5,]  0.3353190  1.1433044 1.419584
  • Now, we continue with a larger budget.
  • If we would like to add 3 function evaluations, the total number of function evaluations is \(5+3=8\).
  • To continue a SPOT run, the command spotLoop() can be used as follows:
    • spotLoop(x, y, fun, lower, upper, control, ...).
  • The arguments are:
    • x: the known candidate solutions that the SPOT loop is started with, specified as a matrix. One row for each point, and one column for each optimized parameter.
    • y: the corresponding observations for each solution in x, specified as a matrix. One row for each point.
    • fun: is the objective function. It should receive a matrix x and should return a matrix y.
    • lower: is the vector that defines the lower boundary of search space. This determines also the dimensionality of the problem.
    • upper: is the vector that defines the upper boundary of search space.
    • control: is the list with control settings for spot.
    • ...: additional parameters passed to fun.
control01$funEvals <- 8
res2 <- spotLoop(res1$x,
                 res1$y,
                 funSphere,
                 lower = c(-2,-3),
                 upper = c(1,2),
                 control01)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
cbind(res2$x, res2$y)
#>            [,1]        [,2]       [,3]
#> [1,] -0.8963358  0.20269226 0.84450200
#> [2,] -1.7919899 -1.28887878 4.87243633
#> [3,]  0.6002650 -0.87830808 1.13174310
#> [4,] -0.5141893 -2.75451149 7.85172411
#> [5,]  0.3353190  1.14330438 1.41958374
#> [6,]  0.6450130  0.20991043 0.46010412
#> [7,]  0.7041126 -0.08754435 0.50343851
#> [8,] -0.2917148  0.07870320 0.09129173

Exploratory Fitness Landscape Analysis

Introduction to SPOT’s Plot Functions

plotFunction

  • The function plotFunction() visualizes the fitness landscape.
  • It generates a (filled) contour plot or perspective / surface plot of a function f().
  • It has several options for changing plotting styles, colors etc., but default values should be fine in many cases.
    • The basic arguments are: plotFunction(f , lower , upper , type)

Example: Plot of the sphere function

  • The following code generates a 2D filled contour plot of the sphere function.
  • Note that plotFunction() requires a function that handles matrix objects, so funSphere() is used.
plotFunction(funSphere, rep(-1,2), rep(1,2)) 

Example: Plotting a user defined function

  • The following code examples show how user defined functions can be plotted. \[ f(\vec{x}) = \sum_{i=1}^n (x_i^3 -1). \]
  • It also illustrates how the color scheme can be modified.
myFunction <- function (x){
  matrix(apply(x, # matrix
               1, # margin (apply over rows)
               function(x) sum(x^3-1) # objective function
               ),
         , 1) # number of columns
  }
plotFunction(myFunction, 
             rep(-1,2), 
             rep(1,2), 
             color.palette = rainbow) 
  • We can also generate a perspective plot of the user defined function as shown in the following code.
plotFunction(myFunction,
             rep(-1,2), 
             rep(1,2), 
             type="persp", 
             theta=10, 
             phi=25, 
             border = NA)

plotModel()

  • Furthermore, plotModel() offers the possibility to visualize models that have already been trained, e.g., during a SPOT run.
  • Some simple examples are given below.

Example: Plotting a trained model

  • First, we generate some training data.
  • To demonstrate how plots from data with more than two input dimensions can be generated, we generate a three-dimensional input design, i.e., we consider a functional relationship of the type \[ f: \mathbb{R}^3 \to \mathbb{R}, \qquad y = f(x_1,x_2,x_3). \]
  • The output is one dimensional.
set.seed(123)
k <- 30
x.test <- designLHD(,rep(-1,3),rep(1,3), control = list(size = k)) 
y.test <- funSphere(x.test)
head( cbind(x.test, y.test))
#>            [,1]        [,2]        [,3]      [,4]
#> [1,]  0.6721562 -0.67693655 -0.06068276 0.9137194
#> [2,] -0.0813375  0.74944336  0.59109728 0.9176771
#> [3,]  0.5462156 -0.39765660 -0.09992404 0.4664671
#> [4,]  0.2437057 -0.29365065  0.21488375 0.1917982
#> [5,] -0.3043235  0.25574608 -0.30583872 0.2515562
#> [6,]  0.4131432  0.02414995  0.12575375 0.1870845
  • Then, we train a standard response surface using SPOT’s buildRSM() function.
  • We generate the default contour plot using plotModel().
fit.test <- buildRSM(x.test,y.test)
plotModel(fit.test)
  • Passing the argument type="contour" to the plotModel() function, a 2D contour plot can be generated as shown in the Figure.
  • By default, the dependent variable \(y\) is plotted against the first two \(x_i\) variables.
  • Note, that the argument which specifies the independent variables \(x_i\) that are plotted.
  • To plot \(y\) against \(x_1\) and \(x_3\), the argument which=c(1,3) can be used.
plotModel(fit.test,which=c(1,3),type="contour",pch1=24,col1="blue")
  • Perspective plots of the same model can be generated as follows. The arguments theta and phi can be used to modify the view point.
  • The figure can be generated with the following code:
plotModel(fit.test,which=c(1,3),type="persp",border="NA",theta=255,phi=20)

plotData()

  • Finally, using plotData(), different models built on provided data can be compared.
  • The plotData() function generates a (filled) contour or perspective plot of a data set with two independent and one dependent variable.
  • The plot is generated by some interpolation or regression model.
  • By default, the LOESS function is used.
  • Some simple examples are given below.

Example: Plotting data using LOESS and random forest

  • The following code shows a comparison of two different models fitting the same data.
  • First, the default LOESS model is used for interpolation.
plotData(x.test,y.test)
  • Then, the random forest was used for interpolation.
plotData(x.test,y.test,type="filled.contour",cex1=1,col1="red",pch1=21,model=buildRandomForest)

Example: Perspective plot

  • The following Figure shows a perspective plot, which is based on the same data that were used in the previous Example.
plotData(x.test,y.test,type="persp",border=NA,model=buildLOESS)

Exploratory Fitness Landscape Analysis Using SPOT

Plotting the Final Model with plotModel()

plotModel(resRf$modelFit)

Plotting the Data with plotData()

  • Results from Example with resRf result data were obtained with the random forest model.
  • But, the data can be fitted to a different model, e.g., a locally (weighted) scatter plot smoothing (LOESS) or Kriging model as follows.

Example: Plotting data using LOESS

  • The following code illustrates the `LOESS’ model fit, which uses the data generated with a random forest model.
plotData(resRf$x,resRf$y,model=buildLOESS)

Example: Plotting data using Kriging

  • Plotting the same data as in the previous Figure with a Kriging model can easily be done.
  • The result can be generated as followings.
plotData(resRf$x,resRf$y,model=buildKriging)
  • This result can be compared to a case in which Kriging models were used during the SPOT run and for the final illustration.
    • The same setting as in the random forest based optimization will be used.
    • Only the meta model will be changed.
    • The SPOT configuration parameters can be changed as follows:
spotConfig$model = buildKriging
spotConfig$optimizer = optimLBFGSB
spotConfig$modelControl = list(algTheta=optimLBFGSB)
## Run SPOT
resK <- spot(x=NULL,
             fun=sann2spot,
             lower=lower,
             upper=upper,
             control=spotConfig)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
  • Now, the Kriging model used during the SPOT run will be used to visualize the fitness landscape.
  • The following code can be used for visualization:
plotModel(resK$modelFit)
  • The landscape generated with the random forest based SPOT run and the landscape from the Example, which used the Kriging-based SPOT run, differ.
  • We can check if longer runs increase the similarities.

Continued Runs

  • The optimization procedure from Example which generated the resRf data, used 50 runs of the SANN algorithm.
  • Now this number is increased to 100 and the procedure is continued.
  • This means, an additional 50 evaluations are necessary since the results from the first runs are reused.

Example: Continued with 50 additional runs

  • The fitness landscape of the random forest meta model with 100 function evaluations is shown at the top in the Figure.
spotConfig$funEvals <- 100
spotConfig$model <- buildRandomForest
res100Rf <- spotLoop(resRf$x,
                     resRf$y,
                     fun=sann2spot,
                     lower=lower,
                     upper=upper,
                     control=spotConfig)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"

Example: resK continued with 50 additional runs

  • In a similar manner, new results can be added to the Kriging based optimization runs from Example resK.
spotConfig$model = buildKriging
spotConfig$optimizer = optimLBFGSB
spotConfig$modelControl = list(algTheta=optimLBFGSB)
res100K <- spotLoop(resK$x,
                    resK$y,
                    fun=sann2spot,
                    lower=lower,
                    upper=upper,
                    control=spotConfig)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
  • A comparison of the models resulting from the two long runs (Example resRf100 and res100K) is shown in the following Figures.
  • A visual inspection indicates that the landscapes are qualitatively similar, e.g., poor algorithm settings can be found in the lower right corner.
    • Comparison of the long runs with 100 function evaluations and two different surrogate models.
    • First: Example resRf100. Long run using Random forest model.
    • Second: Example res100K. Long run with 100 function evaluations using a Kriging model.
plotModel(res100Rf$modelFit)
plotModel(res100K$modelFit)

Response Surface Methodology (RSM)

Example: Path of the steepest descent

x <- designUniformRandom(lower=rep(-5,2), 
                         upper=rep(15,2), 
                         control=list(size=20))
y <- funSphere(x)
fit <- buildRSM(x,y)
predict(fit,cbind(1,2))
#> $y
#>      [,1]
#> [1,]    5
sphere(c(1,2))
#> [1] 5
descentSpotRSM(fit)
#> Path of steepest descent from ridge analysis:
#> $x
#>           V1      V2
#> 1   4.470955  4.7340
#> 2   3.849775  4.0405
#> 3   3.219460  3.3470
#> 4   2.589145  2.6630
#> 5   1.949695  1.9790
#> 6   1.301110  1.3140
#> 7   0.643390  0.6490
#> 8  -0.014330 -0.0160
#> 9  -0.681185 -0.6620
#> 10 -1.348040 -1.3080
#> 
#> $y
#>               [,1]
#>  [1,] 4.240019e+01
#>  [2,] 3.114641e+01
#>  [3,] 2.156733e+01
#>  [4,] 1.379524e+01
#>  [5,] 7.717752e+00
#>  [6,] 3.419483e+00
#>  [7,] 8.351517e-01
#>  [8,] 4.613489e-04
#>  [9,] 9.022570e-01
#> [10,] 3.528076e+00
plot(fit)

RSM and SPOT

rsm100K <- buildRSM(x=res100K$x,
                    y=res100K$y) 
summary(rsm100K$rsmfit)
#> 
#> Call:
#> rsm(formula = y ~ FO(x1, x2) + TWI(x1, x2) + PQ(x1, x2), data = codedData)
#> 
#>             Estimate Std. Error t value  Pr(>|t|)    
#> (Intercept)  31.8826     4.1020  7.7724 9.562e-12 ***
#> x1           34.3340     3.9986  8.5866 1.851e-13 ***
#> x2          -21.4616     3.2609 -6.5815 2.628e-09 ***
#> x1:x2       -18.8025     4.1377 -4.5442 1.638e-05 ***
#> x1^2          1.7335     5.6614  0.3062    0.7601    
#> x2^2         -5.7323     7.5647 -0.7578    0.4505    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Multiple R-squared:  0.706,  Adjusted R-squared:  0.6904 
#> F-statistic: 45.15 on 5 and 94 DF,  p-value: < 2.2e-16
#> 
#> Analysis of Variance Table
#> 
#> Response: y
#>             Df  Sum Sq Mean Sq  F value    Pr(>F)
#> FO(x1, x2)   2 15195.6  7597.8 101.2293 < 2.2e-16
#> TWI(x1, x2)  1  1701.6  1701.6  22.6711  6.96e-06
#> PQ(x1, x2)   2    45.3    22.7   0.3020    0.7400
#> Residuals   94  7055.2    75.1                   
#> Lack of fit 18  1418.3    78.8   1.0624    0.4053
#> Pure error  76  5636.9    74.2                   
#> 
#> Stationary point of response surface:
#>        x1        x2 
#> -2.026932  1.452278 
#> 
#> Stationary point in original units:
#>        V1        V2 
#> -46.23888 113.44796 
#> 
#> Eigenanalysis:
#> eigen() decomposition
#> $values
#> [1]   8.115856 -12.114618
#> 
#> $vectors
#>          [,1]      [,2]
#> x1 -0.8273570 0.5616764
#> x2  0.5616764 0.8273570
(xSteep <- descentSpotRSM(rsm100K) )
#> Path of steepest descent from ridge analysis:
#> $x
#>        V1     V2
#> 1  43.090 53.279
#> 2  39.134 55.472
#> 3  35.178 57.665
#> 4  31.176 59.729
#> 5  27.036 61.707
#> 6  22.804 63.427
#> 7  18.342 64.717
#> 8  13.420 65.018
#> 9   7.716 63.212
#> 10  1.736 58.697
#> 
#> $y
#>             [,1]
#>  [1,] 27.9078720
#>  [2,] 24.1025564
#>  [3,] 20.4579992
#>  [4,] 16.9969449
#>  [5,] 13.6407465
#>  [6,] 10.4796376
#>  [7,]  7.4725554
#>  [8,]  4.6115408
#>  [9,]  1.8285352
#> [10,] -0.9370131
xNew <- xSteep$x[8,]
(yNew <- sann2spot(xNew))
#>           [,1]
#> [1,] 0.6620505
x101 <- rbind(res100K$x, xNew)
y101 <- rbind(res100K$y, yNew)
rsm101K <- buildRSM(x=x101,
                    y=y101) 
summary(rsm101K$rsmfit)
#> 
#> Call:
#> rsm(formula = y ~ FO(x1, x2) + TWI(x1, x2) + PQ(x1, x2), data = codedData)
#> 
#>             Estimate Std. Error t value  Pr(>|t|)    
#> (Intercept)  31.6333     4.0469  7.8167 7.312e-12 ***
#> x1           34.3354     3.9817  8.6233 1.440e-13 ***
#> x2          -21.6229     3.2272 -6.7001 1.467e-09 ***
#> x1:x2       -18.6672     4.1092 -4.5428 1.630e-05 ***
#> x1^2          2.0736     5.5865  0.3712    0.7113    
#> x2^2         -5.6476     7.5304 -0.7500    0.4551    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Multiple R-squared:  0.7059, Adjusted R-squared:  0.6905 
#> F-statistic: 45.61 on 5 and 95 DF,  p-value: < 2.2e-16
#> 
#> Analysis of Variance Table
#> 
#> Response: y
#>             Df  Sum Sq Mean Sq  F value    Pr(>F)
#> FO(x1, x2)   2 15233.0  7616.5 102.3396 < 2.2e-16
#> TWI(x1, x2)  1  1693.7  1693.7  22.7578 6.633e-06
#> PQ(x1, x2)   2    46.0    23.0   0.3092    0.7348
#> Residuals   95  7070.3    74.4                   
#> Lack of fit 19  1433.4    75.4   1.0171    0.4525
#> Pure error  76  5636.9    74.2                   
#> 
#> Stationary point of response surface:
#>        x1        x2 
#> -2.002155  1.394545 
#> 
#> Stationary point in original units:
#>        V1        V2 
#> -45.09915 110.96545 
#> 
#> Eigenanalysis:
#> eigen() decomposition
#> $values
#> [1]   8.313469 -11.887497
#> 
#> $vectors
#>          [,1]      [,2]
#> x1 -0.8313295 0.5557798
#> x2  0.5557798 0.8313295
plot(rsm101K)
descentSpotRSM(rsm101K)
#> Path of steepest descent from ridge analysis:
#> $x
#>        V1     V2
#> 1  43.090 53.279
#> 2  39.180 55.515
#> 3  35.224 57.751
#> 4  31.268 59.901
#> 5  27.220 61.965
#> 6  23.080 63.857
#> 7  18.756 65.448
#> 8  14.018 66.265
#> 9   8.452 65.104
#> 10  2.104 60.417
#> 
#> $y
#>             [,1]
#>  [1,] 27.6519953
#>  [2,] 23.8567529
#>  [3,] 20.1956041
#>  [4,] 16.7367205
#>  [5,] 13.4182661
#>  [6,] 10.2718127
#>  [7,]  7.2813212
#>  [8,]  4.4443996
#>  [9,]  1.7474178
#> [10,] -0.9191026
spotConfig$model = buildKriging
spotConfig$optimizer = optimLBFGSB
spotConfig$modelControl = list(algTheta=optimLBFGSB)
spotConfig$funEvals <- 110
res110K <- spotLoop(x=x101,
                    y=y101,
                    fun=sann2spot,
                    lower=lower,
                    upper=upper,
                    control=spotConfig)
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
plotModel(res110K$modelFit)

Statistical Analysis

Tree-based Analysis

tmaxtempz.df <- data.frame(res100K$x[,1], res100K$x[,2], res100K$y)
names(tmaxtempz.df) <- c("tmax", "temp", "y")
tmaxtempz.tree <- party::ctree(y ~ ., data=tmaxtempz.df)
plot(tmaxtempz.tree, type="simple")

Regression Analysis

Building the Linear Model

  • Data from the SPOT run can be used for building linear models.
  • First, we extract the data from the result file.
  • Then we can use the standard lm() function for building the linear model.
xyz100K.df <- data.frame(res100K$x[,1], res100K$x[,2], res100K$y)
names(xyz100K.df) <- c("x", "y", "z")
lm100K <- lm(z ~ x*y, data=xyz100K.df)
summary(lm100K)
#> 
#> Call:
#> lm(formula = z ~ x * y, data = xyz100K.df)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -11.770  -5.595  -0.195   1.577  38.207 
#> 
#> Coefficients:
#>              Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)  0.934686   4.867470   0.192    0.848    
#> x            1.171357   0.127182   9.210 7.41e-15 ***
#> y           -0.059440   0.082454  -0.721    0.473    
#> x:y         -0.009318   0.001943  -4.796 5.91e-06 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 8.6 on 96 degrees of freedom
#> Multiple R-squared:  0.7041, Adjusted R-squared:  0.6949 
#> F-statistic: 76.15 on 3 and 96 DF,  p-value: < 2.2e-16
  • Diagnostic plots can be generated as follows:
plot(lm100K)

Estimating Variable Effects

  • R’s termplot() function can be used to plot regression terms against their predictors, optionally with standard errors and partial residuals added.
par(mfrow=c(1,2))
termplot(lm100K, partial = TRUE, smooth = panel.smooth, ask=FALSE) 
par(mfrom=c(1,1))
  • The car package provides the function avPlots(), which can be used for visualization as follows:
par(mfrow=c(1,3))
car::avPlots(lm100K,ask=F)   
par(mfrow=c(1,1))

Deterministic Problems and Surrogate Model Based Optimization

res <- spot(,funSphere,c(-5,-5),c(5,5), control=list(optimizer=optimLBFGSB)) 
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
#> [1] "Starting Surrogate Optimization"
#> [1] "*******************************"
res$xbest
#>              [,1]        [,2]
#> [1,] -0.009598582 -0.01446769
res$ybest
#>              [,1]
#> [1,] 0.0003014468

Model Ensembles: Stacking

fit.stack <- buildEnsembleStack(x.test, y.test)
plotModel(fit.stack)
xNew <-  cbind(1,1,1)
predict(fit.stack, xNew)
#> $y
#>        1 
#> 2.857598
funSphere(xNew)
#>      [,1]
#> [1,]    3

Summary