2

I wanted to perform a loop operation that would do the following operation (operations must be element by element).

let mut spec = array![] // matrix mxn
let exponencial = array![] // 1xn

for k in 0..N {
    spectrum [k, ..] = spectrum [k, ..] * exponential;
}

How could I implement this operation correctly and efficiently? My intention is to implement it in a loop to compare with the implementation with other languages. Do I have to use some intermediate temporary array?

Attempt 1

use ndarray::prelude::*; // 0.13.1

fn main() {
    // All rows in same time
    let mut spec = array![[1, 2, 3, 4], [5, 6, 7, 8]]; // mxn
    let exponencial = array![[1, 1, 1, 1]]; // matrix 1xn

    let _result = spec.assign(&(&spec * &exponencial));
    // or spec = &spec*&exponencial;

    //  In the form of a loop. one row each iteration.
    // This is the implementation that I want to make to be able
    // to implement it to be able to compare with other languages
    // 1)
    let mut spec = array![[1, 2, 3, 4], [5, 6, 7, 8]]; // mxn
    let exponencial = array![[1, 1, 1, 1]]; // matrix 1xn
    spec.slice_mut(s![0, ..])
        .assign(&(&(spec.row(0)) * &exponencial.row(0)));
}

playground

error[E0502]: cannot borrow `spec` as immutable because it is also borrowed as mutable
  --> src/main.rs:18:21
   |
17 |     spec.slice_mut(s![0, ..])
   |     ---- mutable borrow occurs here
18 |         .assign(&(&(spec.row(0)) * &exponencial.row(0)));
   |          ------     ^^^^ immutable borrow occurs here
   |          |
   |          mutable borrow later used by call

Attempt 2

use ndarray::prelude::*; // 0.13.1

fn main() {
    // All rows in same time
    let mut spec = array![[1, 2, 3, 4], [5, 6, 7, 8]]; // mxn
    let exponencial = array![[1, 1, 1, 1]]; // matrix 1xn

    let result = spec.assign(&(&spec * &exponencial));
    // or spec = &spec*&exponencial;

    //  In the form of a loop. one row each iteration.
    // This is the implementation that I want to make to be able
    // to implement it to be able to compare with other languages
    // 2)
    let mut spec = array![[1, 2, 3, 4], [5, 6, 7, 8]]; // mxn
    let exponencial = array![[1, 1, 1, 1]]; // matrix 1xn
    spec.row_mut(0) = &spec.row(0) * &exponencial.row(0);
}

playground

error[E0308]: mismatched types
  --> src/main.rs:17:23
   |
17 |     spec.row_mut(0) = &spec.row(0) * &exponencial.row(0);
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `ndarray::ViewRepr`, found struct `ndarray::OwnedRepr`
   |
   = note: expected struct `ndarray::ArrayBase<ndarray::ViewRepr<&mut {integer}>, _>`
              found struct `ndarray::ArrayBase<ndarray::OwnedRepr<{integer}>, _>`

error[E0070]: invalid left-hand side of assignment
  --> src/main.rs:17:21
   |
17 |     spec.row_mut(0) = &spec.row(0) * &exponencial.row(0);
   |     --------------- ^
   |     |
   |     cannot assign to this expression
0

2 Answers 2

2

In addition to bnaecker's answer, if you want to implement it in a loop you could iterate along the axis using axis_iter_mut():

use ndarray::prelude::*;

fn main() {
    let mut spec = array![
        [1, 2, 3, 4],
        [5, 6, 7, 8]
    ];
    
    let expo = array![2, 2, 2, 2];
    
    for mut row in spec.axis_iter_mut(Axis(0)) {
        row *= &expo;
    }
    
    println!("{}", spec);
}
[[2, 4, 6, 8],
 [10, 12, 14, 16]]
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. This solution is approximately as fast as direct multiplication of all the rows at once. ~140ns for spec*expo vs ~140ns for for mut row in spec.axis_iter_mut(Axis(0)) { row *= &expo; } Note: Using the same array of the post examples. Note2: I used criterion to measure performance.
2

I'm assuming that the first (pseudocode) loop you wrote is for k in 0..m, given that the size of your matrix is m-by-n and you want to apply this operation to each row.

If that's the case, then reading through the docs on binary operations and broadcasting, it looks like multiplying the arrays directly should be sufficient:

use ndarray::prelude::*; // 0.13.1

fn main() {
    // All rows in same time
    let spec = array![[1, 2, 3, 4], [5, 6, 7, 8]]; // mxn
    let exponencial = array![[2, 2, 2, 2]]; // matrix 1xn
    println!("{:?}", spec * exponencial);
}

playground

You can see the output has each row doubled:

[[2, 4, 6, 8],
 [10, 12, 14, 16]], shape=[2, 4], strides=[4, 1], layout=C (0x1), const ndim=2

This works by broadcasting, which means operating as if the 1-by-n array exponencial actually has shape m-by-n, by effectively replicating the array along the singleton dimension. This awesome trick was inspired by NumPy, which uses broadcasting heavily to compactly and efficiently express this kind of element-wise product.

4 Comments

My intention is to implement it in a loop to compare with the implementation with other languages
@Shepmaster Welp, I definitely missed that, thanks. Though I'm not sure why it must be a loop to do a cross-language comparison :)
Yeah, seems like you'd want to compare speed across languages using the "normal" mechanisms of each language. "Which is language is fastest when everyone can only go 1 km/hour" doesn't seem useful.
@Shepmaster Exactly, I'd expect the comparison to be between idiomatic implementations in each language. A for-loop in Rust and Python hardly seems fair!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.