Convolution entirely with iterators?

Is there a way to get rid of this for loop and do the entire operation with one line? Playground link.

FYI, I think you wanted 0..11 (0 through 10).

The direct approach would be as follows:

fn main() {

    let iny1:[i32;5] =  [1,2,3,4,5];
    let iny2:[i32;15] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

    let outy2: Vec<i32> = (0..11).map(|i| {
        iny2.iter().skip(i).zip(&iny1).map(|(x,y)|x*y).sum()
    }).collect();

     for k in outy2.iter() {
         println!("{:9}",k);
     }
}

However, I recommend using windows. This is actually how I caught that bug (never mess with indices directly unless you absolutely have to).

fn main() {

    let iny1:[i32;5] =  [1,2,3,4,5];
    let iny2:[i32;15] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

    let outy2: Vec<i32> = iny2.windows(iny1.len()).map(|window| {
        window.iter().zip(&iny1).map(|(x,y)|x*y).sum()
    }).collect();

     for k in outy2.iter() {
         println!("{:9}",k);
     }
}

Thank you! I was sure the right answer would have indicies but I didn't see windows in the doc. I lobe the rust community.

1 Like

For others interested, here it is in the docs: slice - Rust

If I scale this up to signal processing sizes (1024 and 1024^2 instead of 5 and 15) I get a trait bounds error. Looks like std::iter::Iterator is limited to 32 elements. Is there a way around this?

That's a problem with arrays -- we don't have integer generics yet, so most of its traits are manually unrolled up to 32 elements. In this case you're actually missing IntoIterator. You can get around this by manually slicing it, zip(&iny1[..]).

But as you get to larger sizes, you're going to run into trouble holding such large data on the stack, and a Vec will probably serve you better.

Thank you, the vector worked and also somehow nudged the autovectorizer into using SSE.

1 Like