Lecture notes 20190329
Denotational Semantics 2
Remark. Some material in this lecture is from << Software Foundation >>
volume 1 and volume 2.
Require Import PL.Imp6.
Review: Programs' Denotational Semantics
Module Relation_Operators.
Definition id {A: Type}: A → A → Prop := fun a b ⇒ a = b.
Definition empty {A B: Type}: A → B → Prop := fun a b ⇒ False.
Definition concat {A B C: Type} (r1: A → B → Prop) (r2: B → C → Prop): A → C → Prop :=
fun a c ⇒ ∃b, r1 a b ∧ r2 b c.
Definition filter1 {A B: Type} (f: A → Prop): A → B → Prop :=
fun a b ⇒ f a.
Definition filter2 {A B: Type} (f: B → Prop): A → B → Prop :=
fun a b ⇒ f b.
Definition union {A B: Type} (r1 r2: A → B → Prop): A → B → Prop :=
fun a b ⇒ r1 a b ∨ r2 a b.
Definition intersection {A B: Type} (r1 r2: A → B → Prop): A → B → Prop :=
fun a b ⇒ r1 a b ∧ r2 a b.
Definition omega_union {A B: Type} (rs: nat → A → B → Prop): A → B → Prop :=
fun st1 st2 ⇒ ∃n, rs n st1 st2.
End Relation_Operators.
Import Relation_Operators.
Fixpoint aeval (a : aexp) (st : state) : Z :=
match a with
| ANum n ⇒ n
| AId X ⇒ st X
| APlus a1 a2 ⇒ (aeval a1 st) + (aeval a2 st)
| AMinus a1 a2 ⇒ (aeval a1 st) - (aeval a2 st)
| AMult a1 a2 ⇒ (aeval a1 st) * (aeval a2 st)
end.
Definition aexp_dequiv (d1 d2: state → Z): Prop :=
∀st, d1 st = d2 st.
Definition aexp_equiv (a1 a2: aexp): Prop :=
aexp_dequiv (aeval a1) (aeval a2).
Fixpoint beval (b : bexp) (st : state) : Prop :=
match b with
| BTrue ⇒ True
| BFalse ⇒ False
| BEq a1 a2 ⇒ (aeval a1 st) = (aeval a2 st)
| BLe a1 a2 ⇒ (aeval a1 st) ≤ (aeval a2 st)
| BNot b1 ⇒ ¬(beval b1 st)
| BAnd b1 b2 ⇒ (beval b1 st) ∧ (beval b2 st)
end.
Definition if_sem
(b: bexp)
(then_branch else_branch: state → state → Prop)
: state → state → Prop
:=
union
(intersection then_branch (filter1 (beval b)))
(intersection else_branch (filter1 (beval (BNot b)))).
Definition id {A: Type}: A → A → Prop := fun a b ⇒ a = b.
Definition empty {A B: Type}: A → B → Prop := fun a b ⇒ False.
Definition concat {A B C: Type} (r1: A → B → Prop) (r2: B → C → Prop): A → C → Prop :=
fun a c ⇒ ∃b, r1 a b ∧ r2 b c.
Definition filter1 {A B: Type} (f: A → Prop): A → B → Prop :=
fun a b ⇒ f a.
Definition filter2 {A B: Type} (f: B → Prop): A → B → Prop :=
fun a b ⇒ f b.
Definition union {A B: Type} (r1 r2: A → B → Prop): A → B → Prop :=
fun a b ⇒ r1 a b ∨ r2 a b.
Definition intersection {A B: Type} (r1 r2: A → B → Prop): A → B → Prop :=
fun a b ⇒ r1 a b ∧ r2 a b.
Definition omega_union {A B: Type} (rs: nat → A → B → Prop): A → B → Prop :=
fun st1 st2 ⇒ ∃n, rs n st1 st2.
End Relation_Operators.
Import Relation_Operators.
Fixpoint aeval (a : aexp) (st : state) : Z :=
match a with
| ANum n ⇒ n
| AId X ⇒ st X
| APlus a1 a2 ⇒ (aeval a1 st) + (aeval a2 st)
| AMinus a1 a2 ⇒ (aeval a1 st) - (aeval a2 st)
| AMult a1 a2 ⇒ (aeval a1 st) * (aeval a2 st)
end.
Definition aexp_dequiv (d1 d2: state → Z): Prop :=
∀st, d1 st = d2 st.
Definition aexp_equiv (a1 a2: aexp): Prop :=
aexp_dequiv (aeval a1) (aeval a2).
Fixpoint beval (b : bexp) (st : state) : Prop :=
match b with
| BTrue ⇒ True
| BFalse ⇒ False
| BEq a1 a2 ⇒ (aeval a1 st) = (aeval a2 st)
| BLe a1 a2 ⇒ (aeval a1 st) ≤ (aeval a2 st)
| BNot b1 ⇒ ¬(beval b1 st)
| BAnd b1 b2 ⇒ (beval b1 st) ∧ (beval b2 st)
end.
Definition if_sem
(b: bexp)
(then_branch else_branch: state → state → Prop)
: state → state → Prop
:=
union
(intersection then_branch (filter1 (beval b)))
(intersection else_branch (filter1 (beval (BNot b)))).
Soppose then_branch and else_branch are denotations of the then-branch
and else-branch of an if-command. Here, the first clause of union the set of
program state pairs (st1, st2) such that:
- (st1, st2) belongs to then_branch and b is true on st1
- (st1, st2) belongs to else_branch and b is false on st1
Fixpoint iter_loop_body
(b: bexp)
(loop_body: state → state → Prop)
(n: nat)
: state → state → Prop
:=
match n with
| O ⇒
intersection
id
(filter1 (beval (BNot b)))
| S n' ⇒
intersection
(concat
loop_body
(iter_loop_body b loop_body n'))
(filter1 (beval b))
end.
Definition loop_sem (b: bexp) (loop_body: state → state → Prop)
: state → state → Prop
:=
omega_union (iter_loop_body b loop_body).
(b: bexp)
(loop_body: state → state → Prop)
(n: nat)
: state → state → Prop
:=
match n with
| O ⇒
intersection
id
(filter1 (beval (BNot b)))
| S n' ⇒
intersection
(concat
loop_body
(iter_loop_body b loop_body n'))
(filter1 (beval b))
end.
Definition loop_sem (b: bexp) (loop_body: state → state → Prop)
: state → state → Prop
:=
omega_union (iter_loop_body b loop_body).
Suppose loop_body is the denotation of loop body, then
iter_loop_body b loop_body n
defines the begining-ending-program-state pairs of excuting the loop body
for exactly n times. And
loop_sem b loop_body
defines whole while-loop's semantics.
Fixpoint ceval (c: com): state → state → Prop :=
match c with
| CSkip ⇒ id
| CAss X E ⇒
fun st1 st2 ⇒
st2 X = aeval E st1 ∧
∀Y, X ≠ Y → st1 Y = st2 Y
| CSeq c1 c2 ⇒ concat (ceval c1) (ceval c2)
| CIf b c1 c2 ⇒ if_sem b (ceval c1) (ceval c2)
| CWhile b c ⇒ loop_sem b (ceval c)
end.
Definition com_dequiv (d1 d2: state → state → Prop): Prop :=
∀st1 st2, d1 st1 st2 ↔ d2 st1 st2.
Definition cequiv (c1 c2: com): Prop :=
com_dequiv (ceval c1) (ceval c2).
match c with
| CSkip ⇒ id
| CAss X E ⇒
fun st1 st2 ⇒
st2 X = aeval E st1 ∧
∀Y, X ≠ Y → st1 Y = st2 Y
| CSeq c1 c2 ⇒ concat (ceval c1) (ceval c2)
| CIf b c1 c2 ⇒ if_sem b (ceval c1) (ceval c2)
| CWhile b c ⇒ loop_sem b (ceval c)
end.
Definition com_dequiv (d1 d2: state → state → Prop): Prop :=
∀st1 st2, d1 st1 st2 ↔ d2 st1 st2.
Definition cequiv (c1 c2: com): Prop :=
com_dequiv (ceval c1) (ceval c2).
Adding all components up, we can define denotational semantics of progrmas
and program equivalence.
For now, we have successfully defined a fixpoint construction loop_sem
which satisfies the recursive equation loop_recur:
Bourbaki-Witt Theorem
Theorem loop_recur: ∀b loop_body,
com_dequiv
(loop_sem b loop_body)
(union
(intersection
(concat loop_body
(loop_sem b loop_body))
(filter1 (beval b)))
(intersection
id
(filter1 (beval (BNot b))))).
Proof.
com_dequiv
(loop_sem b loop_body)
(union
(intersection
(concat loop_body
(loop_sem b loop_body))
(filter1 (beval b)))
(intersection
id
(filter1 (beval (BNot b))))).
Proof.
intros.
unfold com_dequiv.
intros.
split.
+ intros.
unfold loop_sem, omega_union in H.
unfold union.
destruct H as [n H].
destruct n as [| n'].
- right.
simpl in H.
exact H.
- left.
simpl in H.
unfold concat, intersection in H.
unfold concat, intersection.
destruct H as [[st' [? ?]] ?].
split.
* ∃st'.
split.
{ exact H. }
unfold loop_sem, omega_union.
∃n'.
exact H0.
* exact H1.
+ intros.
unfold loop_sem, omega_union.
unfold union in H.
destruct H.
- unfold intersection, concat in H.
destruct H as [[st' [? ?]] ?].
unfold loop_sem, omega_union in H0.
destruct H0 as [n ?].
∃(S n).
simpl.
unfold intersection, concat.
split.
* ∃st'.
split.
{ exact H. }
{ exact H0. }
* exact H1.
- ∃O.
simpl.
exact H.
Qed.
unfold com_dequiv.
intros.
split.
+ intros.
unfold loop_sem, omega_union in H.
unfold union.
destruct H as [n H].
destruct n as [| n'].
- right.
simpl in H.
exact H.
- left.
simpl in H.
unfold concat, intersection in H.
unfold concat, intersection.
destruct H as [[st' [? ?]] ?].
split.
* ∃st'.
split.
{ exact H. }
unfold loop_sem, omega_union.
∃n'.
exact H0.
* exact H1.
+ intros.
unfold loop_sem, omega_union.
unfold union in H.
destruct H.
- unfold intersection, concat in H.
destruct H as [[st' [? ?]] ?].
unfold loop_sem, omega_union in H0.
destruct H0 as [n ?].
∃(S n).
simpl.
unfold intersection, concat.
split.
* ∃st'.
split.
{ exact H. }
{ exact H0. }
* exact H1.
- ∃O.
simpl.
exact H.
Qed.
This lemma that we proved last time is actually one special case of
Bourbaki-Witt fixpoint theorem.
A partial order (偏序) on a set A is a binary relation R (usually written
as ≤) which is reflexive (自反), transitive (传递), and antisymmetric (反对称).
Formally,
The least element of A w.r.t. a partial order ≤ is also called bottom:
A subset of elements in A is called a chain w.r.t. a partial order ≤
if any two elements in this subset are comparable. For example, if a sequence
xs: nat → A is monotonically increasing:
A partial order ≤ is called complete if every chain has its least upper
bound lub and greatest lower bound glb. In short, the set A (companied
with order ≤) is called a complete partial ordering, CPO (完备偏序集). Some text
books require chains to be nonempty. We do not put such restriction on chain's
definition here. Thus, the empty set is a chain. Its least upper bound is the
least element of A, in other words, bot.
Given two CPOs A, ≤A= and B, ≤B=, a function F: A → B is called
monotonic (单调) if it preserves order. Formally,
A function F: A → B is called continuous (连续) if it preserves lub.
Formally,
The definition of continuous does not require the preservation of glb becasue
CPOs are usually defined in a direction that larger elements are more
defined .
Given a CPO A, we can always construct a sequence of elements as follows:
Main theorem: given a CPO A, if it has a least element, then every monotonic
continuous function F has a fixpoint and the least fixpoint of F is:
Proof.
On one hand, this least upper bound is a fixpoint:
On the other hand, this fixpoint is the least one. For any other fixpoint x,
in other words, suppose F(x) = x. Then,
QED.
Our definition loop_sem is actually a Bourbaki-Witt fixpoint of the
recursive equation defined by loop_recur. In this case, set A is the set of
binary relations between program stats, i.e. A := state → state → Prop.
The equivalence relation defined on A is com_dequiv. The partial order
defined on A is the subset relation, i.e.
Moreoever, this partial ordering is a CPO. The least upper bound, lub, of a
chain is the union of all binary relations in the chain. Specifically,
omega_union defines the lub of a sequence of relations.
In the end, the function that maps d to
For examples of command equivalence, let's start by looking at some trivial
program transformations involving Skip:
Partial Order
∀x: A, x ≤ x;
∀x y z: A, x ≤ y → y ≤ z → x ≤ z;
∀x y: A, x ≤ y → y ≤ x → x = y.
∀x y z: A, x ≤ y → y ≤ z → x ≤ z;
∀x y: A, x ≤ y → y ≤ x → x = y.
∀x: A, bot ≤ x
Chain
∀n: nat, xs n ≤ xs (n + 1),
then it forms a chain.
Monotonic and Continuous Functions
∀x y: A, x ≤A= y → F(x) ≤B= F(y).
∀xs: chain(A), lub(F(xs)) = F(lub(xs))
Here, the lub function on the left hand side means the least upper bound
defined by B and the one on the right hand side is defined by A.
Least fixpoint
bot, F(bot), F(F(bot)), F(F(F(bot))), ...
Obviously, bot ≤ F(bot) is true due to the definition of bot. If F is
monotonic, it is immediately followed by F(bot) ≤ F(F(bot)). Similarly,
F(F(bot)) ≤ F(F(F(bot))), F(F(F(bot))) ≤ F(F(F(F(bot)))) ...
In other words, if F is monotonic, this sequence is a chain.
lub [bot, F(bot), F(F(bot)), F(F(F(bot))), ...].
F (lub [bot, F(bot), F(F(bot)), F(F(F(bot))), ...]) =
lub [F(bot), F(F(bot)), F(F(F(bot))), F(F(F(F(bot)))), ...] =
lub [bot, F(bot), F(F(bot)), F(F(F(bot))), ...].
The first equality is true because F is continuous. The second equality is
true because bot is less than or equal to all other elements in the sequence.
lub [F(bot), F(F(bot)), F(F(F(bot))), F(F(F(F(bot)))), ...] =
lub [bot, F(bot), F(F(bot)), F(F(F(bot))), ...].
bot ≤ x
Thus,
F(bot) ≤ F(x) = x
due to the fact that F is monotonic and x is a fixpoint. And so on,
F(F(bot)) ≤ x, F(F(F(bot))) ≤ x, F(F(F(F(bot)))) ≤ x, ...
That means, x is an upper bound of bot, F(bot), F(F(bot)), .... It must be
greater than or equal to
lub [bot, F(bot), F(F(bot)), F(F(F(bot))), ...].
Denotation of Loops as Bourbaki-Witt Fixpoint
"d1 ≤ d2" := ∀st1 st2, (d1 st1 st2) → (d2 st1 st2).
We can easily show that this binary relation is actually a partial order, i.e.,
it is reflexive, transitive and antisymmetric.
(union
(concat
(intersection loop_body
(filter1 (beval b)))
d)
(intersection
id
(filter1 (beval (BNot b)))))
is monotonic and continuous. And loop_sem is exactly the Bourbaki-Witt
fixpoint of this function.
(concat
(intersection loop_body
(filter1 (beval b)))
d)
(intersection
id
(filter1 (beval (BNot b)))))
Program Equivalence
Theorem skip_left : ∀c,
cequiv
(Skip;; c)
c.
Proof.
intros.
unfold cequiv, com_dequiv.
intros.
split; intros.
+ simpl in H.
unfold concat, id in H.
destruct H as [st' [? ?]].
rewrite H.
exact H0.
+ simpl.
unfold concat, id.
∃st1.
split.
- reflexivity.
- exact H.
Qed.
cequiv
(Skip;; c)
c.
Proof.
intros.
unfold cequiv, com_dequiv.
intros.
split; intros.
+ simpl in H.
unfold concat, id in H.
destruct H as [st' [? ?]].
rewrite H.
exact H0.
+ simpl.
unfold concat, id.
∃st1.
split.
- reflexivity.
- exact H.
Qed.
Also, we can prove that adding a Skip after a command results in an
equivalent program.
Theorem skip_right : ∀c,
cequiv
(c ;; Skip)
c.
Proof.
(* WORKED IN CLASS *)
intros.
unfold cequiv, com_dequiv.
intros.
split; intros.
+ simpl in H.
unfold concat, id in H.
destruct H as [st' [? ?]].
rewrite <- H0.
exact H.
+ simpl.
unfold concat, id.
∃st2.
split.
- exact H.
- reflexivity.
Qed.
cequiv
(c ;; Skip)
c.
Proof.
(* WORKED IN CLASS *)
intros.
unfold cequiv, com_dequiv.
intros.
split; intros.
+ simpl in H.
unfold concat, id in H.
destruct H as [st' [? ?]].
rewrite <- H0.
exact H.
+ simpl.
unfold concat, id.
∃st2.
split.
- exact H.
- reflexivity.
Qed.
Now we show that we can swap the branches of an IF if we also negate its
guard.
Theorem swap_if_branches : ∀b e1 e2,
cequiv
(If b Then e1 Else e2 EndIf)
(If (BNot b) Then e2 Else e1 EndIf).
Proof.
intros.
unfold cequiv, com_dequiv.
intros.
simpl.
unfold if_sem.
unfold union, intersection, filter1.
split; intros.
+ (* -> *)
destruct H as [[? ?] | [? ?]].
- (* b is true *)
right.
split.
* exact H.
* simpl.
cequiv
(If b Then e1 Else e2 EndIf)
(If (BNot b) Then e2 Else e1 EndIf).
Proof.
intros.
unfold cequiv, com_dequiv.
intros.
simpl.
unfold if_sem.
unfold union, intersection, filter1.
split; intros.
+ (* -> *)
destruct H as [[? ?] | [? ?]].
- (* b is true *)
right.
split.
* exact H.
* simpl.
The next line tauto reads "tautology" (重言式). This tactic can be
used to reason able normal logic connectives, including implication,
conjunction, disjuction, negation and logical equivalence.
tauto.
- (* b is false *)
left.
split.
* exact H.
* exact H0.
+ (* <- *)
destruct H as [[? ?] | [? ?]].
- (* b is false *)
right.
split.
* exact H.
* exact H0.
- (* b is true *)
left.
split.
* exact H.
* simpl in H0.
tauto.
Qed.
- (* b is false *)
left.
split.
* exact H.
* exact H0.
+ (* <- *)
destruct H as [[? ?] | [? ?]].
- (* b is false *)
right.
split.
* exact H.
* exact H0.
- (* b is true *)
left.
split.
* exact H.
* simpl in H0.
tauto.
Qed.
An interesting fact about While commands is that any number of copies of
the body can be "unrolled" without changing meaning. Loop unrolling is a common
transformation in real compilers.
Theorem loop_unrolling : ∀b c,
cequiv
(While b Do c EndWhile)
(If b Then (c ;; While b Do c EndWhile) Else Skip EndIf).
Proof.
intros.
unfold cequiv, com_dequiv.
intros.
simpl.
pose proof loop_recur b (ceval c).
unfold com_dequiv in H.
specialize (H st1 st2).
unfold if_sem.
exact H.
Qed.
cequiv
(While b Do c EndWhile)
(If b Then (c ;; While b Do c EndWhile) Else Skip EndIf).
Proof.
intros.
unfold cequiv, com_dequiv.
intros.
simpl.
pose proof loop_recur b (ceval c).
unfold com_dequiv in H.
specialize (H st1 st2).
unfold if_sem.
exact H.
Qed.
Usually, we do not distinguish (c1;;c2);;c3 with c1;;(c2;;c3) when we
write real programs. We do not have to because they have the same behavior.
Theorem seq_assoc : ∀c1 c2 c3,
cequiv ((c1;;c2);;c3) (c1;;(c2;;c3)).
Proof.
intros.
unfold cequiv, com_dequiv.
intros st1 st4.
simpl.
split; unfold concat; intros.
+ (* -> *)
destruct H as [st3 [H H34]].
destruct H as [st2 [H12 H23]].
∃st2.
split.
- exact H12.
- ∃st3.
split.
* exact H23.
* exact H34.
+ (* <- *)
destruct H as [st2 [H12 H]].
destruct H as [st3 [H23 H34]].
∃st3.
split.
- ∃st2.
split.
* exact H12.
* exact H23.
- exact H34.
Qed.
cequiv ((c1;;c2);;c3) (c1;;(c2;;c3)).
Proof.
intros.
unfold cequiv, com_dequiv.
intros st1 st4.
simpl.
split; unfold concat; intros.
+ (* -> *)
destruct H as [st3 [H H34]].
destruct H as [st2 [H12 H23]].
∃st2.
split.
- exact H12.
- ∃st3.
split.
* exact H23.
* exact H34.
+ (* <- *)
destruct H as [st2 [H12 H]].
destruct H as [st3 [H23 H34]].
∃st3.
split.
- ∃st2.
split.
* exact H12.
* exact H23.
- exact H34.
Qed.
The following theorem says, if two expressions are equivalent, then
assigning their value into the same variable has the same behavior.
Theorem CAss_congruence : ∀(X: var) (E E': aexp),
aexp_equiv E E' →
cequiv (CAss X E) (CAss X E').
Proof.
intros.
unfold cequiv, com_dequiv.
intros st1 st2.
simpl.
split; intros.
- (* -> *)
destruct H0.
split.
+ (* For X *)
unfold aexp_equiv, aexp_dequiv in H.
specialize (H st1).
rewrite <- H.
exact H0.
+ (* For other program variables *)
exact H1.
- (* <- *)
destruct H0.
split.
+ (* For X *)
unfold aexp_equiv, aexp_dequiv in H.
specialize (H st1).
rewrite H.
exact H0.
+ (* For other program variables *)
exact H1.
Qed.
aexp_equiv E E' →
cequiv (CAss X E) (CAss X E').
Proof.
intros.
unfold cequiv, com_dequiv.
intros st1 st2.
simpl.
split; intros.
- (* -> *)
destruct H0.
split.
+ (* For X *)
unfold aexp_equiv, aexp_dequiv in H.
specialize (H st1).
rewrite <- H.
exact H0.
+ (* For other program variables *)
exact H1.
- (* <- *)
destruct H0.
split.
+ (* For X *)
unfold aexp_equiv, aexp_dequiv in H.
specialize (H st1).
rewrite H.
exact H0.
+ (* For other program variables *)
exact H1.
Qed.
We will verify that the equivalence on coms really are equivalences
— i.e., that they are reflexive, symmetric, and transitive. The proofs are all
easy.
Lemma refl_com_dequiv : ∀(d : state → state → Prop),
com_dequiv d d.
Lemma refl_cequiv : ∀(c : com), cequiv c c.
Proof.
unfold cequiv.
intros.
apply refl_com_dequiv.
Qed.
Lemma sym_com_dequiv : ∀(d1 d2: state → state → Prop),
com_dequiv d1 d2 → com_dequiv d2 d1.
Lemma sym_cequiv : ∀(c1 c2 : com),
cequiv c1 c2 → cequiv c2 c1.
Proof.
unfold cequiv.
intros.
apply sym_com_dequiv.
exact H.
Qed.
Lemma trans_com_dequiv : ∀(d1 d2 d3 : state → state → Prop),
com_dequiv d1 d2 → com_dequiv d2 d3 → com_dequiv d1 d3.
Lemma trans_cequiv : ∀(c1 c2 c3 : com),
cequiv c1 c2 → cequiv c2 c3 → cequiv c1 c3.
Proof.
unfold cequiv.
intros.
pose proof trans_com_dequiv _ _ _ H H0.
exact H1.
Qed.
com_dequiv d d.
Proof.
unfold com_dequiv.
intros.
tauto.
Qed.
unfold com_dequiv.
intros.
tauto.
Qed.
Lemma refl_cequiv : ∀(c : com), cequiv c c.
Proof.
unfold cequiv.
intros.
apply refl_com_dequiv.
Qed.
Lemma sym_com_dequiv : ∀(d1 d2: state → state → Prop),
com_dequiv d1 d2 → com_dequiv d2 d1.
Proof.
unfold com_dequiv.
intros.
specialize (H st1 st2).
tauto.
Qed.
unfold com_dequiv.
intros.
specialize (H st1 st2).
tauto.
Qed.
Lemma sym_cequiv : ∀(c1 c2 : com),
cequiv c1 c2 → cequiv c2 c1.
Proof.
unfold cequiv.
intros.
apply sym_com_dequiv.
exact H.
Qed.
Lemma trans_com_dequiv : ∀(d1 d2 d3 : state → state → Prop),
com_dequiv d1 d2 → com_dequiv d2 d3 → com_dequiv d1 d3.
Proof.
unfold com_dequiv.
intros.
specialize (H st1 st2).
specialize (H0 st1 st2).
tauto.
Qed.
unfold com_dequiv.
intros.
specialize (H st1 st2).
specialize (H0 st1 st2).
tauto.
Qed.
Lemma trans_cequiv : ∀(c1 c2 c3 : com),
cequiv c1 c2 → cequiv c2 c3 → cequiv c1 c3.
Proof.
unfold cequiv.
intros.
pose proof trans_com_dequiv _ _ _ H H0.
exact H1.
Qed.
Behavioral equivalence is also a congruence. That is, the equivalence of
two subprograms implies the equivalence of the larger programs in which they
are embedded.
The main idea is that the congruence property allows us to replace a small part
of a large program with an equivalent small part and know that the whole large
programs are equivalent without doing an explicit proof about the non-varying
parts — i.e., the "proof burden" of a small change to a large program is
proportional to the size of the change, not the program.
We will prove the congruence property for loops in class. Other congruence
properties' proofs will be left as homework.
Theorem: Equivalence is a congruence for WHILE — that is, if c is
equivalent to c', then While b Do c EndWhile is equivalent to
While b Do c' EndWhile.
Theorem CWhile_congruence : ∀b c c',
cequiv c c' →
cequiv (While b Do c EndWhile) (While b Do c' EndWhile).
Proof.
unfold cequiv, com_dequiv.
intros.
simpl.
unfold loop_sem.
unfold omega_union.
cequiv c c' →
cequiv (While b Do c EndWhile) (While b Do c' EndWhile).
Proof.
unfold cequiv, com_dequiv.
intros.
simpl.
unfold loop_sem.
unfold omega_union.
Hmmm, it seems that we need an auxilliary lemma for iter_loop_body's
congruence.
Abort.
Lemma iter_loop_body_congruence: ∀b loop_body loop_body' n,
com_dequiv loop_body loop_body' →
com_dequiv (iter_loop_body b loop_body n) (iter_loop_body b loop_body' n).
Proof.
intros.
Lemma iter_loop_body_congruence: ∀b loop_body loop_body' n,
com_dequiv loop_body loop_body' →
com_dequiv (iter_loop_body b loop_body n) (iter_loop_body b loop_body' n).
Proof.
intros.
Here, we need to do induction over n.
induction n.
+ simpl.
apply refl_com_dequiv.
+ simpl.
+ simpl.
apply refl_com_dequiv.
+ simpl.
Hmm, it would be better if we have auxilliary lemmas for relation
operators.
Abort.
Here, we first prove that concat has congruence property.
Lemma concat_congruence: ∀(d1 d2 d1' d2': state → state → Prop),
com_dequiv d1 d1' →
com_dequiv d2 d2' →
com_dequiv (concat d1 d2) (concat d1' d2').
Proof.
unfold com_dequiv.
intros.
unfold concat.
split; intros H1; destruct H1 as [st [? ?]].
+ ∃st.
split.
- specialize (H st1 st).
tauto.
- specialize (H0 st st2).
tauto.
+ ∃st.
split.
- specialize (H st1 st).
tauto.
- specialize (H0 st st2).
tauto.
Qed.
com_dequiv d1 d1' →
com_dequiv d2 d2' →
com_dequiv (concat d1 d2) (concat d1' d2').
Proof.
unfold com_dequiv.
intros.
unfold concat.
split; intros H1; destruct H1 as [st [? ?]].
+ ∃st.
split.
- specialize (H st1 st).
tauto.
- specialize (H0 st st2).
tauto.
+ ∃st.
split.
- specialize (H st1 st).
tauto.
- specialize (H0 st st2).
tauto.
Qed.
Also, intersection has congruence property.
Lemma intersection_congruence: ∀(d1 d2 d1' d2': state → state → Prop),
com_dequiv d1 d1' →
com_dequiv d2 d2' →
com_dequiv (intersection d1 d2) (intersection d1' d2').
Proof.
unfold com_dequiv.
intros.
unfold intersection.
specialize (H st1 st2).
specialize (H0 st1 st2).
tauto.
Qed.
com_dequiv d1 d1' →
com_dequiv d2 d2' →
com_dequiv (intersection d1 d2) (intersection d1' d2').
Proof.
unfold com_dequiv.
intros.
unfold intersection.
specialize (H st1 st2).
specialize (H0 st1 st2).
tauto.
Qed.
Third, union has congruence property. Its proof is similar.
Lemma union_congruence: ∀(d1 d2 d1' d2': state → state → Prop),
com_dequiv d1 d1' →
com_dequiv d2 d2' →
com_dequiv (union d1 d2) (union d1' d2').
Proof.
unfold com_dequiv.
intros.
unfold union.
specialize (H st1 st2).
specialize (H0 st1 st2).
tauto.
Qed.
com_dequiv d1 d1' →
com_dequiv d2 d2' →
com_dequiv (union d1 d2) (union d1' d2').
Proof.
unfold com_dequiv.
intros.
unfold union.
specialize (H st1 st2).
specialize (H0 st1 st2).
tauto.
Qed.
In addition, we prove the congruence property of omega_union.
Lemma omega_union_congruence: ∀(ds1 ds2: nat → state → state → Prop),
(∀n, com_dequiv (ds1 n) (ds2 n)) →
com_dequiv (omega_union ds1) (omega_union ds2).
Proof.
unfold com_dequiv.
intros.
unfold omega_union.
split; intros H0; destruct H0 as [n ?]; ∃n.
+ specialize (H n st1 st2).
tauto.
+ specialize (H n st1 st2).
tauto.
Qed.
(∀n, com_dequiv (ds1 n) (ds2 n)) →
com_dequiv (omega_union ds1) (omega_union ds2).
Proof.
unfold com_dequiv.
intros.
unfold omega_union.
split; intros H0; destruct H0 as [n ?]; ∃n.
+ specialize (H n st1 st2).
tauto.
+ specialize (H n st1 st2).
tauto.
Qed.
Now, we are ready to prove the congruence property of iter_loop_body.
Lemma iter_loop_body_congruence: ∀b loop_body loop_body' n,
com_dequiv loop_body loop_body' →
com_dequiv (iter_loop_body b loop_body n) (iter_loop_body b loop_body' n).
Proof.
intros.
induction n.
+ simpl.
apply refl_com_dequiv.
+ simpl.
apply intersection_congruence.
- apply concat_congruence.
* exact H.
* exact IHn.
- apply refl_com_dequiv.
Qed.
com_dequiv loop_body loop_body' →
com_dequiv (iter_loop_body b loop_body n) (iter_loop_body b loop_body' n).
Proof.
intros.
induction n.
+ simpl.
apply refl_com_dequiv.
+ simpl.
apply intersection_congruence.
- apply concat_congruence.
* exact H.
* exact IHn.
- apply refl_com_dequiv.
Qed.
And eventually, we will prove our main theorem.
Theorem CWhile_congruence : ∀b c c',
cequiv c c' →
cequiv (While b Do c EndWhile) (While b Do c' EndWhile).
Proof.
unfold cequiv.
intros.
simpl.
unfold loop_sem.
apply omega_union_congruence.
intros.
apply iter_loop_body_congruence.
exact H.
Qed.
(* Wed Mar 27 17:33:26 UTC 2019 *)
cequiv c c' →
cequiv (While b Do c EndWhile) (While b Do c' EndWhile).
Proof.
unfold cequiv.
intros.
simpl.
unfold loop_sem.
apply omega_union_congruence.
intros.
apply iter_loop_body_congruence.
exact H.
Qed.
(* Wed Mar 27 17:33:26 UTC 2019 *)