---
title: "Design and sequential optimization"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Design and sequential optimization}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

Version 0.12.0 of `magp` includes four functions for Bayesian
optimization:

| Starting point | Function |
|---|---|
| Candidate rows and a fitted model | `magp_expected_improvement()` |
| A fitted model and a need for one new experiment | `magp_next_point()` |
| Completed experiments and responses | `magp_bayes_optimize()` |
| No completed experiments | `magp_bayes_optimize_from_scratch()` |

The examples below show the last two cases. The same objective-function
interface can evaluate a computer simulation or retrieve a response that has
already been measured outside R.

## Define an objective

```{r objective}
library(magp)

objective <- function(quantity_1, quantity_2, quantity_3,
                      sequence_1, sequence_2, sequence_3) {
  quantity <- c(quantity_1, quantity_2, quantity_3)
  sequence <- c(sequence_1, sequence_2, sequence_3)

  -sum((quantity - c(0.2, 0.6, 0.8))^2) -
    0.01 * sum((sequence - c(1, 3, 2))^2)
}
```

The function arguments must match the input column names. The return value must
be one finite number, or a list containing one finite number named `Score` or
`Value`.

## Start without initial data

Use `magp_bayes_optimize_from_scratch()` when no experiments have been run.
The function creates an initial design, evaluates the objective at those rows,
fits a MaGP model, and selects later rows with expected improvement.

```{r complete-workflow, eval=FALSE}
result <- magp_bayes_optimize_from_scratch(
  FUN = objective,
  n_initial = 8,
  q = 3,
  model = "2d",
  direction = "maximize",
  n_iter = 3,
  seed = 4,
  design_control = list(
    sequence_method = "sfta",
    sequence_maxit = 500,
    quantity_maxit = 500,
    alignment_maxit = 500
  ),
  fit_control = list(
    n_starts = 4,
    workers = 2
  ),
  acquisition_control = list(
    n_starts = 5,
    workers = 2
  ),
  verbose = FALSE
)

result$initial_design$design
result$initial_response
result$best_point
result$best_value
result$history
```

Use `direction = "minimize"` when a smaller response is better, or
`direction = "maximize"` when a larger response is better. `n_initial` is the
number of starting experiments. `n_iter` is the maximum number of later
experiments.

The result records the initial design and responses, the best observed point,
every completed evaluation in `history`, and the final fitted model.

## Choose the initial sequence method

The `sequence_method` setting affects only the sequence portion of the initial
design. Choose `"random"` to sample valid permutations, `"sfta"` to improve a
complete sequence design with space-filling threshold accepting, or `"sann"`
to use simulated annealing. The default is `"sann"` for compatibility with
earlier versions.

```{r compare-sequence-methods, eval=FALSE}
random_design <- magp_initial_design(
  n = 12,
  q = 4,
  sequence_method = "random",
  seed = 4
)

sfta_design <- magp_initial_design(
  n = 12,
  q = 4,
  sequence_method = "sfta",
  seed = 4,
  sfta_control = list(nstarts = 5, ncalibrate = 200)
)

rbind(random = random_design$criteria, sfta = sfta_design$criteria)
sfta_design$sequence_search$sfta
```

With a fixed seed, both calls use the same quantitative Latin hypercube before
the final alignment step. SFTA improves the initial sequence design. It does
not change the expected-improvement search used to select later experiments.

## Continue from completed experiments

Use `magp_bayes_optimize()` when initial inputs and responses already exist.
The initial rows are treated as completed experiments and are not evaluated
again.

```{r continue-existing, eval=FALSE}
design <- magp_initial_design(n = 8, q = 3, seed = 4)
X_initial <- design$design
y_initial <- apply(X_initial, 1L, function(row) {
  do.call(objective, as.list(row))
})

result <- magp_bayes_optimize(
  FUN = objective,
  X = X_initial,
  y = y_initial,
  direction = "maximize",
  n_iter = 3,
  seed = 4,
  verbose = FALSE
)

result$best_point
result$best_value
result$history
```

Each iteration fits a model to all available results, selects one unobserved
point, evaluates the objective, and adds the new response. Set `stop_ei` and
`stop_patience` to stop after repeated selections with little expected
improvement.

The control lists are optional. `design_control` changes the initial-design
search, `fit_control` changes model fitting, and `acquisition_control` changes
the next-point search. The function help pages list every available setting and
every returned component.
