Skip to content

Commit

Permalink
BNH and TNK (#424)
Browse files Browse the repository at this point in the history
* add new multiobjective benchmarks

* update notebook
  • Loading branch information
jduerholt committed Aug 12, 2024
1 parent 645a943 commit c0e3888
Show file tree
Hide file tree
Showing 5 changed files with 858 additions and 3 deletions.
14 changes: 12 additions & 2 deletions bofire/benchmarks/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
from bofire.benchmarks.benchmark import Benchmark, GenericBenchmark
from bofire.benchmarks.detergent import Detergent
from bofire.benchmarks.hyperopt import Hyperopt
from bofire.benchmarks.multi import C2DTLZ2, DTLZ2, ZDT1, CrossCoupling, SnarBenchmark
from bofire.benchmarks.multi import (
BNH,
C2DTLZ2,
DTLZ2,
TNK,
ZDT1,
CrossCoupling,
SnarBenchmark,
)
from bofire.benchmarks.single import (
Ackley,
Branin,
Expand All @@ -14,7 +22,9 @@
MultiTaskHimmelblau,
)

AnyMultiBenchmark = Union[C2DTLZ2, Detergent, DTLZ2, ZDT1, CrossCoupling, SnarBenchmark]
AnyMultiBenchmark = Union[
C2DTLZ2, Detergent, DTLZ2, ZDT1, CrossCoupling, SnarBenchmark, BNH, TNK
]
AnySingleBenchmark = Union[
Ackley, Branin, Branin30, Hartmann, Himmelblau, MultiTaskHimmelblau
]
92 changes: 92 additions & 0 deletions bofire/benchmarks/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
MaximizeObjective,
MaximizeSigmoidObjective,
MinimizeObjective,
MinimizeSigmoidObjective,
)
from bofire.data_models.surrogates.api import SingleTaskGPSurrogate
from bofire.utils.torch_tools import tkwargs
Expand Down Expand Up @@ -128,6 +129,97 @@ def _f(self, candidates: pd.DataFrame) -> pd.DataFrame:
return Y


class BNH(Benchmark):
def __init__(self, constraints: bool = True, **kwargs):
super().__init__(**kwargs)
self.constraints = constraints

self._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(w=1.0)),
ContinuousOutput(key="f2", objective=MinimizeObjective(w=1.0)),
]
),
)
if self.constraints:
self._domain.outputs.features.append( # type: ignore
ContinuousOutput(
key="c1",
objective=MinimizeSigmoidObjective(tp=25, steepness=1000),
)
)
self._domain.outputs.features.append( # type: ignore
ContinuousOutput(
key="c2",
objective=MaximizeSigmoidObjective(tp=7.7, steepness=1000),
),
)

def _f(self, candidates: pd.DataFrame) -> pd.DataFrame:
experiments = candidates.eval("f1=4*x1**2 + 4*x2**2", inplace=False)
experiments = experiments.eval("f2=(x1-5)**2 + (x2-5)**2", inplace=False)
experiments["valid_f1"] = 1
experiments["valid_f2"] = 1
if not self.constraints:
return experiments[["f1", "f2", "valid_f1", "valid_f2"]].copy()
experiments = experiments.eval("c1=(x1-5)**2 + x2**2", inplace=False)
experiments = experiments.eval("c2=(x1-8)**2 + (x2+3)**2", inplace=False)
experiments["valid_c1"] = 1
experiments["valid_c2"] = 1
return experiments[
["f1", "f2", "c1", "c2", "valid_c1", "valid_c2", "valid_f1", "valid_f2"]
].copy()


class TNK(Benchmark):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._domain = Domain(
inputs=Inputs(
features=[
ContinuousInput(key="x1", bounds=(0, math.pi)),
ContinuousInput(key="x2", bounds=(0, math.pi)),
]
),
outputs=Outputs(
features=[
ContinuousOutput(key="f1", objective=MinimizeObjective(w=1.0)),
ContinuousOutput(key="f2", objective=MinimizeObjective(w=1.0)),
ContinuousOutput(
key="c1",
objective=MaximizeSigmoidObjective(tp=0.0, steepness=500),
),
ContinuousOutput(
key="c2",
objective=MinimizeSigmoidObjective(tp=0.5, steepness=500),
),
]
),
)

def _f(self, candidates: pd.DataFrame) -> pd.DataFrame:
experiments = candidates.eval("f1=x1", inplace=False)
experiments = experiments.eval("f2=x2", inplace=False)
experiments = experiments.eval(
"c1=x1**2 + x2**2 -1 -0.1*cos(16*arctan(x1/x2))", inplace=False
)
experiments = experiments.eval("c2=(x1-0.5)**2+(x2-0.5)**2", inplace=False)
experiments["valid_c1"] = 1
experiments["valid_c2"] = 1
experiments["valid_f1"] = 1
experiments["valid_f2"] = 1
return experiments[
["f1", "f2", "c1", "c2", "valid_c1", "valid_c2", "valid_f1", "valid_f2"]
].copy()


class C2DTLZ2(DTLZ2):
"""Constrained DTLZ2 benchmark function. Taken from
https://github.com/pytorch/botorch/blob/main/botorch/test_functions/multi_objective.py.
Expand Down
15 changes: 14 additions & 1 deletion tests/bofire/benchmarks/test_multi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import pytest

from bofire.benchmarks.multi import C2DTLZ2, DTLZ2, ZDT1, CrossCoupling, SnarBenchmark
from bofire.benchmarks.multi import (
BNH,
C2DTLZ2,
DTLZ2,
TNK,
ZDT1,
CrossCoupling,
SnarBenchmark,
)


@pytest.mark.parametrize(
Expand All @@ -24,6 +32,10 @@
),
(C2DTLZ2, True, {"dim": 4}),
(C2DTLZ2, False, {"dim": 4}),
(BNH, False, {"constraints": True}),
(BNH, False, {"constraints": False}),
(TNK, False, {}),
(TNK, True, {}),
],
)
def test_multi_objective_benchmarks(cls_benchmark, return_complete, kwargs):
Expand All @@ -43,6 +55,7 @@ def test_multi_objective_benchmarks(cls_benchmark, return_complete, kwargs):

# Define expected number of output variables
expected_output_variables = len(benchmark_function.domain.outputs) * 2
print(Y.shape, expected_output_variables)
# Check, whether expected number of output variables match the actual number
if return_complete:
assert Y.shape == (
Expand Down
Loading

0 comments on commit c0e3888

Please sign in to comment.