Hoare霍尔逻辑(第一部分)
Set Warnings "-notation-overridden,-parsing".
From PLF Require Import Maps.
From Coq Require Import Bool.Bool.
From Coq Require Import Arith.Arith.
From Coq Require Import Arith.EqNat.
From Coq Require Import Arith.PeanoNat. Import Nat.
From Coq Require Import omega.Omega.
From PLF Require Import Imp.
在'逻辑基础'('软件基础' 的第一章) 中,
我们用课程前面的部分中学习的数学工具研究了一个小型编程语言 Imp 。
如果在这里打住,我们已经有了一些实用的东西了:一套用来定义并讨论
程序语言和它们的功能的工具。这些工具应用于程序语言的一些关键性质,
(从数学角度来讲)是严谨和灵活的,并且是易于使用的。
所有这些性质都是程序语言的设计者,编译器作者,以及用户所最关心的。
当然,这些性质中的很多是极为基础的,以至于在我们对编程语言的认知
中甚至不会把它们自然地当做“定理”来对待。但看似直观明显的属性有时候
可能会非常微妙(有时也会错得很微妙!)。
在这一卷稍后,我们将会在讨论'类型(types)'和'类型可靠性
(type soundness)'时,回归到整个语言的元理论性质研究。不过现在
我们要着眼于另外一些问题。
我们的目标是给出一些'软件形式化验证(program verification)'
的例子,也就是说,用 Imp 的精确定义来形式地证明某段程序符合某
个规范。我们会建立一个叫做'弗洛伊德-霍尔逻辑(Floyd-Hoare Logic)'
的系统(一般简称 '霍尔逻辑(Hoare Logic)'),它是 Imp 的语法构造
加上了一个通用的“验证规则”的组合,可以用来说明包含这段结构的程序
之正确性。
霍尔逻辑发源于1960年代,至今为止依然是活跃的研究主题。
它用于规范和验证许多学术界与工业界广泛使用的软件系统,并处于核心地位。
霍尔逻辑组合了两个绝妙的想法:用自然的方式来编写程序的'规范(specifications)'
;和一种用来证明程序正确地实现了规范的'复合证明技巧(compositional proof technique)'
——其中“复合”的意思是,这些证明的结构直接反映了相应程序的结构。
本章概览...
主题:
目标:
计划:
- 我们给 Imp 定义了'抽象语法树(abstract syntax trees)';
还有'求值关系(evaluation relation)'(一个在状态上的偏函数),
它给出了程序的'操作语义(operational semantics)'。
- 我们证明了许多'元理论性质(metatheoretic properties)',也就是
从高层次角度来说,这些性质是关于这整个语言的,而不是关于任何一段
单独的程序。这包括了:
- 求值过程的确定性
- 用不同方式写下的定义之间的等价关系(例如,用求值函数和求值关系来定
义算术表达式的化简规则)
- 保证一系列程序必会停机
- 数个实用的程序变换之正确性(从语义等价的角度来讲)
- 程序的等价关系(在 Equiv 这一章里)。
- 求值过程的确定性
- 推理 Imp 程序'功能正确性(functional correctness)' 的系统方法
- 一种自然表达'程序规范(program specifications)'的记号
- 一种关于程序正确性的'复合的(compositional)'证明技巧
- 程序规范(断言/霍尔三元组)
- 证明规则
- 循环不变式
- 带标注的程序
- 例子
断言
Module ExAssertions.
Definition as1 : Assertion := fun st ⇒ st X = 3.
Definition as2 : Assertion := fun st ⇒ st X ≤ st Y.
Definition as3 : Assertion :=
fun st ⇒ st X = 3 ∨ st X ≤ st Y.
Definition as4 : Assertion :=
fun st ⇒ st Z × st Z ≤ st X ∧
¬ (((S (st Z)) × (S (st Z))) ≤ st X).
Definition as5 : Assertion :=
fun st ⇒ st Z = max (st X) (st Y).
Definition as6 : Assertion := fun st ⇒ True.
Definition as7: Assertion := fun st ⇒ False.
(* 请在此处解答 *)
End ExAssertions.
☐
fun st ⇒ (st Z) × (st Z) ≤ m ∧
¬((S (st Z)) × (S (st Z)) ≤ m)
Z × Z ≤ m ∧ ~((S Z) × (S Z) ≤ m).
Definition assert_implies (P Q : Assertion) : Prop :=
∀ st, P st → Q st.
Notation "P ->> Q" := (assert_implies P Q)
(at level 80) : hoare_spec_scope.
Open Scope hoare_spec_scope.
这里的记号 hoare_spec_scope 告诉 Coq, 这个记号不是全局的,
我们打算把它用在特定的上下文里。Open Scope 告诉 Coq,这个文件就是
一个我们将采用此记号的上下文。
我们也需要一个表达断言之间“当且仅当”的蕴含关系:
We can actually make our informal convention for writing assertions
without explicit mention of states work in formal Coq too.
This technique uses Coq coercions and anotation scopes (much
as we did with %imp in Imp) to automatically lift
aexps, numbers, and Props into Assertions when they appear
in the %assertion scope or when Coq knows the type of an
expression is Assertion.
Definition Aexp : Type := state → nat.
Definition assert_of_Prop (P : Prop) : Assertion := fun _ ⇒ P.
Definition Aexp_of_nat (n : nat) : Aexp := fun _ ⇒ n.
Definition Aexp_of_aexp (a : aexp) : Aexp := fun st ⇒ aeval st a.
Coercion assert_of_Prop : Sortclass >-> Assertion.
Coercion Aexp_of_nat : nat >-> Aexp.
Coercion Aexp_of_aexp : aexp >-> Aexp.
Arguments assert_of_Prop /.
Arguments Aexp_of_nat /.
Arguments Aexp_of_aexp /.
Bind Scope assertion_scope with Assertion.
Bind Scope assertion_scope with Aexp.
Delimit Scope assertion_scope with assertion.
Notation assert P := (P%assertion : Assertion).
Notation mkAexp a := (a%assertion : Aexp).
Notation "~ P" := (fun st ⇒ ¬ assert P st) : assertion_scope.
Notation "P /\ Q" := (fun st ⇒ assert P st ∧ assert Q st) : assertion_scope.
Notation "P \/ Q" := (fun st ⇒ assert P st ∨ assert Q st) : assertion_scope.
Notation "P -> Q" := (fun st ⇒ assert P st → assert Q st) : assertion_scope.
Notation "P <-> Q" := (fun st ⇒ assert P st ↔ assert Q st) : assertion_scope.
Notation "a = b" := (fun st ⇒ mkAexp a st = mkAexp b st) : assertion_scope.
Notation "a <> b" := (fun st ⇒ mkAexp a st ≠ mkAexp b st) : assertion_scope.
Notation "a <= b" := (fun st ⇒ mkAexp a st ≤ mkAexp b st) : assertion_scope.
Notation "a < b" := (fun st ⇒ mkAexp a st < mkAexp b st) : assertion_scope.
Notation "a >= b" := (fun st ⇒ mkAexp a st ≥ mkAexp b st) : assertion_scope.
Notation "a > b" := (fun st ⇒ mkAexp a st > mkAexp b st) : assertion_scope.
Notation "a + b" := (fun st ⇒ mkAexp a st + mkAexp b st) : assertion_scope.
Notation "a - b" := (fun st ⇒ mkAexp a st - mkAexp b st) : assertion_scope.
Notation "a * b" := (fun st ⇒ mkAexp a st × mkAexp b st) : assertion_scope.
One small limitation of this approach is that we don't have
an automatic way to coerce function applications that appear
within an assertion to make appropriate use of the state.
Instead, we use an explicit ap operator to lift the function.
Definition ap {X} (f : nat → X) (x : Aexp) :=
fun st ⇒ f (x st).
Definition ap2 {X} (f : nat → nat → X) (x : Aexp) (y : Aexp) (st : state) :=
f (x st) (y st).
Module ExPrettyAssertions.
Definition as1 : Assertion := X = 3.
Definition as2 : Assertion := X ≤ Y.
Definition as3 : Assertion := X = 3 ∨ X ≤ Y.
Definition as4 : Assertion :=
Z × Z ≤ X ∧
¬ (((ap S Z) × (ap S Z)) ≤ X).
Definition as5 : Assertion :=
Z = ap2 max X Y.
Definition as6 : Assertion := True.
Definition as7 : Assertion := False.
End ExPrettyAssertions.
霍尔三元组
- “如果命令 c 在一个复合断言 P 的状态开始,并且如果 c 最终在一个结束状态停机,这个结束状态会满足断言 Q。”
Definition hoare_triple
(P : Assertion) (c : com) (Q : Assertion) : Prop :=
∀ st st',
st =[ c ]=> st' →
P st →
Q st'.
因为我们将会在霍尔三元组上做很多研究,所以一个紧凑的记号是非常便
利的:
{{P}} c {{Q}}.
(传统的记号是 {P} c {Q},不过单花括号已经被用在 Coq 中其
它东西上了。
{{P}} c {{Q}}.
Notation "{{ P }} c {{ Q }}" :=
(hoare_triple P c Q) (at level 90, c at next level)
: hoare_spec_scope.
练习:1 星, standard, optional (triples)
用中文重新表述下列霍尔三元组。1) {{True}} c {{X = 5}}
2) {{X = m}} c {{X = m + 5)}}
3) {{X ≤ Y}} c {{Y ≤ X}}
4) {{True}} c {{False}}
5) {{X = m}}
c
{{Y = real_fact m}}
6) {{X = m}}
c
{{(Z × Z) ≤ m ∧ ¬(((S Z) × (S Z)) ≤ m)}}
(* 请在此处解答 *)
☐
☐
练习:1 星, standard, optional (valid_triples)
下列的霍尔三元组是否'成立',亦即,表述的 P、c、Q 之间的 关系是否为真?1) {{True}} X ::= 5 {{X = 5}}
2) {{X = 2}} X ::= X + 1 {{X = 3}}
3) {{True}} X ::= 5;; Y ::= 0 {{X = 5}}
4) {{X = 2 ∧ X = 3}} X ::= 5 {{X = 0}}
5) {{True}} SKIP {{False}}
6) {{False}} SKIP {{True}}
7) {{True}} WHILE true DO SKIP END {{False}}
8) {{X = 0}}
WHILE X = 0 DO X ::= X + 1 END
{{X = 1}}
9) {{X = 1}}
WHILE ~(X = 0) DO X ::= X + 1 END
{{X = 100}}
(* 请在此处解答 *)
☐
☐
Theorem hoare_post_true : ∀ (P Q : Assertion) c,
(∀ st, Q st) →
{{P}} c {{Q}}.
Theorem hoare_pre_false : ∀ (P Q : Assertion) c,
(∀ st, ¬ (P st)) →
{{P}} c {{Q}}.
Proof.
intros P Q c H. unfold hoare_triple.
intros st st' Heval HP.
unfold not in H. apply H in HP.
inversion HP. Qed.
intros P Q c H. unfold hoare_triple.
intros st st' Heval HP.
unfold not in H. apply H in HP.
inversion HP. Qed.
证明规则
赋值
{{ Y = 1 }} X ::= Y {{ X = 1 }}
{{ Y + Z = 1 }} X ::= Y + Z {{ X = 1 }}
{{ a = 1 }} X ::= a {{ X = 1 }}
{{ Q [X ⊢> a] }} X ::= a {{ Q }}
{{ (X ≤ 5) [X ⊢> X + 1]
i.e., X + 1 ≤ 5 }}
X ::= X + 1
{{ X ≤ 5 }}
{{ (X = 3) [X ⊢> 3]
i.e., 3 = 3 }}
X ::= 3
{{ X = 3 }}
{{ (0 ≤ X ∧ X ≤ 5) [X ⊢> 3]
i.e., (0 ≤ 3 ∧ 3 ≤ 5) }}
X ::= 3
{{ 0 ≤ X ∧ X ≤ 5 }}
Definition assn_sub X a (P:Assertion) : Assertion :=
fun (st : state) ⇒
P (X !-> aeval st a ; st).
Notation "P [ X ⊢> a ]" := (assn_sub X a P)
(at level 10, X at next level).
也就是说,P [X ⊢> a] 是一个新的断言——我们把它叫做 P' ——
它就是 P,不过当 P 在当前状态中查找变量 X 的时候,P' 使用表
达式 a 的值。
为了演示工作原理,我们来计算一下这几个例子中发生了些什么。首先,假设
P' 是 (X ≤ 5) [X ⊢> 3] ——或者,更形式化地, P' 是 Coq 表达式
fun st ⇒
(fun st' ⇒ st' X ≤ 5)
(X !-> aeval st 3 ; st),
它简化为
fun st ⇒
(fun st' ⇒ st' X ≤ 5)
(X !-> 3 ; st)
并且可以进一步简化为
fun st ⇒
((X !-> 3 ; st) X) ≤ 5
最终是
fun st ⇒
3 ≤ 5.
也就是说,P' 是一个断言指出 3 小于等于 5(像我们想的一样)。
一个更有趣的例子是,假设 P' 是 (X ≤ 5) [X ⊢> X + 1]。形式化地,P'
是 Coq 表达式
fun st ⇒
(fun st' ⇒ st' X ≤ 5)
(X !-> aeval st (X + 1) ; st),
它简化为
fun st ⇒
(X !-> aeval st (X + 1) ; st) X ≤ 5
并且进一步简化为
fun st ⇒
(aeval st (X + 1)) ≤ 5.
也就是说,P' 指出 X + 1 最多是 5。
现在,利用替换的概念,我们可以给出下述赋值证明规则的严谨证明:
我们可以形式化地证明这个规则总是成立。
fun st ⇒
(fun st' ⇒ st' X ≤ 5)
(X !-> aeval st 3 ; st),
fun st ⇒
(fun st' ⇒ st' X ≤ 5)
(X !-> 3 ; st)
fun st ⇒
((X !-> 3 ; st) X) ≤ 5
fun st ⇒
3 ≤ 5.
fun st ⇒
(fun st' ⇒ st' X ≤ 5)
(X !-> aeval st (X + 1) ; st),
fun st ⇒
(X !-> aeval st (X + 1) ; st) X ≤ 5
fun st ⇒
(aeval st (X + 1)) ≤ 5.
(hoare_asgn) | |
{{Q [X ⊢> a]}} X ::= a {{Q}} |
Theorem hoare_asgn : ∀ Q X a,
{{Q [X ⊢> a]}} X ::= a {{Q}}.
Proof.
unfold hoare_triple.
intros Q X a st st' HE HQ.
inversion HE. subst.
unfold assn_sub in HQ. assumption. Qed.
unfold hoare_triple.
intros Q X a st st' HE HQ.
inversion HE. subst.
unfold assn_sub in HQ. assumption. Qed.
下述是一个用到了这个规则的形式化证明。
Example assn_sub_example :
{{(X < 5) [X ⊢> X + 1]}}
X ::= X + 1
{{X < 5}}.
Proof.
(* 课上已完成 *)
apply hoare_asgn. Qed.
当然,更加有帮助的是证明这个更简单的三元组:
{{X < 4}} X ::= X + 1 {{X < 5}}
我们会在下一节中了解怎么做。
1) {{ (X ≤ 10) [X ⊢> 2 × X] }}
X ::= 2 × X
{{ X ≤ 10 }}
2) {{ (0 ≤ X ∧ X ≤ 5) [X ⊢> 3] }}
X ::= 3
{{ 0 ≤ X ∧ X ≤ 5 }}
……翻译成形式化的表达的(名字分别叫 assn_sub_ex1
和 assn_sub_ex2),并且用 hoare_asgn 来证明它们。
{{X < 4}} X ::= X + 1 {{X < 5}}
练习:2 星, standard (hoare_asgn_examples)
将下列非正式的霍尔三元组……1) {{ (X ≤ 10) [X ⊢> 2 × X] }}
X ::= 2 × X
{{ X ≤ 10 }}
2) {{ (0 ≤ X ∧ X ≤ 5) [X ⊢> 3] }}
X ::= 3
{{ 0 ≤ X ∧ X ≤ 5 }}
(* 请在此处解答 *)
(* 请勿修改下面这一行: *)
Definition manual_grade_for_hoare_asgn_examples : option (nat×string) := None.
☐
练习:2 星, standard, recommended (hoare_asgn_wrong)
几乎所有人在看赋值规则第一眼就会觉得它是反向的。如果你还感觉很 迷惑,思考一些“正向”的规则可能有帮助。这里是一个看起来挺自然的 霍尔三元组:(hoare_asgn_wrong) | |
{{ True }} X ::= a {{ X = a }} |
(* 请在此处解答 *)
(* 请勿修改下面这一行: *)
Definition manual_grade_for_hoare_asgn_wrong : option (nat×string) := None.
☐
练习:3 星, advanced (hoare_asgn_fwd)
然而,我们可以通过引入一个'参数' m(一个 Coq 整数)来记录 X 原 来的值,来定义一个赋值的证明规则,它可以更直觉地,“正向地工作”。(hoare_asgn_fwd) | |
{{fun st => P st /\ st X = m}} | |
X ::= a | |
{{fun st => P st' /\ st X = aeval st' a }} | |
(其中 st' = (X !-> m ; st)) |
Theorem hoare_asgn_fwd :
∀ m a P,
{{fun st ⇒ P st ∧ st X = m}}
X ::= a
{{fun st ⇒ P (X !-> m ; st)
∧ st X = aeval (X !-> m ; st) a }}.
Proof.
(* 请在此处解答 *) Admitted.
☐
练习:2 星, advanced, optional (hoare_asgn_fwd_exists)
另外一种定义正向赋值规则的方式是,对变量在赋值之前的值做存在量化。 证明这是成立的。(hoare_asgn_fwd_exists) | |
{{fun st => P st}} | |
X ::= a | |
{{fun st => exists m, P (X !-> m ; st) /\ | |
st X = aeval (X !-> m ; st) a }} |
Theorem hoare_asgn_fwd_exists :
∀ a P,
{{fun st ⇒ P st}}
X ::= a
{{fun st ⇒ ∃ m, P (X !-> m ; st) ∧
st X = aeval (X !-> m ; st) a }}.
Proof.
intros a P.
(* 请在此处解答 *) Admitted.
☐
缩放
{{(X = 3) [X ⊢> 3]}} X ::= 3 {{X = 3}}
{{True}} X ::= 3 {{X = 3}}
{{P'}} c {{Q}} | |
P <<->> P' | (hoare_consequence_pre_equiv) |
{{P}} c {{Q}} |
{{P'}} c {{Q}} | |
P ->> P' | (hoare_consequence_pre) |
{{P}} c {{Q}} |
{{P}} c {{Q'}} | |
Q' ->> Q | (hoare_consequence_post) |
{{P}} c {{Q}} |
Theorem hoare_consequence_pre : ∀ (P P' Q : Assertion) c,
{{P'}} c {{Q}} →
P ->> P' →
{{P}} c {{Q}}.
Proof.
intros P P' Q c Hhoare Himp.
intros st st' Hc HP. apply (Hhoare st st').
assumption. apply Himp. assumption. Qed.
intros P P' Q c Hhoare Himp.
intros st st' Hc HP. apply (Hhoare st st').
assumption. apply Himp. assumption. Qed.
Theorem hoare_consequence_post : ∀ (P Q Q' : Assertion) c,
{{P}} c {{Q'}} →
Q' ->> Q →
{{P}} c {{Q}}.
Proof.
intros P Q Q' c Hhoare Himp.
intros st st' Hc HP.
apply Himp.
apply (Hhoare st st').
assumption. assumption. Qed.
intros P Q Q' c Hhoare Himp.
intros st st' Hc HP.
apply Himp.
apply (Hhoare st st').
assumption. assumption. Qed.
例如,我们可以这样应用第一条规则:
{{ True }} ->>
{{ (X = 1) [X ⊢> 1] }}
X ::= 1
{{ X = 1 }}
或者,形式化地:
{{ True }} ->>
{{ (X = 1) [X ⊢> 1] }}
X ::= 1
{{ X = 1 }}
Example hoare_asgn_example1 :
{{True}} X ::= 1 {{X = 1}}.
Proof.
(* 课上已完成 *)
apply hoare_consequence_pre
with (P' := (X = 1) [X ⊢> 1]).
apply hoare_asgn.
intros st H. unfold assn_sub, t_update. simpl. reflexivity.
Qed.
我们也可以用它来证明之前提到的例子。
{{ X < 4 }} ->>
{{ (X < 5)[X ⊢> X + 1] }}
X ::= X + 1
{{ X < 5 }}
或者,形式化地:
{{ X < 4 }} ->>
{{ (X < 5)[X ⊢> X + 1] }}
X ::= X + 1
{{ X < 5 }}
Example assn_sub_example2 :
{{X < 4}}
X ::= X + 1
{{X < 5}}.
Proof.
(* 课上已完成 *)
apply hoare_consequence_pre
with (P' := (X < 5) [X ⊢> X + 1]).
apply hoare_asgn.
intros st H. unfold assn_sub, t_update. simpl in ×. omega.
Qed.
最后,为了证明中的方便,我们有一个组合起来的缩放规则,可以让
我们同时改变前置条件和后置条件。
{{P'}} c {{Q'}} | |
P ->> P' | |
Q' ->> Q | (hoare_consequence) |
{{P}} c {{Q}} |
Theorem hoare_consequence : ∀ (P P' Q Q' : Assertion) c,
{{P'}} c {{Q'}} →
P ->> P' →
Q' ->> Q →
{{P}} c {{Q}}.
Proof.
intros P P' Q Q' c Hht HPP' HQ'Q.
apply hoare_consequence_pre with (P' := P').
apply hoare_consequence_post with (Q' := Q').
assumption. assumption. assumption. Qed.
intros P P' Q Q' c Hht HPP' HQ'Q.
apply hoare_consequence_pre with (P' := P').
apply hoare_consequence_post with (Q' := Q').
assumption. assumption. assumption. Qed.
题外话:eapply 策略
Example hoare_asgn_example1' :
{{True}}
X ::= 1
{{X = 1}}.
Proof.
eapply hoare_consequence_pre.
apply hoare_asgn.
intros st H. reflexivity. Qed.
一般来说,eapply H 策略和 apply H 的工作方式相同,不过
当匹配目标和 H 的结论时,若无法确定如何实例化 H 的前提中出
现的变量,eapply H 并不会失败,而是会把这些变量
替换为'存在变量(existential variables)'(写做 ?nnn),
它的功能是作为接下来证明中会(通过进一步的合一)确定
的变量的占位符。
如果要 Qed 成功,所有的存在变量都要在证明结束前被确定。
否则 Coq 将会(正义地)拒绝接受这个证明。Coq 策略
会构建一个证明对象,如果这个证明对象中还有一些存在变量没有被确定,
它就不是完整的证明对象了。
Lemma silly1 : ∀ (P : nat → nat → Prop) (Q : nat → Prop),
(∀ x y : nat, P x y) →
(∀ x y : nat, P x y → Q x) →
Q 42.
Proof.
intros P Q HP HQ. eapply HQ. apply HP.
Coq 在 apply HP 之后提出了一个警告。(“All the remaining goals
are on the shelf”,意思是我们已经完成了所有的顶层的证明目标,
然而在这个过程中我们将一些放到一边打算待会做,我们还没有完成它们。)
用 Qed 结束证明会产生一个错误。
Abort.
一个附加的限制是,若项中含有在创建存在变量时还不存在的普通变量,
则存在变量无法被这样的项实例化。
(原因当然是如果我们允许这样做逻辑系统就会变得不再自洽。)
Lemma silly2 :
∀ (P : nat → nat → Prop) (Q : nat → Prop),
(∃ y, P 42 y) →
(∀ x y : nat, P x y → Q x) →
Q 42.
Proof.
intros P Q HP HQ. eapply HQ. destruct HP as [y HP'].
Fail apply HP'.
在这里使用 apply HP' 将会失败并产生如下错误,这里省略了一些细节:
Unable to unify "?y@{...}" with "y" (cannot instantiate
"?y" because "y" is not in its scope: ...
有一个简单的解决办法:把 destruct HP 提到 eapply HQ '之前'。
Unable to unify "?y@{...}" with "y" (cannot instantiate
"?y" because "y" is not in its scope: ...
Abort.
Lemma silly2_fixed :
∀ (P : nat → nat → Prop) (Q : nat → Prop),
(∃ y, P 42 y) →
(∀ x y : nat, P x y → Q x) →
Q 42.
Proof.
intros P Q HP HQ. destruct HP as [y HP'].
eapply HQ. apply HP'.
Qed.
Lemma silly2_fixed :
∀ (P : nat → nat → Prop) (Q : nat → Prop),
(∃ y, P 42 y) →
(∀ x y : nat, P x y → Q x) →
Q 42.
Proof.
intros P Q HP HQ. destruct HP as [y HP'].
eapply HQ. apply HP'.
Qed.
最后一步中的 apply HP' 将目标中的存在变量匹配和变量 y 归一。
注意,assumption 策略并不能在这里正常工作,因为它不能处理
存在变量。然而,Coq 也提供了一个 eassumption 策略,如果当
前有一个前提通过将结论中的存在变量填好而匹配,它就解决目标。
我们可以用它来代替 apply HP',如果你想的话。
Lemma silly2_eassumption : ∀ (P : nat → nat → Prop) (Q : nat → Prop),
(∃ y, P 42 y) →
(∀ x y : nat, P x y → Q x) →
Q 42.
Proof.
intros P Q HP HQ. destruct HP as [y HP']. eapply HQ. eassumption.
Qed.
练习:2 星, standard (hoare_asgn_examples_2)
将下述的非形式化霍尔三元组{{ X + 1 ≤ 5 }} X ::= X + 1 {{ X ≤ 5 }}
{{ 0 ≤ 3 ∧ 3 ≤ 5 }} X ::= 3 {{ 0 ≤ X ∧ X ≤ 5 }}
(* 请在此处解答 *)
(* 请勿修改下面这一行: *)
Definition manual_grade_for_hoare_asgn_examples_2 : option (nat×string) := None.
☐
Theorem hoare_skip : ∀ P,
{{P}} SKIP {{P}}.
Proof.
intros P st st' H HP. inversion H. subst.
assumption. Qed.
intros P st st' H HP. inversion H. subst.
assumption. Qed.
顺序
{{ P }} c1 {{ Q }} | |
{{ Q }} c2 {{ R }} | (hoare_seq) |
{{ P }} c1;;c2 {{ R }} |
Theorem hoare_seq : ∀ P Q R c1 c2,
{{Q}} c2 {{R}} →
{{P}} c1 {{Q}} →
{{P}} c1;;c2 {{R}}.
Proof.
intros P Q R c1 c2 H1 H2 st st' H12 Pre.
inversion H12; subst.
apply (H1 st'0 st'); try assumption.
apply (H2 st st'0); assumption. Qed.
intros P Q R c1 c2 H1 H2 st st' H12 Pre.
inversion H12; subst.
apply (H1 st'0 st'); try assumption.
apply (H2 st st'0); assumption. Qed.
可以注意到在 hoare_seq 中,前提以一个相反的顺序给出
(先 c2 再 c1)。这符合在大部分情况中自然的信息(策略)输入顺序,
因为最自然地构造一个霍尔逻辑证明的方式是,从这个程序的末尾开始
(在后置条件的状态中),然后逆推直到程序开始的地方。
一种非形式化地展示利用组合规则的证明的方式是将其写为“带标注
的程序”,其中中间状态断言 Q 写在 c1 和 c2 之间:
{{ a = n }}
X ::= a;;
{{ X = n }} <--- decoration for Q
SKIP
{{ X = n }}
下面是一个同时包括赋值和组合的例子。
{{ a = n }}
X ::= a;;
{{ X = n }} <--- decoration for Q
SKIP
{{ X = n }}
Example hoare_asgn_example3 : ∀ (a:aexp) (n:nat),
{{(a = n)%assertion}}
X ::= a;; SKIP
{{X = n}}.
Proof.
intros a n. eapply hoare_seq.
- (* 组合右侧 *)
apply hoare_skip.
- (* 组合左侧 *)
eapply hoare_consequence_pre. apply hoare_asgn.
intros st H. simpl in ×. subst. reflexivity.
Qed.
我们一般会将 hoare_seq 和
hoare_consequence_pre 以及 eapply 策略一起使用,如上所示。
{{ True }} ->>
{{ 1 = 1 }}
X ::= 1;;
{{ X = 1 }} ->>
{{ X = 1 ∧ 2 = 2 }}
Y ::= 2
{{ X = 1 ∧ Y = 2 }}
(带 “->>” 的标记代表了使用 hoare_consequence_pre。)
练习:2 星, standard, recommended (hoare_asgn_example4)
将这个“标注程序”翻译成正式证明:{{ True }} ->>
{{ 1 = 1 }}
X ::= 1;;
{{ X = 1 }} ->>
{{ X = 1 ∧ 2 = 2 }}
Y ::= 2
{{ X = 1 ∧ Y = 2 }}
Example hoare_asgn_example4 :
{{True}}
X ::= 1;; Y ::= 2
{{ (X = 1 ∧ Y = 2)}}.
Proof.
(* 请在此处解答 *) Admitted.
☐
练习:3 星, standard (swap_exercise)
写一个 Imp 程序 c,用来交换变量 X 和 Y 并且说明 它符合如下规范:{{X ≤ Y}} c {{Y ≤ X}}
Definition swap_program : com
(* 将本行替换成 ":= _你的_定义_ ." *). Admitted.
Theorem swap_exercise :
{{X ≤ Y}}
swap_program
{{Y ≤ X}}.
Proof.
(* 请在此处解答 *) Admitted.
☐
练习:3 星, standard (hoarestate1)
解释为何下列命题无法被证明:∀ (a : aexp) (n : nat),
{{fun st ⇒ aeval st a = n}}
X ::= 3;; Y ::= a
{{Y = n}}.
(* 请在此处解答 *)
(* 请勿修改下面这一行: *)
Definition manual_grade_for_hoarestate1 : option (nat×string) := None.
☐
条件
{{P}} c1 {{Q}} | |
{{P}} c2 {{Q}} | |
{{P}} TEST b THEN c1 ELSE c2 {{Q}} |
{{ True }}
TEST X = 0
THEN Y ::= 2
ELSE Y ::= X + 1
FI
{{ X ≤ Y }}
{{P /\ b}} c1 {{Q}} | |
{{P /\ ~ b}} c2 {{Q}} | (hoare_if) |
{{P}} TEST b THEN c1 ELSE c2 FI {{Q}} |
Definition bassn b : Assertion :=
fun st ⇒ (beval st b = true).
Coercion bassn : bexp >-> Assertion.
Arguments bassn /.
下列是一些有关于 bassn 的有用的引理:
Lemma bexp_eval_true : ∀ b st,
beval st b = true → (bassn b) st.
Lemma bexp_eval_false : ∀ b st,
beval st b = false → ¬ ((bassn b) st).
现在我们就可以形式化条件命令的证明规则,并且证明它的正确性。
Theorem hoare_if : ∀ P Q (b:bexp) c1 c2,
{{ P ∧ b }} c1 {{Q}} →
{{ P ∧ ¬ b}} c2 {{Q}} →
{{P}} TEST b THEN c1 ELSE c2 FI {{Q}}.
(* Theorem hoare_if : forall P Q b c1 c2,
{{fun st => P st /\ bassn b st}} c1 {{Q}} ->
{{fun st => P st /\ ~ (bassn b st)}} c2 {{Q}} ->
{{P}} TEST b THEN c1 ELSE c2 FI {{Q}}. *)
Proof.
intros P Q b c1 c2 HTrue HFalse st st' HE HP.
inversion HE; subst.
- (* b 是 true *)
apply (HTrue st st').
assumption.
split. assumption.
apply bexp_eval_true. assumption.
- (* b 是 false *)
apply (HFalse st st').
assumption.
split. assumption.
apply bexp_eval_false. assumption. Qed.
intros P Q b c1 c2 HTrue HFalse st st' HE HP.
inversion HE; subst.
- (* b 是 true *)
apply (HTrue st st').
assumption.
split. assumption.
apply bexp_eval_true. assumption.
- (* b 是 false *)
apply (HFalse st st').
assumption.
split. assumption.
apply bexp_eval_false. assumption. Qed.
Example if_example :
{{True}}
TEST X = 0
THEN Y ::= 2
ELSE Y ::= X + 1
FI
{{X ≤ Y}}.
Proof.
(* 课上已完成 *)
apply hoare_if.
- (* Then *)
eapply hoare_consequence_pre. apply hoare_asgn.
unfold bassn, assn_sub, t_update, assert_implies.
simpl. intros st [_ H].
apply eqb_eq in H.
rewrite H. omega.
- (* Else *)
eapply hoare_consequence_pre. apply hoare_asgn.
unfold assn_sub, t_update, assert_implies.
simpl; intros st _. omega.
Qed.
(* 课上已完成 *)
apply hoare_if.
- (* Then *)
eapply hoare_consequence_pre. apply hoare_asgn.
unfold bassn, assn_sub, t_update, assert_implies.
simpl. intros st [_ H].
apply eqb_eq in H.
rewrite H. omega.
- (* Else *)
eapply hoare_consequence_pre. apply hoare_asgn.
unfold assn_sub, t_update, assert_implies.
simpl; intros st _. omega.
Qed.
Theorem if_minus_plus :
{{True}}
TEST X ≤ Y
THEN Z ::= Y - X
ELSE Y ::= X + Z
FI
{{Y = X + Z}}.
Proof.
(* 请在此处解答 *) Admitted.
☐
练习:单侧条件
练习:4 星, standard (if1_hoare)
在这个练习中我们考虑对 Imp 加入形如 IF1 b THEN c FI 的“单边条件”。 这里 b 是个布尔表达式而 c 是一个命令。如果 b 化简为 true, c 就被执行,而如果 b 化简为 false, IF1 b THEN c FI 就啥也不做。Module If1.
Inductive com : Type :=
| CSkip : com
| CAss : string → aexp → com
| CSeq : com → com → com
| CIf : bexp → com → com → com
| CWhile : bexp → com → com
| CIf1 : bexp → com → com.
Bind Scope imp_scope with com.
Notation "'SKIP'" :=
CSkip : imp_scope.
Notation "c1 ;; c2" :=
(CSeq c1 c2) (at level 80, right associativity) : imp_scope.
Notation "X '::=' a" :=
(CAss X a) (at level 60) : imp_scope.
Notation "'WHILE' b 'DO' c 'END'" :=
(CWhile b c) (at level 80, right associativity) : imp_scope.
Notation "'TEST' e1 'THEN' e2 'ELSE' e3 'FI'" :=
(CIf e1 e2 e3) (at level 80, right associativity) : imp_scope.
Notation "'IF1' b 'THEN' c 'FI'" :=
(CIf1 b c) (at level 80, right associativity) : imp_scope.
接下来我们需要拓展求值规则以包含 IF1 的情形。我们把任务交给你……
应该往 ceval 中加入哪条(哪些)命令来化简单边分支命令?
Reserved Notation "st '=[' c ']=>' st'" (at level 40).
Inductive ceval : com → state → state → Prop :=
| E_Skip : ∀ st,
st =[ SKIP ]=> st
| E_Ass : ∀ st a1 n x,
aeval st a1 = n →
st =[ x ::= a1 ]=> (x !-> n ; st)
| E_Seq : ∀ c1 c2 st st' st'',
st =[ c1 ]=> st' →
st' =[ c2 ]=> st'' →
st =[ c1 ;; c2 ]=> st''
| E_IfTrue : ∀ st st' b c1 c2,
beval st b = true →
st =[ c1 ]=> st' →
st =[ TEST b THEN c1 ELSE c2 FI ]=> st'
| E_IfFalse : ∀ st st' b c1 c2,
beval st b = false →
st =[ c2 ]=> st' →
st =[ TEST b THEN c1 ELSE c2 FI ]=> st'
| E_WhileFalse : ∀ b st c,
beval st b = false →
st =[ WHILE b DO c END ]=> st
| E_WhileTrue : ∀ st st' st'' b c,
beval st b = true →
st =[ c ]=> st' →
st' =[ WHILE b DO c END ]=> st'' →
st =[ WHILE b DO c END ]=> st''
(* 请在此处解答 *)
where "st '=[' c ']=>' st'" := (ceval c st st').
现在我们把霍尔三元组的定义和记号重新抄一遍。
Definition hoare_triple
(P : Assertion) (c : com) (Q : Assertion) : Prop :=
∀ st st',
st =[ c ]=> st' →
P st →
Q st'.
Notation "{{ P }} c {{ Q }}" := (hoare_triple P c Q)
(at level 90, c at next level)
: hoare_spec_scope.
最终我们(你)得指出一个关于单边条件语句的证明规则
hoare_if1,并且证明它。你得试着尽可能让它既是成立的,又足够精细(具体)。
(* 请在此处解答 *)
要拿到全部的分数,你还得证明一个定理 hoare_if1_good 指出你的规则足够精细,
能够证明下列的霍尔三元组是成立的:
{{ X + Y = Z }}
IF1 ~(Y = 0) THEN
X ::= X + Y
FI
{{ X = Z }}
Hint: 提示,你的证明会用到其它证明规则。因为我们开了个新模组,你得把你用到
的那些都拷到这来。
{{ X + Y = Z }}
IF1 ~(Y = 0) THEN
X ::= X + Y
FI
{{ X = Z }}
Lemma hoare_if1_good :
{{ X + Y = Z }}
(IF1 ~(Y = 0) THEN
X ::= X + Y
FI)
{{ X = Z }}.
Proof. (* 请在此处解答 *) Admitted.
End If1.
(* 请勿修改下面这一行: *)
Definition manual_grade_for_if1_hoare : option (nat×string) := None.
☐
循环
WHILE b DO c END
{{P}} WHILE b DO c END {{Q}}
{{P}} WHILE b DO c END {{P}}.
{{P}} WHILE b DO c END {{P ∧ ¬b}}
{{P}} c {{P}} | |
{{P}} WHILE b DO c END {{P /\ ~ b}} |
{{P /\ b}} c {{P}} | (hoare_while) |
{{P}} WHILE b DO c END {{P /\ ~ b}} |
Theorem hoare_while : ∀ P (b:bexp) c,
{{P ∧ b}} c {{P}} →
{{P}} WHILE b DO c END {{P ∧ ¬ b}}.
Proof.
intros P b c Hhoare st st' He HP.
(* 像之前见到过的,我们需要对 He 做归纳来推理。
因为,在“继续循环”的情形中,假设会是关于整个循环而不只是关于 c 的。*)
remember (WHILE b DO c END)%imp as wcom eqn:Heqwcom.
induction He;
try (inversion Heqwcom); subst; clear Heqwcom.
- (* E_WhileFalse *)
split. assumption. apply bexp_eval_false. assumption.
- (* E_WhileTrue *)
apply IHHe2. reflexivity.
apply (Hhoare st st'). assumption.
split. assumption. apply bexp_eval_true. assumption.
Qed.
intros P b c Hhoare st st' He HP.
(* 像之前见到过的,我们需要对 He 做归纳来推理。
因为,在“继续循环”的情形中,假设会是关于整个循环而不只是关于 c 的。*)
remember (WHILE b DO c END)%imp as wcom eqn:Heqwcom.
induction He;
try (inversion Heqwcom); subst; clear Heqwcom.
- (* E_WhileFalse *)
split. assumption. apply bexp_eval_false. assumption.
- (* E_WhileTrue *)
apply IHHe2. reflexivity.
apply (Hhoare st st'). assumption.
split. assumption. apply bexp_eval_true. assumption.
Qed.
令人费解的事情是,我们把断言 P 叫做“循环不变式” 并不代表它只是由
(上述问题中的)循环体所保证(也就是 {{P}} c {{P}},其中 c 是循环体),
而实际上 P '加上循环条件为真'才是 c 能够推出后置条件所
需要的前置条件。
这是略微弱化的(但十分重要)前提。例如,如果 P 是断言 X = 0,那么
P '是'下述循环的不变式:
WHILE X = 2 DO X := 1 END
即使它很明显'不是'只由循环体所导出。
WHILE X = 2 DO X := 1 END
Example while_example :
{{X ≤ 3}}
WHILE X ≤ 2
DO X ::= X + 1 END
{{X = 3}}.
Proof.
eapply hoare_consequence_post.
apply hoare_while.
eapply hoare_consequence_pre.
apply hoare_asgn.
unfold bassn, assn_sub, assert_implies, t_update. simpl.
intros st [H1 H2]. apply leb_complete in H2. omega.
unfold bassn, assert_implies. intros st [Hle Hb].
simpl in Hb. destruct ((st X) <=? 2) eqn : Heqle.
exfalso. apply Hb; reflexivity.
apply leb_iff_conv in Heqle. simpl in ×. omega.
Qed.
eapply hoare_consequence_post.
apply hoare_while.
eapply hoare_consequence_pre.
apply hoare_asgn.
unfold bassn, assn_sub, assert_implies, t_update. simpl.
intros st [H1 H2]. apply leb_complete in H2. omega.
unfold bassn, assert_implies. intros st [Hle Hb].
simpl in Hb. destruct ((st X) <=? 2) eqn : Heqle.
exfalso. apply Hb; reflexivity.
apply leb_iff_conv in Heqle. simpl in ×. omega.
Qed.
我们可以使用 WHILE 规则来证明下列的霍尔三元组。
Theorem always_loop_hoare : ∀ P Q,
{{P}} WHILE true DO SKIP END {{Q}}.
Proof.
(* 课上已完成 *)
intros P Q.
apply hoare_consequence_pre with (P' := (True:Assertion)).
eapply hoare_consequence_post.
apply hoare_while.
- (* 循环体维持不变式 *)
apply hoare_post_true. intros st. apply I.
- (* 循环体和循环条件不成立可以导出后条件 *)
simpl. intros st [Hinv Hguard].
exfalso. apply Hguard. reflexivity.
- (* 前条件可以导出不变式 *)
intros st H. constructor. Qed.
(* 课上已完成 *)
intros P Q.
apply hoare_consequence_pre with (P' := (True:Assertion)).
eapply hoare_consequence_post.
apply hoare_while.
- (* 循环体维持不变式 *)
apply hoare_post_true. intros st. apply I.
- (* 循环体和循环条件不成立可以导出后条件 *)
simpl. intros st [Hinv Hguard].
exfalso. apply Hguard. reflexivity.
- (* 前条件可以导出不变式 *)
intros st H. constructor. Qed.
这很显然,因为我们知道 hoare_triple 的定义断言了仅当
该命令停机时其后条件才成立。如果命令不停机,我们可以在后置条件中证明任何我们需要的东西。
我们一般把那些只讨论当命令最终停机(而不证明它们确实停机)的
证明规则叫做描述“部分(partial)”正确性的逻辑。我们也可以给出描述“完全(total)”正确性(也就是带有命令停机的假设)的证明规则。不过在这章里我们只介绍部分正确性。
练习:REPEAT
练习:4 星, advanced (hoare_repeat)
在这个练习中,我们会往 Imp 里面加一种新的命令:REPEAT c UNTIL b END。 请你写出 REPEAT 的求值规则,并且写一个关于它的霍尔逻辑证明规则。 (回想在 Auto 中给出的规则,试着自己把这个写出来,别偷看。)Module RepeatExercise.
Inductive com : Type :=
| CSkip : com
| CAsgn : string → aexp → com
| CSeq : com → com → com
| CIf : bexp → com → com → com
| CWhile : bexp → com → com
| CRepeat : com → bexp → com.
REPEAT 的行为类似于 WHILE,除了它的循环条件是在循环体结束
'后'检查的,并且只要循环条件是 false 循环就会被执行。因为
这样,循环体至少会被执行一次。
Notation "'SKIP'" :=
CSkip.
Notation "c1 ;; c2" :=
(CSeq c1 c2) (at level 80, right associativity).
Notation "X '::=' a" :=
(CAsgn X a) (at level 60).
Notation "'WHILE' b 'DO' c 'END'" :=
(CWhile b c) (at level 80, right associativity).
Notation "'TEST' e1 'THEN' e2 'ELSE' e3 'FI'" :=
(CIf e1 e2 e3) (at level 80, right associativity).
Notation "'REPEAT' e1 'UNTIL' b2 'END'" :=
(CRepeat e1 b2) (at level 80, right associativity).
在下面为 ceval 加入 REPEAT。你可以把 WHILE 的规则当作一个
模板,不过注意 REPEAT 的循环体至少要执行一次,并且循环会在条件
为真的时候结束。
Reserved Notation "st '=[' c ']=>' st'" (at level 40).
Inductive ceval : state → com → state → Prop :=
| E_Skip : ∀ st,
st =[ SKIP ]=> st
| E_Ass : ∀ st a1 n x,
aeval st a1 = n →
st =[ x ::= a1 ]=> (x !-> n ; st)
| E_Seq : ∀ c1 c2 st st' st'',
st =[ c1 ]=> st' →
st' =[ c2 ]=> st'' →
st =[ c1 ;; c2 ]=> st''
| E_IfTrue : ∀ st st' b c1 c2,
beval st b = true →
st =[ c1 ]=> st' →
st =[ TEST b THEN c1 ELSE c2 FI ]=> st'
| E_IfFalse : ∀ st st' b c1 c2,
beval st b = false →
st =[ c2 ]=> st' →
st =[ TEST b THEN c1 ELSE c2 FI ]=> st'
| E_WhileFalse : ∀ b st c,
beval st b = false →
st =[ WHILE b DO c END ]=> st
| E_WhileTrue : ∀ st st' st'' b c,
beval st b = true →
st =[ c ]=> st' →
st' =[ WHILE b DO c END ]=> st'' →
st =[ WHILE b DO c END ]=> st''
(* 请在此处解答 *)
where "st '=[' c ']=>' st'" := (ceval st c st').
下面是一些之前出现的定义,我们把它重新写一遍它就会用新的 ceval。
Definition hoare_triple (P : Assertion) (c : com) (Q : Assertion)
: Prop :=
∀ st st', st =[ c ]=> st' → P st → Q st'.
Notation "{{ P }} c {{ Q }}" :=
(hoare_triple P c Q) (at level 90, c at next level).
为了保证写出了正确的 REPEAT 计算规则,请你证明 ex1_repeat
能够正常计算。
Definition ex1_repeat :=
REPEAT
X ::= 1;;
Y ::= Y + 1
UNTIL X = 1 END.
Theorem ex1_repeat_works :
empty_st =[ ex1_repeat ]=> (Y !-> 1 ; X !-> 1).
Proof.
(* 请在此处解答 *) Admitted.
现在写出并证明一个定理 hoare_repeat 表达一个 repeat
命令的合理证明规则。你可以把 hoare_while 当作一个模板,
试着让你的规则尽可能地精确。
(* 请在此处解答 *)
要拿到全部的分数,请确保(非正式即可)你的规则可以用来证明以下
的霍尔三元组成立。
{{ X > 0 }}
REPEAT
Y ::= X;;
X ::= X - 1
UNTIL X = 0 END
{{ X = 0 ∧ Y > 0 }}
{{ X > 0 }}
REPEAT
Y ::= X;;
X ::= X - 1
UNTIL X = 0 END
{{ X = 0 ∧ Y > 0 }}
End RepeatExercise.
(* 请勿修改下面这一行: *)
Definition manual_grade_for_hoare_repeat : option (nat×string) := None.
☐
总结
(hoare_asgn) | |
{{Q [X ⊢> a]}} X::=a {{Q}} |
(hoare_skip) | |
{{ P }} SKIP {{ P }} |
{{ P }} c1 {{ Q }} | |
{{ Q }} c2 {{ R }} | (hoare_seq) |
{{ P }} c1;;c2 {{ R }} |
{{P /\ b}} c1 {{Q}} | |
{{P /\ ~ b}} c2 {{Q}} | (hoare_if) |
{{P}} TEST b THEN c1 ELSE c2 FI {{Q}} |
{{P /\ b}} c {{P}} | (hoare_while) |
{{P}} WHILE b DO c END {{P /\ ~ b}} |
{{P'}} c {{Q'}} | |
P ->> P' | |
Q' ->> Q | (hoare_consequence) |
{{P}} c {{Q}} |
附加练习
练习:3 星, standard (hoare_havoc)
在这个练习中我们将会为一种 HAVOC 命令实现证明规则,这个命令类似于 Imp 中的 any 表达式。Module Himp.
Inductive com : Type :=
| CSkip : com
| CAsgn : string → aexp → com
| CSeq : com → com → com
| CIf : bexp → com → com → com
| CWhile : bexp → com → com
| CHavoc : string → com.
Notation "'SKIP'" :=
CSkip.
Notation "X '::=' a" :=
(CAsgn X a) (at level 60).
Notation "c1 ;; c2" :=
(CSeq c1 c2) (at level 80, right associativity).
Notation "'WHILE' b 'DO' c 'END'" :=
(CWhile b c) (at level 80, right associativity).
Notation "'TEST' e1 'THEN' e2 'ELSE' e3 'FI'" :=
(CIf e1 e2 e3) (at level 80, right associativity).
Notation "'HAVOC' X" := (CHavoc X) (at level 60).
Reserved Notation "st '=[' c ']=>' st'" (at level 40).
Inductive ceval : com → state → state → Prop :=
| E_Skip : ∀ st,
st =[ SKIP ]=> st
| E_Ass : ∀ st a1 n x,
aeval st a1 = n →
st =[ x ::= a1 ]=> (x !-> n ; st)
| E_Seq : ∀ c1 c2 st st' st'',
st =[ c1 ]=> st' →
st' =[ c2 ]=> st'' →
st =[ c1 ;; c2 ]=> st''
| E_IfTrue : ∀ st st' b c1 c2,
beval st b = true →
st =[ c1 ]=> st' →
st =[ TEST b THEN c1 ELSE c2 FI ]=> st'
| E_IfFalse : ∀ st st' b c1 c2,
beval st b = false →
st =[ c2 ]=> st' →
st =[ TEST b THEN c1 ELSE c2 FI ]=> st'
| E_WhileFalse : ∀ b st c,
beval st b = false →
st =[ WHILE b DO c END ]=> st
| E_WhileTrue : ∀ st st' st'' b c,
beval st b = true →
st =[ c ]=> st' →
st' =[ WHILE b DO c END ]=> st'' →
st =[ WHILE b DO c END ]=> st''
| E_Havoc : ∀ st X n,
st =[ HAVOC X ]=> (X !-> n ; st)
where "st '=[' c ']=>' st'" := (ceval c st st').
霍尔三元组的定义和之前完全一致。
Definition hoare_triple (P:Assertion) (c:com) (Q:Assertion) : Prop :=
∀ st st', st =[ c ]=> st' → P st → Q st'.
Notation "{{ P }} c {{ Q }}" := (hoare_triple P c Q)
(at level 90, c at next level)
: hoare_spec_scope.
前置条件的缩放规则和之前完全一样。
Theorem hoare_consequence_pre : ∀ (P P' Q : Assertion) c,
{{P'}} c {{Q}} →
P ->> P' →
{{P}} c {{Q}}.
Proof.
intros P P' Q c Hhoare Himp.
intros st st' Hc HP. apply (Hhoare st st').
assumption. apply Himp. assumption. Qed.
请通过定义 havoc_pre 来创建一个关于 HAVOC 的证明规则,并
证明此规则是正确的。
Definition havoc_pre (X : string) (Q : Assertion) : Assertion
(* 将本行替换成 ":= _你的_定义_ ." *). Admitted.
Theorem hoare_havoc : ∀ (Q : Assertion) (X : string),
{{ havoc_pre X Q }} HAVOC X {{ Q }}.
Proof.
(* 请在此处解答 *) Admitted.
Complete the following proof without changing any of the provided
commands. If you find that it can't be completed, your definition of
havoc_pre is probably too strong. Find a way to relax it so that
havoc_post can be proved.
Theorem havoc_post : ∀ (P : Assertion) (X : string),
{{ P }} HAVOC X {{ fun st ⇒ ∃ (n:nat), P [X ⊢> n] st }}.
Proof.
intros P X. eapply hoare_consequence_pre.
- apply hoare_havoc.
- unfold assert_implies, assn_sub.
(* 请在此处解答 *) Admitted.
End Himp.
☐
在这个练习中我们会对 Imp 加入语句 ASSERT 和 ASSUME。这两个命令
都是用来指出某个布尔表达式应该在任何一次程序运行到这里的时候都为真。
但是它们有下列区别:
新的一系列命令是:
- 如果 ASSERT 失败了,程序就会进入错误状态并且退出。
- 如果 ASSUME 失败了,程序就不能运行。换句话说这段程序会卡住,没有 最终状态。
Inductive com : Type :=
| CSkip : com
| CAss : string → aexp → com
| CSeq : com → com → com
| CIf : bexp → com → com → com
| CWhile : bexp → com → com
| CAssert : bexp → com
| CAssume : bexp → com.
Notation "'SKIP'" :=
CSkip.
Notation "x '::=' a" :=
(CAss x a) (at level 60).
Notation "c1 ;; c2" :=
(CSeq c1 c2) (at level 80, right associativity).
Notation "'WHILE' b 'DO' c 'END'" :=
(CWhile b c) (at level 80, right associativity).
Notation "'TEST' c1 'THEN' c2 'ELSE' c3 'FI'" :=
(CIf c1 c2 c3) (at level 80, right associativity).
Notation "'ASSERT' b" :=
(CAssert b) (at level 60).
Notation "'ASSUME' b" :=
(CAssume b) (at level 60).
要定义 ASSERT 和 ASSUME 的行为,我们必须要加入一个表示错误
状态的记号,它指出某个 ASSERT 失败了。我们修改一下 ceval
规则,让它是开始状态和“结束状态或者是 error”的关系。result
类型是程序结束时的值,要么是 state 要么是 error。
现在我们可以给出新语言的 ceval 了。
Inductive ceval : com → state → result → Prop :=
(* 稍加修改的旧有规则 *)
| E_Skip : ∀ st,
st =[ SKIP ]=> RNormal st
| E_Ass : ∀ st a1 n x,
aeval st a1 = n →
st =[ x ::= a1 ]=> RNormal (x !-> n ; st)
| E_SeqNormal : ∀ c1 c2 st st' r,
st =[ c1 ]=> RNormal st' →
st' =[ c2 ]=> r →
st =[ c1 ;; c2 ]=> r
| E_SeqError : ∀ c1 c2 st,
st =[ c1 ]=> RError →
st =[ c1 ;; c2 ]=> RError
| E_IfTrue : ∀ st r b c1 c2,
beval st b = true →
st =[ c1 ]=> r →
st =[ TEST b THEN c1 ELSE c2 FI ]=> r
| E_IfFalse : ∀ st r b c1 c2,
beval st b = false →
st =[ c2 ]=> r →
st =[ TEST b THEN c1 ELSE c2 FI ]=> r
| E_WhileFalse : ∀ b st c,
beval st b = false →
st =[ WHILE b DO c END ]=> RNormal st
| E_WhileTrueNormal : ∀ st st' r b c,
beval st b = true →
st =[ c ]=> RNormal st' →
st' =[ WHILE b DO c END ]=> r →
st =[ WHILE b DO c END ]=> r
| E_WhileTrueError : ∀ st b c,
beval st b = true →
st =[ c ]=> RError →
st =[ WHILE b DO c END ]=> RError
(* Assert 和 Assume 的规则 *)
| E_AssertTrue : ∀ st b,
beval st b = true →
st =[ ASSERT b ]=> RNormal st
| E_AssertFalse : ∀ st b,
beval st b = false →
st =[ ASSERT b ]=> RError
| E_Assume : ∀ st b,
beval st b = true →
st =[ ASSUME b ]=> RNormal st
where "st '=[' c ']=>' r" := (ceval c st r).
我们重新定义霍尔三元组:现在,{{P}} c {{Q}} 的意思是,
当 c 在一个满足 P 的状态中启动并且停机在一个状态 r,那么 r
不是错误并且满足 Q。
Definition hoare_triple
(P : Assertion) (c : com) (Q : Assertion) : Prop :=
∀ st r,
st =[ c ]=> r → P st →
(∃ st', r = RNormal st' ∧ Q st').
Notation "{{ P }} c {{ Q }}" :=
(hoare_triple P c Q) (at level 90, c at next level)
: hoare_spec_scope.
为了测试你对这个修改的理解是否正确,请给出一组前置条件和后置条件,
它们可以被 ASSUME 语句导出却不能被 ASSERT 导出。然后证明任何关于
ASSERT 的三元组换成 ASSUME 也是正确的。
Theorem assert_assume_differ : ∃ (P:Assertion) b (Q:Assertion),
({{P}} ASSUME b {{Q}})
∧ ¬ ({{P}} ASSERT b {{Q}}).
(* 请在此处解答 *) Admitted.
Theorem assert_implies_assume : ∀ P b Q,
({{P}} ASSERT b {{Q}})
→ ({{P}} ASSUME b {{Q}}).
Proof.
(* 请在此处解答 *) Admitted.
你的任务是为 ASSERT 和 ASSUME 创建霍尔规则,并且用它们证明
一个小程序是正确的。把你的规则起名为 hoare_assert 和 hoare_assume。
为了你方便点,我们把新语义上的那些旧有的霍尔规则帮你证明好了。
Theorem hoare_asgn : ∀ Q X a,
{{Q [X ⊢> a]}} X ::= a {{Q}}.
Proof.
unfold hoare_triple.
intros Q X a st st' HE HQ.
inversion HE. subst.
∃ (X !-> aeval st a ; st). split; try reflexivity.
assumption. Qed.
Theorem hoare_consequence_pre : ∀ (P P' Q : Assertion) c,
{{P'}} c {{Q}} →
P ->> P' →
{{P}} c {{Q}}.
Proof.
intros P P' Q c Hhoare Himp.
intros st st' Hc HP. apply (Hhoare st st').
assumption. apply Himp. assumption. Qed.
Theorem hoare_consequence_post : ∀ (P Q Q' : Assertion) c,
{{P}} c {{Q'}} →
Q' ->> Q →
{{P}} c {{Q}}.
Proof.
intros P Q Q' c Hhoare Himp.
intros st r Hc HP.
unfold hoare_triple in Hhoare.
assert (∃ st', r = RNormal st' ∧ Q' st').
{ apply (Hhoare st); assumption. }
destruct H as [st' [Hr HQ']].
∃ st'. split; try assumption.
apply Himp. assumption.
Qed.
Theorem hoare_seq : ∀ P Q R c1 c2,
{{Q}} c2 {{R}} →
{{P}} c1 {{Q}} →
{{P}} c1;;c2 {{R}}.
Proof.
intros P Q R c1 c2 H1 H2 st r H12 Pre.
inversion H12; subst.
- eapply H1.
+ apply H6.
+ apply H2 in H3. apply H3 in Pre.
destruct Pre as [st'0 [Heq HQ]].
inversion Heq; subst. assumption.
- (* 找到矛盾的假设 *)
apply H2 in H5. apply H5 in Pre.
destruct Pre as [st' [C _]].
inversion C.
Qed.
把你提出的霍尔规则,hoare_assert 和 hoare_assume 写在下面。
(* 请在此处解答 *)
下列是其它证明规则(用来检查是否合理)
Theorem hoare_skip : ∀ P,
{{P}} SKIP {{P}}.
Proof.
intros P st st' H HP. inversion H. subst.
eexists. split. reflexivity. assumption.
Qed.
Theorem hoare_if : ∀ P Q (b:bexp) c1 c2,
{{ P ∧ b}} c1 {{Q}} →
{{ P ∧ ¬ b}} c2 {{Q}} →
{{P}} TEST b THEN c1 ELSE c2 FI {{Q}}.
Proof.
intros P Q b c1 c2 HTrue HFalse st st' HE HP.
inversion HE; subst.
- (* b 是 true *)
apply (HTrue st st').
assumption.
split. assumption.
apply bexp_eval_true. assumption.
- (* b 是 false *)
apply (HFalse st st').
assumption.
split. assumption.
apply bexp_eval_false. assumption. Qed.
Theorem hoare_while : ∀ P (b:bexp) c,
{{P ∧ b}} c {{P}} →
{{P}} WHILE b DO c END {{ P ∧ ¬b}}.
Proof.
intros P b c Hhoare st st' He HP.
remember (WHILE b DO c END) as wcom eqn:Heqwcom.
induction He;
try (inversion Heqwcom); subst; clear Heqwcom.
- (* E_WhileFalse *)
eexists. split. reflexivity. split.
assumption. apply bexp_eval_false. assumption.
- (* E_WhileTrueNormal *)
clear IHHe1.
apply IHHe2. reflexivity.
clear IHHe2 He2 r.
unfold hoare_triple in Hhoare.
apply Hhoare in He1.
+ destruct He1 as [st1 [Heq Hst1]].
inversion Heq; subst.
assumption.
+ split; assumption.
- (* E_WhileTrueError *)
exfalso. clear IHHe.
unfold hoare_triple in Hhoare.
apply Hhoare in He.
+ destruct He as [st' [C _]]. inversion C.
+ split; assumption.
Qed.
Example assert_assume_example:
{{True}}
ASSUME (X = 1);;
X ::= X + 1;;
ASSERT (X = 2)
{{True}}.
Proof.
(* 请在此处解答 *) Admitted.
End HoareAssertAssume.
☐
{{P}} SKIP {{P}}.
Proof.
intros P st st' H HP. inversion H. subst.
eexists. split. reflexivity. assumption.
Qed.
Theorem hoare_if : ∀ P Q (b:bexp) c1 c2,
{{ P ∧ b}} c1 {{Q}} →
{{ P ∧ ¬ b}} c2 {{Q}} →
{{P}} TEST b THEN c1 ELSE c2 FI {{Q}}.
Proof.
intros P Q b c1 c2 HTrue HFalse st st' HE HP.
inversion HE; subst.
- (* b 是 true *)
apply (HTrue st st').
assumption.
split. assumption.
apply bexp_eval_true. assumption.
- (* b 是 false *)
apply (HFalse st st').
assumption.
split. assumption.
apply bexp_eval_false. assumption. Qed.
Theorem hoare_while : ∀ P (b:bexp) c,
{{P ∧ b}} c {{P}} →
{{P}} WHILE b DO c END {{ P ∧ ¬b}}.
Proof.
intros P b c Hhoare st st' He HP.
remember (WHILE b DO c END) as wcom eqn:Heqwcom.
induction He;
try (inversion Heqwcom); subst; clear Heqwcom.
- (* E_WhileFalse *)
eexists. split. reflexivity. split.
assumption. apply bexp_eval_false. assumption.
- (* E_WhileTrueNormal *)
clear IHHe1.
apply IHHe2. reflexivity.
clear IHHe2 He2 r.
unfold hoare_triple in Hhoare.
apply Hhoare in He1.
+ destruct He1 as [st1 [Heq Hst1]].
inversion Heq; subst.
assumption.
+ split; assumption.
- (* E_WhileTrueError *)
exfalso. clear IHHe.
unfold hoare_triple in Hhoare.
apply Hhoare in He.
+ destruct He as [st' [C _]]. inversion C.
+ split; assumption.
Qed.
Example assert_assume_example:
{{True}}
ASSUME (X = 1);;
X ::= X + 1;;
ASSERT (X = 2)
{{True}}.
Proof.
(* 请在此处解答 *) Admitted.
End HoareAssertAssume.
☐
(* 2022-03-14 05:28:21 (UTC+00) *)