前回はIdrisで「有界な自然数の冪乗を計算する」finPower2 : Fin n -> Fin (power 2 n) という関数を実装したが、いくつか後回しにした証明があった。今回はIdrisの証明支援機能を使ってこれを埋める。
まずはターミナルを開いて idris FinPower2.idr を実行しよう。(この記事はIdrisのインストールが終わっていることが前提になっている)
$ idris FinPower2.idr⏎ ____ __ _ / _/___/ /____(_)____ / // __ / ___/ / ___/ Version 0.9.16 _/ // /_/ / / / (__ ) http://www.idris-lang.org/ /___/\__,_/_/ /_/____/ Type :? for help Idris is free software with ABSOLUTELY NO WARRANTY. For details type :warranty. Metavariables: Main.proof3, Main.proof2, Main.proof1 *FinPower2>
最後から2番目の行に出てきた Metavariables: というのが、前回やり残した証明だ。:type コマンドで型を確認しておこう。
*FinPower2> :type proof1⏎
m : Nat
inductiveHypothesis : S (power2m1 m) = power (fromInteger 2) m
--------------------------------------
proof1 : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
Metavariables: Main.proof3, Main.proof2, Main.proof1
*FinPower2>
元々証明したい等式は S (power2m1 (S m)) = power 2 (S m) だったはずだが、コンパイラによってある程度簡約されている。他の proof2 と proof3 も確認しておこう。
*FinPower2> :type proof2⏎
m : Nat
--------------------------------------
proof2 : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
Metavariables: Main.proof3, Main.proof2, Main.proof1
*FinPower2> :type proof3⏎
m : Nat
k : Fin m
--------------------------------------
proof3 : plus (power 2 m) (power 2 m) =
plus (power 2 m) (plus (power 2 m) 0)
Metavariables: Main.proof3, Main.proof2, Main.proof1
*FinPower2>
証明・一個目
では早速 proof1 の証明を埋めよう。:prove コマンドを使うとインタラクティブな証明モードに入る。
*FinPower2> :prove proof1⏎
---------- Goal: ----------
{hole0} : (m : Nat) ->
(S (power2m1 m) = power (fromInteger 2) m) ->
S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
-Main.proof1>
Goal: 以下にあるのが示すべき命題だ。この状態では (m : Nat) と S (power2m1 m) = power (fromInteger 2) m という引数を取る関数の型になっている。論理で言うと含意(「ならば」)を使った形になっている。intro というtacticを使うと、「仮定」を導入して関数の引数を減らすことができる。
-Main.proof1> intro⏎
---------- Other goals: ----------
{hole0}
---------- Assumptions: ----------
m : Nat
---------- Goal: ----------
{hole1} : (S (power2m1 m) = power (fromInteger 2) m) ->
S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
-Main.proof1>
Assumptions: という項目に m : Nat が増えて、Goal: にあった関数の引数 (m : Nat) がなくなった。もう一回 intro を使うと、帰納法の仮定 (S (power2m1 m) = power (fromInteger 2) m) も Assumptions: に移動する。
-Main.proof1> intro⏎
---------- Other goals: ----------
{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
inductiveHypothesis : S (power2m1 m) = power (fromInteger 2) m
---------- Goal: ----------
{hole2} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
-Main.proof1>
ちなみに、intros というtacticを使うと、複数回の intro を一括でやってくれる。
Goal の左辺の plus (power2m1 m) (plus (power2m1 m) 0) は 2 * power2m1 m を簡約したもの、右辺の plus (power 2 m) (plus (power 2 m) 0) は 2 * power 2 m を簡約したものだと思われる。つまり、実質的に示すべき命題は
S (S (2 * power2m1 m)) = 2 * power 2 m
である。この右辺は帰納法の仮定 S (power2m1 m) = power 2 m を使うと 2 * (S (power2m1 m)) に置き換えられる。
この「置き換え」をやってくれるのが rewrite taticである。rewrite tacticに x=y という型の値(証明)を与えると、goal中の y を x で置き換えてくれる。
-Main.proof1> rewrite inductiveHypothesis⏎
---------- Other goals: ----------
{hole2},{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
inductiveHypothesis : S (power2m1 m) = power (fromInteger 2) m
---------- Goal: ----------
{hole3} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (S (power2m1 m)) (plus (S (power2m1 m)) 0)
-Main.proof1>
ごちゃごちゃして見にくいが、y = power2m1 m と置き換えてみると
S (S (y + (y + 0))) = S y + ((S y) + 0)
を証明しろということである。やっぱりごちゃごちゃしているが、compute tacticを使うと、goalを正規化してくれる(らしい)。
-Main.proof1> compute⏎
---------- Other goals: ----------
{hole2},{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
inductiveHypothesis : S (power2m1 m) = power (fromInteger 2) m
---------- Goal: ----------
{hole3} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
S (plus (power2m1 m) (S (plus (power2m1 m) 0)))
-Main.proof1>
これでも少し見にくいが、y = power2m1 m, z = (power2m1 m) + 0 と置き換えてみると、証明すべき命題は
S (S (y + z)) = S (y + (S z))
となっている。ペアノの公理に \(\mathrm{S }(y + z) = y + (\mathrm{S }z)\) みたいなのがあった気がするし、これは加法の割と基本的な性質である。この証明は標準ライブラリに用意されていて、
plusSuccRightSucc : (left : Nat) -> (right : Nat) -> S (left + right) = left + (S right)
という名前と型がついている。今の状況だと、
plusSuccRightSucc (power2m1 m) ((power2m1 m) + 0) : S (power2m1 m + (power2m1 m + 0)) = power2m1 m + S (power2m1 m + 0)
とやれば欲しい命題になる。これを使って rewrite してやればよい。
-Main.proof1> rewrite plusSuccRightSucc (power2m1 m) ((power2m1 m) + 0)⏎
---------- Other goals: ----------
{hole3},{hole2},{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
inductiveHypothesis : S (power2m1 m) = power (fromInteger 2) m
---------- Goal: ----------
{hole7} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
S (S (plus (power2m1 m) (plus (power2m1 m) 0)))
-Main.proof1>
示すべき命題が自明になった。trivial と打ち込む。
-Main.proof1> trivial⏎ proof1: No more goals. -Main.proof1>
証明が終わった。qed と打ち込むと証明モードが終わる。
-Main.proof1> qed⏎ Proof completed! Main.proof1 = proof intros rewrite inductiveHypothesis compute rewrite plusSuccRightSucc (power2m1 m) ((power2m1 m) + 0) trivial Metavariables: Main.proof3, Main.proof2 *FinPower2>
:addproof コマンドを実行すると、もとのソースコードに今作った証明が書き込まれる。
*FinPower2> :addproof⏎ Added proof Main.proof1 Metavariables: Main.proof3, Main.proof2 *FinPower2>
FinPower2.idr を確認すると、次のようなものが書き込まれている。
(略) ---------- Proofs ---------- Main.proof1 = proof intros rewrite inductiveHypothesis compute rewrite plusSuccRightSucc (power2m1 m) ((power2m1 m) + 0) trivial
証明・二個目
*FinPower2> :prove proof2⏎
---------- Goal: ----------
{hole0} : (m : Nat) ->
S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
-Main.proof2> intros⏎
---------- Other goals: ----------
{hole0}
---------- Assumptions: ----------
m : Nat
---------- Goal: ----------
{hole1} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (power 2 m) (plus (power 2 m) 0)
ここで、前回用意して、さっき証明を完成させた
succPower2m1Power2 : (k : Nat) -> S (power2m1 k) = power 2 k
が活きる。
-Main.proof2> rewrite succPower2m1Power2 m⏎
---------- Other goals: ----------
{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
---------- Goal: ----------
{hole2} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
plus (S (power2m1 m)) (plus (S (power2m1 m)) 0)
-Main.proof2> compute⏎
---------- Other goals: ----------
{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
---------- Goal: ----------
{hole2} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
S (plus (power2m1 m) (S (plus (power2m1 m) 0)))
あとはさっきと同じパターンである。
-Main.proof2> rewrite plusSuccRightSucc (power2m1 m) (power2m1 m + 0)⏎
---------- Other goals: ----------
{hole2},{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
---------- Goal: ----------
{hole7} : S (S (plus (power2m1 m) (plus (power2m1 m) 0))) =
S (S (plus (power2m1 m) (plus (power2m1 m) 0)))
-Main.proof2> trivial⏎
proof2: No more goals.
-Main.proof2> qed⏎
Proof completed!
Main.proof2 = proof
intros
rewrite succPower2m1Power2 m
compute
rewrite plusSuccRightSucc (power2m1 m) (power2m1 m + 0)
trivial
Metavariables: Main.proof3
*FinPower2> :addproof⏎
Added proof Main.proof2
*FinPower2>
証明・三個目
*FinPower2> :prove proof3⏎
---------- Goal: ----------
{hole0} : (m : Nat) ->
Fin m ->
plus (power 2 m) (power 2 m) =
plus (power 2 m) (plus (power 2 m) 0)
-Main.proof3> intros⏎
---------- Other goals: ----------
{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
k : Fin m
---------- Goal: ----------
{hole2} : plus (power 2 m) (power 2 m) =
plus (power 2 m) (plus (power 2 m) 0)
示すべき命題の何が自明じゃないかと言ったら、power 2 m = power 2 m + 0 である。つまり、右から 0 を足しても値は変わらないということである。これは
plusZeroRightNeutral : (left : Nat) -> left + 0 = left
を使えば良い。
-Main.proof3> rewrite plusZeroRightNeutral (power 2 m)⏎
---------- Other goals: ----------
{hole2},{hole1},{hole0}
---------- Assumptions: ----------
m : Nat
k : Fin m
---------- Goal: ----------
{hole3} : plus (plus (power 2 m) 0) (plus (power 2 m) 0) =
plus (plus (power 2 m) 0) (plus (power 2 m) 0)
-Main.proof3> trivial⏎
proof3: No more goals.
-Main.proof3> qed⏎
Proof completed!
Main.proof3 = proof
intros
rewrite plusZeroRightNeutral (power 2 m)
trivial
*FinPower2> :addproof⏎
Added proof Main.proof3
*FinPower2>
最終的な FinPower2.idr の内容
import Data.Fin
power2m1 : Nat -> Nat
power2m1 Z = Z
power2m1 (S k) = S (2 * power2m1 k)
succPower2m1Power2 : (k : Nat) -> S (power2m1 k) = power 2 k
succPower2m1Power2 Z = Refl
succPower2m1Power2 (S m) =
let inductiveHypothesis = succPower2m1Power2 m
in ?proof1
finPower2 : {n : Nat} -> Fin n -> Fin (power 2 n)
finPower2 {n = S m} FZ = f (FS FZ)
where
p : S (S (2 * power2m1 m)) = power 2 (S m)
p = ?proof2
f : Fin (S (S (2 * power2m1 m))) -> Fin (power 2 (S m))
f = replace {P=Fin} p
finPower2 {n = S m} (FS k) = let x = finPower2 k
in replace p (x + x)
where
p : (power 2 m) + (power 2 m) = power 2 (S m)
p = ?proof3
---------- Proofs ----------
Main.proof3 = proof
intros
rewrite plusZeroRightNeutral (power 2 m)
trivial
Main.proof2 = proof
intros
rewrite succPower2m1Power2 m
compute
rewrite plusSuccRightSucc (power2m1 m) (power2m1 m + 0)
trivial
Main.proof1 = proof
intros
rewrite inductiveHypothesis
compute
rewrite plusSuccRightSucc (power2m1 m) ((power2m1 m) + 0)
trivial
参考資料
- Tutorial
- Wikiのマニュアル
- Theorem Proving (注意書きにあるように、古い)

ピンバック: Idrisの有界な自然数で遊んでみる(0) | 雑記帳