Repository: Theano/Theano

Issue:
Opt: Reshape(arange(x)) -> arange(x) when this do not change the shapes, number of dimensions.
```
>>> import theano
>>> i=theano.tensor.iscalar()
>>> m=theano.tensor.mgrid[0:i,]
>>> f=theano.function([i], m)
>>> theano.printing.debugprint(f, print_type=True)
Reshape{1} [@A] <TensorType(int32, vector)> ''   3
 |ARange [@B] <TensorType(int32, vector)> ''   1
 | |TensorConstant{0} [@C] <TensorType(int8, scalar)>
 | |<TensorType(int32, scalar)> [@D] <TensorType(int32, scalar)>
 | |TensorConstant{1} [@E] <TensorType(int8, scalar)>
 |MakeVector [@F] <TensorType(int64, vector)> ''   2
   |Elemwise{Cast{int64}} [@G] <TensorType(int64, scalar)> ''   0
     |<TensorType(int32, scalar)> [@D] <TensorType(int32, scalar)>
```

This happen in gh-2572

Make this more general, in the sense that if the input is 1d and the output is 1d, what ever created it, remove it (add AssertOp if we can't assert that the shape are the same)



Discussion:
There should probably be an assertion by default if we cannot determine that the shapes are equal (or that the new shape is -1).

In fact, there is no optimization local_useless_reshape, only local_reshape_chain. If the shape are the same, just remove it, otherwise, should we set an assert op? I think we could keep it.


Resolving patch:
diff --git a/theano/tensor/opt.py b/theano/tensor/opt.py
--- a/theano/tensor/opt.py
+++ b/theano/tensor/opt.py
@@ -3703,6 +3703,22 @@ def f(node):
                       name='local_reshape_chain')
 
 
+@register_canonicalize
+@register_stabilize
+@gof.local_optimizer([T.Reshape])
+def local_useless_reshape(node):
+    """
+    Remove Reshape when both the input and the output have a
+    single dimension.
+
+    """
+    if isinstance(node.op, T.Reshape):
+        if (node.inputs[0].ndim == 1 and node.outputs[0].ndim == 1 and
+                node.inputs[0].broadcastable ==
+                node.outputs[0].broadcastable):
+            return [node.inputs[0]]
+
+
 @register_canonicalize
 @register_stabilize
 @gof.local_optimizer([T.Reshape])


Test patch:
diff --git a/theano/tensor/tests/test_opt.py b/theano/tensor/tests/test_opt.py
--- a/theano/tensor/tests/test_opt.py
+++ b/theano/tensor/tests/test_opt.py
@@ -3135,7 +3135,7 @@ def test_local_fill_useless():
     assert T.Alloc in ops
     f(m_, x_)
 
-    
+
 class Test_local_useless_elemwise_comparison(unittest.TestCase):
     def test_local_useless_elemwise_comparison(self):
         # TODO: test each case individually.
@@ -3171,7 +3171,7 @@ def test_local_useless_elemwise_comparison(self):
          >Sum{acc_dtype=float64} [@L] ''
          > |X[t] [@M] -> [@I]
         """
-        
+
         mode = theano.compile.get_default_mode().excluding('fusion')
         f = theano.function([X, Y], Z, mode=mode)
         theano.printing.debugprint(f, print_type=True)
@@ -3211,7 +3211,7 @@ def assert_eqs_const(self, f, val):
         assert len(elem.inputs) == 1, elem.inputs
         assert isinstance(elem.inputs[0], T.TensorConstant), elem
         assert T.extract_constant(elem.inputs[0]) == val, val
-    
+
     def assert_identity(self, f):
         topo = f.maker.fgraph.toposort()
         assert len(topo) == 1
@@ -3224,10 +3224,10 @@ def assert_identity(self, f):
     def test_inequality_with_self(self):
         x = T.scalar('x', dtype=config.floatX)
         mode = theano.compile.get_default_mode().including('local_useless_elemwise_comparison')
-        
+
         f = theano.function([x], T.lt(x, x), mode=mode)
         self.assert_eqs_const(f, 0)
-        
+
         f = theano.function([x], T.le(x, x), mode=mode)
         self.assert_eqs_const(f, 1)
 
@@ -3289,10 +3289,10 @@ def test_shape_add_inequality(self):
 
         f = theano.function([x, y], T.ge(x.shape[0]+y.shape[0], 0), mode=mode)
         self.assert_eqs_const(f, 1)
-        
+
     def test_and(self):
         mode = theano.compile.get_default_mode().including('canonicalize')
-        
+
         x = T.scalar('x', dtype='int8')
 
         f = theano.function([x], T.and_(x, 0), mode=mode)
@@ -5704,7 +5704,7 @@ def test_local_flatten_lift():
 
 class Test_Reshape(unittest.TestCase):
     def setUp(self):
-        self.mode = mode_opt 
+        self.mode = mode_opt
         self.op = tensor.Reshape
 
     def test_local_reshape(self):
@@ -5716,6 +5716,16 @@ def test_local_reshape(self):
         assert sum(isinstance(node.op, self.op) for node in topo) == 1
 
 
+def test_local_useless_reshape():
+    mode = theano.compile.get_default_mode().including(
+            'local_useless_reshape')
+    i = T.iscalar('i')
+    m = theano.tensor.mgrid[0:i,]
+    f = theano.function([i], m, mode=mode)
+    topo = f.maker.fgraph.toposort()
+    assert not any(isinstance(n.op, tensor.basic.Reshape) for n in topo)
+
+
 def test_local_reshape_lift():
     x = tensor.tensor4()
     out = T.exp(x).reshape([x.size])