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.

Why hoare_if_first_try is not good?


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?

A short answer is "compiler". But it is really not obvious how compilers are connected with semantic descriptions like hoare_if. To figure out this connection, you need to learn the rest part of this course.

Why is hoare_if strong enough?

To answer this question, we first need to answer: what does "strong enough" mean. We may state it as follows: For any assertions P and Q, any boolean expression b and any commands c1 and c2, if
           {{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

Finally, we need a rule for reasoning about while loops.
Axiom hoare_while : P b c,
  {{ P AND [[b]] }} c {{P}} →
  {{P}} While b Do c EndWhile {{ P AND NOT [[b]] }}.
Here, the proposition P is called a loop invariant (循环不变量).

From axiomatic semantics to program correctness proof

So far, we've introduced four axioms for four program constructor. They are:
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.
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.

Example: Swapping

We want to prove that the following program always swaps the values of variables X and Y. Or, formally, for any x and y,
       {[[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.
    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 }}.

Example: Swapping Using Addition and Subtraction

Here is a program that swaps the values of two variables using addition and subtraction instead of by assigning to a temporary variable.
       X ::= X + Y;;
       Y ::= X - Y;;
       X ::= X - Y
Again, we can prove it correct by three triples for assignments and hoare_seq.
    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 }}.

Example: Simple Conditionals

Here's a simple program using conditionals, with a possible specification:
       {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:
    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]] ) }}.

Example: Reduce to Zero

We can prove it by hoare_while and the following triple:
       {True AND [[!(X == 0)]}}
       X ::= X - 1
       {True }}.

Example: Division

For fixed positive integer m and n, the following program calculates the integer quotient and remainder.
       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:
       {{ 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:
       {{ 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:
       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
    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:
    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]}}.
In the end, hoare_seq and triple 1, 2, 5 tells us:
       {{ 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.

Example: Remainder Only

The following program calculates the remainder only.
       X ::= m;;
       While n ≤ X Do
         X ::= X - n
       EndWhile
We need to use existential quantifiers for help. We may write:
       {{ 0 ≤ m }}
       X ::= m;;
       While n ≤ X Do
         X ::= X - n
       EndWhile
       {{ (EXISTS qn * q + [[X]] == mAND 0 ≤ [[X]AND NOT [[n ≤ X]}}.
And our proof is similar. First,
    1. {{ 0 ≤ m }}
       X ::= m
       {{ (EXISTS qn * q + [[X]] == mAND 0 ≤ [[X]}}.
Second,
    2. {{ (EXISTS qn * q + [[X]] == mAND 0 ≤ [[X]AND [[n ≤ X]}}
       X ::= X - n
       {{ (EXISTS qn * q + [[X]] == mAND 0 ≤ [[X]}}.
By hoare_while and this triple above, we know that
    3. {{ (EXISTS qn * q + [[X]] == mAND 0 ≤ [[X]}}
       While n ≤ X Do
         X ::= X - n
       EndWhile
       {{ (EXISTS qn * q + [[X]] == mAND 0 ≤ [[X]AND NOT [[n ≤ X]}}.
Combining triple 1 and 3 together by hoare_seq proves our original goal.

Example: Slow subtraction

Prove that the following program calculates the subtraction.
    X ::= m;;
    Y ::= p;;
    While !(X == 0) Do
      Y ::= Y - 1;;
      X ::= X - 1
    EndWhile
Solution 1. Using invariant
    [[Y]] - [[X]] == p - m.
Solution 2. Using invariant
    EXISTS k[[Y]] + k == p AND [[X]] + k == m.

Example: Squaring

The following program squares X by repeated addition. Prove its correctness.
  I ::= 0;;
  RES ::= 0;;
  While !(I == X)  Do
    RES ::= RES + X;;
    I ::= I + 1
  EndWhile
Solution. Using invariant
    [[RES]] == [[I]] * m AND [[X]] == m.

Example: Finding Square Roots

The following program computes the (integer) square root of X by naive iteration. Prove its correctness.
    I ::= 0;;
    While (I+1)*(I+1) ≤ X Do
      I ::= I+1
    EndWhile
Solution. Using invariant
    [[I]] * [[I]] ≤ m AND [[X]] == m.

Program correctness proof in Coq

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


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.
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 }}.
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.
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.
In the end, we write "Qed" to complete our proof.
Qed.

End swapping.

(* Tue Mar 5 09:27:45 UTC 2019 *)