| import scipy._lib.uarray as ua |
| from . import _basic_backend |
| from . import _realtransforms_backend |
| from . import _fftlog_backend |
|
|
|
|
| class _ScipyBackend: |
| """The default backend for fft calculations |
| |
| Notes |
| ----- |
| We use the domain ``numpy.scipy`` rather than ``scipy`` because ``uarray`` |
| treats the domain as a hierarchy. This means the user can install a single |
| backend for ``numpy`` and have it implement ``numpy.scipy.fft`` as well. |
| """ |
| __ua_domain__ = "numpy.scipy.fft" |
|
|
| @staticmethod |
| def __ua_function__(method, args, kwargs): |
|
|
| fn = getattr(_basic_backend, method.__name__, None) |
| if fn is None: |
| fn = getattr(_realtransforms_backend, method.__name__, None) |
| if fn is None: |
| fn = getattr(_fftlog_backend, method.__name__, None) |
| if fn is None: |
| return NotImplemented |
| return fn(*args, **kwargs) |
|
|
|
|
| _named_backends = { |
| 'scipy': _ScipyBackend, |
| } |
|
|
|
|
| def _backend_from_arg(backend): |
| """Maps strings to known backends and validates the backend""" |
|
|
| if isinstance(backend, str): |
| try: |
| backend = _named_backends[backend] |
| except KeyError as e: |
| raise ValueError(f'Unknown backend {backend}') from e |
|
|
| if backend.__ua_domain__ != 'numpy.scipy.fft': |
| raise ValueError('Backend does not implement "numpy.scipy.fft"') |
|
|
| return backend |
|
|
|
|
| def set_global_backend(backend, coerce=False, only=False, try_last=False): |
| """Sets the global fft backend |
| |
| This utility method replaces the default backend for permanent use. It |
| will be tried in the list of backends automatically, unless the |
| ``only`` flag is set on a backend. This will be the first tried |
| backend outside the :obj:`set_backend` context manager. |
| |
| Parameters |
| ---------- |
| backend : {object, 'scipy'} |
| The backend to use. |
| Can either be a ``str`` containing the name of a known backend |
| {'scipy'} or an object that implements the uarray protocol. |
| coerce : bool |
| Whether to coerce input types when trying this backend. |
| only : bool |
| If ``True``, no more backends will be tried if this fails. |
| Implied by ``coerce=True``. |
| try_last : bool |
| If ``True``, the global backend is tried after registered backends. |
| |
| Raises |
| ------ |
| ValueError: If the backend does not implement ``numpy.scipy.fft``. |
| |
| Notes |
| ----- |
| This will overwrite the previously set global backend, which, by default, is |
| the SciPy implementation. |
| |
| Examples |
| -------- |
| We can set the global fft backend: |
| |
| >>> from scipy.fft import fft, set_global_backend |
| >>> set_global_backend("scipy") # Sets global backend (default is "scipy"). |
| >>> fft([1]) # Calls the global backend |
| array([1.+0.j]) |
| """ |
| backend = _backend_from_arg(backend) |
| ua.set_global_backend(backend, coerce=coerce, only=only, try_last=try_last) |
|
|
|
|
| def register_backend(backend): |
| """ |
| Register a backend for permanent use. |
| |
| Registered backends have the lowest priority and will be tried after the |
| global backend. |
| |
| Parameters |
| ---------- |
| backend : {object, 'scipy'} |
| The backend to use. |
| Can either be a ``str`` containing the name of a known backend |
| {'scipy'} or an object that implements the uarray protocol. |
| |
| Raises |
| ------ |
| ValueError: If the backend does not implement ``numpy.scipy.fft``. |
| |
| Examples |
| -------- |
| We can register a new fft backend: |
| |
| >>> from scipy.fft import fft, register_backend, set_global_backend |
| >>> class NoopBackend: # Define an invalid Backend |
| ... __ua_domain__ = "numpy.scipy.fft" |
| ... def __ua_function__(self, func, args, kwargs): |
| ... return NotImplemented |
| >>> set_global_backend(NoopBackend()) # Set the invalid backend as global |
| >>> register_backend("scipy") # Register a new backend |
| # The registered backend is called because |
| # the global backend returns `NotImplemented` |
| >>> fft([1]) |
| array([1.+0.j]) |
| >>> set_global_backend("scipy") # Restore global backend to default |
| |
| """ |
| backend = _backend_from_arg(backend) |
| ua.register_backend(backend) |
|
|
|
|
| def set_backend(backend, coerce=False, only=False): |
| """Context manager to set the backend within a fixed scope. |
| |
| Upon entering the ``with`` statement, the given backend will be added to |
| the list of available backends with the highest priority. Upon exit, the |
| backend is reset to the state before entering the scope. |
| |
| Parameters |
| ---------- |
| backend : {object, 'scipy'} |
| The backend to use. |
| Can either be a ``str`` containing the name of a known backend |
| {'scipy'} or an object that implements the uarray protocol. |
| coerce : bool, optional |
| Whether to allow expensive conversions for the ``x`` parameter. e.g., |
| copying a NumPy array to the GPU for a CuPy backend. Implies ``only``. |
| only : bool, optional |
| If only is ``True`` and this backend returns ``NotImplemented``, then a |
| BackendNotImplemented error will be raised immediately. Ignoring any |
| lower priority backends. |
| |
| Examples |
| -------- |
| >>> import scipy.fft as fft |
| >>> with fft.set_backend('scipy', only=True): |
| ... fft.fft([1]) # Always calls the scipy implementation |
| array([1.+0.j]) |
| """ |
| backend = _backend_from_arg(backend) |
| return ua.set_backend(backend, coerce=coerce, only=only) |
|
|
|
|
| def skip_backend(backend): |
| """Context manager to skip a backend within a fixed scope. |
| |
| Within the context of a ``with`` statement, the given backend will not be |
| called. This covers backends registered both locally and globally. Upon |
| exit, the backend will again be considered. |
| |
| Parameters |
| ---------- |
| backend : {object, 'scipy'} |
| The backend to skip. |
| Can either be a ``str`` containing the name of a known backend |
| {'scipy'} or an object that implements the uarray protocol. |
| |
| Examples |
| -------- |
| >>> import scipy.fft as fft |
| >>> fft.fft([1]) # Calls default SciPy backend |
| array([1.+0.j]) |
| >>> with fft.skip_backend('scipy'): # We explicitly skip the SciPy backend |
| ... fft.fft([1]) # leaving no implementation available |
| Traceback (most recent call last): |
| ... |
| BackendNotImplementedError: No selected backends had an implementation ... |
| """ |
| backend = _backend_from_arg(backend) |
| return ua.skip_backend(backend) |
|
|
|
|
| set_global_backend('scipy', try_last=True) |
|
|