| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- from functools import update_wrapper, lru_cache
- import inspect
- from ._pocketfft import helper as _helper
- import numpy as np
- from scipy._lib._array_api import array_namespace
- from scipy._lib._array_api import xp_capabilities
- _init_nd_shape_and_axes = _helper._init_nd_shape_and_axes
- def next_fast_len(target, real=False):
- """Find the next fast size of input data to ``fft``, for zero-padding, etc.
- SciPy's FFT algorithms gain their speed by a recursive divide and conquer
- strategy. This relies on efficient functions for small prime factors of the
- input length. Thus, the transforms are fastest when using composites of the
- prime factors handled by the fft implementation. If there are efficient
- functions for all radices <= `n`, then the result will be a number `x`
- >= ``target`` with only prime factors < `n`. (Also known as `n`-smooth
- numbers)
- Parameters
- ----------
- target : int
- Length to start searching from. Must be a positive integer.
- real : bool, optional
- True if the FFT involves real input or output (e.g., `rfft` or `hfft`
- but not `fft`). Defaults to False.
- Returns
- -------
- out : int
- The smallest fast length greater than or equal to ``target``.
- Notes
- -----
- The result of this function may change in future as performance
- considerations change, for example, if new prime factors are added.
- Calling `fft` or `ifft` with real input data performs an ``'R2C'``
- transform internally.
- Examples
- --------
- On a particular machine, an FFT of prime length takes 11.4 ms:
- >>> from scipy import fft
- >>> import numpy as np
- >>> rng = np.random.default_rng()
- >>> min_len = 93059 # prime length is worst case for speed
- >>> a = rng.standard_normal(min_len)
- >>> b = fft.fft(a)
- Zero-padding to the next regular length reduces computation time to
- 1.6 ms, a speedup of 7.3 times:
- >>> fft.next_fast_len(min_len, real=True)
- 93312
- >>> b = fft.fft(a, 93312)
- Rounding up to the next power of 2 is not optimal, taking 3.0 ms to
- compute; 1.9 times longer than the size given by ``next_fast_len``:
- >>> b = fft.fft(a, 131072)
- """
- pass
- # Directly wrap the c-function good_size but take the docstring etc., from the
- # next_fast_len function above
- _sig = inspect.signature(next_fast_len)
- next_fast_len = update_wrapper(lru_cache(_helper.good_size), next_fast_len)
- next_fast_len = xp_capabilities(out_of_scope=True)(next_fast_len)
- next_fast_len.__wrapped__ = _helper.good_size
- next_fast_len.__signature__ = _sig
- def prev_fast_len(target, real=False):
- """Find the previous fast size of input data to ``fft``.
- Useful for discarding a minimal number of samples before FFT.
- SciPy's FFT algorithms gain their speed by a recursive divide and conquer
- strategy. This relies on efficient functions for small prime factors of the
- input length. Thus, the transforms are fastest when using composites of the
- prime factors handled by the fft implementation. If there are efficient
- functions for all radices <= `n`, then the result will be a number `x`
- <= ``target`` with only prime factors <= `n`. (Also known as `n`-smooth
- numbers)
- Parameters
- ----------
- target : int
- Maximum length to search until. Must be a positive integer.
- real : bool, optional
- True if the FFT involves real input or output (e.g., `rfft` or `hfft`
- but not `fft`). Defaults to False.
- Returns
- -------
- out : int
- The largest fast length less than or equal to ``target``.
- Notes
- -----
- The result of this function may change in future as performance
- considerations change, for example, if new prime factors are added.
- Calling `fft` or `ifft` with real input data performs an ``'R2C'``
- transform internally.
- In the current implementation, prev_fast_len assumes radices of
- 2,3,5,7,11 for complex FFT and 2,3,5 for real FFT.
- Examples
- --------
- On a particular machine, an FFT of prime length takes 16.2 ms:
- >>> from scipy import fft
- >>> import numpy as np
- >>> rng = np.random.default_rng()
- >>> max_len = 93059 # prime length is worst case for speed
- >>> a = rng.standard_normal(max_len)
- >>> b = fft.fft(a)
- Performing FFT on the maximum fast length less than max_len
- reduces the computation time to 1.5 ms, a speedup of 10.5 times:
- >>> fft.prev_fast_len(max_len, real=True)
- 92160
- >>> c = fft.fft(a[:92160]) # discard last 899 samples
- """
- pass
- # Directly wrap the c-function prev_good_size but take the docstring etc.,
- # from the prev_fast_len function above
- _sig_prev_fast_len = inspect.signature(prev_fast_len)
- prev_fast_len = update_wrapper(lru_cache()(_helper.prev_good_size), prev_fast_len)
- prev_fast_len = xp_capabilities(out_of_scope=True)(prev_fast_len)
- prev_fast_len.__wrapped__ = _helper.prev_good_size
- prev_fast_len.__signature__ = _sig_prev_fast_len
- @xp_capabilities()
- def fftfreq(n, d=1.0, *, xp=None, device=None):
- """Return the Discrete Fourier Transform sample frequencies.
- The returned float array `f` contains the frequency bin centers in cycles
- per unit of the sample spacing (with zero at the start). For instance, if
- the sample spacing is in seconds, then the frequency unit is cycles/second.
- Given a window length `n` and a sample spacing `d`::
- f = [0, 1, ..., n/2-1, -n/2, ..., -1] / (d*n) if n is even
- f = [0, 1, ..., (n-1)/2, -(n-1)/2, ..., -1] / (d*n) if n is odd
- Parameters
- ----------
- n : int
- Window length.
- d : scalar, optional
- Sample spacing (inverse of the sampling rate). Defaults to 1.
- xp : array_namespace, optional
- The namespace for the return array. Default is None, where NumPy is used.
- device : device, optional
- The device for the return array.
- Only valid when `xp.fft.fftfreq` implements the device parameter.
- Returns
- -------
- f : ndarray
- Array of length `n` containing the sample frequencies.
- Examples
- --------
- >>> import numpy as np
- >>> import scipy.fft
- >>> signal = np.array([-2, 8, 6, 4, 1, 0, 3, 5], dtype=float)
- >>> fourier = scipy.fft.fft(signal)
- >>> n = signal.size
- >>> timestep = 0.1
- >>> freq = scipy.fft.fftfreq(n, d=timestep)
- >>> freq
- array([ 0. , 1.25, 2.5 , ..., -3.75, -2.5 , -1.25])
- """
- xp = np if xp is None else xp
- # numpy does not yet support the `device` keyword
- # `xp.__name__ != 'numpy'` should be removed when numpy is compatible
- if hasattr(xp, 'fft') and xp.__name__ != 'numpy':
- return xp.fft.fftfreq(n, d=d, device=device)
- if device is not None:
- raise ValueError('device parameter is not supported for input array type')
- return np.fft.fftfreq(n, d=d)
- @xp_capabilities()
- def rfftfreq(n, d=1.0, *, xp=None, device=None):
- """Return the Discrete Fourier Transform sample frequencies
- (for usage with rfft, irfft).
- The returned float array `f` contains the frequency bin centers in cycles
- per unit of the sample spacing (with zero at the start). For instance, if
- the sample spacing is in seconds, then the frequency unit is cycles/second.
- Given a window length `n` and a sample spacing `d`::
- f = [0, 1, ..., n/2-1, n/2] / (d*n) if n is even
- f = [0, 1, ..., (n-1)/2-1, (n-1)/2] / (d*n) if n is odd
- Unlike `fftfreq` (but like `scipy.fftpack.rfftfreq`)
- the Nyquist frequency component is considered to be positive.
- Parameters
- ----------
- n : int
- Window length.
- d : scalar, optional
- Sample spacing (inverse of the sampling rate). Defaults to 1.
- xp : array_namespace, optional
- The namespace for the return array. Default is None, where NumPy is used.
- device : device, optional
- The device for the return array.
- Only valid when `xp.fft.rfftfreq` implements the device parameter.
- Returns
- -------
- f : ndarray
- Array of length ``n//2 + 1`` containing the sample frequencies.
- Examples
- --------
- >>> import numpy as np
- >>> import scipy.fft
- >>> signal = np.array([-2, 8, 6, 4, 1, 0, 3, 5, -3, 4], dtype=float)
- >>> fourier = scipy.fft.rfft(signal)
- >>> n = signal.size
- >>> sample_rate = 100
- >>> freq = scipy.fft.fftfreq(n, d=1./sample_rate)
- >>> freq
- array([ 0., 10., 20., ..., -30., -20., -10.])
- >>> freq = scipy.fft.rfftfreq(n, d=1./sample_rate)
- >>> freq
- array([ 0., 10., 20., 30., 40., 50.])
- """
- xp = np if xp is None else xp
- # numpy does not yet support the `device` keyword
- # `xp.__name__ != 'numpy'` should be removed when numpy is compatible
- if hasattr(xp, 'fft') and xp.__name__ != 'numpy':
- return xp.fft.rfftfreq(n, d=d, device=device)
- if device is not None:
- raise ValueError('device parameter is not supported for input array type')
- return np.fft.rfftfreq(n, d=d)
- @xp_capabilities()
- def fftshift(x, axes=None):
- """Shift the zero-frequency component to the center of the spectrum.
- This function swaps half-spaces for all axes listed (defaults to all).
- Note that ``y[0]`` is the Nyquist component only if ``len(x)`` is even.
- Parameters
- ----------
- x : array_like
- Input array.
- axes : int or shape tuple, optional
- Axes over which to shift. Default is None, which shifts all axes.
- Returns
- -------
- y : ndarray
- The shifted array.
- See Also
- --------
- ifftshift : The inverse of `fftshift`.
- Examples
- --------
- >>> import numpy as np
- >>> freqs = np.fft.fftfreq(10, 0.1)
- >>> freqs
- array([ 0., 1., 2., ..., -3., -2., -1.])
- >>> np.fft.fftshift(freqs)
- array([-5., -4., -3., -2., -1., 0., 1., 2., 3., 4.])
- Shift the zero-frequency component only along the second axis:
- >>> freqs = np.fft.fftfreq(9, d=1./9).reshape(3, 3)
- >>> freqs
- array([[ 0., 1., 2.],
- [ 3., 4., -4.],
- [-3., -2., -1.]])
- >>> np.fft.fftshift(freqs, axes=(1,))
- array([[ 2., 0., 1.],
- [-4., 3., 4.],
- [-1., -3., -2.]])
- """
- xp = array_namespace(x)
- if hasattr(xp, 'fft'):
- return xp.fft.fftshift(x, axes=axes)
- x = np.asarray(x)
- y = np.fft.fftshift(x, axes=axes)
- return xp.asarray(y)
- @xp_capabilities()
- def ifftshift(x, axes=None):
- """The inverse of `fftshift`. Although identical for even-length `x`, the
- functions differ by one sample for odd-length `x`.
- Parameters
- ----------
- x : array_like
- Input array.
- axes : int or shape tuple, optional
- Axes over which to calculate. Defaults to None, which shifts all axes.
- Returns
- -------
- y : ndarray
- The shifted array.
- See Also
- --------
- fftshift : Shift zero-frequency component to the center of the spectrum.
- Examples
- --------
- >>> import numpy as np
- >>> freqs = np.fft.fftfreq(9, d=1./9).reshape(3, 3)
- >>> freqs
- array([[ 0., 1., 2.],
- [ 3., 4., -4.],
- [-3., -2., -1.]])
- >>> np.fft.ifftshift(np.fft.fftshift(freqs))
- array([[ 0., 1., 2.],
- [ 3., 4., -4.],
- [-3., -2., -1.]])
- """
- xp = array_namespace(x)
- if hasattr(xp, 'fft'):
- return xp.fft.ifftshift(x, axes=axes)
- x = np.asarray(x)
- y = np.fft.ifftshift(x, axes=axes)
- return xp.asarray(y)
|