Repository: helmholtz-analytics/heat

Issue:
Implement empty() and empty_like()



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
@@ -1550,6 +1550,81 @@ def array(obj, dtype=None, copy=True, ndmin=0, split=None, device=None, comm=MPI
     return tensor(obj, tuple(gshape), dtype, split, device, comm)
 
 
+def empty(shape, dtype=types.float32, split=None, device=None, comm=MPI_WORLD):
+    """
+    Returns a new uninitialized array of given shape and data type. May be allocated split up across multiple
+    nodes along the specified axis.
+
+    Parameters
+    ----------
+    shape : int or sequence of ints
+        Desired shape of the output array, e.g. 1 or (1, 2, 3,).
+    dtype : ht.dtype
+        The desired HeAT data type for the array, defaults to ht.float32.
+    split: int, optional
+        The axis along which the array is split and distributed, defaults to None (no distribution).
+    device : str, ht.Device or None, optional
+        Specifies the device the tensor shall be allocated on, defaults to None (i.e. globally set default device).
+    comm: Communication, optional
+        Handle to the nodes holding distributed parts or copies of this tensor.
+
+    Returns
+    -------
+    out : ht.tensor
+        Array of zeros with given shape, data type and node distribution.
+
+    Examples
+    --------
+    >>> ht.empty(3)
+    tensor([ 0.0000e+00, -2.0000e+00,  3.3113e+35])
+
+    >>> ht.empty(3, dtype=ht.int)
+    tensor([ 0.0000e+00, -2.0000e+00,  3.3113e+35])
+
+    >>> ht.empty((2, 3,))
+    tensor([[ 0.0000e+00, -2.0000e+00,  3.3113e+35],
+            [ 3.6902e+19,  1.2096e+04,  7.1846e+22]])
+    """
+    return __factory(shape, dtype, split, torch.empty, device, comm)
+
+
+def empty_like(a, dtype=None, split=None, device=None, comm=MPI_WORLD):
+    """
+    Returns a new uninitialized array with the same type, shape and data distribution of given object. Data type and
+    data distribution strategy can be explicitly overriden.
+
+    Parameters
+    ----------
+    a : object
+        The shape and data-type of 'a' define these same attributes of the returned array.
+    dtype : ht.dtype, optional
+        Overrides the data type of the result.
+    split: int, optional
+        The axis along which the array is split and distributed, defaults to None (no distribution).
+    device : str, ht.Device or None, optional
+        Specifies the device the tensor shall be allocated on, defaults to None (i.e. globally set default device).
+    comm: Communication, optional
+        Handle to the nodes holding distributed parts or copies of this tensor.
+
+    Returns
+    -------
+    out : ht.tensor
+        Array of zeros with the same shape, type and split axis as 'a' unless overriden.
+
+    Examples
+    --------
+    >>> x = ht.ones((2, 3,))
+    >>> x
+    tensor([[1., 1., 1.],
+            [1., 1., 1.]])
+
+    >>> ht.empty_like(x)
+    tensor([[ 0.0000e+00, -2.0000e+00,  3.3113e+35],
+            [ 3.6902e+19,  1.2096e+04,  7.1846e+22]])
+    """
+    return __factory_like(a, dtype, split, empty, device, comm)
+
+
 def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, split=None, device=None, comm=MPI_WORLD):
     """
     Returns num evenly spaced samples, calculated over the interval [start, stop]. The endpoint of the interval can
@@ -1773,3 +1848,4 @@ def zeros_like(a, dtype=None, split=None, device=None, comm=MPI_WORLD):
             [0., 0., 0.]])
     """
     return __factory_like(a, dtype, split, zeros, device, comm)
+


Test patch:
diff --git a/heat/core/tests/test_tensor.py b/heat/core/tests/test_tensor.py
--- a/heat/core/tests/test_tensor.py
+++ b/heat/core/tests/test_tensor.py
@@ -507,3 +507,89 @@ def test_zeros_like(self):
             ht.zeros_like(ones, dtype='abc')
         with self.assertRaises(TypeError):
             ht.zeros_like(ones, split='axis')
+
+
+    def test_empty(self):
+        # scalar input
+        simple_empty_float = ht.empty(3)
+        self.assertIsInstance(simple_empty_float, ht.tensor)
+        self.assertEqual(simple_empty_float.shape,  (3,))
+        self.assertEqual(simple_empty_float.lshape, (3,))
+        self.assertEqual(simple_empty_float.split,  None)
+        self.assertEqual(simple_empty_float.dtype,  ht.float32)
+
+        # different data type
+        simple_empty_uint = ht.empty(5, dtype=ht.bool)
+        self.assertIsInstance(simple_empty_uint, ht.tensor)
+        self.assertEqual(simple_empty_uint.shape,  (5,))
+        self.assertEqual(simple_empty_uint.lshape, (5,))
+        self.assertEqual(simple_empty_uint.split,  None)
+        self.assertEqual(simple_empty_uint.dtype,  ht.bool)
+
+        # multi-dimensional
+        elaborate_empty_int = ht.empty((2, 3,), dtype=ht.int32)
+        self.assertIsInstance(elaborate_empty_int, ht.tensor)
+        self.assertEqual(elaborate_empty_int.shape,  (2, 3,))
+        self.assertEqual(elaborate_empty_int.lshape, (2, 3,))
+        self.assertEqual(elaborate_empty_int.split,  None)
+        self.assertEqual(elaborate_empty_int.dtype,  ht.int32)
+
+        # split axis
+        elaborate_empty_split = ht.empty((6, 4,), dtype=ht.int32, split=0)
+        self.assertIsInstance(elaborate_empty_split, ht.tensor)
+        self.assertEqual(elaborate_empty_split.shape,         (6, 4,))
+        self.assertLessEqual(elaborate_empty_split.lshape[0], 6)
+        self.assertEqual(elaborate_empty_split.lshape[1],     4)
+        self.assertEqual(elaborate_empty_split.split,         0)
+        self.assertEqual(elaborate_empty_split.dtype,         ht.int32)
+
+        # exceptions
+        with self.assertRaises(TypeError):
+            ht.empty('(2, 3,)', dtype=ht.float64)
+        with self.assertRaises(ValueError):
+            ht.empty((-1, 3,), dtype=ht.float64)
+        with self.assertRaises(TypeError):
+            ht.empty((2, 3,), dtype=ht.float64, split='axis')
+
+
+    def test_empty_like(self):
+        # scalar
+        like_int = ht.empty_like(3)
+        self.assertIsInstance(like_int, ht.tensor)
+        self.assertEqual(like_int.shape,  (1,))
+        self.assertEqual(like_int.lshape, (1,))
+        self.assertEqual(like_int.split,  None)
+        self.assertEqual(like_int.dtype,  ht.int32)
+
+        # sequence
+        like_str = ht.empty_like('abc')
+        self.assertIsInstance(like_str, ht.tensor)
+        self.assertEqual(like_str.shape,  (3,))
+        self.assertEqual(like_str.lshape, (3,))
+        self.assertEqual(like_str.split,  None)
+        self.assertEqual(like_str.dtype,  ht.float32)
+
+        # elaborate tensor
+        ones = ht.ones((2, 3,), dtype=ht.uint8)
+        like_ones = ht.empty_like(ones)
+        self.assertIsInstance(like_ones, ht.tensor)
+        self.assertEqual(like_ones.shape,  (2, 3,))
+        self.assertEqual(like_ones.lshape, (2, 3,))
+        self.assertEqual(like_ones.split,  None)
+        self.assertEqual(like_ones.dtype,  ht.uint8)
+
+        # elaborate tensor with split
+        ones_split = ht.ones((2, 3,), dtype=ht.uint8, split=0)
+        like_ones_split = ht.empty_like(ones_split)
+        self.assertIsInstance(like_ones_split,          ht.tensor)
+        self.assertEqual(like_ones_split.shape,         (2, 3,))
+        self.assertLessEqual(like_ones_split.lshape[0], 2)
+        self.assertEqual(like_ones_split.lshape[1],     3)
+        self.assertEqual(like_ones_split.split,         0)
+        self.assertEqual(like_ones_split.dtype,         ht.uint8)
+
+        # exceptions
+        with self.assertRaises(TypeError):
+            ht.empty_like(ones, dtype='abc')
+        with self.assertRaises(TypeError):
+            ht.empty_like(ones, split='axis')