# Buggy code
def _hook_get_(hook_self, torch_type):
    def get_(self, reduce=lambda x: x[0]):
        """
        Gets a Torch object from its current owners.

        Args:
        reduce: (EXPERIMENTAL) How to reduce tensors that come from
            multiple workers
        """
        # TODO: fully generalize this to multiple workers; consider
        #       adding arguments for other tensor ids, e.g. mapping workers
        #       to tensors, and a reduce function (for example, would allow
        #       for built-in gradient averaging when Variable.get is done)
        #       (low priority)
        try:
            assert len(self.owners) == 1
        except AssertionError:
            raise NotImplementedError(
                "Only able to get_ tensors belonging \
                                            to a single worker right now."
            )
        if hook_self.local_worker.id in self.owners:
            return self

        _out = hook_self.local_worker.request_obj(
            obj_id=self.id, recipient=self.owners[0]
        )
        x, request_obj_cleanup_method = _out

        hook_self.local_worker.register_object(hook_self.local_worker, x, id=x.id)

        # if self == tensor
        _id = hook_self.local_worker.id  # for brevity
        if type(self) != torch.autograd.variable.Variable:
            _os = self.old_set_(x.type(self.type()))
            self = hook_self.local_worker.register_object(
                hook_self.local_worker, _os, id=self.id, owners=[_id]
            )

        else:
            _os = self.old_set_(x.type(self.data.type()))  # for brevity
            self = hook_self.local_worker.register_object(
                hook_self.local_worker, _os, id=self.id, owners=[_id]
            )

            self.data = hook_self.local_worker.register_object(
                hook_self.local_worker, x.data, id=x.data.id, owners=[_id]
            )
            if x.grad is not None:
                self.grad = hook_self.local_worker.register_object(
                    hook_self.local_worker, x.grad, id=x.grad.id, owners=[_id]
                )

        """for some reason, when retuning obj from request_obj
            method (above), the gradient gets re-initialized without
            being re-registered and as a consequence does not have an
            id, causing the last register_object above to fail
            because x.grad.id does not exist. As a result, we've needed
            to register objects temporarily which seems to
            fix it. Super strange bug which took multiple days to figure
            out. The true cause is still unknown but this
            workaround seems to work well for now. Anyway, we don't need
            the temporary objects past this point.
            request_obj_cleanup_method()"""
        return self

    setattr(torch_type, "get_", get_)

    # TODO: make this a non-inline version
    setattr(torch_type, "get", get_)


# Traceback (RuntimeError)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-14-4207de4e45fd> in <module>()
10 loss.backward()
11
---> 12 model.send_(remote)

/Users/atrask/anaconda/lib/python3.6/site-packages/syft-0.1.0-py3.6.egg/syft/core/hooks.py in send_(self, workers)
907                                                    owners=self.owners, is_pointer=True)
908
--> 909             return hook_self._var_to_pointer(self, hook_self)
910
911         setattr(torch.autograd.variable.Variable, 'send_', send_)

/Users/atrask/anaconda/lib/python3.6/site-packages/syft-0.1.0-py3.6.egg/syft/core/hooks.py in _var_to_pointer(self, var, hook_self)
982         print("_var_to_pointer:" + str(var.data.id))
983         if var.grad is not None:
--> 984             self._var_to_pointer(var.grad, hook_self)
985
986         var.data.old_set_(var.data.__class__(0))

/Users/atrask/anaconda/lib/python3.6/site-packages/syft-0.1.0-py3.6.egg/syft/core/hooks.py in _var_to_pointer(self, var, hook_self)
989                                           id=var.data.id,
990                                           owners=var.owners,
--> 991                                           is_pointer=True)
992         return var
993

/Users/atrask/anaconda/lib/python3.6/site-packages/syft-0.1.0-py3.6.egg/syft/core/workers.py in register_object(self, worker, obj, force_attach_to_worker, temporary, **kwargs)
508             raise RuntimeError(
509                 'Invalid registry: is_pointer is {} but owners is {} on tensor {}'.format(
--> 510                     obj.is_pointer, obj.owners, obj.id))
511         # print("setting object:" + str(obj.id))
512         self.set_obj(obj.id, obj, force=force_attach_to_worker, tmp=temporary)

RuntimeError: Invalid registry: is_pointer is True but owners is [0] on tensor 2841986768

# Fixed code
def _hook_get_(hook_self, torch_type):
    def get_(self, reduce=lambda x: x[0]):
        """
        Gets a Torch object from its current owners.

        Args:
        reduce: (EXPERIMENTAL) How to reduce tensors that come from
            multiple workers
        """
        # TODO: fully generalize this to multiple workers; consider
        #       adding arguments for other tensor ids, e.g. mapping workers
        #       to tensors, and a reduce function (for example, would allow
        #       for built-in gradient averaging when Variable.get is done)
        #       (low priority)
        try:
            assert len(self.owners) == 1
        except AssertionError:
            raise NotImplementedError(
                "Only able to get_ tensors belonging \
                                            to a single worker right now."
            )
        if hook_self.local_worker.id in self.owners:
            return self

        _out = hook_self.local_worker.request_obj(
            obj_id=self.id, recipient=self.owners[0]
        )
        x, request_obj_cleanup_method = _out

        hook_self.local_worker.register_object(hook_self.local_worker, x, id=x.id)

        # if self == tensor
        _id = hook_self.local_worker.id  # for brevity
        if (
            type(self) != torch.autograd.variable.Variable
            and type(self) != torch.nn.parameter.Parameter
        ):
            _os = self.old_set_(x.type(self.type()))
            self = hook_self.local_worker.register_object(
                hook_self.local_worker, _os, id=self.id, owners=[_id]
            )

        else:
            _os = self.old_set_(x.type(self.data.type()))  # for brevity
            self = hook_self.local_worker.register_object(
                hook_self.local_worker, _os, id=self.id, owners=[_id]
            )

            self.data = hook_self.local_worker.register_object(
                hook_self.local_worker, x.data, id=x.data.id, owners=[_id]
            )
            if x.grad is not None:
                self.grad = hook_self.local_worker.register_object(
                    hook_self.local_worker, x.grad, id=x.grad.id, owners=[_id]
                )

        """for some reason, when retuning obj from request_obj
            method (above), the gradient gets re-initialized without
            being re-registered and as a consequence does not have an
            id, causing the last register_object above to fail
            because x.grad.id does not exist. As a result, we've needed
            to register objects temporarily which seems to
            fix it. Super strange bug which took multiple days to figure
            out. The true cause is still unknown but this
            workaround seems to work well for now. Anyway, we don't need
            the temporary objects past this point.
            request_obj_cleanup_method()"""
        return self

    setattr(torch_type, "get_", get_)

    # TODO: make this a non-inline version
    setattr(torch_type, "get", get_)