线性代数
矩阵相加
使用 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(()) }