Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permit BudgetOptimizer.allocate_budget() to take x0 as an argument #1565

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

cluhmann
Copy link
Contributor

@cluhmann cluhmann commented Mar 16, 2025

Description

BudgetOptimizer.allocate_budget() now accepts x0 as an argument.

Related Issue

Checklist

x0 is now accepted via the minimize_kwargs (as the docstring implies is possible). I looked into adding a corresponding test, but these lines in tests/mmm/test_budget_optimizer::test_allocate_budget_custom_minimize_args() confuse me. Any guidance?

kwargs = minimize_mock.call_args_list[0].kwargs

np.testing.assert_array_equal(x=kwargs["x0"], y=np.array([50.0, 50.0]))
assert kwargs["bounds"] == [(0.0, 50.0), (0.0, 50.0)]
assert kwargs["method"] == minimize_kwargs["method"]
assert kwargs["options"] == minimize_kwargs["options"]

📚 Documentation preview 📚: https://pymc-marketing--1565.org.readthedocs.build/en/1565/

Copy link

codecov bot commented Mar 16, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 93.51%. Comparing base (8475c1c) to head (c8a28ec).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1565   +/-   ##
=======================================
  Coverage   93.51%   93.51%           
=======================================
  Files          55       55           
  Lines        6341     6342    +1     
=======================================
+ Hits         5930     5931    +1     
  Misses        411      411           

☔ 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.

# Merge with defaults (preferring user-supplied keys)
minimize_kwargs = {**self.DEFAULT_MINIMIZE_KWARGS, **minimize_kwargs}
# 5. Construct the initial guess (x0) if not provided
if "x0" not in minimize_kwargs:
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think x0 should be hidden in minimize kwargs, I would make it an explicit argument of allocate_budget

minimize_kwargs["x0"] = np.ones(budgets_size) * (
total_budget / budgets_size
)
minimize_kwargs["x0"] = minimize_kwargs["x0"].astype(
Copy link
Contributor

Choose a reason for hiding this comment

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

I would check if it has the expected shape (budgets_size,), as the compiled functions have trust_input=True and won't check if x0 has the promised shape

Copy link
Contributor

Choose a reason for hiding this comment

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

very good point here!

@cetagostini
Copy link
Contributor

kwargs = minimize_mock.call_args_list[0].kwargs
np.testing.assert_array_equal(x=kwargs["x0"], y=np.array([50.0, 50.0]))
assert kwargs["bounds"] == [(0.0, 50.0), (0.0, 50.0)]
assert kwargs["method"] == minimize_kwargs["method"]
assert kwargs["options"] == minimize_kwargs["options"]

Just a hardcoded check based on the data, we could change this to be more dynamic and check if values are equal based on the data, not hardcoded 50/50 🙃 Does that makes it clear?

@juanitorduz juanitorduz requested a review from ricardoV94 March 18, 2025 20:20
self._total_budget.set_value(np.asarray(total_budget, dtype="float64"))

# coordinate user-provided and default minimize_kwargs
if minimize_kwargs is None:
minimize_kwargs = self.DEFAULT_MINIMIZE_KWARGS.copy()
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: no real need to copy

Suggested change
minimize_kwargs = self.DEFAULT_MINIMIZE_KWARGS.copy()
minimize_kwargs = self.DEFAULT_MINIMIZE_KWARGS

@@ -391,8 +392,11 @@ def allocate_budget(
- If None, default bounds of [0, total_budget] per channel are assumed.
- If a dict, must map each channel to (low, high) budget pairs (only valid if there's one dimension).
- If an xarray.DataArray, must have dims (*budget_dims, "bound"), specifying [low, high] per channel cell.
x0 : DataArray, optional
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it should be a np.ndarray not a DataArray

self._budgets_flat.type.dtype
)
# if x0 arg is provided, validate shape
elif x0.shape != (budgets_size,):
Copy link
Contributor

Choose a reason for hiding this comment

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

Should also validate dtype

@cluhmann
Copy link
Contributor Author

Just need to write some tests.

f"""The shape of 'x0' {x0.shape} does not match the expected shape {(budgets_size,)}."""
)
# if x0 arg is provided, validate dtype
elif np.issubdtype(x0.dtype, np.number):
Copy link
Contributor

@ricardoV94 ricardoV94 Mar 24, 2025

Choose a reason for hiding this comment

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

Specifically the x0.dtype must match self._budgets_flat.type.dtype. I think there's a shortcut you can use for the shape and dtype validation: x0 = self._budgets_flat.type.filter(x0), which will do any allowed casting or raise if things cannot be safely cast (and also checks shape)

@github-actions github-actions bot added the tests label Mar 25, 2025
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.

BudgetOptimizer.allocate_budget() doc string doesn't specify default value of x0
3 participants