Repository: OpenMined/PySyft

Issue:
Implement Default new Functionality for Base Tensor Type
**User Story A:** As a Data Scientist using Syft's Base Tensor type, I want to leverage a default method for computing operations on a Tensor of arbitrary type. For this ticket to be complete, new() should return a new tensor. For a reference on the operation this performs check out [PyTorch](http://pytorch.org/docs/master/tensors.html)'s documentation.

**Acceptance Criteria:**
- If the Base Tensor type's attribute "encrypted" is set to True, it should return a NotImplemented error.
- a unit test demonstrating the correct operation on the Base Tensor type implemented over int and float Tensors.
- inline documentation in the python code. For inspiration on inline documentation, please check out [PyTorch](http://pytorch.org/docs/master/tensors.html)'s documentation for this operator.

Discussion:

Patch:
diff --git a/syft/tensor.py b/syft/tensor.py
index 9dc2bab3ba..c01a50dd83 100644
--- a/syft/tensor.py
+++ b/syft/tensor.py
@@ -109,6 +109,30 @@ class TensorBase(object):
         self.data = _ensure_ndarray(arr_like)
         self.encrypted = encrypted
 
+    def new(self, *args, **kwargs):
+        """Constructs a new tensor instance of the same data type.
+
+        Parameters
+        ----------
+        *args
+            Variable length argument list used to instantiate
+            new TensorBase object.
+        **kwargs
+            Arbitrary keyword arguments used to instantiate
+            new TensorBase object.
+
+        Returns
+        -------
+        TensorBase class instance if parent TensorBase
+        has self.encrypted = False, otherwise return NotImplemented
+        error.
+
+        """
+        if self.encrypted:
+            return NotImplemented
+
+        return self.__class__(*args, **kwargs)
+
     def _calc_mul_depth(self, tensor1, tensor2):
         if isinstance(tensor1, TensorBase) and isinstance(tensor2, TensorBase):
             self._mul_depth = max(tensor1._mul_depth, tensor2._mul_depth) + 1


Test patch:
diff --git a/tests/test_tensor.py b/tests/test_tensor.py
index d063417e29..32614ce752 100644
--- a/tests/test_tensor.py
+++ b/tests/test_tensor.py
@@ -1119,6 +1119,29 @@ class mm_test(unittest.TestCase):
         self.assertTrue(np.alltrue(out.data == [[5, 8, 11], [8, 13, 18], [11, 18, 25]]))
 
 
+class newTensorTests(unittest.TestCase):
+    def test_encrypted_error(self):
+
+        t1 = TensorBase(np.array([1, 1, 1]), encrypted=True)
+        t2 = t1.new([1, 1, 2], encrypted=True)
+
+        self.assertEqual(t2, NotImplemented)
+
+    def test_return_new_float_tensor(self):
+
+        t1 = TensorBase(np.array([1, 1, 1]))
+        t2 = t1.new(np.array([1., 1., 2.]))
+
+        self.assertTrue(t2.data.dtype == np.float64)
+
+    def test_return_new_int_tensor(self):
+
+        t1 = TensorBase(np.array([1, 1, 1]))
+        t2 = t1.new(np.array([1, 1, 2]))
+
+        self.assertTrue(t2.data.dtype == np.int64)
+
+
 class half(unittest.TestCase):
     def half_test_1(self):
         t1 = TensorBase(np.array([2, 3, 4]))