prover API リファレンス

prover パッケージ (Luna-Flow/QED/prover) は定理スクリプトを実行する。スクリプトまたはスクリプトのファイルをパースし、求められれば命題プレリュードをインストールし、各ゴールをローワリングし、tactics パッケージでステップを実行し({ ... } の分岐ブロックは自身でスケジュールする)、カーネル定理、構造化された失敗、または hole を含むスクリプトに対する未完了の証明の報告のいずれかを返す。また、ユーザーマニュアルとテストを結びつける回帰コーパスも公開する。

このパッケージは調整と報告を行うのみで、論理上の権限は持たない。結果モデルは prover の設計で説明しており、prover チュートリアルでは MoonBit からスクリプトを実行する。

オプション

ProverOptions、default_prover_options と prover_options

ProverOptions のスイッチは 1 つだけで、実行前に命題プレリュードを状態にインストールするかどうかである。default_prover_options() はこれを有効にし、prover_options(b) は明示的に設定する。

pub struct ProverOptions {
  auto_install_prelude : Bool
}

pub fn default_prover_options() -> ProverOptions
pub fn prover_options(Bool) -> ProverOptions

プレリュードがない場合、状態がすでに定義していない限り、T、F とカタログの定理は利用できない。

スクリプトの実行

prove_theorem_script_detailed

prove_theorem_script_detailed(state, src, opts) は 1 つの定理スクリプトを実行し、3 通りの結果を返す。

pub fn prove_theorem_script_detailed(@kernel.KernelState, String, ProverOptions) -> ProverRunResult

ヘッダの束縛子 (x : A) はゴールのローカルになる。ステップは順に実行され、分岐ブロックが続くステップはまず実行され、その後に各ブロックがそれぞれのフレームで自分のサブゴール上で実行される。最初に失敗したステップでスクリプトは停止する。hole はスクリプトを未完了として停止させる。すべてのステップが成功してもゴールが残る場合、結果は UnsolvedGoals による失敗となる。

prove_theorem_script と prove_theorem_script_with_diagnostics

これらの関数は 1 つのスクリプトを実行し、定理と状態を返す。失敗または未完了の報告はエラーとして返す。

pub fn prove_theorem_script(@kernel.KernelState, String, ProverOptions) -> Result[(@kernel.KernelState, @kernel.Thm), ProverScriptError]
pub fn prove_theorem_script_with_diagnostics(@kernel.KernelState, String, ProverOptions) -> Result[(@kernel.KernelState, @kernel.Thm), ProverScriptError]

2 つの関数は現在のところ同一に振る舞い、どちらも完全な診断情報を ProverScriptError に持たせる。返される状態は証明が実行された状態であり、要求があればプレリュードがインストールされている。証明された定理はそこに追加されない。

prove_theorem_file_results_detailed

prove_theorem_file_results_detailed(state, src, opts) は、定理が qed 行で区切られたファイルのすべての定理を実行し、定理ごとに 1 つの結果を返す。

pub fn prove_theorem_file_results_detailed(@kernel.KernelState, String, ProverOptions) -> ProverFileRunResult

失敗または未完了の定理があってもファイルは停止せず、後続の定理も検査される。ファイル全体が失敗する (FileFailed) のは、パースできないか、プレリュードをインストールできない場合のみである。

prove_theorem_file_detailed

prove_theorem_file_detailed はファイルを実行し、1 つの結果に要約する。失敗があれば最初の失敗、なければ未完了の定理があれば最初のもの、それもなければ最後の成功である。

pub fn prove_theorem_file_detailed(@kernel.KernelState, String, ProverOptions) -> ProverRunResult

結果

ProverRunResult

ProverRunResult は 1 つのスクリプトの結果である。

pub enum ProverRunResult {
  Proved(ProverSuccess)
  Failed(ProverFailure)
  Unfinished(ProverUnfinished)
}

ProverSuccess

ProverSuccess は定理、スクリプトの名前、および証明が行われた状態を持つ。

pub struct ProverSuccess {
  state : @kernel.KernelState
  theorem_name : String
  thm : @kernel.Thm
}

ProverFailure

ProverFailure は、スクリプトがどこで、なぜ失敗したかを記述する。

pub struct ProverFailure {
  theorem_name : String?
  kind : ProverDiagnosticKind
  detail : String
  raw_offset : Int?
  goal_src : String?
  goal_span : @parser.SourceSpan?
  step_index : Int?
  branch_path : Array[Int]
  step_span : @parser.SourceSpan?
  step_src : String?
  current_goal : @tactics.Goal?
  local_hyps : Array[ProverLocalHyp]
  error : ProverError
}

kind と error はどの層が失敗したかを示し、detail はエラーを文字列化したものである。位置のフィールドはソース中の失敗位置を示す。パースエラーでは raw_offset、ローワリングエラーではゴールのスパン、タクティクのエラーではステップのインデックス、分岐パス、ステップのスパンとステップのテキストに加え、その時点のゴールとローカルを持つ。該当しないフィールドは None または空になる。

ProverUnfinished

ProverUnfinished は hole に到達したスクリプトを報告する。

pub struct ProverUnfinished {
  theorem_name : String
  detail : String
  raw_offset : Int?
  goal_src : String?
  goal_span : @parser.SourceSpan?
  step_index : Int?
  branch_path : Array[Int]
  step_span : @parser.SourceSpan?
  step_src : String?
  current_goal : @tactics.Goal
  local_hyps : Array[ProverLocalHyp]
  hole_name : String?
}

失敗と同じ位置のフィールドに加え、hole が表すゴール(常に存在する)と、あれば hole の名前を持つ。これは定理ではない。

ProverLocalHyp と prover_local_hyp

ProverLocalHyp は報告中の名前付きローカル仮定であり、prover_local_hyp がそれを構築する。

pub struct ProverLocalHyp {
  name : String
  term : @kernel.Term
}

pub fn prover_local_hyp(String, @kernel.Term) -> ProverLocalHyp

prover_unfinished

prover_unfinished は、定理名、ゴールのソース、ゴールのスパン、ステップのインデックス、分岐パス、ステップのスパン、ステップのソース、現在のゴール、ローカル、hole の名前、詳細、生のオフセットの順のフィールドから Unfinished の結果を構築する。

pub fn prover_unfinished(String, String?, @parser.SourceSpan?, Int?, Array[Int], @parser.SourceSpan?, String?, @tactics.Goal, Array[ProverLocalHyp], String?, String, Int?) -> ProverRunResult

固定された未完了の報告を必要とするテストやレンダラのために存在する。

ProverError と ProverDiagnosticKind

ProverError は失敗した層のエラーを包み、ProverDiagnosticKind はその層を名指す。

pub enum ProverError {
  Parse(@parser.ParseError)
  Bridge(@parser.ParseBridgeError)
  Tactic(@tactics.TacticExecError)
  Sig(@kernel.SigError)
  Logic(@kernel.LogicError)
}

pub enum ProverDiagnosticKind {
  Parse
  Bridge
  Tactic
  Sig
  Logic
}

ProverScriptError

ProverScriptError は prove_theorem_script のエラー側である。

pub enum ProverScriptError {
  Failure(ProverFailure)
  Unfinished(ProverUnfinished)
}

ProverFileRunResult、ProverFileReport と ProverFileItemResult

ファイルの実行は、全体として失敗するか、定理ごとに順番に 1 つの項目を返す。

pub enum ProverFileRunResult {
  FileChecked(ProverFileReport)
  FileFailed(ProverFailure)
}

pub struct ProverFileReport {
  state : @kernel.KernelState
  items : Array[ProverFileItemResult]
}

pub enum ProverFileItemResult {
  ProverItemProved(ProverSuccess)
  ProverItemFailed(ProverFailure)
  ProverItemUnfinished(ProverUnfinished)
}
test "run scripts" {
  let st = @kernel.empty_kernel_state()
  let opts = @prover.default_prover_options()
  let ok = @prover.prove_theorem_script_detailed(st, "theorem truth_file : ⊢ T := by exact truth", opts)
  inspect(ok is @prover.Proved({ theorem_name: "truth_file", .. }), content="true")
  // the manual's bad_branch example: a structured failure
  let bad = @prover.prove_theorem_script_detailed(
    st,
    "theorem bad_branch (x : bool) : ⊢ x -> x ∨ x := by\n  intro h\n  left { exact truth }",
    opts,
  )
  guard bad is @prover.Failed(f) else { fail("expected a failure") }
  inspect(f.detail, content="GoalShapeMismatch(exact witness does not directly close current goal)")
  assert_eq((f.step_index, f.branch_path, f.step_src), (Some(3), [1], Some("exact truth")))
  // a hole is reported, not proved
  let unf = @prover.prove_theorem_script_detailed(
    st,
    "theorem unfinished_branch (x : bool) : ⊢ x -> x ∨ (x ∨ x) := by\n  intro h\n  right { right { hole h1 } }",
    opts,
  )
  guard unf is @prover.Unfinished(u) else { fail("expected an unfinished proof") }
  assert_eq((u.hole_name, u.step_index, u.branch_path), (Some("h1"), Some(4), [1, 1]))
}
test "run a file" {
  let src =
    #|theorem before_hole : ⊢ T := by exact truth
    #|qed
    #|
    #|theorem unfinished_demo (x : bool) : ⊢ x -> x ∨ (x ∨ x) := by
    #|  intro h
    #|  right { right { hole h1 } }
    #|qed
    #|
    #|theorem after_hole : ⊢ T := by exact truth
    #|qed
  let st = @kernel.empty_kernel_state()
  guard @prover.prove_theorem_file_results_detailed(st, src, @prover.default_prover_options())
    is @prover.FileChecked(report) else {
    fail("expected the file to parse")
  }
  let kinds = report.items.map(item => match item {
    @prover.ProverItemProved(_) => "ok"
    @prover.ProverItemFailed(_) => "error"
    @prover.ProverItemUnfinished(_) => "unfinished"
  })
  assert_eq(kinds, ["ok", "unfinished", "ok"])
}

ファイルは examples/multi_with_hole.qed である。

回帰コーパス

コーパスは、テストが実行し、ドキュメントが引用してよいスクリプトの唯一のリストである。各ケースは、識別子、スクリプト、ステップのレイアウト、およびそれが示す機能または失敗を持つ。

positive_corpus_cases、negative_corpus_cases、unfinished_corpus_cases と quantifier_corpus_cases

これらの関数は、種類ごとのケースを返す。

pub fn positive_corpus_cases() -> Array[PositiveCorpusCase]
pub fn negative_corpus_cases() -> Array[NegativeCorpusCase]
pub fn unfinished_corpus_cases() -> Array[UnfinishedCorpusCase]
pub fn quantifier_corpus_cases() -> Array[QuantifierCorpusCase]

PositiveCorpusCase

肯定ケースは、真偽値の定数 const_names を宣言した後に expected_goal を証明しなければならない。

pub struct PositiveCorpusCase {
  case_id : String
  const_names : Array[String]
  expected_goal : String
  script : String
  layout : ScriptLayout
  catalog_class : PositiveCatalogClass
  capability : PositiveTacticCapability
}

NegativeCorpusCase

否定ケースは、エラーの形 expected_error で失敗しなければならない。定数は、真偽値、単項または二項の真偽値関数として宣言される。

pub struct NegativeCorpusCase {
  case_id : String
  bool_consts : Array[String]
  unary_bool_consts : Array[String]
  binary_bool_consts : Array[String]
  script : String
  layout : ScriptLayout
  failure_class : NegativeFailureClass
  expected_error : NegativeErrorShape
}

UnfinishedCorpusCase

未完了ケースは、指定されたステップ、分岐パス、hole で、指定された詳細とともに停止しなければならない。

pub struct UnfinishedCorpusCase {
  case_id : String
  const_names : Array[String]
  expected_goal : String
  script : String
  layout : ScriptLayout
  expected_step_index : Int
  expected_branch_path : Array[Int]
  expected_hole_name : String?
  expected_detail : String
}

QuantifierCorpusCase と QuantifierSurfaceOutcome

量化子のケースは、束縛子と生の forall の表層を扱い、成功、失敗、未完了のいずれにもなり得る。

pub struct QuantifierCorpusCase {
  case_id : String
  script : String
  layout : ScriptLayout
  kind : MappingCaseKind
  outcome : QuantifierSurfaceOutcome
  capability_label : String
  manual_anchor : String
  expected_step_index : Int?
  expected_branch_path : Array[Int]
  expected_hole_name : String?
  expected_detail : String?
}

pub enum QuantifierSurfaceOutcome {
  QuantifierSuccess
  QuantifierFailure
  QuantifierUnfinished
}

分類の列挙型

これらの列挙型はケースを分類する。以下のラベル関数は、それらをテストとドキュメントで使われる安定した文字列として出力する。

pub enum ScriptLayout {
  InlineSequential
  BlockSequential
  BranchStructured
}

pub enum PositiveCatalogClass {
  LocalFact
  DirectClose
  ImplicationBacked
  ContextDerived
  StructuralOnly
}

pub enum PositiveTacticCapability {
  IntroExact
  Assumption
  ExactNamedDirectClose
  ExactContextDerived
  ApplyLocalImp
  ApplyNamedImp
  Split
  Left
  Right
  Mixed
}

pub enum NegativeFailureClass {
  FailureUnknownName
  FailureWrongModeTheoremUsage
  FailureGoalShapeMismatch
  FailureNonBoolConnector
  FailureUnsupportedHonestFailure
  FailureShadowingOrScopeDrift
}

pub enum NegativeErrorShape {
  ErrorTacticUnknownName
  ErrorTacticApplyMismatch
  ErrorTacticGoalShapeMismatch
  ErrorTacticUnsolvedGoals
  ErrorLogicNotBoolTerm
}

script_layout_label、catalog_label、capability_label、negative_failure_class_label、negative_error_shape_label と quantifier_surface_outcome_label

これらの関数は、分類値の安定したスネークケースのラベルを返す。例えば inline_sequential、local_fact、intro_exact である。

pub fn script_layout_label(ScriptLayout) -> String
pub fn catalog_label(PositiveCatalogClass) -> String
pub fn capability_label(PositiveTacticCapability) -> String
pub fn negative_failure_class_label(NegativeFailureClass) -> String
pub fn negative_error_shape_label(NegativeErrorShape) -> String
pub fn quantifier_surface_outcome_label(QuantifierSurfaceOutcome) -> String

対応表

対応表は、各コーパスケースを、それが現れ得るドキュメントのアンカー(例えば manual:runnable_examples)に結びつけ、公開された例かどうかを示す。

MappingMatrixEntry と mapping_matrix_entries

pub struct MappingMatrixEntry {
  case_id : String
  kind : MappingCaseKind
  script : String
  catalog_or_failure_label : String
  capability_or_error_label : String
  doc_anchor : String
  doc_visibility : MappingDocVisibility
}

pub fn mapping_matrix_entries() -> Array[MappingMatrixEntry]

MappingCaseKind、mapping_case_positive、mapping_case_negative、mapping_case_unfinished

MappingCaseKind は、ケースが肯定的か、否定的か、未完了かを表す。三つの関数はそのコンストラクタを返す。

pub enum MappingCaseKind {
  PositiveCase
  NegativeCase
  UnfinishedCase
}

pub fn mapping_case_positive() -> MappingCaseKind
pub fn mapping_case_negative() -> MappingCaseKind
pub fn mapping_case_unfinished() -> MappingCaseKind

MappingDocVisibility とそのコンストラクタ関数

MappingDocVisibility は、ケースをドキュメントで例として引用してよいか、失敗例として引用してよいか、まったく引用してはならないかを表す。mapping_doc_visibility_public_unfinished_example は PublicExample を返す。すなわち、未完了の例は公開例である。

pub enum MappingDocVisibility {
  PublicExample
  PublicFailureExample
  InternalOnly
}

pub fn mapping_doc_visibility_public_example() -> MappingDocVisibility
pub fn mapping_doc_visibility_public_failure_example() -> MappingDocVisibility
pub fn mapping_doc_visibility_public_unfinished_example() -> MappingDocVisibility
pub fn mapping_doc_visibility_internal_only() -> MappingDocVisibility
test "corpus" {
  let first = @prover.positive_corpus_cases()[0]
  inspect(first.case_id, content="pos_intro_exact_identity")
  inspect(first.script, content="theorem pos_intro_exact_identity : ⊢ P -> P := by intro h; exact h")
  inspect(@prover.capability_label(first.capability), content="intro_exact")
  // every corpus case appears in the mapping matrix with an anchor
  let entry = @prover.mapping_matrix_entries().search_by(e => e.case_id == first.case_id).unwrap()
  inspect(@prover.mapping_matrix_entries()[entry].doc_anchor, content="manual:runnable_examples")
}