import os
import pandas as pd
import bofire.strategies.api as strategies
from bofire.benchmarks.multi import DTLZ2
from bofire.data_models.api import Domain, Inputs, Outputs
from bofire.data_models.features.api import ContinuousInput, ContinuousOutput
from bofire.data_models.objectives.api import MinimizeObjective
from bofire.data_models.strategies.api import (
MoboStrategy,
QparegoStrategy,
RandomStrategy,
)
from bofire.runners.api import run
from bofire.utils.multiobjective import compute_hypervolume
SMOKE_TEST = os.environ.get("SMOKE_TEST")DTLZ2 Benchmark
Imports
Manual setup of the optimization domain
The following cell shows how to manually setup the optimization problem in BoFire for didactic purposes. In the following the implemented benchmark module is then used.
input_features = Inputs(
features=[ContinuousInput(key=f"x_{i}", bounds=(0, 1)) for i in range(6)],
)
# here the minimize objective is used, if you want to maximize you have to use the maximize objective.
output_features = Outputs(
features=[
ContinuousOutput(key=f"f_{i}", objective=MinimizeObjective(w=1.0))
for i in range(2)
],
)
# no constraints are present so we can create the domain
domain = Domain(inputs=input_features, outputs=output_features)Random Strategy
def sample(domain):
datamodel = RandomStrategy(domain=domain)
sampler = strategies.map(data_model=datamodel)
sampled = sampler.ask(10)
return sampled
def hypervolume(domain: Domain, experiments: pd.DataFrame) -> float:
return compute_hypervolume(domain, experiments, ref_point={"f_0": 1.1, "f_1": 1.1})
random_results = run(
DTLZ2(dim=6),
strategy_factory=lambda domain: strategies.map(RandomStrategy(domain=domain)),
n_iterations=50 if not SMOKE_TEST else 1,
metric=hypervolume,
initial_sampler=sample,
n_runs=1,
n_procs=1,
) 0%| | 0/1 [00:00<?, ?it/s]Run 0: 0%| | 0/1 [00:00<?, ?it/s]Run 0: 0%| | 0/1 [00:00<?, ?it/s, Current Best:=0.000]Run 0: 100%|██████████| 1/1 [00:00<00:00, 44.24it/s, Current Best:=0.000]
MOBO Strategy
Automatic run
def strategy_factory(domain: Domain):
data_model = MoboStrategy(domain=domain, ref_point={"f_0": 1.1, "f_1": 1.1})
return strategies.map(data_model)
results = run(
DTLZ2(dim=6),
strategy_factory=strategy_factory,
n_iterations=50 if not SMOKE_TEST else 1,
metric=hypervolume,
initial_sampler=sample,
n_runs=1,
n_procs=1,
) 0%| | 0/1 [00:00<?, ?it/s]Run 0: 0%| | 0/1 [00:01<?, ?it/s]Run 0: 0%| | 0/1 [00:01<?, ?it/s, Current Best:=0.000]Run 0: 100%|██████████| 1/1 [00:01<00:00, 1.64s/it, Current Best:=0.000]Run 0: 100%|██████████| 1/1 [00:01<00:00, 1.64s/it, Current Best:=0.000]
Manual setup
Using the default Models
# we get the domain from the benchmark module, in real use case we have to build it on our own
# make sure that the objective is set correctly
domain = DTLZ2(dim=6).domain
# we generate training data
experiments = DTLZ2(dim=6).f(domain.inputs.sample(10), return_complete=True)
# we setup the strategy
# providing of a reference point is not mandatory but can help
# the reference point has to be wrt to the assigned objective always worse than the points on the paretofront.
data_model = MoboStrategy(domain=domain, ref_point={"f_0": 1.1, "f_1": 1.1})
recommender = strategies.map(data_model=data_model)
# we tell the strategy our historical data
recommender.tell(experiments=experiments)
# we ask for a new point to evaluate
candidates = recommender.ask(candidate_count=1)
# we show the candidate
display(candidates)
# this candidate has to be then provided to the benchmark function and evaluated and then told back to the optimizer to get the next candidate| x_0 | x_1 | x_2 | x_3 | x_4 | x_5 | f_0_pred | f_1_pred | f_0_sd | f_1_sd | f_0_des | f_1_des | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.0 | 1.0 | 0.082245 | 0.91796 | 0.667678 | 0.474128 | 0.147971 | 0.56786 | 0.206938 | 0.322621 | -0.147971 | -0.56786 |
Setup specific models
from bofire.data_models.kernels.api import RBFKernel, ScaleKernel
from bofire.data_models.surrogates.api import BotorchSurrogates, SingleTaskGPSurrogate
# in this case you would use non default kernels for the different outputs
# it is also possible to build the models for a subset of the complete features
data_model = MoboStrategy(
domain=domain,
ref_point={"f_0": 1.1, "f_1": 1.1},
surrogate_specs=BotorchSurrogates(
surrogates=[
SingleTaskGPSurrogate(
inputs=domain.inputs,
outputs=Outputs(features=[domain.outputs[0]]),
kernel=ScaleKernel(base_kernel=RBFKernel(ard=True)),
),
SingleTaskGPSurrogate(
inputs=domain.inputs,
outputs=Outputs(features=[domain.outputs[1]]),
kernel=ScaleKernel(base_kernel=RBFKernel(ard=False)),
),
],
),
)
recommender = strategies.map(data_model=data_model)
# we tell the strategy our historical data
recommender.tell(experiments=experiments)
# we ask for a new point to evaluate
candidates = recommender.ask(candidate_count=1)
# we show the candidate
display(candidates)| x_0 | x_1 | x_2 | x_3 | x_4 | x_5 | f_0_pred | f_1_pred | f_0_sd | f_1_sd | f_0_des | f_1_des | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.946332 | 0.953495 | 0.0 | 1.0 | 0.0 | 0.0 | 0.043738 | 0.698734 | 0.106396 | 0.499075 | -0.043738 | -0.698734 |
QPAREGO Strategy
results_qparego = run(
DTLZ2(dim=6),
strategy_factory=lambda domain: strategies.map(QparegoStrategy(domain=domain)),
n_iterations=50 if not SMOKE_TEST else 1,
metric=hypervolume,
initial_sampler=sample,
n_runs=1,
n_procs=1,
) 0%| | 0/1 [00:00<?, ?it/s]/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/botorch/acquisition/monte_carlo.py:502: NumericsWarning:
qNoisyExpectedImprovement has known numerical issues that lead to suboptimal optimization performance. It is strongly recommended to simply replace
qNoisyExpectedImprovement --> qLogNoisyExpectedImprovement
instead, which fixes the issues and has the same API. See https://arxiv.org/abs/2310.20708 for details.
Run 0: 0%| | 0/1 [00:01<?, ?it/s]Run 0: 0%| | 0/1 [00:01<?, ?it/s, Current Best:=0.017]Run 0: 100%|██████████| 1/1 [00:01<00:00, 1.12s/it, Current Best:=0.017]Run 0: 100%|██████████| 1/1 [00:01<00:00, 1.12s/it, Current Best:=0.017]
Performance Plot
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(results[0][0].f_0, results[0][0].f_1, label="qehvi")
ax.scatter(results_qparego[0][0].f_0, results_qparego[0][0].f_1, label="qparego")
ax.scatter(random_results[0][0].f_0, random_results[0][0].f_1, label="random")
ax.legend()
ax.set_xlabel("f_0")
ax.set_ylabel("f_1")
fig.show()