线性代数

vector 总和

ndarray-badge

这个ndarray箱子支持多种创建数组的方法 —— 该食谱主要专注std::Vecndarray::Array的创建,通过from_vec完成。把两个数组加在一起和把两个数字加在一起,没有什么不同。在数组上,使用&符号,能在一次算术运算中,阻止消耗数组的操作。没有&的话,数组会被消耗。

在第一个示例中,数组ab在 let 语句(z = a + b)中移动:。 在第二个示例中,数组cd不会移动,而是为w创建一个新的数组。在 vector 总和(w = &c + &d)之后,更新cd,对w值是没有影响的。另外,在打印c时按预期工作,打印b时出错,因为移动了。见两个数组的二元运算符更多细节。

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 范数

ndarray-badge

这个食谱演示了Array1类型,ArrayView1类型,fold方法,以及dot方法,它计算给定 vector 的l1l2范数。l2 范数的计算是两者中,比较简单的一个,因为它是一个 vector 的点乘积的平方根,如l2_norm函数所示。而 l1 范数,在l1_norm函数,由fold方法完成,它是元素绝对值的总和。(这也可以用x.mapv(f64::abs).scalar_sum()执行,但这将为mapv的结果分配一个新的数组。)

注意l1_norml2_norm都拿了ArrayView1类型。这个食谱考虑了 vector 范数,因此范数函数,只需要接受一维 view(就是ArrayView1)。当这个函数换成接受一个&Array1<f64>类型参数,这将要求调用者具有,所有权数组的一个引用,这比仅对一个 view 的访问,更为严格(因为一个 view,是可以通过任何 array 或 view 创建的,而不仅仅是所有权数组)。对调用者来说,最方便的参数类型是&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));
}

矩阵相加

ndarray-badge cat-science-badge

ndarray::arr2创建两维矩阵,并把它们加在一起。

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

矩阵乘法

ndarray-badge cat-science-badge

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

用 vector 和 矩阵,乘一个标量

ndarray-badge cat-science-badge

ndarray::arr1创建一个,一维数组(vector),二维数组(矩阵)就用ndarray::arr2。 首先,将一个标量乘以 vector ,得到另一个 vector。然后,矩阵用ndarray::Array2::dot,乘以这个新的 vector。 (dot会执行矩阵乘法,而*运算符scalar * vector,执行逐个元素的相乘。)在ndarray中,一维数组根据上下文,可以解释为行或列 vector。如果 vector 表示的方向很重要,则要用二维数组的一行或一列。在本例中,vector 是右手边的一维数组,因此,dot将其作为列 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);
}

反转矩阵

[![nalgebra-badge]][nalgebra] cat-science-badge

使用nalgebra::Matrix3创建 3x3 矩阵,如果可能的话,将其反转。

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