# Buggy code
def simplify(tensor: "AdditiveSharingTensor") -> tuple:
    """
    This function takes the attributes of a AdditiveSharingTensor and saves them in a tuple
    Args:
        tensor (AdditiveSharingTensor): a AdditiveSharingTensor
    Returns:
        tuple: a tuple holding the unique attributes of the additive shared tensor
    Examples:
        data = simplify(tensor)
    """

    chain = None
    if hasattr(tensor, "child"):
        chain = sy.serde._simplify(tensor.child)
    return (tensor.id, tensor.field, tensor.crypto_provider.id, chain)


# Traceback (KeyError)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/code/PySyft/syft/frameworks/torch/hook/hook_args.py in register_response(attr, response, response_ids, owner)
656         # Load the utility function to register the response and transform tensors with pointers
--> 657         register_response_function = register_response_functions[attr_id]
658         # Try running it

KeyError: 'numpy'

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
<ipython-input-1-95fc1ed4192b> in <module>
13 x = torch.tensor([1.])
14 x_ptr = x.send(alice)
---> 15 x_fp = x_ptr.fix_prec()

~/code/PySyft/syft/frameworks/torch/tensors/interpreters/native.py in fix_prec(self, *args, **kwargs)
666         prec_fractional = kwargs.get("precision_fractional", 3)
667         max_precision = _get_maximum_precision()
--> 668         if self._requires_large_precision(max_precision, base, prec_fractional):
669             return (
670                 syft.LargePrecisionTensor(*args, **kwargs)

~/code/PySyft/syft/frameworks/torch/tensors/interpreters/native.py in _requires_large_precision(self, max_precision, base, precision_fractional)
691         # We need to use NumPy here as log2 is not yet implemented for LongTensor PyTorch objects
692         return np.any(
--> 693             np.log2(np.abs(self.clone().detach().numpy()) + 1) + base_fractional > max_precision
694         )
695

~/code/PySyft/syft/frameworks/torch/hook/hook.py in overloaded_native_method(self, *args, **kwargs)
675                 # Send the new command to the appropriate class and get the response
676                 method = getattr(new_self, method_name)
--> 677                 response = method(*new_args, **new_kwargs)
678
679                 # For inplace methods, just directly return self

~/code/PySyft/syft/frameworks/torch/hook/hook.py in overloaded_pointer_method(self, *args, **kwargs)
511             command = (attr, self, args, kwargs)
512
--> 513             response = owner.send_command(location, command)
514
515             return response

~/code/PySyft/syft/workers/base.py in send_command(self, recipient, message, return_ids)
425
426         try:
--> 427             ret_val = self.send_msg(codes.MSGTYPE.CMD, message, location=recipient)
428         except ResponseSignatureError as e:
429             ret_val = None

~/code/PySyft/syft/workers/base.py in send_msg(self, msg_type, message, location)
221
222         # Step 2: send the message and wait for a response
--> 223         bin_response = self._send_msg(bin_message, location)
224
225         # Step 3: deserialize the response

~/code/PySyft/syft/workers/virtual.py in _send_msg(self, message, location)
8 class VirtualWorker(BaseWorker, FederatedClient):
9     def _send_msg(self, message: bin, location: BaseWorker) -> bin:
---> 10         return location._recv_msg(message)
11
12     def _recv_msg(self, message: bin) -> bin:

~/code/PySyft/syft/workers/virtual.py in _recv_msg(self, message)
11
12     def _recv_msg(self, message: bin) -> bin:
---> 13         return self.recv_msg(message)
14
15     @staticmethod

~/code/PySyft/syft/workers/base.py in recv_msg(self, bin_message)
252             print(f"worker {self} received {sy.codes.code2MSGTYPE[msg_type]} {contents}")
253         # Step 1: route message to appropriate function
--> 254         response = self._message_router[msg_type](contents)
255
256         # Step 2: Serialize the message to simple python objects

~/code/PySyft/syft/workers/base.py in execute_command(self, message)
391             try:
392                 response = sy.frameworks.torch.hook_args.register_response(
--> 393                     command_name, response, list(return_ids), self
394                 )
395                 return response

~/code/PySyft/syft/frameworks/torch/hook/hook_args.py in register_response(attr, response, response_ids, owner)
664         register_response_functions[attr_id] = register_response_function
665         # Run it
--> 666         new_response = register_response_function(response, response_ids=response_ids, owner=owner)
667
668     # Remove the artificial tuple

~/code/PySyft/syft/frameworks/torch/hook/hook_args.py in <lambda>(x, **kwargs)
757         f = many_fold
758
--> 759     return lambda x, **kwargs: f(lambdas, x, **kwargs)

~/code/PySyft/syft/frameworks/torch/hook/hook_args.py in two_fold(lambdas, args, **kwargs)
514
515 def two_fold(lambdas, args, **kwargs):
--> 516     return lambdas[0](args[0], **kwargs), lambdas[1](args[1], **kwargs)
517
518

~/code/PySyft/syft/frameworks/torch/hook/hook_args.py in <lambda>(i, **kwargs)
735         if isinstance(r, (list, tuple))  # if the rule is a list or tuple.
736         # Last if not, rule is probably == 1 so use type to return the right transformation.
--> 737         else lambda i, **kwargs: register_tensor(i, **kwargs)
738         for a, r in zip(response, rules)  # And do this for all the responses / rules provided
739     ]

~/code/PySyft/syft/frameworks/torch/hook/hook_args.py in register_tensor(tensor, owner, response_ids)
706             and each id is pop out when needed.
707     """
--> 708     tensor.owner = owner
709     try:
710         tensor.id = response_ids.pop(-1)

AttributeError: 'numpy.ndarray' object has no attribute 'owner'

# Fixed code
def simplify(tensor: "AdditiveSharingTensor") -> tuple:
    """
    This function takes the attributes of a AdditiveSharingTensor and saves them in a tuple
    Args:
        tensor (AdditiveSharingTensor): a AdditiveSharingTensor
    Returns:
        tuple: a tuple holding the unique attributes of the additive shared tensor
    Examples:
        data = simplify(tensor)
    """

    chain = None
    if hasattr(tensor, "child"):
        chain = sy.serde._simplify(tensor.child)

    # Don't delete the remote values of the shares at simplification
    tensor.set_garbage_collect_data(False)

    return (tensor.id, tensor.field, tensor.crypto_provider.id, chain)