aesop

aesop は汎用的かつ強力な自動証明のためのタクティクです。

Automated Extensible Search for Obvious Proofs (自明な証明の拡張可能な自動探索)からその名があります。様々なタクティクやルールを使用しながら最良優先探索を行い、証明を自動で終わらせようとします。

import Aesop -- `aesop` を使用するため
import Mathlib.Tactic.Says

-- 以下 `X` `Y` `Z`を集合とする
variable {X Y Z : Type}

/-- 関数の単射性 -/
def Function.Injective (f : X → Y) : Prop := ∀ ⦃a₁ a₂ : X⦄, f a₁ = f a₂ → a₁ = a₂

open Function

-- 合成 `g ∘ f` が単射なら、`f` も単射
example {f : X → Y} {g : Y → Z} (hgfinj : Injective (g ∘ f)) : Injective f := by
  rw [Injective]
  show ∀ ⦃a₁ a₂ : X⦄, f a₁ = f a₂ → a₁ = a₂

  -- `simp_all` では示せない
  fail_if_success simp_all

  -- 示すべきことがまだまだあるように見えるが、一発で証明が終わる
  aesop

aesop?

aesop が成功するとき、aesop? に置き換えるとゴールを達成するのにどんなタクティクを使用したか教えてくれます。

example {f : X → Y} {g : Y → Z} (hgfinj : Injective (g ∘ f)) : Injective f := by
  rw [Injective]
  aesop? says
    intro a₁ a₂ a
    apply hgfinj
    simp_all only [comp_apply]

上記の例により、とくに aesop が実行の過程で simp_all タクティクや intro タクティク等を使用することがわかります。 特に、aesopsimp_all の強化版であるということができます。 実際には aesopsimp_all とは異なり、単純化だけでなく「試行錯誤しながらよい証明を探索する」ということができます。これについて詳しくは add_aesop_rules のページを参照してください。

カスタマイズ

aesop はユーザがカスタマイズ可能です。補題やタクティクを登録することで、証明可能な命題を増やすことができます。

/-- 自然数 n が正の数であることを表す命題 -/
inductive Pos : Nat → Prop
  | succ n : Pos (n + 1)

example : Pos 1 := by
  -- ルールが登録されていないので、`aesop` で示すことはできない
  fail_if_success aesop

  -- 手動でコンストラクタを `apply` することで証明できる
  apply Pos.succ

-- `Pos` 関連のルールを `aesop` に憶えさせる
attribute [aesop safe constructors] Pos

-- `aesop` で証明できるようになった!
example : Pos 1 := by aesop

上記の例のように [aesop] 属性によってルールを追加することもできますし、add_aesop_rules というコマンドでルールを追加することもできます。

example (n : Nat) (h : Pos n) : 0 < n := by
  -- ルールが足りないので、`aesop` で示すことはできない
  fail_if_success aesop

  -- 手動で `h : Pos n` を分解して証明する
  rcases h with ⟨h⟩
  simp

-- `Pos` 関連のルールを `aesop` に追加
add_aesop_rules safe cases [Pos]

-- `aesop` で証明できるようになった!
example (n : Nat) (h : Pos n) : 0 < n := by
  aesop

カスタマイズ方法の詳細を知りたい方はaesopのリポジトリをご参照ください。また、内部のロジックの詳細については論文 Aesop: White-Box Best-First Proof Search for Lean で説明されています。