Skip to content

Commit

Permalink
Multiplicative additive sobo objectives (#481)
Browse files Browse the repository at this point in the history
* added MultiplicativeAdditive data model

* added actual multiplicative model (callable is missing)

* added torch functions

* added test for objective

* added test for sobo stategy: multiplicative_additive

* changed additive/multiplicative calculations:
- Removed scaling by x**(1/(....)) to avoid numerical errors, if x<0
- included weight transformation for multiplicative objectives from (0, 1] to [1, inf) scale to avoid numerical errors at weights != 1.0
- added tests for weights != 1.0

* added notebook for comparison of merging objectives

* after hooks

* addet .idea/ folder (pycharm) to gitignore

* after hooks

* Apply pre-commit fixes

* Delete .idea directory

* corrected tests for multiplicative_additive_botorch_objective

* after pre-commit

* lint specifications

* corrected weightings calc in test for multiplicative objective

* after hooks

* changed docstrings to google docstring format

* easy fixes, spelling errors

* forgot linting

* easy fixes, spelling errors

* removed denominator additive from multiplicative_additive_sobo strategy

* after hooks

* fixed typing

* tensor initialization of objectives

* after hooks

* avoiding torch size error

* avoid linting error

* after hooks

* reverting test-renaming

* revert isinstance list comprehension to tuple.... solution

* testing copilot suggestions for linting errors

* reverting wrong copilot suggestions

* added test for _callables_and_weights

* after hooks

* added test for SOBO strategy data model

* added test for SOBO strategy data model

* added new sobo strategy to a mysterious list

* after hooks

* still trying to get rid of the linting error, expecting tuple(types)

* WIP

* WIP

* WIP

* WIP

* WIP

* minor corrections
  • Loading branch information
LukasHebing authored Jan 9, 2025
1 parent 9bb1a36 commit 43f3601
Show file tree
Hide file tree
Showing 11 changed files with 726 additions and 98 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ venv.bak/
.vscode/
.devcontainer

# pycharm project settings
.idea/

# Rope project settings
.ropeproject

Expand Down
2 changes: 2 additions & 0 deletions bofire/data_models/strategies/actual_strategy_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from bofire.data_models.strategies.predictives.sobo import (
AdditiveSoboStrategy,
CustomSoboStrategy,
MultiplicativeAdditiveSoboStrategy,
MultiplicativeSoboStrategy,
SoboStrategy,
)
Expand All @@ -32,6 +33,7 @@
AdditiveSoboStrategy,
ActiveLearningStrategy,
MultiplicativeSoboStrategy,
MultiplicativeAdditiveSoboStrategy,
CustomSoboStrategy,
MultiFidelityStrategy,
QehviStrategy,
Expand Down
2 changes: 2 additions & 0 deletions bofire/data_models/strategies/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from bofire.data_models.strategies.predictives.sobo import (
AdditiveSoboStrategy,
CustomSoboStrategy,
MultiplicativeAdditiveSoboStrategy,
MultiplicativeSoboStrategy,
SoboStrategy,
)
Expand Down Expand Up @@ -61,6 +62,7 @@
ActiveLearningStrategy,
AdditiveSoboStrategy,
MultiplicativeSoboStrategy,
MultiplicativeAdditiveSoboStrategy,
CustomSoboStrategy,
QehviStrategy,
QnehviStrategy,
Expand Down
55 changes: 52 additions & 3 deletions bofire/data_models/strategies/predictives/sobo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Literal, Optional, Type
from typing import List, Literal, Optional, Type

from pydantic import Field, field_validator
import pydantic
from pydantic import Field, field_validator, model_validator

from bofire.data_models.acquisition_functions.api import (
AnySingleObjectiveAcquisitionFunction,
Expand Down Expand Up @@ -74,7 +75,25 @@ def validate_is_multiobjective(cls, v, info):
return v


class MultiplicativeSoboStrategy(SoboBaseStrategy):
class _CheckAdaptableWeightsMixin:
"""
Contains an additional validator for weights in multiplicative objective merging.
Additional validation of weights for adaptable weights, in multiplicative calculations. Adaption to (1, inf)
requires w>=1e-8
"""

@model_validator(mode="after")
def check_adaptable_weights(cls, self):
for obj in self.domain.outputs.get_by_objective():
if obj.objective.w < 1e-8:
raise pydantic.ValidationError(
f"Weight transformation to (1, inf) requires w>=1e-8 . Violated by feature {obj.key}."
)
return self


class MultiplicativeSoboStrategy(SoboBaseStrategy, _CheckAdaptableWeightsMixin):
type: Literal["MultiplicativeSoboStrategy"] = "MultiplicativeSoboStrategy"

@field_validator("domain")
Expand All @@ -86,6 +105,36 @@ def validate_is_multiobjective(cls, v, info):
return v


class MultiplicativeAdditiveSoboStrategy(SoboBaseStrategy, _CheckAdaptableWeightsMixin):
"""
Mixed, weighted multiplicative (primary, strict) and additive (secondary, non-strict) objectives.
The formular for a mixed objective with two multiplicative features (f1, and f2 with weights w1 and w2) and two
additive features (f3 and f4 with weights w3 and w4) is:
additive_objective = 1 + f3*w3 + f4*w4
objective = f1^w1 * f2^w2 * additive_objective
"""

type: Literal["MultiplicativeAdditiveSoboStrategy"] = (
"MultiplicativeAdditiveSoboStrategy"
)
use_output_constraints: bool = True
additive_features: List[str] = Field(default_factory=list)

@field_validator("additive_features")
def validate_additive_features(cls, v, values):
domain = values.data["domain"]
for feature in v:
if feature not in domain.outputs.get_keys():
raise ValueError(
f"Feature {feature} is not an output feature of the domain."
)
return v


class CustomSoboStrategy(SoboBaseStrategy):
type: Literal["CustomSoboStrategy"] = "CustomSoboStrategy"
use_output_constraints: bool = True
Expand Down
2 changes: 2 additions & 0 deletions bofire/strategies/mapper_actual.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from bofire.strategies.predictives.sobo import (
AdditiveSoboStrategy,
CustomSoboStrategy,
MultiplicativeAdditiveSoboStrategy,
MultiplicativeSoboStrategy,
SoboStrategy,
)
Expand All @@ -29,6 +30,7 @@
data_models.SoboStrategy: SoboStrategy,
data_models.AdditiveSoboStrategy: AdditiveSoboStrategy,
data_models.MultiplicativeSoboStrategy: MultiplicativeSoboStrategy,
data_models.MultiplicativeAdditiveSoboStrategy: MultiplicativeAdditiveSoboStrategy,
data_models.CustomSoboStrategy: CustomSoboStrategy,
data_models.MultiFidelityStrategy: MultiFidelityStrategy,
data_models.QehviStrategy: QehviStrategy,
Expand Down
37 changes: 37 additions & 0 deletions bofire/strategies/predictives/sobo.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
from bofire.data_models.objectives.api import ConstrainedObjective, Objective
from bofire.data_models.strategies.api import AdditiveSoboStrategy as AdditiveDataModel
from bofire.data_models.strategies.api import CustomSoboStrategy as CustomDataModel
from bofire.data_models.strategies.api import (
MultiplicativeAdditiveSoboStrategy as MultiplicativeAdditiveDataModel,
)
from bofire.data_models.strategies.api import (
MultiplicativeSoboStrategy as MultiplicativeDataModel,
)
Expand All @@ -28,6 +31,7 @@
from bofire.utils.torch_tools import (
get_additive_botorch_objective,
get_custom_botorch_objective,
get_multiplicative_additive_objective,
get_multiplicative_botorch_objective,
get_objective_callable,
get_output_constraints,
Expand Down Expand Up @@ -240,13 +244,46 @@ def _get_objective_and_constraints(
objective=get_multiplicative_botorch_objective( # type: ignore
outputs=self.domain.outputs,
experiments=self.experiments,
adapt_weights_to_1_inf=True,
),
),
None,
1e-3,
)


class MultiplicativeAdditiveSoboStrategy(SoboStrategy):
def __init__(
self,
data_model: MultiplicativeAdditiveDataModel,
**kwargs,
):
self.additive_features = data_model.additive_features
super().__init__(data_model=data_model, **kwargs)

def _get_objective_and_constraints(
self,
) -> Tuple[
GenericMCObjective,
Union[List[Callable[[torch.Tensor], torch.Tensor]], None],
Union[List, float],
]:
# we absorb all constraints into the objective
assert self.experiments is not None, "No experiments available."
return (
GenericMCObjective(
objective=get_multiplicative_additive_objective( # type: ignore
outputs=self.domain.outputs,
experiments=self.experiments,
additive_features=self.additive_features,
adapt_weights_to_1_inf=True,
)
),
None,
1e-3,
)


class CustomSoboStrategy(SoboStrategy):
def __init__(
self,
Expand Down
Loading

0 comments on commit 43f3601

Please sign in to comment.