From aa400c3eb5380adf6520e4c046df91a8851f227a Mon Sep 17 00:00:00 2001 From: Sayed Qaiser Ali Date: Sat, 23 Sep 2023 00:59:31 +0530 Subject: [PATCH 1/3] erf function implementation --- keras/backend/jax/math.py | 4 ++++ keras/backend/numpy/math.py | 4 ++++ keras/backend/tensorflow/math.py | 4 ++++ keras/backend/torch/math.py | 5 +++++ keras/ops/math.py | 36 ++++++++++++++++++++++++++++++ keras/ops/math_test.py | 38 ++++++++++++++++++++++++++++++++ 6 files changed, 91 insertions(+) diff --git a/keras/backend/jax/math.py b/keras/backend/jax/math.py index 8aa7ad1cecd..6ecca9741e6 100644 --- a/keras/backend/jax/math.py +++ b/keras/backend/jax/math.py @@ -248,3 +248,7 @@ def istft( def rsqrt(x): return jax.lax.rsqrt(x) + + +def erf(x): + return jax.lax.erf(x) diff --git a/keras/backend/numpy/math.py b/keras/backend/numpy/math.py index 84c0bcc30d7..2b4467ec75b 100644 --- a/keras/backend/numpy/math.py +++ b/keras/backend/numpy/math.py @@ -302,3 +302,7 @@ def istft( def rsqrt(x): return 1.0 / np.sqrt(x) + + +def erf(x): + return np.array(scipy.special.erf(x)) diff --git a/keras/backend/tensorflow/math.py b/keras/backend/tensorflow/math.py index 89d6bb5d37c..be30de85c6c 100644 --- a/keras/backend/tensorflow/math.py +++ b/keras/backend/tensorflow/math.py @@ -239,3 +239,7 @@ def istft( def rsqrt(x): return tf.math.rsqrt(x) + + +def erf(x): + return tf.math.erf(x) diff --git a/keras/backend/torch/math.py b/keras/backend/torch/math.py index a5b4c38da02..c30dfe75eb2 100644 --- a/keras/backend/torch/math.py +++ b/keras/backend/torch/math.py @@ -408,3 +408,8 @@ def istft( def rsqrt(x): x = convert_to_tensor(x) return torch.rsqrt(x) + + +def erf(x): + x = convert_to_tensor(x) + return torch.erf(x) diff --git a/keras/ops/math.py b/keras/ops/math.py index 8e508c3d04a..01d367c74f6 100644 --- a/keras/ops/math.py +++ b/keras/ops/math.py @@ -929,3 +929,39 @@ def rsqrt(x): return Rsqrt().symbolic_call(x) x = backend.convert_to_tensor(x) return backend.math.rsqrt(x) + + +class Erf(Operation): + def compute_output_spec(self, x): + return KerasTensor(shape=x.shape, dtype=x.dtype) + + def call(self, x): + return backend.math.erf(x) + + +@keras_core_export("keras_core.ops.erf") +def erf(x): + """Computes the error function of x element-wise. + + Args: + x: input tensor + + Returns: + A tensor with the same type as `x`. + + Examples: + + Basic usage + >>> x = np.array([-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]) + >>> y = Erf()(x) + + Using `float32` data type + >>> x_float32 = np.array([-3.0, -2.0], dtype=np.float32) + >>> y_float32 = Erf()(x_float32) + + Using large values + >>> x_large = np.array([1e10, -1e10]) + >>> y_large = Erf()(x_large) + """ + + return Erf()(x) diff --git a/keras/ops/math_test.py b/keras/ops/math_test.py index 2f0c47f0fc6..08276726339 100644 --- a/keras/ops/math_test.py +++ b/keras/ops/math_test.py @@ -835,3 +835,41 @@ def test_rsqrt(self): x = np.array([[1, 4, 9], [16, 25, 36]], dtype="float32") self.assertAllClose(kmath.rsqrt(x), 1 / np.sqrt(x)) self.assertAllClose(kmath.Rsqrt()(x), 1 / np.sqrt(x)) + + def test_erf_operation_basic(self): + # Sample values for testing + sample_values = np.array([-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]) + + # Expected output using numpy's approximation of the error function + expected_output = (2 / np.sqrt(np.pi)) * np.vectorize(math.erf)( + sample_values + ) + + # Output from the erf operation in keras_core + output_from_erf_op = kmath.erf(sample_values) + + # Assert that the outputs are close + self.assertAllClose(expected_output, output_from_erf_op, atol=1e-4) + + def test_erf_operation_dtype(self): + # Test for float32 and float64 data types + for dtype in ("float32", "float64"): + sample_values = np.array( + [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0], dtype=dtype + ) + expected_output = (2 / np.sqrt(np.pi)) * np.vectorize(math.erf)( + sample_values + ) + output_from_erf_op = kmath.erf(sample_values) + self.assertAllClose(expected_output, output_from_erf_op, atol=1e-4) + + def test_erf_operation_edge_cases(self): + # Test for edge cases + edge_values = np.array([1e5, -1e5, 1e-5, -1e-5], dtype=np.float64) + expected_edge_output = (2 / np.sqrt(np.pi)) * np.vectorize(math.erf)( + edge_values + ) + output_from_edge_erf_op = kmath.erf(edge_values) + self.assertAllClose( + expected_edge_output, output_from_edge_erf_op, atol=1e-4 + ) From 091b1fb245af8b8f5fb874f395774faec74a822e Mon Sep 17 00:00:00 2001 From: Sayed Qaiser Ali Date: Sat, 23 Sep 2023 10:26:54 +0530 Subject: [PATCH 2/3] used scipy.special.erf instead of math.erf in math_test.py erf function testing --- keras/ops/math.py | 2 +- keras/ops/math_test.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/keras/ops/math.py b/keras/ops/math.py index 01d367c74f6..e13bb398b8b 100644 --- a/keras/ops/math.py +++ b/keras/ops/math.py @@ -939,7 +939,7 @@ def call(self, x): return backend.math.erf(x) -@keras_core_export("keras_core.ops.erf") +@keras_export("keras_core.ops.erf") def erf(x): """Computes the error function of x element-wise. diff --git a/keras/ops/math_test.py b/keras/ops/math_test.py index 08276726339..25ce5b39d01 100644 --- a/keras/ops/math_test.py +++ b/keras/ops/math_test.py @@ -841,9 +841,9 @@ def test_erf_operation_basic(self): sample_values = np.array([-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]) # Expected output using numpy's approximation of the error function - expected_output = (2 / np.sqrt(np.pi)) * np.vectorize(math.erf)( - sample_values - ) + expected_output = (2 / np.sqrt(np.pi)) * np.vectorize( + scipy.special.erf + )(sample_values) # Output from the erf operation in keras_core output_from_erf_op = kmath.erf(sample_values) @@ -857,18 +857,18 @@ def test_erf_operation_dtype(self): sample_values = np.array( [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0], dtype=dtype ) - expected_output = (2 / np.sqrt(np.pi)) * np.vectorize(math.erf)( - sample_values - ) + expected_output = (2 / np.sqrt(np.pi)) * np.vectorize( + scipy.special.erf + )(sample_values) output_from_erf_op = kmath.erf(sample_values) self.assertAllClose(expected_output, output_from_erf_op, atol=1e-4) def test_erf_operation_edge_cases(self): # Test for edge cases edge_values = np.array([1e5, -1e5, 1e-5, -1e-5], dtype=np.float64) - expected_edge_output = (2 / np.sqrt(np.pi)) * np.vectorize(math.erf)( - edge_values - ) + expected_edge_output = (2 / np.sqrt(np.pi)) * np.vectorize( + scipy.special.erf + )(edge_values) output_from_edge_erf_op = kmath.erf(edge_values) self.assertAllClose( expected_edge_output, output_from_edge_erf_op, atol=1e-4 From 19d93dd63df2442c67e5b63b6f4216fd98bc3be6 Mon Sep 17 00:00:00 2001 From: Sayed Qaiser Ali <66676360+sqali@users.noreply.github.com> Date: Sat, 23 Sep 2023 10:39:21 +0530 Subject: [PATCH 3/3] Update math.py @keras_export("keras_core.ops.erf") corrected to @keras_export("keras.ops.erf") --- keras/ops/math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keras/ops/math.py b/keras/ops/math.py index e13bb398b8b..7497b7e456b 100644 --- a/keras/ops/math.py +++ b/keras/ops/math.py @@ -939,7 +939,7 @@ def call(self, x): return backend.math.erf(x) -@keras_export("keras_core.ops.erf") +@keras_export("keras.ops.erf") def erf(x): """Computes the error function of x element-wise.