Linear Algebra

Vector Sum The ndarray crate supports a number of ways to create arrays -- this recipe focuses on creating `ndarray::Array`s from `std::Vec` via `from_vec`. Adding two arrays together is no different than adding two numbers together. Using the `&` operand on the arrays within an arithmetic operation prevents the operation from consuming the arrays. Without `&`, the arrays are consumed.

In the first example, arrays `a` and `b` are moved in the let-statement `z = a + b`. In the second example, the arrays `c` and `d` are not moved and instead, a new array is created for `w`. Updating either of `c` or `d` after the vector sum has no effect the value of `w`. Additionally, while printing `c` works as expected, it would be an error to print `b` due to the move. See Binary Operators With Two Arrays for additional detail.

``````extern crate ndarray;
use ndarray::Array;

fn main() {
let a = Array::from_vec(vec![1., 2., 3., 4., 5.]);
let b = Array::from_vec(vec![5., 4., 3., 2., 1.]);
let mut c = Array::from_vec(vec![1., 2., 3., 4., 5.]);
let mut d = Array::from_vec(vec![5., 4., 3., 2., 1.]);

let z = a + b;
let w =  &c + &d;

let epsilon = 1e-8;
for elem in z.iter() {
let diff: f32 = *elem - 6.;
assert!(diff.abs() < epsilon);
}

println!("c = {}", c);
c = 10.;
d = 10.;

for elem in w.iter() {
let diff: f32 = *elem - 6.;
assert!(diff.abs() < epsilon);
}

}
``````

Vector Norm This recipe demonstrates use of the `Array1` type, `ArrayView1` type, `fold` method, and `dot` method in computing the l1 and l2 norms of a given vector. The l2 norm calculation is the simpler of the two, as it is the square root of the dot product of a vector with itself, shown in the function `l2_norm`. The l1 norm, shown in the function `l1_norm`, is computed by a `fold` operation that sums the absolute values of the elements. (This could also be performed with `x.mapv(f64::abs).scalar_sum()`, but that would allocate a new array for the result of the `mapv`.)

Note that both `l1_norm` and `l2_norm` take the `ArrayView1` type. This recipe considers vector norms, so the norm functions only need to accept one dimensional views (hence `ArrayView1`). While the functions could take a parameter of type `&Array1<f64>` instead, that would require the caller to have a reference to an owned array, which is more restrictive than just having access to a view (since a view can be created from any array or view, not just an owned array). The most convenient argument type for the caller would be `&ArrayBase<S, Ix1> where S: Data`, because then the caller could use `&array` or `&view` instead of `x.view()`. If the function is part of your public API, that may be a better choice for the benefit of your users, but for internal functions, the more concise `ArrayView1<f64>` may be preferable.

``````#[macro_use(array)]
extern crate ndarray;

use ndarray::{Array1, ArrayView1};

fn l1_norm(x: ArrayView1<f64>) -> f64 {
x.fold(0., |acc, elem| acc + elem.abs())
}

fn l2_norm(x: ArrayView1<f64>) -> f64 {
x.dot(&x).sqrt()
}

fn normalize(mut x: Array1<f64>) -> Array1<f64> {
let norm = l2_norm(x.view());
x.mapv_inplace(|e| e/norm);
x
}

fn main() {
let x = array![1., 2., 3., 4., 5.];
println!("||x||_2 = {}", l2_norm(x.view()));
println!("||x||_1 = {}", l1_norm(x.view()));
println!("Normalizing x yields {:?}", normalize(x));
}
``````

Creates two matrices with `ndarray::arr2` and adds them together.

``````extern crate ndarray;

use ndarray::arr2;

fn main() {
let a = arr2(&[[1, 2, 3],
[4, 5, 6]]);

let b = arr2(&[[6, 5, 4],
[3, 2, 1]]);

println!("Sum: {}", a + b);
}
``````

Multiplying matrices

Creates two matrices with `ndarray::arr2` and performs matrix multiplication on them with `ndarray::ArrayBase::dot`.

``````extern crate ndarray;

use ndarray::arr2;

fn main() {
let a = arr2(&[[1, 2, 3],
[4, 5, 6]]);

let b = arr2(&[[6, 3],
[5, 2],
[4, 1]]);

println!("{}", a.dot(&b));
}
``````

Multiply a scalar with a vector with a matrix

Creates a 1-D array (vector) with `ndarray::arr1` and a 2-D array (matrix) with `ndarray::arr2`. First, a scalar is multiplied by the vector to get another vector. Then, the matrix is multiplied by the new vector with `ndarray::Array2::dot`. (`dot` performs matrix multiplication, while the `*` operator performs element-wise multiplication.) In `ndarray`, 1-D arrays can be interpreted as either row or column vectors depending on context. If representing the orientation of a vector is important, a 2-D array with one row or one column must be used instead. In this example, the vector is a 1-D array on the right-hand side, so `dot` handles it as a column vector.

``````extern crate ndarray;

use ndarray::{arr1, arr2, Array1};

fn main() {
let scalar = 4;

let vector = arr1(&[1, 2, 3]);

let matrix = arr2(&[[4, 5, 6],
[7, 8, 9]]);

let new_vector: Array1<_> = scalar * vector;
println!("{}", new_vector);

let new_matrix = matrix.dot(&new_vector);
println!("{}", new_matrix);
}
``````

Invert matrix

Creates a 3x3 matrix with `nalgebra::Matrix3` and inverts it, if possible.

``````extern crate nalgebra;

use nalgebra::Matrix3;

fn main() {
let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0);
println!("m1 = {}", m1);
match m1.try_inverse() {
Some(inv) => {
println!("The inverse of m1 is: {}", inv);
}
None => {
println!("m1 is not invertible!");
}
}
}
``````