在上述兩組符號中,我們將
每個
手寫示範
T | T | T |
T | F | F |
F | T | F |
F | F | F |
T | T | T |
T | F | T |
F | T | T |
F | F | F |
T | T | T |
T | F | F |
F | T | T |
F | F | T |
T | T | T | T | T | T | |||
T | T | F | T | T | F | |||
T | F | T | T | F | T | |||
T | F | F | F | F | F | |||
F | T | T | T | F | F | |||
F | T | F | T | F | F | |||
F | F | T | T | F | F | |||
F | F | F | F | F | F |
所以
在一個公理化系統中,如果一個
Completeness 則是反過來,在一個公理化系統下,如果有
對
在這兩個性質的形式化公理系統中,驗證一個
Isabelle/HOL 提供以下:
Week 1, Mon: intro 第 20 頁
If I prove it on the computer, it is correct, right?
No, because:
需要 Type 是因為第 6 個理由:
logic could be inconsistent
Type 可減少 logic 產生自相矛盾的情況
能檢查證明,自然就可定義公理化系統
以下案例說明
term "(λy. x y) t"
type:
lemma "1+2=3"
apply arith
theory Example imports Main
begin
lemma "A∧(B∨C)⟹(A∧B)∨(A∧C)"
apply(erule conjE)
apply (erule disjE)
apply (rule disjI1)
apply (rule conjI)
apply assumption
apply assumption
apply (rule disjI2)
apply (rule conjI)
apply assumption
apply assumption
done
end
像以上的
=
是等號的意思一個集合,給定一個等價關係,可把這集合分割成數個子集合,而子集合內的任兩個元素都滿足給定的等價關係,這樣的子集合稱為等價類
舉個例子:
集合
假如
那可以分割成兩個等價類:
可看到
可滿足的意思:在某個前提,logic formula 是成立的 (
可滿足性就是討論
這邊所講的演算法,會利用上面的等價類
舉例說明:
手寫示範
在 z3 的 build 目錄下,輸入以下命令:
./z3 -in
隨後可進入命令交談模式。
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(assert(and (and (= a b) (= b c)) (= a c)))
(check-sat)
輸出
(declare-const a Int)
(assert(and (> a 10) (< a 5)))
(check-sat)
輸出
int show(int x) {
if (x < 10)
x = x - 1;
assert(x != 9);
return 0;
}
驗證 show
函式時,帶入任意的 int 數值,執行不會出現 assert 錯誤訊息
SSA 是 Static Single Assignment
SSA 最主要的用途,是藉由簡化變數的特性,提升編譯器最佳化的能力。舉例來說:
y := 1
y := 2
x := y
不難見到,第一行變數的數值指派並非必要,因為 y 在第二行再次指派,y 的數值在第三行被使用,一個程式通常會進行定義可達性分析 (reaching definition analysis) 來測定。在 SSA 下,將會變成以下形式:
y1 := 1
y2 := 2
x1 := y2
其中 y1 表示第 1 次變數 y 的數值指派,y2 表示第 2 次,後者又用於變數 x 的第 1 次指派。顯然 y1 沒被用到,所以編譯器可輕易消除。
再者考慮以下形式:
由於涉及分支指令,w2 的數值指派究竟是圖片左邊的 y1,還是右邊的 y2 呢?編譯器不能貿然確認,這時就引入
首先把 C 程式轉成 SSA 形式:
int show(int x0) {
if (x0 < 10)
x1 = x0 - 1;
x2 = phi(x0, x1)
assert(x2 != 9);
return 0;
}
phi 為
assert 內是
這不是
所以轉為算
然後是兩個 CNF 用
由 =
先算
再算
只要其中一個是滿足,整個
整個
來看寫成的z3 code是如何
(declare-const x0 Int)
(declare-const x1 Int)
(declare-const x2 Int)
(assert(and (and (< x0 10) (= x1 (- x0 1))) (and (= x2 x1) (= x2 9) ) ))
(check-sat)
(declare-const x0 Int)
(declare-const x1 Int)
(declare-const x2 Int)
(assert(and (or (= x0 10) (> x0 10)) (and (= x2 x0) (= x2 9) ) ))
(check-sat)
結果:
$ ./z3 -in
(declare-const x0 Int)
(declare-const x1 Int)
(declare-const x2 Int)
(assert(and (and (< x0 10) (= x1 (- x0 1))) (and (= x2 x1) (= x2 9) ) ))
(check-sat)
unsat
不滿足
$ ./z3 -in
(declare-const x0 Int)
(declare-const x1 Int)
(declare-const x2 Int)
(assert(and (or (= x0 10) (> x0 10)) (and (= x2 x0) (= x2 9) ) ))
(check-sat)
unsat
也是不滿足
因此兩者在執行不會出現 assert 錯誤訊息
對照 C 程式編譯後的執行結果:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int show(int x) {
if (x < 10)
x = x - 1;
assert(x != 9);
return 0;
}
int main() {
show(3);
return 0;
}
結果:
$ gcc -o test test.c
$ ./test
$
果然沒出現 assert 錯誤訊息,我們可見到形式化驗證的威力:在不需要執行程式的狀況下,充分檢驗各種輸入造成的影響。
在 軟體缺失導致的危害 提到 1970 年代推出的首款廣體民航客機波音 747 軟體由大約 40 萬行程式碼構成,而 2011 年引進的波音 787 的軟體規模則是波音 747 的 16 倍,約 650 萬行程式碼。換言之,你我的性命緊繫於一系列極為複雜的軟體系統之中,如果程式開發者總是撰寫程式碼後,只用上述有限的數值輸入並對結果,勢必無法顧及全面,難保日後不出問題,這也是為何形式化驗證大量用於航空和醫療這類關鍵的領域中。
int show(int x) {
if (x <= 10)
x = x - 1;
assert(x != 9);
return 0;
}
當輸入某個整數值時,執行會出現 assert 錯誤訊息
formal verification