Repository: OpenMined/PySyft

Issue:
Torch 1.1 Integration
So PyTorch just released torch 1.1 and it causes a lot of failing Unit Tests because of some breaking API updates. We need to address them.


Discussion:

Resolving patch:
diff --git a/syft/frameworks/torch/hook/hook.py b/syft/frameworks/torch/hook/hook.py
--- a/syft/frameworks/torch/hook/hook.py
+++ b/syft/frameworks/torch/hook/hook.py
@@ -365,7 +365,8 @@ def data(self, new_data):
                 if hasattr(self, "child"):
                     del self.child
 
-                self.native_param_data.set_(new_data)  # .wrap()
+                with torch.no_grad():
+                    self.set_(new_data)
             return self
 
         torch.nn.Parameter.data = data
@@ -408,7 +409,8 @@ def grad(self, new_grad):
                 self.child.grad = new_grad  # .wrap()
             else:
                 if self.native_param_grad is not None:
-                    self.native_param_grad.set_(new_grad)  # .wrap()
+                    with torch.no_grad():
+                        self.native_param_grad = new_grad
                 elif new_grad is not None:
                     self.native_param_grad = new_grad
             return self
diff --git a/syft/frameworks/torch/hook/hook_args.py b/syft/frameworks/torch/hook/hook_args.py
--- a/syft/frameworks/torch/hook/hook_args.py
+++ b/syft/frameworks/torch/hook/hook_args.py
@@ -23,6 +23,8 @@
 hook_method_response_functions = {}
 get_tensor_type_functions = {}
 
+base_types = {int, float, str, bool, bytes, bytearray, complex}
+
 one = lambda _args: 1
 
 # dict to specify the action depending of the type found
@@ -274,9 +276,17 @@ def build_rule(args):
     """
 
     type_args = type(args)
+    # for list, tuple but also tensors and syft tensors
     if type_args in type_rule:
         return type_rule[type_args](args)
+    # for int, float, str, etc
+    elif type_args in base_types:
+        return 0
     else:
+        # New kind of return with pytorch 1.1
+        if "torch.return_types" in str(type_args):
+            return type_rule[tuple](args)
+        # Still remain ellipsis, slices, etc.
         return 0
 
 
diff --git a/syft/frameworks/torch/tensors/interpreters/native.py b/syft/frameworks/torch/tensors/interpreters/native.py
--- a/syft/frameworks/torch/tensors/interpreters/native.py
+++ b/syft/frameworks/torch/tensors/interpreters/native.py
@@ -286,13 +286,15 @@ def send(
 
             if self._is_parameter():
                 if inplace:
-                    self.data.set_()
+                    with torch.no_grad():
+                        self.set_()
                     self.data = ptr
                     output = self
                 else:
                     wrapper = torch.Tensor()
                     param_wrapper = torch.nn.Parameter(wrapper)
-                    param_wrapper.data.set_()
+                    with torch.no_grad():
+                        param_wrapper.set_()
                     param_wrapper.data = ptr
                     output = param_wrapper
             else:


Test patch:
diff --git a/test/conftest.py b/test/conftest.py
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -32,7 +32,9 @@ def hook():
 def workers(hook):
     # To run a plan locally the local worker can't be a client worker,
     # since it needs to register objects
-    hook.local_worker.is_client_worker = False
+    # LaRiffle edit: doing this increases the reference count on pointers and
+    # breaks the auto garbage collection for pointer of pointers, see #2150
+    # hook.local_worker.is_client_worker = False
 
     # reset the hook and the local worker
     syft.local_worker.clear_objects()
diff --git a/test/torch/tensors/test_gc.py b/test/torch/tensors/test_gc.py
--- a/test/torch/tensors/test_gc.py
+++ b/test/torch/tensors/test_gc.py
@@ -11,14 +11,16 @@
 def test_explicit_garbage_collect_pointer(workers):
     """Tests whether deleting a PointerTensor garbage collects the remote object too"""
 
+    alice, bob = workers["alice"], workers["bob"]
+
     # create tensor
     x = torch.Tensor([1, 2])
 
     # send tensor to bob
-    x_ptr = x.send(workers["bob"])
+    x_ptr = x.send(bob)
 
     # ensure bob has tensor
-    assert x.id in workers["bob"]._objects
+    assert x.id in bob._objects
 
     # delete pointer to tensor, which should
     # automatically garbage collect the remote
@@ -26,29 +28,31 @@ def test_explicit_garbage_collect_pointer(workers):
     del x_ptr
 
     # ensure bob's object was garbage collected
-    assert x.id not in workers["bob"]._objects
+    assert x.id not in bob._objects
 
 
 def test_explicit_garbage_collect_double_pointer(workers):
     """Tests whether deleting a pointer to a pointer garbage collects
     the remote object too"""
 
+    alice, bob = workers["alice"], workers["bob"]
+
     # create tensor
     x = torch.Tensor([1, 2])
 
     # send tensor to bob and then pointer to alice
-    x_ptr = x.send(workers["bob"])
-    x_ptr_ptr = x_ptr.send(workers["alice"])
+    x_ptr = x.send(bob)
+    x_ptr_ptr = x_ptr.send(alice)
 
     # ensure bob has tensor
-    assert x.id in workers["bob"]._objects
+    assert x.id in bob._objects
 
     # delete pointer to pointer to tensor, which should automatically
     # garbage collect the remote object on Bob's machine
     del x_ptr_ptr
 
     # ensure bob's object was garbage collected
-    assert x.id not in workers["bob"]._objects
+    assert x.id not in bob._objects
     # TODO: shouldn't we check that alice's object was
     # garbage collected as well?
     # assert x.id not in workers["alice"]._objects
@@ -57,13 +61,13 @@ def test_explicit_garbage_collect_double_pointer(workers):
     x = torch.Tensor([1, 2])
     x_id = x.id
     # send tensor to bob and then pointer to alice
-    x = x.send(workers["bob"]).send(workers["alice"])
+    x = x.send(bob).send(alice)
     # ensure bob has tensor
-    assert x_id in workers["bob"]._objects
+    assert x_id in bob._objects
     # delete pointer to pointer to tensor
     del x
     # ensure bob's object was garbage collected
-    assert x_id not in workers["bob"]._objects
+    assert x_id not in bob._objects
     # TODO: shouldn't we check that alice's object was
     # garbage collected as well?
     # assert x.id not in workers["alice"]._objects
@@ -72,14 +76,16 @@ def test_explicit_garbage_collect_double_pointer(workers):
 def test_implicit_garbage_collection_pointer(workers):
     """Tests whether GCing a PointerTensor GCs the remote object too."""
 
+    alice, bob = workers["alice"], workers["bob"]
+
     # create tensor
     x = torch.Tensor([1, 2])
 
     # send tensor to bob
-    x_ptr = x.send(workers["bob"])
+    x_ptr = x.send(bob)
 
     # ensure bob has tensor
-    assert x.id in workers["bob"]._objects
+    assert x.id in bob._objects
 
     # delete pointer to tensor, which should
     # automatically garbage collect the remote
@@ -87,47 +93,49 @@ def test_implicit_garbage_collection_pointer(workers):
     x_ptr = "asdf"
 
     # ensure bob's object was garbage collected
-    assert x.id not in workers["bob"]._objects
+    assert x.id not in bob._objects
 
 
 def test_implicit_garbage_collect_double_pointer(workers):
     """Tests whether GCing a pointer to a pointer garbage collects
     the remote object too"""
 
+    alice, bob = workers["alice"], workers["bob"]
+
     # create tensor
     x = torch.Tensor([1, 2])
 
     # send tensor to bob and then pointer to alice
-    x_ptr = x.send(workers["bob"])
-    x_ptr_ptr = x_ptr.send(workers["alice"])
+    x_ptr = x.send(bob)
+    x_ptr_ptr = x_ptr.send(alice)
 
     # ensure bob has tensor
-    assert x.id in workers["bob"]._objects
+    assert x.id in bob._objects
 
     # delete pointer to pointer to tensor, which should automatically
     # garbage collect the remote object on Bob's machine
     x_ptr_ptr = "asdf"
 
     # ensure bob's object was garbage collected
-    assert x.id not in workers["bob"]._objects
+    assert x.id not in bob._objects
     # TODO: shouldn't we check that alice's object was
     # garbage collected as well?
-    # assert x.id not in workers["alice"]._objects
+    # assert x.id not in alice._objects
 
     # Chained version
     x = torch.Tensor([1, 2])
     x_id = x.id
     # send tensor to bob and then pointer to alice
-    x = x.send(workers["bob"]).send(workers["alice"])
+    x = x.send(bob).send(alice)
     # ensure bob has tensor
-    assert x_id in workers["bob"]._objects
+    assert x_id in bob._objects
     # delete pointer to pointer to tensor
     x = "asdf"
     # ensure bob's object was garbage collected
-    assert x_id not in workers["bob"]._objects
+    assert x_id not in bob._objects
     # TODO: shouldn't we check that alice's object was
     # garbage collected as well?
-    # assert x.id not in workers["alice"]._objects
+    # assert x.id not in alice._objects
 
 
 # TESTING IN PLACE METHODS
@@ -135,6 +143,7 @@ def test_implicit_garbage_collect_double_pointer(workers):
 
 def test_inplace_method_on_pointer(workers):
     bob = workers["bob"]
+
     tensor = torch.tensor([[1.0, 2], [4.0, 2]])
     pointer = tensor.send(bob)
     pointer.add_(pointer)
@@ -150,16 +159,18 @@ def test_explicit_garbage_collect_logging_on_pointer(workers):
     Tests whether deleting a LoggingTensor on a PointerTensor
     garbage collects the remote object too
     """
+    alice, bob = workers["alice"], workers["bob"]
+
     x = torch.Tensor([1, 2])
     x_id = x.id
 
-    x = x.send(workers["bob"])
+    x = x.send(bob)
     x = LoggingTensor().on(x)
-    assert x_id in workers["bob"]._objects
+    assert x_id in bob._objects
 
     del x
 
-    assert x_id not in workers["bob"]._objects
+    assert x_id not in bob._objects
 
 
 def test_implicit_garbage_collect_logging_on_pointer(workers):
@@ -167,13 +178,15 @@ def test_implicit_garbage_collect_logging_on_pointer(workers):
     Tests whether GCing a LoggingTensor on a PointerTensor
     garbage collects the remote object too
     """
+    alice, bob = workers["alice"], workers["bob"]
+
     x = torch.Tensor([1, 2])
     x_id = x.id
 
-    x = x.send(workers["bob"])
+    x = x.send(bob)
     x = LoggingTensor().on(x)
-    assert x_id in workers["bob"]._objects
+    assert x_id in bob._objects
 
     x = "open-source"
 
-    assert x_id not in workers["bob"]._objects
+    assert x_id not in bob._objects