Repository: OpenMined/PySyft (python)

Issue:
Implement Default cumprod Functionality for Base Tensor Type
**User Story A:** As a Data Scientist using Syft's Base Tensor type, we want to implement a default method for computing operations on a Tensor of arbitrary type. For this ticket to be complete, cumprod() 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.

Pull request description:
cumprod added to TensorBase class
Closes #34

Patch:
diff --git a/syft/tensor.py b/syft/tensor.py
index c1aa0e310e..859dd0ff4f 100644
--- a/syft/tensor.py
+++ b/syft/tensor.py
@@ -858,6 +858,19 @@ class TensorBase(object):
         else:
             return self.data.size
 
+    def cumprod(self, dim=0):
+        """Returns the cumulative product of elements in the dimension dim."""
+        if self.encrypted:
+            return NotImplemented
+        return syft.math.cumprod(self, dim)
+
+    def cumprod_(self, dim=0):
+        """calculate in-place the cumulative product of elements in the dimension dim."""
+        if self.encrypted:
+            return NotImplemented
+        self.data = syft.math.cumprod(self, dim).data
+        return self
+
     def split(self, split_size, dim=0):
         """Returns tuple of tensors of equally sized tensor/chunks (if possible)"""
         if self.encrypted:


Test patch:
diff --git a/tests/test_tensor.py b/tests/test_tensor.py
index fcded4a250..edd6a9d92c 100644
--- a/tests/test_tensor.py
+++ b/tests/test_tensor.py
@@ -647,6 +647,23 @@ class nonzeroTests(unittest.TestCase):
         self.assertTrue(np.array_equal(t2.data, np.array([[0, 1, 1], [0, 1, 2]])))
 
 
+class cumprodTest(unittest.TestCase):
+    def testCumprod(self):
+        t1 = TensorBase(np.array([[1, 2, 3], [4, 5, 6]]))
+        t2 = TensorBase(np.array([[1.0, 2.0, 3.0], [4.0, 10.0, 18.0]]))
+        t3 = TensorBase(np.array([[1, 2, 6], [4, 20, 120]]))
+        self.assertTrue(np.equal(t1.cumprod(dim=0), t2).all())
+        self.assertTrue(np.equal(t1.cumprod(dim=1), t3).all())
+
+    def testCumprod_(self):
+        t1 = TensorBase(np.array([[1, 2, 3], [4, 5, 6]]))
+        t2 = TensorBase(np.array([[1.0, 2.0, 3.0], [4.0, 10.0, 18.0]]))
+        t3 = TensorBase(np.array([[1, 2, 6], [4, 20, 120]]))
+        self.assertTrue(np.equal(t1.cumprod_(dim=0), t2).all())
+        t1 = TensorBase(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]))
+        self.assertTrue(np.equal(t1.cumprod_(dim=1), t3).all())
+
+
 class splitTests(unittest.TestCase):
     def testSplit(self):
         t1 = TensorBase(np.arange(8.0))