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

frexp implementation #11182

Merged
merged 3 commits into from
Mar 1, 2023
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
24 changes: 24 additions & 0 deletions ivy/array/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -1226,3 +1226,27 @@ def ldexp(
ivy.array([8.0, 8.0, 6.0])
"""
return ivy.ldexp(self._data, x2, out=out, **kwargs)

def frexp(self: ivy.Array, /, *, out: Optional[Tuple[ivy.Array, ivy.Array]] = None, **kwargs) -> ivy.Array:
"""
ivy.Array instance method variant of ivy.frexp. This method simply wraps
the function, and so the docstring for ivy.frexp also applies to this
method with minimal changes.
Parameters
----------
self
Input array.
out
Alternate output array in which to place the result.
The default is None.
Returns
-------
ret
The next representable values of x1 in the direction of x2.
Examples
--------
>>> x = ivy.array([1.0, 2.0, 3.0])
>>> x.frexp()
ivy.array([[0.5, 0.5, 0.75], [1, 2, 2]])
"""
return ivy.frexp(self._data, out=out, **kwargs)
90 changes: 90 additions & 0 deletions ivy/container/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -3448,3 +3448,93 @@ def ldexp(
}
"""
return self.static_ldexp(self, x2, out=out)

@staticmethod
def static_frexp(
x: Union[ivy.Array, ivy.NativeArray, 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.frexp. This method simply
wraps the function, and so the docstring for ivy.frexp also applies to this
method with minimal changes.

Parameters
----------
x
The container whose arrays should be split into mantissa and exponent.
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.

Returns
-------
ret
container including the mantissa and exponent of x.

Examples
--------
With one :class:`ivy.Container` input:
>>> x = ivy.Container(a=ivy.array([1, 2, 3]), b=ivy.array([1, 5, 10]))
>>> ivy.Container.static_frexp(x)
{
a: (ivy.array([0.5, 0.5, 0.75]), ivy.array([1, 1, 2])),
b: (ivy.array([0.5, 0.625, 0.625]), ivy.array([1, 3, 4]))
}
"""
return ContainerBase.cont_multi_map_in_function(
"frexp",
x,
key_chains=key_chains,
to_apply=to_apply,
prune_unapplied=prune_unapplied,
map_sequences=map_sequences,
out=out,
)

def frexp(
self: ivy.Container,
/,
*,
out: Optional[ivy.Container] = None,
) -> ivy.Container:
"""ivy.Container instance method variant of ivy.frexp. This method simply
wraps the function, and so the docstring for ivy.frexp also applies to this
method with minimal changes.
Parameters
----------
self
The container whose arrays should be split into mantissa and exponent.
out
optional output container, for writing the result to.
Returns
-------
ret
container including the mantissa and exponent of x.
Examples
--------
With one :class:`ivy.Container` input:
>>> x = ivy.Container(a=ivy.array([1, 2, 3]), b=ivy.array([1, 5, 10]))
>>> x.frexp()
{
a: (ivy.array([0.5, 0.5, 0.75]), ivy.array([1, 1, 2])),
b: (ivy.array([0.5, 0.625, 0.625]), ivy.array([1, 3, 4]))
}
"""
return self.static_frexp(self, out=out)
6 changes: 6 additions & 0 deletions ivy/functional/backends/jax/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,9 @@ def ldexp(
x1: JaxArray, x2: Union[JaxArray, int], /, *, out: Optional[JaxArray] = None
) -> JaxArray:
return jnp.ldexp(x1, x2)


def frexp(
x: JaxArray, /, *, out: Optional[Tuple[JaxArray, JaxArray]] = None
) -> Tuple[JaxArray, JaxArray]:
return jnp.frexp(x)
9 changes: 9 additions & 0 deletions ivy/functional/backends/numpy/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,12 @@ def ldexp(
out: Optional[np.ndarray] = None,
) -> np.ndarray:
return np.ldexp(x1, x2, out=out)


def frexp(
x: np.ndarray,
/,
*,
out: Optional[Union[Tuple[np.ndarray, np.ndarray], Tuple[None, None]]] = (None, None)
) -> Tuple[np.ndarray, np.ndarray]:
return np.frexp(x, out=out)
16 changes: 16 additions & 0 deletions ivy/functional/backends/tensorflow/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,3 +644,19 @@ def ldexp(
x2 = tf.cast(x2, x1.dtype)
ret = x1 * tf.math.pow(2, x2)
return ret


@with_unsupported_dtypes({"1.11.0 and below": ("unsigned")}, backend_version)
def frexp(
x: Union[tf.Tensor, tf.Variable],
/,
*,
out: Optional[Union[Tuple[tf.Tensor, tf.Tensor], Tuple[tf.Variable, tf.Variable]]] = None,
) -> Union[Tuple[tf.Tensor, tf.Tensor], Tuple[tf.Variable, tf.Variable]]:
e = tf.math.floor(tf.math.log(tf.math.abs(x)) / tf.cast(tf.math.log(2.), x.dtype))
e = tf.cast(e, x.dtype)
while tf.reduce_any(tf.abs(x / tf.math.pow(2, e)) >= 1):
e += tf.cast(tf.abs(x / tf.math.pow(2, e)) >= 1, e.dtype)
m = x / tf.math.pow(2, e)
e = tf.cast(e, tf.int32)
return m, e
9 changes: 9 additions & 0 deletions ivy/functional/backends/torch/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,12 @@ def ldexp(
out: Optional[torch.Tensor] = None,
) -> torch.Tensor:
return torch.ldexp(x1, x2, out=out)


def frexp(
x: torch.Tensor,
/,
*,
out: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
) -> Tuple[torch.Tensor, torch.Tensor]:
return torch.frexp(x, out=out)
33 changes: 33 additions & 0 deletions ivy/functional/ivy/experimental/elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -1451,3 +1451,36 @@ def ldexp(
ivy.array([1, 4, 12])
"""
return ivy.current_backend(x1, x2).ldexp(x1, x2, out=out)


@to_native_arrays_and_back
@handle_out_argument
@handle_nestable
@handle_exceptions
@handle_array_like_without_promotion
def frexp(
x: Union[ivy.Array, ivy.NativeArray],
/,
*,
out: Optional[Tuple[ivy.Array, ivy.Array]] = None,
) -> Tuple[ivy.Array, ivy.Array]:
"""
Decompose the elements of x into mantissa and twos exponent.
Parameters
----------
x
Input array.
out
optional output array, for writing the result to.
It must have a shape that the inputs broadcast to.
Returns
-------
ret
A tuple of two arrays, the mantissa and the twos exponent.
Examples
--------
>>> x = ivy.array([1, 2, 3])
>>> ivy.frexp(x)
(ivy.array([0.5, 0.5, 0.75]), ivy.array([1, 2, 2]))
"""
return ivy.current_backend(x).frexp(x, out=out)
Original file line number Diff line number Diff line change
Expand Up @@ -1210,3 +1210,38 @@ def test_ldexp(
x1=x[0],
x2=x[1],
)


# frexp
@handle_test(
fn_tree="functional.ivy.experimental.frexp",
dtype_and_x=helpers.dtype_and_values(
available_dtypes=["float32", "float64"],
num_arrays=1,
shared_dtype=True,
min_value=-100,
max_value=100,
min_num_dims=1,
max_num_dims=3,
),
test_gradients=st.just(False),
)
def test_frexp(
*,
dtype_and_x,
test_flags,
backend_fw,
fn_name,
on_device,
ground_truth_backend,
):
input_dtype, x = dtype_and_x
helpers.test_function(
ground_truth_backend=ground_truth_backend,
input_dtypes=input_dtype,
test_flags=test_flags,
fw=backend_fw,
fn_name=fn_name,
on_device=on_device,
x=x[0],
)