mutable 教程
本教程演示如何把 @mutable.Matrix 和 @mutable.Vector 用作工作缓冲区:原地构造和编辑它们,通过行、列和转置视图进行操作,并在妥善处理错误的前提下运行数值例程(逆矩阵、行列式、Cholesky、特征值、统计)。算法及其精度在 mutable 设计中解释。
快速上手
moon add Luna-Flow/linear-algebra@0.5.0
///|
import {
"Luna-Flow/linear-algebra/mutable",
}
///|
test "first mutable matrix" {
let m = @mutable.Matrix::from_2d_array([[1.0, 2.0], [3.0, 4.0]])
m.set(0, 1, 9.0)
inspect(m, content="|1, 9|\n|3, 4|")
inspect(m.determinant().unwrap(), content="-23")
}
set 原地修改 m;determinant 返回 Result,因为它只对方阵有定义。
日常任务
通过视图编辑行和列
///|
test "normalize each row to sum one" {
let m = @mutable.Matrix::from_2d_array([[1.0, 3.0], [2.0, 2.0], [0.0, 5.0]])
for i in 0..<m.row() {
let row = m.row_view(i)
let mut sum = 0.0
row.each(x => sum = sum + x)
row.map_inplace(x => x / sum)
}
inspect(m, content="|0.25, 0.75|\n|0.5, 0.5|\n|0, 1|")
let first_col = m.col_view(0)
first_col[2] = 1.0
inspect(m.get(2, 0), content="1")
}
求解小型线性方程组
没有针对右端项的公开求解器;对于小型方程组,请乘以逆矩阵,并处理奇异的情形:
///|
fn mut_tut_solve(
a : @mutable.Matrix[Double],
b : @mutable.Vector[Double],
) -> Result[@mutable.Vector[Double], @la_error.LinearAlgebraError] {
let inv = match a.inverse() {
Ok(inv) => inv
Err(e) => return Err(e)
}
inv.mul_vec(b)
}
///|
test "solve 2x + y = 5, x + 3y = 10" {
let a = @mutable.Matrix::from_2d_array([[2.0, 1.0], [1.0, 3.0]])
let x = mut_tut_solve(a, @mutable.Vector::from_array([5.0, 10.0])).unwrap()
inspect((x[0] - 1.0).abs() < 1.0e-12, content="true")
inspect((x[1] - 3.0).abs() < 1.0e-12, content="true")
let singular = @mutable.Matrix::from_2d_array([[1.0, 2.0], [2.0, 4.0]])
match mut_tut_solve(singular, @mutable.Vector::from_array([1.0, 2.0])) {
Err(e) => inspect(e.is_singular_matrix(), content="true")
Ok(_) => fail("singular systems have no unique solution")
}
}
如上所示,请带容差比较浮点结果,而不是使用 ==。
检查正定性并进行分解
///|
test "covariance-like matrix" {
let c = @mutable.Matrix::from_2d_array([
[4.0, 2.0, 0.0],
[2.0, 5.0, 1.0],
[0.0, 1.0, 3.0],
])
inspect(c.is_symmetric(), content="true")
match c.cholesky_decomposition() {
Some(l) => {
let back = l * l.transpose()
let mut err = 0.0
back.each_row_col((i, j, x) => err = err + (x - c.get(i, j)).abs())
inspect(err < 1.0e-12, content="true")
}
None => fail("c is positive definite")
}
let indefinite = @mutable.Matrix::from_2d_array([[1.0, 2.0], [2.0, 1.0]])
inspect(indefinite.cholesky_decomposition() is None, content="true")
}
对称矩阵的特征值
///|
test "vibration modes of a three-mass chain" {
let k = @mutable.Matrix::from_2d_array([
[2.0, -1.0, 0.0],
[-1.0, 2.0, -1.0],
[0.0, -1.0, 2.0],
])
let (values, vectors) = k.eigen()
let sorted = values.iter().to_array()
sorted.sort()
let expected = [2.0 - 1.4142135623730951, 2.0, 2.0 + 1.4142135623730951]
for i in 0..<3 {
inspect((sorted[i] - expected[i]).abs() < 1.0e-12, content="true")
}
let check = vectors.transpose() * vectors
inspect((check.get(0, 0) - 1.0).abs() < 1.0e-12, content="true")
inspect(check.get(0, 1).abs() < 1.0e-12, content="true")
}
返回的特征值未排序;当顺序重要时,请自行排序。
汇总统计
///|
test "statistics of a measurement grid" {
let grid = @mutable.Matrix::from_2d_array([
[2.0, 4.0],
[4.0, 4.0],
[5.0, 5.0],
[7.0, 9.0],
])
inspect(grid.mean().unwrap(), content="5")
inspect(grid.std_dev().unwrap(), content="2")
inspect(grid.min_element().unwrap(), content="2")
let empty : @mutable.Matrix[Double] = @mutable.Matrix::new(0, 2, 0.0)
inspect(empty.mean() is Err(_), content="true")
}
进一步了解
避免复制。 Matrix::from_array 和 Vector::from_array 直接接管你传入的数组。这很快,但意味着数组和矩阵会一起变化;需要独立时请调用 copy()。
不复制的转置。 to_transpose() 是 的视图;两个视图的乘积会复用矩阵内核。需要独立的矩阵时,请使用 transpose() 或 materialize()。
在热循环中使用非受检形式。 在确立前置条件之后(例如由你自己的代码构造的方阵),unchecked_* 方法会跳过校验。切勿在未检查过的形状上使用 unchecked_matmul:它不做校验,可能返回错误的结果。
缩放。 这些例程以 的绝对阈值判断“零”。请先把数据缩放到量级为一;否则很小但正则的矩阵会被报告为奇异。参见设计页面。
泛型代码。 把矩阵包装为 @default.DenseMatrix,即可传给以 algebra trait 为约束的函数;用 inner() 取回。
常见陷阱
- 忘记
reduce_row_elimination会修改矩阵。 它原地变换接收者并将其返回。请先调用copy()。 - 期望 特征向量已归一化。 对 输入,特征向量列未归一化;更大的输入返回标准正交的列。
- 在整数上使用数值例程。
determinant、inverse、rank、eigen等需要Tolerance,而它只为Float和Double提供。要得到精确的整数行列式,请使用@immut.Matrix::determinant。 Float精度。 容差远低于Float的精度;接近奇异的Float矩阵不会被检测出来。请优先使用Double。- 在对称谱上使用幂法。 模相等的特征值 会阻碍收敛;此时
power_method返回None。
后续步骤
- mutable API:每个方法及其错误与开销。
- mutable 设计:LU、Cholesky、QL 及其误差界。
- 错误教程:处理受检结果。
- immut 教程:精确的、面向值的计算。