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 や数値 API(ビュー、分解、統計量)も使う場合は DenseMatrix/DenseVector を使ってください。構築後に値が変わってはならない場合、たとえばプログラムの各部分で値を共有する場合は、Immutable* ラッパーを使ってください。
ラッパー間の変換。 container のアダプタ には 4 つのラッパーすべての辞書があるので、matrix_convert でそれらの間でデータを移せます。
新しいバックエンドを書く。 疎な型や固定サイズの型は、これらのラッパーに変換するのではなく、algebra のチュートリアル のように自身に algebra の trait を実装すべきです。
よくある落とし穴
DenseVector::from_arrayがコピーすると期待する。@mutable.Vector::from_arrayと同様に配列を共有します。配列を再利用する場合は先にコピーしてください。axpyがインプレースで更新すると期待する。 新しいベクトルを返します。- 互換性のない形状を掛ける。
*とmatvecは中断します。shape()で形状を検査するか、内側の型の検査付きメソッドを使ってください。 - ラッパーで分解を探す。 分解は
inner()にあります。
次のステップ
- backends/default API。
- 汎用側を書くには algebra のチュートリアル。
- ラップされた型については mutable のチュートリアル と immut のチュートリアル。