Repository: OpenMined/PySyft

Issue:
Implement Default frac 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, frac() should return a new tensor and frac_() should perform the operation inline. 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
@@ -410,6 +410,34 @@ def baddbmm_(self, tensor2, mat, beta=1, alpha=1):
             self.data += (mat.data * beta)
             return self
 
+    def exp(self):
+        """Computes the exponential of each element in tensor."""
+        if self.encrypted:
+            return NotImplemented
+        out = np.exp(self.data)
+        return TensorBase(out)
+
+    def exp_(self):
+        """Computes the exponential of each element inplace."""
+        if self.encrypted:
+            return NotImplemented
+        self.data = np.exp(self.data)
+        return self
+
+    def frac(self):
+        """"Computes the fractional portion of each element in tensor."""
+        if self.encrypted:
+            return NotImplemented
+        out = np.modf(self.data)[0]
+        return TensorBase(out)
+
+    def frac_(self):
+        """"Computes the fractional portion of each element inplace."""
+        if self.encrypted:
+            return NotImplemented
+        self.data = np.modf(self.data)[0]
+        return self
+
     def sigmoid_(self):
         """
             Performs inline sigmoid function on the Tensor elementwise


Test patch:
diff --git a/tests/test_tensor.py b/tests/test_tensor.py
--- a/tests/test_tensor.py
+++ b/tests/test_tensor.py
@@ -289,3 +289,31 @@ def testbaddbmm_(self):
         t1.baddbmm_(t2, mat, beta=2, alpha=2)
         self.assertTrue(np.array_equal(t1.data, [[[62, 92], [96, 142]],
                                                  [[122, 184], [28, 42]]]))
+
+
+class expTests(unittest.TestCase):
+    def testexp(self):
+        t3 = TensorBase(np.array([[[1, 3], [3, 5]],
+                                 [[5, 7], [9, 1]]]))
+        out = t3.exp()
+        self.assertTrue(np.allclose(out.data, [[[2.71828183e+00, 2.00855369e+01], [2.00855369e+01, 1.48413159e+02]],
+                                               [[1.48413159e+02, 1.09663316e+03], [8.10308393e+03, 2.71828183e+00]]]))
+
+    def testexp_(self):
+        t3 = TensorBase(np.array([[[1, 3], [3, 5]],
+                                 [[5, 7], [9, 1]]]))
+        t3.exp_()
+        self.assertTrue(np.allclose(t3.data, [[[2.71828183e+00, 2.00855369e+01], [2.00855369e+01, 1.48413159e+02]],
+                                              [[1.48413159e+02, 1.09663316e+03], [8.10308393e+03, 2.71828183e+00]]]))
+
+
+class fracTests(unittest.TestCase):
+    def testfrac(self):
+        t3 = TensorBase(np.array([1.23, 4.56, 7.89]))
+        out = t3.frac()
+        self.assertTrue(np.allclose(out.data, [0.23, 0.56, 0.89]))
+
+    def testfrac_(self):
+        t3 = TensorBase(np.array([1.23, 4.56, 7.89]))
+        t3.frac_()
+        self.assertTrue(np.allclose(t3.data, [0.23, 0.56, 0.89]))