Repository: helmholtz-analytics/heat

Issue:
Implement tanh()



Discussion:

Resolving patch:
diff --git a/heat/core/tensor.py b/heat/core/tensor.py
--- a/heat/core/tensor.py
+++ b/heat/core/tensor.py
@@ -1222,6 +1222,30 @@ def tan(self, out=None):
         """
         return trigonometrics.tan(self, out)
 
+    def tanh(self, out=None):
+        """
+        Return the hyperbolic tangent, element-wise.
+
+        Parameters
+        ----------
+        x : ht.tensor
+            The value for which to compute the hyperbolic tangent.
+        out : ht.tensor or None, optional
+            A location in which to store the results. If provided, it must have a broadcastable shape. If not provided
+            or set to None, a fresh tensor is allocated.
+
+        Returns
+        -------
+        hyperbolic tangent : ht.tensor
+            A tensor of the same shape as x, containing the hyperbolic tangent of each element in this tensor.
+
+        Examples
+        --------
+        >>> ht.tanh(ht.arange(-6, 7, 2))
+        tensor([-1.0000, -0.9993, -0.9640,  0.0000,  0.9640,  0.9993,  1.0000])
+        """
+        return trigonometrics.tanh(self, out)
+
     def transpose(self, axes=None):
         """
         Permute the dimensions of an array.
diff --git a/heat/core/trigonometrics.py b/heat/core/trigonometrics.py
--- a/heat/core/trigonometrics.py
+++ b/heat/core/trigonometrics.py
@@ -7,7 +7,8 @@
     'cosh',
     'sin',
     'sinh',
-    'tan'
+    'tan',
+    'tanh'
 ]
 
 
@@ -140,3 +141,28 @@ def tan(x, out=None):
     tensor([ 0.29100619, -1.15782128,  2.18503986,  0., -2.18503986, 1.15782128, -0.29100619])
     """
     return local_op(torch.tan, x, out)
+
+
+def tanh(x, out=None):
+    """
+    Return the hyperbolic tangent, element-wise.
+
+    Parameters
+    ----------
+    x : ht.tensor
+        The value for which to compute the hyperbolic tangent.
+    out : ht.tensor or None, optional
+        A location in which to store the results. If provided, it must have a broadcastable shape. If not provided
+        or set to None, a fresh tensor is allocated.
+
+    Returns
+    -------
+    hyperbolic tangent : ht.tensor
+        A tensor of the same shape as x, containing the hyperbolic tangent of each element in this tensor.
+
+    Examples
+    --------
+    >>> ht.tanh(ht.arange(-6, 7, 2))
+    tensor([-1.0000, -0.9993, -0.9640,  0.0000,  0.9640,  0.9993,  1.0000])
+    """
+    return local_op(torch.tanh, x, out)


Test patch:
diff --git a/heat/core/tests/test_trigonometrics.py b/heat/core/tests/test_trigonometrics.py
--- a/heat/core/tests/test_trigonometrics.py
+++ b/heat/core/tests/test_trigonometrics.py
@@ -220,3 +220,46 @@ def test_tan(self):
         with self.assertRaises(TypeError):
             ht.tan('hello world')
 
+    def test_tanh(self):
+        # base elements
+        elements = 30
+        comparison = torch.arange(elements, dtype=torch.float64).tanh()
+
+        # tangent of float32
+        float32_tensor = ht.arange(elements, dtype=ht.float32)
+        float32_tanh = ht.tanh(float32_tensor)
+        self.assertIsInstance(float32_tanh, ht.tensor)
+        self.assertEqual(float32_tanh.dtype, ht.float32)
+        self.assertEqual(float32_tanh.dtype, ht.float32)
+        self.assertTrue(torch.allclose(float32_tanh._tensor__array.type(torch.double), comparison))
+
+        # tangent of float64
+        float64_tensor = ht.arange(elements, dtype=ht.float64)
+        float64_tanh = ht.tanh(float64_tensor)
+        self.assertIsInstance(float64_tanh, ht.tensor)
+        self.assertEqual(float64_tanh.dtype, ht.float64)
+        self.assertEqual(float64_tanh.dtype, ht.float64)
+        self.assertTrue(torch.allclose(float64_tanh._tensor__array.type(torch.double), comparison))
+
+        # tangent of ints, automatic conversion to intermediate floats
+        int32_tensor = ht.arange(elements, dtype=ht.int32)
+        int32_tanh = ht.tanh(int32_tensor)
+        self.assertIsInstance(int32_tanh, ht.tensor)
+        self.assertEqual(int32_tanh.dtype, ht.float64)
+        self.assertEqual(int32_tanh.dtype, ht.float64)
+        self.assertTrue(torch.allclose(int32_tanh._tensor__array.type(torch.double), comparison))
+
+        # tangent of longs, automatic conversion to intermediate floats
+        int64_tensor = ht.arange(elements, dtype=ht.int64)
+        int64_tanh = ht.tanh(int64_tensor)
+        self.assertIsInstance(int64_tanh, ht.tensor)
+        self.assertEqual(int64_tanh.dtype, ht.float64)
+        self.assertEqual(int64_tanh.dtype, ht.float64)
+        self.assertTrue(torch.allclose(int64_tanh._tensor__array.type(torch.double), comparison))
+
+        # check exceptions
+        with self.assertRaises(TypeError):
+            ht.tanh([1, 2, 3])
+        with self.assertRaises(TypeError):
+            ht.tanh('hello world')
+