(** Remark. Some material in this lecture is from << Software Foundation >>
volume 2. *)
Require Import PL.Imp.
Import Assertion_S.
Import Assertion_S_Tac.
Import Concrete_Pretty_Printing.
(** Last time, we discovered a common pattern in describing a strongest
postcondition of an assignment command. That is, given a precondition [P],
and an assignment command [X := E], their postcondition can be:
There exists an old value x of program variable
X, such that (1) the precondition P would hold if
the value of X would be x (2) the value of X is
result of evaluating the expression E if treating
all occurrences of X in E as x.
For example, the strongest postcondition of
{{ n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[X]} < n }}
Y ::= 0
{{ ??? }}
can be:
{{ EXISTS y, n * y + {[X]} = m AND
0 <= {[X]} AND {[X]} < n AND {[Y]} = 0 }}.
Today, we will describe that in logic formally.
*)
(* ################################################################# *)
(** * Symbolic substitution *)
(* ================================================================= *)
(** ** The assertion language *)
(** We have seen many assertions. They mainly follow the following syntax rules.
[P ::= t < t, t = t, t <= t, ...
| {[ B ]}
| P AND P
| P OR P
| NOT P
| EXISTS x, P
| FORALL x, P]
[t ::= 0, 1, -1, ...
| x
| {[ E ]}
| t + t
| t - t
| t * t
| ...]
where [x] represents logical variables, [E] represents integer program
expressions and [B] represents boolean integer program expressions. When we
define an assertion that
P would hold if the value of X would be x
or an expression that
replacing all occurrences of X in E with x
we can complete the construction according to [P]'s (or [E]'s) syntax.
*)
(* ================================================================= *)
(** ** Symbolic substitution in program expressions *)
(** Suppose [E] is an integer-type expression of the programming language,
E [X |-> E0]
represents the result of replacing every occurence of program variable [X] with
program expression [E0] in [E]. This result is still an integer-type
expression. *)
(** What is the result of the following symbolic substitution ([x] is some
constant)?
(X + Y) [ X |-> x ]
*)
(** [x + Y] *)
(** What is the result of the following symbolic substitution?
(X - Y) [ X |-> 1 ]
*)
(** [1 - Y] *)
(** What is the result of the following symbolic substitution?
(X + Y) [ X |-> X - Y ]
*)
(** [(X - Y) + Y] *)
(** What is the result of the following symbolic substitution?
(X * Y) [ X |-> 1 ]
*)
(** [1 * Y] *)
(** What is the result of the following symbolic substitution?
(X + 1) [ X |-> 1 ]
*)
(** [1 + 1] *)
(**
- [ (X * Y) [ X |-> 1] ] is [ 1 * Y ], not [Y].
- [ (X + 1) [ X |-> 1] ] is [ 1 + 1 ], not [2].
Remark. [1 * Y] and [Y] are two different program expressions although their
values are always the same.
*)
(** We can also apply symbolic substitution on a boolean expression.
Specifically, for a boolean-type expression [B] and an integer-type expression
[E0], we use
B [X |-> E0]
to represent the result of replacing every occurence of program variable [X]
with [E0] in [B], e.g.
(!(X <= Y))[X |-> 0]
is [ ! (0 <= Y) ].
*)
(* ================================================================= *)
(** ** Symbolic substitution in assertions *)
(** We can also define symbolic substitution in assertions. We use
P [X |-> E0]
to represent the result of replacing every occurence of program variable [X]
with program expression [E0] in assertion [P]. For example,
({[X + Y <= Z]} AND 0 <= {[X]} + {[Y]}) [X |-> X - Y]
({[X + Y <= Z]} AND 0 <= {[X]} + {[Y]}) [X |-> x]
({[X + Y <= Z]} AND 0 <= {[X]} + {[Y]}) [X |-> 0]
are
{[(X - Y) + Y <= Z]} AND 0 <= {[X - Y]} + {[Y]}
{[x + Y <= Z]} AND 0 <= {[x]} + {[Y]}.
{[0 + Y <= Z]} AND 0 <= {[0]} + {[Y]}.
In other words, the effect of symbolic substitution on an assertion [P] is
applying substitution on those program expressions mentioned in [P]. *)
(* ################################################################# *)
(** * Assignment rule (forward) *)
(** The following axiom describes the behavior of assignment commands. *)
Axiom hoare_asgn_fwd : forall P `(X: var) E,
{{ P }}
X ::= E
{{ EXISTS x, P [X |-> x] AND {[X]} = {[ E [X |-> x] ]} }}.
(** Write down the postcondition described by [hoare_asgn_fwd].
{{ {[X]} = x AND {[Y]} = y }}
TEMP ::= X;;
{{ ??? }}
*)
(** [ EXISTS x0, {[x0]} = x AND {[Y]} = y AND {[TEMP]} = {[x]} ] *)
(** Write down the postcondition described by [hoare_asgn_fwd].
{{ 0 <= {[Y]} }}
X ::= Y;;
{{ ??? }}
*)
(** [ EXISTS x0, 0 <= {[Y]} AND {[X]} = {[Y]} ] *)
(** Write down the postcondition described by [hoare_asgn_fwd].
{{ n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[n <= X]} }}
X ::= X - n
{{ ??? }}
*)
(** [ EXISTS x0, n * {[Y]} + {[x0]} = m AND 0 <= {[x0]} AND {[n <= x0]} AND {[X]} = {[x0 - n]} ] *)
(** Write down the postcondition described by [hoare_asgn_fwd].
{{ n * {[Y]} + {[X]} + n = m AND 0 <= {[X]} }}
Y ::= Y + 1
{{ ??? }}
*)
(** [ EXISTS y0, n * {[y0]} + {[X]} + n = m AND 0 <= {[X]} AND {[Y]} = {[y0 + 1]} ] *)
(** Write down the postcondition described by [hoare_asgn_fwd].
{{ {[X]} = x AND {[Y]} = y }}
X ::= X + Y;;
{{ ??? }}
*)
(** [ EXISTS x0, {[x0]} = x AND {[Y]} = y AND {[X]} = {[x0 + Y]} ] *)
(* ################################################################# *)
(** * Assignment rule (backward) *)
(** When C. A. R. Hoare first proposed Hoare logic for program correctness
proof in 1960s, the assignment rule is not in the forward direction; it was in
the backward direction. Robert W. Floyd proposed forward assignment rule later.
Due to this reason, people also use the nomenclature "Floyd-Hoare" logic
sometimes. (Another reason is that the original ideas of Hoare logic were seeded
by Floyd's work of a similar system for flowcharts.) *)
Axiom hoare_asgn_bwd : forall P `(X: var) E,
{{ P [ X |-> E] }} X ::= E {{ P }}.
(** Write down the precondition described by [hoare_asgn_bwd].
{{ ??? }}
X ::= X + Y;;
{{ {[X]} = x + y AND {[Y]} = y }}
*)
(** [ {[X + Y]} = x + y AND {[Y]} = y ] *)
(** Write down the precondition described by [hoare_asgn_bwd].
{{ ??? }}
Y ::= Y + 1
{{ n * {[Y]} + {[X]} = m AND 0 <= {[X]} }}
*)
(** [ n * {[Y + 1]} + {[X]} = m AND 0 <= {[X]} ] *)
(* ################################################################# *)
(** * Consequence rule *)
Axiom hoare_consequence : forall (P P' Q Q' : Assertion) c,
P |-- P' ->
{{P'}} c {{Q'}} ->
Q' |-- Q ->
{{P}} c {{Q}}.
(* ################################################################# *)
(** * Summary: axiomatic semantics *)
Module Axiomatic_semantics.
Axiom hoare_seq : forall (P Q R: Assertion) (c1 c2: com),
{{P}} c1 {{Q}} ->
{{Q}} c2 {{R}} ->
{{P}} c1;;c2 {{R}}.
Axiom hoare_skip : forall P,
{{P}} Skip {{P}}.
Axiom hoare_if : forall P Q b c1 c2,
{{ P AND {[b]} }} c1 {{ Q }} ->
{{ P AND NOT {[b]} }} c2 {{ Q }} ->
{{ P }} If b Then c1 Else c2 EndIf {{ Q }}.
Axiom hoare_while : forall P b c,
{{ P AND {[b]} }} c {{P}} ->
{{P}} While b Do c EndWhile {{ P AND NOT {[b]} }}.
Axiom hoare_asgn_fwd : forall P `(X: var) E,
{{ P }}
X ::= E
{{ EXISTS x, P [X |-> x] AND {[X]} = {[ E [X |-> x] ]} }}.
Axiom hoare_asgn_bwd : forall P `(X: var) E,
{{ P [ X |-> E] }} X ::= E {{ P }}.
Axiom hoare_consequence : forall (P P' Q Q' : Assertion) c,
P |-- P' ->
{{P'}} c {{Q'}} ->
Q' |-- Q ->
{{P}} c {{Q}}.
End Axiomatic_semantics.
Module div_mod_dec_again.
Import Axiomatic_semantics.
(** Coq proof time! Now, we are able to prove a nicer Hoare triple with simpler
hypothese. *)
Local Instance X: var := new_var().
Local Instance Y: var := new_var().
Hypothesis derivation1: forall m n: Z,
0 <= m AND {[X]} = m AND {[Y]} = 0 |--
n * {[Y]} + {[X]} = m AND 0 <= {[X]}.
Hypothesis derivation2: forall m n: Z,
EXISTS z, n * {[Y]} + z = m AND 0 <= z AND n <= z AND {[X]} = z - n |--
n * {[Y]} + {[X]} + n = m AND 0 <= {[X]}.
Hypothesis derivation3: forall m n: Z,
EXISTS z, n * z + {[X]} + n = m AND 0 <= {[X]} AND {[Y]} = z + 1 |--
n * {[Y]} + {[X]} = m AND 0 <= {[X]}.
Hypothesis derivation4: forall m n: Z,
n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND NOT {[n <= X]} |--
n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[X]} < n.
Fact div_mod_dec_correct: forall m n: Z,
{{ 0 <= m }}
X ::= m;;
Y ::= 0;;
While n <= X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[X]} < n }}.
Proof.
intros.
eapply hoare_seq.
{ apply hoare_asgn_fwd. }
assert_subst.
assert_simpl.
(** After eliminating the first assignment command using [hoare_seq] and
[hoare_asgn_fwd], we see the substitution symbol and the existential quantifier
in our precondition. Our library [PL.Imp] provided the two tactics to simplify
such assertions. *)
eapply hoare_seq.
{ apply hoare_asgn_fwd. }
assert_subst.
assert_simpl.
apply hoare_consequence with
(n * {[Y]} + {[X]} = m AND 0 <= {[X]})%assert
(n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND NOT {[ n <= X ]} )%assert.
1: { apply (derivation1 m n). }
2: { apply derivation4. }
apply hoare_while.
eapply hoare_seq.
{ apply hoare_asgn_fwd. }
assert_subst.
assert_simpl.
eapply hoare_consequence.
+ apply derivation2.
+ apply hoare_asgn_fwd.
+ assert_subst.
assert_simpl.
apply derivation3.
Qed.
(** Now we try to prove the same Hoare triple again with forward proof style. *)
Fact div_mod_dec_correct_again: forall m n: Z,
{{ 0 <= m }}
X ::= m;;
Y ::= 0;;
While n <= X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[X]} < n }}.
Proof.
intros.
pose proof hoare_asgn_fwd
(n * {[Y]} + {[X]} = m AND
0 <= {[X]} AND {[n <= X]})%assert
X (X - n).
(** These two tactics [assert_subst] and [assert_simpl] can also simplify
assertions in assumptions *)
assert_subst in H.
assert_simpl in H.
pose proof hoare_asgn_fwd
(n * {[Y]} + {[X]} + n = m AND 0 <= {[X]})%assert
Y (Y + 1).
assert_subst in H0.
assert_simpl in H0.
pose proof hoare_consequence _ _ _ _ _
(derivation2 m n) H0 (derivation3 m n).
pose proof hoare_seq _ _ _ _ _ H H1.
clear H H0 H1.
(** It is unfortunate that we cannot achieve a Hoare triple for the while loop
here using [H2] and [hoare_while] because the assertion that loop condition is
true
{[n <= X]}
has been simplified into [ n <= {[X]} ]. Thus, we [assert] the triple that we
want to prove. *)
assert ({{n * {[Y]} + {[X]} = m AND 0 <= {[X]}}} While n <= X Do (X ::= X - n);; Y ::= Y + 1 EndWhile {{n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND NOT {[n <= X]}}}).
(** After [assert], we first prove the asserted triple. *)
{
apply hoare_while.
assert_simpl.
exact H2.
}
clear H2.
(** We then prove the original proof goal with an extra assumption---the
asserted triple [H]. *)
pose proof hoare_asgn_fwd
(0 <= m)%assert
X
m.
assert_subst in H0.
assert_simpl in H0.
pose proof hoare_asgn_fwd
(0 <= m AND {[X]} = m)%assert
Y
0.
assert_subst in H1.
assert_simpl in H1.
pose proof hoare_consequence _ _ _ _ _
(derivation1 m n) H (derivation4 m n).
pose proof hoare_seq _ _ _ _ _ H1 H2.
pose proof hoare_seq _ _ _ _ _ H0 H3.
exact H4.
Qed.
End div_mod_dec_again.
(* ################################################################# *)
(** * Derived proof rules and programmable proof tactics *)
Module derived_rules.
Import Assertion_S_Rules.
Import Axiomatic_semantics.
(** We know that an assertion can always derive itself. This property is called
[derives_refl] in our [Imp] library. *)
Check derives_refl.
(* : forall P : Assertion, P |-- P *)
(** Using it, we can derive two specialized consequence rule. The first one is a
single-sided consequence rule for preconditions. *)
Corollary hoare_consequence_pre: forall P P' Q c,
P |-- P' ->
{{ P' }} c {{ Q }} ->
{{ P }} c {{ Q }}.
Proof.
intros.
eapply hoare_consequence.
+ exact H.
+ exact H0.
+ apply derives_refl.
Qed.
(** Similarly, we have a single-sided consequence rule for postconditions
as well. *)
Corollary hoare_consequence_post: forall P Q Q' c,
{{ P }} c {{ Q' }} ->
Q' |-- Q ->
{{ P }} c {{ Q }}.
Proof.
intros.
eapply hoare_consequence.
+ apply derives_refl.
+ exact H.
+ exact H0.
Qed.
(** Since we prove these two rules from primary rules, we call them _derived
rules_ (导出规则). There are other derived proof rules. They can be useful for
some special situations. The following rule is a weaker version of [hoare_if]
(we have shown why it is weak in our previous lectures). We can derive it from
our primary [hoare_if] rule now. But this proof is based on the following
properties of [AND]. *)
Check AND_left1.
(* : forall P Q R : Assertion,
P |-- R -> P AND Q |-- R *)
Check AND_left2.
(* : forall P Q R : Assertion,
Q |-- R -> P AND Q |-- R *)
Corollary hoare_if_weak : forall P Q b c1 c2,
{{P}} c1 {{Q}} ->
{{P}} c2 {{Q}} ->
{{P}} If b Then c1 Else c2 EndIf {{Q}}.
Proof.
intros.
apply hoare_if.
+ eapply hoare_consequence_pre.
2: { exact H. }
apply AND_left1.
apply derives_refl.
+ eapply hoare_consequence_pre.
2: { exact H0. }
apply AND_left1.
apply derives_refl.
Qed.
(** Also, we have seen a lot of examples, in which we use [hoare_seq] and
[hoare_asgn_fwd] together when the first command is an assignment. We can pack
them together: *)
Corollary hoare_asgn_seq: forall P `(X: var) E c Q,
{{ EXISTS x, P [X |-> x] AND {[X]} = {[ E [X |-> x] ]} }} c {{ Q }} ->
{{ P }} X ::= E ;; c {{ Q }}.
Proof.
intros.
eapply hoare_seq.
+ apply hoare_asgn_fwd.
+ exact H.
Qed.
(** What if the assignment is the only command? We have also seen a combination
of [hoare_asgn_fwd] and [hoare_consequence]. *)
Corollary hoare_asgn_conseq: forall P `(X: var) E Q,
EXISTS x, P [X |-> x] AND {[X]} = {[ E [X |-> x] ]} |-- Q ->
{{ P }} X ::= E {{ Q }}.
Proof.
intros.
eapply hoare_consequence_post.
+ apply hoare_asgn_fwd.
+ exact H.
Qed.
(** These derived rule can make Coq formalized proofs shorter; and more
importantly, they help to describe how we understand Hoare logic proofs. We do
not apply proof rules one by one in our mind, we use proof rule "combos"
instead. Now, let's redo our proofs about swapping. *)
Local Instance X: var := new_var().
Local Instance Y: var := new_var().
Local Instance TEMP: var := new_var().
Hypothesis der1: forall x y,
EXISTS x0, x0 = x AND {[Y]} = y AND {[TEMP]} = x0 AND {[X]} = {[Y]} |--
{[X]} = y AND {[TEMP]} = x.
Hypothesis der2: forall x y,
{[X]} = y AND {[TEMP]} = x AND {[Y]} = {[TEMP]} |--
{[X]} = y AND {[Y]} = x.
Fact swaping_correct:
forall x y: Z,
{{ {[X]} = x AND {[Y]} = y }}
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ {[X]} = y AND {[Y]} = x }}.
Proof.
(* WORKED IN CLASS *)
intros.
apply hoare_asgn_seq.
assert_subst.
assert_simpl.
apply hoare_asgn_seq.
assert_subst.
assert_simpl.
eapply hoare_consequence_pre.
{ apply der1. }
apply hoare_asgn_conseq.
assert_subst.
assert_simpl.
apply der2.
Qed.
End derived_rules.
(* ################################################################# *)
(** * Decorated program as informal Hoare logic proof *)
(** The beauty of Hoare Logic is that it is _compositional_ （可组合的）: the
structure of proofs exactly follows the structure of programs. This suggests
that we can record the essential ideas of a proof by "decorating" a program with
appropriate assertions on each of its commands. *)
(** In <>, such informal proofs are called _decorated
programs_. In other text books or programming language literature, they are
also called _annotated programs_ or _commented programs_ (带注释程序）. *)
(** Here are two sample decorated programs. *)
(** Sample 1
/* 0 <= m */
X ::= m;;
Y ::= 0;;
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} */
While n <= X Do
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[n <= X]} */
X ::= X - n;;
Y ::= Y + 1
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} */
EndWhile
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND NOT {[n <= X]} */
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[X]} < n */.
*)
(** Sample 2
/* 0 <= m */
X ::= m;;
/* EXISTS x, 0 <= m AND {[X]} = m */
/* 0 <= m AND {[X]} = m */
Y ::= 0;;
/* EXISTS y, 0 <= m AND {[X]} = m AND {[Y]} = 0 */
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} */
While n <= X Do
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[n <= X]} */
X ::= X - n;;
/* EXISTS x, n * {[Y]} + {[x]} = m AND
0 <= {[x]} AND {[n <= x]} AND {[X]} = {[x - n]} */
/* EXISTS x, n * {[Y]} + x = m AND
0 <= x AND n <= x AND {[X]} = x - n */
/* n * {[Y]} + {[X]} + n = m AND 0 <= {[X]} */
Y ::= Y + 1
/* EXISTS y, n * {[y]} + {[X]} + n = m AND
0 <= {[X]} AND {[Y]} = {[y + 1]} */
/* EXISTS y, n * y + {[X]} + n = m AND
0 <= {[X]} AND {[Y]} = y + 1 */
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} */
EndWhile
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND NOT {[n <= X]} */
/* n * {[Y]} + {[X]} = m AND 0 <= {[X]} AND {[X]} < n */.
*)
(* ################################################################# *)
(** * Exercises *)
(** **** Exercise: 3 stars, standard (remainder_only) *)
Module remainder_only.
Import Axiomatic_semantics.
Local Instance X: var := new_var().
(** In the this task, you can write hypothese about assignment commands without
proving them. *)
(* FILL IN HERE *)
Fact remainder_only_correct: forall m n: Z,
{{ 0 <= m }}
X ::= m;;
While n <= X Do
X ::= X - n
EndWhile
{{ (EXISTS q, n * q + {[X]} = m) AND 0 <= {[X]} AND NOT {[n <= X]} }}.
Proof.
intros.
(* FILL IN HERE *) Admitted.
(** [] *)
End remainder_only.
(** **** Exercise: 2 stars, standard (reduce_to_zero_alter1)
You are only allowed write assertion derivations as hypotheses in this
exercise. *)
Module reduce_to_zero_alter1.
Import Axiomatic_semantics.
Import derived_rules.
Local Instance X: var := new_var().
(** Hint: the following decorated program shows a proof skeleton.
/* True */
While !(X <= 0) Do
/* True AND {[ !(X <= 0) ]} */
/* True */
X ::= X - 1
/* EXISTS x, True AND {[X]} = {[x - 1]} */
/* True */
EndWhile
/* True AND NOT {[ !(X <= 0) ]} */
/* {[X]} <= 0 */.
*)
(* FILL IN HERE *)
Fact reduce_to_zero_correct:
{{ True }}
While !(X <= 0) Do
X ::= X - 1
EndWhile
{{ {[X]} <= 0 }}.
Proof.
(* FILL IN HERE *) Admitted.
(** [] *)
End reduce_to_zero_alter1.
(** **** Exercise: 3 stars, standard (reduce_to_zero_alter2)
You are only allowed write assertion derivations as hypotheses in this
exercise. *)
Module reduce_to_zero_alter2.
Import Axiomatic_semantics.
Import derived_rules.
Local Instance X: var := new_var().
(* FILL IN HERE *)
Fact reduce_to_zero_correct:
{{ 0 <= {[X]} }}
While !(X <= 0) Do
X ::= X - 1
EndWhile
{{ {[X]} = 0 }}.
Proof.
(* FILL IN HERE *) Admitted.
(** [] *)
End reduce_to_zero_alter2.
(* 2021-03-07 20:15 *)