线性代数

矩阵相加

ndarray-badge cat-science-badge

使用 ndarray::arr2 创建两个二维(2-D)矩阵,并按元素方式求和。

注意:sum 的计算方式为 let sum = &a + &b,借用 & 运算符获得 ab 的引用,可避免销毁他们,使它们可以稍后显示。这样,就创建了一个包含其和的新数组。

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-badge cat-science-badge

使用 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-badge cat-science-badge

使用 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-badge

ndarray crate 支持多种创建数组的方法——此实例使用 fromstd::Vec 创建数组 ndarray::Array。然后,对数组以元素方式求和。

下面的实例按元素方式比较两个浮点型 vector。浮点数的存储通常不精确,因此很难进行精确的比较。但是,approx crate 中的 assert_abs_diff_eq! 宏允许方便地比较浮点型元素。要将 approxndarray 两个 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 范数

ndarray-badge

这个实例展示了 Array1 类型、ArrayView1 类型、fold 方法,以及 dot 方法在计算给定 vector 的 l1l2 范数时的用法。

  • l2_norm 函数是两者中较简单的,它计算一个 vector 与自身的点积(dot product,数量积)的平方根。
  • l1_norm 函数通过 fold 运算来计算元素的绝对值(也可以通过 x.mapv(f64::abs).scalar_sum() 执行,但是会为 mapv 的结果分配一个新的数组)。

请注意:l1_norml2_norm 都采用 ArrayView1 类型。这个实例考虑了 vector 范数,所以范数函数只需要接受一维视图(ArrayView1)。虽然函数可以使用类型为 &Array1<f64> 的参数,但这将要求调用方引用拥有所有权的数组,这比访问视图更为严格(因为视图可以从任意数组或视图创建,而不仅仅是从拥有所有权的数组创建)。

ArrayArrayView 都是 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-badge cat-science-badge

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!");
        }
    }
}

(反)序列化矩阵

ndarray-badge cat-science-badge

本实例实现将矩阵序列化为 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(())
}