We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG][TENSOR] TensorZeros generated in TensorDiag.tile have the same key even if they have different shapes
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**

`TensorDiag.tile` may generate chunks whose op is TensorZeros, they will have the same key even if their shape are different.

**To Reproduce**

```python
In [94]: a = mt.arange(5, chunk_size=2)                                         

In [95]: d = mt.diag(a)                                                         

In [96]: d.tiles()                                                              
Out[96]: <mars.tensor.core.Tensor at 0x136df1dc8>

In [99]: d.chunks[1].shape, d.chunks[1].op.key                                  
Out[99]: ((2, 2), 'd6d8d339b2cbac64ae65cb29ff3f6785')

In [100]: d.chunks[2].shape, d.chunks[1].op.key                                 
Out[100]: ((2, 1), 'd6d8d339b2cbac64ae65cb29ff3f6785')
```

**Expected behavior**

Chunks of TensorZeros should have different keys if their shapes are different, this is rightly handled for TensorZeros.tile, but when the TensorZeros op is created manually, this bug could happen.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mars/tensor/expressions/datasource/core.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Copyright 1999-2018 Alibaba Group Holding Ltd.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 import itertools
18 
19 import numpy as np
20 
21 from .... import opcodes as OperandDef
22 from ....operands import DataSource
23 from ....compat import izip
24 from ....config import options
25 from ..utils import normalize_shape, decide_chunk_sizes
26 from ..core import TensorOperandMixin
27 
28 
29 class TensorDataSource(DataSource, TensorOperandMixin):
30     """
31     Tensor data source base class, provide universal tile logic,
32     subclass can overwrite tile method.
33     """
34 
35     __slots__ = ()
36 
37     def to_chunk_op(self, *args):
38         chunk_shape, idx, chunk_size = args
39         chunk_op = self.copy().reset_key()
40         chunk_op.params = {'size': chunk_shape, 'index': idx}  # to make op key different
41         return chunk_op
42 
43     @classmethod
44     def tile(cls, op):
45         tensor = op.outputs[0]
46 
47         chunk_size = tensor.params.raw_chunk_size or options.tensor.chunk_size
48         chunk_size = decide_chunk_sizes(tensor.shape, chunk_size, tensor.dtype.itemsize)
49         chunk_size_idxes = (range(len(size)) for size in chunk_size)
50 
51         out_chunks = []
52         for chunk_shape, chunk_idx in izip(itertools.product(*chunk_size),
53                                            itertools.product(*chunk_size_idxes)):
54             chunk_op = op.to_chunk_op(chunk_shape, chunk_idx, chunk_size)
55             out_chunk = chunk_op.new_chunk(None, chunk_shape, index=chunk_idx)
56             out_chunks.append(out_chunk)
57 
58         new_op = op.copy()
59         return new_op.new_tensors(op.inputs, tensor.shape, chunks=out_chunks, nsplits=chunk_size)
60 
61 
62 class TensorNoInput(TensorDataSource):
63     """
64     Tensor operand with no inputs.
65     """
66 
67     def check_inputs(self, inputs):
68         # no inputs
69         if inputs and len(inputs) > 0:
70             raise ValueError("Tensor data source has no inputs")
71 
72     def calc_shape(self, *inputs_shape):
73         return self.outputs[0].shape
74 
75     def __call__(self, shape, chunk_size=None):
76         shape = normalize_shape(shape)
77         return self.new_tensor(None, shape, raw_chunk_size=chunk_size)
78 
79 
80 class TensorHasInput(TensorDataSource):
81     """
82     Tensor operand with a single input.
83     """
84 
85     @property
86     def input(self):
87         return self._input
88 
89     def check_inputs(self, inputs):
90         # no inputs
91         if len(inputs) != 1:
92             raise ValueError("Tensor can only have 1 input")
93 
94     def _set_inputs(self, inputs):
95         super(TensorHasInput, self)._set_inputs(inputs)
96         self._input = self._inputs[0]
97 
98     @classmethod
99     def tile(cls, op):
100         out_chunks = []
101         for c in op.input.chunks:
102             out_chunk = op.copy().reset_key().new_chunk([c], c.shape, index=c.index)
103             out_chunks.append(out_chunk)
104 
105         new_op = op.copy()
106         return new_op.new_tensors(op.inputs, op.outputs[0].shape, chunks=out_chunks,
107                                   nsplits=op.input.nsplits)
108 
109     def calc_shape(self, *inputs_shape):
110         return inputs_shape[0]
111 
112     def __call__(self, a):
113         return self.new_tensor([a], a.shape)
114 
115 
116 class TensorLike(TensorHasInput):
117     def _set_inputs(self, inputs):
118         super(TensorLike, self)._set_inputs(inputs)
119         if self.dtype is None:
120             self._dtype = self.input.dtype
121         if self.gpu is None:
122             self._gpu = self.input.op.gpu
123 
124         # FIXME: remove when cupy supports other dtypes
125         if self._gpu and self._dtype not in (np.float32, np.float64):
126             raise NotImplementedError('Sparse tensor on GPU only supports float32 and float64')
127 
128 
129 class TensorFetch(TensorNoInput):
130     _op_type_ = OperandDef.FETCH
131 
132     def __init__(self, dtype=None, **kw):
133         super(TensorFetch, self).__init__(_dtype=dtype, **kw)
134 
135     @classmethod
136     def tile(cls, op):
137         raise NotImplementedError('Fetch tile cannot be handled by operand itself')
138
```
--- 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/mars/tensor/expressions/datasource/core.py b/mars/tensor/expressions/datasource/core.py
--- a/mars/tensor/expressions/datasource/core.py
+++ b/mars/tensor/expressions/datasource/core.py
@@ -72,6 +72,14 @@
     def calc_shape(self, *inputs_shape):
         return self.outputs[0].shape
 
+    def _new_chunks(self, inputs, shape, **kw):
+        self.params['shape'] = shape  # set shape to make the operand key different
+        return super(TensorNoInput, self)._new_chunks(inputs, shape, **kw)
+
+    def _new_entities(self, inputs, shape, **kw):
+        self.params['shape'] = shape  # set shape to make the operand key different
+        return super(TensorNoInput, self)._new_entities(inputs, shape, **kw)
+
     def __call__(self, shape, chunk_size=None):
         shape = normalize_shape(shape)
         return self.new_tensor(None, shape, raw_chunk_size=chunk_size)

{"golden_diff": "diff --git a/mars/tensor/expressions/datasource/core.py b/mars/tensor/expressions/datasource/core.py\n--- a/mars/tensor/expressions/datasource/core.py\n+++ b/mars/tensor/expressions/datasource/core.py\n@@ -72,6 +72,14 @@\n     def calc_shape(self, *inputs_shape):\n         return self.outputs[0].shape\n \n+    def _new_chunks(self, inputs, shape, **kw):\n+        self.params['shape'] = shape  # set shape to make the operand key different\n+        return super(TensorNoInput, self)._new_chunks(inputs, shape, **kw)\n+\n+    def _new_entities(self, inputs, shape, **kw):\n+        self.params['shape'] = shape  # set shape to make the operand key different\n+        return super(TensorNoInput, self)._new_entities(inputs, shape, **kw)\n+\n     def __call__(self, shape, chunk_size=None):\n         shape = normalize_shape(shape)\n         return self.new_tensor(None, shape, raw_chunk_size=chunk_size)\n", "issue": "[BUG][TENSOR] TensorZeros generated in TensorDiag.tile have the same key even if they have different shapes\n<!--\r\nThank you for your contribution!\r\n\r\nPlease review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.\r\n-->\r\n\r\n**Describe the bug**\r\n\r\n`TensorDiag.tile` may generate chunks whose op is TensorZeros, they will have the same key even if their shape are different.\r\n\r\n**To Reproduce**\r\n\r\n```python\r\nIn [94]: a = mt.arange(5, chunk_size=2)                                         \r\n\r\nIn [95]: d = mt.diag(a)                                                         \r\n\r\nIn [96]: d.tiles()                                                              \r\nOut[96]: <mars.tensor.core.Tensor at 0x136df1dc8>\r\n\r\nIn [99]: d.chunks[1].shape, d.chunks[1].op.key                                  \r\nOut[99]: ((2, 2), 'd6d8d339b2cbac64ae65cb29ff3f6785')\r\n\r\nIn [100]: d.chunks[2].shape, d.chunks[1].op.key                                 \r\nOut[100]: ((2, 1), 'd6d8d339b2cbac64ae65cb29ff3f6785')\r\n```\r\n\r\n**Expected behavior**\r\n\r\nChunks of TensorZeros should have different keys if their shapes are different, this is rightly handled for TensorZeros.tile, but when the TensorZeros op is created manually, this bug could happen.\r\n\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# Copyright 1999-2018 Alibaba Group Holding Ltd.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport itertools\n\nimport numpy as np\n\nfrom .... import opcodes as OperandDef\nfrom ....operands import DataSource\nfrom ....compat import izip\nfrom ....config import options\nfrom ..utils import normalize_shape, decide_chunk_sizes\nfrom ..core import TensorOperandMixin\n\n\nclass TensorDataSource(DataSource, TensorOperandMixin):\n    \"\"\"\n    Tensor data source base class, provide universal tile logic,\n    subclass can overwrite tile method.\n    \"\"\"\n\n    __slots__ = ()\n\n    def to_chunk_op(self, *args):\n        chunk_shape, idx, chunk_size = args\n        chunk_op = self.copy().reset_key()\n        chunk_op.params = {'size': chunk_shape, 'index': idx}  # to make op key different\n        return chunk_op\n\n    @classmethod\n    def tile(cls, op):\n        tensor = op.outputs[0]\n\n        chunk_size = tensor.params.raw_chunk_size or options.tensor.chunk_size\n        chunk_size = decide_chunk_sizes(tensor.shape, chunk_size, tensor.dtype.itemsize)\n        chunk_size_idxes = (range(len(size)) for size in chunk_size)\n\n        out_chunks = []\n        for chunk_shape, chunk_idx in izip(itertools.product(*chunk_size),\n                                           itertools.product(*chunk_size_idxes)):\n            chunk_op = op.to_chunk_op(chunk_shape, chunk_idx, chunk_size)\n            out_chunk = chunk_op.new_chunk(None, chunk_shape, index=chunk_idx)\n            out_chunks.append(out_chunk)\n\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, tensor.shape, chunks=out_chunks, nsplits=chunk_size)\n\n\nclass TensorNoInput(TensorDataSource):\n    \"\"\"\n    Tensor operand with no inputs.\n    \"\"\"\n\n    def check_inputs(self, inputs):\n        # no inputs\n        if inputs and len(inputs) > 0:\n            raise ValueError(\"Tensor data source has no inputs\")\n\n    def calc_shape(self, *inputs_shape):\n        return self.outputs[0].shape\n\n    def __call__(self, shape, chunk_size=None):\n        shape = normalize_shape(shape)\n        return self.new_tensor(None, shape, raw_chunk_size=chunk_size)\n\n\nclass TensorHasInput(TensorDataSource):\n    \"\"\"\n    Tensor operand with a single input.\n    \"\"\"\n\n    @property\n    def input(self):\n        return self._input\n\n    def check_inputs(self, inputs):\n        # no inputs\n        if len(inputs) != 1:\n            raise ValueError(\"Tensor can only have 1 input\")\n\n    def _set_inputs(self, inputs):\n        super(TensorHasInput, self)._set_inputs(inputs)\n        self._input = self._inputs[0]\n\n    @classmethod\n    def tile(cls, op):\n        out_chunks = []\n        for c in op.input.chunks:\n            out_chunk = op.copy().reset_key().new_chunk([c], c.shape, index=c.index)\n            out_chunks.append(out_chunk)\n\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, op.outputs[0].shape, chunks=out_chunks,\n                                  nsplits=op.input.nsplits)\n\n    def calc_shape(self, *inputs_shape):\n        return inputs_shape[0]\n\n    def __call__(self, a):\n        return self.new_tensor([a], a.shape)\n\n\nclass TensorLike(TensorHasInput):\n    def _set_inputs(self, inputs):\n        super(TensorLike, self)._set_inputs(inputs)\n        if self.dtype is None:\n            self._dtype = self.input.dtype\n        if self.gpu is None:\n            self._gpu = self.input.op.gpu\n\n        # FIXME: remove when cupy supports other dtypes\n        if self._gpu and self._dtype not in (np.float32, np.float64):\n            raise NotImplementedError('Sparse tensor on GPU only supports float32 and float64')\n\n\nclass TensorFetch(TensorNoInput):\n    _op_type_ = OperandDef.FETCH\n\n    def __init__(self, dtype=None, **kw):\n        super(TensorFetch, self).__init__(_dtype=dtype, **kw)\n\n    @classmethod\n    def tile(cls, op):\n        raise NotImplementedError('Fetch tile cannot be handled by operand itself')\n", "path": "mars/tensor/expressions/datasource/core.py"}], "after_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# Copyright 1999-2018 Alibaba Group Holding Ltd.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport itertools\n\nimport numpy as np\n\nfrom .... import opcodes as OperandDef\nfrom ....operands import DataSource\nfrom ....compat import izip\nfrom ....config import options\nfrom ..utils import normalize_shape, decide_chunk_sizes\nfrom ..core import TensorOperandMixin\n\n\nclass TensorDataSource(DataSource, TensorOperandMixin):\n    \"\"\"\n    Tensor data source base class, provide universal tile logic,\n    subclass can overwrite tile method.\n    \"\"\"\n\n    __slots__ = ()\n\n    def to_chunk_op(self, *args):\n        chunk_shape, idx, chunk_size = args\n        chunk_op = self.copy().reset_key()\n        chunk_op.params = {'size': chunk_shape, 'index': idx}  # to make op key different\n        return chunk_op\n\n    @classmethod\n    def tile(cls, op):\n        tensor = op.outputs[0]\n\n        chunk_size = tensor.params.raw_chunk_size or options.tensor.chunk_size\n        chunk_size = decide_chunk_sizes(tensor.shape, chunk_size, tensor.dtype.itemsize)\n        chunk_size_idxes = (range(len(size)) for size in chunk_size)\n\n        out_chunks = []\n        for chunk_shape, chunk_idx in izip(itertools.product(*chunk_size),\n                                           itertools.product(*chunk_size_idxes)):\n            chunk_op = op.to_chunk_op(chunk_shape, chunk_idx, chunk_size)\n            out_chunk = chunk_op.new_chunk(None, chunk_shape, index=chunk_idx)\n            out_chunks.append(out_chunk)\n\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, tensor.shape, chunks=out_chunks, nsplits=chunk_size)\n\n\nclass TensorNoInput(TensorDataSource):\n    \"\"\"\n    Tensor operand with no inputs.\n    \"\"\"\n\n    def check_inputs(self, inputs):\n        # no inputs\n        if inputs and len(inputs) > 0:\n            raise ValueError(\"Tensor data source has no inputs\")\n\n    def calc_shape(self, *inputs_shape):\n        return self.outputs[0].shape\n\n    def _new_chunks(self, inputs, shape, **kw):\n        self.params['shape'] = shape  # set shape to make the operand key different\n        return super(TensorNoInput, self)._new_chunks(inputs, shape, **kw)\n\n    def _new_entities(self, inputs, shape, **kw):\n        self.params['shape'] = shape  # set shape to make the operand key different\n        return super(TensorNoInput, self)._new_entities(inputs, shape, **kw)\n\n    def __call__(self, shape, chunk_size=None):\n        shape = normalize_shape(shape)\n        return self.new_tensor(None, shape, raw_chunk_size=chunk_size)\n\n\nclass TensorHasInput(TensorDataSource):\n    \"\"\"\n    Tensor operand with a single input.\n    \"\"\"\n\n    @property\n    def input(self):\n        return self._input\n\n    def check_inputs(self, inputs):\n        # no inputs\n        if len(inputs) != 1:\n            raise ValueError(\"Tensor can only have 1 input\")\n\n    def _set_inputs(self, inputs):\n        super(TensorHasInput, self)._set_inputs(inputs)\n        self._input = self._inputs[0]\n\n    @classmethod\n    def tile(cls, op):\n        out_chunks = []\n        for c in op.input.chunks:\n            out_chunk = op.copy().reset_key().new_chunk([c], c.shape, index=c.index)\n            out_chunks.append(out_chunk)\n\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, op.outputs[0].shape, chunks=out_chunks,\n                                  nsplits=op.input.nsplits)\n\n    def calc_shape(self, *inputs_shape):\n        return inputs_shape[0]\n\n    def __call__(self, a):\n        return self.new_tensor([a], a.shape)\n\n\nclass TensorLike(TensorHasInput):\n    def _set_inputs(self, inputs):\n        super(TensorLike, self)._set_inputs(inputs)\n        if self.dtype is None:\n            self._dtype = self.input.dtype\n        if self.gpu is None:\n            self._gpu = self.input.op.gpu\n\n        # FIXME: remove when cupy supports other dtypes\n        if self._gpu and self._dtype not in (np.float32, np.float64):\n            raise NotImplementedError('Sparse tensor on GPU only supports float32 and float64')\n\n\nclass TensorFetch(TensorNoInput):\n    _op_type_ = OperandDef.FETCH\n\n    def __init__(self, dtype=None, **kw):\n        super(TensorFetch, self).__init__(_dtype=dtype, **kw)\n\n    @classmethod\n    def tile(cls, op):\n        raise NotImplementedError('Fetch tile cannot be handled by operand itself')\n", "path": "mars/tensor/expressions/datasource/core.py"}]}