prompt: 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()
  
  ```
  
golden_diff: 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)
  
verification_info: {"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"}]}