scipy minimize generator#424
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
@copilot address the coverage, linting and notebook failures |
…fix notebook ruff format
Fixed in commits
|
|
@copilot add tests to achieve needed coverage |
Added 8 new tests in the latest commit covering all previously untested branches in
Also removed a dead-code |
There was a problem hiding this comment.
Pull request overview
This pull request adds a new sequential optimization generator, ScipyGenerator, that adapts scipy.optimize.minimize to Xopt’s external ask/tell workflow, and updates tests, docs, and generator registration to support it.
Changes:
- Introduce
ScipyGenerator(sequential wrapper aroundscipy.optimize.minimize) and register it under generator namescipy. - Add unit tests (including serialization/restart coverage) for the new generator and update existing sequential serialization tests.
- Add documentation + example notebook for
ScipyGenerator, and update MkDocs navigation and algorithms/index references; refactor LatinHypercubeGenerator imports/location.
Reviewed changes
Copilot reviewed 13 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| xopt/generators/sequential/scipy.py | New ScipyGenerator implementation wrapping scipy.optimize.minimize in a sequential ask/tell pattern |
| xopt/generators/sequential/init.py | Export ScipyGenerator from the sequential generators package |
| xopt/generators/init.py | Register ScipyGenerator for dynamic loading; improve defaults handling for default_factory fields |
| xopt/generators/scipy/init.py | Removes old scipy generator re-exports (package content removed) |
| xopt/generators/latin_hypercube.py | Adds/relocates LatinHypercubeGenerator under a new import path |
| xopt/tests/generators/sequential/test_scipy.py | Adds test coverage for ScipyGenerator |
| xopt/tests/generators/sequential/test_serialization.py | Adds ScipyGenerator to sequential generator serialization/restart tests |
| xopt/tests/generators/test_latin_hypercube.py | Updates import path for LatinHypercubeGenerator |
| docs/api/generators/sequential/scipy.md | API documentation page for ScipyGenerator |
| docs/examples/sequential/scipy.ipynb | New example notebook demonstrating ScipyGenerator usage |
| docs/examples/other/latin_hypercube.ipynb | Updates import path for LatinHypercubeGenerator and relocates example |
| mkdocs.yml | Adds new docs + example notebook to navigation; updates Latin Hypercube example path |
| docs/index.md | Mentions the new scipy sequential generator in the feature list |
| docs/algorithms.md | Adds ScipyGenerator to algorithms list (but link maintenance needed) |
| .gitignore | Ignores local test output artifacts |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
FYI I'll try catch up this week |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
electronsandstuff
left a comment
There was a problem hiding this comment.
OK, overall I think it's a cool concept. Obviously it's a bit of a hack to get it to work, but it's kind of cool. Because of that, I would strongly recommend adding the below mentioned test. I can't think of how to do it outside of this without threading. There are also obvious performance issues, but it might be good enough for now.
| "source": [ | ||
| "# ScipyGenerator with scipy.optimize.minimize\n", | ||
| "\n", | ||
| "This notebook demonstrates how to use Xopt's `ScipyGenerator` to drive any supported scipy `optimize.minimize` method in a sequential ask/tell workflow." |
There was a problem hiding this comment.
"ask/tell" isn't jargon we use anywhere else. This feels like an LLM'ism making up terms. I would recommend using the names used elsewhere for the generator. Check your PR for other uses of this phrase.
| "source": [ | ||
| "## Notes on performance\n", | ||
| "\n", | ||
| "`ScipyGenerator` bridges scipy's in-process `minimize` API into Xopt's external evaluation loop by replaying cached points each step. This adds overhead that is small for expensive evaluations but can be noticeable for very fast toy objectives." |
There was a problem hiding this comment.
Style thing: I don't know if I'd add low level details in the example
| pip-delete-this-directory.txt | ||
|
|
||
| # Test output files | ||
| all_sequential_tests.txt |
There was a problem hiding this comment.
I searched for these and couldn't find where they are generated. Could you point out what makes them otherwise remove? I'd also suggest using a tempfile.TemporaryDirectory as an alternative to not make extra files even if not commited.
There was a problem hiding this comment.
Formatting, make titles title case. Probably don't need a header for imports.
| cached points. This is robust and method-agnostic, but adds repeated optimizer | ||
| bookkeeping work compared to a persistent in-memory scipy run. | ||
| - Point keys are rounded (12 decimals) before cache lookup to avoid fragile | ||
| floating-point equality checks. This improves replay stability across methods |
There was a problem hiding this comment.
I am a little worried about dealing with "numerically close" points like this. My understanding is that the scipy optimizers are deterministic, so the hack to get it to work in Xopt is you feed it back the same points and it will always revisit the same sequence seen before and you replay it the objective value. I am worried about trying to do things where we feed it points "nearby" without carefully thinking about how this affects the algorithm.
If the cache is really just replaying points, I would imagine it can be a list, not a dict right? Ie the list of points in the order they have been seen by the optimizer, then we just replay without hashing the variables in a dict. This should work under the same assumptions as in the current version of the code and also be significantly faster (no computation during replay.
| name = "scipy" | ||
| supports_single_objective: bool = True | ||
|
|
||
| method: str = Field( |
There was a problem hiding this comment.
Literal["a", "b", "c"] would give better type annotation and validation. I wonder if there is a way that we can get the list of strings names of methods from scipy and fill this list automatically.
There was a problem hiding this comment.
Given that we are doing something not supported by scipy, I think this PR needs the following test:
Given a test objective function, perform an optimization once with scipy.minimize and once with Xopt.ScipyGenerator. Collect all of the x points and confirm all of them are the same down to numerical precision. Parameterize the test by method and do for each of the methods we are supporting in scipy (presumably all that support bounds).
This will ensure that the replay method is doing what we think it should even if scipy changes.
There was a problem hiding this comment.
This move will break the import from xopt.generators.scipy import LatinHypercubeGenerator. I would recommend keeping this import and a deprecation warning in __init__.py to be removed later. Especially since we showed that import in our example. We may want to note somwhere and make sure our documentation is consistent with only loading from xopt.generators and not the internal things to avoid in the future.
|
Thanks for the comments @electronsandstuff, I will work on this over the next couple of weeks and ping you again when its ready |
Overview
This pull request introduces a new generic sequential optimization generator,
ScipyGenerator, that wrapsscipy.optimize.minimizeand integrates it with Xopt's ask/tell workflow. It includes comprehensive documentation, an example notebook, and full test coverage. Additionally, it updates the generator registry and navigation to reflect the new functionality, and refactors some imports for consistency.Integration Model
Xopt evaluates objective functions externally, one point at a time.
scipy.optimize.minimizeexpects an in-process callable objective.ScipyGeneratorbridges this mismatch by replaying known evaluations:X.data.minimizeis called with an objective wrapper that checks the cache first.minimize, and returns that point to Xopt.step,minimizeis called again with the larger cache.Performance Notes
step, where N is the number of collected evaluations.minimizerestarts eachstep, so there is repeated optimizer bookkeeping overhead.Key changes:
New Feature: Generic Scipy Minimize Generator
ScipyGeneratorinxopt.generators.sequential.scipy, providing a sequential ask/tell interface to anyscipy.optimize.minimizemethod. The generator bridges the difference between Xopt's external evaluation and scipy's in-process callable objective, including robust caching and replay logic.ScipyGeneratorcovering point generation, multiple point error handling, restart/serialization, and direct generator usage intest_scipy.py.ScipyGeneratorin the serialization test suite. [1] [2]Documentation & Examples
ScipyGeneratordescribing its integration model, performance considerations, and configuration indocs/api/generators/sequential/scipy.md.docs/examples/sequential/scipy.ipynbdemonstrating usage with the Rosenbrock function and showing convergence.mkdocs.ymlto include the new example and API documentation. [1] [2]Generator Registry & Import Refactoring
scipyas a generator name and addedScipyGeneratorto the dynamic generator import logic and__all__lists. [1] [2] [3]xopt/generators/scipy/__init__.pyand refactoredLatinHypercubeGeneratorimports for consistency. [1] [2]