Skip to content

Commit

Permalink
Isolate the "no feasible points" error to objective threshold inferen…
Browse files Browse the repository at this point in the history
…ce during MOO (#2929)

Summary:
Pull Request resolved: #2929

### The problem

For a MMO problem, if no thresholds are provided on the objectives, we try to infer them during gen().  If there are outcome constraints, and none of the observed data points are feasible, we cannot infer the thresholds and error out. This is reproduced in N5821958.

However, the error makes it look like candidate generation failed, when in fact it is the inference of the thresholds.  We could simply suggest the users to provide thresholds if they can and the error will be gone.

### The caveats

The same error message are being referenced in multiple places.
In particular, it is being used in PosteriorMeanPriorSampler, and acquisition functions for non-MBM models.  However, in those cases, we do not actually filter for feasibility/outcome constraints.  Instead, we only filter for points where all metrics (objectives and outcome constraints) are attached, and sometimes parameter constraints as well.  So in such cases, the error should happen very rarely, and the error message is misleading at best.

### This diff

Change the error message to a more actionable when incurred during reference point inference.  And change the error from all other places mentioned above to "no observed points" error message.

Reviewed By: saitcakmak

Differential Revision: D64691734

fbshipit-source-id: f499184046df005bc5771de27304f04b2b9a6fdf
  • Loading branch information
Susan Xia authored and facebook-github-bot committed Oct 22, 2024
1 parent 29226d8 commit 7d37679
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 15 deletions.
4 changes: 2 additions & 2 deletions ax/models/tests/test_botorch_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
_get_model,
get_and_fit_model,
get_warping_transform,
NO_FEASIBLE_POINTS_MESSAGE,
NO_OBSERVED_POINTS_MESSAGE,
)
from ax.utils.common.testutils import TestCase
from ax.utils.common.typeutils import checked_cast, not_none
Expand Down Expand Up @@ -399,7 +399,7 @@ def test_get_acquisition_func(self) -> None:
for acqf_con, exp_con in zip(acqf_constraints, expected_constraints):
self.assertTrue(torch.allclose(acqf_con(samples), exp_con(samples)))

with self.assertRaisesRegex(ValueError, NO_FEASIBLE_POINTS_MESSAGE):
with self.assertRaisesRegex(ValueError, NO_OBSERVED_POINTS_MESSAGE):
_get_acquisition_func(
model=model,
acquisition_function_name=acqf_name,
Expand Down
9 changes: 3 additions & 6 deletions ax/models/tests/test_botorch_moo_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import numpy as np
import torch
from ax.core.search_space import SearchSpaceDigest
from ax.models.torch.botorch_defaults import NO_OBSERVED_POINTS_MESSAGE
from ax.models.torch.botorch_moo import MultiObjectiveBotorchModel
from ax.models.torch.botorch_moo_defaults import (
get_outcome_constraint_transforms,
Expand Down Expand Up @@ -234,9 +235,7 @@ def test_get_qLogEHVI_input_validation_errors(self) -> None:
weights = torch.ones(2)
objective_thresholds = torch.zeros(2)
mm = MockModel(MockPosterior())
with self.assertRaisesRegex(
ValueError, "There are no feasible observed points."
):
with self.assertRaisesRegex(ValueError, NO_OBSERVED_POINTS_MESSAGE):
get_qLogEHVI(
model=mm,
objective_weights=weights,
Expand All @@ -261,9 +260,7 @@ def test_get_qLogNEHVI_input_validation_errors(self) -> None:
model = MultiObjectiveBotorchModel()
weights = torch.ones(2)
objective_thresholds = torch.zeros(2)
with self.assertRaisesRegex(
ValueError, "There are no feasible observed points."
):
with self.assertRaisesRegex(ValueError, NO_OBSERVED_POINTS_MESSAGE):
get_qLogNEHVI(
# pyre-fixme[6]: For 1st param expected `Model` but got
# `Optional[Model]`.
Expand Down
8 changes: 4 additions & 4 deletions ax/models/torch/botorch_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@


MIN_OBSERVED_NOISE_LEVEL = 1e-6
NO_FEASIBLE_POINTS_MESSAGE = (
"There are no feasible observed points. This likely means that one "
"or more outcome constraints or objective thresholds is set too strictly. "
NO_OBSERVED_POINTS_MESSAGE = (
"There are no observed points meeting all parameter "
"constraints or have all necessary metrics attached."
)


Expand Down Expand Up @@ -402,7 +402,7 @@ def _get_acquisition_func(
raise NotImplementedError(f"{acquisition_function_name=} not implemented yet.")

if X_observed is None:
raise ValueError(NO_FEASIBLE_POINTS_MESSAGE)
raise ValueError(NO_OBSERVED_POINTS_MESSAGE)
# construct Objective module
if chebyshev_scalarization:
with torch.no_grad():
Expand Down
12 changes: 9 additions & 3 deletions ax/models/torch/botorch_moo_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

import torch
from ax.exceptions.core import AxError
from ax.models.torch.botorch_defaults import NO_FEASIBLE_POINTS_MESSAGE
from ax.models.torch.botorch_defaults import NO_OBSERVED_POINTS_MESSAGE
from ax.models.torch.utils import (
_get_X_pending_and_observed,
get_outcome_constraint_transforms,
Expand Down Expand Up @@ -81,6 +81,12 @@
tuple[Tensor, Tensor, Tensor],
]

NO_FEASIBLE_POINTS_MESSAGE = (
" Cannot infer objective thresholds due to no observed feasible points. "
" This likely means that one or more outcome constraints is set too strictly. "
" Consider adding thresholds to your objectives to bypass this error."
)


def get_weighted_mc_objective_and_objective_thresholds(
objective_weights: Tensor, objective_thresholds: Tensor
Expand Down Expand Up @@ -262,7 +268,7 @@ def _get_NEHVI(
seed: int | None = None,
) -> qNoisyExpectedHypervolumeImprovement | qLogNoisyExpectedHypervolumeImprovement:
if X_observed is None:
raise ValueError(NO_FEASIBLE_POINTS_MESSAGE)
raise ValueError(NO_OBSERVED_POINTS_MESSAGE)
# construct Objective module
(
objective,
Expand Down Expand Up @@ -439,7 +445,7 @@ def _get_EHVI(
seed: int | None = None,
) -> qExpectedHypervolumeImprovement | qLogExpectedHypervolumeImprovement:
if X_observed is None:
raise ValueError(NO_FEASIBLE_POINTS_MESSAGE)
raise ValueError(NO_OBSERVED_POINTS_MESSAGE)
# construct Objective module
(
objective,
Expand Down

0 comments on commit 7d37679

Please sign in to comment.