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

Fix wrong desirabilities in predictive strategies #506

Merged
merged 4 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 17 additions & 2 deletions bofire/data_models/domain/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,24 +783,39 @@
def __call__(
self,
experiments: pd.DataFrame,
experiments_adapt: Optional[pd.DataFrame] = None,
predictions: bool = False,
) -> pd.DataFrame:
"""Evaluate the objective for every feature.

Args:
experiments (pd.DataFrame): Experiments for which the objectives should be evaluated.
experiments_adapt (pd.DataFrame, optional): Experimental values which are used to update the objective
parameters on the fly. This is for example needed when a `MovingMaximizeSigmoidObjective` is used
as this depends on the best experimental value achieved so far. For this reason `experiments_adapt`
has to be provided if `predictions=True` ie. that the objectives of candidates are evaluated.
Defaults to None.
predictions (bool, optional): If True use the prediction columns in the dataframe to calc the
desirabilities `f"{feat.key}_pred`.
desirabilities `f"{feat.key}_pred`, furthermore `experiments_adapt` has to be provided.

Returns:
pd.DataFrame: Objective values for the experiments of interest.

"""
if predictions and experiments_adapt is None:
raise ValueError(
"If predictions are used, `experiments_adapt` has to be provided.",
)
else:
experiments_adapt = (
experiments if experiments_adapt is None else experiments_adapt
)

desis = pd.concat(
[
feat(
experiments[f"{feat.key}_pred" if predictions else feat.key],
experiments[f"{feat.key}_pred" if predictions else feat.key],
experiments_adapt[feat.key].dropna(),

Check failure on line 818 in bofire/data_models/domain/features.py

View workflow job for this annotation

GitHub Actions / lint

Object of type "None" is not subscriptable (reportOptionalSubscript)
)
for feat in self.features
if feat.objective is not None
Expand Down
6 changes: 4 additions & 2 deletions bofire/strategies/predictives/predictive.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@ def predict(self, experiments: pd.DataFrame) -> pd.DataFrame:
predictions=predictions,
outputs=self.domain.outputs,
)
desis = self.domain.outputs(predictions, predictions=True)
predictions = pd.concat((predictions, desis), axis=1)
objectives = self.domain.outputs(
predictions, experiments_adapt=self.experiments, predictions=True
)
predictions = pd.concat((predictions, objectives), axis=1)
predictions.index = experiments.index
return predictions

Expand Down
27 changes: 27 additions & 0 deletions tests/bofire/data_models/domain/test_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
ConstrainedObjective,
MaximizeObjective,
MaximizeSigmoidObjective,
MovingMaximizeSigmoidObjective,
Objective,
TargetObjective,
)
Expand Down Expand Up @@ -300,6 +301,32 @@ def test_outputs_call(features, samples):
]


def test_outputs_call_adapt_experiment():
outputs = Outputs(
features=[
ContinuousOutput(key="of1", objective=MaximizeObjective()),
ContinuousOutput(
key="of2",
objective=MovingMaximizeSigmoidObjective(tp=0, steepness=10, w=1.0),
),
],
)
candidates = pd.DataFrame(
columns=["of1_pred", "of2_pred"], data=[[1.0, 5.0], [2.0, 5.0]]
)

experiments = pd.DataFrame(columns=["of1", "of2"], data=[[1.0, 5.0], [2.0, 6.0]])

with pytest.raises(
ValueError,
match="If predictions are used, `experiments_adapt` has to be provided.",
):
outputs(candidates, predictions=True)

outputs(experiments)
outputs(candidates, experiments_adapt=experiments, predictions=True)


def test_categorical_objective_methods():
obj = ConstrainedCategoricalObjective(
categories=["a", "b"],
Expand Down
Loading