From 5bbf8fb6acf7ddd8dd2472e3536a4a11e7cf3d0b Mon Sep 17 00:00:00 2001 From: Giacomo Pessotto <105750581+Giac3@users.noreply.github.com> Date: Fri, 24 Feb 2023 19:15:32 +0000 Subject: [PATCH] added conj elementwise function to ivy experimental API (#9958) Co-authored-by: Felix Hirwa Nshuti --- ivy/array/experimental/elementwise.py | 28 ++++ ivy/container/experimental/elementwise.py | 129 +++++++++++++++++- .../backends/jax/experimental/elementwise.py | 9 ++ .../numpy/experimental/elementwise.py | 9 ++ .../tensorflow/experimental/elementwise.py | 9 ++ .../torch/experimental/elementwise.py | 10 ++ .../ivy/experimental/elementwise.py | 56 ++++++++ .../test_core/test_elementwise.py | 30 ++++ 8 files changed, 277 insertions(+), 3 deletions(-) diff --git a/ivy/array/experimental/elementwise.py b/ivy/array/experimental/elementwise.py index 2e4efba65a845..41c0f23f0e4ec 100644 --- a/ivy/array/experimental/elementwise.py +++ b/ivy/array/experimental/elementwise.py @@ -1164,3 +1164,31 @@ def binarizer( Binarized output data """ return ivy.binarizer(self._data, threshold=threshold, out=out) + + def conj(self: ivy.Array, /, *, out: Optional[ivy.Array] = None) -> ivy.Array: + """ + ivy.Array instance method variant of ivy.conj. This method simply wraps + the function, and so the docstring for ivy.conj also applies to this + method with minimal changes. + + Parameters + ---------- + self + input array. + 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 complex conjugates of values in the input array, + with the same dtype as the input array. + + Examples + -------- + >>> x = ivy.array([4+3j, 6+2j, 1-6j]) + >>> x.conj() + ivy.array([4-3j, 6-2j, 1+6j]) + """ + return ivy.conj(self._data, out=out) diff --git a/ivy/container/experimental/elementwise.py b/ivy/container/experimental/elementwise.py index b84d5a3bbf80b..5f144d1fd11c2 100644 --- a/ivy/container/experimental/elementwise.py +++ b/ivy/container/experimental/elementwise.py @@ -3139,7 +3139,6 @@ def static_binarizer( Maps the values of the input tensor to either 0 or 1, element-wise, based on the outcome of a comparison against a threshold value. - Parameters ---------- self @@ -3190,7 +3189,6 @@ def binarizer( Maps the values of the input tensor to either 0 or 1, element-wise, based on the outcome of a comparison against a threshold value. - Parameters ---------- threshold @@ -3210,7 +3208,6 @@ def binarizer( out optional output container, for writing the result to. It must have a shape that the inputs broadcast to. - Returns ------- ret @@ -3225,3 +3222,129 @@ def binarizer( map_sequences=map_sequences, out=out, ) + + @staticmethod + def static_conj( + x: Union[ivy.Container, ivy.Array, ivy.NativeArray], + /, + *, + 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.conj. + This method simply wraps the function, and so the docstring for + ivy.conj also applies to this method with minimal changes. + + Parameters + ---------- + x + input container. + 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 output array(s) of the same + dtype as the input array(s) with the complex conjugates of + the complex values present in the input array. If x is a + container of scalar(s) then a container of scalar(s) + will be returned. + + Examples + -------- + >>> x = ivy.Container(a=ivy.array([-1+5j, 0-0j, 1.23j]), + ... b=ivy.array([7.9, 0.31+3.3j, -4.2-5.9j])) + >>> z = ivy.Container.static_conj(x) + >>> print(z) + { + a: ivy.array([-1-5j, 0+0j, -1.23j]), + b: ivy.array([7.9, 0.31-3.3j, -4.2+5.9j]) + } + """ + return ContainerBase.cont_multi_map_in_function( + "conj", + x, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) + + def conj( + 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.conj. + This method simply wraps the function, and so the docstring + for ivy.conj also applies to this method with minimal changes. + + Parameters + ---------- + self + input container. + 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 output array(s) of the same dtype + as the input array(s) with the complex conjugates of the + complex values present in the input array. + If x is a container of scalar(s) then a container of + scalar(s) will be returned. + + Examples + -------- + >>> x = ivy.Container(a=ivy.array([-1j, 0.335+2.345j, 1.23+7j]),\ + b=ivy.array([0.0, 1.2+3.3j, 1+0j])) + >>> x.conj() + { + a: ivy.array([1j, 0.335-2345j, 1.23-7j]), + b: ivy.array([0.0, 1.2-3.3j, 1-0j]) + } + """ + return self.static_conj( + self, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + ) diff --git a/ivy/functional/backends/jax/experimental/elementwise.py b/ivy/functional/backends/jax/experimental/elementwise.py index 2b1d7b1d65190..4a64505b6285c 100644 --- a/ivy/functional/backends/jax/experimental/elementwise.py +++ b/ivy/functional/backends/jax/experimental/elementwise.py @@ -524,3 +524,12 @@ def xlogy(x: JaxArray, y: JaxArray, /, *, out: Optional[JaxArray] = None) -> Jax def real(x: JaxArray, /, *, out: Optional[JaxArray] = None) -> JaxArray: return jnp.real(x) + + +def conj( + x: Union[JaxArray], + /, + *, + out: Optional[JaxArray] = None, +) -> JaxArray: + return jnp.conj(x) diff --git a/ivy/functional/backends/numpy/experimental/elementwise.py b/ivy/functional/backends/numpy/experimental/elementwise.py index 0cecbff7b30da..b59d238960ab8 100644 --- a/ivy/functional/backends/numpy/experimental/elementwise.py +++ b/ivy/functional/backends/numpy/experimental/elementwise.py @@ -429,3 +429,12 @@ def xlogy( def real(x: np.ndarray, /, *, out: Optional[np.ndarray] = None) -> np.ndarray: return np.real(x) + + +def conj( + x: Union[np.ndarray], + /, + *, + out: Optional[np.ndarray] = None, +) -> np.ndarray: + return np.conj(x, out=out) diff --git a/ivy/functional/backends/tensorflow/experimental/elementwise.py b/ivy/functional/backends/tensorflow/experimental/elementwise.py index 46ea4156f8bc3..0e4794b6bbb0a 100644 --- a/ivy/functional/backends/tensorflow/experimental/elementwise.py +++ b/ivy/functional/backends/tensorflow/experimental/elementwise.py @@ -613,3 +613,12 @@ def real( out: Optional[Union[tf.Tensor, tf.Variable]] = None, ) -> Union[tf.Tensor, tf.Variable]: return tf.math.real(x) + + +def conj( + x: Union[tf.Tensor, tf.Variable], + /, + *, + out: Optional[Union[tf.Tensor, tf.Variable]] = None, +) -> Union[tf.Tensor, tf.Variable]: + return tf.math.conj(x) diff --git a/ivy/functional/backends/torch/experimental/elementwise.py b/ivy/functional/backends/torch/experimental/elementwise.py index 3d7d4f567c0ae..0defcf3abc3f4 100644 --- a/ivy/functional/backends/torch/experimental/elementwise.py +++ b/ivy/functional/backends/torch/experimental/elementwise.py @@ -436,3 +436,13 @@ def xlogy( def real(x: torch.Tensor, /, *, out: Optional[torch.Tensor] = None) -> torch.Tensor: return torch.real(x) + + +def conj( + x: Union[torch.Tensor], + /, + *, + out: Optional[torch.Tensor] = None, +) -> torch.Tensor: + conj_x = torch.conj(x) + return torch.resolve_conj(input=conj_x) diff --git a/ivy/functional/ivy/experimental/elementwise.py b/ivy/functional/ivy/experimental/elementwise.py index 900f5e14650cf..4fadca6473832 100644 --- a/ivy/functional/ivy/experimental/elementwise.py +++ b/ivy/functional/ivy/experimental/elementwise.py @@ -1358,3 +1358,59 @@ def binarizer( xc = ivy.copy_array(x, out=out) bin = ivy.where(xc > threshold, 1, 0) return bin + + +@to_native_arrays_and_back +@handle_out_argument +@handle_nestable +@handle_exceptions +@handle_array_like_without_promotion +def conj( + x: Union[ivy.Array, ivy.NativeArray], + /, + *, + out: Optional[ivy.Array] = None, +) -> ivy.Array: + """Computes the complex conjugate of complex values in x + + 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 + an arrray of the same dtype as the input array with + the complex conjugates of the complex values present + in the input array. If x is a scalar then a scalar + will be returned. + + The descriptions above assume an array input for simplicity, but + the method also accepts :class:`ivy.Container` instances + in place of: class:`ivy.Array` or :class:`ivy.NativeArray` + instances, as shown in the type hints and also the examples below. + + + Examples + -------- + With :class:`ivy.Array` inputs: + >>> x = ivy.array([4.2-0j, 3j, 7+5j]) + >>> z = ivy.conj(x) + >>> print(z) + ivy.array([4.2+0j, -3j, 7+5j]) + + With :class:`ivy.Container` input: + >>> x = ivy.Container(a=ivy.array([-6.7-7j, 0.314+0.355j, 1.23]),\ + b=ivy.array([5j, 5.32-6.55j, 3.001])) + >>> z = ivy.conj(x) + >>> print(z) + { + a: ivy.array([-6.7+7j, 0.314-0.355j, 1.23]), + b: ivy.array([-5j, 5.32+6.55j, 3.001]) + } + """ + return ivy.current_backend(x).conj(x, out=out) diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_elementwise.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_elementwise.py index ac8708f80b627..db067b4d3c39a 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_elementwise.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_elementwise.py @@ -1125,3 +1125,33 @@ def test_binarizer( x=x[0], threshold=threshold, ) + + +# conj +@handle_test( + fn_tree="conj", + dtype_and_x=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("real_and_complex") + ), + test_with_out=st.just(False), +) +def test_conj( + *, + 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], + )