Skip to content
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

ENH: Determine current fft backend in use (for specified inputs) #14758

Open
anntzer opened this issue Sep 21, 2021 · 15 comments
Open

ENH: Determine current fft backend in use (for specified inputs) #14758

anntzer opened this issue Sep 21, 2021 · 15 comments
Labels
enhancement A new feature or improvement scipy.fft uarray Items related to the uarray backend

Comments

@anntzer
Copy link
Contributor

anntzer commented Sep 21, 2021

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 that scipy.fft.fft(*args, **kwargs) would dispatch to (as an object compatible with set_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

@anntzer anntzer added the enhancement A new feature or improvement label Sep 21, 2021
@rgommers
Copy link
Member

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 ... ?

@anntzer
Copy link
Contributor Author

anntzer commented Sep 21, 2021

The point is to have deterministic behavior.

@hameerabbasi
Copy link
Contributor

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.

@anntzer
Copy link
Contributor Author

anntzer commented Sep 21, 2021

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).

@hameerabbasi
Copy link
Contributor

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.

@anntzer
Copy link
Contributor Author

anntzer commented Sep 22, 2021

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()

@hameerabbasi
Copy link
Contributor

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?

@anntzer
Copy link
Contributor Author

anntzer commented Sep 22, 2021

I have a few concerns with that approach:

  • AFAICT, scipy currently uses its own private copy of uarray, which I cannot access (without touching internals). It will use a globally installed uarray if available, but there are no uarray wheels for Py39.
  • I'm not sure that the documentation is sufficient for someone to write a multimethod from scratch :-( and based on the examples, it seems to be a rather complex endeavour to answer a relatively simple question. Also, would that mean that each and every third party who may want to know what backend is in use would need to register its own multimethod?
  • 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.

@anntzer
Copy link
Contributor Author

anntzer commented Nov 17, 2021

Not really in any hurry, but @rgommers' recent email on array type dispatch seems like a good opportunity to re-raise this?

@rgommers rgommers added the uarray Items related to the uarray backend label Nov 17, 2021
@rgommers
Copy link
Member

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.

@hameerabbasi
Copy link
Contributor

@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.

@rgommers
Copy link
Member

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.

@peterbell10
Copy link
Member

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.

@anntzer
Copy link
Contributor Author

anntzer commented Dec 5, 2021

Thanks for the suggestion. However, unless I am mistaken(?), no released version of scipy currently includes determine_backend?

@anntzer
Copy link
Contributor Author

anntzer commented Dec 12, 2021

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A new feature or improvement scipy.fft uarray Items related to the uarray backend
Projects
None yet
Development

No branches or pull requests

4 participants