Skip to content

[Bug]: Multi-metric outcome constraints are silently reduced to a single metric #5309

Closed

Description

@benrabe93

What happened?

A linear outcome constraint over several metrics, such as "a + 2 * b <= 1.0", is accepted by Client.configure_optimization. When candidates are generated, though, all but one metric is dropped and the remaining one gets weight 1. There is no error or warning, so the optimizer works with a different constraint than the one the user specified.

Please provide a minimal, reproducible example of the unexpected behavior.

import ax.adapter.torch as torch_adapter
from ax.adapter.adapter_utils import extract_outcome_constraints
from ax.api.client import Client
from ax.api.configs import RangeParameterConfig

captured = []
def spy(outcome_constraints, outcomes):
    result = extract_outcome_constraints(outcome_constraints, outcomes)
    captured.append((outcomes, result))
    return result
torch_adapter.extract_outcome_constraints = spy

client = Client()
client.configure_experiment(
    parameters=[RangeParameterConfig(name="x", parameter_type="float", bounds=(0.0, 1.0))]
)
client.configure_optimization(objective="obj", outcome_constraints=["a + 2 * b <= 1.0"])

while not captured:  # run until the model-based node is used
    for idx, params in client.get_next_trials(max_trials=1).items():
        x = params["x"]
        client.complete_trial(trial_index=idx, raw_data={"obj": x, "a": x, "b": 1 - x})

outcomes, (A, b) = captured[-1]
print(outcomes, A)

Expected: one row of A with non-zero entries for both a and b, in the ratio 1 : 2.

Actual:
['a', 'b', 'obj'] [[0. 1. 0.]]
Only b is left, with weight 1.

Please paste any relevant traceback/logs produced by the example provided.

Ax Version

1.3.1

Python Version

3.12

Operating System

Linux Mint

(Optional) Describe any potential fixes you've considered to the issue outlined above.

Cause

extract_outcome_constraints (ax/adapter/adapter_utils.py) only applies all of a constraint's weights when the constraint is an instance of ScalarizedOutcomeConstraint. Otherwise it uses c.metric_signatures[0] with weight 1. ScalarizedOutcomeConstraint is now deprecated in favour of OutcomeConstraint(expression=...), and optimization_config_from_string creates a plain OutcomeConstraint for multi-metric expressions. Those constraints therefore always take the single-metric branch.

The same isinstance check in Derelativize._compute_sq_val (ax/adapter/transforms/derelativize.py) has the same effect on relative constraints. For example, "a + b >= 0.9 * baseline" is accepted, even though the docstring of configure_optimization says scalarized constraints cannot be relative. Its bound is then derived from only the first metric's status quo value.

Suggested fix

In both places, branch on len(c.metric_weights) > 1 instead of isinstance(c, ScalarizedOutcomeConstraint), and use c.metric_weights for the coefficients. Separately, reject relative constraints over several metrics when parsing, if they're not meant to be supported.

Pull Request

None

Code of Conduct

  • I agree to follow Ax's Code of Conduct

Activity

  1. changed the title [-][Bug]: Multi-metric outcome constraints from are silently reduced to a single metric[/-] [+][Bug]: Multi-metric outcome constraints are silently reduced to a single metric[/+] on Oct 22, 2026
  2. akhil9tiet commented on Oct 22, 2026

    @akhil9tiet

    I'd like to work on this — please assign it to me.

  3. saitcakmak commented on Oct 22, 2026

    @saitcakmak
    Contributor

    Thanks for flagging and providing a nice repro. Fixed in #5310.

    Thanks for the offer @akhil9tiet. Due to review requirements, it is often simpler for me to fix the bug directly than to review & land an external PR these days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions