Skip to content

Commit

Permalink
Replace normalization and auto_normalize keyes with scaling and auto_…
Browse files Browse the repository at this point in the history
…scale in ObjectiveFunctionConfig
  • Loading branch information
DanSava committed Feb 5, 2025
1 parent a522de5 commit b544d6a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 16 deletions.
62 changes: 50 additions & 12 deletions src/everest/config/objective_function_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from pydantic import BaseModel, Field, PositiveFloat, field_validator
from typing import Self

from pydantic import BaseModel, Field, PositiveFloat, field_validator, model_validator

from ert.config.parsing import ConfigWarning


class ObjectiveFunctionConfig(BaseModel, extra="forbid"): # type: ignore
Expand Down Expand Up @@ -29,18 +33,30 @@ class ObjectiveFunctionConfig(BaseModel, extra="forbid"): # type: ignore
normalization: float | None = Field(
default=None,
description="""
normalization is a multiplication factor defined per objective function.
The value of each objective function is multiplied by the related normalization value.
When optimizing with respect to multiple objective functions, it is important
that the normalization is set so that all the normalized objectives have the same order
of magnitude. Ultimately, the normalized objectives are used in computing
the weighted sum that Everest tries to optimize.
normalization key is deprecated and has been replaced with scaling
""",
)
scaling: float | None = Field(
default=None,
description="""
scaling is a multiplication factor defined per objective function.
The value of each objective function is multiplied by the related scaling value.
When optimizing with respect to multiple objective functions, it is important
that the scaling is set so that all the scaled objectives have the same order
of magnitude. Ultimately, the scaled objectives are used in computing
the weighted sum that Everest tries to optimize.
""",
)
auto_normalize: bool | None = Field(
default=None,
description="""
auto_normalize key is deprecated has been replaced with auto_scale.
""",
)
auto_scale: bool | None = Field(
default=None,
description="""
auto_normalize can be set to true to automatically
determine the normalization factor from the objective value in batch 0.
Expand All @@ -61,9 +77,31 @@ class ObjectiveFunctionConfig(BaseModel, extra="forbid"): # type: ignore
""",
)

@field_validator("normalization")
@field_validator("scaling")
@classmethod
def validate_normalization_is_not_zero(cls, normalization): # pylint: disable=E0213
if normalization == 0.0:
def validate_scaling_is_not_zero(cls, scaling) -> float | None:
if scaling == 0.0:
raise ValueError("Normalization value cannot be zero")
return normalization
return scaling

@model_validator(mode="after")
def deprecate_normalization(self) -> Self:
if self.normalization is not None:
ConfigWarning.deprecation_warn(
"normalization key is deprecated and has been replaced with scaling"
)
if self.auto_normalize is not None:
ConfigWarning.deprecation_warn(
"auto_normalize key is deprecated and has been replaced with auto_scale"
)
return self

@model_validator(mode="after")
def make_scaling_backwards_compatible(self) -> Self:
if self.scaling is None and self.normalization is not None:
self.scaling = self.normalization
if self.scaling == 0.0:
raise ValueError("Scaling value cannot be zero")
if self.auto_scale is None and self.auto_normalize is not None:
self.auto_scale = self.auto_normalize
return self
2 changes: 1 addition & 1 deletion src/everest/everest_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def init(self, everest_config: EverestConfig) -> None:
"weight": pl.Series(weights / sum(weights), dtype=pl.Float64),
"normalization": pl.Series(
[
1.0 if obj.normalization is None else obj.normalization
1.0 if obj.scaling is None else obj.scaling
for obj in everest_config.objective_functions
],
dtype=pl.Float64,
Expand Down
4 changes: 2 additions & 2 deletions src/everest/optimizer/everest2ropt.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def _parse_objectives(objective_functions: list[ObjectiveFunctionConfig], ropt_c
for objective in objective_functions:
assert isinstance(objective.name, str)
weights.append(objective.weight or 1.0)
scales.append(1.0 / (objective.normalization or 1.0))
auto_scale.append(objective.auto_normalize or False)
scales.append(1.0 / (objective.scaling or 1.0))
auto_scale.append(objective.auto_scale or False)

# If any objective specifies an objective type, we have to specify
# function estimators in ropt to implement these types. This is done by
Expand Down
53 changes: 52 additions & 1 deletion tests/everest/test_config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from ert.config import ConfigWarning
from ert.config.parsing import ConfigValidationError
from everest.config import EverestConfig, ModelConfig
from everest.config import EverestConfig, ModelConfig, ObjectiveFunctionConfig
from everest.config.control_variable_config import ControlVariableConfig
from everest.config.sampler_config import SamplerConfig
from everest.simulator.everest_to_ert import everest_to_ert_config
Expand Down Expand Up @@ -1068,3 +1068,54 @@ def test_warning_empty_controls_and_objectives(controls, objectives, error_msg):
objective_functions=objectives,
controls=controls,
)


def test_deprecated_objective_function_normalization():
with pytest.warns(
ConfigWarning, match="normalization key is deprecated .* replaced with scaling"
):
ObjectiveFunctionConfig(name="test", normalization=10)


def test_deprecated_objective_function_auto_normalize():
with pytest.warns(
ConfigWarning,
match="auto_normalize key is deprecated .* replaced with auto_scale",
):
ObjectiveFunctionConfig(name="test", auto_normalize=True)


@pytest.mark.parametrize(
"normalization, scaling, auto_normalize, auto_scale",
[
(None, None, None, None),
(0.2, None, None, None),
(0.42, 0.24, None, None),
(None, 0.24, None, None),
(None, None, True, None),
(None, None, True, False),
(None, None, None, False),
(0.42, 0.24, True, False),
],
)
def test_objective_function_scaling_is_backward_compatible_with_scaling(
normalization, auto_normalize, scaling, auto_scale
):
o = ObjectiveFunctionConfig(
name="test",
normalization=normalization,
auto_normalize=auto_normalize,
scaling=scaling,
auto_scale=auto_scale,
)
if scaling is None and normalization is not None:
assert o.scaling == o.normalization
else:
assert o.scaling == scaling
assert o.normalization == normalization

if auto_scale is None and auto_normalize is not None:
assert o.auto_scale == o.auto_normalize
else:
assert o.auto_scale == auto_scale
assert o.auto_normalize == auto_normalize

0 comments on commit b544d6a

Please sign in to comment.