线性代数
矩阵相加
使用 ndarray::arr2
创建两个二维(2-D)矩阵,并按元素方式求和。
注意:sum 的计算方式为 let sum = &a + &b
,借用 &
运算符获得 a
和 b
的引用,可避免销毁他们,使它们可以稍后显示。这样,就创建了一个包含其和的新数组。
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
对它们执行矩阵乘法。
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));
}
标量、vector、矩阵相乘
使用 ndarray::arr1
创建一维(1-D)数组(vector),使用 ndarray::arr2
创建二维(2-D)数组(矩阵)。
首先,一个标量乘以一个 vector 得到另一个 vector。然后,使用 ndarray::Array2::dot
将矩阵乘以新的 vector(矩阵相乘使用 dot
函数,而 *
运算符执行元素方式的乘法)。
在 ndarray
crate 中,根据上下文,一维数组可以解释为行 vector 或列 vector。如果 vector 表示的方向很重要,则必须使用只有一行或一列的二维(2-D)数组。在本实例中,vector 是右侧的一维(1-D)数组,因此 dot
函数将其处理为列 vector。
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);
}
Vector 比较
ndarray crate 支持多种创建数组的方法——此实例使用 from
从 std::Vec
创建数组 ndarray::Array
。然后,对数组以元素方式求和。
下面的实例按元素方式比较两个浮点型 vector。浮点数的存储通常不精确,因此很难进行精确的比较。但是,approx
crate 中的 assert_abs_diff_eq!
宏允许方便地比较浮点型元素。要将 approx
和 ndarray
两个 crate一起使用,必须在 Cargo.toml
文件中的 ndarray
依赖项添加 approx
特性。例如:ndarray = { version = "0.13", features = ["approx"] }
。
此实例还包含其他所有权示例。在这里,let z = a + b
执行后,会销毁 a
and b
,然后所有权会转移到 z
。或者,let w = &c + &d
创建一个新的 vector,而不销毁 c
或者 d
,允许以后对它们进行修改。有关其他详细信息,请参见带有两个数组的二进制运算符。
use approx::assert_abs_diff_eq;
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.]));
}
Vector 范数
这个实例展示了 Array1
类型、ArrayView1
类型、fold
方法,以及 dot
方法在计算给定 vector 的 l1 和 l2 范数时的用法。
+ l2_norm
函数是两者中较简单的,它计算一个 vector 与自身的点积(dot product,数量积)的平方根。
+ l1_norm
函数通过 fold
运算来计算元素的绝对值(也可以通过 x.mapv(f64::abs).scalar_sum()
执行,但是会为 mapv
的结果分配一个新的数组)。
请注意:l1_norm
和 l2_norm
都采用 ArrayView1
类型。这个实例考虑了 vector 范数,所以范数函数只需要接受一维视图(ArrayView1
)。虽然函数可以使用类型为 &Array1<f64>
的参数,但这将要求调用方引用拥有所有权的数组,这比访问视图更为严格(因为视图可以从任意数组或视图创建,而不仅仅是从拥有所有权的数组创建)。
Array
和 ArrayView
都是 ArrayBase
的类型别名。于是,大多数的调用方参数类型可以是 &ArrayBase<S, Ix1> where S: Data
,这样调用方就可以使用 &array
或者 &view
而不是 x.view()
。如果该函数是公共 API 的一部分,那么对于用户来说,这可能是一个更好的选择。对于内部函数,更简明的 ArrayView1<f64>
或许更合适。
use ndarray::{array, 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));
}
矩阵求逆
用 nalgebra::Matrix3
创建一个 3x3 的矩阵,如果可能的话,将其求逆。
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!");
}
}
}
(反)序列化矩阵
本实例实现将矩阵序列化为 JSON,以及从 JSON 反序列化出矩阵。序列化由 serde_json::to_string
处理,serde_json::from_str
则执行反序列化。
请注意:序列化后再反序列化将返回原始矩阵。
extern crate nalgebra;
extern crate serde_json;
use nalgebra::DMatrix;
fn main() -> Result<(), std::io::Error> {
let row_slice: Vec<i32> = (1..5001).collect();
let matrix = DMatrix::from_row_slice(50, 100, &row_slice);
// 序列化矩阵
let serialized_matrix = serde_json::to_string(&matrix)?;
// 反序列化出矩阵
let deserialized_matrix: DMatrix<i32> = serde_json::from_str(&serialized_matrix)?;
// 验证反序列化出的矩阵 `deserialized_matrix` 等同于原始矩阵 `matrix`
assert!(deserialized_matrix == matrix);
Ok(())
}