MathLabs

数学の基礎

証明支援系による形式証明(Lean)

Leanのような証明支援系は、コンパイラがプログラムを検査するのと同じ方法で数学の証明を検査する:「この証明は正しいか」という問いを、型判定 Γ⊢t:P\Gamma \vdash t : P が成り立つかという問いに帰着させ、小さな信頼されたカーネルが検証する。

直観読み切れない証明をなぜ信頼できるのか

Lean 4の数学ライブラリMathlibは今や数百万行の証明を持ち、誰も一行ずつ読み切れる量ではない。それでも数学者たちはそれを信頼している。エレベーター検査の合格印を信じてケーブルを自分で溶接し直さないのと同じ理由だ:小さく固定された検査手順(カーネル)がすべての一手を確認しており、カーネル自体は手で検証できるほど短い。

補題をノード、それらの論理的依存関係を有向辺として示す対話的グラフ。
検査済み証明の依存グラフ:各ノードは補題、各辺は以前の結果の利用を表し、カーネルはすべての辺をたどる。

大学帰納的構成の計算

定義: 項、型、そして型判定

Leanの中核論理は帰納的構成の計算(CIC)であり、型が値に依存できる依存型理論である(依存関数型 Π (x:A) B(x)\Pi\,(x:A)\,B(x) は「すべての x:Ax:A に対し型 B(x)B(x) の値がある」と読む)。新しい型は帰納的宣言(自然数、リスト、命題としての等号型)によって作られる。すべての対象は型判定 Γ⊢t:P\Gamma \vdash t : P「文脈 Γ\Gamma のもとで、項 tt は型 PP を持つ」によって分類される。

Γ⊢t:P\Gamma \vdash t : P

カリー・ハワード対応のもとでは、命題 PP 自体が一つの型であり、PP の証明とはその型の項 tt である——証明することはプログラムすることに等しい。これが、プログラムをコンパイルするために元々必要だった型検査アルゴリズムが、証明の検査にも十分である理由である。

⊢t:P  ⟹  P\vdash t : P \;\Longrightarrow\; P
証明支援系の二つの層
層仕事信頼
エラボレータ/タクティク高水準タクティクスクリプトから証明項 tt を探索非信頼:ここのバグは時間の浪費に過ぎない
カーネルtt が本当に型 PP を持つか再検査信頼される:これがデ・ブラウン基準

発展デ・ブラウン基準とEq.recによる等号

デ・ブラウン基準とは、証明支援系が信頼できるのは、小さく独立に検査可能なカーネルがすべての証明項を確認する場合であり、それゆえ ∣kernel∣≪∣tactic engine∣|\text{kernel}| \ll |\text{tactic engine}| が成り立つ——健全性は数百行のコードに依拠し、その項を生成したタクティクエンジンには依拠しない、という基準である。帰納的等号 a=ba = b 自体は単一の構成子 refl:∀ a, a=a\mathrm{refl} : \forall\, a,\ a = a によって定義され、その除去子 Eq.rec\mathsf{Eq.rec}(J規則)は、カーネルが等号の他のすべての性質を導くために必要な唯一の原始概念である。

Eq.rec:{C:∀ y, a=y→Sort}→C a (refl a)→∀ y (h:a=y), C y h\mathsf{Eq.rec} : \{C : \forall\, y,\ a=y \to \mathrm{Sort}\} \to C\,a\,(\mathrm{refl}\,a) \to \forall\, y\,(h:a=y),\, C\,y\,h

refla:a=a\mathrm{refl}_a : a = a と Eq.rec\mathsf{Eq.rec} のみから構成される項 Eq.symm:a=b→b=a\mathrm{Eq.symm} : a = b \to b = a が存在する。対称性は追加の公理ではなくCICの定理である。

なぜ正しいのか?

対称性、推移性、合同性などのために等号ごとに別の公理が必要だとすれば、信頼されたカーネルは == に関する新しい事実ごとに肥大化してしまう。すべてを一つの除去子から導くことでカーネルを小さく保てる。

証明

aa を固定する。任意の bb について h:a=bh : a = b を受け取り b=ab = a の証明を返す関数が欲しい。モチーフ C(y,h):=(y=a)C(y, h) := (y = a) を用いて Eq.rec\mathsf{Eq.rec} を適用する——これは点 yy と aa を yy に結ぶ証明 hh で添字付けられた命題の族である。

除去子は基底ケース C(a,refl a)C(a, \mathrm{refl}\,a)、すなわち a=aa = a の証明を要求する。refl a\mathrm{refl}\,a 自身を与えればよい。基底ケースは常に出発点 aa における refl\mathrm{refl} で添字付けられるため、これは正当である。

Eq.rec\mathsf{Eq.rec} は、すべての yy とすべての h:a=yh : a = y に対して C(y,h)=(y=a)C(y,h) = (y = a) の証明を返す。y:=b, h:=hy := b,\ h := h を代入すれば、まさに b=ab = a の証明が得られる。

これを Eq.symm h:=Eq.rec (C:=λy h, y=a) (refl a) b h\mathrm{Eq.symm}\,h := \mathsf{Eq.rec}\,(C := \lambda y\, h,\, y = a)\,(\mathrm{refl}\,a)\,b\,h としてまとめると、型 Eq.symm:a=b→b=a\mathrm{Eq.symm} : a = b \to b = a の項が得られる。カーネルはこの適用を Eq.rec\mathsf{Eq.rec} の型シグネチャと照合するだけで受理し、「対称性」という概念について推論する必要はカーネルの水準では一切ない。

Eq.rec\mathsf{Eq.rec} のみから構成される項 Eq.trans:a=b→b=c→a=c\mathrm{Eq.trans} : a = b \to b = c \to a = c が存在する。

なぜ正しいのか?

等式を連鎖させること(a=b=ca=b=c ゆえに a=ca=c)はほぼすべての証明で使われる。それが導出された項ではなく公理であれば、カーネルは計算によってそれを検証するのではなく、その公理を信頼せざるを得なくなる。

証明

a,ba,b と h1:a=bh_1 : a = b を固定する。任意の cc に対する h2:b=ch_2 : b = c が与えられたとき、a=ca = c の証明が欲しい。動機 C(y,h):=(a=y)C(y, h) := (a = y)(点 yy と bb を yy に結ぶ証明 hh で添字付け)を用いて h2h_2 に Eq.rec\mathsf{Eq.rec} を適用する。

必要な基底ケースは C(b,refl b)C(b, \mathrm{refl}\,b)、すなわち a=ba = b であり、これはまさに h1h_1 である。よって h1h_1 が Eq.rec\mathsf{Eq.rec} に与える基底ケースの証明となる。

Eq.rec\mathsf{Eq.rec} は、すべての cc とすべての h2:b=ch_2 : b = c に対して C(c,h2)=(a=c)C(c, h_2) = (a = c) の証明を返す。これがまさに求めていたものである。

よって Eq.trans h1 h2:=Eq.rec (C:=λy h, a=y) h1 c h2\mathrm{Eq.trans}\,h_1\,h_2 := \mathsf{Eq.rec}\,(C := \lambda y\, h,\, a = y)\,h_1\,c\,h_2 は型 Eq.trans:a=b→b=c→a=c\mathrm{Eq.trans} : a = b \to b = c \to a = c を持つ。対称性と共通するパターンに注目せよ:どちらの証明も適切な動機 CC を選ぶことで事実を等号に沿って「転送」し、実際の作業は refl\mathrm{refl} における Eq.rec\mathsf{Eq.rec} に対するカーネルの固定された還元規則に委ねている。

大学実世界での応用:コンパイラから数論まで

形式証明はもはや実験室だけの珍品ではない。Coqで形式検証されたCコンパイラCompCertは、生成されたアセンブリが常にソースプログラムと同じ振る舞いをすることの機械検査済み証明を持つ——これはどんなテストスイートも与えられない保証である。AWSとSignalは形式検証された暗号コード(AWSのs2n TLSライブラリ、HACL、Fなどのツール)を用い、メモリ安全性とサイドチャネルに関するバグの一群をまるごと排除している。純粋数学では、Leanの液体テンソル実験(2020–2022)がPeter Scholzeの凝縮数学における難しい定理を形式化し、Terence Taoのチームは2023年に人間による証明が発表されてから数か月のうちに多項式Freiman–Ruzsa(PFR)予想の証明をLeanで形式化した。

例: n+0=nn+0=n の証明の型検査

カーネルの水準で、帰納法により構成された ∀ n:N, n+0=n\forall\, n : \mathbb{N},\ n + 0 = n の項がなぜ受理されるかを示せ。

解答

N\mathbb{N} 上の加法は第二引数に関する再帰で定義される:n+0:=nn + 0 := n、n+succ m:=succ (n+m)n + \mathrm{succ}\,m := \mathrm{succ}\,(n+m)。よってゴール n+0=nn + 0 = n の基底ケースは計算によってちょうど n=nn = n に還元され、それは refl n\mathrm{refl}\,n である——カーネルは論理的な作業なしに、単に定義を展開するだけでこれを受理する。

一般的な定理 ∀n, n+0=n\forall n,\ n + 0 = n については、この基底ケースは上の定義から自明であり、この特定の命題には帰納法さえ不要である(第二引数に関する再帰であるため帰納法が必要な ∀n, 0+n=n\forall n,\ 0 + n = n とは対照的である)。

カーネルの役割は単に、項 n+0n+0 に対して ++ を再帰的定義に従って展開し nn に到達し、ゴールの両辺が定義的に等しいことを確認することであり、「この証明は正しいか」を停止する計算に帰着させる——これこそデ・ブラウン基準が働いている様子である。

例: 型付き等号としての検証済みコンパイル

CompCertは正しさの定理を意味論保存性として述べる。「証明はその型の項である」ことが、通常のコンパイラテストに比べエンジニアにとって何をもたらすかを説明せよ。

解答

CompCertの定理は「アセンブリ AA にコンパイルされるすべてのソースプログラム SS について、AA の観測可能な振る舞いはすべて SS の振る舞いである」という形をしており、テストスイートに含まれるものだけでなくすべてのソースプログラムに関する全称量化された言明である。

テストスイートは有限個の (S,A)(S,A) の組しか検査できず、4,000,001番目のプログラムでの誤コンパイルを排除することは決してできない。全称量化された言明のCoq証明項 tt は、その代わりに量化子全体について一度だけカーネルによって型検査される——他のすべてのCompCertの補題に使われるのと同じカーネルであり、テストケースごとに新しい信頼が持ち込まれることはない。

証明項が全域的であり、定理がコンパイラの実際のCoqソースコード(OCamlに抽出される)について述べているため、保証を破るコンパイラへの変更は単に型検査に失敗する。これは有限の回帰テストスイートが完全に見逃すような回帰を捕捉する——これこそがCompCertが生成するコードにおいて、GCCやClangで数百件見つかったファジングキャンペーンによっても誤コンパイルバグがゼロであった理由である。

型判定 Γ⊢t:P\Gamma \vdash t : P は何を主張するか?

デ・ブラウン基準を最もよく要約すると:

Eq.symm:a=b→b=a\mathrm{Eq.symm} : a = b \to b = a の証明において、基底ケース C(a,refl a)C(a, \mathrm{refl}\,a) に対してどの命題が与えられるか?

生成されたアセンブリが常にソースプログラムのように振る舞うことの機械検査済み証明に依拠する実世界のシステムはどれか?

参考文献

  1. Leonardo de Moura, Sebastian Ullrich (2021). The Lean 4 Theorem Prover and Programming Language
  2. Xavier Leroy (2009). Formal verification of a realistic compiler
  3. Terence Tao, et al. (2023). A Proof of the Polynomial Freiman-Ruzsa Conjecture · arXiv:2311.05762