Repository navigation
the trait IntoIterator is not implemented for ndarray::Zip #1182
Description
Activity
Zipis not an iterator so traversal needs to be done using its for_each method or similar methods - this is by design, it cannot give good performance in a for loop.Indexes are tuples because when this feature was designed
let (i, j) = (1, 2)was valid in Rust whilelet [i, j] = [1, 2]was not - nowadays both are, so we could have picked arrays at this point.Fwiw, here are a couple of ways using
Zip's existing methods to do a filter and sum:use ndarray::prelude::*; use ndarray::Zip; fn main() { let voxels = Array3::zeros([3, 4, 5]); // Using `for_each`. let mut sum1 = 0.; Zip::indexed(&voxels).for_each(|position, &density| { if ... { sum1 += ...; } }); // Using `fold`. let sum2 = Zip::indexed(&voxels).fold(0., |acc, position, &density| { if ... { acc + ... } else { acc } }); }
this is by design, it cannot give good performance in a for loop
Why wouldn't it give good performance ? I mean the current
Zip::for_eachis already doing kind of a sequential iteration right ?
Also what's the point in it being a Parallelterator but not a SequentialIterator ? I mean for the design consistence, because what can be parallelized can easily be sequential (that's just like parallel but with only 1 thread in total)nowadays both are, so we could have picked arrays at this point.
Do you think this could change in the future ? When I'm working with algorithms generic to dimensions, I often need to do things like
Zip::indexed(&array).for_each(|position: [usize;N], field| { Vector::from(position) + offset // using the index position as a vector matrix_transform * Vector::from(position) // using the index position as a point in space position[selected_dimension] += 1 // selecting dimensions by index // etc })
and instead I have to specialize the implementation for each dimension, extracting the index coordinates one by one from the tuple
Zip::indexed(&array).for_each(|position: (usize,usize,usize), field| { // specific to dimension 3 Vector::new(position.0, position.1, position.2) + offset // this is specific to array of dimension 3 matrix_transform * Vector::newposition.0, position.1, position.2) // specific AND verbose // much more verbose when working indices if selected_dimension == 0 {position.0 += 1;} else if selected_dimension == 1 {position.1 += 1;} else if selected_dimension == 2 {position.2 += 1;} else { panic!("unsupported dimension"); } // etc })
here are a couple of ways using Zip's existing methods to do a filter and sum
That's what I ended up with ;) but I find it less ... idiomatic rust. Also I cannot combine
Zipwith the methods provided by random traits from other crates ...- locked and limited conversation to collaborators
on Jul 7, 2022
Hello dear maintainers, and thank you for your work on this crate !
I'm currently writing some voxel-processing functions and I'm facing a strange fact: the
Zipiterator-like struct ofndarraydoesn't seems to implement theIntoIteratortrait. It does implement however theIntoParallelIteratortrait, but it is of no help in my case where I want to filter and sum over the iterator elements.The probleme of not implementing the
Iteratortrait is a general concern I think, but I'm specifically trying to do something like this:None of the above ways are working because of the
IntoIteratortrait missing. I also triedArrayBase::indexed_iter()but it gives tuple indices instead of fixed-arrays (not convenient for further compuations); plus it iterates in the logical order so not the most efficient depending on the input strides.Could possibily you implement
IntoIteratorforZipas it already haveIntoParallelIterator?Edit:
in fact, both
Zip::indexedandArrayBase::indexed_iteryield a tuple for index. not sure why because the docs says I should get a[usize; 3]instead. I would be much more convenient to have such an array in my opinion ... but this is an other question.