diff --git a/src/array_api_extra/__init__.py b/src/array_api_extra/__init__.py index ee8a593f..54fd4ba2 100644 --- a/src/array_api_extra/__init__.py +++ b/src/array_api_extra/__init__.py @@ -1,6 +1,6 @@ """Extra array functions built on top of the array API standard.""" -from ._delegation import expand_dims, isclose, nan_to_num, one_hot, pad +from ._delegation import expand_dims, isclose, nan_to_num, one_hot, pad, sinc from ._lib._at import at from ._lib._funcs import ( apply_where, @@ -12,7 +12,6 @@ kron, nunique, setdiff1d, - sinc, ) from ._lib._lazy import lazy_apply diff --git a/src/array_api_extra/_delegation.py b/src/array_api_extra/_delegation.py index a2d8cf8d..26d8d0cd 100644 --- a/src/array_api_extra/_delegation.py +++ b/src/array_api_extra/_delegation.py @@ -18,7 +18,7 @@ from ._lib._utils._helpers import asarrays from ._lib._utils._typing import Array, DType -__all__ = ["expand_dims", "isclose", "nan_to_num", "one_hot", "pad"] +__all__ = ["expand_dims", "isclose", "nan_to_num", "one_hot", "pad", "sinc"] def expand_dims( @@ -414,3 +414,97 @@ def pad( return xp.nn.functional.pad(x, tuple(pad_width), value=constant_values) # type: ignore[arg-type] # pyright: ignore[reportArgumentType] return _funcs.pad(x, pad_width, constant_values=constant_values, xp=xp) + + +def sinc(x: Array, /, *, xp: ModuleType | None = None) -> Array: + r""" + Return the normalized sinc function. + + The sinc function is equal to :math:`\sin(\pi x)/(\pi x)` for any argument + :math:`x\ne 0`. ``sinc(0)`` takes the limit value 1, making ``sinc`` not + only everywhere continuous but also infinitely differentiable. + + .. note:: + + Note the normalization factor of ``pi`` used in the definition. + This is the most commonly used definition in signal processing. + Use ``sinc(x / xp.pi)`` to obtain the unnormalized sinc function + :math:`\sin(x)/x` that is more common in mathematics. + + Parameters + ---------- + x : array + Array (possibly multi-dimensional) of values for which to calculate + ``sinc(x)``. Must have a real floating point dtype. + xp : array_namespace, optional + The standard-compatible namespace for `x`. Default: infer. + + Returns + ------- + array + ``sinc(x)`` calculated elementwise, which has the same shape as the input. + + Notes + ----- + The name sinc is short for "sine cardinal" or "sinus cardinalis". + + The sinc function is used in various signal processing applications, + including in anti-aliasing, in the construction of a Lanczos resampling + filter, and in interpolation. + + For bandlimited interpolation of discrete-time signals, the ideal + interpolation kernel is proportional to the sinc function. + + References + ---------- + #. Weisstein, Eric W. "Sinc Function." From MathWorld--A Wolfram Web + Resource. https://mathworld.wolfram.com/SincFunction.html + #. Wikipedia, "Sinc function", + https://en.wikipedia.org/wiki/Sinc_function + + Examples + -------- + >>> import array_api_strict as xp + >>> import array_api_extra as xpx + >>> x = xp.linspace(-4, 4, 41) + >>> xpx.sinc(x, xp=xp) + Array([-3.89817183e-17, -4.92362781e-02, + -8.40918587e-02, -8.90384387e-02, + -5.84680802e-02, 3.89817183e-17, + 6.68206631e-02, 1.16434881e-01, + 1.26137788e-01, 8.50444803e-02, + -3.89817183e-17, -1.03943254e-01, + -1.89206682e-01, -2.16236208e-01, + -1.55914881e-01, 3.89817183e-17, + 2.33872321e-01, 5.04551152e-01, + 7.56826729e-01, 9.35489284e-01, + 1.00000000e+00, 9.35489284e-01, + 7.56826729e-01, 5.04551152e-01, + 2.33872321e-01, 3.89817183e-17, + -1.55914881e-01, -2.16236208e-01, + -1.89206682e-01, -1.03943254e-01, + -3.89817183e-17, 8.50444803e-02, + 1.26137788e-01, 1.16434881e-01, + 6.68206631e-02, 3.89817183e-17, + -5.84680802e-02, -8.90384387e-02, + -8.40918587e-02, -4.92362781e-02, + -3.89817183e-17], dtype=array_api_strict.float64) + """ + + if xp is None: + xp = array_namespace(x) + + if not xp.isdtype(x.dtype, "real floating"): + err_msg = "`x` must have a real floating data type." + raise ValueError(err_msg) + + if ( + is_numpy_namespace(xp) + or is_cupy_namespace(xp) + or is_jax_namespace(xp) + or is_torch_namespace(xp) + or is_dask_namespace(xp) + ): + return xp.sinc(x) + + return _funcs.sinc(x, xp=xp) diff --git a/src/array_api_extra/_lib/_funcs.py b/src/array_api_extra/_lib/_funcs.py index 53b7c7e0..b25e6e3e 100644 --- a/src/array_api_extra/_lib/_funcs.py +++ b/src/array_api_extra/_lib/_funcs.py @@ -864,86 +864,10 @@ def setdiff1d( return x1_[_helpers.in1d(x1_, x2_, assume_unique=True, invert=True, xp=xp)] -def sinc(x: Array, /, *, xp: ModuleType | None = None) -> Array: - r""" - Return the normalized sinc function. - - The sinc function is equal to :math:`\sin(\pi x)/(\pi x)` for any argument - :math:`x\ne 0`. ``sinc(0)`` takes the limit value 1, making ``sinc`` not - only everywhere continuous but also infinitely differentiable. - - .. note:: - - Note the normalization factor of ``pi`` used in the definition. - This is the most commonly used definition in signal processing. - Use ``sinc(x / xp.pi)`` to obtain the unnormalized sinc function - :math:`\sin(x)/x` that is more common in mathematics. - - Parameters - ---------- - x : array - Array (possibly multi-dimensional) of values for which to calculate - ``sinc(x)``. Must have a real floating point dtype. - xp : array_namespace, optional - The standard-compatible namespace for `x`. Default: infer. - - Returns - ------- - array - ``sinc(x)`` calculated elementwise, which has the same shape as the input. - - Notes - ----- - The name sinc is short for "sine cardinal" or "sinus cardinalis". - - The sinc function is used in various signal processing applications, - including in anti-aliasing, in the construction of a Lanczos resampling - filter, and in interpolation. - - For bandlimited interpolation of discrete-time signals, the ideal - interpolation kernel is proportional to the sinc function. - - References - ---------- - #. Weisstein, Eric W. "Sinc Function." From MathWorld--A Wolfram Web - Resource. https://mathworld.wolfram.com/SincFunction.html - #. Wikipedia, "Sinc function", - https://en.wikipedia.org/wiki/Sinc_function - - Examples - -------- - >>> import array_api_strict as xp - >>> import array_api_extra as xpx - >>> x = xp.linspace(-4, 4, 41) - >>> xpx.sinc(x, xp=xp) - Array([-3.89817183e-17, -4.92362781e-02, - -8.40918587e-02, -8.90384387e-02, - -5.84680802e-02, 3.89817183e-17, - 6.68206631e-02, 1.16434881e-01, - 1.26137788e-01, 8.50444803e-02, - -3.89817183e-17, -1.03943254e-01, - -1.89206682e-01, -2.16236208e-01, - -1.55914881e-01, 3.89817183e-17, - 2.33872321e-01, 5.04551152e-01, - 7.56826729e-01, 9.35489284e-01, - 1.00000000e+00, 9.35489284e-01, - 7.56826729e-01, 5.04551152e-01, - 2.33872321e-01, 3.89817183e-17, - -1.55914881e-01, -2.16236208e-01, - -1.89206682e-01, -1.03943254e-01, - -3.89817183e-17, 8.50444803e-02, - 1.26137788e-01, 1.16434881e-01, - 6.68206631e-02, 3.89817183e-17, - -5.84680802e-02, -8.90384387e-02, - -8.40918587e-02, -4.92362781e-02, - -3.89817183e-17], dtype=array_api_strict.float64) - """ - if xp is None: - xp = array_namespace(x) +def sinc(x: Array, /, *, xp: ModuleType) -> Array: + # numpydoc ignore=PR01,RT01 + """See docstring in `array_api_extra._delegation.py`.""" - if not xp.isdtype(x.dtype, "real floating"): - err_msg = "`x` must have a real floating data type." - raise ValueError(err_msg) # no scalars in `where` - array-api#807 y = xp.pi * xp.where( xp.astype(x, xp.bool),