# 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[0] = 10.; d[1] = 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)); }`

## Adding matrices

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 `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); }`