Skip to content

Conversation

@till-m
Copy link
Owner

@till-m till-m commented May 20, 2025

Summary by CodeRabbit

  • New Features

    • Suggestion methods for acquisition functions now require a random state to be provided at call time, improving reproducibility and clarity.
    • Optimization routines have been generalized with updated parameter names for better consistency.
  • Bug Fixes

    • Improved error handling in optimization routines, with clearer feedback on failures.
  • Refactor

    • Random state handling has been removed from acquisition function initialization and is now passed explicitly to relevant methods.
    • Parameter n_l_bfgs_b has been renamed to n_smart across the user interface for greater clarity.
  • Tests

    • Updated tests to align with the new random state handling and parameter naming conventions.
    • Removed deprecated test cases and improved test clarity.

@till-m till-m requested a review from Copilot May 20, 2025 12:50
@coderabbitai
Copy link

coderabbitai bot commented May 20, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes refactor the handling of random state in acquisition functions by removing its management from constructors and serialization, instead requiring it to be passed explicitly to suggest() and related methods. Parameter naming is updated for clarity, optimization routines are generalized, and tests are adjusted to reflect these interface changes.

Changes

File(s) Change Summary
bayes_opt/acquisition.py Refactored acquisition functions to remove internal random state storage and serialization; added random_state parameter to suggest() and internal methods; renamed n_l_bfgs_b to n_smart; improved error handling and generalized optimization routines; updated meta-acquisition classes accordingly.
bayes_opt/bayesian_optimization.py Updated to instantiate acquisition functions without random_state; now passes random_state explicitly to suggest() calls.
tests/test_acquisition.py Updated tests to remove random_state from constructors, passing it explicitly to methods as needed; renamed n_l_bfgs_b to n_smart; removed test_base_acquisition; updated custom acquisition class constructors.
tests/test_bayesian_optimization.py Added a print statement for the loop index in a test that compares suggestions from two optimizers.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant BayesianOptimization
    participant AcquisitionFunction
    participant GaussianProcessRegressor
    participant TargetSpace

    User->>BayesianOptimization: suggest(random_state)
    BayesianOptimization->>AcquisitionFunction: suggest(gp, target_space, ..., random_state)
    AcquisitionFunction->>GaussianProcessRegressor: (fit if required)
    AcquisitionFunction->>TargetSpace: (access bounds, etc.)
    AcquisitionFunction->>AcquisitionFunction: _acq_min(..., random_state)
    AcquisitionFunction->>AcquisitionFunction: _random_sample_minimize(..., random_state)
    AcquisitionFunction->>AcquisitionFunction: _smart_minimize(..., random_state)
    AcquisitionFunction-->>BayesianOptimization: suggested_point
    BayesianOptimization-->>User: suggested_point
Loading

Poem

In the fields of code where rabbits play,
Random seeds now hop a different way.
No longer tucked inside a class’s den,
They’re passed along with each new pen.
Tests and names refreshed with care—
Reproducible hops, everywhere!
🐇✨


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR centralizes the use of random_state in suggest() calls (instead of constructors), renames the n_l_bfgs_b parameter to n_smart, and updates tests and default acquisition-function instantiations accordingly.

  • Deprecate random_state in acquisition-function constructors and emit warnings
  • Rename n_l_bfgs_bn_smart across API and tests
  • Update defaults in bayesian_optimization.py and propagate random_state through suggest()

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

File Description
tests/test_bayesian_optimization.py Accidental debug print(i) introduced into loop
tests/test_acquisition.py Removed constructor random_state, updated suggest() calls
bayes_opt/bayesian_optimization.py Drop ctor random_state, pass it into suggest()
bayes_opt/acquisition.py Deprecate ctor random_state, rename n_l_bfgs_bn_smart, add random_state param and warnings

@till-m
Copy link
Owner Author

till-m commented May 20, 2025

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented May 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

Actionable comments posted: 2

🔭 Outside diff range comments (2)
bayes_opt/acquisition.py (2)

309-318: ⚠️ Potential issue

Seeds for smart optimiser are chosen from the worst instead of the best samples

np.argsort(ys)[-n_x_seeds:] returns indices of the largest acquisition values, but we want the smallest (best) values to start L-BFGS-B / DE.

-            idxs = np.argsort(ys)[-n_x_seeds:]
+            idxs = np.argsort(ys)[:n_x_seeds]

This simple one-liner noticeably improves convergence quality.


1225-1268: 💡 Verification agent

🧩 Analysis chain

GPHedge.suggest() may crash when an int seed is supplied

_sample_idx_from_softmax_gains expects a RandomState and calls .rand().
If callers provide an int (allowed by the public signature), an AttributeError is raised.

Add an early conversion:

     def suggest(
         self,
         gp: GaussianProcessRegressor,
@@
-        self.i += 1
+        random_state = ensure_rng(random_state)
+        self.i += 1

Also adjust the subsequent call:

-        idx = self._sample_idx_from_softmax_gains(random_state=random_state)
+        idx = self._sample_idx_from_softmax_gains(random_state)

I’ll inspect the implementation of GPHedge.suggest, _sample_idx_from_softmax_gains, and see if ensure_rng is available.


🏁 Script executed:

#!/bin/bash
set -e

# Locate acquisition file(s)
echo "=== Finding acquisition.py files ==="
fd acquisition.py

# Show GPHedge class around suggest() definition
echo "=== GPHedge.suggest signature ==="
rg -A5 -B5 "class GPHedge" -n --color=never bayes_opt/acquisition.py

# Find suggest() method in GPHedge
echo "=== suggest() definition ==="
rg -n "def suggest" -n --color=never bayes_opt/acquisition.py

# Inspect the body for random_state usage
echo "=== suggest() body context ==="
rg -n "random_state" -A5 -B5 --color=never bayes_opt/acquisition.py

# Locate _sample_idx_from_softmax_gains
echo "=== _sample_idx_from_softmax_gains implementation ==="
rg -n "def _sample_idx_from_softmax_gains" -A20 --color=never bayes_opt/acquisition.py

# Check for ensure_rng helper
echo "=== ensure_rng references ==="
rg -n "ensure_rng" -n --color=never .

Length of output: 22036


Convert random_state to a RandomState in GPHedge.suggest

The override in GPHedge.suggest never calls ensure_rng(), so if you pass an int seed, _sample_idx_from_softmax_gains will do random_state.rand() on an int and blow up. Add an early conversion and pass the RandomState onward:

--- a/bayes_opt/acquisition.py
+++ a/bayes_opt/acquisition.py
@@ class GPHedge(AcquisitionFunction):
     def suggest(
         self,
         gp: GaussianProcessRegressor,
         target_space: TargetSpace,
         n_random: int = 10_000,
         n_smart: int = 10,
         fit_gp: bool = True,
         random_state: int | RandomState | None = None,
     ) -> NDArray[Float]:
+        # Ensure we always have a numpy RandomState
+        random_state = ensure_rng(random_state)
         if len(target_space) == 0:
             msg = (
                 "Cannot suggest a point without previous samples. Use "
@@
         self.previous_candidates = np.array(x_max)
-        idx = self._sample_idx_from_softmax_gains(random_state=random_state)
+        idx = self._sample_idx_from_softmax_gains(random_state)
         return x_max[idx]
  • Adds the missing ensure_rng(random_state) call.
  • Passes the converted RandomState into _sample_idx_from_softmax_gains.

Fixes the AttributeError when an int seed is supplied.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 2c78f7c and 2600edb.

📒 Files selected for processing (4)
  • bayes_opt/acquisition.py (27 hunks)
  • bayes_opt/bayesian_optimization.py (2 hunks)
  • tests/test_acquisition.py (11 hunks)
  • tests/test_bayesian_optimization.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
bayes_opt/acquisition.py (4)
tests/test_acquisition.py (4)
  • random_state (45-46)
  • target_space (55-56)
  • fun (137-141)
  • gp (50-51)
bayes_opt/util.py (1)
  • ensure_rng (8-30)
bayes_opt/target_space.py (5)
  • max (617-645)
  • TargetSpace (34-707)
  • random_sample (558-596)
  • bounds (187-194)
  • res (647-678)
bayes_opt/parameter.py (4)
  • random_sample (68-87)
  • random_sample (262-281)
  • random_sample (359-381)
  • bounds (59-61)
🔇 Additional comments (2)
bayes_opt/bayesian_optimization.py (1)

252-255: 👍 Correct propagation of random_state to the new API

Passing random_state=self._random_state matches the refactored suggest() signature and retains reproducibility.
No further action required.

bayes_opt/acquisition.py (1)

73-76: Deprecation warning handled correctly

Nice touch issuing a deprecation warning when a user still passes random_state to the constructor.
Implementation is concise and future-proof.

Comment on lines 581 to 585
# Test that multiple subsequent suggestions match
for _ in range(5):
print(i)
suggestion1 = optimizer.suggest()
suggestion2 = new_optimizer.suggest()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

print(i) will raise NameError and pollute test output

Inside the loop you declared for _ in range(5):, the loop variable is _, not i, therefore print(i) triggers a NameError, breaking the whole test suite.
Even if it were valid, uncontrolled print statements make tests noisy and slow CI pipelines.

-    for _ in range(5):
-        print(i)
+    for _ in range(5):
+        # Optional: keep an index for debugging
+        # print(_)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Test that multiple subsequent suggestions match
for _ in range(5):
print(i)
suggestion1 = optimizer.suggest()
suggestion2 = new_optimizer.suggest()
# Test that multiple subsequent suggestions match
for _ in range(5):
# Optional: keep an index for debugging
# print(_)
suggestion1 = optimizer.suggest()
suggestion2 = new_optimizer.suggest()
🤖 Prompt for AI Agents
In tests/test_bayesian_optimization.py around lines 581 to 585, remove the
print(i) statement inside the loop since the loop variable is _ and i is
undefined, causing a NameError. Also, eliminate the print statement to avoid
noisy test output and slow CI. Simply delete or comment out the print(i) line to
fix the issue.

Comment on lines 92 to 100
def test_acquisition_optimization(gp, target_space):
acq = MockAcquisition(random_state=42)
acq = MockAcquisition()
target_space.register(params={"x": 2.5, "y": 0.5}, target=3.0)
res = acq.suggest(gp=gp, target_space=target_space)
assert np.array([3.0, 1.0]) == pytest.approx(res)

with pytest.raises(ValueError):
acq.suggest(gp=gp, target_space=target_space, n_random=0, n_l_bfgs_b=0)
acq.suggest(gp=gp, target_space=target_space, n_random=0, n_smart=0)

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Flaky tests: deterministic RNG removed

After the refactor, the acquisition no longer stores an internal RNG, so calls like

res = acq.suggest(gp=gp, target_space=target_space)

now draw from an unseeded generator, potentially giving different minima and sporadic test failures.
Pass the shared random_state fixture instead:

-    res = acq.suggest(gp=gp, target_space=target_space)
+    res = acq.suggest(gp=gp, target_space=target_space, random_state=random_state)

Apply the same fix to other MockAcquisition tests for full determinism.

🤖 Prompt for AI Agents
In tests/test_acquisition.py around lines 92 to 100, the test calls acq.suggest
without passing a random_state, causing nondeterministic results due to the
removal of internal RNG. Fix this by passing the shared random_state fixture to
acq.suggest as an argument to ensure deterministic behavior. Also, update other
MockAcquisition tests similarly to use the shared random_state for full test
determinism.

@till-m till-m closed this May 22, 2025
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