Constraints Refactor 4: Introduce DiscreteLinearConstraint and Align with Conti Counterpart - #883
Constraints Refactor 4: Introduce DiscreteLinearConstraint and Align with Conti Counterpart#883Scienfitz wants to merge 42 commits into
DiscreteLinearConstraint and Align with Conti Counterpart#883Conversation
DiscreteBatchConstraint._get_invalid was never reached: batch constraints are excluded from search-space filtering, the only caller of get_invalid.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Replace DiscreteSumConstraint with DiscreteLinearConstraint using operator/rhs/ coefficients/tolerance interface that mirrors ContinuousLinearConstraint. Rework DiscreteProductConstraint to use operator/rhs/tolerance instead of condition, with a transitional deprecated condition field resolved in __attrs_post_init__. Deprecate DiscreteSumConstraint as a factory function that warns and returns DiscreteLinearConstraint. Add serialization redirects for both legacy DiscreteSumConstraint type and legacy DiscreteProductConstraint condition payload.
5a8e668 to
46d2b91
Compare
There was a problem hiding this comment.
Pull request overview
This PR continues the constraints refactor to unify discrete and continuous linear constraints behind a consistent operator/rhs/coefficients interface, while also formalizing “pruning” semantics for discrete constraints via a common DiscretePruningConstraint base and an exclude inversion flag.
Changes:
- Introduces
DiscretePruningConstraintand refactors discrete constraints to implement “matching rows” logic with centralizedexcludeinversion; adds/renames discrete constraint types (DiscreteFilteringConstraint,DiscreteDegeneracyConstraint,DiscreteLinearConstraint) and provides deprecation wrappers/redirects. - Updates search space construction and constraint application to work with pruning constraints and the renamed pruning-order constant.
- Updates tests, Hypothesis strategies, docs, examples, and changelog to reflect the new APIs and deprecations.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/validation/test_constraint_validation.py | Updates validation tests to use DiscreteLinearConstraint(operator/rhs) instead of DiscreteSumConstraint(condition=...). |
| tests/test_searchspace.py | Updates SearchSpace tests to use the new discrete linear constraint API. |
| tests/test_deprecations.py | Adds deprecation and legacy-deserialization coverage for renamed/merged discrete constraints. |
| tests/test_campaign.py | Updates campaign tests to use DiscreteFilteringConstraint(exclude=True) instead of DiscreteExcludeConstraint. |
| tests/serialization/test_constraint_serialization.py | Updates constraint roundtrip tests to new discrete constraint strategy generators and names. |
| tests/hypothesis_strategies/constraints.py | Refactors Hypothesis strategies to generate new discrete pruning constraint types and new linear interface. |
| tests/hypothesis_strategies/alternative_creation/test_searchspace.py | Updates alternative creation tests to use DiscreteLinearConstraint(operator/rhs). |
| tests/constraints/test_constraints_polars.py | Updates Polars/Pandas parity tests to use DiscreteLinearConstraint and DiscreteDegeneracyConstraint. |
| tests/constraints/test_constraints_discrete.py | Updates discrete constraint tests for the new linear interface. |
| tests/constraints/test_constrained_cartesian_product.py | Updates constrained cartesian product scenarios and ordering constant rename. |
| tests/constraints/test_batch_constraint.py | Updates batch-constraint tests to the new filtering constraint semantics (exclude=True). |
| tests/conftest.py | Updates shared fixtures to new constraint names and parameters (exclude, rhs, etc.). |
| examples/Mixtures/slot_based.py | Updates mixture example to use DiscreteDegeneracyConstraint and DiscreteLinearConstraint. |
| examples/Constraints_Discrete/prodsum_constraints.py | Updates prodsum example to use DiscreteLinearConstraint(operator/rhs) and updated product constraint API. |
| examples/Constraints_Discrete/filtering_constraints.py | Renames/updates the discrete filtering example to use DiscreteFilteringConstraint(exclude=True). |
| examples/Constraints_Continuous/hybrid_space.py | Updates hybrid example to use DiscreteLinearConstraint(operator/rhs) alongside continuous constraints. |
| docs/concepts/getting_recommendations.md | Updates docs snippet to use DiscreteFilteringConstraint(exclude=True). |
| docs/components/constraints.md | Reworks discrete constraints docs to introduce pruning semantics and updated constraint types/names. |
| CHANGELOG.md | Documents the new/renamed discrete constraints, pruning semantics, and deprecations. |
| baybe/searchspace/utils.py | Refactors constraint application paths to operate on DiscretePruningConstraint and renamed order constant. |
| baybe/searchspace/discrete.py | Updates constraint ordering logic to the renamed pruning-order list and handles unknown constraint classes. |
| baybe/constraints/discrete.py | Implements new discrete pruning constraints, deprecation wrappers, and legacy (de)serialization redirects. |
| baybe/constraints/base.py | Adds DiscretePruningConstraint abstraction and centralizes exclude inversion behavior for pruning constraints. |
| baybe/constraints/init.py | Re-exports new constraint names and the renamed pruning-order constant. |
| baybe/campaign.py | Updates candidate toggling to accept DiscretePruningConstraint collections (instead of all DiscreteConstraint). |
Suppressed comments (1)
baybe/constraints/discrete.py:73
DiscreteFilteringConstraintdoes not validate thatconditionshas the same length asparameters. This can silently ignore trailing parameters in the Pandas path (becausezip(parameters, conditions)truncates) and can raiseIndexErrorin the Polars path (it indexesself.parameters[k]for each condition). Enforce a 1:1 mapping to avoid inconsistent behavior.
conditions: list[Condition] = field(validator=min_len(1))
"""List of individual conditions."""
combiner: str = field(default="AND", validator=in_(_valid_logic_combiners))
"""Operator encoding how to combine the individual conditions."""
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
46d2b91 to
355c024
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (4)
baybe/constraints/discrete.py:304
- The new direct product-constraint interface accepts
NaNor infinite tolerances because neither branch checks finiteness. Such an object is constructed successfully and then fails only when filtering builds the internalThresholdCondition; reject non-finite values during construction.
# Validate tolerance
if (
self.operator not in _valid_tolerance_operators
and self.tolerance is not None
):
baybe/constraints/discrete.py:922
- Legacy redirects are registered only for
DiscretePruningConstraint, but real search-space payloads deserialize constraint fields asDiscreteConstraint(baybe/searchspace/discrete.py:103,765,810). A nested legacytype: "DiscreteSumConstraint"therefore bypasses this hook and the generic base lookup fails because that name is now a function rather than a subclass. RouteDiscreteConstraintdeserialization through these redirects while retainingDiscreteBatchConstraintsupport.
converter.register_structure_hook(
DiscretePruningConstraint, _structure_pruning_constraint
)
baybe/constraints/discrete.py:245
- The deprecated product interface is not backward-compatible for positional calls. Previously
DiscreteProductConstraint(parameters, condition)was valid; now the condition binds tooperator, whose string validator raises before__attrs_post_init__can translate it. Preserve detection of a positionalThresholdCondition(or use a wrapper/custom initializer) so existing calls receive the promised deprecation path.
operator: str = field(default="", validator=instance_of(str))
"""The comparison operator (e.g. ``"="``, ``">="``, ``"<"``)."""
baybe/constraints/discrete.py:192
- This validator accepts non-finite tolerances: both
NaNand infinity pass the comparisons, leaving an invalid constraint that fails only later when_build_condition()constructs aThresholdCondition. Validate finiteness eagerly, consistent with the previous condition-based interface.
This issue also appears on line 300 of the same file.
@tolerance.validator
def _validate_tolerance( # noqa: DOC101, DOC103
self, attribute: Any, value: float | None
) -> None:
Closes #875
Based on #881