よくわかるCoqプログラミング2. Coq とは ?
Coq はINRIA (フランス国立情報学自動制御研究所) によって開発されている定理証
明支援系である.Coq は,表現力の高い型をもつラムダ計算であるCalculus of
Inductive Constructions と呼ばれる計算体系に基づいている(以後,「項」はCoq の
型システムにより拡張されたラムダ項を指すものとする).型や関数を自ら定義すること
により,関数型プログラミング言語として用いることができる. また,Curry-Howard 対
応により,命題に対応づけられる型をもつ項を構成することにより,証明を構成すること
ができる.Coq の型システムは依存型をもち,関数をはじめとした項をパラメータとし
て取る型を定義することにより,述語を表現することができる.さらに,それら述語を
tactic と呼ばれる言語を用いて対話的に証明することが可能である.
( “定理証明支援系Coqを用いたプログラム運算” より引用 )
3. Coq とは ?
「定理証明支援系」と呼ばれる物
関数型言語の文法で定義や関数を記述する事ができる
命題や関数に対して対話的に自然演繹をベースとした
証明を構築する事ができる
内部で定義した関数を他の言語で書き出す事ができる
関数の性質を証明しながらプログラムが作れるので安全
なプログラムが書ける!!
4. 自然演繹
「自然な」証明体系をモデルとして提供する証明論理の
手法
単純な規則を組み合わせる事により証明を構築していく
OR記号(∨)の交換法則の証明
������ ������
[������ ∨ ������] ������ ∨ ������ ∨ ������ ������ ∨ ������ (∨ ������)
(∨ ������)
������ ∨ ������ (⇒ ������)
������ ∨ ������ ⇒ ������ ∨ ������
藤田先生の講義資料がわかりやすいよ!
5. 規則の一部
������ ������ ������ ∧ ������ ������ ∧ ������
∧ ������ ∧ ������ ∧ ������
������ ∧ ������ ������ ������
������ [������]
������ ������ ������ ∨ ������ ⋮ ⋮
∨ ������ ∨ ������ ������ ������ ∨ ������
������ ∨ ������ ������ ∨ ������ ������
[������]
⋮
������ ������ ⇒ ������ ������ ¬������ ������
⇒ ������ ⇒ ������ ¬������
������ ⇒ ������ ������ ������
7. 例:ド・モルガンの法則
¬������ ∨ ¬������ ⇒ ¬(������ ∧ ������) の証明
2 1 3 1
������ ¬������ ������ ¬������
5
⊥ ⊥ ¬������ ∨ ¬������
1
⊥
2
������ ⇒⊥ 3 ������ ∧ ������ 4 4
������ ⇒ ������ ⇒⊥ ������ ������ ∧ ������
������ ⇒⊥ ������
⊥ 4
¬(������ ∧ ������) 5
¬������ ∨ ¬������ ⇒ ¬(������ ∧ ������)
注) 「⊥」は矛盾を示す定数, 「¬P」は「P⇒⊥」の略記
13. 完全に一致
������: ������ [������]
⋮ ⋮
������: ������ (������������������) ������ ⇒ ������
������������. ������: ������ → ������ ������ ⇒ ������
������: ������ → ������ ������: ������ ������ ⇒ ������ ������
(������������������) ⇒ ������
������������ ∶ ������ ������
15. Coqことはじめ
Coq公式ページ ( http://coq.inria.fr/ ) からインストー
ラーをダウンロードできる
実行するとCoq本体と「Coq IDE」というGUI環境がインス
トールされる
16. [������]
Coqでの証明の例 ������ ⇒ ������
• 線が引いてあるのがユー
ザーの入力
• 途中に現れている出力が前
提(既知の部分)と目標(未
知の部分)を示している
(IDEだと入力エリアの横に
表示される)
18. 操作の例(一部)
intro → , ~ , ∀に対しての導入 “forall P : …” や
“A → …”の前半を前提に追加
intros 可能な限りintroを実行する
し後半を新たな目標とする
apply (H P) →, ∀に対しての除去 目標の中のPに定理Hを適応した
物を新たな目標とする
case H ∨, ∧に対しての除去 P∨QなるHと目標GからP→Gと
Q→Gの2つを新たな目標にする
HがP∧Qの場合は P→Q→Gを新た
な目標にする
destruct H caseと同じ働きだがHを前提から
消去しintrosを行う
rewrite H = (等号)に対しての除去 等号を含んでいる定理Hを用いて
目標を書き換える
unfold f 関数の展開 目標の中の関数fを定義式に書き
換える
19. 基本的な方針
前提に以下の物がある時
H:forall X, A->B 目標がBであれば apply H
H:~A 目標がFalseであれば apply H
H:A∧B case H, destruct H
H:A∨B
目標に以下の物がある時
forall X, A intro, intros
A->B
~A intro, unfold
その他 SerchPattern という命令が存在する.
コレを使えば使えそうな定理を探す事ができる
これらを繰り返す事で機械的に証明を行う事ができる
20. 証明の例
∧, ∨の交換法則
ド・モルガンの定理
������ ⇒ ������ ⇒ ������ ⇒ ������ ⇒ (������ ⇒ ������)
������ ∨ ¬������ ⇒ ¬¬������ ⇒ ������
21. ∨の交換法則
2 ������ 1 ������ 1
������ ∨ ������ ������ ∨ ������ ∨ ������ ������ ∨ ������ ∨ ������ (∨ ������1 )
������ ∨ ������ (⇒ ������ 2 )
������ ∨ ������ ⇒ ������ ∨ ������
Fact orsw : forall (A B:Prop), A∨B->B∨A. (*宣言*)
intros.
case H. (* H:A∨B から A→B∨A と B→B∨A を目標とする*)
intro.
apply or_intror. (* or_intror : forall A B:Prop, B->A∨B *)
apply H0. (* H0:A *)
intro.
apply or_introl. (* or_introl : forall A B:Prop, A->A∨B *)
apply H0.
Qed.
22. ∧の交換法則
������ ∧ ������ 1 ������ ∧ ������ 1
������ ������
������ ∧ ������ (⇒ ������1 )
������ ∧ ������ ⇒ ������ ∧ ������
Goal forall (P Q:Prop), P∧Q->Q∧P.
intros.
case H. (* H:P∧Q *)
intros.
apply conj. (* conj:forall A B:Prop, A→B→A∧B *)
apply H1.
apply H0.
Qed.
23. ド・モルガンの法則
2 1 3 1
������ ¬������ ������ ¬������
⊥ ⊥ 5
¬������ ∨ ¬������
1
⊥
2
Goal forall (P Q:Prop), ~P∨~Q -> ~(P∧Q). ������ ⇒⊥ ������ ∧ ������ 4
intros. ������ ⇒ ������ ⇒⊥
3
������ ������ ∧ ������ 4
intro. ������ ⇒⊥ ������
case H0. (* H0:P∧Q *) ⊥ 4
intros. ¬(������ ∧ ������) 5
case H. (* H:¬P∨¬Q *) ¬������ ∨ ¬������ ⇒ ¬(������ ∧ ������)
intro.
apply H3. (* H3:¬P *)
apply H1. (* H1:P *)
intro.
apply H3.
apply H2. (*H2:Q*)
Qed.
24. 三段論法的な何か
������ ⇒ ������ 3 ������ 1
(⇒ ������) [������ ⇒ ������]2
������ (⇒ ������)
������ ⇒ ������1
������ ⇒ ������ ⇒ ������ 2
������ ⇒ ������ ⇒ ������ ⇒ ������
(⇒ ������ 3 )
������ ⇒ ������ ⇒ ������ ⇒ ������ ⇒ ������ ⇒ ������
Goal forall (A B C:Prop), (B->C)->(A->B)->(A->C).
intros.
apply H.
apply H0.
apply H1.
Qed.
25. 背理法 2 1
¬������ ¬¬������
⊥
������ 2
������ ⇒ ������ 1 ������ ∨ ¬¬������ 3
¬¬������ ⇒ ������ ¬¬������ ⇒ ������ (⇒ ������2 )
¬¬������ ⇒ ������
(⇒ ������3 )
������ ∨ ¬������ ⇒ ¬������ ⇒ ������
Goal forall (A:Prop), A∧~A -> ~~A -> A.
unfold not.
intros.
destruct H.
apply H.
apply False_ind. (* False_ind: forall P : Prop, False -> P *)
apply H0.
apply H.
Qed.
※この証明では背理法の妥当性そのものは証明できていない事に注意!
26. その他有用なコマンド
assert P 命題Pを新たな目標として追加する
cut P 命題Pを新たな目標として追加した上で現在の目標Gを P→Gに書き換える
discriminate 前提に間違った等式(「2=3」等)がある場合直ちに全体を真とする
replace a with b 目標のaをbに変えた上で「b=a」を新たな目標として追加する
simpl 目標の式をベータ簡約する
simpl in H 前提Hをベータ簡約する
auto 可能な限り自動で証明を行う
tauto
Require Import M モジュールMで定義されている物を使用可能にする
実はさっきの例程度の証明だったら tauto というコマンド
で自動的にやってくれる
28. 関数とデータ型の定義
Inductive name : Type :=
データ型の定義
cName:Type [ | cName:type ]*
Definition name : [param]* : Type := exp 非再帰関数や定理の定義
(停止性が明らかな)再帰関数の
Fixpoint name : [param]* : Type := exp
定義
(停止性が明らかでない)再帰関
Function name : [param]*
数の定義(Recdefのインポートが
{measure exp1} : Type := exp2
必要)
「measure exp1」の exp1 にはこの関数が停止する根拠を書かな
ければいけない. Functionで関数を定義しようとすると即座に停
止性についての証明が要求される
29. 定義の例 : リストとその演算
Inductive list (A : Type) : Type :=
| nil : list A
| cons : A -> list A -> list A.
Fixpoint app (A : Type)(l l' : list A) : list A :=
match l with
| nil => l'
| cons x xs => cons x (app A xs l')
end.
match v with … end
vについてのパターンマッチをするための構文
30. 定義の例 : 自然数とその演算
module Plus where
Inductive nat : Set :=
| O : nat
import qualified Prelude
| S : nat -> nat.
data Nat =
Fixpoint plus (n m : nat) : nat :=
O
match n with
| S Nat
| O => m
| S p => S (plus p m)
plus :: Nat -> Nat -> Nat
end.
plus n m =
Coqでの定義 case n of {
O -> m;
S p -> S (plus p m)}
(Haskell用に出力した場合)
31. 帰納法を用いた証明
証明に帰納法を用いる時は induction という操作を行う
「induction x」 とすると, 目標の中のxに対して適切
な帰納法の目標を設定してくれる
list の場合は 「P:list A→Prop」に対して「P(nil)」と
「∀x xs ,P(xs)→P(x:xs)」を目標にしてくれる
32. l++nil=l の証明
Require Import List.
(* listについての演算子等が定義されてる *)
Theorem app_zero : forall (A:Type)(l:list A), l++nil=l.
intros. (* 目標が l ++ nil = l になる *)
induction l.
simpl. (* 目標の式をベータ簡約する操作 *)
reflexivity. (* 目標が正しい等式の時に証明を完了する *)
simpl.
apply (f_equal (cons a)).
(*
f_equal : forall (A B : Type) (f : A -> B) (x y : A),
x = y -> f x = f y
*)
apply IHl.
Qed.
33. n+0 = n の証明
Goal forall ( n:nat ),n+0 = n.
intros.
induction n.
reflexivity. (* 目標が等式の時に証明を完了する *)
simpl. (* 目標の式をベータ簡約する操作 *)
f_equal.
apply IHn.
Qed.
34. negb : bool→bool
クイックソート leb : nat→nat→bool
filter : (A→bool)→list A→list A
Requie Import List.
Fixpoint qsort (l:list nat):list nat:=
match l with
| nil => nil
| x::xs => qsort (filter (leb x) xs) ++
(x::nil)++
qsort (filter (fun t=>negb(leb x t)) xs)
end.
クイックソートを定義しようとしてこのように定義しても受け
付けてくれない
この表記だと再帰部分のリストが引数より小さくなっている
事が直ちに保証できない
35. クイックソートの定義部分
Function qsort (l:list nat) {measure length l} : list nat :=
match l with
| nil => nil
| x :: xs => qsort (filter (leb x) xs)
++ (x ::nil)++
qsort (filter (fun t=>negb(leb x t)) xs)
end.
定義部分はFunctionを用いてこのようになる
この入力の直後に再帰部分に対して length l が確かに
小さくなる事について証明を求められるが…
36. 補助定理の証明
Lemma fil_len : forall (l:list nat) (p:nat->bool),
length (filter p l) <= length l.
intros.
induction l.
unfold length.
auto.
simpl.
case (p a). その前にこのような定理を証明して
simpl. おく
apply le_n_S.
apply IHl. フィルタ関数に通したリストは長さが
auto. 必ず元のリスト以下になっている事の
Qed. 証明
37. 停止性の証明
Function qsort (l : list nat) {measure length l} : list
nat :=
(中略)
end.
intros.
simpl.
apply le_n_S. 先の定理(fil_len)を定義した後に関
apply fil_len. 数の定義をする
intros.
intros からが停止性の証明の部分
simpl.
apply le_n_S. ここまでやって初めてクイックソートが
apply fil_len. 定義できる
Defined.
39. 参考資料
Yves Bertot. Coq in a hurry.
池淵未来. プログラミングCoq
(http://www.iij-ii.co.jp/lab/techdoc/coqt/)
Gerard Huet and Christine Paulim Mohring.
Interactive Theorem Proving and Program
Development