BNH Benchmark

Imports

import os
import warnings

import matplotlib.pyplot as plt
import pandas as pd

import bofire.strategies.api as strategies
from bofire.benchmarks.api import BNH
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 (
    MaximizeSigmoidObjective,
    MinimizeObjective,
    MinimizeSigmoidObjective,
)
from bofire.data_models.strategies.api import MoboStrategy, RandomStrategy
from bofire.plot.api import plot_objective_plotly
from bofire.runners.api import run
from bofire.utils.multiobjective import compute_hypervolume


warnings.simplefilter("once")
SMOKE_TEST = os.environ.get("SMOKE_TEST")

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:
    if "c1" in experiments.columns:
        return compute_hypervolume(
            domain,
            experiments.loc[(experiments.c1 <= 25) & (experiments.c2 >= 7.7)],
            ref_point={"f1": 140, "f2": 50},
        )
    return compute_hypervolume(domain, experiments, ref_point={"f1": 140, "f2": 50})


random_results = run(
    BNH(constraints=True),
    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:=4642.581]Run 0: 100%|██████████| 1/1 [00:00<00:00, 40.43it/s, Current Best:=4642.581]

MOBO Strategy

Without Constraints

def strategy_factory(domain: Domain):
    data_model = MoboStrategy(domain=domain, ref_point={"f1": 140, "f2": 50})
    return strategies.map(data_model)


results = run(
    BNH(constraints=False),
    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:03<?, ?it/s]Run 0:   0%|          | 0/1 [00:03<?, ?it/s, Current Best:=4907.896]Run 0: 100%|██████████| 1/1 [00:03<00:00,  3.95s/it, Current Best:=4907.896]Run 0: 100%|██████████| 1/1 [00:03<00:00,  3.95s/it, Current Best:=4907.896]

With Constraints

Manual Setup of the Domain

domain = Domain(
    inputs=Inputs(
        features=[
            ContinuousInput(key="x1", bounds=(0, 5)),
            ContinuousInput(key="x2", bounds=(0, 3)),
        ],
    ),
    outputs=Outputs(
        features=[
            ContinuousOutput(key="f1", objective=MinimizeObjective()),
            ContinuousOutput(key="f2", objective=MinimizeObjective()),
            # these are the output constraints, choose MinimizeSigmoidObjective for lower bound constraints
            # and MaximizeSigmoidObjective for upper bound constraints
            # tp is the threshold point, steepness is the steepness of the sigmoid that is applied to the constraint
            # usually a steepness of 1000 is fine.
            ContinuousOutput(
                key="c1",
                objective=MinimizeSigmoidObjective(tp=25, steepness=1000),
            ),
            ContinuousOutput(
                key="c2",
                objective=MaximizeSigmoidObjective(tp=7.7, steepness=1000),
            ),
        ],
    ),
)

One can visualize the objectives in the following way:

feat = domain.outputs.get_by_key("c1")

if not SMOKE_TEST:
    plot_objective_plotly(feat, lower=20, upper=30)  # type: ignore
feat = domain.outputs.get_by_key("c2")

if not SMOKE_TEST:
    plot_objective_plotly(feat, lower=5, upper=10)  # type: ignore

Run tbe optimization

The warnings can be ignored. They are stemming just from an internal postprocessing step and will be removed soon.

c_results = run(
    BNH(constraints=True),
    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]/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/pandas/core/arraylike.py:399: RuntimeWarning:

overflow encountered in exp

Run 0:   0%|          | 0/1 [00:03<?, ?it/s]Run 0:   0%|          | 0/1 [00:03<?, ?it/s, Current Best:=4624.504]Run 0: 100%|██████████| 1/1 [00:03<00:00,  3.94s/it, Current Best:=4624.504]Run 0: 100%|██████████| 1/1 [00:03<00:00,  3.94s/it, Current Best:=4624.504]
if not SMOKE_TEST:
    fig, ax = plt.subplots()
    ax.plot(results[0][1], label="without constraints")
    ax.plot(c_results[0][1], label="with constraints")
    ax.set_xlabel("iteration")
    ax.set_ylabel("hypervolume")
    ax.legend()
    plt.show()