Repository: OpenMined/PySyft

Issue:
URGENT: remove all % operators and replace with torch.fmod()
@channel - BUG IN PYTORCH 0.3.1

The modulus operator doesn't always work.
https://github.com/pytorch/pytorch/issues/1164

torch.fmod() works correctly
torch.remainder() and the % sign do NOT work.

We need to swap out all uses of % for torch.fmod() pronto


Discussion:
I think I can take this. @iamtrask 
Thank you @ionlights !!
Note that % work fine if it's just between two python ints... it's only when it's computing on one or more torch tensors that it's an issue
Hey @ionlights - how's it goin?
:wave: Sorry, got caught up finishing up some assignments due an hour ago. :joy:

I should be able to finish this up towards late afternoon today (Oct 08) in EST.

Resolving patch:
diff --git a/syft/core/frameworks/torch/tensor.py b/syft/core/frameworks/torch/tensor.py
--- a/syft/core/frameworks/torch/tensor.py
+++ b/syft/core/frameworks/torch/tensor.py
@@ -1276,8 +1276,8 @@ def encode(self, rational):
 
     def decode(self):
         save = self.child.child * 1
-        self.child.child = None  # <-- This is doing magic things
-        value = self.child.long() % self.field
+        self.child.child = None # <-- This is doing magic things
+        value = torch.fmod(self.child.long(), self.field)
         if len(value.size()) == 0:
             # raise TypeError("Can't decode empty tensor")
             return None
@@ -1541,15 +1541,15 @@ def check_and_scale_precision_if_needed(self, other):
     def __add__(self, other):
 
         a, b = self.check_and_scale_precision_if_needed(other)
-        return (a + b) % self.field
+        return torch.fmod((a + b), self.field)
 
     def __sub__(self, other):
         a, b = self.check_and_scale_precision_if_needed(other)
-        return (a - b) % self.field
+        return torch.fmod((a - b), self.field)
 
     def __rsub__(self, other):
         a, b = self.check_and_scale_precision_if_needed(other)
-        return (b - a) % self.field
+        return torch.fmod((b - a), self.field)
 
     def __mul__(self, other):
         a, b = self.check_and_scale_precision_if_needed(other)
@@ -1559,95 +1559,88 @@ def __gt__(self, other):
 
         a, b = self.check_and_scale_precision_if_needed(other)
         result = (a > b).long() * self.base ** self.precision_fractional
-        result = sy._FixedPrecisionTensor(
-            result,
-            base=self.base,
-            field=self.field,
-            precision_fractional=self.precision_fractional,
-            precision_integral=self.precision_integral,
-            already_encoded=True,
-        ).wrap(True)
+
+        result = sy._FixedPrecisionTensor(result,
+                                          base=self.base,
+                                          field=self.field,
+                                          precision_fractional=self.precision_fractional,
+                                          precision_integral=self.precision_integral,
+                                          already_encoded=True).wrap(True)
+
         return result
 
     def __lt__(self, other):
 
         a, b = self.check_and_scale_precision_if_needed(other)
         result = (a < b).long() * self.base ** self.precision_fractional
-        result = sy._FixedPrecisionTensor(
-            result,
-            base=self.base,
-            field=self.field,
-            precision_fractional=self.precision_fractional,
-            precision_integral=self.precision_integral,
-            already_encoded=True,
-        ).wrap(True)
+
+        result = sy._FixedPrecisionTensor(result,
+                                          base=self.base,
+                                          field=self.field,
+                                          precision_fractional=self.precision_fractional,
+                                          precision_integral=self.precision_integral,
+                                          already_encoded=True).wrap(True)
+
         return result
 
     def __ge__(self, other):
 
         a, b = self.check_and_scale_precision_if_needed(other)
         result = (a >= b).long() * self.base ** self.precision_fractional
-        result = sy._FixedPrecisionTensor(
-            result,
-            base=self.base,
-            field=self.field,
-            precision_fractional=self.precision_fractional,
-            precision_integral=self.precision_integral,
-            already_encoded=True,
-        ).wrap(True)
+
+        result = sy._FixedPrecisionTensor(result,
+                                          base=self.base,
+                                          field=self.field,
+                                          precision_fractional=self.precision_fractional,
+                                          precision_integral=self.precision_integral,
+                                          already_encoded=True).wrap(True)
+
         return result
 
     def __le__(self, other):
 
         a, b = self.check_and_scale_precision_if_needed(other)
         result = (a <= b).long() * self.base ** self.precision_fractional
-        result = sy._FixedPrecisionTensor(
-            result,
-            base=self.base,
-            field=self.field,
-            precision_fractional=self.precision_fractional,
-            precision_integral=self.precision_integral,
-            already_encoded=True,
-        ).wrap(True)
+
+        result = sy._FixedPrecisionTensor(result,
+                                          base=self.base,
+                                          field=self.field,
+                                          precision_fractional=self.precision_fractional,
+                                          precision_integral=self.precision_integral,
+                                          already_encoded=True).wrap(True)
+
         return result
 
     def __eq__(self, other):
 
         a, b = self.check_and_scale_precision_if_needed(other)
         result = (a == b).long() * self.base ** self.precision_fractional
-        result = sy._FixedPrecisionTensor(
-            result,
-            base=self.base,
-            field=self.field,
-            precision_fractional=self.precision_fractional,
-            precision_integral=self.precision_integral,
-            already_encoded=True,
-        ).wrap(True)
+
+        result = sy._FixedPrecisionTensor(result,
+                                          base=self.base,
+                                          field=self.field,
+                                          precision_fractional=self.precision_fractional,
+                                          precision_integral=self.precision_integral,
+                                          already_encoded=True).wrap(True)
+
         return result
 
     def __div__(self, other):
         # if other is not a fixed tensor, convert it to a fixed one
-        if not hasattr(other, "precision_fractional"):
-            other = other.fix_precision(precision_fractional=self.precision_fractional)
 
-        if self.precision_fractional == other.precision_fractional:
-            gp_response = (
-                self.child * 10 ** self.precision_fractional / other.child
-            ) % self.field
-        elif self.precision_fractional > other.precision_fractional:
-            gp_response = (
-                self.child / other.child * 10 ** other.precision_fractional
-            ) % self.field
-
-        elif self.precision_fractional < other.precision_fractional:
-            gp_response = (
-                (
-                    self.child
-                    * 10 ** (2 * other.precision_fractional - self.precision_fractional)
-                )
-                / other.child
-            ) % self.field
-        return gp_response
+        if (not hasattr(other, 'precision_fractional')):
+            other = other.fix_precision(precision_fractional = self.precision_fractional)
+
+          if (self.precision_fractional > other.precision_fractional):
+            gp_response = (self.child / other.child * 10 ** other.precision_fractional)
+        elif (self.precision_fractional < other.precision_fractional):
+            gp_response = ((self.child *10 ** (2 * other.precision_fractional
+                           - self.precision_fractional)) / other.child)
+        else:
+            gp_response = (self.child * 10 ** self.precision_fractional / other.child)
+            
+        return torch.fmod(gp_response, self.field)
+
 
     # def __mul__(self, other):
     #     # if other is not a fixed tensor, convert it to a fixed one
@@ -1957,7 +1950,7 @@ def sum(self, *args, **kwargs):
         return gp_response
 
     def cumsum(self, *args, **kwargs):
-        gp_response = self.child.cumsum(*args, **kwargs) % spdz.field
+        gp_response = torch.fmod(self.child.cumsum(*args, **kwargs), spdz.field)
         return gp_response
 
     def __mul__(self, other):
@@ -2071,7 +2064,7 @@ def get(self, deregister_ptr=False):
             var.child = None
             if hasattr(self, "grad") and self.grad is not None:
                 var_grad = self.grad.shares.child.sum_get()
-                value = var_grad.data % spdz.field
+                value = torch.fmod(var_grad.data, spdz.field)
                 # TODO: Add this thing for negative values
                 # gate = (value > spdz.torch_max_value).long()
                 # neg_nums = (value - spdz.torch_field) * gate
@@ -2082,7 +2075,7 @@ def get(self, deregister_ptr=False):
                 var.assign_grad_(var_grad)
             return var
         # TODO: have deregister_ptr do something
-        value = self.shares.child.sum_get() % spdz.field
+        value = torch.fmod(self.shares.child.sum_get(), spdz.field)
 
         gate = (value > spdz.torch_max_value).long()
 
diff --git a/syft/spdz/spdz.py b/syft/spdz/spdz.py
--- a/syft/spdz/spdz.py
+++ b/syft/spdz/spdz.py
@@ -20,7 +20,7 @@
 
 def encode(rational, precision_fractional=PRECISION_FRACTIONAL, mod=field):
     upscaled = (rational * BASE ** precision_fractional).long()
-    field_element = upscaled % mod
+    field_element = torch.fmod(upscaled, mod)
     return field_element
 
 
@@ -83,10 +83,10 @@ def swap_shares(shares):
 
 
 def truncate(x, interface, amount=PRECISION_FRACTIONAL, mod=field):
-    print("truncating")
-    if interface.get_party() == 0:
-        return (x / BASE ** amount) % mod
-    return (mod - ((mod - x) / BASE ** amount)) % mod
+
+    if (interface.get_party() == 0):
+        return torch.fmod((x / BASE ** amount), mod)
+    return torch.fmod((mod - ((mod - x) / BASE ** amount)), mod)
 
 
 def public_add(x, y, interface):
@@ -98,11 +98,11 @@ def public_add(x, y, interface):
 
 def spdz_add(a, b, mod=field):
     c = a + b
-    return c % mod
+    return torch.fmod(c, mod)
 
 
 def spdz_neg(a, mod=field):
-    return (mod - a) % mod
+    return torch.fmod((mod - a), mod)
 
 
 def spdz_mul(x, y, workers, mod=field):
@@ -112,18 +112,22 @@ def spdz_mul(x, y, workers, mod=field):
     triple = generate_mul_triple_communication(shape, workers)
     a, b, c = triple
 
-    d = (x - a) % mod
-    e = (y - b) % mod
+    d = torch.fmod((x - a), mod)
+    e = torch.fmod((y - b), mod)
 
-    delta = d.child.sum_get() % mod
-    epsilon = e.child.sum_get() % mod
+    delta = torch.fmod(d.child.sum_get(), mod)
+    epsilon = torch.fmod(e.child.sum_get(), mod)
 
     epsilon_delta = epsilon * delta
 
     delta = delta.broadcast(workers)
     epsilon = epsilon.broadcast(workers)
 
-    z = (c + (delta * b) % mod + (epsilon * a) % mod) % mod
+    z = torch.fmod((c
+                    + torch.fmod((delta * b), mod)
+                    + torch.fmod((epsilon * a), mod)
+                    ), mod)
+
 
     z.child.public_add_(epsilon_delta)
 
@@ -142,21 +146,21 @@ def spdz_matmul(x, y, workers, mod=field):
     assert x_width == y_height, f"dimension mismatch: {x_width!r} != {y_height!r}"
     a, b, c = generate_matmul_triple_communication(shapes, workers)
 
-    r = (x - a) % mod
-    s = (y - b) % mod
+    r = torch.fmod((x - a), mod)
+    s = torch.fmod((y - b), mod)
 
     # Communication
-    rho = r.child.sum_get() % mod
-    sigma = s.child.sum_get() % mod
-    rho_sigma = torch.mm(rho, sigma) % mod
+    rho = torch.fmod(r.child.sum_get(), mod)
+    sigma = torch.fmod(s.child.sum_get(), mod)
+    rho_sigma = torch.fmod(torch.mm(rho, sigma), mod)
 
     rho = rho.broadcast(workers)
     sigma = sigma.broadcast(workers)
 
-    a_sigma = torch.mm(a, sigma) % mod
-    rho_b = torch.mm(rho, b) % mod
+    a_sigma = torch.fmod(torch.mm(a, sigma), mod)
+    rho_b = torch.fmod(torch.mm(rho, b), mod)
 
-    z = (a_sigma + rho_b + c) % mod
+    z = torch.fmod((a_sigma + rho_b + c), mod)
     z.child.public_add_(rho_sigma)
 
     return z


Test patch: