Repository: OpenMined/PySyft
Pull request #235 (2017-09-13 16:36:46)

Capsule integration changes


Patch:
diff --git a/syft/__init__.py b/syft/__init__.py
index 8dcafd66a1..5ef508a73a 100644
--- a/syft/__init__.py
+++ b/syft/__init__.py
@@ -5,7 +5,7 @@ from syft import test
 from syft.tensor import equal, TensorBase
 from syft.math import cumprod, cumsum, ceil, dot, matmul, addmm, addcmul
 from syft.math import addcdiv, addmv, addbmm, baddbmm, transpose
-from syft.math import unsqueeze
+from syft.math import unsqueeze, zeros, ones, rand, randn
 
 s = str(he)
 s += str(nn)
@@ -14,5 +14,5 @@ s += str(test)
 s += str(equal) + str(TensorBase) + str(cumprod) + str(cumsum) + str(ceil)
 s += str(dot) + str(matmul) + str(addmm) + str(addcmul) + str(addcdiv)
 s += str(addmv) + str(addbmm) + str(baddbmm)
-s += str(transpose)
+s += str(transpose) + str(rand) + str(randn) + str(ones) + str(zeros)
 s += str(unsqueeze)
diff --git a/syft/he/keys.py b/syft/he/keys.py
new file mode 100644
index 0000000000..34b1dd2156
--- /dev/null
+++ b/syft/he/keys.py
@@ -0,0 +1,5 @@
+import syft
+
+
+def Paillier(n_length=1024):
+    return syft.he.paillier.keys.KeyPair().generate(n_length)
diff --git a/syft/he/paillier/basic.py b/syft/he/paillier/basic.py
index 2edf67302a..24b3e0a2ba 100644
--- a/syft/he/paillier/basic.py
+++ b/syft/he/paillier/basic.py
@@ -1,4 +1,5 @@
 import numpy as np
+import pickle
 from ...tensor import TensorBase
 
 
@@ -63,7 +64,8 @@ class PaillierTensor(TensorBase):
             else:
                 return NotImplemented
         else:
-            return PaillierTensor(self.public_key, self.data * float(tensor), False)
+            op = self.data * float(tensor)
+            return PaillierTensor(self.public_key, op, False)
 
     def __truediv__(self, tensor):
         """Performs element-wise division between two tensors"""
@@ -76,7 +78,8 @@ class PaillierTensor(TensorBase):
             else:
                 return NotImplemented
         else:
-            return PaillierTensor(self.public_key, self.data * (1 / float(tensor)), False)
+            op = self.data * (1 / float(tensor))
+            return PaillierTensor(self.public_key, op, False)
 
     def sum(self, dim=None):
         """Returns the sum of all elements in the input array."""
@@ -84,15 +87,22 @@ class PaillierTensor(TensorBase):
             return NotImplemented
 
         if dim is None:
-            return self.data.sum()
+            return PaillierTensor(self.public_key, self.data.sum(), False)
         else:
-            return self.data.sum(axis=dim)
+            op = self.data.sum(axis=dim)
+            return PaillierTensor(self.public_key, op, False)
+
+    def dot(self, plaintext_x):
+        if(not plaintext_x.encrypted):
+            return (self * plaintext_x).sum(plaintext_x.dim() - 1)
+        else:
+            return NotImplemented
 
     def __str__(self):
-        return str(self.data)
+        return "PaillierTensor: " + str(self.data)
 
     def __repr__(self):
-        return repr(self.data)
+        return "PaillierTensor: " + repr(self.data)
 
 
 class Float():
@@ -161,3 +171,9 @@ class Float():
         """This is kindof a boring/uninformative __str__"""
 
         return 'e'
+
+    def serialize(self):
+        return pickle.dumps(self)
+
+    def deserialize(b):
+        return pickle.loads(b)
diff --git a/syft/he/paillier/keys.py b/syft/he/paillier/keys.py
index 5ea670a75b..15f81d217c 100644
--- a/syft/he/paillier/keys.py
+++ b/syft/he/paillier/keys.py
@@ -1,6 +1,7 @@
 import phe as paillier
 import numpy as np
 import pickle
+import syft
 from .basic import Float, PaillierTensor
 from ...tensor import TensorBase
 from ..abstract.keys import AbstractSecretKey, AbstractPublicKey
@@ -36,12 +37,33 @@ class SecretKey(AbstractSecretKey):
     def serialize(self):
         return pickle.dumps(self.sk)
 
+    def deserialize(b):
+        return SecretKey(pickle.loads(b))
+
 
 class PublicKey(AbstractPublicKey):
 
     def __init__(self, pk):
         self.pk = pk
 
+    def zeros(self, dim):
+        """Returns an encrypted tensor of zeros"""
+        return syft.zeros(dim).encrypt(self)
+
+    def ones(self, dim):
+        """Returns an encrypted tensor of ones"""
+        return syft.ones(dim).encrypt(self)
+
+    def rand(self, dim):
+        """Returns an encrypted tensor with initial numbers sampled from a
+        uniform distribution from 0 to 1."""
+        return syft.rand(dim).encrypt(self)
+
+    def randn(self, dim):
+        """Returns an encrypted tensor with initial numbers sampled from a
+        standard normal distribution"""
+        return syft.randn(dim).encrypt(self)
+
     def encrypt(self, x, same_type=False):
         """Encrypts x. X can be either an encrypted int or a numpy
         vector/matrix/tensor."""
@@ -72,6 +94,9 @@ class PublicKey(AbstractPublicKey):
     def serialize(self):
         return pickle.dumps(self.pk)
 
+    def deserialize(b):
+        return PublicKey(pickle.loads(b))
+
 
 class KeyPair(AbstractKeyPair):
 
diff --git a/syft/math.py b/syft/math.py
index 482841230f..b239827f47 100644
--- a/syft/math.py
+++ b/syft/math.py
@@ -8,10 +8,33 @@ from .tensor import _ensure_tensorbase
 
 __all__ = [
     'cumprod', 'cumsum', 'ceil', 'dot', 'floor', 'matmul', 'addmm', 'addcmul',
-    'addcdiv', 'addmv', 'addbmm', 'baddbmm', 'sigmoid', 'unsqueeze', 'tanh'
+    'addcdiv', 'addmv', 'addbmm', 'baddbmm', 'sigmoid', 'unsqueeze', 'tanh',
+    'zeros', 'ones', 'rand', 'randn'
 ]
 
 
+def zeros(dim):
+    """Returns a tensor of zeros"""
+    return TensorBase(np.zeros(dim))
+
+
+def ones(dim):
+    """Returns a tensor of ones"""
+    return TensorBase(np.ones(dim))
+
+
+def rand(dim):
+    """Returns a tensor with numbers initialized according to a uniform
+    distribution from 0 to 1"""
+    return TensorBase(np.random.rand(dim))
+
+
+def randn(dim):
+    """Returns a tensor with initial numbers sampled from a standard normal
+    distribution"""
+    return TensorBase(np.random.randn(dim))
+
+
 def dot(tensor1, tensor2):
     """Returns inner product of two tensors.
 
diff --git a/syft/tensor.py b/syft/tensor.py
index 6f010dd1fa..0aed1c3ef8 100644
--- a/syft/tensor.py
+++ b/syft/tensor.py
@@ -2,6 +2,7 @@
 import numpy as np
 import syft
 import scipy
+import pickle
 
 __all__ = [
     'equal', 'TensorBase',
@@ -60,7 +61,11 @@ class TensorBase(object):
         if self.encrypted:
             return NotImplemented
         else:
-            return pubkey.encrypt(self)
+            if(type(pubkey) == syft.he.paillier.keys.PublicKey):
+                out = syft.he.paillier.PaillierTensor(pubkey, self.data)
+                return out
+            else:
+                return NotImplemented
 
     def decrypt(self, seckey):
         """Decrypts the tensor using a Secret Key"""
@@ -118,6 +123,9 @@ class TensorBase(object):
         if self.encrypted:
             return NotImplemented
 
+        if tensor.encrypted:
+            return tensor.dot(self)
+
         return syft.dot(self, tensor)
 
     def __matmul__(self, tensor):
@@ -581,10 +589,10 @@ class TensorBase(object):
         return self
 
     def __str__(self):
-        return str(self.data)
+        return "BaseTensor: " + str(self.data)
 
     def __repr__(self):
-        return repr(self.data)
+        return "BaseTensor: " + repr(self.data)
 
     def rsqrt(self):
         """Returns reciprocal of square root of Tensor element wise"""
@@ -1025,3 +1033,9 @@ class TensorBase(object):
             return NotImplemented
         hist, edges = np.histogram(np.array(self.data), bins=bins, range=(min, max))
         return TensorBase(hist)
+
+    def serialize(self):
+        return pickle.dumps(self)
+
+    def deserialize(b):
+        return pickle.loads(b)


Test patch:
diff --git a/tests/test_math.py b/tests/test_math.py
index ec0b82be4f..1d66568765 100644
--- a/tests/test_math.py
+++ b/tests/test_math.py
@@ -6,6 +6,21 @@ import syft
 from syft import TensorBase
 
 
+class ConvenienceTests(unittest.TestCase):
+    def testZeros(self):
+        self.assertTrue((syft.zeros(5).data == np.zeros(5)).all())
+
+    def testOnes(self):
+        self.assertTrue((syft.ones(5).data == np.ones(5)).all())
+
+    def testRand(self):
+        np.random.seed(0)
+        x = syft.rand(5).data
+        np.random.seed(0)
+        y = np.random.rand(5)
+        self.assertTrue((x == y).all())
+
+
 class DotTests(unittest.TestCase):
     def testDotInt(self):
         t1 = TensorBase(np.array([1, 2, 3]))
diff --git a/tests/test_paillier.py b/tests/test_paillier.py
index f8ce2cb37e..d2ace2e2f0 100644
--- a/tests/test_paillier.py
+++ b/tests/test_paillier.py
@@ -1,7 +1,9 @@
 from syft.he.paillier import KeyPair, PaillierTensor
+from syft.he.keys import Paillier
 from syft import TensorBase
 import unittest
 import numpy as np
+import syft as sy
 
 
 # Here's our "unit tests".
@@ -12,6 +14,21 @@ class DimTests(unittest.TestCase):
         self.assertTrue(x.dim() == 1)
 
 
+class DotTests(unittest.TestCase):
+    def testDotProduct(self):
+
+        pk, sk = Paillier()
+
+        x = pk.ones(10)
+        y = sy.ones(10)
+
+        out1 = y.dot(x).decrypt(sk)
+        out2 = x.dot(y).decrypt(sk)
+
+        self.assertEqual(out1, 10)
+        self.assertEqual(out2, 10)
+
+
 class AddTests(unittest.TestCase):
 
     def testSimple(self):