Repository: OpenMined/PySyft

Issue:
SVD is returning 4 pointers instead of 3
## Description
When sending a tensor to a worker and performing SVD, returns four pointers instead of three. Also, the third one is not gettable. By experimentation, I have had to solve the issue using `U, s, _, V = x.svd()`.

## How to Reproduce
```python
import torch
import syft as sy

hook = sy.TorchHook(torch)
bob = sy.VirtualWorker(hook, id='bob')

x = torch.rand(250, 84).send(bob)  # Synthetic tensor
x.svd()

# Output:
# ((Wrapper)>[PointerTensor | me:88822589827 -> bob:10423896311],
#  (Wrapper)>[PointerTensor | me:22528885369 -> bob:34285527022],
#  (Wrapper)>[PointerTensor | me:46709676193 -> bob:67244907535],
#  (Wrapper)>[PointerTensor | me:235847656 -> bob:15738446586])
```

## Expected Behavior
Should return **three** pointers: `U, s, V = x.svd()`

## System Information
 - Official released Docker container
 - Same for pip package:
   - Ubuntu 18.04.5 LTS (Bionic Beaver)
   - Python 3.6.9


Discussion:
this is pretty weird I run this code in debug mode, stepped into every function, the things seemed correct while computing the response (3 tensors were returned),  and in the end got this error : (which shows things were correct 3 tensor returned as expected)
```
Exception has occurred: ValueError
not enough values to unpack (expected 4, got 3)
  File "/home/nilansh/Anton/OpenMined/PySyft/debug.py", line 8, in <module>
    U, s, _, V = x.svd()

```

but returns 4 values when run as normal python code.

@LaRiffle  
want to work on it.

Resolving patch:
diff --git a/syft/generic/object_storage.py b/syft/generic/object_storage.py
--- a/syft/generic/object_storage.py
+++ b/syft/generic/object_storage.py
@@ -6,6 +6,7 @@
 from syft.generic.frameworks.types import FrameworkTensorType
 from syft.generic.abstract.tensor import AbstractTensor
 from syft.workers.abstract import AbstractWorker
+import torch
 
 
 class ObjectStore:
@@ -62,7 +63,8 @@ def de_register_obj(self, obj: object, _recurse_torch_objs: bool = True):
                 more complex and needs to be explored. Is not supported at the
                 moment.
         """
-        if hasattr(obj, "id"):
+        has_id = hasattr(obj, "_id") if isinstance(obj, torch.Tensor) else hasattr(obj, "id")
+        if has_id:
             self.rm_obj(obj.id)
         if hasattr(obj, "_owner"):
             del obj._owner


Test patch:
diff --git a/test/generic/pointers/test_pointer_tensor.py b/test/generic/pointers/test_pointer_tensor.py
--- a/test/generic/pointers/test_pointer_tensor.py
+++ b/test/generic/pointers/test_pointer_tensor.py
@@ -398,6 +398,18 @@ def test_remote_T(workers):
     assert (bob_xT.get() == x.T).all()
 
 
+def test_remote_svd(workers):
+    """Test pointer.svd() functionality"""
+    bob = workers["bob"]
+    x = th.rand(5, 3)
+    local_u, local_s, local_v = x.svd()
+    bob_x = x.send(bob)
+    bob_u, bob_s, bob_v = bob_x.svd()
+    assert (local_u == bob_u.get()).all()
+    assert (local_s == bob_s.get()).all()
+    assert (local_v == bob_v.get()).all()
+
+
 def test_remote_function_with_multi_ouput(workers):
     """
     Functions like .split return several tensors, registration and response