Lecture notes 20190305
Hoare Logic 2
Remark. Some material in this lecture is from << Software Foundation >>
volume 2.
Like our last lecture, we need to import Imp1 first.
Require Import PL.Imp1.
Axiom hoare_if_first_try : ∀P Q b c1 c2,
{{P}} c1 {{Q}} →
{{P}} c2 {{Q}} →
{{P}} If b Then c1 Else c2 EndIf {{Q}}.
Module Playground_for_Counterexample.
Local Instance X: var := new_var().
Local Instance Y: var := new_var().
Definition sample_triple :=
{{ True }}
If X == 0
Then Y ::= 2
Else Y ::= X + 1
EndIf
{{ [[X]] ≤ [[Y]] }}.
End Playground_for_Counterexample.
- As a rule, hoare_if_first_try is NOT wrong.
- As a Hoare triple, sample_triple is TRUE.
- But hoare_if_first_try is NOT STRONG ENOUGH to prove sample_triple.
Who ensures that a program behaves as the program semantics decribes?
Why is hoare_if strong enough?
{{P}} If b Then c1 Else c2 EndIf {{Q}} is true,
then we can prove it by hoare_if. The problem is that the highlighted sentence
above is not a formal proposition, since we have no way for now to describe
program semantics other than using Hoare triples. That is, we can only use one
example after another to (informally) demonstrate the proof power of hoare_if.
Axiomatic semantics for loops
Axiom hoare_while : ∀P b c,
{{ P AND [[b]] }} c {{P}} →
{{P}} While b Do c EndWhile {{ P AND NOT [[b]] }}.
{{ P AND [[b]] }} c {{P}} →
{{P}} While b Do c EndWhile {{ P AND NOT [[b]] }}.
Here, the proposition P is called a loop invariant (循环不变量).
So far, we've introduced four axioms for four program constructor. They are:
From axiomatic semantics to program correctness proof
Module Axiomatic_semantics.
Axiom hoare_seq : ∀(P Q R: Assertion) (c1 c2: com),
{{P}} c1 {{Q}} →
{{Q}} c2 {{R}} →
{{P}} c1;;c2 {{R}}.
Axiom hoare_skip : ∀P,
{{P}} Skip {{P}}.
Axiom hoare_if : ∀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 : ∀P b c,
{{ P AND [[b]] }} c {{P}} →
{{P}} While b Do c EndWhile {{ P AND NOT [[b]] }}.
End Axiomatic_semantics.
Axiom hoare_seq : ∀(P Q R: Assertion) (c1 c2: com),
{{P}} c1 {{Q}} →
{{Q}} c2 {{R}} →
{{P}} c1;;c2 {{R}}.
Axiom hoare_skip : ∀P,
{{P}} Skip {{P}}.
Axiom hoare_if : ∀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 : ∀P b c,
{{ P AND [[b]] }} c {{P}} →
{{P}} While b Do c EndWhile {{ P AND NOT [[b]] }}.
End Axiomatic_semantics.
Because we use these axioms about Hoare triples to define program semantics,
such definition is called an axiomatic semantics. And we say that these axioms
form a proof system, or a Hoare logic.
Now, we are going to use Hoare logic to prove programs correct. In other
words, we will use these axioms to prove that a specific program satisfies a
specific program specification defined by pre/postconditions.
We want to prove that the following program always swaps the values of
variables X and Y. Or, formally, for any x and y,
Here is a program that swaps the values of two variables using addition and
subtraction instead of by assigning to a temporary variable.
Here's a simple program using conditionals, with a possible specification:
We can prove it by hoare_while and the following triple:
For fixed positive integer m and n, the following program calculates the
integer quotient and remainder.
In the end, hoare_seq and triple 1, 2, 5 tells us:
The following program calculates the remainder only.
We need to use existential quantifiers for help. We may write:
Prove that the following program calculates the subtraction.
Solution 1. Using invariant
The following program squares X by repeated addition. Prove its
correctness.
Solution. Using invariant
The following program computes the (integer) square root of X
by naive iteration. Prove its correctness.
Solution. Using invariant
For now, we have learned how to prove a program correct by Hoare logic.
We are going to formalize these proofs in the rest part of this lecture.
Example: Swapping
{{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
It is due to the following three triples and hoare_seq.
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
1. {{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X
{{ [[Y]] == y AND [[TEMP]] == x }}
2. {{ [[Y]] == y AND [[TEMP]] == x }}
X ::= Y
{{ [[X]] == y AND [[TEMP]] == x }}
3. {{ [[X]] == y AND [[TEMP]] == x }}
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
TEMP ::= X
{{ [[Y]] == y AND [[TEMP]] == x }}
2. {{ [[Y]] == y AND [[TEMP]] == x }}
X ::= Y
{{ [[X]] == y AND [[TEMP]] == x }}
3. {{ [[X]] == y AND [[TEMP]] == x }}
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
Example: Swapping Using Addition and Subtraction
X ::= X + Y;;
Y ::= X - Y;;
X ::= X - Y
Again, we can prove it correct by three triples for assignments and hoare_seq.
Y ::= X - Y;;
X ::= X - Y
1. {{ [[X]] == x AND [[Y]] == y }}
X ::= X + Y
{{ [[X]] == x + y AND [[Y]] == y }}
2. {{ [[X]] == x + y AND [[Y]] == y }}
Y ::= X - Y
{{ [[X]] == x + y AND [[Y]] == x }}
3. {{ [[X]] == x + y AND [[Y]] == x }}
X ::= X - Y
{{ [[X]] == y AND [[Y]] == x }}.
X ::= X + Y
{{ [[X]] == x + y AND [[Y]] == y }}
2. {{ [[X]] == x + y AND [[Y]] == y }}
Y ::= X - Y
{{ [[X]] == x + y AND [[Y]] == x }}
3. {{ [[X]] == x + y AND [[Y]] == x }}
X ::= X - Y
{{ [[X]] == y AND [[Y]] == x }}.
Example: Simple Conditionals
{{ True }}
If A ≤ B
Then C ::= B - A
Else C ::= A - B
EndIf
{{ 0 ≤ [[C]] AND ( [[C]] + [[A]] == [[B]] OR [[C]] + [[B]] == [[A]] ) }}.
We can prove it by hoare_if and the following two facts:
If A ≤ B
Then C ::= B - A
Else C ::= A - B
EndIf
{{ 0 ≤ [[C]] AND ( [[C]] + [[A]] == [[B]] OR [[C]] + [[B]] == [[A]] ) }}.
1. {{ True AND [[A ≤ B]] }}
C ::= B - A
{{ 0 ≤ [[C]] AND ( [[C]] + [[A]] == [[B]] OR [[C]] + [[B]] == [[A]] ) }}.
2. {{ True AND NOT [[A ≤ B]] }}
C ::= A - B
{{ 0 ≤ [[C]] AND ( [[C]] + [[A]] == [[B]] OR [[C]] + [[B]] == [[A]] ) }}.
C ::= B - A
{{ 0 ≤ [[C]] AND ( [[C]] + [[A]] == [[B]] OR [[C]] + [[B]] == [[A]] ) }}.
2. {{ True AND NOT [[A ≤ B]] }}
C ::= A - B
{{ 0 ≤ [[C]] AND ( [[C]] + [[A]] == [[B]] OR [[C]] + [[B]] == [[A]] ) }}.
Example: Reduce to Zero
{{ True AND [[!(X == 0)]] }}
X ::= X - 1
{{ True }}.
X ::= X - 1
{{ True }}.
Example: Division
X ::= m;;
Y ::= 0;;
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
This program will will always terminate with the variable X set to the
remainder when m is divided by n and Y set to the quotient. We may write
a specification as follows:
Y ::= 0;;
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ 0 ≤ m AND 0 < n }}
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 }}.
Considering that the loop condition is n ≤ X, we want to modify this
specification a little bit:
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 }}.
{{ 0 ≤ m AND 0 < n }}
X ::= m;;
Y ::= 0;;
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
Now, we can prove it using the following loop invariant:
X ::= m;;
Y ::= 0;;
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
n * [[Y]] + [[X]] == m AND 0 ≤ [[X]].
On one hand, we have:
1. {{ 0 ≤ m AND 0 < n }}
X ::= m
{{ [[X]] == m AND 0 ≤ [[X]] }};
2. {{ [[X]] == m AND 0 ≤ [[X]] }}
Y ::= 0
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] }}.
On the other hand, we have
X ::= m
{{ [[X]] == m AND 0 ≤ [[X]] }};
2. {{ [[X]] == m AND 0 ≤ [[X]] }}
Y ::= 0
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] }}.
3. {{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] AND [[n ≤ X]] }}
X ::= X - n
{{ n * [[Y]] + [[X]] + n == m AND 0 ≤ [[X]] }};
4. {{ n * [[Y]] + [[X]] + n == m AND 0 ≤ [[X]] }}
Y ::= Y + 1
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] }}.
Using hoare_seq, hoare_while and triple 3, 4, we know that:
X ::= X - n
{{ n * [[Y]] + [[X]] + n == m AND 0 ≤ [[X]] }};
4. {{ n * [[Y]] + [[X]] + n == m AND 0 ≤ [[X]] }}
Y ::= Y + 1
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] }}.
5. {{ 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]] }}.
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
{{ 0 ≤ m AND 0 < n }}
X ::= m;;
Y ::= 0;;
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
BTW, since 0 < n is totally not used in the proof, you may remove it from the
precondition. This make sense because this program will not terminate if n is
not positive.
X ::= m;;
Y ::= 0;;
While n ≤ X Do
X ::= X - n;;
Y ::= Y + 1
EndWhile
{{ n * [[Y]] + [[X]] == m AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
Example: Remainder Only
X ::= m;;
While n ≤ X Do
X ::= X - n
EndWhile
While n ≤ X Do
X ::= X - n
EndWhile
{{ 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]] }}.
And our proof is similar. First,
X ::= m;;
While n ≤ X Do
X ::= X - n
EndWhile
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
1. {{ 0 ≤ m }}
X ::= m
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] }}.
Second,
X ::= m
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] }}.
2. {{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] AND [[n ≤ X]] }}
X ::= X - n
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] }}.
By hoare_while and this triple above, we know that
X ::= X - n
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] }}.
3. {{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] }}
While n ≤ X Do
X ::= X - n
EndWhile
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
Combining triple 1 and 3 together by hoare_seq proves our original goal.
While n ≤ X Do
X ::= X - n
EndWhile
{{ (EXISTS q, n * q + [[X]] == m) AND 0 ≤ [[X]] AND NOT [[n ≤ X]] }}.
Example: Slow subtraction
X ::= m;;
Y ::= p;;
While !(X == 0) Do
Y ::= Y - 1;;
X ::= X - 1
EndWhile
Y ::= p;;
While !(X == 0) Do
Y ::= Y - 1;;
X ::= X - 1
EndWhile
[[Y]] - [[X]] == p - m.
Solution 2. Using invariant
EXISTS k, [[Y]] + k == p AND [[X]] + k == m.
Example: Squaring
I ::= 0;;
RES ::= 0;;
While !(I == X) Do
RES ::= RES + X;;
I ::= I + 1
EndWhile
RES ::= 0;;
While !(I == X) Do
RES ::= RES + X;;
I ::= I + 1
EndWhile
[[RES]] == [[I]] * m AND [[X]] == m.
Example: Finding Square Roots
I ::= 0;;
While (I+1)*(I+1) ≤ X Do
I ::= I+1
EndWhile
While (I+1)*(I+1) ≤ X Do
I ::= I+1
EndWhile
[[I]] * [[I]] ≤ m AND [[X]] == m.
Program correctness proof in Coq
Example: Swapping
Module swapping.
Import Axiomatic_semantics.
Local Instance X: var := new_var().
Local Instance Y: var := new_var().
Local Instance TEMP: var := new_var().
We are going to prove:
{{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
This is based on 3 Hoare triples about assignment commands. In fact, we have not
proved them yet in any precise way. They are just true by own intuition. Thus we
wrote them down as hypothesis here.
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
Hypothesis triple1: ∀x y: Z,
{{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X
{{ [[Y]] == y AND [[TEMP]] == x }}.
Hypothesis triple2: ∀x y: Z,
{{ [[Y]] == y AND [[TEMP]] == x }}
X ::= Y
{{ [[X]] == y AND [[TEMP]] == x }}.
Hypothesis triple3: ∀x y: Z,
{{ [[X]] == y AND [[TEMP]] == x }}
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
{{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X
{{ [[Y]] == y AND [[TEMP]] == x }}.
Hypothesis triple2: ∀x y: Z,
{{ [[Y]] == y AND [[TEMP]] == x }}
X ::= Y
{{ [[X]] == y AND [[TEMP]] == x }}.
Hypothesis triple3: ∀x y: Z,
{{ [[X]] == y AND [[TEMP]] == x }}
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
Then we start our theorem proving. Usually, a theorem statement starts with
"Theorem", "Lemma", "Corollary", "Fact" or "Example".
Fact swaping_correct:
∀x y: Z,
{{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
Proof.
intros.
∀x y: Z,
{{ [[X]] == x AND [[Y]] == y }}
TEMP ::= X;;
X ::= Y;;
Y ::= TEMP
{{ [[X]] == y AND [[Y]] == x }}.
Proof.
intros.
We have seen this command "intros" in our introduction to this course. It
will move universally quantified variables in the conclusion into assumptions.
apply hoare_seq with ([[Y]] == y AND [[TEMP]] == x)%assert.
This tactic says: we choose to use hoare_seq to prove our conclusion. The
"with" clause indicates the middle condition. We use %assert to let CoqIDE
know that this argument is an assertion. This tactic reduces the original proof goal into two smaller ones—-one is a
Hoare triple for the first command and the other is a Hoare triple for the last
two assignment commands. They corresponds to two assumptions of hoare_seq
respectively. This is reasonable—-in order to prove something using
hoare_seq, one have to prove its assumptions first.
apply triple1.
The first proof goal is our first hypothesis.
apply hoare_seq with ([[X]] == y AND [[TEMP]] == x)%assert.
The second proof goal needs hoare_seq again.
apply triple2.
apply triple3.
apply triple3.
In the end, we write "Qed" to complete our proof.
Qed.
End swapping.
(* Tue Mar 5 09:27:45 UTC 2019 *)
End swapping.
(* Tue Mar 5 09:27:45 UTC 2019 *)