itl_expr チュートリアル

このチュートリアルでは、ITF1788 スイートの ITL 形式で書かれた IEEE 1788 の区間テストケースを ball_float に対して実行する方法を説明します。ITL テキストをケースに構文解析し、各ケースを実行して、結果を要約します。ファイル全体を対象とするコマンドラインランナーは itl_expr_cli です。

クイックスタート

moon.pkg にパッケージを追加します。

import {
  "Luna-Flow/floating/frontend/itl_expr",
}

テストケースのブロックを構文解析して実行します。

///|
test "quick start" {
  let source =
    #|testcase demo {
    #|  add [1.0,2.0] [3.0,4.0] = [4.0,6.0];
    #|  intersection [1.0,3.0] [2.0,4.0] = [2.0,3.0];
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  let summary = @itl_expr.summarize_results(
    cases.map(case => @itl_expr.execute_case(case)),
  )
  inspect(summary.passed_cases(), content="2")
  inspect(summary.success(), content="true")
}

各文 operation operand… = expected; は、ID が testcase:index である 1 つの ItlCase になります。

日常的なタスク

構文解析されたケースを調べる

///|
test "parsed cases" {
  let source =
    #|testcase bounds {
    #|  inf [-0x1.8p1,2.5] = -3.0;
    #|  mul [1.0,2.0]
    #|      [entire] = [entire];
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  inspect(cases.map(c => c.id()).join(" "), content="bounds:1 bounds:2")
  inspect(cases[1].operation(), content="mul")
  inspect(cases[1].operands().join(" "), content="[1.0,2.0] [entire]")
  inspect(cases[1].expected(), content="[entire]")
}

文は複数行にまたがってもよく、; で終わります。端点は 10 進または 16 進(0x1.8p1)のテキストで書くことができ、リテラル [empty]、[entire]、[nai] も解釈されます。

装飾を検査する

期待値に装飾の接尾辞(_com、_dac、_def、_trv、_ill)を付けると、装飾もテスト対象になります。接尾辞がなければ集合だけが比較されます。失敗メッセージでは区間を中心・半径形式で表示します。

///|
test "decorations" {
  let source =
    #|testcase deco {
    #|  add [1.0,2.0]_com [3.0,4.0]_com = [4.0,6.0]_com;
    #|  add [1.0,2.0]_com [3.0,4.0]_com = [4.0,6.0]_def;
    #|  add [1.0,2.0]_com [3.0,4.0]_com = [4.0,6.0];
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  let passed = cases.map(c => @itl_expr.execute_case(c).passed().to_string())
  inspect(passed.join(" "), content="true false true")
  inspect(
    @itl_expr.execute_case(cases[1]).message(),
    content="expected 5p0 +/- 1p0_def, got 5p0 +/- 1p0_com",
  )
}

数値、真偽値、重なり状態

区間の結果のほか、ITL のケースは数値(inf、sup、mid、rad、wid、mag、mig)、真偽値(isEmpty、subset、isMember、…)、または重なり状態の名前を期待値とすることができます。

///|
test "other result kinds" {
  let source =
    #|testcase kinds {
    #|  wid [1.0,3.5] = 2.5;
    #|  subset [1.0,2.0] [0.0,3.0] = true;
    #|  isMember 4.0 [1.0,3.0] = false;
    #|  overlap [1.0,2.0] [2.0,3.0] = meets;
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  let summary = @itl_expr.summarize_results(
    cases.map(c => @itl_expr.execute_case(c)),
  )
  inspect(summary.passed_cases(), content="4")
}

サポートされないケースと不正なケース

実行器が実装していないケースは Unsupported、オペランドを読み取れないケースは Diagnostic になります。どちらも失敗には数えられませんが、診断があると success() は false になります。

///|
test "dispositions" {
  let source =
    #|testcase odd {
    #|  mulRevToPair [1.0,2.0] [3.0,4.0] = [1.0,2.0];
    #|  sqrt [one,two] = [1.0,2.0];
    #|}
  let results = @itl_expr.parse_itl(source)
    .unwrap()
    .map(c => @itl_expr.execute_case(c))
  let summary = @itl_expr.summarize_results(results)
  inspect(summary.unsupported_cases(), content="1")
  inspect(summary.diagnostic_cases(), content="1")
  inspect(summary.success(), content="false")
}

さらに進んで

  • コーパスの実行。 just conformance run interval は固定された ITF1788 コーパスを取得し、native ランナーをビルドして、testdata/interval/interpreter_stages.json に列挙された strict なフェーズを実行します。検証 を参照してください。
  • 演算による絞り込み。 CLI の --operation NAME オプションはその演算のケースだけを残します。コードでは、実行前に cases を operation() で絞り込みます。
  • 精度。 execute_case(case, precision=p) は端点を p ビットで解析します。区間演算自体は binary64 に丸められるため、ITF1788 のデータには既定値の 53 を使ってください。

よくある落とし穴

  • 10 進の端点は最近接に丸められます。 0.1 のような端点は外向きに丸められるのではなく最も近い binary64 の数として読まれるため、[0.1,0.1] はその binary64 の数の単集合になります。
  • シグナルは検査されません。 期待値の後に置かれた signal … のような ITL の注釈があると期待値を読み取れなくなり、そのようなケースは合格ではなくサポート外または診断になります。
  • 構文解析エラーは入力を拒否します。 いずれかの文に = や演算がない場合、またはテキストが文の途中で終わっている場合、parse_itl は診断だけを返します。

次のステップ