From e3fe5e8cd6c846896d10ad03bc32cc29011f6e45 Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 13:18:21 +0200 Subject: [PATCH 01/24] add polynomial kernel --- bofire/data_models/kernels/api.py | 3 +++ bofire/data_models/kernels/continuous.py | 6 ++++++ bofire/kernels/mapper.py | 15 +++++++++++++++ tests/bofire/data_models/test_kernels.py | 21 +++++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/bofire/data_models/kernels/api.py b/bofire/data_models/kernels/api.py index 4f9eaaa63..975be871a 100644 --- a/bofire/data_models/kernels/api.py +++ b/bofire/data_models/kernels/api.py @@ -13,6 +13,7 @@ ContinuousKernel, LinearKernel, MaternKernel, + PolynomialKernel, RBFKernel, ) from bofire.data_models.kernels.kernel import Kernel @@ -23,6 +24,7 @@ AnyContinuousKernel = Union[ MaternKernel, LinearKernel, + PolynomialKernel, RBFKernel, ] @@ -36,6 +38,7 @@ ScaleKernel, HammondDistanceKernel, LinearKernel, + PolynomialKernel, MaternKernel, RBFKernel, TanimotoKernel, diff --git a/bofire/data_models/kernels/continuous.py b/bofire/data_models/kernels/continuous.py index 6c68e6391..9925a411c 100644 --- a/bofire/data_models/kernels/continuous.py +++ b/bofire/data_models/kernels/continuous.py @@ -24,3 +24,9 @@ class MaternKernel(ContinuousKernel): class LinearKernel(ContinuousKernel): type: Literal["LinearKernel"] = "LinearKernel" variance_prior: Optional[AnyPrior] = None + + +class PolynomialKernel(ContinuousKernel): + type: Literal["PolynomialKernel"] = "PolynomialKernel" + offset_prior: Optional[AnyPrior] = None + degree: int = 2 diff --git a/bofire/kernels/mapper.py b/bofire/kernels/mapper.py index 33bd0c9c5..ce95cf042 100644 --- a/bofire/kernels/mapper.py +++ b/bofire/kernels/mapper.py @@ -57,6 +57,20 @@ def map_LinearKernel( ) +def map_PolynomialKernel( + data_model: data_models.PolynomialKernel, + batch_shape: torch.Size, + ard_num_dims: int, + active_dims: List[int], +) -> gpytorch.kernels.PolynomialKernel: + return gpytorch.kernels.PolynomialKernel( + batch_shape=batch_shape, + active_dims=active_dims, + power=data_model.power, + offset_prior=priors.map(data_model.offset_prior), + ) + + def map_AdditiveKernel( data_model: data_models.AdditiveKernel, batch_shape: torch.Size, @@ -131,6 +145,7 @@ def map_TanimotoKernel( data_models.RBFKernel: map_RBFKernel, data_models.MaternKernel: map_MaternKernel, data_models.LinearKernel: map_LinearKernel, + data_models.PolynomialKernel: map_PolynomialKernel, data_models.AdditiveKernel: map_AdditiveKernel, data_models.MultiplicativeKernel: map_MultiplicativeKernel, data_models.ScaleKernel: map_ScaleKernel, diff --git a/tests/bofire/data_models/test_kernels.py b/tests/bofire/data_models/test_kernels.py index 1f7936e76..39984db43 100644 --- a/tests/bofire/data_models/test_kernels.py +++ b/tests/bofire/data_models/test_kernels.py @@ -14,6 +14,7 @@ LinearKernel, MaternKernel, MultiplicativeKernel, + PolynomialKernel, RBFKernel, ScaleKernel, TanimotoKernel, @@ -154,6 +155,26 @@ def test_scale_kernel(): assert hasattr(k, "outputscale_prior") is False +def test_poly_kernel(): + kernel = PolynomialKernel(degree=2, offset_prior=BOTORCH_SCALE_PRIOR()) + k = kernels.map( + kernel, + batch_shape=torch.Size(), + ard_num_dims=10, + active_dims=list(range(5)), + ) + assert hasattr(k, "offset_prior") + assert isinstance(k.offset_prior, gpytorch.priors.GammaPrior) + kernel = PolynomialKernel(degree=2) + k = kernels.map( + kernel, + batch_shape=torch.Size(), + ard_num_dims=10, + active_dims=list(range(5)), + ) + assert hasattr(k, "offset_prior") is False + + @pytest.mark.parametrize( "kernel, ard_num_dims, active_dims, expected_kernel", [ From 342c3bc8cd6ab74b7518b3d070c6a28f42269c7b Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 13:37:00 +0200 Subject: [PATCH 02/24] add polynomial kernel --- bofire/data_models/kernels/api.py | 1 + bofire/data_models/kernels/continuous.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bofire/data_models/kernels/api.py b/bofire/data_models/kernels/api.py index 975be871a..02e4bda76 100644 --- a/bofire/data_models/kernels/api.py +++ b/bofire/data_models/kernels/api.py @@ -25,6 +25,7 @@ MaternKernel, LinearKernel, PolynomialKernel, + PolynomialKernel, RBFKernel, ] diff --git a/bofire/data_models/kernels/continuous.py b/bofire/data_models/kernels/continuous.py index 9925a411c..57a5da643 100644 --- a/bofire/data_models/kernels/continuous.py +++ b/bofire/data_models/kernels/continuous.py @@ -29,4 +29,4 @@ class LinearKernel(ContinuousKernel): class PolynomialKernel(ContinuousKernel): type: Literal["PolynomialKernel"] = "PolynomialKernel" offset_prior: Optional[AnyPrior] = None - degree: int = 2 + power: int = 2 From 300c656f32214bca46a2c42755ddfc2693366177 Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 13:45:12 +0200 Subject: [PATCH 03/24] add polynomial kernel --- bofire/kernels/mapper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bofire/kernels/mapper.py b/bofire/kernels/mapper.py index ce95cf042..91f117636 100644 --- a/bofire/kernels/mapper.py +++ b/bofire/kernels/mapper.py @@ -67,7 +67,9 @@ def map_PolynomialKernel( batch_shape=batch_shape, active_dims=active_dims, power=data_model.power, - offset_prior=priors.map(data_model.offset_prior), + offset_prior=priors.map(data_model.offset_prior) + if data_model.offset_prior is not None + else None, ) From 1092002e42b91c77db8882bce84486b4f5fc9a2b Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 13:58:44 +0200 Subject: [PATCH 04/24] add polynomial kernel --- tests/bofire/data_models/test_kernels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bofire/data_models/test_kernels.py b/tests/bofire/data_models/test_kernels.py index 39984db43..c2d5d7ed1 100644 --- a/tests/bofire/data_models/test_kernels.py +++ b/tests/bofire/data_models/test_kernels.py @@ -156,7 +156,7 @@ def test_scale_kernel(): def test_poly_kernel(): - kernel = PolynomialKernel(degree=2, offset_prior=BOTORCH_SCALE_PRIOR()) + kernel = PolynomialKernel(power=2, offset_prior=BOTORCH_SCALE_PRIOR()) k = kernels.map( kernel, batch_shape=torch.Size(), @@ -165,7 +165,7 @@ def test_poly_kernel(): ) assert hasattr(k, "offset_prior") assert isinstance(k.offset_prior, gpytorch.priors.GammaPrior) - kernel = PolynomialKernel(degree=2) + kernel = PolynomialKernel(power=2) k = kernels.map( kernel, batch_shape=torch.Size(), From 4ea3a08757528bdea41da76abad2a636f04993ed Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 14:23:48 +0200 Subject: [PATCH 05/24] add quadratic surrogate --- bofire/data_models/surrogates/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bofire/data_models/surrogates/api.py b/bofire/data_models/surrogates/api.py index 3ba5749e7..eaf44e852 100644 --- a/bofire/data_models/surrogates/api.py +++ b/bofire/data_models/surrogates/api.py @@ -15,6 +15,7 @@ MixedSingleTaskGPSurrogate, ) from bofire.data_models.surrogates.mlp import MLPEnsemble + from bofire.data_models.surrogates.quadratic import QuadraticSurrogate from bofire.data_models.surrogates.random_forest import RandomForestSurrogate from bofire.data_models.surrogates.single_task_gp import ( SingleTaskGPHyperconfig, @@ -36,6 +37,7 @@ SaasSingleTaskGPSurrogate, XGBoostSurrogate, LinearSurrogate, + QuadraticSurrogate, TanimotoGPSurrogate, ] @@ -47,6 +49,7 @@ SaasSingleTaskGPSurrogate, XGBoostSurrogate, LinearSurrogate, + QuadraticSurrogate, TanimotoGPSurrogate, ] except ImportError: From 2a2baecbd3012bd5b034766962f73d0380ac71ea Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 14:32:16 +0200 Subject: [PATCH 06/24] adding linear kernel not necessary --- bofire/data_models/kernels/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bofire/data_models/kernels/api.py b/bofire/data_models/kernels/api.py index 02e4bda76..975be871a 100644 --- a/bofire/data_models/kernels/api.py +++ b/bofire/data_models/kernels/api.py @@ -25,7 +25,6 @@ MaternKernel, LinearKernel, PolynomialKernel, - PolynomialKernel, RBFKernel, ] From c95f26ca278341b70b0ab6409348499582dd77a9 Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 14:52:12 +0200 Subject: [PATCH 07/24] adding linear kernel not necessary --- bofire/data_models/surrogates/quadratic.py | 22 +++++++++++ tests/bofire/surrogates/test_quadratic.py | 43 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 bofire/data_models/surrogates/quadratic.py create mode 100644 tests/bofire/surrogates/test_quadratic.py diff --git a/bofire/data_models/surrogates/quadratic.py b/bofire/data_models/surrogates/quadratic.py new file mode 100644 index 000000000..c9291dda6 --- /dev/null +++ b/bofire/data_models/surrogates/quadratic.py @@ -0,0 +1,22 @@ +from typing import Literal + +from pydantic import Field + +from bofire.data_models.kernels.api import ( + LinearKernel, + PolynomialKernel, +) +from bofire.data_models.priors.api import BOTORCH_NOISE_PRIOR, AnyPrior + +# from bofire.data_models.strategies.api import FactorialStrategy +from bofire.data_models.surrogates.botorch import BotorchSurrogate +from bofire.data_models.surrogates.scaler import ScalerEnum +from bofire.data_models.surrogates.trainable import TrainableSurrogate + + +class QuadraticSurrogate(BotorchSurrogate, TrainableSurrogate): + type: Literal["QuadraticSurrogate"] = "QuadraticSurrogate" + + kernel: LinearKernel = Field(default_factory=lambda: PolynomialKernel(power=2)) + noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) + scaler: ScalerEnum = ScalerEnum.NORMALIZE diff --git a/tests/bofire/surrogates/test_quadratic.py b/tests/bofire/surrogates/test_quadratic.py new file mode 100644 index 000000000..43cd61362 --- /dev/null +++ b/tests/bofire/surrogates/test_quadratic.py @@ -0,0 +1,43 @@ +import numpy as np +from pandas.testing import assert_frame_equal + +import bofire.surrogates.api as surrogates +from bofire.data_models.domain.api import Inputs, Outputs +from bofire.data_models.features.api import ContinuousInput, ContinuousOutput +from bofire.data_models.kernels.api import PolynomialKernel +from bofire.data_models.surrogates.api import QuadraticSurrogate + + +def test_QuadraticSurrogate(): + N_EXPERIMENTS = 10 + + inputs = Inputs( + features=[ + ContinuousInput(key="a", bounds=(0, 40)), + ContinuousInput(key="b", bounds=(20, 60)), + ] + ) + outputs = Outputs(features=[ContinuousOutput(key="c")]) + + experiments = inputs.sample(N_EXPERIMENTS) + experiments["c"] = ( + experiments["a"] * 2.2 + + experiments["b"] * -0.05 + + experiments["b"] + + np.random.normal(loc=0, scale=5, size=N_EXPERIMENTS) + ) + experiments["valid_c"] = 1 + + surrogate_data = QuadraticSurrogate(inputs=inputs, outputs=outputs) + surrogate = surrogates.map(surrogate_data) + + assert isinstance(surrogate, surrogates.SingleTaskGPSurrogate) + assert isinstance(surrogate.kernel, PolynomialKernel(power=2)) + + # check dump + surrogate.fit(experiments=experiments) + preds = surrogate.predict(experiments) + dump = surrogate.dumps() + surrogate.loads(dump) + preds2 = surrogate.predict(experiments) + assert_frame_equal(preds, preds2) From 6e0fbc921fc0fc87f263d747c26d1b001528508a Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 15:11:10 +0200 Subject: [PATCH 08/24] adding linear kernel not necessary --- bofire/surrogates/mapper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bofire/surrogates/mapper.py b/bofire/surrogates/mapper.py index 5be8d4f3e..88c7616f1 100644 --- a/bofire/surrogates/mapper.py +++ b/bofire/surrogates/mapper.py @@ -19,6 +19,7 @@ data_models.SaasSingleTaskGPSurrogate: SaasSingleTaskGPSurrogate, data_models.XGBoostSurrogate: XGBoostSurrogate, data_models.LinearSurrogate: SingleTaskGPSurrogate, + data_models.QuadraticSurrogate: SingleTaskGPSurrogate, data_models.TanimotoGPSurrogate: SingleTaskGPSurrogate, } From 26be3f6473f95d6332bf41309f75d00f6117c687 Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 15:23:04 +0200 Subject: [PATCH 09/24] fix test --- tests/bofire/surrogates/test_quadratic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bofire/surrogates/test_quadratic.py b/tests/bofire/surrogates/test_quadratic.py index 43cd61362..149c4989d 100644 --- a/tests/bofire/surrogates/test_quadratic.py +++ b/tests/bofire/surrogates/test_quadratic.py @@ -32,7 +32,7 @@ def test_QuadraticSurrogate(): surrogate = surrogates.map(surrogate_data) assert isinstance(surrogate, surrogates.SingleTaskGPSurrogate) - assert isinstance(surrogate.kernel, PolynomialKernel(power=2)) + assert isinstance(surrogate.kernel, PolynomialKernel) # check dump surrogate.fit(experiments=experiments) From dab5f7f51111376871dee83360dc48d5ec6a2a4b Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 5 Oct 2023 15:42:27 +0200 Subject: [PATCH 10/24] change kernel tzpe hint --- bofire/data_models/surrogates/quadratic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bofire/data_models/surrogates/quadratic.py b/bofire/data_models/surrogates/quadratic.py index c9291dda6..8677acfd0 100644 --- a/bofire/data_models/surrogates/quadratic.py +++ b/bofire/data_models/surrogates/quadratic.py @@ -3,7 +3,6 @@ from pydantic import Field from bofire.data_models.kernels.api import ( - LinearKernel, PolynomialKernel, ) from bofire.data_models.priors.api import BOTORCH_NOISE_PRIOR, AnyPrior @@ -17,6 +16,6 @@ class QuadraticSurrogate(BotorchSurrogate, TrainableSurrogate): type: Literal["QuadraticSurrogate"] = "QuadraticSurrogate" - kernel: LinearKernel = Field(default_factory=lambda: PolynomialKernel(power=2)) + kernel: PolynomialKernel = Field(default_factory=lambda: PolynomialKernel(power=2)) noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) scaler: ScalerEnum = ScalerEnum.NORMALIZE From a7bf562b3991e8f94409df8c80224d0bde5ce0a4 Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 14:58:16 +0200 Subject: [PATCH 11/24] generalize quadratic surrogate --- bofire/data_models/surrogates/api.py | 6 +-- bofire/data_models/surrogates/quadratic.py | 21 ----------- bofire/surrogates/mapper.py | 2 +- tests/bofire/surrogates/test_quadratic.py | 43 ---------------------- 4 files changed, 4 insertions(+), 68 deletions(-) delete mode 100644 bofire/data_models/surrogates/quadratic.py delete mode 100644 tests/bofire/surrogates/test_quadratic.py diff --git a/bofire/data_models/surrogates/api.py b/bofire/data_models/surrogates/api.py index eaf44e852..cf8109636 100644 --- a/bofire/data_models/surrogates/api.py +++ b/bofire/data_models/surrogates/api.py @@ -15,7 +15,7 @@ MixedSingleTaskGPSurrogate, ) from bofire.data_models.surrogates.mlp import MLPEnsemble - from bofire.data_models.surrogates.quadratic import QuadraticSurrogate + from bofire.data_models.surrogates.polynomial import PolynomialSurrogate from bofire.data_models.surrogates.random_forest import RandomForestSurrogate from bofire.data_models.surrogates.single_task_gp import ( SingleTaskGPHyperconfig, @@ -37,7 +37,7 @@ SaasSingleTaskGPSurrogate, XGBoostSurrogate, LinearSurrogate, - QuadraticSurrogate, + PolynomialSurrogate, TanimotoGPSurrogate, ] @@ -49,7 +49,7 @@ SaasSingleTaskGPSurrogate, XGBoostSurrogate, LinearSurrogate, - QuadraticSurrogate, + PolynomialSurrogate, TanimotoGPSurrogate, ] except ImportError: diff --git a/bofire/data_models/surrogates/quadratic.py b/bofire/data_models/surrogates/quadratic.py deleted file mode 100644 index 8677acfd0..000000000 --- a/bofire/data_models/surrogates/quadratic.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Literal - -from pydantic import Field - -from bofire.data_models.kernels.api import ( - PolynomialKernel, -) -from bofire.data_models.priors.api import BOTORCH_NOISE_PRIOR, AnyPrior - -# from bofire.data_models.strategies.api import FactorialStrategy -from bofire.data_models.surrogates.botorch import BotorchSurrogate -from bofire.data_models.surrogates.scaler import ScalerEnum -from bofire.data_models.surrogates.trainable import TrainableSurrogate - - -class QuadraticSurrogate(BotorchSurrogate, TrainableSurrogate): - type: Literal["QuadraticSurrogate"] = "QuadraticSurrogate" - - kernel: PolynomialKernel = Field(default_factory=lambda: PolynomialKernel(power=2)) - noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) - scaler: ScalerEnum = ScalerEnum.NORMALIZE diff --git a/bofire/surrogates/mapper.py b/bofire/surrogates/mapper.py index 88c7616f1..a4f588085 100644 --- a/bofire/surrogates/mapper.py +++ b/bofire/surrogates/mapper.py @@ -19,7 +19,7 @@ data_models.SaasSingleTaskGPSurrogate: SaasSingleTaskGPSurrogate, data_models.XGBoostSurrogate: XGBoostSurrogate, data_models.LinearSurrogate: SingleTaskGPSurrogate, - data_models.QuadraticSurrogate: SingleTaskGPSurrogate, + data_models.PolynomialSurrogate: SingleTaskGPSurrogate, data_models.TanimotoGPSurrogate: SingleTaskGPSurrogate, } diff --git a/tests/bofire/surrogates/test_quadratic.py b/tests/bofire/surrogates/test_quadratic.py deleted file mode 100644 index 149c4989d..000000000 --- a/tests/bofire/surrogates/test_quadratic.py +++ /dev/null @@ -1,43 +0,0 @@ -import numpy as np -from pandas.testing import assert_frame_equal - -import bofire.surrogates.api as surrogates -from bofire.data_models.domain.api import Inputs, Outputs -from bofire.data_models.features.api import ContinuousInput, ContinuousOutput -from bofire.data_models.kernels.api import PolynomialKernel -from bofire.data_models.surrogates.api import QuadraticSurrogate - - -def test_QuadraticSurrogate(): - N_EXPERIMENTS = 10 - - inputs = Inputs( - features=[ - ContinuousInput(key="a", bounds=(0, 40)), - ContinuousInput(key="b", bounds=(20, 60)), - ] - ) - outputs = Outputs(features=[ContinuousOutput(key="c")]) - - experiments = inputs.sample(N_EXPERIMENTS) - experiments["c"] = ( - experiments["a"] * 2.2 - + experiments["b"] * -0.05 - + experiments["b"] - + np.random.normal(loc=0, scale=5, size=N_EXPERIMENTS) - ) - experiments["valid_c"] = 1 - - surrogate_data = QuadraticSurrogate(inputs=inputs, outputs=outputs) - surrogate = surrogates.map(surrogate_data) - - assert isinstance(surrogate, surrogates.SingleTaskGPSurrogate) - assert isinstance(surrogate.kernel, PolynomialKernel) - - # check dump - surrogate.fit(experiments=experiments) - preds = surrogate.predict(experiments) - dump = surrogate.dumps() - surrogate.loads(dump) - preds2 = surrogate.predict(experiments) - assert_frame_equal(preds, preds2) From 62d79089202ea3d836667e0bba2bc83b625a03c8 Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 15:01:42 +0200 Subject: [PATCH 12/24] generalize quadratic surrogate --- bofire/data_models/surrogates/polynomial.py | 24 ++++++++++++ tests/bofire/surrogates/test_polynomial.py | 43 +++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 bofire/data_models/surrogates/polynomial.py create mode 100644 tests/bofire/surrogates/test_polynomial.py diff --git a/bofire/data_models/surrogates/polynomial.py b/bofire/data_models/surrogates/polynomial.py new file mode 100644 index 000000000..5503db11d --- /dev/null +++ b/bofire/data_models/surrogates/polynomial.py @@ -0,0 +1,24 @@ +from typing import Literal + +from pydantic import Field + +from bofire.data_models.kernels.api import ( + PolynomialKernel, +) +from bofire.data_models.priors.api import BOTORCH_NOISE_PRIOR, AnyPrior + +# from bofire.data_models.strategies.api import FactorialStrategy +from bofire.data_models.surrogates.botorch import BotorchSurrogate +from bofire.data_models.surrogates.scaler import ScalerEnum +from bofire.data_models.surrogates.trainable import TrainableSurrogate + + +class PolynomialSurrogate(BotorchSurrogate, TrainableSurrogate): + type: Literal["PolynomialSurrogate"] = "PolynomialSurrogate" + noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) + scaler: ScalerEnum = ScalerEnum.NORMALIZE + + def __init__(self, power=2): + self.kernel: PolynomialKernel = Field( + default_factory=lambda: PolynomialKernel(power=power) + ) diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py new file mode 100644 index 000000000..f6b504cf4 --- /dev/null +++ b/tests/bofire/surrogates/test_polynomial.py @@ -0,0 +1,43 @@ +import numpy as np +from pandas.testing import assert_frame_equal + +import bofire.surrogates.api as surrogates +from bofire.data_models.domain.api import Inputs, Outputs +from bofire.data_models.features.api import ContinuousInput, ContinuousOutput +from bofire.data_models.kernels.api import PolynomialKernel +from bofire.data_models.surrogates.api import PolynomialSurrogate + + +def test_QuadraticSurrogate(): + N_EXPERIMENTS = 10 + + inputs = Inputs( + features=[ + ContinuousInput(key="a", bounds=(0, 40)), + ContinuousInput(key="b", bounds=(20, 60)), + ] + ) + outputs = Outputs(features=[ContinuousOutput(key="c")]) + + experiments = inputs.sample(N_EXPERIMENTS) + experiments["c"] = ( + experiments["a"] * 2.2 + + experiments["b"] * -0.05 + + experiments["b"] + + np.random.normal(loc=0, scale=5, size=N_EXPERIMENTS) + ) + experiments["valid_c"] = 1 + + surrogate_data = PolynomialSurrogate(inputs=inputs, outputs=outputs, power=2) + surrogate = surrogates.map(surrogate_data) + + assert isinstance(surrogate, surrogates.SingleTaskGPSurrogate) + assert isinstance(surrogate.kernel, PolynomialKernel) + + # check dump + surrogate.fit(experiments=experiments) + preds = surrogate.predict(experiments) + dump = surrogate.dumps() + surrogate.loads(dump) + preds2 = surrogate.predict(experiments) + assert_frame_equal(preds, preds2) From ebbff71e98234d8cd113fa32369698c8981aea2f Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 15:19:47 +0200 Subject: [PATCH 13/24] generalize quadratic surrogate --- bofire/data_models/surrogates/quadratic.py | 21 ----------- tests/bofire/surrogates/test_quadratic.py | 43 ---------------------- 2 files changed, 64 deletions(-) delete mode 100644 bofire/data_models/surrogates/quadratic.py delete mode 100644 tests/bofire/surrogates/test_quadratic.py diff --git a/bofire/data_models/surrogates/quadratic.py b/bofire/data_models/surrogates/quadratic.py deleted file mode 100644 index 8677acfd0..000000000 --- a/bofire/data_models/surrogates/quadratic.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Literal - -from pydantic import Field - -from bofire.data_models.kernels.api import ( - PolynomialKernel, -) -from bofire.data_models.priors.api import BOTORCH_NOISE_PRIOR, AnyPrior - -# from bofire.data_models.strategies.api import FactorialStrategy -from bofire.data_models.surrogates.botorch import BotorchSurrogate -from bofire.data_models.surrogates.scaler import ScalerEnum -from bofire.data_models.surrogates.trainable import TrainableSurrogate - - -class QuadraticSurrogate(BotorchSurrogate, TrainableSurrogate): - type: Literal["QuadraticSurrogate"] = "QuadraticSurrogate" - - kernel: PolynomialKernel = Field(default_factory=lambda: PolynomialKernel(power=2)) - noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) - scaler: ScalerEnum = ScalerEnum.NORMALIZE diff --git a/tests/bofire/surrogates/test_quadratic.py b/tests/bofire/surrogates/test_quadratic.py deleted file mode 100644 index 149c4989d..000000000 --- a/tests/bofire/surrogates/test_quadratic.py +++ /dev/null @@ -1,43 +0,0 @@ -import numpy as np -from pandas.testing import assert_frame_equal - -import bofire.surrogates.api as surrogates -from bofire.data_models.domain.api import Inputs, Outputs -from bofire.data_models.features.api import ContinuousInput, ContinuousOutput -from bofire.data_models.kernels.api import PolynomialKernel -from bofire.data_models.surrogates.api import QuadraticSurrogate - - -def test_QuadraticSurrogate(): - N_EXPERIMENTS = 10 - - inputs = Inputs( - features=[ - ContinuousInput(key="a", bounds=(0, 40)), - ContinuousInput(key="b", bounds=(20, 60)), - ] - ) - outputs = Outputs(features=[ContinuousOutput(key="c")]) - - experiments = inputs.sample(N_EXPERIMENTS) - experiments["c"] = ( - experiments["a"] * 2.2 - + experiments["b"] * -0.05 - + experiments["b"] - + np.random.normal(loc=0, scale=5, size=N_EXPERIMENTS) - ) - experiments["valid_c"] = 1 - - surrogate_data = QuadraticSurrogate(inputs=inputs, outputs=outputs) - surrogate = surrogates.map(surrogate_data) - - assert isinstance(surrogate, surrogates.SingleTaskGPSurrogate) - assert isinstance(surrogate.kernel, PolynomialKernel) - - # check dump - surrogate.fit(experiments=experiments) - preds = surrogate.predict(experiments) - dump = surrogate.dumps() - surrogate.loads(dump) - preds2 = surrogate.predict(experiments) - assert_frame_equal(preds, preds2) From 081ab8fe9a78d49cce4e9179dab268e8e4037e6c Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 15:35:25 +0200 Subject: [PATCH 14/24] fix init --- bofire/data_models/surrogates/polynomial.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/bofire/data_models/surrogates/polynomial.py b/bofire/data_models/surrogates/polynomial.py index 5503db11d..4c812a179 100644 --- a/bofire/data_models/surrogates/polynomial.py +++ b/bofire/data_models/surrogates/polynomial.py @@ -2,6 +2,8 @@ from pydantic import Field +from bofire.data_models.domain.api import Inputs, Outputs +from bofire.data_models.features.api import TInputTransformSpecs from bofire.data_models.kernels.api import ( PolynomialKernel, ) @@ -18,7 +20,22 @@ class PolynomialSurrogate(BotorchSurrogate, TrainableSurrogate): noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) scaler: ScalerEnum = ScalerEnum.NORMALIZE - def __init__(self, power=2): + def __init__( + self, + type: str, + inputs: Inputs, + outputs: Outputs, + power: int, + input_preprocessing_specs: TInputTransformSpecs = dict, + dump: str | None = None, + ): + super().__init__( + type=type, + inputs=inputs, + outputs=outputs, + input_preprocessing_specs=input_preprocessing_specs, + dump=dump, + ) self.kernel: PolynomialKernel = Field( default_factory=lambda: PolynomialKernel(power=power) ) From a0551b75733958696a61d043576d508f9dbd816f Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 15:38:23 +0200 Subject: [PATCH 15/24] fix init --- bofire/data_models/surrogates/polynomial.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bofire/data_models/surrogates/polynomial.py b/bofire/data_models/surrogates/polynomial.py index 4c812a179..028c694b7 100644 --- a/bofire/data_models/surrogates/polynomial.py +++ b/bofire/data_models/surrogates/polynomial.py @@ -3,7 +3,6 @@ from pydantic import Field from bofire.data_models.domain.api import Inputs, Outputs -from bofire.data_models.features.api import TInputTransformSpecs from bofire.data_models.kernels.api import ( PolynomialKernel, ) @@ -26,15 +25,11 @@ def __init__( inputs: Inputs, outputs: Outputs, power: int, - input_preprocessing_specs: TInputTransformSpecs = dict, - dump: str | None = None, ): super().__init__( type=type, inputs=inputs, outputs=outputs, - input_preprocessing_specs=input_preprocessing_specs, - dump=dump, ) self.kernel: PolynomialKernel = Field( default_factory=lambda: PolynomialKernel(power=power) From 5bbe12ee89d4d1af6e96df633f609b712d473c53 Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 15:50:50 +0200 Subject: [PATCH 16/24] fix init --- bofire/data_models/surrogates/polynomial.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bofire/data_models/surrogates/polynomial.py b/bofire/data_models/surrogates/polynomial.py index 028c694b7..b2c7a72bc 100644 --- a/bofire/data_models/surrogates/polynomial.py +++ b/bofire/data_models/surrogates/polynomial.py @@ -21,13 +21,11 @@ class PolynomialSurrogate(BotorchSurrogate, TrainableSurrogate): def __init__( self, - type: str, inputs: Inputs, outputs: Outputs, power: int, ): super().__init__( - type=type, inputs=inputs, outputs=outputs, ) From 2ce616ea14cf5180f12bbcd8aefa9c8b5e676fc8 Mon Sep 17 00:00:00 2001 From: linznedd Date: Fri, 6 Oct 2023 15:51:27 +0200 Subject: [PATCH 17/24] fix init --- tests/bofire/surrogates/test_polynomial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py index f6b504cf4..1a3557c35 100644 --- a/tests/bofire/surrogates/test_polynomial.py +++ b/tests/bofire/surrogates/test_polynomial.py @@ -8,7 +8,7 @@ from bofire.data_models.surrogates.api import PolynomialSurrogate -def test_QuadraticSurrogate(): +def test_polynomial_surrogate(): N_EXPERIMENTS = 10 inputs = Inputs( From 4a69e53337951dedcf1219e81ec8cd3e415264b6 Mon Sep 17 00:00:00 2001 From: linznedd Date: Mon, 9 Oct 2023 15:39:47 +0200 Subject: [PATCH 18/24] fix init --- bofire/data_models/surrogates/polynomial.py | 18 ++++++------------ tests/bofire/surrogates/test_polynomial.py | 4 +++- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/bofire/data_models/surrogates/polynomial.py b/bofire/data_models/surrogates/polynomial.py index b2c7a72bc..bd2225374 100644 --- a/bofire/data_models/surrogates/polynomial.py +++ b/bofire/data_models/surrogates/polynomial.py @@ -16,19 +16,13 @@ class PolynomialSurrogate(BotorchSurrogate, TrainableSurrogate): type: Literal["PolynomialSurrogate"] = "PolynomialSurrogate" + + kernel: PolynomialKernel = Field(default_factory=lambda: PolynomialKernel(power=2)) noise_prior: AnyPrior = Field(default_factory=lambda: BOTORCH_NOISE_PRIOR()) scaler: ScalerEnum = ScalerEnum.NORMALIZE - def __init__( - self, - inputs: Inputs, - outputs: Outputs, - power: int, - ): - super().__init__( - inputs=inputs, - outputs=outputs, - ) - self.kernel: PolynomialKernel = Field( - default_factory=lambda: PolynomialKernel(power=power) + @staticmethod + def from_power(power: int, inputs: Inputs, outputs: Outputs): + return PolynomialSurrogate( + kernel=PolynomialKernel(power=power), inputs=inputs, outputs=outputs ) diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py index 1a3557c35..62fb3238f 100644 --- a/tests/bofire/surrogates/test_polynomial.py +++ b/tests/bofire/surrogates/test_polynomial.py @@ -28,7 +28,9 @@ def test_polynomial_surrogate(): ) experiments["valid_c"] = 1 - surrogate_data = PolynomialSurrogate(inputs=inputs, outputs=outputs, power=2) + surrogate_data = PolynomialSurrogate.from_power( + power=2, inputs=inputs, outputs=outputs + ) surrogate = surrogates.map(surrogate_data) assert isinstance(surrogate, surrogates.SingleTaskGPSurrogate) From 34aa157668ae4b48ca397858967d1a5f2b2b89e8 Mon Sep 17 00:00:00 2001 From: linznedd Date: Tue, 10 Oct 2023 16:06:54 +0200 Subject: [PATCH 19/24] add polynomial_surrogate to AnyBotochSurrogate --- bofire/data_models/surrogates/botorch_surrogates.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bofire/data_models/surrogates/botorch_surrogates.py b/bofire/data_models/surrogates/botorch_surrogates.py index b78f6c778..44bdc6b72 100644 --- a/bofire/data_models/surrogates/botorch_surrogates.py +++ b/bofire/data_models/surrogates/botorch_surrogates.py @@ -13,6 +13,7 @@ MixedSingleTaskGPSurrogate, ) from bofire.data_models.surrogates.mlp import MLPEnsemble +from bofire.data_models.surrogates.polynomial import PolynomialSurrogate from bofire.data_models.surrogates.random_forest import RandomForestSurrogate from bofire.data_models.surrogates.single_task_gp import SingleTaskGPSurrogate from bofire.data_models.surrogates.tanimoto_gp import TanimotoGPSurrogate @@ -26,6 +27,7 @@ SaasSingleTaskGPSurrogate, TanimotoGPSurrogate, LinearSurrogate, + PolynomialSurrogate, ] From 01f5d213fa5c0a1bc71abf6ced642149104a51e3 Mon Sep 17 00:00:00 2001 From: linznedd Date: Tue, 10 Oct 2023 16:21:05 +0200 Subject: [PATCH 20/24] add test that botorch surrogate can be composed --- tests/bofire/surrogates/test_linear.py | 21 +++++++++++++++++- tests/bofire/surrogates/test_polynomial.py | 25 +++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/tests/bofire/surrogates/test_linear.py b/tests/bofire/surrogates/test_linear.py index fe6fbb180..7507b4136 100644 --- a/tests/bofire/surrogates/test_linear.py +++ b/tests/bofire/surrogates/test_linear.py @@ -5,7 +5,7 @@ from bofire.data_models.domain.api import Inputs, Outputs from bofire.data_models.features.api import ContinuousInput, ContinuousOutput from bofire.data_models.kernels.api import LinearKernel -from bofire.data_models.surrogates.api import LinearSurrogate +from bofire.data_models.surrogates.api import BotorchSurrogates, LinearSurrogate def test_LinearSurrogate(): @@ -41,3 +41,22 @@ def test_LinearSurrogate(): surrogate.loads(dump) preds2 = surrogate.predict(experiments) assert_frame_equal(preds, preds2) + + +def test_can_define_botorch_surrogate(): + inputs = Inputs( + features=[ + ContinuousInput(key="a", bounds=(0, 40)), + ContinuousInput(key="b", bounds=(20, 60)), + ] + ) + outputs = Outputs(features=[ContinuousOutput(key="c"), ContinuousOutput(key="d")]) + _ = ( + BotorchSurrogates( + surrogates=[ + LinearSurrogate(inputs=inputs, outputs=Outputs(features=[outputs[0]])), + LinearSurrogate(inputs=inputs, outputs=Outputs(features=[outputs[1]])), + ] + ), + ) + assert True diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py index 62fb3238f..b991ed2ea 100644 --- a/tests/bofire/surrogates/test_polynomial.py +++ b/tests/bofire/surrogates/test_polynomial.py @@ -5,7 +5,7 @@ from bofire.data_models.domain.api import Inputs, Outputs from bofire.data_models.features.api import ContinuousInput, ContinuousOutput from bofire.data_models.kernels.api import PolynomialKernel -from bofire.data_models.surrogates.api import PolynomialSurrogate +from bofire.data_models.surrogates.api import BotorchSurrogates, PolynomialSurrogate def test_polynomial_surrogate(): @@ -43,3 +43,26 @@ def test_polynomial_surrogate(): surrogate.loads(dump) preds2 = surrogate.predict(experiments) assert_frame_equal(preds, preds2) + + +def test_can_define_botorch_surrogate(): + inputs = Inputs( + features=[ + ContinuousInput(key="a", bounds=(0, 40)), + ContinuousInput(key="b", bounds=(20, 60)), + ] + ) + outputs = Outputs(features=[ContinuousOutput(key="c"), ContinuousOutput(key="d")]) + _ = ( + BotorchSurrogates( + surrogates=[ + PolynomialSurrogate( + inputs=inputs, outputs=Outputs(features=[outputs[0]]) + ), + PolynomialSurrogate( + inputs=inputs, outputs=Outputs(features=[outputs[1]]) + ), + ] + ), + ) + assert True From 8d1351cb13f70dfcd1f0f631594e2677e6e42c15 Mon Sep 17 00:00:00 2001 From: linznedd Date: Tue, 10 Oct 2023 16:22:25 +0200 Subject: [PATCH 21/24] add test that botorch surrogate can be composed --- tests/bofire/surrogates/test_linear.py | 2 +- tests/bofire/surrogates/test_polynomial.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bofire/surrogates/test_linear.py b/tests/bofire/surrogates/test_linear.py index 7507b4136..411b238aa 100644 --- a/tests/bofire/surrogates/test_linear.py +++ b/tests/bofire/surrogates/test_linear.py @@ -50,7 +50,7 @@ def test_can_define_botorch_surrogate(): ContinuousInput(key="b", bounds=(20, 60)), ] ) - outputs = Outputs(features=[ContinuousOutput(key="c"), ContinuousOutput(key="d")]) + outputs = [ContinuousOutput(key="c"), ContinuousOutput(key="d")] _ = ( BotorchSurrogates( surrogates=[ diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py index b991ed2ea..60bca638e 100644 --- a/tests/bofire/surrogates/test_polynomial.py +++ b/tests/bofire/surrogates/test_polynomial.py @@ -52,7 +52,7 @@ def test_can_define_botorch_surrogate(): ContinuousInput(key="b", bounds=(20, 60)), ] ) - outputs = Outputs(features=[ContinuousOutput(key="c"), ContinuousOutput(key="d")]) + outputs = [ContinuousOutput(key="c"), ContinuousOutput(key="d")] _ = ( BotorchSurrogates( surrogates=[ From 93320de8f007dab51f102f7ae2f16bc8efb7eb85 Mon Sep 17 00:00:00 2001 From: linznedd Date: Tue, 10 Oct 2023 17:33:14 +0200 Subject: [PATCH 22/24] add test that botorch surrogate can be composed --- tests/bofire/surrogates/test_linear.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bofire/surrogates/test_linear.py b/tests/bofire/surrogates/test_linear.py index 411b238aa..bb1f611ee 100644 --- a/tests/bofire/surrogates/test_linear.py +++ b/tests/bofire/surrogates/test_linear.py @@ -47,7 +47,7 @@ def test_can_define_botorch_surrogate(): inputs = Inputs( features=[ ContinuousInput(key="a", bounds=(0, 40)), - ContinuousInput(key="b", bounds=(20, 60)), + ContinuousInput(key="b", bounds=(20, 80)), ] ) outputs = [ContinuousOutput(key="c"), ContinuousOutput(key="d")] From 12a7c0473be916d2773a7afc72a7640178ce4483 Mon Sep 17 00:00:00 2001 From: linznedd Date: Wed, 11 Oct 2023 09:03:55 +0200 Subject: [PATCH 23/24] add test that botorch surrogate can be composed --- tests/bofire/surrogates/test_polynomial.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py index f4acb3022..6e255913b 100644 --- a/tests/bofire/surrogates/test_polynomial.py +++ b/tests/bofire/surrogates/test_polynomial.py @@ -44,12 +44,12 @@ def test_polynomial_surrogate(): preds2 = surrogate.predict(experiments) assert_frame_equal(preds, preds2) - + def test_can_define_botorch_surrogate(): inputs = Inputs( features=[ ContinuousInput(key="a", bounds=(0, 40)), - ContinuousInput(key="b", bounds=(20, 60)), + ContinuousInput(key="b", bounds=(20, 80)), ] ) outputs = [ContinuousOutput(key="c"), ContinuousOutput(key="d")] @@ -66,4 +66,3 @@ def test_can_define_botorch_surrogate(): ), ) assert True - From ebb41fc0a0b2906d04ed1fbaa8ee92f6ff0da498 Mon Sep 17 00:00:00 2001 From: linznedd Date: Thu, 12 Oct 2023 08:32:08 +0200 Subject: [PATCH 24/24] add test that botorch surrogate can be composed --- tests/bofire/surrogates/test_linear.py | 3 +-- tests/bofire/surrogates/test_polynomial.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/bofire/surrogates/test_linear.py b/tests/bofire/surrogates/test_linear.py index bb1f611ee..d9549e328 100644 --- a/tests/bofire/surrogates/test_linear.py +++ b/tests/bofire/surrogates/test_linear.py @@ -51,7 +51,7 @@ def test_can_define_botorch_surrogate(): ] ) outputs = [ContinuousOutput(key="c"), ContinuousOutput(key="d")] - _ = ( + ( BotorchSurrogates( surrogates=[ LinearSurrogate(inputs=inputs, outputs=Outputs(features=[outputs[0]])), @@ -59,4 +59,3 @@ def test_can_define_botorch_surrogate(): ] ), ) - assert True diff --git a/tests/bofire/surrogates/test_polynomial.py b/tests/bofire/surrogates/test_polynomial.py index 6e255913b..c8264b9ab 100644 --- a/tests/bofire/surrogates/test_polynomial.py +++ b/tests/bofire/surrogates/test_polynomial.py @@ -53,7 +53,7 @@ def test_can_define_botorch_surrogate(): ] ) outputs = [ContinuousOutput(key="c"), ContinuousOutput(key="d")] - _ = ( + ( BotorchSurrogates( surrogates=[ PolynomialSurrogate( @@ -65,4 +65,3 @@ def test_can_define_botorch_surrogate(): ] ), ) - assert True