Repository: mars-project/mars

Issue:
[BUG] Something got wrong for tensor's view behavior
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**

Something got wrong for tensor's view behavior.

**To Reproduce**

```
In [23]: a = mt.ones((5, 8), dtype=int)                                         

In [24]: b = a[:3, :2]                                                          

In [25]: b[0, 0] = 2                                                            

In [26]: b.execute()                                                            
Out[26]: 
array([[2, 1],
       [1, 1],
       [1, 1]])

In [27]: a.execute()                                                            
Out[27]: 
array([[2, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1]])

In [28]: c = b.ravel()  # create view                                                        

In [29]: c[1] = 4                                                               

In [30]: c.execute()                                                            
Out[30]: array([2, 4, 1, 1, 1, 1])

In [31]: b.execute()                                                            
Out[31]: 
array([[2, 4],
       [1, 1],
       [1, 1]])

In [32]: a.execute()   # wrong, a[1, 0] should be 4                                                         
Out[32]: 
array([[2, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1]])
```



Discussion:
`TensorIndexSetValue` on a *view* should still be a *view*. I could submit a pr for a fix.

Resolving patch:
diff --git a/mars/tensor/indexing/setitem.py b/mars/tensor/indexing/setitem.py
--- a/mars/tensor/indexing/setitem.py
+++ b/mars/tensor/indexing/setitem.py
@@ -59,6 +59,14 @@ def __call__(self, a, index, value):
         self._value = value
         return self.new_tensor(inputs, a.shape, order=a.order)
 
+    def on_output_modify(self, new_output):
+        return new_output
+
+    def on_input_modify(self, new_input):
+        new_op = self.copy().reset_key()
+        new_inputs = [new_input] + self.inputs[1:]
+        return new_op.new_tensor(new_inputs, shape=self.outputs[0].shape)
+
     @classmethod
     def tile(cls, op):
         tensor = op.outputs[0]
@@ -122,6 +130,7 @@ def _setitem(a, item, value):
     if not (np.isscalar(value) or (isinstance(value, tuple) and a.dtype.fields)):
         value = broadcast_to(value, shape).astype(a.dtype)
 
-    op = TensorIndexSetValue(dtype=a.dtype, sparse=a.issparse(), indexes=index, value=value)
+    # __setitem__ on a view should be still a view, see GH #732.
+    op = TensorIndexSetValue(dtype=a.dtype, sparse=a.issparse(), indexes=index, value=value, _create_view=a.op.create_view)
     ret = op(a, index, value)
     a.data = ret.data
diff --git a/mars/tensor/reshape/reshape.py b/mars/tensor/reshape/reshape.py
--- a/mars/tensor/reshape/reshape.py
+++ b/mars/tensor/reshape/reshape.py
@@ -297,7 +297,7 @@ def execute(cls, ctx, op):
 
         data = ctx[op.inputs[0].key]
         indices = list(np.nonzero(data))
-        nz_data = data[indices]
+        nz_data = data[tuple(indices)]
 
         for idx in range(len(old_shape)):
             indices[idx] = np.add(indices[idx], axis_offset[idx], out=indices[idx])


Test patch:
diff --git a/mars/tensor/base/tests/test_base.py b/mars/tensor/base/tests/test_base.py
--- a/mars/tensor/base/tests/test_base.py
+++ b/mars/tensor/base/tests/test_base.py
@@ -489,9 +489,15 @@ def testCreateView(self):
         # no view cuz of fancy indexing
         self.assertFalse(arr9.op.create_view)
 
+        arr9[0][0][0] = 100
+        self.assertFalse(arr9.op.create_view)
+
         arr10 = arr[:3, None, :5]
         self.assertTrue(arr10.op.create_view)
 
+        arr10[0][0][0] = 100
+        self.assertFalse(arr10.op.create_view)
+
         data = np.array([[[0], [1], [2]]])
         x = tensor(data)
 
diff --git a/mars/tensor/tests/test_core_execute.py b/mars/tensor/tests/test_core_execute.py
--- a/mars/tensor/tests/test_core_execute.py
+++ b/mars/tensor/tests/test_core_execute.py
@@ -67,6 +67,23 @@ def testViewDataOnSlice(self):
         np.testing.assert_array_equal(b.execute(), npb)
         np.testing.assert_array_equal(a.execute(), npa)
 
+    def testSetItemOnView(self):
+        a = ones((5, 8), dtype=int)
+        b = a[:3]
+        b[0, 0] = 2
+        c = b.ravel()  # create view
+        c[1] = 4
+
+        npa = np.ones((5, 8), dtype=int)
+        npb = npa[:3]
+        npb[0, 0] = 2
+        npc = npb.ravel()  # create view
+        npc[1] = 4
+
+        np.testing.assert_array_equal(a.execute(), npa)
+        np.testing.assert_array_equal(b.execute(), npb)
+        np.testing.assert_array_equal(c.execute(), npc)
+
     def testViewDataOnTranspose(self):
         data = np.random.rand(10, 20)
         a = tensor(data, chunk_size=6)