Simulation loop using a BoTorch test function

This example shows a simulation loop for a single target with a BoTorch test function as lookup.

This example assumes some basic familiarity with using BayBE and how to use BoTorch test functions in discrete searchspaces. We thus refer to

  1. campaign for a basic example on how to use BayBE and

  2. discrete_space for details on using a BoTorch test function.

Imports

import os
import sys
from pathlib import Path
import numpy as np
import seaborn as sns
from botorch.test_functions import Rastrigin
from baybe import Campaign
from baybe.objective import Objective
from baybe.parameters import NumericalDiscreteParameter
from baybe.recommenders import RandomRecommender
from baybe.searchspace import SearchSpace
from baybe.simulation import simulate_scenarios
from baybe.targets import NumericalTarget
from baybe.utils.botorch_wrapper import botorch_function_wrapper
from baybe.utils.plotting import create_example_plots

Parameters for a full simulation loop

For the full simulation, we need to define the number of Monte Carlo runs and the number of experiments to be conducted per run.

SMOKE_TEST = "SMOKE_TEST" in os.environ
N_MC_ITERATIONS = 2 if SMOKE_TEST else 30
N_DOE_ITERATIONS = 2 if SMOKE_TEST else 15
BATCH_SIZE = 1 if SMOKE_TEST else 3
POINTS_PER_DIM = 10

Defining the test function

See discrete_space for details.

DIMENSION = 4
TestFunctionClass = Rastrigin
if not hasattr(TestFunctionClass, "dim"):
    TestFunction = TestFunctionClass(dim=DIMENSION)
else:
    print(
        f"\nYou choose a dimension of {DIMENSION} for the test function"
        f"{TestFunctionClass}. However, this function can only be used in "
        f"{TestFunctionClass().dim} dimension, so the provided dimension is replaced."
    )
    TestFunction = TestFunctionClass()
    DIMENSION = TestFunctionClass().dim
BOUNDS = TestFunction.bounds
WRAPPED_FUNCTION = botorch_function_wrapper(test_function=TestFunction)

Creating the searchspace and the objective

parameters = [
    NumericalDiscreteParameter(
        name=f"x_{k+1}",
        values=list(
            np.linspace(
                BOUNDS[0, k],
                BOUNDS[1, k],
                POINTS_PER_DIM,
            )
        ),
        tolerance=0.01,
    )
    for k in range(DIMENSION)
]
searchspace = SearchSpace.from_product(parameters=parameters)
objective = Objective(
    mode="SINGLE", targets=[NumericalTarget(name="Target", mode="MIN")]
)

Constructing campaigns

seq_greedy_EI_campaign = Campaign(
    searchspace=searchspace,
    objective=objective,
)
random_campaign = Campaign(
    searchspace=searchspace,
    recommender=RandomRecommender(),
    objective=objective,
)

Performing the simulation loop

We use simulate_scenarios to simulate a full experiment.

scenarios = {
    "Sequential greedy EI": seq_greedy_EI_campaign,
    "Random": random_campaign,
}
results = simulate_scenarios(
    scenarios,
    WRAPPED_FUNCTION,
    batch_size=BATCH_SIZE,
    n_doe_iterations=N_DOE_ITERATIONS,
    n_mc_iterations=N_MC_ITERATIONS,
)

We use the plotting utility to create plots.

path = Path(sys.path[0])
ax = sns.lineplot(
    data=results,
    marker="o",
    markersize=10,
    x="Num_Experiments",
    y="Target_CumBest",
    hue="Scenario",
)
create_example_plots(
    ax=ax,
    path=path,
    base_name="botorch_analytical",
)
../../_images/botorch_analytical_light.svg../../_images/botorch_analytical_dark.svg