Repository: OpenMined/PySyft

Issue:
AttributeError: 'int' object has no attribute 'is_wrapper'
**Describe the bug**
Execute Part 11 - Secure Deep Learning Classification.ipynb ecounter "AttributeError: 'int' object has no attribute 'is_wrapper'"

**To Reproduce**
Steps to reproduce the behavior:
1. Go to 'examples/tutorials/Part 11 - Secure Deep Learning Classification.ipynb'
2. Click on 'run all'
3. Scroll down to 'cell 17 test(args, model, private_test_loader)'
4. See error 

**Expected behavior**
No error here:).
**Screenshots**
![1](https://user-images.githubusercontent.com/8589224/59334626-7fe0e280-8d2d-11e9-9c9b-e556e3c1d099.png)
**Desktop (please complete the following information):**
 On Google colaboratory



Discussion:
Hey, indeed there is a bug in our last release 0.1.18 when operating with constant values, I'm working on a fix right now.

Resolving patch:
diff --git a/syft/frameworks/torch/tensors/interpreters/precision.py b/syft/frameworks/torch/tensors/interpreters/precision.py
--- a/syft/frameworks/torch/tensors/interpreters/precision.py
+++ b/syft/frameworks/torch/tensors/interpreters/precision.py
@@ -96,6 +96,10 @@ def truncate(self, precision_fractional):
     def add(self, _self, other):
         """Add two fixed precision tensors together.
         """
+        if isinstance(other, int):
+            scaled_int = other * self.base ** self.precision_fractional
+            return getattr(_self, "add")(scaled_int)
+
         if _self.is_wrapper and not other.is_wrapper:
             # If we try to add a FPT>(wrap)>AST and a FPT>torch.tensor,
             # we want to perform AST + torch.tensor
@@ -125,6 +129,10 @@ def __iadd__(self, other):
     def sub(self, _self, other):
         """Subtracts a fixed precision tensor from another one.
         """
+        if isinstance(other, int):
+            scaled_int = other * self.base ** self.precision_fractional
+            return getattr(_self, "sub")(scaled_int)
+
         if _self.is_wrapper and not other.is_wrapper:
             # If we try to subtract a FPT>(wrap)>AST and a FPT>torch.tensor,
             # we want to perform AST - torch.tensor
@@ -166,7 +174,10 @@ def mul(self, other):
                 self.precision_fractional == other.precision_fractional
             ), "In mul, all args should have the same precision_fractional"
 
-        if self.child.is_wrapper and not other.child.is_wrapper:
+        if isinstance(other, int):
+            new_self = self.child
+            new_other = other * self.base ** self.precision_fractional
+        elif self.child.is_wrapper and not other.child.is_wrapper:
             # If we try to multiply a FPT>(wrap)>AST with a FPT>torch.tensor),
             # we want to perform AST * torch.tensor
             new_self = self.child


Test patch:
diff --git a/test/torch/tensors/test_additive_shared.py b/test/torch/tensors/test_additive_shared.py
--- a/test/torch/tensors/test_additive_shared.py
+++ b/test/torch/tensors/test_additive_shared.py
@@ -180,6 +180,24 @@ def test_mul(workers):
     assert (z == (t * t)).all()
 
 
+def test_operate_with_integer_constants(workers):
+    bob, alice, james = (workers["bob"], workers["alice"], workers["james"])
+    x = th.tensor([2.0])
+    x_sh = x.fix_precision().share(alice, bob, crypto_provider=james)
+
+    r_sh = x_sh + 10
+    assert r_sh.get().float_prec() == x + 10
+
+    r_sh = x_sh - 7
+    assert r_sh.get().float_prec() == x - 7
+
+    r_sh = x_sh * 2
+    assert r_sh.get().float_prec() == x * 2
+
+    r_sh = x_sh / 2
+    assert r_sh.get().float_prec() == x / 2
+
+
 def test_stack(workers):
     bob, alice, james = (workers["bob"], workers["alice"], workers["james"])
     t = torch.tensor([1.3, 2])
diff --git a/test/torch/tensors/test_precision.py b/test/torch/tensors/test_precision.py
--- a/test/torch/tensors/test_precision.py
+++ b/test/torch/tensors/test_precision.py
@@ -261,6 +261,25 @@ def test_torch_nn_functional_linear():
     assert (result == expected).all()
 
 
+def test_operate_with_integer_constants():
+    x = torch.tensor([1.0])
+    x_fp = x.fix_precision()
+
+    r_fp = x_fp + 10
+    r = r_fp.float_precision()
+    assert r == x + 10
+
+    r_fp = x_fp - 7
+    r = r_fp.float_precision()
+    assert r == x - 7
+
+    r_fp = x_fp * 2
+    assert r_fp.float_precision() == x * 2
+
+    r_fp = x_fp / 5
+    assert r_fp.float_precision() == x / 5
+
+
 def test_fixed_precision_and_sharing(workers):
 
     bob, alice = (workers["bob"], workers["alice"])