backends/default 教程
本教程演示如何把 backends/default 的稠密包装类型用作现成的数据,供针对 algebra trait 编写的泛型代码使用,以及在需要时如何下沉到完整的具体 API。背景知识见 backends/default 设计。
快速上手
moon add Luna-Flow/linear-algebra@0.5.0
///|
import {
"Luna-Flow/linear-algebra/algebra",
"Luna-Flow/linear-algebra/backends/default",
}
///|
test "multiply two dense matrices" {
let a = @default.DenseMatrix::from_2d_array([[1, 2], [3, 4]])
let b = @default.DenseMatrix::from_2d_array([[0, 1], [1, 0]])
inspect((a * b).inner(), content="|2, 1|\n|4, 3|")
}
日常任务
在两种包装类型上运行泛型算法
针对 MatMulMatrix 编写的辅助函数同样接受可变和不可变的包装类型:
///|
fn[M : @algebra.MatMulMatrix] def_tut_commutator(a : M, b : M) -> M {
a * b - b * a
}
///|
test "commutator on two representations" {
let a = @default.DenseMatrix::from_2d_array([[0, 1], [0, 0]])
let b = @default.DenseMatrix::from_2d_array([[0, 0], [1, 0]])
inspect(def_tut_commutator(a, b).inner(), content="|1, 0|\n|0, -1|")
let ai = @default.ImmutableDenseMatrix::from_2d_array([[0, 1], [0, 0]])
let bi = @default.ImmutableDenseMatrix::from_2d_array([[0, 0], [1, 0]])
inspect(def_tut_commutator(ai, bi).inner(), content="|1, 0|\n|0, -1|")
}
计算线性模型
matvec、dot 和 axpy 涵盖了线性模型 中常见的向量算术:
///|
test "a tiny linear layer" {
let w = @default.DenseMatrix::from_2d_array([[0.5, -1.0], [2.0, 0.0]])
let x = @default.DenseVector::from_array([2.0, 1.0])
let b = @default.DenseVector::from_array([0.1, 0.2])
let y = w.matvec(x) + b
inspect(y.inner(), content="|0.1, 4.2|")
inspect(y.dot(y), content="17.650000000000002")
}
17.650000000000002 的最后一位来自舍入: 无法被精确表示。
下沉到具体 API
包装类型只暴露 trait 层面的运算。其他功能请通过 inner() 使用,例如受检的求逆:
///|
test "checked inverse through inner" {
let a = @default.DenseMatrix::from_2d_array([[4.0, 7.0], [2.0, 6.0]])
let inv = @default.DenseMatrix::from_backend(a.inner().inverse().unwrap())
let id = a * inv
inspect((id.inner().get(0, 0) - 1.0).abs() < 1.0e-12, content="true")
inspect(id.inner().get(0, 1).abs() < 1.0e-12, content="true")
}
不复制地包装已有矩阵
from_backend 共享内部值,因此之后的写入是可见的:
///|
test "wrapping shares storage" {
let m = @mutable.Matrix::from_2d_array([[1, 2], [3, 4]])
let d = @default.DenseMatrix::from_backend(m)
m.set(0, 0, 10)
inspect(d.inner().get(0, 0), content="10")
}
进一步了解
选择包装类型。 如果还要使用 @mutable 的原地与数值 API(视图、分解、统计),请使用 DenseMatrix/DenseVector。如果值在构造后不得改变,例如要在程序的不同部分之间共享,请使用 Immutable* 包装类型。
在包装类型之间转换。 container 适配器为全部四种包装类型提供了字典,因此 matrix_convert 可以在它们之间搬运数据。
编写新的后端。 稀疏或定长类型应当为自身实现 algebra trait(如 algebra 教程所示),而不是转换成这些包装类型。
常见陷阱
- 以为
DenseVector::from_array会复制。 它与@mutable.Vector::from_array一样共享数组。如果数组会被复用,请先复制。 - 以为
axpy会原地更新。 它返回新向量。 - 对不兼容的形状做乘法。
*和matvec会中止;请用shape()检查形状,或使用内部类型的受检方法。 - 在包装类型上寻找分解。 它们在
inner()上。
后续步骤
- backends/default API。
- algebra 教程:编写泛型一侧的代码。
- mutable 教程和 immut 教程:被包装的类型。