Repository: OpenMined/PySyft

Issue:
Implement Default sum 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, sum() 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:

Resolving patch:
diff --git a/syft/tensor.py b/syft/tensor.py
--- a/syft/tensor.py
+++ b/syft/tensor.py
@@ -8,7 +8,7 @@ def _ensure_ndarray(arr):
 
 class TensorBase(object):
     """
-    A base tensor class that perform basic element-wise operation such as 
+    A base tensor class that perform basic element-wise operation such as
     addition, subtraction, multiplication and division
     """
 
@@ -83,3 +83,20 @@ def __itruediv__(self, arr_like):
         arr_like = _ensure_ndarray(arr_like)
         self.data = self.data / arr_like
         return self.data
+
+    def shape(self):
+        """Returns a tuple of input array dimensions."""
+        if self.encrypted:
+            return NotImplemented
+
+        return self.data.shape
+
+    def sum(self, dim=None):
+        """Returns the sum of all elements in the input array."""
+        if self.encrypted:
+            return NotImplemented
+
+        if dim is None:
+            return self.data.sum()
+        else:
+            return self.data.sum(axis=dim)


Test patch:
diff --git a/tests/test_tensor.py b/tests/test_tensor.py
--- a/tests/test_tensor.py
+++ b/tests/test_tensor.py
@@ -7,7 +7,7 @@ class AddTests(unittest.TestCase):
     def testSimple(self):
         t = TensorBase(np.array([1,2,3]))
         self.assertTrue(np.array_equal(t + np.array([1,2,3]), [2,4,6]))
-    
+
     def testInplace(self):
         t = TensorBase(np.array([1,2,3]))
         t += np.array([1,2,3])
@@ -21,7 +21,7 @@ class SubTests(unittest.TestCase):
     def testSimple(self):
         t = TensorBase(np.array([1,2,3]))
         self.assertTrue(np.array_equal(t - np.array([1,2,3]), [0,0,0]))
-    
+
     def testInplace(self):
         t = TensorBase(np.array([1,2,3]))
         t -= np.array([1,2,3])
@@ -58,9 +58,23 @@ def testInplace(self):
     def testScalar(self):
         t = TensorBase(np.array([2,4,6]))
         self.assertTrue(np.array_equal(t / 2, [1, 2, 3]))
-    
+
+class ShapeTests(unittest.TestCase):
+    def testShape(self):
+        t = TensorBase(np.array([[0, 1], [0, 5]]))
+        self.assertTrue(np.array_equal(t.shape(), (2, 2)))
+
+class SumTests(unittest.TestCase):
+    def testDimNoneInt(self):
+        t = TensorBase(np.array([1,2,3]))
+        self.assertTrue(np.array_equal(t.sum(), 6))
+
+    def testDimIsNotNoneInt(self):
+        t = TensorBase(np.array([[0, 1], [0, 5]]))
+        self.assertTrue(np.array_equal(t.sum(dim=1), [1, 5]))
+
 def main():
     unittest.main()
 
 if __name__ == '__main__':
-    main()
\ No newline at end of file
+    main()