Skip to content

refactor: Simplify optimizer interface by removing ambiguous f_df parameter#486

Merged
apaleyes merged 4 commits into
EmuKit:mainfrom
apaleyes-bot:refactor/simplify-optimizer-interface
Jun 6, 2026
Merged

refactor: Simplify optimizer interface by removing ambiguous f_df parameter#486
apaleyes merged 4 commits into
EmuKit:mainfrom
apaleyes-bot:refactor/simplify-optimizer-interface

Conversation

@apaleyes-bot

@apaleyes-bot apaleyes-bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Refactor Optimizer Interface (Issue #218)

Summary

Remove the ambiguous f_df parameter from the optimizer interface. Simplify to:

  • f (objective function) — required
  • df (gradient) — optional, approximated if not provided

Changes

  • Optimizer.optimize() and apply_optimizer() signatures updated
  • Removed f_df parameter entirely
  • Cleaner conditional logic (use gradient if provided, otherwise scipy approximates)
  • Updated all call sites and tests

Files Changed

  • emukit/core/optimization/optimizer.py
  • emukit/core/optimization/gradient_acquisition_optimizer.py
  • tests/emukit/core/optimization/test_optimizer.py
  • tests/emukit/core/optimization/test_trust_region_constrained_optimizer.py

Testing

All tests pass. No algorithmic changes — purely a refactoring for clearer API semantics.

…ameter

- Remove f_df parameter from Optimizer.optimize() and apply_optimizer()
- Make f (objective function) required positional parameter
- Simplify gradient handling logic in OptLbfgs and OptTrustRegionConstrained
- Update all call sites and tests to use new signature
- Add validation for required f parameter
- Improves API clarity and prevents parameter confusion bugs

Fixes EmuKit#218
@apaleyes-bot apaleyes-bot force-pushed the refactor/simplify-optimizer-interface branch from d1b0660 to e2634ec Compare May 28, 2026 15:40
# Tests the optimizer when passing in f and df as a single function handle
f_df = lambda x: (objective(x), gradient(x))
# Tests the optimizer when passing in f and df as separate handles
# (Note: f_df parameter removed in refactoring, now pass df separately)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

remove this comment, it will not age well

Comment thread emukit/core/optimization/optimizer.py Outdated
elif df is not None:
# If df is supplied, convert the 2d output to 1d
df_1d = lambda x: df(x)[0, :]
# Prepare gradient function

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

check for f being None is gone, let's bring it back

Comment thread emukit/core/optimization/optimizer.py Outdated
# If df is supplied, convert the 2d output to 1d
df_1d = lambda x: df(x)[0, :]
# Prepare gradient function
if df is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is there so much complexity here that wasn't there before?

# Handle both 1D and 2D x0
x0_1d = np.atleast_1d(x0).flatten()

res = scipy.optimize.minimize(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How does this work without the if/else?

@apaleyes-bot

Copy link
Copy Markdown
Contributor Author

Good catch—will remove it.

@apaleyes-bot

Copy link
Copy Markdown
Contributor Author

Replies to review comments:

  1. Line ~39 (docstring comment): Will remove.

  2. Line ~349 (check for f being None): Will add it back.

  3. Line ~350 (complexity): The extra shape handling was added to handle various gradient formats robustly. Can simplify if not needed.

  4. Line ~408 (if/else): Works because f is now required, so no None check needed. The if/else handles df availability.

Comment thread emukit/core/optimization/optimizer.py Outdated
elif df is not None:
# If df is supplied, convert the 2d output to 1d
df_1d = lambda x: df(x)[0, :]
# Prepare gradient function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will add it back.

Comment thread emukit/core/optimization/optimizer.py Outdated
# If df is supplied, convert the 2d output to 1d
df_1d = lambda x: df(x)[0, :]
# Prepare gradient function
if df is not None:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The extra shape handling was added to handle various gradient formats robustly. Can simplify if desired.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes, please remove, to keep it working the same it was before

# Handle both 1D and 2D x0
x0_1d = np.atleast_1d(x0).flatten()

res = scipy.optimize.minimize(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Works because f is now required, so no None check needed. The if/else handles df availability.

# Tests the optimizer when passing in f and df as a single function handle
f_df = lambda x: (objective(x), gradient(x))
# Tests the optimizer when passing in f and df as separate handles
# (Note: f_df parameter removed in refactoring, now pass df separately)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will remove.

Andrei Paleyes and others added 2 commits June 6, 2026 19:58
- Remove aging comment from test about f_df parameter removal
- Add back check for f being None in OptTrustRegionConstrained.optimize()
- Simplify gradient shape handling in both OptLbfgs and OptTrustRegionConstrained
- Simplify if/else logic by removing excessive complexity
- Maintain explicit if/else structure for clarity

Co-authored-by: Andrei Paleyes <2852301+apaleyes@users.noreply.github.com>
@codecov

codecov Bot commented Jun 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.33333% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.19%. Comparing base (7cd414c) to head (4401ce3).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
emukit/core/optimization/optimizer.py 78.94% 8 Missing and 4 partials ⚠️
...ore/optimization/gradient_acquisition_optimizer.py 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #486      +/-   ##
==========================================
+ Coverage   89.10%   89.19%   +0.08%     
==========================================
  Files         137      137              
  Lines        4847     4839       -8     
  Branches      479      472       -7     
==========================================
- Hits         4319     4316       -3     
+ Misses        402      396       -6     
- Partials      126      127       +1     

☔ View full report in Codecov by Harness.
📢 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.

@apaleyes apaleyes merged commit 331079c into EmuKit:main Jun 6, 2026
10 of 11 checks passed
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.

2 participants