Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support different noise levels for different outputs in test functions #2136

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions botorch/test_functions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from typing import List, Optional, Tuple
from typing import List, Optional, Tuple, Union

import torch

from botorch.exceptions.errors import InputDataError
from torch import Tensor
from torch.nn import Module
Expand All @@ -26,7 +27,11 @@ class BaseTestProblem(Module, ABC):
_bounds: List[Tuple[float, float]]
_check_grad_at_opt: bool = True

def __init__(self, noise_std: Optional[float] = None, negate: bool = False) -> None:
def __init__(
self,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""Base constructor for test functions.

Args:
Expand Down Expand Up @@ -60,7 +65,12 @@ def forward(self, X: Tensor, noise: bool = True) -> Tensor:
X = X if batch else X.unsqueeze(0)
f = self.evaluate_true(X=X)
if noise and self.noise_std is not None:
f += self.noise_std * torch.randn_like(f)
_noise = (
Tensor(self.noise_std)
if isinstance(self.noise_std, list)
else self.noise_std
)
f += _noise * torch.randn_like(f)
if self.negate:
f = -f
return f if batch else f.squeeze(0)
Expand Down Expand Up @@ -147,7 +157,11 @@ class MultiObjectiveTestProblem(BaseTestProblem):
_ref_point: List[float]
_max_hv: float

def __init__(self, noise_std: Optional[float] = None, negate: bool = False) -> None:
def __init__(
self,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""Base constructor for multi-objective test functions.

Args:
Expand Down
24 changes: 16 additions & 8 deletions botorch/test_functions/multi_objective.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
import math
from abc import ABC, abstractmethod
from math import pi
from typing import Optional
from typing import List, Optional, Union

import torch
from botorch.exceptions.errors import UnsupportedError
Expand Down Expand Up @@ -116,7 +116,11 @@ class BraninCurrin(MultiObjectiveTestProblem):
_ref_point = [18.0, 6.0]
_max_hv = 59.36011874867746 # this is approximated using NSGA-II

def __init__(self, noise_std: Optional[float] = None, negate: bool = False) -> None:
def __init__(
self,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""
Args:
noise_std: Standard deviation of the observation noise.
Expand Down Expand Up @@ -174,7 +178,7 @@ class DH(MultiObjectiveTestProblem, ABC):
def __init__(
self,
dim: int,
noise_std: Optional[float] = None,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""
Expand Down Expand Up @@ -334,7 +338,7 @@ def __init__(
self,
dim: int,
num_objectives: int = 2,
noise_std: Optional[float] = None,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""
Expand Down Expand Up @@ -600,7 +604,7 @@ class GMM(MultiObjectiveTestProblem):

def __init__(
self,
noise_std: Optional[float] = None,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
num_objectives: int = 2,
) -> None:
Expand Down Expand Up @@ -926,7 +930,7 @@ def __init__(
self,
dim: int,
num_objectives: int = 2,
noise_std: Optional[float] = None,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""
Expand Down Expand Up @@ -1234,7 +1238,11 @@ class ConstrainedBraninCurrin(BraninCurrin, ConstrainedBaseTestProblem):
_ref_point = [80.0, 12.0]
_max_hv = 608.4004237022673 # from NSGA-II with 90k evaluations

def __init__(self, noise_std: Optional[float] = None, negate: bool = False) -> None:
def __init__(
self,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""
Args:
noise_std: Standard deviation of the observation noise.
Expand Down Expand Up @@ -1337,7 +1345,7 @@ class MW7(MultiObjectiveTestProblem, ConstrainedBaseTestProblem):
def __init__(
self,
dim: int,
noise_std: Optional[float] = None,
noise_std: Optional[Union[float, List[float]]] = None,
negate: bool = False,
) -> None:
r"""
Expand Down
43 changes: 28 additions & 15 deletions test/test_functions/test_multi_objective.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def evaluate_true(self, X):
class TestBaseTestMultiObjectiveProblem(BotorchTestCase):
def test_base_mo_problem(self):
for negate in (True, False):
for noise_std in (None, 1.0):
for noise_std in (None, 1.0, [1.0, 2.0]):
f = DummyMOProblem(noise_std=noise_std, negate=negate)
self.assertEqual(f.noise_std, noise_std)
self.assertEqual(f.negate, negate)
Expand Down Expand Up @@ -155,6 +155,7 @@ def functions(self) -> List[BaseTestProblem]:
DTLZ4(dim=5, num_objectives=2),
DTLZ5(dim=5, num_objectives=2),
DTLZ7(dim=5, num_objectives=2),
DTLZ7(dim=5, num_objectives=2, noise_std=[0.1, 0.2]),
]

def test_init(self):
Expand Down Expand Up @@ -216,7 +217,10 @@ class TestGMM(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [GMM(num_objectives=4)]
return [
GMM(num_objectives=4),
GMM(num_objectives=4, noise_std=[0.0, 0.1, 0.2, 0.3]),
]

def test_init(self):
f = self.functions[0]
Expand Down Expand Up @@ -269,7 +273,7 @@ class TestMW7(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [MW7(dim=3)]
return [MW7(dim=3), MW7(dim=3, noise_std=[0.1, 0.2])]

def test_init(self):
for f in self.functions:
Expand All @@ -290,6 +294,8 @@ def functions(self) -> List[BaseTestProblem]:
ZDT1(dim=3, num_objectives=2),
ZDT2(dim=3, num_objectives=2),
ZDT3(dim=3, num_objectives=2),
ZDT3(dim=3, num_objectives=2, noise_std=0.1),
ZDT3(dim=3, num_objectives=2, noise_std=[0.1, 0.2]),
]

def test_init(self):
Expand Down Expand Up @@ -362,7 +368,7 @@ class TestCarSideImpact(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [CarSideImpact()]
return [CarSideImpact(), CarSideImpact(noise_std=[0.1, 0.2, 0.3, 0.4])]


class TestPenicillin(
Expand All @@ -372,7 +378,7 @@ class TestPenicillin(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [Penicillin()]
return [Penicillin(), Penicillin(noise_std=[0.1, 0.2, 0.3])]


class TestToyRobust(
Expand All @@ -382,7 +388,7 @@ class TestToyRobust(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [ToyRobust()]
return [ToyRobust(), ToyRobust(noise_std=[0.1, 0.2])]


class TestVehicleSafety(
Expand All @@ -392,7 +398,7 @@ class TestVehicleSafety(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [VehicleSafety()]
return [VehicleSafety(), VehicleSafety(noise_std=[0.1, 0.2, 0.3])]


# ------------------ Constrained Multi-objective test problems ------------------ #
Expand All @@ -406,7 +412,7 @@ class TestBNH(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [BNH()]
return [BNH(), BNH(noise_std=[0.1, 0.2])]


class TestSRN(
Expand All @@ -417,7 +423,7 @@ class TestSRN(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [SRN()]
return [SRN(), SRN(noise_std=[0.1, 0.2])]


class TestCONSTR(
Expand All @@ -428,7 +434,7 @@ class TestCONSTR(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [CONSTR()]
return [CONSTR(), CONSTR(noise_std=[0.1, 0.2])]


class TestConstrainedBraninCurrin(
Expand All @@ -439,7 +445,10 @@ class TestConstrainedBraninCurrin(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [ConstrainedBraninCurrin()]
return [
ConstrainedBraninCurrin(),
ConstrainedBraninCurrin(noise_std=[0.1, 0.2]),
]


class TestC2DTLZ2(
Expand All @@ -450,7 +459,11 @@ class TestC2DTLZ2(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [C2DTLZ2(dim=3, num_objectives=2)]
return [
C2DTLZ2(dim=3, num_objectives=2),
C2DTLZ2(dim=3, num_objectives=2, noise_std=0.1),
C2DTLZ2(dim=3, num_objectives=2, noise_std=[0.1, 0.2]),
]

def test_batch_exception(self):
f = C2DTLZ2(dim=3, num_objectives=2)
Expand All @@ -466,7 +479,7 @@ class TestDiscBrake(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [DiscBrake()]
return [DiscBrake(), DiscBrake(noise_std=[0.1, 0.2])]


class TestWeldedBeam(
Expand All @@ -477,7 +490,7 @@ class TestWeldedBeam(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [WeldedBeam()]
return [WeldedBeam(), WeldedBeam(noise_std=[0.1, 0.2])]


class TestOSY(
Expand All @@ -488,4 +501,4 @@ class TestOSY(
):
@property
def functions(self) -> List[BaseTestProblem]:
return [OSY()]
return [OSY(), OSY(noise_std=[0.1, 0.2])]