Линейная алгебра
Сложение матриц
Создаёт две двумерные матрицы (2-D) с помощью ndarray::arr2
и суммирует их поэлементно.
Обратите внимание, что сумма вычисляется как let sum = &a + &b
. Оператор &
используется, чтобы избежать поглощения a
и b
и сделать их доступными для отображения позже. Создаётся новый массив, содержащий их сумму.
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]]); let sum = &a + &b; println!("{}", a); println!("+"); println!("{}", b); println!("="); println!("{}", sum); }
Перемножение матриц
Создаёт две матрицы с помощью ndarray::arr2
и выполняет перемножение матриц с помощью 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)); }
Умножение скаляра на вектор и на матрицу
Создаёт одномерный 1-D массив (вектор) с помощью ndarray::arr1
и двумерных 2-D массив (матрицу) с помощью ndarray::arr2
.
Сначала скаляр умножается на вектор, чтобы получить другой вектор. Затем матрица умножается на новый вектор с помощью ndarray::Array2::dot
. (Матричное умножение выполняется с использованием метода dot
, а оператор *
выполняет поэлементное умножение.)
В ndarray
массивы можно интерпретировать как либо векторы строк, либо столбцов, в зависимости от контекста. Если важно представить ориентацию вектора, необходимо использовать двумерный массив с одной строкой или одним столбцом. В этом примере вектор является 1-D массивом справа, поэтому dot
обрабатывает его как вектор столбца.
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); }
Сравнение вектора
Крейт ndarray поддерживает несколько способов создания массивов - этот рецепт создаёт ndarray::Array
массивы из std::Vec
, используя from
. Затем он суммирует массивы поэлементно.
Этот рецепт содержит пример поэлементного сравнения двух векторов с плавающей точкой. Числа с плавающей точкой часто хранятся неточно, что затрудняет точное сравнение. Тем не менее, assert_abs_diff_eq!
макрос из крейта approx
позволяет удобное, поэлементное сравнение. Для того, чтобы использовать approx
крейта с ndarray
, то функция approx
должна быть добавлена к ndarray
зависимости в Cargo.toml
. Например, ndarray = { version = "0.13", features = ["approx"] }
.
Этот рецепт также содержит дополнительные примеры владения. Здесь let z = a + b
потребляет a
и b
, обновляет переменную a
результатом, а затем переносит владение в z
. В качестве альтернативы, let w = &c + &d
создает новый вектор без использования c
или d
, что позволяет их модифицировать позже. См. Бинарные операторы с двумя массивами для дополнительной информации.
#[macro_use(assert_abs_diff_eq)] extern crate approx; extern crate ndarray; use ndarray::Array; fn main() { let a = Array::from(vec![1., 2., 3., 4., 5.]); let b = Array::from(vec![5., 4., 3., 2., 1.]); let mut c = Array::from(vec![1., 2., 3., 4., 5.]); let mut d = Array::from(vec![5., 4., 3., 2., 1.]); let z = a + b; let w = &c + &d; assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.])); println!("c = {}", c); c[0] = 10.; d[1] = 10.; assert_abs_diff_eq!(w, Array::from(vec![6., 6., 6., 6., 6.])); }
Норма вектора
Этот рецепт демонстрирует использование типа Array1
, типа ArrayView1
, метода fold
, метода dot
при вычислении нормы l1 и [l2] для данного вектора.
- Функция
l2_norm
является более простой из двух, так как она вычисляет квадратный корень из точечного произведения вектора с самим собой. - Функция
l1_norm
вычисляется с помощью операцииfold
, которая суммирует абсолютные значения элементов. (Это также может быть выполнено с помощьюx.mapv(f64::abs).scalar_sum()
, но это выделит новый массив для результатаmapv
.)
Обратите внимание, что и l1_norm
и l2_norm
принимают аргумент типа ArrayView1
. Этот рецепт учитывает векторные нормы, поэтому функции норм должны принимать только одномерные представления (следовательно, ArrayView1
). Хотя функции могут принимать параметр типа &Array1<f64>
, это потребует от вызывающей стороны иметь ссылку на собственный массив, что является более ограничительным, чем просто доступ к представлению (поскольку представление может быть создано из любого массива или просмотр, а не просто принадлежащий массив).
Типы Array
и ArrayView
являются псевдонимами для типов ArrayBase
. Таким образом, наиболее общий тип аргумента для вызывающей стороны будет &ArrayBase<S, Ix1> where S: Data
, потому что когда вызывающая сторона может использовать &array
или & view
вместо x.view()
. Если функция является частью публичного API, это может быть лучшим выбором для удобства пользователей. Для внутренних функций предпочтительным может быть более лаконичный ArrayView1<f64>
.
#[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)); }
[l2]: https://docs.rs/ndarray/*/ndarray/type.ArrayView1.html ## Инвертирование матрицы
Создаёт матрицу 3x3 с помощью nalgebra::Matrix3
и инвертирует её, если возможно.
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!"); } } }