-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
ENH: Replace Fortran COBYLA with Python version from PRIMA #22350
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
Conversation
looks like just one real CI failure from a brief look:
|
It appears that the issue with the one you pointed out is that one of the bounds is fixed, and this leads COBYLA to go a little haywire when trying to satisfy it. I'll add some code to detect fixed bounds and eliminate them, which is both a sensible thing to do and also what COBYQA does. And there's also test_basinhopping. It takes longer now than the 20s allotted for it, taking up to 33.8 seconds on Windows fail slow full. Obviously since the algorithm is now in Python it's not surprising that it's slower, so I've increased the window for that test to 35s. Besides those two there are some mypy failures which I need to look into. |
e527ef1
to
7ebb473
Compare
Fixed bounds and test_basinhopping are fixed, last up is the mypy failures. |
please just apply this with your next round of changes: diff --git a/mypy.ini b/mypy.ini
index 95deab150a..3c5dfd9e86 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -751,3 +751,6 @@ ignore_errors = True
[mypy-scipy._lib.array_api_compat.*]
ignore_errors = True
+
+[mypy-scipy._lib.pyprima.*]
+ignore_errors = True
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a cursory review - I haven't audited the changes to the tests in detail - but looks very promising, thanks!
.gitmodules
Outdated
@@ -32,3 +32,7 @@ | |||
path = subprojects/highs | |||
url = https://github.com/scipy/HiGHs | |||
shallow = true | |||
[submodule "scipy/_lib/prima"] | |||
path = scipy/_lib/prima | |||
url = https://github.com/nbelakovski/prima |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we will want to mirror this repo under the SciPy org?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either that, or perhaps better have a repo with a script to extract only the relevant parts from upstream. prima
is a very heavy repo, with a large majority of the content being benchmarks and code in other languages. So we could probably keep things a lot leaner without too much process overhead if we just copied out pyprima/
and ensured we had a good way to record the exact commit hash and repo the content came from.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be happy to look into this more if this sounds like the way to go, to avoid giving @nbelakovski even more homework. Getting PRIMA into the current great shape has already been a massive lift I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably use a similar script to https://github.com/lucascolley/scikit-learn/blob/xpx/maint_tools/vendor_array_api_extra.sh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a first step to get this merged, could we simply fork this repo under the scipy org?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can take a look at the script approach later today
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbelakovski nbelakovski#1 passes tests for me locally. Would you mind taking a look and merging? @rgommers, what are the next steps to move that repo under the scipy
org?
I ran the DFO (Derivative Free Optimization) benchmarks to compare against the existing COBYLA implementation and here are the results
43 problems are improved either in final value or in number of function evaluations. 22 are worse in those metrics. A notable outlier is test #27. I did some digging and found this this is the objective function it is trying to evaluate
If I remove the maxfun limitation on the new implementation it eventually gets to the optimum value, although it takes ~13000 function evaluations on my machine as compared to <1000 for the old implementation. Problem 17 is also somewhat of an outlier. If I tighten the tolerance on it to 1e-6 from the default 1e-4 the result becomes comparable to the old implementation. Different problems will fare better or worse depending on the initial/final trust region radius (which in scipy are called rhobeg and tol respectively) and these parameters are exposed to the user so I don't think this outlier is something to be overly concerned with. |
6de248e
to
75d7ca2
Compare
For me the benchmarks look like a net win. Let's include one sentence about the parameters that can be tweaked in the release notes to give users a hint. |
Not to block, or in fact take away, anything from this great addition, I just want to understand, is this going to live in your fork and not in prima repo? I don't have any background info hence my question. |
At the moment yes. It's my understanding that @zaikunzhang is occupied and that PRIMA is not particularly high on his list of priorities. As such I haven't had a chance to discuss this with him and see if he has any comments, but given my previous work with him, I think he would take it as a good sign that the Python implementation is bit-for-bit identical* to the Fortran version on all problems in the test set that we used for the Python bindings. Hopefully he is able to get bandwidth sometime in the future and we'll be able to merge the Python implementation back into the prima repo. I've been rebasing my work on the latest stuff from him so at the moment the fork is up to date with the latest. All that said, I don't think we should wait for him to move forward on this. * This is true when the USE_NAIVE_MATH flag in Python is on, and Fortran is compiled in debug mode. I've chosen to default that flag to off so that numpy routines are used for martix multiplication and so forth instead of the naive implementations. My reasoning is that most practical problems do not need to preserve all floating point calculations, but when the flag is on it sometimes results in more function evaluations and therefore more matrix math, and so the speedup/slowdown is not as significant as you might think. And also Fortran behaves differently when compiled with any optimization so there's a parallel there. I'm open to changing this. |
4b2bccc
to
44a5542
Compare
Vendor separate minimal repo
Test failures are unrelated, this should be mergeable now. |
We will want to transfer lucascolley/pyprima to the SciPy org before merge. |
Argh, missed that, sry. Maintainer should have rights to create repos under scipy org, at least the button works for me. Is there an approval process to follow? |
thanks Daniel, done! |
Thanks @lucascolley . If there are no more comments, I will merge this next week. |
@lucascolley @nbelakovski : the new dependency checker |
I would suggest just adding [[modules]]
path = "scipy._lib.pyprima"
unchecked = true Like we have for cobyqa |
PyPRIMA imports scipy in order to use the constraint objects, which results in a circular dependency violation. COBYQA does the same thing, so like with COBYQA we set unchecked to true.
Agreed, and done. |
Alright, I pulled the trigger here finally. Thanks for your tremendous work here @nbelakovski ! Hat tip to all reviewers as well. |
And of course thanks for your great work @nbelakovski |
Thanks all, and thanks for the help in getting it across the finish line! |
(sorry for reposting) Hello! Author of PRIMA here. Thanks for working on this. I am sorry for not being able to respond. For anything derived from PRIMA, may I request that the following is stated clearly?
As an example, see lfortran.org/blog/2025/03/lfortran-compiles-prima Note that PRIMA contains bug fixes and improvements that do not exist in Powell's original implementation of the solvers. Results produced by PRIMA are surely different from those produced by the original solvers by Powell. Therefore, it is simply wrong to pretend that PRIMA is just Powell's solvers. It took me years to develop PRIMA, which almost cost my career and my life. (This partially explains why I have been unable to respond recently. I have to strive to survive as a young professor). As a professor, all I need is a citation / acknowledgment when my work is useful, which is not well recognized in academia when it comes to software development. In addition, for obvious reasons, people tend to cite Powell without mentioning me when they use PRIMA instead of Powell's original solvers. (I have no doubt that my contribution is insignificant compared with Powell's, and I acknowledge this whenever I introduce PRIMA to others. See libprima.net for example). This is crucial for my career. Thank you very much. |
@dschmitz89 and @lucascolley thank you for finishing the review/merge here! Sorry for going missing in action for a while. @nbelakovski awesome work, thank you 🙏🏼
We definitely recognize that problem and the need for citations. I know you saw it, but just for completeness: gh-22738 added references to your paper in two places. |
PRIMA now has a pure python implementation of COBYLA!
I'm pleased to announce that after a lot of effort the PRIMA COBYLA algorithm is now implemented in Python. A considerable amount of effort went into validating this implementation against the Fortran and I'm happy to report that against a battery of over 500 problems from cutest, the Fortran and Python implementations get the same result bit for bit.
Reference issue
Closes gh-18118
Additional information
This commit also