Open anntzer opened 3 years ago
Interesting, I don't think this has been requested before (Cc @hameerabbasi, @peterbell10 in case I missed it).
The reason is that I would like my library to use the explicit pyfftw class-based API iff the end user has configured the pyfftw backend on scipy (via
scipy.fft.set_backend
or a variant thereof), thus reusing the configuration on scipy and avoiding a separate configuration knob on my library.
Is there a reason you want your library's choice depend on the user choice, rather than always using pyfftw if it's installed? Is it not always faster, or is it about deterministic behavior (which should not depend on optional installed packages), or ... ?
The point is to have deterministic behavior.
I'm interested... Why not just set the backend to pyfftw
manually rather than have a configuration knob for deterministic behaviour?
Use set_backend
as a context Manager inside each function. I think there's even a contextlib
utility function to turn that into a decorator instead of a context manager.
I don't want to add a dependency on pyfftw (in particular because they still don't have py39 wheels available, but that's a bit of a side point).
Even if we were able to return an object with your proposed get_backend
, you would need something to compare it to, and doing that cleanly would require a dependency on pyfftw
. Is pyfftw
an example here, or do you have other variants of your class for other backends? Or do you need any backend but just want it to be the same one every time?
Forgive the questions, I'm just wondering if there's a way to solve your use-case without extending uarray
/SciPy, or to do it in a cleaner manner.
I don't need a dependency on pyfftw; I can check e.g. getattr(backend, "__name__", None) == "pyfftw.interfaces.scipy_fft"
which would work for practical purposes (or get_backend
can itself directly return a str, but that seems worse -- now you need to spec the mapping between backends and strs). Currently there's only specific support for pyfftw, but that's just because that's the alternate backend I'm most familiar with; there's no reason not to support more of them. Basically, the end result would be something like
backend = get_backend(inputs) # whatever API we decide on
backend_name = getattr(backend, "__name__", None)
if backend_name == "pyfftw.interfaces.scipy_fft":
import pyfftw # OK, we know pyfftw is indeed available, so we can import it
enable_pyfftw_specific_path()
elif backend_name == "<whatever>":
import whatever
enable_whatever_specific_path()
elif ...:
...
elif backend == "scipy": # fallback
enable_scipy_specific_path()
else: # some unknown backend, may or may not emit warning
warnings.warn("Don't know this backend, falling back to scipy.fft")
enable_scipy_specific_path()
Ah, in that case, I would add multimethods and register them with the correct backend instead of actually playing around with backends in this manner. See the uarray
docs.
cc @peterbell10 do we have a common registration mechanism that was added to SciPy's backends?
I have a few concerns with that approach:
Not really in any hurry, but @rgommers' recent email on array type dispatch seems like a good opportunity to re-raise this?
Ah yes, thanks for pointing this out @anntzer. We should address this and the other open issues related to the fft
backend over the next month or two. I added a new uarray
label.
@anntzer I'm in the process of releasing wheels for 3.9 and 3.10. If you need examples, there are many in the Quansight-Labs/unumpy repo.
There is a separate issue about improving the docs for the scipy.fft
backend. We should also take apart one or two actual implementations, and annotate them thoroughly in the developer docs and ensure it's clear what each line of code is for.
That's still separate from @anntzer's third point here:
It is not clear to me whether I need to register a multimethod implementation for each of the backends I want to support, and if so whether I can do that without adding a dependency on the backend (e.g. pyfftw) at the toplevel.
uarray
has got an internal determine_backend
function which uses __ua_convert__
to figure out which backend will accept input of the given type. I think this should roughly achieve what you're after:
def get_backend(func, *args, **kwargs):
dispatchables = func.arg_extractor(*args, **kwargs)
return _uarray.determine_backend(func.domain, dispatchables, True)
One major problem though is that SciPy's backend doesn't implement __ua_convert__
, so determine_backend
will never pick it even if dispatch might.
Thanks for the suggestion. However, unless I am mistaken(?), no released version of scipy currently includes determine_backend
?
I tried this with the just released scipy 1.8.0rc1, but I get
In [13]: get_backend(scipy.fft.fft, np.random.rand(10))
---------------------------------------------------------------------------
BackendNotImplementedError Traceback (most recent call last)
<ipython-input-13-bc4b0a480613> in <module>
----> 1 get_backend(scipy.fft.fft, np.random.rand(10))
<ipython-input-1-fa23ddecdc1c> in get_backend(func, *args, **kwargs)
1 def get_backend(func, *args, **kwargs):
2 dispatchables = func.arg_extractor(*args, **kwargs)
----> 3 return _uarray.determine_backend(func.domain, dispatchables, True)
4
BackendNotImplementedError: No backends could accept input of this type.
(both when the active backend is the default scipy.fft, and when it is pyfftw)
Is your feature request related to a problem? Please describe.
I would like a way to know which backend (e.g. scipy's own fft or pyfftw.interfaces.scipy_fft) would be used when e.g.
scipy.fft.fft(some_specified_object)
is called. (See below for a proposed API.)The reason is that I would like my library to use the explicit pyfftw class-based API iff the end user has configured the pyfftw backend on scipy (via
scipy.fft.set_backend
or a variant thereof), thus reusing the configuration on scipy and avoiding a separate configuration knob on my library.Describe the solution you'd like.
A possible API would be something like
scipy.fft.fft.get_backend(*args, **kwargs)
returning the backend thatscipy.fft.fft(*args, **kwargs)
would dispatch to (as an object compatible withset_backend
, so either "scipy" or pyfftw.interfaces.scipy_fft, in the case above).Describe alternatives you've considered.
No response
Additional context (e.g. screenshots, GIFs)
No response