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

sinc backend implementation #4462

Merged
merged 20 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion ivy/array/extensions.py
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
# extension file
# global
import abc
from typing import Optional

# local
import ivy


class ArrayWithElementwise(abc.ABC):
def sinc(self: ivy.Array, *, out: Optional[ivy.Array] = None) -> ivy.Array:
"""
ivy.Array instance method variant of ivy.sinc. This method simply wraps the
function, and so the docstring for ivy.sinc also applies to this method
with minimal changes.

Parameters
----------
self
input array whose elements are each expressed in radians. Should have a
floating-point data type.
out
optional output array, for writing the result to. It must have a shape that
the inputs broadcast to.

Returns
-------
ret
an array containing the sinc of each element in ``self``. The returned
array must have a floating-point data type determined by
:ref:`type-promotion`.

Examples
--------
>>> x = ivy.array([0.5, 1.5, 2.5, 3.5])
>>> y = x.sinc()
>>> print(y)
ivy.array([0.637,-0.212,0.127,-0.0909])
"""
return ivy.sinc(self._data, out=out)
132 changes: 131 additions & 1 deletion ivy/container/extensions.py
Original file line number Diff line number Diff line change
@@ -1 +1,131 @@
# extension file
# global
from typing import Optional, Union, List, Dict

# local
import ivy
from ivy.container.base import ContainerBase


class ContainerWithElementwise(ContainerBase):
@staticmethod
def static_sinc(
x: ivy.Container,
/,
*,
key_chains: Optional[Union[List[str], Dict[str, str]]] = None,
to_apply: bool = True,
prune_unapplied: bool = False,
map_sequences: bool = False,
out: Optional[ivy.Container] = None,
) -> ivy.Container:
"""
ivy.Container static method variant of ivy.sinc. This method simply
wraps the function, and so the docstring for ivy.sinc also
applies to this method with minimal changes.

Parameters
----------
x
input container whose elements are each expressed in radians.
Should have a floating-point data type.
key_chains
The key-chains to apply or not apply the method to. Default is None.
to_apply
If True, the method will be applied to key_chains, otherwise key_chains
will be skipped. Default is True.
prune_unapplied
Whether to prune key_chains for which the function was not applied.
Default is False.
map_sequences
Whether to also map method to sequences (lists, tuples). Default is False.
out
optional output container, for writing the result to. It must have a shape
that the inputs broadcast to.

Returns
-------
ret
a container containing the sinc of each element in ``x``. The returned
container must have a floating-point data type determined by
:ref:`type-promotion`.

Examples
--------
>>> x = ivy.Container(a=ivy.array([0.5, 1.5, 2.5]),\
b=ivy.array([3.5, 4.5, 5.5]))
>>> y = ivy.Container.static_sinc(x)
>>> print(y)
{
a: ivy.array([0.636, -0.212, 0.127]),
b: ivy.array([-0.090, 0.070, -0.057])
}
"""
return ContainerBase.multi_map_in_static_method(
"sinc",
x,
key_chains=key_chains,
to_apply=to_apply,
prune_unapplied=prune_unapplied,
map_sequences=map_sequences,
out=out,
)

def sinc(
self: ivy.Container,
*,
key_chains: Optional[Union[List[str], Dict[str, str]]] = None,
to_apply: bool = True,
prune_unapplied: bool = False,
map_sequences: bool = False,
out: Optional[ivy.Container] = None,
) -> ivy.Container:
"""
ivy.Container instance method variant of ivy.sinc. This method simply
wraps the function, and so the docstring for ivy.sinc also
applies to this method with minimal changes.

Parameters
----------
self
input container whose elements are each expressed in radians.
Should have a floating-point data type.
key_chains
The key-chains to apply or not apply the method to. Default is None.
to_apply
If True, the method will be applied to key_chains, otherwise key_chains
will be skipped. Default is True.
prune_unapplied
Whether to prune key_chains for which the function was not applied.
Default is False.
map_sequences
Whether to also map method to sequences (lists, tuples). Default is False.
out
optional output container, for writing the result to. It must have a shape
that the inputs broadcast to.

Returns
-------
ret
a container containing the sinc of each element in ``self``.
The returned container must have a floating-point data type
determined by :ref:`type-promotion`.

Examples
--------
>>> x = ivy.Container(a=ivy.array([0.5, 1.5, 2.5]),\
b=ivy.array([3.5, 4.5, 5.5]))
>>> y = x.sinc()
>>> print(y)
{
a: ivy.array([0.637,-0.212,0.127]),
b: ivy.array([-0.0909,0.0707,-0.0579])
}
"""
return self.static_sinc(
self,
key_chains=key_chains,
to_apply=to_apply,
prune_unapplied=prune_unapplied,
map_sequences=map_sequences,
out=out,
)
7 changes: 7 additions & 0 deletions ivy/functional/backends/jax/extensions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import logging
from typing import Optional
import ivy
from ivy.functional.ivy.extensions import (
_verify_coo_components,
_verify_csr_components,
_is_coo_not_csr,
)
from ivy.functional.backends.jax import JaxArray
import jax.numpy as jnp


def is_native_sparse_array(x):
Expand Down Expand Up @@ -49,3 +52,7 @@ def native_sparse_array_to_indices_values_and_shape(x):
indices, values and shape."
)
return None, None, None


def sinc(x: JaxArray, /, *, out: Optional[JaxArray] = None) -> JaxArray:
return jnp.sinc(x)
8 changes: 8 additions & 0 deletions ivy/functional/backends/numpy/extensions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from typing import Optional
import logging
import ivy
import numpy as np
from ivy.functional.ivy.extensions import (
_verify_coo_components,
_verify_csr_components,
_is_coo_not_csr,
)
from ivy.functional.backends.numpy.helpers import _handle_0_dim_output


def is_native_sparse_array(x):
Expand Down Expand Up @@ -50,3 +53,8 @@ def native_sparse_array_to_indices_values_and_shape(x):
indices, values and shape."
)
return None, None, None


@_handle_0_dim_output
def sinc(x: np.ndarray, /, *, out: Optional[np.ndarray] = None) -> np.ndarray:
return np.sinc(x).astype(x.dtype)
11 changes: 11 additions & 0 deletions ivy/functional/backends/tensorflow/extensions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Union, Optional
import ivy
from ivy.functional.ivy.extensions import (
_verify_coo_components,
Expand Down Expand Up @@ -60,3 +61,13 @@ def native_sparse_array_to_indices_values_and_shape(x):
if isinstance(x, tf.SparseTensor):
return x.indices, x.values, x.dense_shape
raise ivy.exceptions.IvyException("not a SparseTensor")


def sinc(
x: Union[tf.Tensor, tf.Variable],
/,
*,
out: Optional[Union[tf.Tensor, tf.Variable]] = None,
) -> Union[tf.Tensor, tf.Variable]:
tf.experimental.numpy.experimental_enable_numpy_behavior()
return tf.cast(tf.experimental.numpy.sinc(x), x.dtype)
11 changes: 11 additions & 0 deletions ivy/functional/backends/torch/extensions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from typing import Optional
import ivy
from ivy.functional.ivy.extensions import (
_verify_coo_components,
_verify_csr_components,
_is_data_not_indices_values_and_shape,
_is_coo_not_csr,
)
from ivy.functional.backends.torch.elementwise import _cast_for_unary_op
import torch


Expand Down Expand Up @@ -59,3 +61,12 @@ def native_sparse_array_to_indices_values_and_shape(x):
elif x.layout == torch.sparse_csr:
return [x.crow_indices(), x.col_indices()], x.values(), x.size()
raise ivy.exceptions.IvyException("not a sparse COO/CSR Tensor")


def sinc(x: torch.Tensor, /, *, out: Optional[torch.Tensor] = None) -> torch.Tensor:
x = _cast_for_unary_op(x)
return torch.sinc(x, out=out)


sinc.support_native_out = True
sinc.unsupported_dtypes = ("float16",)
86 changes: 85 additions & 1 deletion ivy/functional/ivy/extensions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
from typing import Optional, Union
import ivy
from ivy.func_wrapper import inputs_to_native_arrays
from ivy.func_wrapper import (
handle_out_argument,
to_native_arrays_and_back,
handle_nestable,
integer_arrays_to_float,
inputs_to_native_arrays
)
from ivy.exceptions import handle_exceptions


Expand Down Expand Up @@ -369,3 +376,80 @@ def native_sparse_array(
@handle_exceptions
def native_sparse_array_to_indices_values_and_shape(x):
return ivy.current_backend().native_sparse_array_to_indices_values_and_shape(x)


@integer_arrays_to_float
@to_native_arrays_and_back
@handle_out_argument
@handle_nestable
@handle_exceptions
def sinc(
x: Union[ivy.Array, ivy.NativeArray],
/,
*,
out: Optional[ivy.Array] = None,
) -> ivy.Array:
"""Calculates an implementation-dependent approximation of the principal value of
the normalized sinc function, having domain ``(-infinity, +infinity)`` and
codomain ``[-0.217234, 1]``, for each element ``x_i`` of the input array ``x``.
Each element ``x_i`` is assumed to be expressed in radians.

**Special cases**

For floating-point operands,

- If x_i is NaN, the result is NaN.
- If ``x_i`` is ``0``, the result is ``1``.
- If ``x_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``.

Parameters
----------
x
input array. Should have a floating-point data type.
out
optional output array, for writing the result to. It must have a shape that the
inputs broadcast to.

Returns
-------
ret
an array containing the normalized sinc function of each element in x.
The returned array must have a floating-point data type determined
by :ref:`type-promotion`.

Examples
--------
With :code:`ivy.Array` input:

>>> x = ivy.array([0.5, 1.5, 2.5, 3.5])
>>> y = x.sinc()
>>> print(y)
ivy.array([0.637,-0.212,0.127,-0.0909])

>>> x = ivy.array([1.5, 0.5, -1.5])
>>> y = ivy.zeros(3)
>>> ivy.sinc(x, out=y)
>>> print(y)
ivy.array(([-0.212,0.637,-0.212])


With :code:`ivy.NativeArray` input:

>>> x = ivy.array([0.5, 1.5, 2.5, 3.5])
>>> y = ivy.sinc(x)
>>> print(y)
ivy.array([0.637,-0.212,0.127,-0.0909])

With :code:`ivy.Container` input:

>>> x = ivy.Container(a=ivy.array([0.5, 1.5, 2.5]),\
b=ivy.array([3.5, 4.5, 5.5]))
>>> y = x.sinc()
>>> print(y)
{
a: ivy.array([0.637,-0.212,0.127]),
b: ivy.array([-0.0909,0.0707,-0.0579])
}

"""
return ivy.current_backend(x).sinc(x, out=out)
36 changes: 36 additions & 0 deletions ivy_tests/test_ivy/test_functional/test_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,39 @@ def test_sparse_csr(
class_name="SparseArray",
method_name="to_dense_array",
)


# sinc
@handle_cmd_line_args
@given(
dtype_and_x=helpers.dtype_and_values(
available_dtypes=helpers.get_dtypes("float"),
large_abs_safety_factor=4,
small_abs_safety_factor=4,
),
num_positional_args=helpers.num_positional_args(fn_name="sinc"),
)
def test_sinc(
*,
dtype_and_x,
as_variable,
with_out,
num_positional_args,
native_array,
container,
instance_method,
fw,
):
input_dtype, x = dtype_and_x
helpers.test_function(
input_dtypes=input_dtype,
as_variable_flags=as_variable,
with_out=with_out,
num_positional_args=num_positional_args,
native_array_flags=native_array,
container_flags=container,
instance_method=instance_method,
fw=fw,
fn_name="sinc",
x=np.asarray(x, dtype=input_dtype),
)