Repository: Theano/Theano

Issue:
Incorrect seq slice in scan with one step.
When using scan function, if a seq use multiple taps with all tap above 0, and n_steps equals 1 which will cause Theano to use recurrent function directly instead of using scan loop, the recurrent function will receive wrong seq slice.
Test code of one step:
```python
def test_mintap_onestep():
    seq = theano.tensor.ivector("seq")
    seq_info = dict(input=seq, taps=[2])

    def accum(seq_t, prev_sum):
        new_sum = prev_sum + seq_t
        return new_sum

    rs, updates = theano.scan(fn=accum,
                              sequences=seq_info,
                              outputs_info=0,
                              n_steps=1)

    f = theano.function(inputs=[seq],
                        outputs=rs)
    _seq = np.arange(20).astype("int32")
    _sum = f(_seq)
    print("sum %f" % _sum)
    assert _sum == 2
```
Will print:
>  sum 0.000000

and raise a AssertError.

Test code of two step:
```python
def test_mintap_twostep():
    seq = theano.tensor.fvector("seq")
    seq_info = dict(input=seq, taps=[2])

    def accum(seq_t, prev_sum):
        new_sum = prev_sum + seq_t
        return new_sum

    rs, updates = theano.scan(fn=accum,
                              sequences=seq_info,
                              outputs_info=np.float32(0),
                              n_steps=2)

    f = theano.function(inputs=[seq],
                        outputs=rs)
    _seq = np.arange(20).astype("float32")
    _sum = f(_seq)
    _sum = _sum[0]
    print("sum %f" % _sum)
    assert _sum == 2
```
That's will be ok.


Discussion:

Resolving patch:
diff --git a/theano/scan_module/scan.py b/theano/scan_module/scan.py
--- a/theano/scan_module/scan.py
+++ b/theano/scan_module/scan.py
@@ -490,6 +490,12 @@ def wrap_into_list(x):
             # go through the indicated slice
             mintap = np.min(seq['taps'])
             maxtap = np.max(seq['taps'])
+            # We cut the sequence such that seq[i] to correspond to
+            # seq[i-k]. For the purposes of cutting the sequences, we
+            # need to pretend tap 0 is used to avoid cutting the sequences
+            # too long if the taps are all lower or all higher than 0.
+            maxtap_proxy = max(maxtap, 0)
+            mintap_proxy = min(mintap, 0)
             for k in seq['taps']:
                 # create one slice of the input
                 # Later on, if we decide not to use scan because we are
@@ -500,9 +506,9 @@ def wrap_into_list(x):
 
                 # If not we need to use copies, that will be replaced at
                 # each frame by the corresponding slice
-                actual_slice = seq['input'][k - mintap]
+                actual_slice = seq['input'][k - mintap_proxy]
                 _seq_val = tensor.as_tensor_variable(seq['input'])
-                _seq_val_slice = _seq_val[k - mintap]
+                _seq_val_slice = _seq_val[k - mintap_proxy]
                 nw_slice = _seq_val_slice.type()
 
                 # Try to transfer test_value to the new variable
@@ -529,12 +535,6 @@ def wrap_into_list(x):
                         nw_name = seq['input'].name + '[t%d]' % k
                     nw_slice.name = nw_name
 
-                # We cut the sequence such that seq[i] to correspond to
-                # seq[i-k]. For the purposes of cutting the sequences, we
-                # need to pretend tap 0 is used to avoid cutting the sequences
-                # too long if the taps are all lower or all higher than 0.
-                maxtap_proxy = max(maxtap, 0)
-                mintap_proxy = min(mintap, 0)
                 start = (k - mintap_proxy)
                 nw_name = None
                 if k == maxtap_proxy:


Test patch:
diff --git a/theano/scan_module/tests/test_scan.py b/theano/scan_module/tests/test_scan.py
--- a/theano/scan_module/tests/test_scan.py
+++ b/theano/scan_module/tests/test_scan.py
@@ -5738,3 +5738,23 @@ def accum(prev_value, step):
         outputs=rs)
 
     _sum, total_steps = f(100, 100)
+
+def test_mintap_onestep():
+    seq = theano.tensor.ivector("seq")
+    seq_info = dict(input=seq, taps=[2])
+
+    def accum(seq_t, prev_sum):
+        new_sum = prev_sum + seq_t
+        return new_sum
+
+    rs, updates = theano.scan(fn=accum,
+                              sequences=seq_info,
+                              outputs_info=0,
+                              n_steps=1)
+
+    f = theano.function(inputs=[seq],
+                        outputs=rs)
+    _seq = np.arange(20).astype("int32")
+    _sum = f(_seq)
+    print("sum %f" % _sum)
+    assert _sum == 2