error チュートリアル
このチュートリアルでは、検査付きの線形代数呼び出しの失敗を扱う方法を示します。何がうまくいかなかったのかを識別し、意味のある場合は回復し、複数の検査付きステップを連鎖させ、自作のヘルパーからの失敗も同じ語彙で報告します。エラー型の背後にある考え方は エラーの設計 にあります。
クイックスタート
moon add Luna-Flow/linear-algebra@0.5.0
///|
import {
"Luna-Flow/linear-algebra/error" @la_error,
"Luna-Flow/linear-algebra/mutable",
}
検査付きの呼び出しは Result[_, LinearAlgebraError] を返します。結果で分岐し、述語を使って失敗を分類します。
///|
test "classify a failed inverse" {
let m = @mutable.Matrix::from_2d_array([[1.0, 2.0], [2.0, 4.0]])
let status = match m.inverse() {
Ok(_) => "ok"
Err(e) => if e.is_singular_matrix() { "singular" } else { e.message }
}
inspect(status, content="singular")
}
出力は singular です。
日常的なタスク
失敗ごとに異なる回復をする
///|
fn err_tut_inverse_report(m : @mutable.Matrix[Double]) -> String {
match m.inverse() {
Ok(inv) => "inverse has \{inv.row()} rows"
Err(e) =>
if e.is_non_square_matrix() {
"not square: use a least-squares method instead"
} else if e.is_singular_matrix() {
"singular: regularize the matrix"
} else {
e.message
}
}
}
///|
test "different recovery paths" {
let rect = @mutable.Matrix::from_2d_array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
inspect(
err_tut_inverse_report(rect),
content="not square: use a least-squares method instead",
)
let ok = @mutable.Matrix::from_2d_array([[2.0, 0.0], [0.0, 4.0]])
inspect(err_tut_inverse_report(ok), content="inverse has 2 rows")
}
検査付きステップを連鎖させる
両方のステップが成功したときだけ を計算します。早期の return Err(e) は、最初の失敗をそのまま受け渡します。
///|
fn err_tut_trace_of_power(
a : @immut.Matrix[Int],
k : Int,
) -> Result[Int, @la_error.LinearAlgebraError] {
let p = match a.pow(k) {
Ok(p) => p
Err(e) => return Err(e)
}
p.trace()
}
///|
test "trace of a matrix power" {
let a = @immut.Matrix::from_2d_array([[1, 1], [1, 0]])
inspect(err_tut_trace_of_power(a, 5).unwrap(), content="11")
match err_tut_trace_of_power(a, -1) {
Err(e) => inspect(e.is_negative_exponent(), content="true")
Ok(_) => fail("negative powers are not defined")
}
}
Fibonacci 行列の は なので、トレースは です。
自作のヘルパーから失敗を報告する
コンストラクタを使って、共通の種類のエラーを返します。
///|
fn err_tut_column_sum(
m : @mutable.Matrix[Int],
col : Int,
) -> Result[Int, @la_error.LinearAlgebraError] {
guard col >= 0 && col < m.col() else {
return Err(
@la_error.LinearAlgebraError::index_out_of_bounds(
"column \{col} is outside 0..<\{m.col()}",
),
)
}
let mut sum = 0
m.each_col(col, x => sum = sum + x)
Ok(sum)
}
///|
test "a custom checked helper" {
let m = @mutable.Matrix::from_2d_array([[1, 2], [3, 4]])
inspect(err_tut_column_sum(m, 1).unwrap(), content="6")
match err_tut_column_sum(m, 5) {
Err(e) => {
inspect(e.is_index_out_of_bounds(), content="true")
inspect(e.message, content="column 5 is outside 0..<2")
}
Ok(_) => fail("column 5 does not exist")
}
}
スカラーの失敗をラップする
ヘルパーがスカラーの検査付き演算と行列演算を混在させる場合は、ArithmeticError をラップして、ヘルパーのエラー型を 1 つにまとめます。
///|
fn err_tut_rms(
m : @mutable.Matrix[Double],
) -> Result[Double, @la_error.LinearAlgebraError] {
let n = (m.row() * m.col()).to_double()
let mut sum = 0.0
m.each(x => sum = sum + x * x)
let ctx = @lf_arith.ArithmeticContext::new(53)
match @la_arithmetic.CheckedDiv::checked_div(sum, n, ctx) {
Ok(mean_sq) => Ok(mean_sq.sqrt())
Err(e) => Err(@la_error.LinearAlgebraError::arithmetic_failure(e))
}
}
///|
test "wrapping a scalar error" {
let m = @mutable.Matrix::from_2d_array([[3.0, 4.0]])
inspect(err_tut_rms(m).unwrap(), content="3.5355339059327378")
let empty : @mutable.Matrix[Double] = @mutable.Matrix::new(0, 0, 0.0)
match err_tut_rms(empty) {
Err(e) => inspect(e.is_arithmetic_failure(), content="true")
Ok(_) => fail("0 / 0 must fail")
}
}
さらに進んで
検査なし形式。 すべての検査付きメソッドには、Err を返す代わりに中断する unchecked_* 版が対としてあります(unchecked_inverse は Option を返します)。これを使うのは、コードが前提条件を確立した後だけにしてください。たとえば、正方として構築した行列に対する内側のループなどです。x が定義域にあるときは常に checked(x) == Ok(unchecked(x)) という法則が成り立ちます。
今も中断するメソッド。 本リリースでは、すべての部分演算に検査付き形式があるわけではありません。from_2d_array のようなコンストラクタは行の長さが揃っていない入力で、演算子 +、-、* は形状の不一致で、eigen は非対称な入力で中断します。immut と mutable の API ページにはそれぞれ明記してあります。入力がプログラムの外から来る場合は、先に検証してください。
container 層からのエラー。 container の辞書とアルゴリズムは同じエラー型を返すので、ストレージを変換してから計算するパイプラインでも、エラー型の間の変換は不要です。
よくある落とし穴
messageでマッチする。 メッセージは診断用であり、リリース間で変わる可能性があります。述語かkindで分岐してください。- エラーを直接出力する。
LinearAlgebraErrorにはShowもDebugも実装されていません。e.messageを出力してください。 - エラー全体を比較する。
==はメッセージも比較します。分類だけが重要な場合はa.kind == b.kindを比較してください。 - 予約された種類が発生すると想定する。
NonConvergence、RaggedRows、InvalidLengthは本リリースでは生成されません。
次のステップ
- すべてのコンストラクタと述語については error API。
- 検査付き・検査なしの法則については エラーの設計。
- 検査付きの行列演算そのものについては mutable のチュートリアル と immut のチュートリアル。