Lecture notes 20190315

First Order Logic 1

Remark. Some material in this lecture is from << Software Foundation >> volume 1 and volume 2.
Require Import PL.Imp4.

Review: Hoare logic

We have learnt that we can use decorated programs to represent Hoare logic proofs. Also, we have learnt how to write formal Hoare logic proofs in Coq. We will review these technique in the following examples.
Module swapping_asgn_fwd.

Import Axiomatic_semantics.

Local Instance X: var := new_var().
Local Instance Y: var := new_var().
Local Instance TEMP: var := new_var().
We want to prove that
  x yZ,
       {[[X]] == x AND [[Y]] == y }}
       TEMP ::= X;;
       X ::= Y;;
       Y ::= TEMP
       {[[X]] == y AND [[Y]] == x }}.
We can write a decorated program to illustrate our proof.
  /* [[X]] == x AND [[Y]] == y */
  TEMP ::= X;;
  /* EXISTS t[[X]] == x AND [[Y]] == y AND [[TEMP]] == [[X]] */
  /* [[X]] == x AND [[Y]] == y AND [[TEMP]] == [[X]] */
  X ::= Y;;
  /* EXISTS x0[[x0]] == x AND [[Y]] == y AND
                [[TEMP]] == [[x0]AND [[X]] == [[Y]] */
  /* [[Y]] == y AND [[TEMP]] == x AND [[X]] == [[Y]] */
  Y ::= TEMP
  /* EXISTS y0[[y0]] == y AND [[TEMP]] == x AND
               [[X]] == [[y0]AND [[Y]] == [[TEMP]] */
  /* [[X]] == y AND [[Y]] == x */.
Hypothesis derivation1: x y: Z,
  EXISTS x0, [[x0]] == x AND [[Y]] == y AND
                [[TEMP]] == [[x0]] AND [[X]] == [[Y]] ⊢
  [[Y]] == y AND [[TEMP]] == x AND [[X]] == [[Y]].

Hypothesis derivation2: x y: Z,
  EXISTS y0, [[y0]] == y AND [[TEMP]] == x AND [[X]] == [[y0]] AND [[Y]] == [[TEMP]] ⊢
  [[X]] == y AND [[Y]] == x.

Fact swaping_correct:
  x y: Z,
       {{ [[X]] == x AND [[Y]] == y }}
       TEMP ::= X;;
       X ::= Y;;
       Y ::= TEMP
       {{ [[X]] == y AND [[Y]] == x }}.
Proof.
  intros.
  eapply hoare_seq.
  apply hoare_asgn_fwd.
  assert_subst.
  assert_simpl.
  eapply hoare_seq.
  apply hoare_asgn_fwd.
  assert_subst.
  eapply hoare_consequence.
  apply derivation1.
  apply hoare_asgn_fwd.
  assert_subst.
  apply derivation2.
Qed.

End swapping_asgn_fwd.

Module swapping_asgn_bwd.

Import Axiomatic_semantics.

Local Instance X: var := new_var().
Local Instance Y: var := new_var().
Local Instance TEMP: var := new_var().
We can also use hoare_asgn_bwd to prove it. Here is the decorated program.
  /* [[X]] == x AND [[Y]] == y */
  /* [[Y]] == y AND [[X]] == x */.
  TEMP ::= X;;
  /* [[Y]] == y AND [[TEMP]] == x */.
  X ::= Y;;
  /* [[X]] == y AND [[TEMP]] == x */.
  Y ::= TEMP
  /* [[X]] == y AND [[Y]] == x */.
Hypothesis derivation1: x y: Z,
  [[X]] == x AND [[Y]] == y
  [[Y]] == y AND [[X]] == x.

Hypothesis derivation2: x y: Z,
  [[X]] == y AND [[Y]] == x
  [[X]] == y AND [[Y]] == x.

Fact swaping_correct:
  x y: Z,
       {{ [[X]] == x AND [[Y]] == y }}
       TEMP ::= X;;
       X ::= Y;;
       Y ::= TEMP
       {{ [[X]] == y AND [[Y]] == x }}.
Proof.
  intros.
  pose proof hoare_asgn_bwd
               ([[X]] == y AND [[Y]] == x)%assert
               Y
               TEMP.
  assert_subst in H.
  pose proof hoare_asgn_bwd
               ([[X]] == y AND [[TEMP]] == x)%assert
               X
               Y.
  assert_subst in H0.
  pose proof hoare_asgn_bwd
               ([[Y]] == y AND [[TEMP]] == x)%assert
               TEMP
               X.
  assert_subst in H1.
  pose proof hoare_seq _ _ _ _ _ H0 H.
  pose proof hoare_seq _ _ _ _ _ H1 H2.
  pose proof hoare_consequence _ _ _ _ _
              (derivation1 x y) H3 (derivation2 x y).
  exact H4.
Qed.

End swapping_asgn_bwd.

Module test_prime.

Local Instance A: var := new_var().
Local Instance N: var := new_var().
Local Instance Res: var := new_var().
Local Instance X: var := new_var().
Local Instance Y: var := new_var().
The following program will test whether A is a prime number or not, very slowly.
  N ::= 2;;
  Res ::= 0;;
  While !(A == NDo
    X ::= A;;
    Y ::= 0;;
    While N ≤ X Do
      X ::= X - N;;
      Y ::= Y + 1
    EndWhile;;
    If (X == 0)
    Then Res ::= 1
    Else Skip
    EndIf;;
    N ::= N + 1
  EndWhile.
Although it is a little bit longer than our previous examples, we can still prove it correct.
  /* [[A]] == m AND 2 ≤ m */
  N ::= 2;;
  Res ::= 0;;
  /* [[A]] == m AND 2 ≤ m AND [[N]] == 2 AND [[Res]] == 0 */
  /* [[A]] == m AND [[N]] ≤ [[A]AND 2 ≤ m AND
     (([[Res]] == 1 AND EXISTS kEXISTS q,
          2 ≤ k AND k < [[N]AND k * q == mOR
     ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
          2 ≤ k AND k < [[N]AND k * q == m)) */
  While !(A == NDo
    /* [[A]] == m AND [[N]] ≤ [[A]AND 2 ≤ m AND
       (([[Res]] == 1 AND EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == mOR
       ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == m)) AND
       [[!(A == N)]] */
    X ::= A;;
    Y ::= 0;;
    /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
       (([[Res]] == 1 AND EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == mOR
       ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == m)) AND
       [[N]] * [[Y]] + [[X]] == m AND 0 ≤ [[X]] */
    While N ≤ X Do
      /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
         (([[Res]] == 1 AND EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == mOR
         ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == m)) AND
         [[N]] * [[Y]] + [[X]] == m AND 0 ≤ [[X]AND [[N ≤ X]] */
      X ::= X - N;;
      Y ::= Y + 1
      /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
         (([[Res]] == 1 AND EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == mOR
         ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == m)) AND
         [[N]] * [[Y]] + [[X]] == m AND 0 ≤ [[X]] */
    EndWhile;;
    /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
       (([[Res]] == 1 AND EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == mOR
       ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == m)) AND
       [[N]] * [[Y]] + [[X]] == m AND 0 ≤ [[X]AND NOT [[N ≤ X]] */
    If (X == 0)
    Then
      /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
         (([[Res]] == 1 AND EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == mOR
         ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == m)) AND
         [[N]] * [[Y]] + [[X]] == m AND 0 ≤ [[X]AND
         NOT [[N ≤ X]AND [[X == 0]] */
      Res ::= 1
      /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
         (([[Res]] == 1 AND EXISTS kEXISTS q,
              2 ≤ k AND k < [[N + 1]AND k * q == mOR
         ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
              2 ≤ k AND k < [[N + 1]AND k * q == m)) */
    Else
      /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
         (([[Res]] == 1 AND EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == mOR
         ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
              2 ≤ k AND k < [[N]AND k * q == m)) AND
         [[N]] * [[Y]] + [[X]] == m AND 0 ≤ [[X]AND
         NOT [[N ≤ X]AND NOT [[X == 0]] */
      Skip
      /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
         (([[Res]] == 1 AND EXISTS kEXISTS q,
              2 ≤ k AND k < [[N + 1]AND k * q == mOR
         ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
              2 ≤ k AND k < [[N + 1]AND k * q == m)) */
    EndIf;;
    /* [[A]] == m AND [[N]] < [[A]AND 2 ≤ m AND
       (([[Res]] == 1 AND EXISTS kEXISTS q,
            2 ≤ k AND k < [[N + 1]AND k * q == mOR
       ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
            2 ≤ k AND k < [[N + 1]AND k * q == m)) */
    N ::= N + 1
    /* [[A]] == m AND [[N]] ≤ [[A]AND 2 ≤ m AND
       (([[Res]] == 1 AND EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == mOR
       ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
            2 ≤ k AND k < [[N]AND k * q == m)) */
  EndWhile
  /* [[A]] == m AND [[N]] ≤ [[A]AND 2 ≤ m AND
     (([[Res]] == 1 AND EXISTS kEXISTS q,
          2 ≤ k AND k < [[N]AND k * q == mOR
     ([[Res]] == 0 AND NOT EXISTS kEXISTS q,
          2 ≤ k AND k < [[N]AND k * q == m)) AND
     NOT [[ !(A == N]] */
Coq's formal proof of this program will be part of your second homework.
End test_prime.

Summary

Till now, we used a simple imperative programming language (指令式程序语言) as an example to introduce the following concept:
  • assertions (including syntactic substitution and assertion derivation)
  • Hoare triples
  • axiomatic semantics.
Also we have learnt how to use Hoare logic to prove a program correct. We learnt to do it both on paper and in Coq. Moreover, we learnt to use decorated programs to illustrate basic ideas in such proofs.
For Coq usage, we learnt the following tactics:
  • intros
  • apply
  • pose proof
  • exact
  • eapply
  • assert.

Assertion entailment

For now, we still have to assume the correctness of some assertion derivation. For example, the following Coq code is copied from our lab exercise last time.
Module reduce_to_zero_alter2.
Import Axiomatic_semantics.

Local Instance X: var := new_var().

Hypothesis derivation1:
  0 ≤ [[X]] ⊢ 0 ≤ [[X]].

Hypothesis derivation2:
  0 ≤ [[X]] AND NOT [[! (X ≤ 0)]] ⊢ [[X]] == 0.

Hypothesis derivation3:
  0 ≤ [[X]] AND [[! (X ≤ 0)]] ⊢ 0 ≤ [[X]] - 1.

Hypothesis derivation4:
  0 ≤ [[X]] ⊢ 0 ≤ [[X]].

Fact reduce_to_zero_correct:
       {{ 0 ≤ [[X]] }}
       While !(X ≤ 0) Do
         X ::= X - 1
       EndWhile
       {{ [[X]] == 0 }}.
Proof.
  assert ({{0 ≤ [[X]] AND [[! (X ≤ 0)]}}} X ::= X - 1 {{0 ≤ [[X]}}}).
  {
    pose proof hoare_asgn_bwd
                 (0 ≤ [[X]])%assert
                 X
                 (X - 1).
    assert_subst in H.
    assert_simpl in H.
    eapply hoare_consequence.
    apply derivation3.
    exact H.
    apply derivation4.
  }
  apply hoare_consequence with
          (0 ≤ [[X]])%assert
          (0 ≤ [[X]] AND NOT [[ !(X ≤ 0) ]])%assert.
  apply derivation1.
  apply hoare_while.
  apply H.
  apply derivation2.
Qed.
The question is: do we really need to put such derivation into hypothesis? Can we prove them instead? The answer is yes.
Lemma der1:
  0 ≤ [[X]] ⊢ 0 ≤ [[X]].
Proof.
Obviously, this proof goal is equivalent to say: for any integer x, if 0 x then 0 x. The following tactic entailer provided by our Imp library apply this transition for us.
  entailer.
Now, we know that we should use "intros" to drag this universally quantified variable X' above the line.
  intros.
Oops, it does more!! But that is reasonable. Proving A B (reads "A implies B" is equivalent with proving B with assumption A. At this, position, we know what to do now:
  exact H.
Qed.

Lemma der2:
  0 ≤ [[X]] AND NOT [[! (X ≤ 0)]] ⊢ [[X]] == 0.
Proof.
  entailer.
  intros.
The symbol "/\" (see the assumption H) represents "and" in Coq and the symbol "~" represents negation or "not". We know that this proof goal is true But at this position, what we have already learnt cannot help us much. The following tactic "omega" is to solve linear programming problems. Let's try it.
  omega.
Bingo!
Qed.
Let's try it again.
Lemma der3:
  0 ≤ [[X]] AND [[! (X ≤ 0)]] ⊢ 0 ≤ [[X]] - 1.
Proof.
  entailer.
  intros.
  omega.
Qed.
Now we have got all four hypotheses proved.
End reduce_to_zero_alter2.
Here are more examples.
Module more_entailments.

Local Instance X: var := new_var().
Local Instance Y: var := new_var().
The following derivation is used in verifying slow_sub:
    {True }}
    X ::= m;;
    Y ::= p;;
    While !(X == 0) Do
      Y ::= Y - 1;;
      X ::= X - 1
    EndWhile
    {[[Y]] - [[X]] == p - m AND NOT [[ !(X == 0) ]}}.
Lemma der1: m: Z,
  True[[m]] == m.
Proof.
  intros.
  entailer.
  omega.
Qed.

Lemma der2: m p: Z,
  [[X]] == m AND [[Y]] == p[[Y]] - [[X]] == p - m.
Proof.
  intros.
  entailer.
  intros.
  omega.
Qed.

Lemma der3: m p: Z,
  [[Y]] - [[X]] == p - m AND [[ !(X == 0) ]] ⊢
  [[Y-1]] - [[X]] == p - m - 1.
Proof.
  intros.
  entailer.
  intros.
  omega.
Qed.

Lemma der4: m p: Z,
  [[Y]] - [[X]] == p - m - 1 ⊢
  [[Y]] - [[X - 1]] == p - m.
Proof.
  intros.
  entailer.
  intros.
  omega.
Qed.

End more_entailments.

Proving True

When proving the following Hoare triple:
       {True }}
       While !(X ≤ 0) Do
         X ::= X - 1
       EndWhile
       {[[X]] ≤ 0 }}
We need to reason about True for many times. If you forget, here is the decorated program:
       /* True */
       While !(X ≤ 0) Do
         /* True AND [[ !(X ≤ 0) ]] */
         /* True */
         X ::= X - 1
         /* EXISTS xTrue AND [[X]] == [[x - 1]] */
         /* True */
       EndWhile
       /* True AND NOT [[ !(X ≤ 0) ]] */
       /* [[X]] ≤ 0 */.
We need the following derivation:
  True ⊢ True
  True AND [[! (X ≤ 0)]] ⊢ True
  EXISTS xTrue AND [[X]] == x - 1 ⊢ True
  True AND NOT [[! (X ≤ 0)]] ⊢ [[X]] ≤ 0.
We know how to prove the first one:
Module reduce_to_zero_alter1.

Local Instance X: var := new_var().

Lemma der1:
  TrueTrue.
Proof.
  intros.
  entailer.
  intros.
  exact H.
Qed.
But actually, we do not need to derive True from something else. It is by itself true.
Lemma der2:
  True AND [[! (X ≤ 0)]] ⊢ True.
Proof.
  entailer.
  intros.
In Coq, I is a proof of True. Whenever you want to prove True, use exact I.
  exact I.
Qed.
Now we can prove the rest two hypotheses.
Lemma der3:
  EXISTS x, True AND [[X]] == x - 1 ⊢ True.
Proof.
  entailer.
  intros.
  exact I.
Qed.

Lemma der4:
  True AND NOT [[! (X ≤ 0)]] ⊢ [[X]] ≤ 0.
Proof.
  entailer.
  intros.
  omega.
Qed.

End reduce_to_zero_alter1.
The tactic exact I is also useful in normal Coq proofs.
Theorem True_is_true: True.
Proof.
  exact I.
Qed.

Fact stupid_fact: x y:Z, x = yTrue.
Proof.
  intros.
  exact I.
Qed.

Conjunction

To prove a conjunction, use the split tactic. It will generate two subgoals, one for each part of the statement:
Lemma True2: TrueTrue.
Proof.
  split.
  exact I.
  exact I.
Qed.

Lemma and_intro : A B : Prop, ABAB.
Proof.
  intros A B HA HB. split.
  - apply HA.
  - apply HB.
Qed.

Example and_exercise :
  n m : Z, n + 2*m = 0 → 2*n + m = 0 → n = 0 ∧ True.
Proof.
  intros.
  split.
  omega.
  exact I.
Qed.
To use a conjunctive hypothesis to help prove something else, we use the destruct tactic.
Lemma and_example2 :
  n m : Z, n = 0 ∧ (Truem = 0) → n + m = 0.
Proof.
  intros.
  destruct H.
  pose proof H0 I.
  omega.
Qed.

Lemma and_example2' :
  n m : Z, n = 0 ∧ (Truem = 0) → n + m = 0.
Proof.
  intros.
  destruct H as [Hn H].
  pose proof H I as Hm.
  omega.
Qed.

Theorem and_commut : P Q : Prop,
  PQQP.
Proof.
  intros.
  destruct H as [HP HQ].
  split.
  - exact HQ.
  - exact HP.
Qed.

Exercise: 2 stars, standard (and_assoc)

Theorem and_assoc : P Q R : Prop,
  P ∧ (QR) → (PQ) ∧ R.
Proof.
  intros.
  destruct H as [HP [HQ HR]].
  (* FILL IN HERE *) Admitted.

Disjunction

Another important connective is the disjunction, or logical or, of two propositions: A B is true when either A or B is.
Lemma or_example :
  n m : Z, n = 0 ∨ m = 0 → n * m = 0.
Proof.
  intros.
  destruct H.
  - rewrite H.
    omega.
  - rewrite H.
    omega.
Qed.

Lemma or_example2 :
  P Q R: Prop, (PR) → (QR) → (PQR).
Proof.
  intros.
  destruct H1 as [HP | HQ].
  - apply H.
    exact HP.
  - pose proof H0 HQ.
    exact H1.
Qed.
Conversely, to show that a disjunction holds, we need to show that one of its sides does. This is done via two tactics, left and right. As their names imply, the first one requires proving the left side of the disjunction, while the second requires proving its right side. Here is a trivial use...
Lemma or_introl : A B : Prop, AAB.
Proof.
  intros.
  left.
  exact H.
Qed.

Lemma or_intror : A B : Prop, BAB.
Proof.
  intros.
  right.
  exact H.
Qed.

Exercise: 1 star, standard (or_commut)

Theorem or_commut : P Q : Prop,
  PQQP.
Proof.
  (* FILL IN HERE *) Admitted.

Implication

If conjunction and disjunction are treated as important connectives in logic, then implication must be foundamental. And we have actually used it for many times. We have learnt how to prove the following one:
Theorem modus_ponens: P Q: Prop,
  P ∧ (PQ) → Q.
Proof.
  intros.
  destruct H.
  pose proof H0 H.
  exact H1.
Qed.
But we can also prove it in alternative ways.
Theorem modus_ponens_alter1: P Q: Prop,
  P ∧ (PQ) → Q.
Proof.
  intros.
  destruct H.
  revert H.
  exact H0.
Qed.

Theorem modus_ponens_alter2: P Q: Prop,
  P ∧ (PQ) → Q.
Proof.
  intros.
  destruct H.
  specialize (H0 H).
  exact H0.
Qed.

(* Fri Mar 15 07:58:05 UTC 2019 *)