What happened?
Hello.
I've noticed that parameter_constraints provided to ax.api.client.Client.configure_experiment don't work properly for ChoiceParameters.
Below you'll find a minimum example as a demonstration.
Thanks in advance for taking the time to address this bug!
Please provide a minimal, reproducible example of the unexpected behavior.
from ax.api.client import Client
from ax.api.configs import RangeParameterConfig
parameters = [
RangeParameterConfig(name="p1", bounds=(0.0, 1.0), parameter_type="float"),
RangeParameterConfig(name="p2", bounds=(0.0, 1.0), parameter_type="float", step_size=0.01),
]
parameter_constraints = ["p1 <= p2"]
client = Client()
client.configure_experiment(parameters=parameters, parameter_constraints=parameter_constraints)
client.configure_optimization(objective="score")
client.configure_generation_strategy(initialize_with_center=False)
client.get_next_trials(max_trials=5)
Output:
{0: {'p1': 0.39548560976982117, 'p2': 0.31},
1: {'p1': 0.8505668397992849, 'p2': 0.73},
2: {'p1': 0.5795025620609522, 'p2': 0.03},
3: {'p1': 0.1595833096653223, 'p2': 0.89},
4: {'p1': 0.10090977512300014, 'p2': 0.17}}
Please paste any relevant traceback/logs produced by the example provided.
Ax Version
1.3.1
Python Version
3.12.13
Operating System
Ubuntu 26
(Optional) Describe any potential fixes you've considered to the issue outlined above.
OrderedChoiceToIntegerRange re-encodes an ordered numeric choice to its list indices 0..n-1, so value v_j becomes index j. The constraint is carried through ParameterConstraint.clone_with_transformed_parameters, which is a silent no-op — coefficients are never rescaled, so the constraint ends up enforced against the index instead of the value. (By contrast, UnitX correctly rescales constraints on float RangeParameters, which is why an all-
RangeParameter search space respects the same constraint.)
A linear constraint w·p <= b stays linear after index encoding only if j -> v_j is affine, i.e. the choice values are evenly spaced. This gives a two-part fix:
-
Remap constraints for the evenly-spaced (representable) case, inline in OrderedChoiceToIntegerRange.transform_search_space, mirroring how UnitX rescales float-range constraints. With affine encoding v_j = a·j + c (step a, offset c):
w·v_j <= b => (w·a)·j <= b - w·c
i.e. new weight w·a, new bound b - w·c.
-
Reject the non-representable case early. For non-uniformly-spaced values no correct linear constraint exists, so add a check to validate_constraint_parameters (next to the existing log-scale rejection) that raises a clear error when a constrained ordered ChoiceParameter's values are not evenly spaced — turning a silently-violating optimization into an actionable configuration-time error.
Note: clone_with_transformed_parameters receives only the post-transform parameters, so it cannot recover a/c on its own; doing the remap inside the transform (where both original and encoded parameters are in scope) is the
cleaner fix.
Pull Request
None
Code of Conduct
What happened?
Hello.
I've noticed that
parameter_constraintsprovided toax.api.client.Client.configure_experimentdon't work properly forChoiceParameters.Below you'll find a minimum example as a demonstration.
Thanks in advance for taking the time to address this bug!
Please provide a minimal, reproducible example of the unexpected behavior.
Output:
Please paste any relevant traceback/logs produced by the example provided.
Ax Version
1.3.1
Python Version
3.12.13
Operating System
Ubuntu 26
(Optional) Describe any potential fixes you've considered to the issue outlined above.
OrderedChoiceToIntegerRangere-encodes an ordered numeric choice to its list indices0..n-1, so valuev_jbecomes indexj. The constraint is carried throughParameterConstraint.clone_with_transformed_parameters, which is a silent no-op — coefficients are never rescaled, so the constraint ends up enforced against the index instead of the value. (By contrast,UnitXcorrectly rescales constraints on floatRangeParameters, which is why an all-RangeParametersearch space respects the same constraint.)A linear constraint
w·p <= bstays linear after index encoding only ifj -> v_jis affine, i.e. the choice values are evenly spaced. This gives a two-part fix:Remap constraints for the evenly-spaced (representable) case, inline in
OrderedChoiceToIntegerRange.transform_search_space, mirroring howUnitXrescales float-range constraints. With affine encodingv_j = a·j + c(stepa, offsetc):i.e. new weight
w·a, new boundb - w·c.Reject the non-representable case early. For non-uniformly-spaced values no correct linear constraint exists, so add a check to
validate_constraint_parameters(next to the existing log-scale rejection) that raises a clear error when a constrained ordered ChoiceParameter's values are not evenly spaced — turning a silently-violating optimization into an actionable configuration-time error.Note:
clone_with_transformed_parametersreceives only the post-transform parameters, so it cannot recovera/con its own; doing the remap inside the transform (where both original and encoded parameters are in scope) is thecleaner fix.
Pull Request
None
Code of Conduct