Skip to content

Fix .max() for categorical#601

Open
NathanDeMaria wants to merge 2 commits intobayesian-optimization:masterfrom
NathanDeMaria:FixCategoricalMax
Open

Fix .max() for categorical#601
NathanDeMaria wants to merge 2 commits intobayesian-optimization:masterfrom
NathanDeMaria:FixCategoricalMax

Conversation

@NathanDeMaria
Copy link

@NathanDeMaria NathanDeMaria commented Mar 8, 2026

Before this, taking the .max() on a target space would return incorrect values. It needed to use .array_to_params to properly cast back from one-hot encoding used for categorical params

Summary by CodeRabbit

Release Notes

  • Improvements

    • Enhanced handling of categorical parameters in Bayesian optimization through centralized parameter conversion and validation.
  • Tests

    • Added comprehensive test coverage for categorical parameter scenarios, including optimization with constraints.

@coderabbitai
Copy link

coderabbitai bot commented Mar 8, 2026

📝 Walkthrough

Walkthrough

The pull request refactors parameter conversion in TargetSpace by replacing inline dict(zip()) operations with a centralized array_to_params() method call in both the max and res methods. Additionally, new test cases are added to validate the behavior of categorical parameters within these methods.

Changes

Cohort / File(s) Summary
Parameter Conversion Refactoring
bayes_opt/target_space.py
Replaces direct dictionary construction via dict(zip(self.keys, params)) with self.array_to_params() method calls in max() and res() methods, centralizing parameter conversion logic.
Categorical Parameter Tests
tests/test_target_space.py
Adds three new test cases: test_max_categorical for max selection with categorical parameters, test_res_categorical for res output validation, and test_res_categorical_with_constraints for constraint handling with categorical inputs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 With whiskers twitched and hoppy cheer,
We hop through params, crystal clear!
No more zip-zip, dict by dict—
array_to_params makes it slick! ✨
Categorical bounds, now tested true,
This refactored path's both clean and new! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix .max() for categorical' directly matches the main change: fixing the .max() method to properly handle categorical parameters using array_to_params conversion.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bayes_opt/target_space.py (1)

675-675: ⚠️ Potential issue | 🟠 Major

Inconsistent handling: res() has the same issue for constrained spaces.

The res() method at line 675 still uses dict(zip(self.keys, p)) for the constrained case, while the unconstrained case at line 671 correctly uses array_to_params. This will exhibit the same categorical parameter bug that was fixed in max().

🐛 Proposed fix
-        params = [dict(zip(self.keys, p)) for p in self.params]
+        params = [self.array_to_params(p) for p in self.params]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bayes_opt/target_space.py` at line 675, The res() method currently builds
params for the constrained branch using dict(zip(self.keys, p)) which reproduces
the categorical handling bug; update res() to use the same array_to_params
helper as the unconstrained branch (i.e., call array_to_params(p, self.space) or
the existing array_to_params usage pattern found in max()) so that categorical
and transformed parameters are reconstructed correctly instead of zipping raw
arrays to self.keys.
🧹 Nitpick comments (1)
tests/test_target_space.py (1)

256-274: LGTM! Good test coverage for the categorical fix.

The test effectively validates that max() correctly returns the categorical parameter value ("c") rather than the one-hot encoded representation.

Consider also asserting the target value for completeness:

💡 Optional: Assert target value as well
     expected = {"first_float": 0.1, "categorical_value": "c", "second_float": 0.8}
-    assert space.max()["params"] == expected
+    result = space.max()
+    assert result["params"] == expected
+    assert result["target"] == 0.8
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_target_space.py` around lines 256 - 274, Add an assertion in
test_max_categorical to also verify the returned objective value: after
asserting space.max()["params"] == expected, assert that space.max()["target"]
== 0.8 so the test confirms the TargetSpace.max() returns both the correct
categorical params (handled in TargetSpace and _f) and the correct target value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@bayes_opt/target_space.py`:
- Line 675: The res() method currently builds params for the constrained branch
using dict(zip(self.keys, p)) which reproduces the categorical handling bug;
update res() to use the same array_to_params helper as the unconstrained branch
(i.e., call array_to_params(p, self.space) or the existing array_to_params usage
pattern found in max()) so that categorical and transformed parameters are
reconstructed correctly instead of zipping raw arrays to self.keys.

---

Nitpick comments:
In `@tests/test_target_space.py`:
- Around line 256-274: Add an assertion in test_max_categorical to also verify
the returned objective value: after asserting space.max()["params"] == expected,
assert that space.max()["target"] == 0.8 so the test confirms the
TargetSpace.max() returns both the correct categorical params (handled in
TargetSpace and _f) and the correct target value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06c2c916-bc9c-463b-9e35-0db8ab4e1067

📥 Commits

Reviewing files that changed from the base of the PR and between 25084fe and 4a48db3.

📒 Files selected for processing (3)
  • bayes_opt/parameter.py
  • bayes_opt/target_space.py
  • tests/test_target_space.py

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/test_target_space.py (1)

329-329: Minor: Unused lambda argument p2.

The constraint function ignores p2, which triggers a linter warning. Consider using an underscore prefix to indicate it's intentionally unused.

🔧 Suggested fix
-        constraint=NonlinearConstraint(lambda p1, p2: p1 - 2, 0, 5)
+        constraint=NonlinearConstraint(lambda p1, _p2: p1 - 2, 0, 5)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_target_space.py` at line 329, The lambda passed to
NonlinearConstraint uses an unused parameter named p2 which triggers a linter
warning; rename the unused parameter to _p2 (or _ ) in the lambda used in the
NonlinearConstraint call so intent is clear (e.g., the lambda in the
constraint=NonlinearConstraint(...) expression).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_target_space.py`:
- Line 329: The lambda passed to NonlinearConstraint uses an unused parameter
named p2 which triggers a linter warning; rename the unused parameter to _p2 (or
_ ) in the lambda used in the NonlinearConstraint call so intent is clear (e.g.,
the lambda in the constraint=NonlinearConstraint(...) expression).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06a2a20f-423a-48f0-b779-faa5cc13036a

📥 Commits

Reviewing files that changed from the base of the PR and between 4a48db3 and b873c18.

📒 Files selected for processing (2)
  • bayes_opt/target_space.py
  • tests/test_target_space.py

@codecov
Copy link

codecov bot commented Mar 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.78%. Comparing base (25084fe) to head (b873c18).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #601   +/-   ##
=======================================
  Coverage   97.78%   97.78%           
=======================================
  Files          10       10           
  Lines        1221     1221           
=======================================
  Hits         1194     1194           
  Misses         27       27           

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant