To better understand the use of taps , you must first understand how scan uses the outputs_info argument outputs_info general and how the provided values for it ( initial , to be precise) change the nature of the result.
scan expects you to specify the type of output that you expect from this operation (unless, of course, you have any initial values, and just specify None , in which case it will start the first round { step }, and the output will not be sent as a parameter in fn in subsequent rounds).
So scan used to iterate over the provided sequences . This means that with step n (and without taps specified for sequences or outputs_info ), this fn will be applied to the nth elements of each of the sequences along with the output (s) generated by the previous (n-1 th) step . Therefore, the default value of taps for sequences is 0 , and for outputs_info is -1 .
Another way to look at this would be to look at all the sequences consisting of slices in their first dimension. Thus, for a certain step, the current slice of sequence(s) and the output slice of the previous step are passed to fn , and the calculated output is added to the results as a new slice, which will then be used for the next step . Obviously, each of the output slices will have the same shape. And if you provide an initial slice as part of outputs_info , then it should also have the same form as when creating the fn application. In your example, if output_info=[dict(initial=x0)] , it will take [2, 3] as the first slice and use it for the first step as the argument a1 to addf .
But often when processing signals (and elsewhere) you need more than just the last data points in time as causal information. Here I used time as a way of representing steps . Anyway, here taps is useful and helps to specify exactly which data points from sequences and results should be used for the current step . In your example, this means that for the current step third last output should be passed to fn .
And here you need to be careful when describing initial for outputs_info . Since scanning first breaks the initial value into slices according to the size of the first. Then the first fragment from this set of slices will be considered the earliest fragment (the third in your example) required to calculate the output of the first step .. p>
Suppose in your example taps=[-2] and input = [2, 3] . In this case, the scan will split the input into slices and use the first slice (here the value is 2) as an argument a1 - addf . The resulting value of 4 will be added to the output, and for the next step, the slices will include [2, 3, 4], of which the value 3 is in the second last (-2) tap. And so on. However, with taps=[-3] and the same input , there is no single value that resembles the saying that you collected values at times (t-3) and (t-2), but did not collect the value in (t- one).
So, if you think that your output has a certain shape, and you need several output taps beyond -1, then the initial value should be a list of elements of the desired output form and have exactly as many elements as you need to extract the earliest slice.
TL; DR: In your example, if you want to get 2d vectors from each step and use taps=[-3] , then input should be a list of 3 such 2d vectors. If you want to get unambiguous results, then input should be a list with 3 integers. A list of 2 integers makes no sense at all in this context. It would be reasonable if taps is -2 or -1 or [-2, -1] .