このエントリは Theorem Prover Advent Calendar 2015 - Qiita の 23 日目です。
とは言ってみたものの、内容はいわゆる定理証明器の話ではありません。モデル検査器 Alloy を利用して古典命題論理のシークエント計算をモデル化し、証明図を自動生成させることを目標にします。
なお、以下で部分的に引用しているモデル定義の完全版は alloy_sequent.als · GitHub にアップロードしてあります。
モデル化してみる
命題論理式の実装
まず対象となる論理式を Alloy で記述します。
abstract sig Formula {} sig Atom extends Formula {} sig Not extends Formula { of : one Formula, } abstract sig BinaryLogicOperator extends Formula { left : one Formula, right : one Formula, } sig And, Or, Imply extends BinaryLogicOperator {}
二項演算子たちを定義する際、独立に定義せず間に抽象シグネチャ BinaryLogicOperator
を挟んで extends
で継承しています。これは Alloy 上の関係には多相性がなく、同じ名前の関係は同じシグネチャ間にしか定義することができないためです。つまり演算子をそれぞれ個別に定義した場合、引数を指す関係名を、例えば andRight : And -> Formula
のように演算子ごとにつける必要が生じてしまいます。
そこで、引数を left
, right
とした BinaryLogicOperator
を経由することで、すべての演算子に共通して課したい制約を
fact formulaeAreAcyclic { all f : Formula | f not in f.^(of + right + left) }
のように統一的に記述できるというわけです。
シークエントの実装
さて、次にシークエントの定義を記述します。
sig Sequent { antecedents : set Formula, succedents : set Formula, }
通常の記法で言えばシークエント の が antecedents
, が succedents
です。
本来、これらは「論理式の列」として個数と順番を考えて保持されるべきですが、今回は古典命題論理しか考えないので個数も順番も無視して単なる集合 set
として扱います。部分構造論理が好きな人は潔く諦めましょう。
推論規則の実装
シークエントができたら、推論規則も Alloy に載せましょう。
論理式の場合と同じく、もとになる抽象シグネチャを定義します。
abstract sig InferenceRule { premises : some Sequent, conclusion : one Sequent, }
前提 premises
は最大 2 個になり得るので some
で、結論 conclusion
は必ず 1 個なので one
で定義しておきました。
なお、公理は前提が空なる推論規則であると考えて premises
を set
で定義することも考えられますが、この場合、検証の際に証明図を構成する InferenceRule
の数が増えてしまいます。ここでは探索空間の最適化を優先することにして、公理は始シークエント InitialSequent
として別途定義することにします。
そして、InferenceRule
を継承して個々の推論規則を実装します。今回のように継承先によって異なる性質が要求される場合、Alloy ではシグネチャファクトで制約を記述するのが便利です。例えば以下の規則
sig NotRight extends InferenceRule { } { one premises some f : Formula, n : Not { f in premises.antecedents f in n.of n in conclusion.succedents premises.antecedents - f = conclusion.antecedents premises.succedents = conclusion.succedents - n } }
は否定の導入規則
に対応しています。
同様にして各論理演算子に対応する規則 NotLeft
, AndRight
, AndLeft
, OrRight
, OrLeft
, ImplyRight
, ImplyLeft
を定義しておきます。
なお、すでに述べた通りシークエントに登場するコンテクストを集合として実装しているため、構造規則が必要ない(逆に言えば表現できない)ことに留意しましょう。また cut 規則も本質的には必要ないはずなのであらかじめ外しておきます。
証明図の実装
最後に、ここまで登場した要素を積み上げて全体で一つの証明図になるよう制約を加えます。
fact noIsolatedFormulae { Formula in Sequent.(antecedents + succedents) } sig RootSequent in Sequent { } { this not in InferenceRule.premises } sig InitialSequent in Sequent { } { some antecedents & succedents } fact rulesFormAProofTree { one RootSequent Sequent - InferenceRule.conclusion = InitialSequent no r : InferenceRule | r in r.^(conclusion.~premises) all s : Sequent - InitialSequent | one conclusion.s }
ここでは、シークエントの中で特殊なポジションを持つものが in
記法により定義されています。
証明されるべき最終的な主張が RootSequent
, また公理として扱われる始シーケント
が InitialSequent
です (2015/12/23 誤植を修正)。
証明図全体は制約 rulesFormAProofTree
により、RouteSequent
を根とし、InitialSequent
を葉とする二分木になります。
検証してみる
無矛盾性の検証
まず、ここまでに定義した Alloy モデルを使って、矛盾を表す前件、後件ともに空なるシークエントが証明されない(であろう)ことを確認してみましょう。
assert consistency { no s : Sequent | s.antecedents = none && s.succedents = none } check consistency for 3
assert
に対して check
を実行すると、Alloy はアサーションに対する反例を探索します。シークエント計算が正しくモデル化されていれば、ここでは反例が発見されないはずです。
排中律の検証
もう一つ、今度は簡単な定理の例として排中律の証明を Alloy に探索させてみましょう。求めるべき主張は なので以下の pred
を満たす証明図を run
で探索させれば OK です。
pred excludedMiddle { some x : Atom, n : Not, o : Or { x in o.left n in o.right x in n.of RootSequent.antecedents = none RootSequent.succedents = o } } run excludedMiddle for 3
以下のような結果が出力されます。
まとめ
とりあえず、今回は試みとして最も単純な古典命題論理のシークエント計算を Alloy でモデル化し、証明を自動探索させてみました。
この他にも例えば、真偽値をシグネチャとして用意し、論理式の評価結果を制約として加えれば、シークエント計算の健全性を示すことができるかもしれません。また、他の体系、例えば直観主義に変更すれば、上で示した排中律の証明は発見できなくなったりするはずです。次なる拡張の方向性はいくつかありそうですが、それはまた別の話。