We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Dim does not work with multipointer tensors
**Describe the bug**
Calling dim on a multipointer tensor returns a multipointer tensor where the values of the children are all ints. The return signature should be an int

**To Reproduce**
create a multipointer tensor and call .dim()

**Expected behavior**
the value returned should be an int


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `syft/frameworks/torch/tensors/interpreters/multi_pointer.py`
Content:
```
1 import torch
2 from typing import List
3 from typing import Union
4 
5 import syft as sy
6 from syft.frameworks.torch.tensors.interpreters.abstract import AbstractTensor
7 from syft.frameworks.torch.tensors.interpreters import AdditiveSharingTensor
8 from syft.workers import BaseWorker
9 from syft.frameworks.torch.overload_torch import overloaded
10 
11 from syft.workers import AbstractWorker
12 
13 
14 class MultiPointerTensor(AbstractTensor):
15     ""
16 
17     def __init__(
18         self,
19         location: BaseWorker = None,
20         id_at_location: Union[str, int] = None,
21         register: bool = False,
22         owner: BaseWorker = None,
23         id: Union[str, int] = None,
24         garbage_collect_data: bool = True,
25         point_to_attr: str = None,
26         tags: List[str] = None,
27         description: str = None,
28         children: List[AbstractTensor] = [],
29     ):
30 
31         super().__init__(tags, description)
32 
33         self.location = location
34         self.id_at_location = id_at_location
35         self.owner = owner
36         self.id = id
37         self.garbage_collect_data = garbage_collect_data
38         self.point_to_attr = point_to_attr
39 
40         self.child = {}
41         for c in children:
42             assert c.shape == children[0].shape
43             self.child[c.location.id] = c
44 
45     def __str__(self):
46         type_name = type(self).__name__
47         out = f"[" f"{type_name}]"
48         for v in self.child.values():
49             out += "\n\t-> " + str(v)
50         return out
51 
52     def __eq__(self, other):
53         return torch.eq(self, other)
54 
55     def __add__(self, other):
56         """
57         Adding a MultiPointer (MPT) and an AdditiveShared Tensor (AST) should return an
58         AdditiveShared Tensor, so if we have this configuration, we permute self and
59         other to use the fact that other.__add__(...) return an object of type other
60 
61         Else, we just redirect to .add which works well
62         """
63         if isinstance(other, AdditiveSharingTensor):
64             return other.__add__(self)
65         else:
66             return self.add(other)
67 
68     def __mul__(self, other):
69         """
70         See __add__ for details but, MPT * AST should return AST
71         """
72         if isinstance(other, AdditiveSharingTensor):
73             return other.__mul__(self)
74         else:
75             return self.mul(other)
76 
77     @property
78     def shape(self) -> torch.Size:
79         """This method returns the shape of the data being pointed to.
80         This shape information SHOULD be cached on self._shape, but
81         occasionally this information may not be present. If this is the
82         case, then it requests the shape information from the remote object
83         directly (which is inefficient and should be avoided)."""
84 
85         return list(self.child.values())[0].shape
86 
87     def get(self, sum_results: bool = False) -> torch.Tensor:
88 
89         results = list()
90         for v in self.child.values():
91             results.append(v.get())
92 
93         if sum_results:
94             return sum(results)
95 
96         return results
97 
98     def virtual_get(self, sum_results: bool = False):
99         """Get the value of the tensor without calling get - Only for VirtualWorkers"""
100 
101         results = list()
102         for v in self.child.values():
103             value = v.location._objects[v.id_at_location]
104             results.append(value)
105 
106         if sum_results:
107             return sum(results)
108 
109         return results
110 
111     @staticmethod
112     def dispatch(args, worker):
113         """
114         utility function for handle_func_command which help to select
115         shares (seen as elements of dict) in an argument set. It could
116         perhaps be put elsewhere
117 
118         Args:
119             args: arguments to give to a functions
120             worker: owner of the shares to select
121 
122         Return:
123             args where the MultiPointerTensor are replaced by
124             the appropriate share
125         """
126         return map(lambda x: x[worker] if isinstance(x, dict) else x, args)
127 
128     @classmethod
129     def handle_func_command(cls, command):
130         """
131         Receive an instruction for a function to be applied on a Syft Tensor,
132         Replace in the args all the LogTensors with
133         their child attribute, forward the command instruction to the
134         handle_function_command of the type of the child attributes, get the
135         response and replace a Syft Tensor on top of all tensors found in
136         the response.
137 
138         Args:
139             command: instruction of a function command: (command name,
140             <no self>, arguments[, kwargs])
141 
142         Returns:
143             the response of the function command
144         """
145 
146         cmd, _, args, kwargs = command
147 
148         tensor = args[0]
149 
150         # Check that the function has not been overwritten
151         try:
152             # Try to get recursively the attributes in cmd = "<attr1>.<attr2>.<attr3>..."
153             cmd = cls.rgetattr(cls, cmd)
154             return cmd(*args, **kwargs)
155         except AttributeError:
156             pass
157 
158         # TODO: I can't manage the import issue, can you?
159         # Replace all LoggingTensor with their child attribute
160         new_args, new_kwargs, new_type = sy.frameworks.torch.hook_args.hook_function_args(
161             cmd, args, kwargs
162         )
163 
164         results = {}
165         for worker, share in new_args[0].items():
166             new_type = type(share)
167             new_args_worker = tuple(MultiPointerTensor.dispatch(new_args, worker))
168 
169             # build the new command
170             new_command = (cmd, None, new_args_worker, new_kwargs)
171 
172             # Send it to the appropriate class and get the response
173             results[worker] = new_type.handle_func_command(new_command)
174 
175         # Put back MultiPointerTensor on the tensors found in the response
176         response = sy.frameworks.torch.hook_args.hook_response(
177             cmd, results, wrap_type=cls, wrap_args=tensor.get_class_attributes()
178         )
179 
180         return response
181 
182     def set_garbage_collect_data(self, value):
183         shares = self.child
184         for _, share in shares.items():
185             share.child.garbage_collect_data = value
186 
187     @staticmethod
188     def simplify(tensor: "MultiPointerTensor") -> tuple:
189         """
190         This function takes the attributes of a MultiPointerTensor and saves them in a tuple
191         Args:
192             tensor (MultiPointerTensor): a MultiPointerTensor
193         Returns:
194             tuple: a tuple holding the unique attributes of the additive shared tensor
195         Examples:
196             data = simplify(tensor)
197         """
198 
199         chain = None
200         if hasattr(tensor, "child"):
201             chain = sy.serde.simplify(tensor.child)
202         return (tensor.id, chain)
203 
204     @staticmethod
205     def detail(worker: AbstractWorker, tensor_tuple: tuple) -> "MultiPointerTensor":
206         """
207         This function reconstructs a MultiPointerTensor given it's attributes in form of a tuple.
208         Args:
209             worker: the worker doing the deserialization
210             tensor_tuple: a tuple holding the attributes of the MultiPointerTensor
211         Returns:
212             MultiPointerTensor: a MultiPointerTensor
213         Examples:
214             multi_pointer_tensor = detail(data)
215         """
216 
217         tensor_id, chain = tensor_tuple
218 
219         tensor = sy.MultiPointerTensor(owner=worker, id=tensor_id)
220 
221         if chain is not None:
222             chain = sy.serde._detail(worker, chain)
223             tensor.child = chain
224 
225         return tensor
226
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/syft/frameworks/torch/tensors/interpreters/multi_pointer.py b/syft/frameworks/torch/tensors/interpreters/multi_pointer.py
--- a/syft/frameworks/torch/tensors/interpreters/multi_pointer.py
+++ b/syft/frameworks/torch/tensors/interpreters/multi_pointer.py
@@ -84,6 +84,12 @@
 
         return list(self.child.values())[0].shape
 
+    def dim(self) -> int:
+        """This method fixes the error that the result of dim was a list of ints
+        stored inside a multipointer tensor"""
+
+        return len(self.shape)
+
     def get(self, sum_results: bool = False) -> torch.Tensor:
 
         results = list()

{"golden_diff": "diff --git a/syft/frameworks/torch/tensors/interpreters/multi_pointer.py b/syft/frameworks/torch/tensors/interpreters/multi_pointer.py\n--- a/syft/frameworks/torch/tensors/interpreters/multi_pointer.py\n+++ b/syft/frameworks/torch/tensors/interpreters/multi_pointer.py\n@@ -84,6 +84,12 @@\n \n         return list(self.child.values())[0].shape\n \n+    def dim(self) -> int:\n+        \"\"\"This method fixes the error that the result of dim was a list of ints\n+        stored inside a multipointer tensor\"\"\"\n+\n+        return len(self.shape)\n+\n     def get(self, sum_results: bool = False) -> torch.Tensor:\n \n         results = list()\n", "issue": "Dim does not work with multipointer tensors\n**Describe the bug**\r\nCalling dim on a multipointer tensor returns a multipointer tensor where the values of the children are all ints. The return signature should be an int\r\n\r\n**To Reproduce**\r\ncreate a multipointer tensor and call .dim()\r\n\r\n**Expected behavior**\r\nthe value returned should be an int\r\n\n", "before_files": [{"content": "import torch\nfrom typing import List\nfrom typing import Union\n\nimport syft as sy\nfrom syft.frameworks.torch.tensors.interpreters.abstract import AbstractTensor\nfrom syft.frameworks.torch.tensors.interpreters import AdditiveSharingTensor\nfrom syft.workers import BaseWorker\nfrom syft.frameworks.torch.overload_torch import overloaded\n\nfrom syft.workers import AbstractWorker\n\n\nclass MultiPointerTensor(AbstractTensor):\n    \"\"\n\n    def __init__(\n        self,\n        location: BaseWorker = None,\n        id_at_location: Union[str, int] = None,\n        register: bool = False,\n        owner: BaseWorker = None,\n        id: Union[str, int] = None,\n        garbage_collect_data: bool = True,\n        point_to_attr: str = None,\n        tags: List[str] = None,\n        description: str = None,\n        children: List[AbstractTensor] = [],\n    ):\n\n        super().__init__(tags, description)\n\n        self.location = location\n        self.id_at_location = id_at_location\n        self.owner = owner\n        self.id = id\n        self.garbage_collect_data = garbage_collect_data\n        self.point_to_attr = point_to_attr\n\n        self.child = {}\n        for c in children:\n            assert c.shape == children[0].shape\n            self.child[c.location.id] = c\n\n    def __str__(self):\n        type_name = type(self).__name__\n        out = f\"[\" f\"{type_name}]\"\n        for v in self.child.values():\n            out += \"\\n\\t-> \" + str(v)\n        return out\n\n    def __eq__(self, other):\n        return torch.eq(self, other)\n\n    def __add__(self, other):\n        \"\"\"\n        Adding a MultiPointer (MPT) and an AdditiveShared Tensor (AST) should return an\n        AdditiveShared Tensor, so if we have this configuration, we permute self and\n        other to use the fact that other.__add__(...) return an object of type other\n\n        Else, we just redirect to .add which works well\n        \"\"\"\n        if isinstance(other, AdditiveSharingTensor):\n            return other.__add__(self)\n        else:\n            return self.add(other)\n\n    def __mul__(self, other):\n        \"\"\"\n        See __add__ for details but, MPT * AST should return AST\n        \"\"\"\n        if isinstance(other, AdditiveSharingTensor):\n            return other.__mul__(self)\n        else:\n            return self.mul(other)\n\n    @property\n    def shape(self) -> torch.Size:\n        \"\"\"This method returns the shape of the data being pointed to.\n        This shape information SHOULD be cached on self._shape, but\n        occasionally this information may not be present. If this is the\n        case, then it requests the shape information from the remote object\n        directly (which is inefficient and should be avoided).\"\"\"\n\n        return list(self.child.values())[0].shape\n\n    def get(self, sum_results: bool = False) -> torch.Tensor:\n\n        results = list()\n        for v in self.child.values():\n            results.append(v.get())\n\n        if sum_results:\n            return sum(results)\n\n        return results\n\n    def virtual_get(self, sum_results: bool = False):\n        \"\"\"Get the value of the tensor without calling get - Only for VirtualWorkers\"\"\"\n\n        results = list()\n        for v in self.child.values():\n            value = v.location._objects[v.id_at_location]\n            results.append(value)\n\n        if sum_results:\n            return sum(results)\n\n        return results\n\n    @staticmethod\n    def dispatch(args, worker):\n        \"\"\"\n        utility function for handle_func_command which help to select\n        shares (seen as elements of dict) in an argument set. It could\n        perhaps be put elsewhere\n\n        Args:\n            args: arguments to give to a functions\n            worker: owner of the shares to select\n\n        Return:\n            args where the MultiPointerTensor are replaced by\n            the appropriate share\n        \"\"\"\n        return map(lambda x: x[worker] if isinstance(x, dict) else x, args)\n\n    @classmethod\n    def handle_func_command(cls, command):\n        \"\"\"\n        Receive an instruction for a function to be applied on a Syft Tensor,\n        Replace in the args all the LogTensors with\n        their child attribute, forward the command instruction to the\n        handle_function_command of the type of the child attributes, get the\n        response and replace a Syft Tensor on top of all tensors found in\n        the response.\n\n        Args:\n            command: instruction of a function command: (command name,\n            <no self>, arguments[, kwargs])\n\n        Returns:\n            the response of the function command\n        \"\"\"\n\n        cmd, _, args, kwargs = command\n\n        tensor = args[0]\n\n        # Check that the function has not been overwritten\n        try:\n            # Try to get recursively the attributes in cmd = \"<attr1>.<attr2>.<attr3>...\"\n            cmd = cls.rgetattr(cls, cmd)\n            return cmd(*args, **kwargs)\n        except AttributeError:\n            pass\n\n        # TODO: I can't manage the import issue, can you?\n        # Replace all LoggingTensor with their child attribute\n        new_args, new_kwargs, new_type = sy.frameworks.torch.hook_args.hook_function_args(\n            cmd, args, kwargs\n        )\n\n        results = {}\n        for worker, share in new_args[0].items():\n            new_type = type(share)\n            new_args_worker = tuple(MultiPointerTensor.dispatch(new_args, worker))\n\n            # build the new command\n            new_command = (cmd, None, new_args_worker, new_kwargs)\n\n            # Send it to the appropriate class and get the response\n            results[worker] = new_type.handle_func_command(new_command)\n\n        # Put back MultiPointerTensor on the tensors found in the response\n        response = sy.frameworks.torch.hook_args.hook_response(\n            cmd, results, wrap_type=cls, wrap_args=tensor.get_class_attributes()\n        )\n\n        return response\n\n    def set_garbage_collect_data(self, value):\n        shares = self.child\n        for _, share in shares.items():\n            share.child.garbage_collect_data = value\n\n    @staticmethod\n    def simplify(tensor: \"MultiPointerTensor\") -> tuple:\n        \"\"\"\n        This function takes the attributes of a MultiPointerTensor and saves them in a tuple\n        Args:\n            tensor (MultiPointerTensor): a MultiPointerTensor\n        Returns:\n            tuple: a tuple holding the unique attributes of the additive shared tensor\n        Examples:\n            data = simplify(tensor)\n        \"\"\"\n\n        chain = None\n        if hasattr(tensor, \"child\"):\n            chain = sy.serde.simplify(tensor.child)\n        return (tensor.id, chain)\n\n    @staticmethod\n    def detail(worker: AbstractWorker, tensor_tuple: tuple) -> \"MultiPointerTensor\":\n        \"\"\"\n        This function reconstructs a MultiPointerTensor given it's attributes in form of a tuple.\n        Args:\n            worker: the worker doing the deserialization\n            tensor_tuple: a tuple holding the attributes of the MultiPointerTensor\n        Returns:\n            MultiPointerTensor: a MultiPointerTensor\n        Examples:\n            multi_pointer_tensor = detail(data)\n        \"\"\"\n\n        tensor_id, chain = tensor_tuple\n\n        tensor = sy.MultiPointerTensor(owner=worker, id=tensor_id)\n\n        if chain is not None:\n            chain = sy.serde._detail(worker, chain)\n            tensor.child = chain\n\n        return tensor\n", "path": "syft/frameworks/torch/tensors/interpreters/multi_pointer.py"}], "after_files": [{"content": "import torch\nfrom typing import List\nfrom typing import Union\n\nimport syft as sy\nfrom syft.frameworks.torch.tensors.interpreters.abstract import AbstractTensor\nfrom syft.frameworks.torch.tensors.interpreters import AdditiveSharingTensor\nfrom syft.workers import BaseWorker\nfrom syft.frameworks.torch.overload_torch import overloaded\n\nfrom syft.workers import AbstractWorker\n\n\nclass MultiPointerTensor(AbstractTensor):\n    \"\"\n\n    def __init__(\n        self,\n        location: BaseWorker = None,\n        id_at_location: Union[str, int] = None,\n        register: bool = False,\n        owner: BaseWorker = None,\n        id: Union[str, int] = None,\n        garbage_collect_data: bool = True,\n        point_to_attr: str = None,\n        tags: List[str] = None,\n        description: str = None,\n        children: List[AbstractTensor] = [],\n    ):\n\n        super().__init__(tags, description)\n\n        self.location = location\n        self.id_at_location = id_at_location\n        self.owner = owner\n        self.id = id\n        self.garbage_collect_data = garbage_collect_data\n        self.point_to_attr = point_to_attr\n\n        self.child = {}\n        for c in children:\n            assert c.shape == children[0].shape\n            self.child[c.location.id] = c\n\n    def __str__(self):\n        type_name = type(self).__name__\n        out = f\"[\" f\"{type_name}]\"\n        for v in self.child.values():\n            out += \"\\n\\t-> \" + str(v)\n        return out\n\n    def __eq__(self, other):\n        return torch.eq(self, other)\n\n    def __add__(self, other):\n        \"\"\"\n        Adding a MultiPointer (MPT) and an AdditiveShared Tensor (AST) should return an\n        AdditiveShared Tensor, so if we have this configuration, we permute self and\n        other to use the fact that other.__add__(...) return an object of type other\n\n        Else, we just redirect to .add which works well\n        \"\"\"\n        if isinstance(other, AdditiveSharingTensor):\n            return other.__add__(self)\n        else:\n            return self.add(other)\n\n    def __mul__(self, other):\n        \"\"\"\n        See __add__ for details but, MPT * AST should return AST\n        \"\"\"\n        if isinstance(other, AdditiveSharingTensor):\n            return other.__mul__(self)\n        else:\n            return self.mul(other)\n\n    @property\n    def shape(self) -> torch.Size:\n        \"\"\"This method returns the shape of the data being pointed to.\n        This shape information SHOULD be cached on self._shape, but\n        occasionally this information may not be present. If this is the\n        case, then it requests the shape information from the remote object\n        directly (which is inefficient and should be avoided).\"\"\"\n\n        return list(self.child.values())[0].shape\n\n    def dim(self) -> int:\n        \"\"\"This method fixes the error that the result of dim was a list of ints\n        stored inside a multipointer tensor\"\"\"\n\n        return len(self.shape)\n\n    def get(self, sum_results: bool = False) -> torch.Tensor:\n\n        results = list()\n        for v in self.child.values():\n            results.append(v.get())\n\n        if sum_results:\n            return sum(results)\n\n        return results\n\n    def virtual_get(self, sum_results: bool = False):\n        \"\"\"Get the value of the tensor without calling get - Only for VirtualWorkers\"\"\"\n\n        results = list()\n        for v in self.child.values():\n            value = v.location._objects[v.id_at_location]\n            results.append(value)\n\n        if sum_results:\n            return sum(results)\n\n        return results\n\n    @staticmethod\n    def dispatch(args, worker):\n        \"\"\"\n        utility function for handle_func_command which help to select\n        shares (seen as elements of dict) in an argument set. It could\n        perhaps be put elsewhere\n\n        Args:\n            args: arguments to give to a functions\n            worker: owner of the shares to select\n\n        Return:\n            args where the MultiPointerTensor are replaced by\n            the appropriate share\n        \"\"\"\n        return map(lambda x: x[worker] if isinstance(x, dict) else x, args)\n\n    @classmethod\n    def handle_func_command(cls, command):\n        \"\"\"\n        Receive an instruction for a function to be applied on a Syft Tensor,\n        Replace in the args all the LogTensors with\n        their child attribute, forward the command instruction to the\n        handle_function_command of the type of the child attributes, get the\n        response and replace a Syft Tensor on top of all tensors found in\n        the response.\n\n        Args:\n            command: instruction of a function command: (command name,\n            <no self>, arguments[, kwargs])\n\n        Returns:\n            the response of the function command\n        \"\"\"\n\n        cmd, _, args, kwargs = command\n\n        tensor = args[0]\n\n        # Check that the function has not been overwritten\n        try:\n            # Try to get recursively the attributes in cmd = \"<attr1>.<attr2>.<attr3>...\"\n            cmd = cls.rgetattr(cls, cmd)\n            return cmd(*args, **kwargs)\n        except AttributeError:\n            pass\n\n        # TODO: I can't manage the import issue, can you?\n        # Replace all LoggingTensor with their child attribute\n        new_args, new_kwargs, new_type = sy.frameworks.torch.hook_args.hook_function_args(\n            cmd, args, kwargs\n        )\n\n        results = {}\n        for worker, share in new_args[0].items():\n            new_type = type(share)\n            new_args_worker = tuple(MultiPointerTensor.dispatch(new_args, worker))\n\n            # build the new command\n            new_command = (cmd, None, new_args_worker, new_kwargs)\n\n            # Send it to the appropriate class and get the response\n            results[worker] = new_type.handle_func_command(new_command)\n\n        # Put back MultiPointerTensor on the tensors found in the response\n        response = sy.frameworks.torch.hook_args.hook_response(\n            cmd, results, wrap_type=cls, wrap_args=tensor.get_class_attributes()\n        )\n\n        return response\n\n    def set_garbage_collect_data(self, value):\n        shares = self.child\n        for _, share in shares.items():\n            share.child.garbage_collect_data = value\n\n    @staticmethod\n    def simplify(tensor: \"MultiPointerTensor\") -> tuple:\n        \"\"\"\n        This function takes the attributes of a MultiPointerTensor and saves them in a tuple\n        Args:\n            tensor (MultiPointerTensor): a MultiPointerTensor\n        Returns:\n            tuple: a tuple holding the unique attributes of the additive shared tensor\n        Examples:\n            data = simplify(tensor)\n        \"\"\"\n\n        chain = None\n        if hasattr(tensor, \"child\"):\n            chain = sy.serde.simplify(tensor.child)\n        return (tensor.id, chain)\n\n    @staticmethod\n    def detail(worker: AbstractWorker, tensor_tuple: tuple) -> \"MultiPointerTensor\":\n        \"\"\"\n        This function reconstructs a MultiPointerTensor given it's attributes in form of a tuple.\n        Args:\n            worker: the worker doing the deserialization\n            tensor_tuple: a tuple holding the attributes of the MultiPointerTensor\n        Returns:\n            MultiPointerTensor: a MultiPointerTensor\n        Examples:\n            multi_pointer_tensor = detail(data)\n        \"\"\"\n\n        tensor_id, chain = tensor_tuple\n\n        tensor = sy.MultiPointerTensor(owner=worker, id=tensor_id)\n\n        if chain is not None:\n            chain = sy.serde._detail(worker, chain)\n            tensor.child = chain\n\n        return tensor\n", "path": "syft/frameworks/torch/tensors/interpreters/multi_pointer.py"}]}