# Buggy code
def _hook_var_ser(hook_self):
    def ser(self, include_data=True):
        var_msg = {}
        var_msg["torch_type"] = re.search("<class '(.*)'>", str(self.__class__)).group(
            1
        )
        var_msg["requires_grad"] = self.requires_grad
        var_msg["volatile"] = self.volatile
        var_msg["data"] = self.data.ser(include_data)
        if self.grad is not None:
            var_msg["grad"] = self.grad.ser(include_data)
        else:
            var_msg["grad"] = None
        var_msg["id"] = self.id
        if type(self.owners[0]) is int:
            var_msg["owners"] = self.owners
        else:
            var_msg["owners"] = list(map(lambda x: x.id, self.owners))
        var_msg["is_pointer"] = not include_data
        return json.dumps(var_msg)

    def deser(self, obj_msg):
        if "data" in obj_msg.keys():
            data_msg = json.loads(obj_msg["data"])
            tensor_type = hook_self.types_guard(data_msg["torch_type"])
            data_obj = tensor_type.deser(tensor_type, data_msg)
            # data_obj = hook_self.build_tensor(data_msg, tensor_type)
            data = hook_self.local_worker.handle_register(data_obj, data_msg)

        if "grad" in obj_msg.keys():
            if obj_msg["grad"] is not None:
                grad_msg = json.loads(obj_msg["grad"])
                var_type = hook_self.types_guard(grad_msg["torch_type"])
                grad_obj = hook_self._build_var(grad_msg, var_type)
                grad = hook_self.local_worker.handle_register(
                    grad_obj, grad_msg, force_attach_to_worker=False, temporary=True
                )

            else:
                grad = None
        var = self(
            data, volatile=obj_msg["volatile"], requires_grad=obj_msg["requires_grad"]
        )
        # var.grad = grad
        if grad is not None:
            setattr(var, "grad", grad)
        else:
            var.grad = None

        # this returns grad because garbage collection seems to do something really strange
        # if grad isn't returned here. It re-initializes the gradient somehow but in a way
        # where it's not registered (which is bad)
        return var

    torch.autograd.variable.Variable.ser = ser
    torch.autograd.variable.Variable.deser = deser


# 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_var_ser(hook_self):
    def ser(self, include_data=True):
        """Serializes a variable into a JSON object"""

        var_msg = {}
        var_msg["torch_type"] = re.search("<class '(.*)'>", str(self.__class__)).group(
            1
        )
        var_msg["requires_grad"] = self.requires_grad
        var_msg["volatile"] = self.volatile
        var_msg["data"] = self.data.ser(include_data)
        if self.grad is not None:
            var_msg["grad"] = self.grad.ser(include_data)
        else:
            var_msg["grad"] = None
        var_msg["id"] = self.id
        if type(self.owners[0]) is int:
            var_msg["owners"] = self.owners
        else:
            var_msg["owners"] = list(map(lambda x: x.id, self.owners))
        var_msg["is_pointer"] = not include_data
        return json.dumps(var_msg)

    def deser(self, obj_msg):
        """Deserializes a JSON object into a variable"""

        if "data" in obj_msg.keys():
            data_msg = json.loads(obj_msg["data"])
            tensor_type = hook_self.types_guard(data_msg["torch_type"])
            data_obj = tensor_type.deser(tensor_type, data_msg)
            # data_obj = hook_self.build_tensor(data_msg, tensor_type)
            data = hook_self.local_worker.handle_register(data_obj, data_msg)

        if "grad" in obj_msg.keys():
            if obj_msg["grad"] is not None:
                grad_msg = json.loads(obj_msg["grad"])

                var_type = hook_self.types_guard(grad_msg["torch_type"])
                grad_obj = hook_self._build_var(grad_msg, var_type)

                grad = hook_self.local_worker.handle_register(
                    grad_obj, grad_msg, force_attach_to_worker=False, temporary=True
                )
            else:
                grad = None

        # nn.parameter.Parameter does not accept "volatile" as an input param.
        # https://pytorch.org/docs/0.3.1/autograd.html#variable
        if self == torch.nn.parameter.Parameter:
            var = self(data, requires_grad=obj_msg["requires_grad"])
        else:
            var = self(
                data,
                volatile=obj_msg["volatile"],
                requires_grad=obj_msg["requires_grad"],
            )

        # var.grad = grad
        if grad is not None:
            setattr(var, "grad", grad)
        else:
            var.grad = None

        # this returns grad because garbage collection seems to do something really strange
        # if grad isn't returned here. It re-initializes the gradient somehow but in a way
        # where it's not registered (which is bad)
        return var

    torch.autograd.variable.Variable.ser = ser
    torch.autograd.variable.Variable.deser = deser