Repository: helmholtz-analytics/heat

Issue:
Implement sinh



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
@@ -1054,6 +1054,31 @@ def sin(self, out=None):
         """
         return trigonometrics.sin(self, out)
 
+    def sinh(self, out=None):
+        """
+        Return the hyperbolic sine, element-wise.
+
+        Parameters
+        ----------
+        x : ht.tensor
+            The value for which to compute the hyperbolic sine.
+        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 sine : ht.tensor
+            A tensor of the same shape as x, containing the trigonometric sine of each element in this tensor.
+            Negative input elements are returned as nan. If out was provided, square_roots is a reference to it.
+
+        Examples
+        --------
+        >>> ht.sinh(ht.arange(-6, 7, 2))
+        tensor([[-201.7132,  -27.2899,   -3.6269,    0.0000,    3.6269,   27.2899,  201.7132])
+        """
+        return trigonometrics.sinh(self, out)
+
     def sqrt(self, out=None):
         """
         Return the non-negative square-root of the tensor element-wise.
diff --git a/heat/core/trigonometrics.py b/heat/core/trigonometrics.py
--- a/heat/core/trigonometrics.py
+++ b/heat/core/trigonometrics.py
@@ -5,6 +5,7 @@
 __all__ = [
     'cos',
     'sin',
+    'sinh',
     'tan'
 ]
 
@@ -61,6 +62,32 @@ def sin(x, out=None):
     return local_op(torch.sin, x, out)
 
 
+def sinh(x, out=None):
+    """
+    Return the hyperbolic sine, element-wise.
+
+    Parameters
+    ----------
+    x : ht.tensor
+        The value for which to compute the hyperbolic sine.
+    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 sine : ht.tensor
+        A tensor of the same shape as x, containing the trigonometric sine of each element in this tensor.
+        Negative input elements are returned as nan. If out was provided, square_roots is a reference to it.
+
+    Examples
+    --------
+    >>> ht.sinh(ht.arange(-6, 7, 2))
+    tensor([[-201.7132,  -27.2899,   -3.6269,    0.0000,    3.6269,   27.2899,  201.7132])
+    """
+    return local_op(torch.sinh, x, out)
+
+
 def tan(x, out=None):
     """
     Compute tangent element-wise.


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
@@ -91,6 +91,49 @@ def test_sin(self):
         with self.assertRaises(TypeError):
             ht.sin('hello world')
 
+    def test_sinh(self):
+        # base elements
+        elements = 30
+        comparison = torch.arange(elements, dtype=torch.float64).sinh()
+
+        # sine of float32
+        float32_tensor = ht.arange(elements, dtype=ht.float32)
+        float32_sinh = ht.sinh(float32_tensor)
+        self.assertIsInstance(float32_sinh, ht.tensor)
+        self.assertEqual(float32_sinh.dtype, ht.float32)
+        self.assertEqual(float32_sinh.dtype, ht.float32)
+        self.assertTrue(torch.allclose(float32_sinh._tensor__array.type(torch.double), comparison))
+
+        # sine of float64
+        float64_tensor = ht.arange(elements, dtype=ht.float64)
+        float64_sinh = ht.sinh(float64_tensor)
+        self.assertIsInstance(float64_sinh, ht.tensor)
+        self.assertEqual(float64_sinh.dtype, ht.float64)
+        self.assertEqual(float64_sinh.dtype, ht.float64)
+        self.assertTrue(torch.allclose(float64_sinh._tensor__array.type(torch.double), comparison))
+
+        # sine of ints, automatic conversion to intermediate floats
+        int32_tensor = ht.arange(elements, dtype=ht.int32)
+        int32_sinh = ht.sinh(int32_tensor)
+        self.assertIsInstance(int32_sinh, ht.tensor)
+        self.assertEqual(int32_sinh.dtype, ht.float64)
+        self.assertEqual(int32_sinh.dtype, ht.float64)
+        self.assertTrue(torch.allclose(int32_sinh._tensor__array.type(torch.double), comparison))
+
+        # sine of longs, automatic conversion to intermediate floats
+        int64_tensor = ht.arange(elements, dtype=ht.int64)
+        int64_sinh = ht.sinh(int64_tensor)
+        self.assertIsInstance(int64_sinh, ht.tensor)
+        self.assertEqual(int64_sinh.dtype, ht.float64)
+        self.assertEqual(int64_sinh.dtype, ht.float64)
+        self.assertTrue(torch.allclose(int64_sinh._tensor__array.type(torch.double), comparison))
+
+        # check exceptions
+        with self.assertRaises(TypeError):
+            ht.sinh([1, 2, 3])
+        with self.assertRaises(TypeError):
+            ht.sinh('hello world')
+
     def test_tan(self):
         # base elements
         elements = 30