Multidimensional arrays in Rust like Julia?

Hello all

So I'm coming from a Numpy/Julia background and I'm very new to Rust. Most of my work is in writing scientific code and I was looking to Rust as a viable alternative to learning C or FORTRAN properly. I was wondering if there are ways to initialize arrays of arbitrary dimensions with either a uniform value or a random value from a given distribution. So something in Julia one could do for this is

using Distributions

myarr = rand(Normal(), 5, 5, 5)

I know about this post and this somewhat works for 2D arrays, but is there a general way to generate an N dimensional array initialized with either random numbers or a single number?

Looking around the ndarray docs, there is definitely Array::from_elem((5, 5, 5), 0.0) and Array::from_shape_fn((5, 5, 5), |_| rng.sample(StandardNormal)) (the latter type is defined in rand_distr).

Oh that definitely does the job! Thank you so much! I apologize for the simple question, I was having trouble figuring out what the correct crate was because there's also a Array2D package and navigating the documentation with the novel syntax was a bit difficult for me. Appreciate the help!

I'm a bit confused about what rng is in your example above. I'm trying to do it like so -

use rand;
use rand_distr;
use ndarray;

fn main() {
    let rng = rand::thread_rng();

    let A = ndarray::Array::from_shape_fn((5, 5, 5,), |_| rng.sample(rand_distr::StandardNormal));
    println!("{:?}", A);
}

However it throws an error saying no method sample found. I also tried it like so -

cocoafedora@fedora:~/Documents/Code/RustLearning/random_arrays$ cargo run
   Compiling random_arrays v0.1.0 (/home/cocoafedora/Documents/Code/RustLearning/random_arrays)
error[E0061]: this function takes 2 arguments but 1 argument was supplied
   --> src/main.rs:8:59
    |
8   |     let A = ndarray::Array::from_shape_fn((5, 5, 5,), |_| rand::Rng::sample(rand_distr::StandardNormal));
    |                                                           ^^^^^^^^^^^^^^^^^ -------------------------- supplied 1 argument
    |                                                           |
    |                                                           expected 2 arguments
    |
note: associated function defined here
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/rng.rs:152:8
    |
152 |     fn sample<T, D: Distribution<T>>(&mut self, distr: D) -> T {
    |        ^^^^^^

For more information about this error, try `rustc --explain E0061`.
error: could not compile `random_arrays` due to previous error

I'm a bit confused how to resolve this issue.

The code in your snippet and error do not match.

rng is supposed to be a random number generator from the rand crate; rand::thread_rng() should do the job. The sample() method is defined by trait rang::Rng so you'll need to import that trait explicitly if you want to be able to use it.

Hello

Sorry for the late reply. So I'm a bit confused. I used this from the rust book and it worked. But when I modify the code so that the main function doesn't return anything it stops working. Could somebody tell me what's happening? Really appreciate all the help.

Code without main function's return value changed -

use rand_distr::{Distribution, Normal, NormalError};
use rand::thread_rng;
use ndarray::{Array};

fn main() -> Result<(), NormalError> {
    let mut rng = thread_rng();
    let normal = Normal::new(0., 1.0)?;

    let a = Array::from_shape_fn((5, 5, 5), |_| normal.sample(&mut rng));

    println!("{:?}", a);
    Ok(())
}

Code with the main function's return type removed -

use rand_distr::{Distribution, Normal, NormalError};
use rand::thread_rng;
use ndarray::{Array};

fn main() {
    let mut rng = thread_rng();
    let normal = Normal::new(0., 1.0)?;

    let a = Array::from_shape_fn((5, 5, 5), |_| normal.sample(&mut rng));

    println!("{:?}", a);
}

Corresponding error -

cocoafedora@fedora:~/Documents/Code/RustLearning/random_arrays$ cargo run
   Compiling random_arrays v0.1.0 (/home/cocoafedora/Documents/Code/RustLearning/random_arrays)
warning: unused import: `NormalError`
 --> src/main.rs:1:40
  |
1 | use rand_distr::{Distribution, Normal, NormalError};
  |                                        ^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
  --> src/main.rs:7:38
   |
5  | / fn main() {
6  | |     let mut rng = thread_rng();
7  | |     let normal = Normal::new(0., 1.0)?;
   | |                                      ^ cannot use the `?` operator in a function that returns `()`
8  | |
...  |
11 | |     println!("{:?}", a);
12 | | }
   | |_- this function should return `Result` or `Option` to accept `?`
   |
   = help: the trait `FromResidual<Result<Infallible, NormalError>>` is not implemented for `()`

error[E0283]: type annotations needed for `rand_distr::Normal<{float}>`
   --> src/main.rs:7:18
    |
7   |     let normal = Normal::new(0., 1.0)?;
    |         ------   ^^^^^^^^^^^ cannot infer type for type `{float}`
    |         |
    |         consider giving `normal` the explicit type `rand_distr::Normal<{float}>`, where the type parameter `{float}` is specified
    |
    = note: multiple `impl`s satisfying `{float}: Float` found in the `num_traits` crate:
            - impl Float for f32;
            - impl Float for f64;
note: required by a bound in `rand_distr::Normal::<F>::new`
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_distr-0.4.3/src/normal.rs:147:10
    |
147 | where F: Float, StandardNormal: Distribution<F>
    |          ^^^^^ required by this bound in `rand_distr::Normal::<F>::new`

error[E0283]: type annotations needed for `rand_distr::Normal<{float}>`
   --> src/main.rs:7:18
    |
7   |     let normal = Normal::new(0., 1.0)?;
    |         ------   ^^^^^^^^^^^ cannot infer type for type `{float}`
    |         |
    |         consider giving `normal` the explicit type `rand_distr::Normal<{float}>`, where the type parameter `{float}` is specified
    |
    = note: multiple `impl`s satisfying `StandardNormal: Distribution<{float}>` found in the `rand_distr` crate:
            - impl Distribution<f32> for StandardNormal;
            - impl Distribution<f64> for StandardNormal;
note: required by a bound in `rand_distr::Normal::<F>::new`
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_distr-0.4.3/src/normal.rs:147:33
    |
147 | where F: Float, StandardNormal: Distribution<F>
    |                                 ^^^^^^^^^^^^^^^ required by this bound in `rand_distr::Normal::<F>::new`

error[E0283]: type annotations needed for `rand_distr::Normal<{float}>`
   --> src/main.rs:7:18
    |
7   |     let normal = Normal::new(0., 1.0)?;
    |         ------   ^^^^^^ cannot infer type for type `{float}`
    |         |
    |         consider giving `normal` the explicit type `rand_distr::Normal<{float}>`, where the type parameter `{float}` is specified
    |
    = note: multiple `impl`s satisfying `{float}: Float` found in the `num_traits` crate:
            - impl Float for f32;
            - impl Float for f64;
note: required by a bound in `rand_distr::Normal`
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_distr-0.4.3/src/normal.rs:118:10
    |
118 | where F: Float, StandardNormal: Distribution<F>
    |          ^^^^^ required by this bound in `rand_distr::Normal`
help: consider specifying the type argument in the function call
    |
7   |     let normal = Normal::<F>::new(0., 1.0)?;
    |                        +++++

error[E0283]: type annotations needed for `rand_distr::Normal<{float}>`
   --> src/main.rs:7:18
    |
7   |     let normal = Normal::new(0., 1.0)?;
    |         ------   ^^^^^^ cannot infer type for type `{float}`
    |         |
    |         consider giving `normal` the explicit type `rand_distr::Normal<{float}>`, where the type parameter `{float}` is specified
    |
    = note: multiple `impl`s satisfying `StandardNormal: Distribution<{float}>` found in the `rand_distr` crate:
            - impl Distribution<f32> for StandardNormal;
            - impl Distribution<f64> for StandardNormal;
note: required by a bound in `rand_distr::Normal`
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_distr-0.4.3/src/normal.rs:118:33
    |
118 | where F: Float, StandardNormal: Distribution<F>
    |                                 ^^^^^^^^^^^^^^^ required by this bound in `rand_distr::Normal`
help: consider specifying the type argument in the function call
    |
7   |     let normal = Normal::<F>::new(0., 1.0)?;
    |                        +++++

error[E0283]: type annotations needed for `rand_distr::Normal<{float}>`
   --> src/main.rs:7:18
    |
7   |     let normal = Normal::new(0., 1.0)?;
    |         ------   ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type `{float}`
    |         |
    |         consider giving `normal` the explicit type `rand_distr::Normal<{float}>`, where the type parameter `{float}` is specified
    |
    = note: multiple `impl`s satisfying `{float}: Float` found in the `num_traits` crate:
            - impl Float for f32;
            - impl Float for f64;
note: required by a bound in `rand_distr::Normal`
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_distr-0.4.3/src/normal.rs:118:10
    |
118 | where F: Float, StandardNormal: Distribution<F>
    |          ^^^^^ required by this bound in `rand_distr::Normal`
help: consider specifying the type argument in the function call
    |
7   |     let normal = Normal::new(0., 1.0)::<F>?;
    |                                      +++++

error[E0283]: type annotations needed for `rand_distr::Normal<{float}>`
   --> src/main.rs:7:18
    |
7   |     let normal = Normal::new(0., 1.0)?;
    |         ------   ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type `{float}`
    |         |
    |         consider giving `normal` the explicit type `rand_distr::Normal<{float}>`, where the type parameter `{float}` is specified
    |
    = note: multiple `impl`s satisfying `StandardNormal: Distribution<{float}>` found in the `rand_distr` crate:
            - impl Distribution<f32> for StandardNormal;
            - impl Distribution<f64> for StandardNormal;
note: required by a bound in `rand_distr::Normal`
   --> /home/cocoafedora/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_distr-0.4.3/src/normal.rs:118:33
    |
118 | where F: Float, StandardNormal: Distribution<F>
    |                                 ^^^^^^^^^^^^^^^ required by this bound in `rand_distr::Normal`
help: consider specifying the type argument in the function call
    |
7   |     let normal = Normal::new(0., 1.0)::<F>?;
    |                                      +++++

Some errors have detailed explanations: E0277, E0283.
For more information about an error, try `rustc --explain E0277`.
warning: `random_arrays` (bin "random_arrays") generated 1 warning
error: could not compile `random_arrays` due to 7 previous errors; 1 warning emitted

You are using the ? operator to propagate the error from Normal::new(), which means that the enclosing function (i.e. main() in this case) must return a Result.

2 Likes

Using ? on a Result is approximately:

match result {
    Ok(v) => v,
    Err(e) => return Err(e.into()),
}

So naturally you need to be in a Result-returning function.

1 Like

Oh dear lord I'm dumb. Thank you all so much!