0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Lean4 入門: 排中律⇔二重否定除去⇔パース則

Posted at

はじめに

Coq と似た後発の定理証明支援に LEAN というのがあって、VSCode上でやるならかなり快適。少し入門してみたので、簡単に証明できるいつものやつ、つまり 排中律 ⇔ 二重否定除去 などを証明しながらCoqとの違いを感じてみます。

ざっくりいって Lean4 は Coq とおなじ CIC のための道具で、

  • Coqのようにタクティックでの証明もできるし
  • Agdaのように対話的に直接証明項を構成できるし
  • それらを一つの証明の中で混ぜながらもできる
  • UTF8 を使った多彩な記号を使った syntax でみやすい

といったなかなかイケてる仕様であり、少し触っただけでもVSCodeの補完・Copilot機能とあいまってかなり快適に定理証明ができそうだという感触をもちました

本文

まずは排中律とかを定義してみます:

def EM := forall (P:Prop), P  ¬P
def DNE := forall (P:Prop), ¬¬P  P
def MORGAN4 := forall (P Q:Prop), ¬(¬P  ¬Q) -> P  Q
def PEIRCE := forall (P Q:Prop), ((P  Q)  P)  P
def IMPLCASE:= forall (P Q:Prop), (P  Q)  ¬P  Q

この5つの同値性を証明しながら Lean らしさをさがしてみます。タクティックを使う証明はCoqとあまり変わらないので、証明項を直接構成してみます。

まず EM↔IMPCASE をみてみます:

theorem em_impcase_iff : EM  IMPLCASE := 
  (fun (em:EM) P Q (h: P Q) => show (¬P  Q) from 
    match (em P) with
    | Or.inl hp => Or.inr (h hp)
    | Or.inr hnp => Or.inl hnp)
  ,
  (fun (h: IMPLCASE) P => show (P  ¬P) from
    Or.comm.mp (h P P id))
  

軽く説明です:

  • Iffは単一のコンストラクタのみを持つのでLeanの anonymous constructor とよばれる ⟨ h1 , h2 ⟩ という形で書くことができます。一つ目が⇒の証明項、二つ目が⇐の証明項です。

  • ⇒の証明: いわゆる intros タクティックに相当する fun によるラムダ抽象をし終わったあとのGoalを show でアノテートすることで可読性をあげています(なくても通ります)。排中律のおかげで P∨¬P の証明項が得られるので場合分けで分析できます。一つ目のブランチでは P を仮定して証明項を構成し、二つ目のブランチでは ¬P を仮定して証明項を構成します。matchを使いましたが Or.elim のような除去則を使っても同じです。

  • ⇐の証明:IMPCASEのQにPを代入すると即座に¬P∨Pが得られるのでOr.commでひっくり返してできあがりです。P→Pの証明項は 恒等関数 id です。

続いて EM->DNE,DNE->MORGAN4,MORGAN4->EM というループからこの3つが同値であることを示します:

theorem em_imp_dne : EM  DNE := fun (em: EM) (P: Prop) (h: ¬¬P) =>
  show P from Or.elim (em P) id (fun hnp => absurd hnp h)

theorem dne_imp_morgan4 : DNE  MORGAN4 := fun (dne: DNE) (P Q: Prop) (h: ¬(¬P  ¬Q)) =>
  suffices h1: ¬¬(P  Q) from (dne _ h1)
  fun h2 => h (fun hp => h2 (Or.inl hp)), (fun hq => h2 (Or.inr hq))

theorem morgan4_imp_em : MORGAN4  EM := fun (m4: MORGAN4) (P: Prop) =>
  have h: ¬(¬P  ¬¬P) := fun h1 => h1.right (fun hp => h1.left hp)
  m4 P (¬P) h
  • 1つめ: 先ほどは match を使いましたが今回は Or.elim という除去則を使っています。やることは同じです
  • 2つめでは suffices ? from ? をつかっています。これはCoqでいうところの enough タクティックです。二重否定が得られれば二重否定除去からすぐにGoalがえられるので、問題はこの二重否定をどう構成するかです。否定なので 1回だけ intro すなわちラムダ抽象ができ、 ¬(P ∨ Q) をコンテキストに加えて False がGoalになります。そこでコンテキストの ¬(¬P ∧ ¬Q)) にapply すれば結局 ¬P ∧ ¬Q を示せばよいとわかります。Andは単一のコンストラクタをもつので前述の anonymous constructor の記法が使えます
  • 3つめでは have を使っています。これは Coq での assert タクティックと同じでサブゴールを設定するものです。タクティックをつかった後ろからの推論ではなく、順繰りの人間にわかりやすい推論を提供できます。もちろんこれも suffices を使ってもよかったですし、どちらも使わないこともできました

Iff の証明は自明です:

theorem em_dne_iff : EM  DNE :=  em_imp_dne,
 fun (dne: DNE) => morgan4_imp_em (dne_imp_morgan4 dne) 

theorem em_morgan4_iff : EM  MORGAN4 := 
  (fun (em:EM) => dne_imp_morgan4 (em_imp_dne em)),
    morgan4_imp_em 

最後にパースの法則がらみの証明です


theorem peirce_imp_dne : PEIRCE  DNE := fun (peirce: PEIRCE) (P: Prop) (hnnp: ¬¬P) =>
  peirce P False (fun h: P  False => absurd h hnnp)

theorem em_imp_peirce : EM  PEIRCE := fun (em: EM) (P Q: Prop) (h: (P  Q)  P) =>
  Or.elim (em P) id (fun hnp => h (fun hp => absurd hp hnp ))

theorem em_peirce_iff : EM  PEIRCE := Iff.intro
  em_imp_peirce
  (fun (peirce: PEIRCE) => morgan4_imp_em (dne_imp_morgan4 (peirce_imp_dne peirce)))

特に新しい要素は出せませんでした

さいごに

タクティックによる証明は自動化しやすいのですが、いかんせん読みずらすぎます。だからといって直接証明項を構成するのも、少し複雑になった言明に対しては複雑になりすぎます。この中間あたりが私にとっては一番居心地がよく、LEANとエディタによる支援という環境はかなり心地が良いです。もう少しLeanを触っていこうかなとおもいます。みなさまもぜひどうぞ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?