Skip to content

refactor(budget-optimizer): collapse default_constraints into constraints#2570

Open
anevolbap wants to merge 6 commits into
pymc-labs:mainfrom
anevolbap:refactor/1416-simplify-constraints
Open

refactor(budget-optimizer): collapse default_constraints into constraints#2570
anevolbap wants to merge 6 commits into
pymc-labs:mainfrom
anevolbap:refactor/1416-simplify-constraints

Conversation

@anevolbap
Copy link
Copy Markdown
Contributor

@anevolbap anevolbap commented May 14, 2026

Closes #1416. See the issue thread for design discussion. Notebook updates land in a follow-up PR.

Summary

  • Rename custom_constraints to constraints on BudgetOptimizer.
  • Drop default_constraints field. Empty constraints auto-adds the default sum constraint; non-empty means the caller is in charge. Pass build_default_sum_constraint() explicitly to keep the default alongside customs.
  • Legacy kwargs stay as deprecated aliases for one release (removal in 0.21.0).
  • MMM.optimize_budget: default_constraints: bool | None = None. Legacy path only runs when explicitly passed.
  • Drop the UserWarning("Using default equality constraint"). It fired on the documented happy path under the new contract.
  • Deprecate the default kwarg of set_constraints (in-scope cleanup).
  • Edge case preserved: default_constraints=False with empty custom_constraints still raises.

Test plan

  • tests/mmm/test_budget_optimizer.py (29 pass, 8 new)
  • tests/mmm/test_budget_optimizer_mmm.py + tests/mmm/test_cost_per_unit.py (78 pass, 1 new)
  • pre-commit clean
  • codecov patch coverage: 100%
  • Notebook updates (follow-up PR)

…ints

Drop default_constraints and rename custom_constraints to constraints on
BudgetOptimizer. Empty constraints auto-adds the default sum constraint;
non-empty means the caller is in charge. Pass build_default_sum_constraint()
explicitly to keep the default alongside customs.

Old kwargs stay as deprecated aliases that warn and preserve current
behavior for one release. On MMM.optimize_budget, default_constraints is
now bool | None and only triggers the legacy path when explicitly set.

Closes pymc-labs#1416.
@anevolbap anevolbap force-pushed the refactor/1416-simplify-constraints branch from 93540f3 to 58f539c Compare May 14, 2026 14:44
@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.91%. Comparing base (8409763) to head (c5deff5).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2570      +/-   ##
==========================================
+ Coverage   93.89%   93.91%   +0.01%     
==========================================
  Files          92       92              
  Lines       14062    14091      +29     
==========================================
+ Hits        13204    13234      +30     
+ Misses        858      857       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

anevolbap and others added 5 commits May 14, 2026 14:36
… path

Add tests for the two uncovered branches of the legacy translation in
BudgetOptimizer._migrate_legacy_constraint_kwargs and for the legacy
forwarding path in MMM.optimize_budget. Also pin the deprecation/removal
versions in the warning message and docstrings to match the project
convention (deprecated in 0.20.0, removed in 0.21.0).
…, deprecate set_constraints default kwarg

The "Using default equality constraint" UserWarning fired on the
documented happy path (empty constraints auto-add the default sum),
turning it into noise. Drop it and clean up the 11 test assertion
sites. The new contract is in the docstring instead.

Also: deprecate the `default` kwarg of `set_constraints`, since it is
now redundant under the new contract. Preserve the legacy strict
behaviour for `default_constraints=False` with empty custom_constraints
by raising a ValueError in the migration shim instead of silently
auto-adding the default.

Add tests for: the deprecated `set_constraints(default=...)` kwarg,
the strict-no-constraint edge case, and Constraint round-trip through
the legacy migrator.
…pe in docstring

Add an inline comment in the migration validator explaining why the
conflict path raises TypeError (re-raised as-is by Pydantic) while the
edge case raises ValueError (wrapped into ValidationError, which is
itself a ValueError so callers are unaffected).

Also fix the docstring type for `default_constraints` on
MMM.optimize_budget from `bool, optional` to `bool or None, optional`
to match the actual annotation.
Copy link
Copy Markdown
Contributor Author

@anevolbap anevolbap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a few inline notes to ease review.

_constraints: dict = PrivateAttr()
_compiled_constraints: list[dict] = PrivateAttr()

@model_validator(mode="before")
Copy link
Copy Markdown
Contributor Author

@anevolbap anevolbap May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pydantic v2 idiom for renaming a Pydantic field at construction time. Field(deprecated=...) only fires on attribute access, not when kwargs are passed.

# value error.
raise TypeError(
"Pass either `constraints` or the deprecated "
"`custom_constraints`/`default_constraints`, not both."
Copy link
Copy Markdown
Contributor Author

@anevolbap anevolbap May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last branch (empty custom_constraints with default_constraints=False) preserves the pre-PR error instead of silently auto-adding the default.

)
self.set_constraints(constraints=self.constraints)

def set_constraints(self, constraints, default=None) -> None:
Copy link
Copy Markdown
Contributor Author

@anevolbap anevolbap May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default is reachable only from internal callers after this PR. Deprecating it now keeps the removal in the same cycle.

Comment thread pymc_marketing/mmm/mmm.py
model=self,
compile_kwargs=self.compile_kwargs,
)
if default_constraints is None:
Copy link
Copy Markdown
Contributor Author

@anevolbap anevolbap May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two branches so the DeprecationWarning has one source of truth (BudgetOptimizer's validator).

@anevolbap anevolbap changed the title [WIP] refactor(budget-optimizer): collapse default_constraints into constraints refactor(budget-optimizer): collapse default_constraints into constraints May 15, 2026
@anevolbap anevolbap marked this pull request as ready for review May 15, 2026 09:55
@juanitorduz juanitorduz requested a review from cetagostini May 15, 2026 12:00
@juanitorduz
Copy link
Copy Markdown
Collaborator

@carlosagostini :)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Simplify optimizer parameter default_constraints

2 participants