gearsAgda とその証明 TerminateingLoopS BTreeのinsertの証明 RBTreeのinsertの証明 証明主導のプログラミング LLMとプログラムの証明 今後のインフラなライブラリはすべて証明付きになる
agda による CbC の形式化
code : {t : Set } → Input → (Output → t )
という形式を使う ({} は省略可能な引数) これに対応したλ計算を作れるかもしれないし、作れないかもしれない これはプログラムの単位、コンパイラのBasic Blockに相当する
→ tの形式の関数が codeGear
dataGear は、その入力になる構造体
これらはメタレベルでは、一つにまとめられる codeGearの接続はメタレベルで行われる codeGear の中では再帰呼び出しはしない(normal levelでは) Loop と stack で記述する
code : {t : Set } → Input → Pre → (Output → Post → t )
Hoare logic になるように、Pre condition と Post condition をつける これは普通の codeGear であり、メタなデータが引数として付加されている つまり、PreもPostも Agdaのデータ構造/型(証明と命題)になっている
codeGearには、loop は継続を呼び出すことで実現される これを loop にするには、CbCでは問題ない Agda の場合は終了条件が必要になる 条件/Invariantは、型のレベルで記述され、値では、その証明が渡される これで、証明付きの実行可能なプログラミング言語が手に入った これは、アセンブラレベルの記述で、再帰呼び出しは「しない」
TerminatingLoopS : {t : Set } (Index : Set ) → {Invraiant : Index → Set } → ( reduce : Index → ℕ) → (r : Index) → (p : Invraiant r) → (loop : (r : Index) → Invraiant r → (next : (r1 : Index) → Invraiant r1 → reduce r1 < reduce r → t ) → t) → t これはinductionの命題であり、実際にAgdaで証明される。 loop に codeGear を入れると、loop の変数で loop を構成できる Indexが必ず減少する必要がある。そうでないと停止エラーになる gearsAgda の github
RBTreeのinsertは、Find と Replace の二つある その他はやってないので良くわからない
Hoare logic のinvariantを用意すると、プログラムの証明が可能になる。
BTree
Binary Tree の証明 treeInvariant 2分木の性質 stackInvariant 木をたどるstackの性質 replacedTree 木の置き換え
RBTree
赤黒木の証明 RBtreeInvariant 赤黒木の深さと色の性質 replacedRBTree 木の置き換え
insertRBTreeP : {n m : Level} {A : Set n} {t : Set m} → (tree : bt (Color ∧ A)) → (key : ℕ) → (value : A) → treeInvariant tree → RBtreeInvariant tree → (exit : (stack : List (bt (Color ∧ A))) → stack ≡ (tree ∷ []) → RBI key value tree stack → t ) → t
insertRBTreeP {n} {m} {A} {t} orig key value ti rbi exit = TerminatingLoopS (bt (Color ∧ A) ∧ List (bt (Color ∧ A))) {λ p → RBtreeInvariant (proj1 p) ∧ stackInvariant key (proj1 p) orig (proj2 p) } (λ p → bt-depth (proj1 p)) ⟪ orig , orig ∷ [] ⟫ ⟪ rbi , s-nil ⟫ $ λ p RBP findLoop → findRBT key (proj1 p) orig (proj2 p) RBP (λ t1 s1 P2 lt1 → findLoop ⟪ t1 , s1 ⟫ P2 lt1 ) $ λ tr1 st P2 O → createRBT key value orig rbi ti tr1 st (proj2 P2) O $ λ st rbi → TerminatingLoopS (List (bt (Color ∧ A))) {λ stack → RBI key value orig stack } (λ stack → length stack ) st rbi $ λ stack rbi replaceLoop → replaceRBP key value orig stack rbi (λ stack1 rbi1 lt → replaceLoop stack1 rbi1 lt ) $ λ stack eq r → exit stack eq r
insertRBTestP1 = insertRBTreeP leaf 1 1 t-leaf rb-leaf $ λ _ x0 P0 → insertRBTreeP (RBI.repl P0) 2 1 (RBI.replti P0) (RBI.replrb P0) $ λ _ x0 P0 → insertRBTreeP (RBI.repl P0) 3 2 (RBI.replti P0) (RBI.replrb P0) $ λ _ x0 P0 → insertRBTreeP (RBI.repl P0) 2 2 (RBI.replti P0) (RBI.replrb P0) $ λ _ x P → RBI.repl P0
普通に動く。これから証明を取り除くのは、理論的には可能 JavaScript / Haskell での出力も可能(agdaの機能、試してない) アルゴリズムは証明部分から切り離されて動く必要がある 切り離して、メモリ管理などを追加すれば、CbC (Continuation based C)で動作する(はず)
stackInvariant treeInvariant の修正
RBtreeInvariant replaceRBTree RBI RBI-state PG / PareentGrand
バランスを修正しながら木を再構成する 1) 入れ替える親が黒なら、そのまま赤で挿入する 2) 親が赤なら top なら黒に変更して挿入 3) uncle が赤なら filp して二つ上に 4) uncle が黒なら rotate して終わり 5) 挿入が終わったら、そのまま木を新しく置換する
rebuild rorate top-black モードに対応する invariant が必要
stack が空であること、treeInvariant と RBtreeInvariantと、replaceRBT insertRBTreeP : {n m : Level} {A : Set n} {t : Set m} → (tree : bt (Color ∧ A)) → (key : ℕ) → (value : A) → treeInvariant tree → RBtreeInvariant tree → (exit : (stack : List (bt (Color ∧ A))) → stack ≡ (tree ∷ []) → RBI key value tree stack → t ) → t
すべての可能なRBTreeの入力を受け付ける 出力がRBTreeになっている 書くのに時間がかかる
left node のkeyは、すべて parent の keyよりも小さい BinaryTreeでは不要 だが、ありえない入力がありえる これは見落とし
tr< : {n : Level} {A : Set n} → (key : ℕ) → bt A → Set tr< {_} {A} key leaf = ⊤ tr< {_} {A} key (node key₁ value tr tr₁) = (key₁ < key ) ∧ tr< key tr ∧ tr< key tr₁ tr> : {n : Level} {A : Set n} → (key : ℕ) → bt A → Set tr> {_} {A} key leaf = ⊤ tr> {_} {A} key (node key₁ value tr tr₁) = (key < key₁ ) ∧ tr> key tr ∧ tr> key tr₁
data treeInvariant {n : Level} {A : Set n} : (tree : bt A) → Set n where t-leaf : treeInvariant leaf t-single : (key : ℕ) → (value : A) → treeInvariant (node key value leaf leaf) t-right : (key key₁ : ℕ) → {value value₁ : A} → {t₁ t₂ : bt A} → key < key₁ → tr> key t₁ → tr> key t₂ → treeInvariant (node key₁ value₁ t₁ t₂) → treeInvariant (node key value leaf (node key₁ value₁ t₁ t₂)) ...
-safe ---cubical-compatible これらの修正にまた時間がかかる
rt-property-leaf' : {n : Level} {A : Set n} {key : ℕ} {value : A} {repl : bt A} → replacedTree key value leaf repl → repl ≡ node key value leaf leaf rt-property-leaf' {n} {A} {key} {value} {.(node key value leaf leaf)} r-leaf = refl
これにたいして
Warnings /Users/kono/src/GearsAgda/BTree.agda:379,1-84 warning: -W[no]UnsupportedIndexedMatch This clause uses pattern-matching features that are not yet supported by Cubical Agda, the function to which it belongs will not compute when applied to transports.
これを (r-right x rt)などに変えると
/Users/kono/src/GearsAgda/BTree.agda:380,72-84 The case for the constructor r-right is impossible because unification ended with a conflicting equation
これを解決するには replaceTree の変数を全部、束縛抜きにすればよい
rt-property-leaf : {n : Level} {A : Set n} {key : ℕ} {value : A} {repl : bt A} → replacedTree key value leaf repl → repl ≡ node key value leaf leaf rt-property-leaf {n} {A} {key} {value} {repl} rt = lem00 leaf refl rt where lem00 : (tree : bt A) → tree ≡ leaf → replacedTree key value tree repl → repl ≡ node key value leaf leaf lem00 leaf eq r-leaf = refl lem00 .(node key _ _ _) () r-node lem00 .(node _ _ _ _) () (r-right x rt) lem00 .(node _ _ _ _) () (r-left x rt)
まず、Invariantを書く Invariantの操作の証明がめんどう RBTReplace→treeInvariantの保存
個々の操作は難しくはないが場合わけが多い
テストは不要 (だが書く)
証明抜きに信用するのか? Invariant はどうする
証明の接続はどうする 証明付きライブラリの構成は? スクリプトはどうする?
Coq Lean Isabel HOL
GearsAgda →CbC でいいわけ? Functional Assembler
RBTree/Btreeの削除がない (やる気がね) Model 検査 with Proof Gears OS自体の検証 Model checkerの検証 プログラム生成 メモリやトランザクションの検証