container API
Luna-Flow/linear-algebra/container は、汎用コードが格納方式を知らずに線形コンテナを観測・構築・編集する方法を記述します。能力は 操作辞書 であり、一つのコンテナ型 V または M と一つの要素型 T に対する関数のレコードです。五つの汎用アルゴリズム(map、convert、transpose)はこの辞書に対して書かれています。リポジトリ自身の型に対する既製の辞書は container/adapters にあります。
ソース: src/container/vector_ops.mbt、src/container/matrix_ops.mbt、src/container/generic_algorithms.mbt。モデルは container の設計 で説明し、外部ライブラリが辞書を公開する方法は 統合ガイド で示しています。
インポート
///|
import {
"Luna-Flow/linear-algebra/container",
"Luna-Flow/linear-algebra/container/adapters" @container_adapters,
"Luna-Flow/linear-algebra/error" @la_error,
}
すべての辞書に共通する約束
辞書の関数はすべて検査付きです。Result を返し、どの引数に対しても中断してはなりません。
- 形状の外のインデックスでの読み取りや編集は、種類
IndexOutOfBoundsのエラーを返します。 - 負の長さや次元での構築は種類
NegativeDimensionのエラーを返し、初期化関数を呼びません。 - 退化した形状 と は有効であり、正確に保たれなければなりません。これらは とは異なります。
- 行列の座標は 0 始まりの
(row, col)です。ビルダーは初期化関数を(row, col)で呼び出します。
レコードのフィールドは公開されていて読み取れます。フィールドを関数として呼ぶには、フィールドアクセスを括弧で囲みます: (ops.get)(m, 0, 1)。
ベクトルの辞書
VectorReadOps
VectorReadOps[V, T] は要素 T を持つベクトル的なコンテナ V を観測します。
pub struct VectorReadOps[V, T] {
length : (V) -> Int
get : (V, Int) -> Result[T, @error.LinearAlgebraError]
}
length(v) は要素数 を返し、get(v, i) は なら要素 を、そうでなければ IndexOutOfBounds を返します。
VectorReadOps::new
VectorReadOps::new(length, get) は二つの関数から読み取り辞書を作ります。
pub fn[V, T] VectorReadOps::new((V) -> Int, (V, Int) -> Result[T, @error.LinearAlgebraError]) -> Self[V, T]
VectorBuildOps
VectorBuildOps[V, T] はインデックスの関数から型 V のベクトルを構築します。
pub struct VectorBuildOps[V, T] {
tabulate : (Int, (Int) -> T) -> Result[V, @error.LinearAlgebraError]
}
tabulate(n, f) はベクトル を返し、 なら NegativeDimension を返します。
VectorBuildOps::new
VectorBuildOps::new(tabulate) は構築辞書を作ります。
pub fn[V, T] VectorBuildOps::new((Int, (Int) -> T) -> Result[V, @error.LinearAlgebraError]) -> Self[V, T]
VectorPersistentEditOps
VectorPersistentEditOps[V, T] は要素を一つ置き換えた新しい値を返します。
pub struct VectorPersistentEditOps[V, T] {
set : (V, Int, T) -> Result[V, @error.LinearAlgebraError]
}
set(v, i, x) は、インデックス が x である以外は v と等しいベクトルを返します。実装がバージョン間で格納領域を共有していても、引数 v は変化してはなりません。
VectorPersistentEditOps::new
VectorPersistentEditOps::new(set) は永続編集辞書を作ります。
pub fn[V, T] VectorPersistentEditOps::new((V, Int, T) -> Result[V, @error.LinearAlgebraError]) -> Self[V, T]
VectorMutableEditOps
VectorMutableEditOps[V, T] は要素を一つインプレースで置き換えます。
pub struct VectorMutableEditOps[V, T] {
set : (V, Int, T) -> Result[Unit, @error.LinearAlgebraError]
}
set(v, i, x) は v のインデックス に x を書き込みます。エラーのときは何も書き込みません。
VectorMutableEditOps::new
VectorMutableEditOps::new(set) は可変編集辞書を作ります。
pub fn[V, T] VectorMutableEditOps::new((V, Int, T) -> Result[Unit, @error.LinearAlgebraError]) -> Self[V, T]
行列の辞書
MatrixReadOps
MatrixReadOps[M, T] は行列的なコンテナを観測します。
pub struct MatrixReadOps[M, T] {
shape : (M) -> (Int, Int)
get : (M, Int, Int) -> Result[T, @error.LinearAlgebraError]
}
shape(m) は (rows, cols) を返し、get(m, r, c) は成分 または IndexOutOfBounds を返します。
MatrixReadOps::new
MatrixReadOps::new(shape, get) は行列の読み取り辞書を作ります。
pub fn[M, T] MatrixReadOps::new((M) -> (Int, Int), (M, Int, Int) -> Result[T, @error.LinearAlgebraError]) -> Self[M, T]
MatrixBuildOps
MatrixBuildOps[M, T] は座標の関数から行列を構築します。
pub struct MatrixBuildOps[M, T] {
tabulate : (Int, Int, (Int, Int) -> T) -> Result[M, @error.LinearAlgebraError]
}
tabulate(r, c, f) は成分が の行列を返し、 または なら NegativeDimension を返します。
MatrixBuildOps::new
MatrixBuildOps::new(tabulate) は行列の構築辞書を作ります。
pub fn[M, T] MatrixBuildOps::new((Int, Int, (Int, Int) -> T) -> Result[M, @error.LinearAlgebraError]) -> Self[M, T]
MatrixPersistentEditOps
MatrixPersistentEditOps[M, T] は成分を一つ置き換えた新しい行列を返し、引数は変更しません。
pub struct MatrixPersistentEditOps[M, T] {
set : (M, Int, Int, T) -> Result[M, @error.LinearAlgebraError]
}
MatrixPersistentEditOps::new
MatrixPersistentEditOps::new(set) は永続編集辞書を作ります。
pub fn[M, T] MatrixPersistentEditOps::new((M, Int, Int, T) -> Result[M, @error.LinearAlgebraError]) -> Self[M, T]
MatrixMutableEditOps
MatrixMutableEditOps[M, T] は成分を一つインプレースで置き換えます。
pub struct MatrixMutableEditOps[M, T] {
set : (M, Int, Int, T) -> Result[Unit, @error.LinearAlgebraError]
}
MatrixMutableEditOps::new
MatrixMutableEditOps::new(set) は可変編集辞書を作ります。
pub fn[M, T] MatrixMutableEditOps::new((M, Int, Int, T) -> Result[Unit, @error.LinearAlgebraError]) -> Self[M, T]
素の Array[Int] 向けの辞書を、そのフィールドを通じて使う例:
///|
fn cont_api_array_read() -> @container.VectorReadOps[Array[Int], Int] {
@container.VectorReadOps::new(xs => xs.length(), (xs, i) => {
guard i >= 0 && i < xs.length() else {
return Err(
@la_error.LinearAlgebraError::index_out_of_bounds("index \{i}"),
)
}
Ok(xs[i])
})
}
///|
test "a hand-written read dictionary" {
let ops = cont_api_array_read()
inspect((ops.length)([4, 5, 6]), content="3")
inspect((ops.get)([4, 5, 6], 1).unwrap(), content="5")
match (ops.get)([4, 5, 6], 3) {
Err(e) => inspect(e.is_index_out_of_bounds(), content="true")
Ok(_) => fail("index 3 is out of bounds")
}
}
ジェネリックアルゴリズム
すべてのアルゴリズムは、まずソース全体を行優先で一時バッファに読み込み、それから対象の tabulate を呼びます。読み取りエラーは何かを構築する前に返されるので、部分的に構築された対象が観測されることはありません。いずれも 要素に対して 回の辞書呼び出しを行い、 の追加メモリを使います。
vector_map
vector_map(source, source_ops, target_ops, f) は、要素 が f(source[i]) である対象型のベクトルを作ります。
pub fn[V1, V2, A, B] vector_map(V1, VectorReadOps[V1, A], VectorBuildOps[V2, B], (A) -> B) -> Result[V2, @error.LinearAlgebraError]
source_ops.length が負の長さを報告すれば NegativeDimension を、そうでなければ最初の読み取りエラーを返し、最後に tabulate が返したものをそのまま返します。f は各要素についてインデックス順にちょうど一回呼ばれます。
vector_convert
vector_convert(source, source_ops, target_ops) は、要素型を保ったままベクトルを別の表現にコピーします。
pub fn[V1, V2, T] vector_convert(V1, VectorReadOps[V1, T], VectorBuildOps[V2, T]) -> Result[V2, @error.LinearAlgebraError]
恒等関数を使った vector_map です。
matrix_map
matrix_map(source, source_ops, target_ops, f) は、成分 が f(source[i][j]) である同じ形状の行列を作ります。
pub fn[M1, M2, A, B] matrix_map(M1, MatrixReadOps[M1, A], MatrixBuildOps[M2, B], (A) -> B) -> Result[M2, @error.LinearAlgebraError]
matrix_convert
matrix_convert(source, source_ops, target_ops) は、要素型と形状を保ったまま行列を別の表現にコピーします。
pub fn[M1, M2, T] matrix_convert(M1, MatrixReadOps[M1, T], MatrixBuildOps[M2, T]) -> Result[M2, @error.LinearAlgebraError]
matrix_transpose
matrix_transpose(source, source_ops, target_ops) は source の転置を対象の表現で作ります。形状 のソースに対して形状は で、成分 はソースの成分 に等しくなります。
pub fn[M1, M2, T] matrix_transpose(M1, MatrixReadOps[M1, T], MatrixBuildOps[M2, T]) -> Result[M2, @error.LinearAlgebraError]
@algebra.TransposeMatrix::transpose と異なり、対象の型はソースの型と異なってもかまいません。
リポジトリのアダプターを使った三つの行列アルゴリズム:
///|
test "map, convert and transpose across representations" {
let source = @mutable.Matrix::from_2d_array([[1, 2, 3], [4, 5, 6]])
let read = @container_adapters.mutable_matrix_read_ops()
let doubled : @immut.Matrix[Int] = @container.matrix_map(
source,
read,
@container_adapters.immutable_matrix_build_ops(),
x => x * 2,
).unwrap()
inspect(doubled, content="|2, 4, 6|\n|8, 10, 12|")
let as_text : @immut.Matrix[String] = @container.matrix_map(
source,
read,
@container_adapters.immutable_matrix_build_ops(),
x => "<\{x}>",
).unwrap()
inspect(as_text[1][2], content="<6>")
let t : @immut.Matrix[Int] = @container.matrix_transpose(
source,
read,
@container_adapters.immutable_matrix_build_ops(),
).unwrap()
inspect(t, content="|1, 4|\n|2, 5|\n|3, 6|")
let copy : @mutable.Matrix[Int] = @container.matrix_convert(
t,
@container_adapters.immutable_matrix_read_ops(),
@container_adapters.mutable_matrix_build_ops(),
).unwrap()
debug_inspect(copy.shape(), content="(3, 2)")
}
ベクトルのアルゴリズムも同じように動きます。
///|
test "vector map and convert" {
let v = @immut.Vector::from_array([1, 2, 3])
let squares : @mutable.Vector[Int] = @container.vector_map(
v,
@container_adapters.immutable_vector_read_ops(),
@container_adapters.mutable_vector_build_ops(),
x => x * x,
).unwrap()
inspect(squares, content="|1, 4, 9|")
let back : @immut.Vector[Int] = @container.vector_convert(
squares,
@container_adapters.mutable_vector_read_ops(),
@container_adapters.immutable_vector_build_ops(),
).unwrap()
inspect(back, content="|1, 4, 9|")
}