# 线性代数

### 矩阵相加

使用 ndarray::arr2 可以创建二阶矩阵，并计算它们的和。

    use ndarray::arr2;

    fn main() {
        let a = arr2(&[[1, 2, 3],
                       [4, 5, 6]]);

        let b = arr2(&[[6, 5, 4],
                       [3, 2, 1]]);

        // 借用 a 和 b，求和后生成新的矩阵 sum
        let sum = &a + &b;

        println!("{}", a);
        println!("+");
        println!("{}", b);
        println!("=");
        println!("{}", sum);
    }

### 矩阵相乘

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

### 标量、向量、矩阵相乘

在 `ndarry`中，1 阶数组根据上下文既可以作为行向量也可以作为列向量。如果对你来说，这个行或列的方向很重要，可以考虑使用一行或一列的 2 阶数组来表示。

在下面例子中，由于 1 阶数组处于乘号的右边位置，因此 `dot` 会把它当成列向量来处理。

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

### 向量比较

浮点数通常是不精确的，因此比较浮点数不是一件简单的事。approx 提供的 assert_abs_diff_eq! 宏提供了方便的按元素比较的方式。为了使用 `approx` ，你需要在 `ndarray` 的依赖中开启相应的 feature：例如，在 `Cargo.toml` 中修改 `ndarray` 的依赖引入为 `ndarray = { version = "0.13", features = ["approx"] }`。

    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.]);

      // 消耗 a 和 b 的所有权
      let z = a + b;
      // 借用 c 和 d
      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.]));

    }

### 向量范数( norm )

需要注意的是 `Array` 和 `ArrayView` 都是 `ArrayBase` 的别名。因此一个更通用的参数应该是 `&ArrayBase<S, Ix1> where S: Data`，特别是在你提供一个公共 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 ，然后再反序列化为原矩阵。

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

        // 验证反序列化后的矩阵跟原始矩阵相等
        assert!(deserialized_matrix == matrix);

        Ok(())
    }