×
Copy
Open
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
国分 佑樹 Kuniwak
Slide 2
Slide 2 text
2
Slide 3
Slide 3 text
3 Go を書く際に 定理証明支援系 を使うと便利です Go に限らず、いつか必要になった時のために 定理証明支援系 を思い出してほしい (今はわからずとも必要なときにキーワードから調べられるように)
Slide 4
Slide 4 text
4
Slide 5
Slide 5 text
5 Go 言語の slice の capacity が十分かどうか判定する 判定には定理証明支援系の Isabelle1 を使う 1 https://isabelle.in.tum.de/
Slide 6
Slide 6 text
6 Go 言語の slice の capacity が十分かどうか判定する 判定には定理証明支援系の Isabelle1 を使う 1 https://isabelle.in.tum.de/ slice の capacity
Slide 7
Slide 7 text
7
Slide 8
Slide 8 text
8 要素を代入 要素を参照 slice を確保
Slide 9
Slide 9 text
9 要素を代入 要素を参照 slice を確保 3 length を指定
Slide 10
Slide 10 text
10 length を 超えたアクセス length は 3
Slide 11
Slide 11 text
11 length が 3 なので 0, 1, 2 へアクセスできる 範囲外へのアクセスは panic になる
Slide 12
Slide 12 text
12 panic にならない length を拡張 length は 0
Slide 13
Slide 13 text
13
Slide 14
Slide 14 text
14
Slide 15
Slide 15 text
15 slice は連続したメモリ領域を要求する slice のメモリ領域が足らなくなるとより 大きな容量の 新しい領域が確保 される そしてこの新しい領域へ今までの要素が すべて コピー される
Slide 16
Slide 16 text
16 そして length 指定が 0 なら、最初は 小さなメモリ領域が確保される ここで何度も append を実行すれば 何度も メモリの再確保 と コピー が走る これはそこそこ重い処理なので避けたい
Slide 17
Slide 17 text
17 length は 0 だが、はじめから 3 要素分の capacity を確保 容量は十分なので メモリの再確保は 走らない
Slide 18
Slide 18 text
18
Slide 19
Slide 19 text
19 2 無駄なメモリ確保なうえに、GC のコストが高くなりこれがプログラムに よってはボトルネックになるほど遅くなることがある(あった)
Slide 20
Slide 20 text
20 3 Go 言語にユーザー定義可能な総称型がくるまでは、都度 list.List の 要素の型を固定した wrapper を書かないと型の取り違えに気づけない
Slide 21
Slide 21 text
21 ● length を超えてアクセスすると panic ● 不十分な capacity で都度 append は 遅い ● 巨大 capacity の事前確保は 無駄 → 必要最小限を一度に確保 がベスト!
Slide 22
Slide 22 text
22 いつでもぴったりの capacity が直感的に わかるとは限らない その場合、次のような手段で capacity の 十分さの仮説を検証することになる
Slide 23
Slide 23 text
23
Slide 24
Slide 24 text
24 1. 少数のサンプルで試す 2. 機械生成された大量のサンプルで試す 3. 形式検証する
Slide 25
Slide 25 text
25 1. 少数のサンプルで試す 2. 機械生成された大量のサンプルで試す 3. 形式検証する
Slide 26
Slide 26 text
26 従来の自動テストを活用して、capacity を 減らしても panic しないことを確認する 従来の知識だけで実施可能 サンプルが少ないので見逃しがあるかも
Slide 27
Slide 27 text
27 1. 少数のサンプルで試す 2. 機械生成された大量のサンプルで試す 3. 形式検証する
Slide 28
Slide 28 text
28 testing/quick パッケージ4 などによる機械生成の 大量のサンプルで panic しないことを確認する 従来の知識の延長線上なので習得容易 網羅的ではないのでまだ見逃しがあるかも 4 https://golang.org/pkg/testing/quick/
Slide 29
Slide 29 text
29 1. 少数のサンプルで試す 2. 機械生成された大量のサンプルで試す 3. 形式検証する
Slide 30
Slide 30 text
30 数理論理学の武器を駆使して capacity の十分さを 証明 する 入力を網羅した検証が可能(後述) 形式検証の知識が必要(勉強すればできます)
Slide 31
Slide 31 text
31 数理論理学の武器を駆使して capacity の十分さを 証明 する 入力を網羅した検証が可能(後述) 形式検証の知識が必要(勉強すればできます)
Slide 32
Slide 32 text
32 この 証明 とは数学での証明のこと 証明 は今まで脳内でやっていた プログラムの検証を厳密にしたもの
Slide 33
Slide 33 text
33 これから形式検証における 証明 の流れを説明していく 一度に説明すると大変なので 順を追って説明していく
Slide 34
Slide 34 text
34 なお本来は capacity が十分かつ 最小と検証できたら素晴らしいが、 以降の説明が難しくなってしまう そのため以降では capacity が 十分であることだけに着目する
Slide 35
Slide 35 text
35 以降の例は capacity が足らないとpanic して 気付けるように length 指定とインデックス アクセスにしている そのため capacity 指定と append を使っていないが、 実際のコードではたやすく panic されては困る そのため、length 指定とインデックスアクセスを capacity 指定/append に読み替えてほしい
Slide 36
Slide 36 text
36 1. 簡単な例の証明 2. 少し難しい例の証明 3. 定理証明支援系 4. 現実的な例の証明
Slide 37
Slide 37 text
37 1. 簡単な例の証明 2. 少し難しい例の証明 3. 定理証明支援系 4. 現実的な例の証明
Slide 38
Slide 38 text
38 引数に与えられたslice と同じ 内容の新しいslice を作成して返す なお i, x := range xs の方が 読みやすいが、説明の しやすさのために崩している
Slide 39
Slide 39 text
39 この length は勘で決めたので、 これで十分かどうか検証したい ちなみに capacity を省略しているが、 この場合は length と同じ値になる 今回は十分な length であれば十分な capacity といえる
Slide 40
Slide 40 text
40 先ほどの length の指定にあたって、 panic しうる危ない部分がここ どんな場合でもここで i < ys の長さ にならないといけない これを検証してみよう
Slide 41
Slide 41 text
41 今回は勘で ys の長さを len(xs) にしているので、検証したいこと である i < ys の長さ は i < len(xs) と言い換えられる
Slide 42
Slide 42 text
42 現状をまとめると、i < len(xs) が なりたつと検証できれば ys[i] の 部分では panic しないといえる 検証したいことがわかったので 検証に使える材料を集めてこよう
Slide 43
Slide 43 text
43 ループの回っている間は この 条件式 i < len(xs) が なりたっていることに注目
Slide 44
Slide 44 text
44 そして ys[i] の代入の 直前までに i は変更 されていない つまり、ys[i] の直前でも i < len(xs) がなりたつ
Slide 45
Slide 45 text
45 これまでの事実をまとめると: ys[i] の直前で i < len(xs) が なりたっている 検証したいのは ys[i] の直前で i < len(xs) がなりたっていること この上から下を証明してみよう
Slide 46
Slide 46 text
46 これまでの事実をまとめると: ys[i] の直前で i < len(xs) が なりたっている 検証したいのは ys[i] の直前で i < len(xs) がなりたっていること この上から下を証明してみよう 前提より明らか i < len(xs) i < len(xs)
Slide 47
Slide 47 text
47 こうして ys の length が len(xs) で十分であると いえる(証明終わり)
Slide 48
Slide 48 text
48 capacity の検証から脱線するが 同様の考え方で xs[i] の方でも panic を起こさないと検証できる
Slide 49
Slide 49 text
49 これまでの証明の過程で具体的な i や xs の 値を与えなかったことを思い出してほしい この証明は具体的な i や xs の値に依存して いないので、どんな i や xs でも当てはまる これが入力のサンプルを必要とする他の 検証方法にはない最大の利点
Slide 50
Slide 50 text
50 簡単な例で検証の流れを確認できた 現実の例はもっと難しいことがある もう少し難しい例をみてみよう
Slide 51
Slide 51 text
51 1. 簡単な例の証明 2. 少し難しい例の証明 3. 定理証明支援系 4. 現実的な例の証明
Slide 52
Slide 52 text
52 前の簡単な例の関数と似ているが ys へアクセスするインデックスの 変化が複雑な関数
Slide 53
Slide 53 text
53 今回検証したい slice は ys length をまだ決めていないので 以降の処理の流れを見た上で 推測する
Slide 54
Slide 54 text
54 ys の length の設定次第で panic しうるのがこの部分 代入先インデックスの j は ループカウンタ i がループ ごとに加算されていく
Slide 55
Slide 55 text
55 ループ n 回目 j の値 0 0 1 1 2 3 3 6 破線時点のループ n 回目の j の値は次のようになる: ループ n 回目
Slide 56
Slide 56 text
56 ループ n 回目 j の値 0 0 1 1 2 3 3 6 ループ n 回目の j は 0 から n までの数の合計と 同じになっている ループ n 回目
Slide 57
Slide 57 text
57 このような 0 から n までの 数の合計 S(n) は次のように あらわせる: n 回目のループの j の値も この式で次のようにあらわせる S(n) = n (n + 1) 2 ループ n 回目
Slide 58
Slide 58 text
58 n 回目のループの j の値 v (n) は 先ほどの S(n) を利用すると: j v (n) = n (n + 1) 2 j この時点の j の値が v (n) j ループ n 回目
Slide 59
Slide 59 text
59 j を v (n) であらわせた ただし ys の length をまだ 未設定だったので話を戻す j
Slide 60
Slide 60 text
60 これまでの推測からこの length 指定を仮で決める
Slide 61
Slide 61 text
61 ループ回数は len(xs) より小さい ので雑に length の指定に S(n) を 当てはめてみた(これは後にもう 少し改善する) ただし雑に当てはめただけなので 問題がないかどうか検証していく
Slide 62
Slide 62 text
62 この ys[j] が panic しないためには、 どんな場合でも v (n) が ys の 長さより小さい必要がある つまり検証したいことは次のとおり: v (n) < len(xs)*(len(xs) + 1)/2 ※ Go の int の除算は 0 に近い方へ丸められるが x(x+1) の形は常に偶数なので丸めは発生しない この時点の j の値が v (n) j ループ n 回目 j j
Slide 63
Slide 63 text
63 検証したいことがわかったので コードから検証の材料を集めよう
Slide 64
Slide 64 text
64 ys[j] の代入の直前で i は更新されて いないので、ys[j] の代入の直前で i < len(xs) がなりたつ(材料1) この時点では i < len(xs) が成立 ループ
Slide 65
Slide 65 text
65 ループ条件評価直前でループ回数 n と ループカウンタ i は等しい(材料2) ループ n 回目 この時点で n = i が成立
Slide 66
Slide 66 text
66 前の方で v (n) は次のように あらわせることわかった: j v (n) = (材料3) n (n + 1) 2 j この時点の j の値が v (n) j ループ n 回目
Slide 67
Slide 67 text
67 これまでに3つの材料が集まった: 材料1:i < len(xs) がなりたつ 材料2:ループ回数 n とループ カウンタ i は等しい これらの材料から以下を証明しよう: v (n) < len(xs)*(len(xs) + 1)/2 材料3:v (n) = n (n + 1) 2 j j
Slide 68
Slide 68 text
68 材料1:i < len(xs) がなりたつ 材料2:ループ回数 n とループ カウンタ i は等しい v (n) < len(xs)*(len(xs) + 1)/2 材料3:v (n) = n (n + 1) 2 j これらを整理すると、 n < len(xs) がなりたつもとで 以下を証明すればよい: ここまでくればあとは 紙の上でも証明できる n (n + 1) 2 < len(xs) (len(xs) + 1) 2 j
Slide 69
Slide 69 text
69 簡単な例と同じ考え方で slice へのインデックス アクセスで panic しないことを証明できた ただしこれは紙の上の証明なので間違っている かもしれない 間違いを含んだ証明では正しい検証に ならないので 間違わない工夫 が必要になる
Slide 70
Slide 70 text
70 1. 簡単な例の証明 2. 少し難しい例の証明 3. 定理証明支援系 4. 現実的な例の証明
Slide 71
Slide 71 text
71 定理証明支援系はその名のとおり数学の 証明を以下の 2 つの方向から支援する: 1. 証明を検査してくれる ○ 検査された証明は正しいとわかる5 2. 証明を一部手伝ってくれる ○ 場合によっては全自動で証明できる 5 ここでは定理証明支援系の正しさを仮定している
Slide 72
Slide 72 text
72 6 https://isabelle.in.tum.de/
Slide 73
Slide 73 text
73 ここから Isabelle のコードが登場するが、 時間の都合上詳しくコードを解説できない ここでは Isabelle というツールで証明を できて、かつ便利だと伝えたいだけなので 丁寧に追わなくとも問題ない
Slide 74
Slide 74 text
74 v (n) の定義 j これから今回の例を Isabelle 上で証明していく まず準備として証明に必要となる v (n) を定義する: j
Slide 75
Slide 75 text
75 これから今回の例を Isabelle 上で証明していく まず準備として証明に必要となる v (n) を定義する: j v (n) の定義 j ループ回数
Slide 76
Slide 76 text
76 これから今回の例を Isabelle 上で証明していく まず準備として証明に必要となる v (n) を定義する: j ループ回数 v (n) の定義 j v (n) = n (n + 1) 2 j
Slide 77
Slide 77 text
77 ⟹ 証明 証明したいこと
Slide 78
Slide 78 text
78 ⟹ 証明 証明したいこと これまでの証明したいことをそのままここに記述する 記述部分は前提と帰結に分けて書いている ⟹
Slide 79
Slide 79 text
79 ⟹ 証明 前提 先ほど集めてきた材料を 前提側にいれる v_j は先ほど定義したので 暗黙的に前提になっている 材料1 材料2
Slide 80
Slide 80 text
80 証明 ⟹ 帰結 前提のもとで証明したかったことを帰結にいれる ⟹
Slide 81
Slide 81 text
81 ⟹ 証明したいこと 証明 証明したいことを定義できたので 証明する手順を指示していく
Slide 82
Slide 82 text
82 証明したいこと ⟹ 証明 Isabelle の機能に自動証明があるので利用する
Slide 83
Slide 83 text
83 証明したいこと ⟹ 証明 ここの部分には別の Isabelle の機能が自動で 見つけてきたヒントを指定している
Slide 84
Slide 84 text
84 証明したいこと ⟹ 証明 自動証明だけで証明できたので証明を完了する なお証明が完了していないと done でエラーに なるのでわかる
Slide 85
Slide 85 text
85 ● 複雑なコードになるにつれ証明を 間違う可能性が高くなってきた ● Isabelle で証明を検査することで 証明の間違いを防いだ ● かつ Isabelle の自動証明によって 手軽に証明を完了できた
Slide 86
Slide 86 text
86 先ほどは雑な推測により length を決めたが 別のより小さい length も試してみたい より小さい
Slide 87
Slide 87 text
87 ⟹ 証明 証明したいこと
Slide 88
Slide 88 text
88 ⟹ 証明 証明したいこと ⟹ より小さい length に書き換えたものを追加して証明を再開
Slide 89
Slide 89 text
89 ⟹ 証明したいこと 証明 たくさん書いてあるので大変そうに見えるが、実際は全部 Isabelle が自動で見つけてきて証明してくれた
Slide 90
Slide 90 text
90 1. 簡単な例の証明 2. 少し難しい例の証明 3. 定理証明支援系 4. 現実的な例の証明
Slide 91
Slide 91 text
91 これまでの検証では slice 内の要素に capacity が依存しない例を扱ってきた 実際のプログラムでは slice 内の要素に 応じて必要な capacity が変わりうる このようなプログラムも検証してみよう
Slide 92
Slide 92 text
92 先頭は内部の slice の長さ slice が平たく埋め込まれている 先頭の数値をみながら 分割した slice を返す 入力の長さが一定なら 埋め込まれた slice が 長いほど荒く分割されて 外側の slice は短くなる
Slide 93
Slide 93 text
93
Slide 94
Slide 94 text
94 今回も結果の slice の length を検証する
Slide 95
Slide 95 text
95 今回の length 設定で危ない部分はここ
Slide 96
Slide 96 text
96 このインデックスはループカウンタの i になっている つまり xss の length はループ回数より大きい必要がある ループ
Slide 97
Slide 97 text
97 xss の length がループ回数より 大きいことを検証する
Slide 98
Slide 98 text
98 注意が必要なのは、ループ条件にループカウンタ i が 使われていないのでループ回数がわかりづらいということ ここで読んだ値が大きいほど j が大きく増加するので 少ないループ回数でループ条件を満たさなくなる
Slide 99
Slide 99 text
99 まずループ条件評価直前の j の値をループ回数 n と xs であらわそう ループ n 回目 ループ条件評価直前の j の値を v (n, xs) とする j するとループ条件 v (n, xs) < len(xs) をはじめて 偽にする最小の n がループ終了までの回数になる j
Slide 100
Slide 100 text
100 ループ条件評価直前の j の値を v (n, xs) とする j ループ n 回目 するとループ条件 v (n, xs) < len(xs) をはじめて 偽にする最小の n がループ終了までの回数になる j ループ条件 v (n, xs) < len(xs) をはじめて 偽にする最小の n j
Slide 101
Slide 101 text
101 7 実はこのループ条件の考え方は P.64 でも使っていた ループ条件 v (n, xs) < len(xs) をはじめて偽にする 「最小の n」がループ終了までの回数になる この「最小の n」を N とおくと、調べるべき ループ回数の範囲は 0 以上 N 未満となる v (n, xs) が複雑なので N の値を具体的には求められないが、 条件 v (n, xs) < len(xs) がなりたつとすれば 7、 n の範囲を 0 以上 N 未満と制限したときと同じことになる j j j
Slide 102
Slide 102 text
102 実際に v (n, xs) の表現を考えてみる k の宣言時点では前回のループの j の値を参照している j 前回のループの j の値 ループ条件評価直前の j の値を v (n, xs) とする j ループ n 回目
Slide 103
Slide 103 text
103 j ループ n 回目 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j 前のループの j の値 前のループの j の値 ループ条件評価直前の j の値を v (n, xs) とする v (n, xs) は前のループのときの値を含んでいるので漸化式になる j
Slide 104
Slide 104 text
104 v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j 前のループの j の値 前のループの j の値 ループ n 回目 0 (n = 0) v (n, xs) = j ループ条件評価直前の j の値を v (n, xs) とする v (n, xs) は前のループのときの値を含んでいるので漸化式になる j v (0, xs) はループ実行前なので 0 のまま j j
Slide 105
Slide 105 text
105 0 (n = 0) ループ条件評価直前の j の値を v (n, xs) とする j ループ n 回目 v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j 前のループの j の値 前のループの j の値 v (n, xs) は前のループのときの値を含んでいるので漸化式になる j v (n, xs) は1つ前の ループ時点の値を参照 j
Slide 106
Slide 106 text
106 整理すると、上のような漸化式で v (n, xs) を表現でき、 これを使って表現した v (n, xs) < len(xs) が なりたつ範囲を検証すればよい 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j 前のループの j の値 前のループの j の値 j j
Slide 107
Slide 107 text
107 v (n, xs) についてはわかってきたので 検証したいことを再確認しよう j
Slide 108
Slide 108 text
108 xss の length がループ回数より 大きいことを検証したい ただし検証するのは v (n, xs) < len(xs) がなりたつ 範囲でよい j
Slide 109
Slide 109 text
109 Isabelle の記述 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j v (n, xs) の定義 j
Slide 110
Slide 110 text
110 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j j Isabelle の記述 v (n, xs) の定義 (n = 0) v (n, xs) = j (n > 0) Isabelle も場合ごとにわけて記述する
Slide 111
Slide 111 text
111 v (n, xs) の定義 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j j Isabelle の記述 ループ回数 入力の slice に相当するもの ループ回数(Suc n は引数に n+1 が与えられたときと考えればよい)
Slide 112
Slide 112 text
112 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j v (n, xs) の定義 j 0 (n = 0) v (n, xs) = j Isabelle の記述 引数が 0 の場合
Slide 113
Slide 113 text
113 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j v (n, xs) の定義 j v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j Isabelle の記述 引数が n +1 の場合
Slide 114
Slide 114 text
114 Isabelle の記述 0 (n = 0) v (n, xs) = j v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j v (n, xs) の定義 j v (n, xs) を定義できたので 証明したいことの記述に移ろう j
Slide 115
Slide 115 text
115 ⟹ 証明したいこと 証明 証明したいこと xss の length がループ回数より 大きいことを検証したい ただし検証するのは v (n, xs) < len(xs) がなりたつ 範囲でよい j
Slide 116
Slide 116 text
116 証明したいこと xss の length がループ回数より 大きいことを検証したい ただし検証するのは v (n, xs) < len(xs) がなりたつ 範囲でよい j 証明 ⟹ 証明したいこと ⟹ xss の length がループ回数より 大きいことを検証したい
Slide 117
Slide 117 text
117 ⟹ 証明したいこと xss の length がループ回数より 大きいことを検証したい ただし検証するのは v (n, xs) < len(xs) がなりたつ 範囲でよい j 証明 証明したいこと ただし検証するのは v (n, xs) < len(xs) がなりたつ 範囲でよい j
Slide 118
Slide 118 text
118 証明したいこと 証明 ⟹ 証明したいこと ⟹ この形をよくみると、 n ≦ v_j n xs がなりたてば、前提の ② v_j n xs < length xs から ③ がいえる: n < length xs ② ③ ①
Slide 119
Slide 119 text
119 証明したいこと ① 証明したいこと ⟹ ② 証明の形をよくみると、 n ≦ v_j n xs 証明 n ≦ v_j n xs この意味を考えると、 v (n, xs) の値は常に n 以上であるということ 漸化式をみるとループごと に少なくとも +1 されて いるのでなりたちそう v (n - 1, xs) + 1 + xs[v (n - 1, xs)] (n > 0) j j ループごとに j の値は少なくとも元の値より +1 される ① j
Slide 120
Slide 120 text
120 証明したいこと ① 証明したいこと ② 証明の形をよくみると、 n ≦ v_j n xs 証明 ⟹ n ≦ v_j n xs 先にこれを証明して後の 証明を楽にしておこう このように本体の証明から 切り出した部分を 補題とよぶ
Slide 121
Slide 121 text
121 ⟶ 補題 証明
Slide 122
Slide 122 text
122 ⟶ 証明 補題 補題で解いておきたかったもの
Slide 123
Slide 123 text
123 ⟶ 証明 補題 ⟶ ループ条件の前提はここでもなりたつのでもってきた
Slide 124
Slide 124 text
124 ⟶ 証明 補題 この補題を証明するには工夫が必要になる ⟶ v_j を漸化式であらわしていたことを思いだそう
Slide 125
Slide 125 text
125 補題 ⟶ 証明 v_j は漸化式なので具体的な値がわかりづらい 無理やり n=k のときの具体的な v_j の値を求めても、 n=k - 1 のときの v_j の値が必要でこれを求めるには n=k - 2 のときの v_j の値が必要で、… のようにどこまでも展開できてしまう そこで 数学的帰納法 を使ってこの補題を解いてみよう
Slide 126
Slide 126 text
126 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える
Slide 127
Slide 127 text
127 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える 証明したい自然数に関する命題 P(n) ここに先ほどの補題をあてはめることを考えよう: 補題 ⟶
Slide 128
Slide 128 text
128 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ n = 0 のとき P(0) がなりたつ
Slide 129
Slide 129 text
129 まず補題の n を 0 へ書き換えた: ⟶
Slide 130
Slide 130 text
130 まず補題の n を 0 へ書き換えた: そして n=0 のときの v_j の値は 0 とわかっている: ⟶ ⟶
Slide 131
Slide 131 text
131 まず補題の n を 0 へ書き換えた: そして n=0 のときの v_j の値は 0 とわかっている: ⟶ ⟶ どんな xs が与えられても 0 ≦ 0 はなりたつので P(0) はなりたつ
Slide 132
Slide 132 text
132 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ n = 0 のとき P(0) がなりたつ なりたつと証明できた
Slide 133
Slide 133 text
133 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ
Slide 134
Slide 134 text
134 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える ⟶ ⟹ ⟶ P(k) 補題 ⟶ P(k) を前提に P(k+1) をいいたい P(k+1)
Slide 135
Slide 135 text
135 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える ⟶ ⟹ ⟶ P(k) P(k+1) k と k+1 の両方のときの v_j があらわれるので一見複雑そうに 見えるが、これを整理すると簡単な不等式にできる なぜ簡単にできるかというと、v_j は漸化式だったので k+1 のときの v_j の値を k のときの値で表現できるため
Slide 136
Slide 136 text
136 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える ⟶ ⟹ ⟶ P(k) P(k+1) そこで k のときの v_j の値だけがあらわれるように 不等式を変形すればすぐ証明できる なお、時間が足らないのでここでの証明は割愛する
Slide 137
Slide 137 text
137 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える なりたつと証明できた どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ
Slide 138
Slide 138 text
138 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える このとき、どんな n についても P(n) がなりたつ 先ほど当てはめた P(n) を思い出すと: 補題 ⟶
Slide 139
Slide 139 text
139 証明したい自然数に関する命題 P(n) について: n = 0 のとき P(0) がなりたつ8 どんな k についても、n = k のとき P(k) を 仮定すると P(k+1) もなりたつ このとき、どんな n についても P(n) がなりたつ 8 自然数が 0 を含まない定義なら P(0) を P(1) と読み替える 数学的帰納法により補題を証明できた!
Slide 140
Slide 140 text
140 ⟶ 補題 証明
Slide 141
Slide 141 text
141 補題 証明 自動証明の前に n について数学的帰納法を使うよう指示 するとあっさりと解かれる 補題を証明できたのでこれを使ってもとの証明を解いてみよう
Slide 142
Slide 142 text
142 ⟹ 証明したいこと 証明 証明したいこと
Slide 143
Slide 143 text
143 ⟹ 証明したいこと 証明したいこと 証明 こちらも Isabelle に任せてみると補題を使って あっさりと証明されてしまった つまり、検証した部分では panic が起こらないことを検証できた
Slide 144
Slide 144 text
144 1. 簡単な例の証明 2. 少し難しい例の証明 3. 定理証明支援系 4. 現実的な例の証明
Slide 145
Slide 145 text
145 ● 形式検証は網羅的に検証できる方法 ● Isabelle は形式検証で使える強力なツール ● 問題によっては数学的帰納法を駆使した ● ほとんどは自動で capacity を検証できた
Slide 146
Slide 146 text
146 もう少しだけ続きます
Slide 147
Slide 147 text
147 これまでは capacity だけを扱ってきた 実際には形式検証の適用範囲はさらに広く プログラムの性質 を証明できる
Slide 148
Slide 148 text
148 プログラムの性質 の典型的な例: ● どんな入力も考慮されている ● デッドロックしない ● いつか結果が帰ってくる ● 結果がソートされている ● …
Slide 149
Slide 149 text
149 実は従来の自動テストの本質も 性質 の検証 ただし自動テストは簡単だが 網羅性はない
Slide 150
Slide 150 text
150 もし従来のテストで非現実的と諦めた検証が あるなら 形式検証 を試してみてほしい(例) ● どんな入力も考慮されている ● デッドロックしない ● いつか結果が帰ってくる ● 結果がソートされている ● …
Slide 151
Slide 151 text
151 あるバイナリフォーマットの 3-way merge ツールの 実装で「処理が不定となる入力が存在しない」の 性質を 形式検証 で確かめている
Slide 152
Slide 152 text
152
Slide 153
Slide 153 text
153 これまでの検証で「ループ直前では〜が なりたつ」のような Go 言語の振る舞いに 関する仮定 をたくさん使ってきた この仮定は本当に正しかったのだろうか? もし仮定に誤りがあればこれまでの検証を 正しいとはいえなくなる
Slide 154
Slide 154 text
154
Slide 155
Slide 155 text
155 Go のプログラム意味論を使えばこれまでの 証明で使った仮定の正しさを証明できる9 興味があれば「プログラム意味論の基礎10」や 「Featherweight Go11」を読もう 9 後述するが現場では厳密な方法ではない別の方法をとっている 10 https://www.saiensu.co.jp/search/?isbn=978-4-7819-1483-1&y=2020 11 https://arxiv.org/abs/2005.11710
Slide 156
Slide 156 text
156 私の現場では、少し不安が残るがプログラム 意味論を使わない別の方法をとっている この方法を Isabelle ファースト と呼んでいる Isabelle ファースト とこれの対になる Go ファースト を比較してみよう
Slide 157
Slide 157 text
157 先に Go のプログラムを書き、ここから Isabelle のプログラムと定理に書き換える 先に Isabelle のプログラムと定理を書き、 ここから Go のコードへ書き換える
Slide 158
Slide 158 text
158 12 defer とか panic とかでどういう定理がなりたつんですか!?? のように なぜか Go の機能や標準ライブラリの証明までする羽目になる 証明が難しい!Go のいろいろな事情12 に 振り回されるので大変 すでに進んでしまったプロジェクトに 後からいれやすい
Slide 159
Slide 159 text
159 証明しやすい。証明に不要な Go の事情を全部 省いて証明しやすい状態からスタートできる。 Go のコードに落とすのは難しくない defer や panic、関数ポインタなどは証明が 難しいので進んでは使えない
Slide 160
Slide 160 text
160
Slide 161
Slide 161 text
161
Slide 162
Slide 162 text
162
Slide 163
Slide 163 text
163 まとめると、テストケースが多くないと 安心できない状況が予期されるケースは 先に Isabelle で書き始めてしまう方 が 安心して開発できる
Slide 164
Slide 164 text
164 まとめると、テストケースが多くないと 安心できない状況が予期されるケースは 先に Isabelle で書き始めてしまう方 が 安心して開発できる テストケースが多くないと 安心できない状況
Slide 165
Slide 165 text
165 うちは医療や航空宇宙、公共 インフラでないので…と思った方へ 諦めている/気づいていないだけで 身近な例もたくさんあります
Slide 166
Slide 166 text
166 実は身近にいろいろ 活躍できる場面 がある: ● 私の現場の 3-way merge ツールのような 失敗が一見わからない上に放置すると 復旧が高コストになるパターン ● 数学的にきれいに仕様を書けるが 入力パターン数が多いもの(ソートなど)
Slide 167
Slide 167 text
167
Slide 168
Slide 168 text
168 プログラムの性質を手軽に検証する方法を紹介 ● 例として Go の capacity の十分性を検証 ● 来るべき証明が必要な開発に備えて 定理証明支援系 を学んでいきましょう
Slide 169
Slide 169 text
169