mathrustlinear-algebrarust-ndarray

Use reduce to perform sequential kronecker product to multiple arrays


I am trying to use Rust to perform sequential kronecker product to multiple matrices, i.e. I want to do

enter image description here

In python, I know I can do

import numpy as np
from functools import reduce

X = np.array([[0, 1], [1, 0])
matrices = [X for _ in range(8)]
product = reduce(np.kron, matrices)

and get the desired result. How to do the same in Rust?

My Rust code look like the following for now:

use ndarray::{array, ArrayBase, OwnedRepr, Dim};
use ndarray::linalg::kron;
use num::complex::Complex64 as Complex;



fn X() -> ArrayBase<OwnedRepr<Complex>, Dim<[usize; 2]>> {
    array![
        [Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)],
        [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]
    ]
}

fn main() {    
    let mut matrices = Vec::new();
    for _ in 0..8 {
        matrices.push(X());
    }
    let product = matrices
        .iter()
        .reduce(|g1, g2| kron(g1, g2));

}

Here's the error matrix I got:

error

Here's the things I don't understand:

How can I achieve this?


Solution

  • Unlike Python, in Rust you are required to think about ownership.

    ndarray includes multiple version of arrays: Array, ArrayView, ArrayViewMut, ArcArray and CowArray. They are analogous to the many ways one can view a data in Rust: owned (T or Box<T>) - this is Array which is an alias of ArrayBase<OwnedRepr>, shared reference (&T) - ArrayView, mutable reference (&mut T) - ArrayViewMut, shared ownership (Rc<T> and Arc<T>) - ArcArray, and copy-on-write (Cow<T>) - CowArray.

    iter() gives you an iterator over references to the elements, that is, &Array, while kron() returns owned arrays - Array. But reduce() requires both the inputs and the output to be of the same type, because the output becomes the input of the next item. So you need to unify them.

    There are multiple ways you can do that. For example, by using into_iter() instead of iter():

    let product = matrices.into_iter().reduce(|g1, g2| kron(&g1, &g2));
    

    Or by cloning the first matrix and using fold():

    fn kron_many(matrices: &[Array2<Complex>]) -> Option<Array2<Complex>> {
        let (first, rest) = matrices.split_first()?;
        let first = first.clone();
        Some(rest.iter().fold(first, |g1, g2| kron(&g1, g2)))
    }
    
    fn main() {
        let mut matrices = Vec::new();
        for _ in 0..8 {
            matrices.push(X());
        }
        let product = kron_many(&matrices);
    }
    

    Or without cloning, by using CowArray to represent a maybe-owned array:

    let product = matrices
        .iter()
        .map(|matrix| CowArray::from(matrix))
        .reduce(|g1, g2| kron(&g1, &g2).into());