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
Cythonize scalar function root finders #9216
Comments
If I understand correctly then: What is the implication of the new PR? Are you now staying that vectorization is not the right approach and it should be replaced with a cython optimized API? |
PR #8431 is not new, it was opened in February, around the same time as #8357 .
No, I still think that for However, in pvlib-python for example Also, the Cython optimize API serves other purposes in addition to just vectorizing the calculations. For example, using Cython, it is trivial to parallelize the calculation with low overhead using Are you against adding the Cython optimize API? Is anyone in favor of this? I worry that I have wasted my time, especially if there is need or desire for these features, other than my own. |
I'm not against. The existing Would be good to get a sense of what others think of |
Would be good to get a sense of what others think of cython_optimize *in
principle* (not the current API) - @person142
<https://github.com/person142>, @pv <https://github.com/pv>, other
optimize-interested devs?
In `scipy.integrate.quad` the `func` object to integrate can either be a
Python callable or a `LowLevelCallable` with one of a few signatures. I
think that if any of the `optimize` functions (I'm more interested in
minimize related) could offer something similar, then that would be really
really good. In orther words the `optimize.minimize` call signature remains
more or less the same, it's just that `func` could either be a Python
callable or a `LowLevelCallable`. Obviously the backend minimizer would
have to be able to make use of that `LowLevelCallable`. Presumably for some
minimizers this would mean translating core iterative code to cython (this
is one of the targets I have in mind for the object-oriented minimizer PR),
those whose functionality is already written in using extensions might be
able to benefit more easily.
I wouldn't be a fan (-10) of having two different functions for the same
thing, e.g. integrate.quad/integrate.cquad
|
@andyfaff thanks for your feedback. I copied the
My goal as I mentioned in this comment was to use Also I tried to keep the solvers signatures identical to their Python counterparts, hence why |
IMO it's very valuable. I would definitely use Cython brentq, the other scalar root-finders might be lower priority. |
@mikofski Please take my questions as my attempt to understand what the issue is and what you are proposing, I'm not expressing an opinion. Procedurally, any discussion of this Issue and PR should be in the context of today's SciPy, which has two implementations of What I'd like to see are the use cases, how they are handled in today's SciPy, what the issue is with using current implementations, how that might be changed, and then the benefit(s) of such change. That's why I was asking for a new Issue to be filed, to make all that explicit in today's context. |
On Tue, 4 Sep 2018 at 21:52, Paul van Mulbregt ***@***.***> wrote:
@mikofski <https://github.com/mikofski> Please take my questions as my
attempt to understand what the issue is and what you are proposing, I'm not
expressing an opinion. Procedurally, any discussion of this Issue and PR
should be in the context of today's SciPy, which has two implementations of
newton, rather than the SciPy of January which had one. The PR gh-8431
<#8431> would then add a 3rd
implementation.
What I'd like to see are the use cases, how they are handled in today's
SciPy, what the issue is with using current implementations, how that might
be changed, and then the benefit(s) of such change. That's why I was asking
for a new Issue to be filed, to make all that explicit in today's context.
Paul's reply was a lot more considered than my attempt. This is pretty much
my view as well. I'm more interested from a machinery point of view, i.e.
how can `minimize`/`differential_evolution`/`basinhopping`/etc be
performance enhanced by using `LowLevelCallable`.
|
PR #8431 does not attempt to modify any of the minimizers, sorry, only the scalar-function root finders. In addition, it does not currently address The use cases for Cython optimize API are twofold:
For example, pvlib-python uses There may be other use cases, and a few individuals have already stated that it may be useful. Also based on the success of other Cython API[s] such as cc: @andyfaff and @pvanmulbregt |
@pvanmulbregt are you concerned that there are too many implementations of |
I'm concerned about the number of sub-case specializations of The title of the Issue "Cythonize scalar function root finders" is implementation-centric rather than user-centric. It's also a little ambiguous on one key point --- whether the cythonized root finders will be replacing the existing python/C root-finders. (The PR gh-8341 doesn't replace, it adds new ones handling a subset of functions.) The discussion may go in different directions based on this point, |
No, I do not intend or expect the Cython versions of any of the zero-finders to replace the existing Python API[s] because usage of the Cython versions requires a non-trivial effort by the user to write and compile Cython code, and additional software to be installed, such as the Cython package and a C-compiler, which are not required for users who only wish to use the SciPy Python API[s]. The usage of the Cython API is fundamentally different from the Python API: Python API>>> from math import exp
>>> from scipy.optimize import brentq
>>> brentq(lambda x, c0, c1: c0 - exp(x) / c1, a=0.5, b=1.0, args=(1.0, 2.0),
... xtol=0.001, rtol=0.001, maxiter=10, full_output=True)
(0.68314716056,
converged: True
flag: 'converged'
function_calls: 6
iterations: 5
root: 0.68314716056) A similar calculation using the Cython API would probably not be worth the effort if there were not significant performance improvements. Cython APIimport cython
from libc.math cimport exp
from scipy.optimize.cython_optimize cimport zeros_struct
ctypedef struct test_full_output:
int funcalls
int iterations
int error_num
double root
ctypedef struct test_params:
double C0
double C1
@cython.cdivision(True)
cdef double f(double x, void *args):
cdef test_params *myargs = <test_params *> args
return myargs.C0 - exp(x) / myargs.C1
cdef test_full_output show_full_output(dict args, double xa, double xb,
double xtol, double rtol, int mitr):
cdef test_params myargs = args
cdef test_full_output full_output
full_output.root = zeros_struct.brentq(f, xa, xb, <test_params *> &myargs,
xtol, rtol, mitr, <zeros_struct.scipy_zeros_parameters *> &full_output)
return full_output
def test_cython_brentq(args={'C0'=1.0, 'C1': 0.2}, xa=0.5, xb=1.0,
xtol=1e-3, rtol=1e-3, mitr=10):
"""test Cython brentq with full output"""
return show_full_output(args, xa, xb, xtol, rtol, mitr) After compiling the Cython code, then in Python ... >>> test_cython_brentq()
{'funcalls': 6,
'iterations': 5,
'error_num': 0,
'root': 0.68314716056} I'm also concerned about the duplicate implementations of Newton. One idea that I think could possibly combine the various Newton implementations would be to implement a Python extension module, based on the current Python extension module for |
@mikofski the question was not about the API; it's clear that any changes there need to be backwards compatible. It's about the implementations. I.e. will there be duplication of implementation between Python - Cython - C.
Yes that sounds better. If you have a Cython API for
|
Yes, unfortunately #8431 currently has duplicate implementations that serve different purposes:
Based on my answer above, I think the first suggestion would be best, but I realize that there are many other opinions from others with much more experience and knowledge than myself, and that this would be a decision that the maintainers would make. One nice outcome of having a single implementation in C, exposed from both Cython and Python, would be that we could probably remove the Another option to consider would be to NOT cythonize Newton, Halley, and secant, and to only cythonize Brent, Ridder, and bisect, since they are already implemented in C and exposed via Python. Therefore there would be no more implementations of Newton, Halley, or secant and we would delete the C-implementation in the PR. |
Thanks for sharing your real-world use case of solving solar cell equations. It helped clarify for me some aspects of the computation costs. Some long thoughts follow. My naive model of the computation is that Computers are much faster now than they were 40 years ago :-) when
When solving multiple equations, my (still naive) model is
[This does perhaps raise the question as to what will most benefit the user --- a faster implementation to solve one equation (helps cases 1-4 a little), or an implementation that solves multiple equations simultaneously (may help cases 2 & 3 a lot)? But I suspect the stopping criteria for the latter is not as simple as for a single equation.] Having new said all that, my Ideal scenario would be one where there is a single implementation in SciPy for the algorithm, and the user sees benefit without having to change the way they build the function. I.e. They could write it in Python/Cython/..., the SciPy would be agnostic. And SciPy would have a single implementation to maintain. |
If I were doing this today, I would only implement brentq and bisect. Back when I wrote these in (2002?), I just threw in everything, just because. |
@charris (Having read Brett Cannon's blog post, I feel compelled to clarify, just in case.) My comment was only meant to imply that IMO it became clear over time that brentq turned out to be so robust a magic-black-box-well-oiled-machine of a routine, that it just shadows the other ones. IOW, thanks a lot for writing it! |
@mikofski The change of title suggests a quite different topic than the original. My suggestion would be to restore the old title and then create a new Issue to cover the new topic. If the original topic is no longer an Issue, also close it out. That way all the existing comments, which were presumably written with the original topic in mind and are mostly relevant to that topic, do not clog up the new Issue. Additionally a new Issue is likely to draw new thoughts and comments more than a title change in an existing Issue! |
@mikofski did you mean to close this issue? After reading through the whole thing again, my summary is:
It's still hard to weigh the pros and cons of each approach, given the multiple use cases. We should try and write a summary document together I think, because my head hurts .... |
I closed it because I thought #7242 explained the need better, whereas this PR seems to justify the implementation. I thought closing this might be helpful, but happy to reopen if that's not the case. |
Okay, let's leave this one closed then. There's still useful content here, I'll cross-link from gh-7242. |
@person142 responsed to #8354 that his preference was for a cython optimize api.
#8357 only vectorized Newton methods. #8431 allows users to use a "tight loop" in C with Brent, Ridder, and other root finders.
We decided it was okay to split up Newton
We discussed which callbacks to support and the fact that
cython_optimize
should be pure C so it can free the GIL in this commentThis commit has links to annotated html that shows that
zeros_struct
andzeros_array
are pure C, no Python so they can release the GIL to call Cythonprange
The Cython optimize API in #8431 was discussed in this SciPy-Dev post and this one too when I specifically asked about callback signatures.
The text was updated successfully, but these errors were encountered: