Module easy_ml::matrices::iterators

source ·
Expand description

Iterators over parts of a Matrix

§Examples

Extending a matrix with new columns

use easy_ml::matrices::Matrix;

// we start with some matrix where the first and second columns correspond
// to x and y points
let mut matrix = Matrix::from(vec![
    vec![ 3.0, 4.0 ],
    vec![ 8.0, 1.0 ],
    vec![ 2.0, 9.0 ]]);
// insert a third column based on the formula x * y
matrix.insert_column_with(2, matrix.column_iter(0)
    // join together the x and y columns
    .zip(matrix.column_iter(1))
    // compute the values for the new column
    .map(|(x, y)| x * y)
    // Collect into a vector so we stop immutably borrowing from `matrix`.
    // This is only neccessary when we use the data from a Matrix to modify itself,
    // because the rust compiler enforces that we do not mutably and immutably borrow
    // something at the same time. If we used data from a different Matrix to update
    // `matrix` then we could stop at map and pass the iterator directly.
    .collect::<Vec<f64>>()
    // now that the Vec created owns the data for the new column and we have stopped
    // borrowing immutably from `matrix` we turn the vec back into an iterator and
    // mutably borrow `matrix` to add the new column
    .drain(..));
assert_eq!(matrix.get(0, 2), 3.0 * 4.0);
assert_eq!(matrix.get(1, 2), 8.0 * 1.0);
assert_eq!(matrix.get(2, 2), 2.0 * 9.0);

§Matrix layout and iterator performance

Internally the Matrix type uses a flattened array with row major storage of the data. Due to CPU cache lines this means row major access of elements is likely to be faster than column major indexing, once a matrix is large enough, so you should favor iterating through each row.

use easy_ml::matrices::Matrix;
let matrix = Matrix::from(vec![
   vec![ 1, 2 ],
   vec![ 3, 4 ]]);
// storage of elements is [1, 2, 3, 4]

// row major access
for row in 0..2 {
    for column in 0..2 {
        println!("{}", matrix.get(row, column));
    }
} // -> 1 2 3 4
matrix.row_major_iter().for_each(|e| println!("{}", e)); // -> 1 2 3 4
matrix.row_major_iter().with_index().for_each(|e| println!("{:?}", e)); // -> ((0, 0), 1) ((0, 1), 2), ((1, 0), 3), ((1, 1), 4)

// column major access
for column in 0..2 {
    for row in 0..2 {
        println!("{}", matrix.get(row, column));
    }
} // -> 1 3 2 4
matrix.column_major_iter().for_each(|e| println!("{}", e)); // -> 1 3 2 4
matrix.column_major_iter().with_index().for_each(|e| println!("{:?}", e)); // // -> ((0, 0), 1), ((1, 0), 3), ((0, 1), 2), ((1, 1), 4)

Iterators are also able to elide array indexing bounds checks which may improve performance over explicit calls to get or get_reference in a loop, however you can use unsafe getters to elide bounds checks in loops as well so you are not forced to use iterators even if the checks are a performance concern.

Structs§