Lecture notes 20210308 Hoare Logic 4

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

Import Assertion_S.
Import Assertion_S_Tac.
Import Assertion_S_Rules.
Import Concrete_Pretty_Printing.
Import Axiomatic_semantics.
Import Derived_Rules.

Review

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:
  • rewrite
  • intros
  • apply
  • pose proof
  • exact
  • eapply
  • assert.

Assertion entailment

For now, we still have to assume the correctness of some assertion derivation.
Module reduce_to_zero.

Local Instance X: var := new_var().
The question is: do we really need to put derivation into hypothesis? Can we prove them instead? The answer is yes.
Fact derivation_example:
  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. We know that this proof goal is true. But at this position, what we have already learnt cannot help us much. The following tactic "lia" is to solve Linear Integer Arithmetic systems. Let's try it.
  lia.
Qed.

Lemma der1:
  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". Let's see whether lia can handle conjunctions in assumptions.
  lia.
Bingo!
Qed.
Now we have got both hypotheses proved.
End reduce_to_zero.
Coq also provide an automatic verification tactic for Non-linear Integer Arithmetics, nia. It supports ring operations on integers (plus, minus and multiplication). However, it is not a complete solver, i.e. it may fail to prove a provable fact. Here are two examples in which nia succeeds.
Goal x y: Z, (x + 1) * y < y + x * y + 1.
Proof.
  intros.
  nia.
Qed.
In principle, nia knows to use commutative-ring equations (commutativity and associativity of sum and multiplication, and the distributive law) and to apply linear comparisons.
Goal x y: Z, (x + 1) * (x + 1) ≥ 0.
Proof.
  intros.
  nia.
Qed.
For non-linear inequalities, nia may succeed.
Goal x y: Z, (x + y) * (x + y) ≥ 0.
Proof.
  intros.
  Fail nia.
Abort.
... and may fail as well.

Proving True


Module reduce_to_zero_der.

Local Instance X: var := new_var().
We do not need to derive True from anything else. It is by itself true.
Lemma der1:
  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.

End reduce_to_zero_der.

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, A -> B -> AB.
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.
  + lia.
  + 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 ∧ (True -> m = 0) -> n + m = 0.
Proof.
  intros.
  destruct H.
  pose proof H0 I.
  lia.
Qed.

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

Theorem and_commut : P Q : Prop,
  PQ -> QP.
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.
    lia.
  - rewrite H.
    lia.
Qed.

Lemma or_example2 :
  P Q R: Prop, (P -> R) -> (Q -> R) -> (PQ -> R).
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, A -> AB.
Proof.
  intros.
  left.
  exact H.
Qed.

Lemma or_intror : A B : Prop, B -> AB.
Proof.
  intros.
  right.
  exact H.
Qed.

Exercise: 1 star, standard (or_commut)

Theorem or_commut : P Q : Prop,
  PQ -> QP.
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 ∧ (P -> Q) -> 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 ∧ (P -> Q) -> Q.
Proof.
  intros.
  destruct H.
  revert H.
  exact H0.
Qed.

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

Falsehood and Negation

Since False is a contradictory proposition, the principle of explosion also applies to it. If we get False into the proof context, we can use contradiction to complete any goal:
Theorem ex_falso_quodlibet : (P:Prop),
  False -> P.
Proof.
  (* WORKED IN CLASS *)
  intros.
  contradiction.
Qed.
The Latin ex falso quodlibet means, literally, "from falsehood follows whatever you like"; this is another common name for the principle of explosion.
The tactic contradiction also works if both P and ¬P appear in the proof context.
Theorem contradiction_implies_anything : P Q : Prop,
  (P ∧ ¬P) -> Q.
Proof.
  (* WORKED IN CLASS *)
  intros.
  destruct H.
  contradiction.
Qed.
Besides the fact that P and ¬P cannot be both true, one of them must be true. This principle is called law of excluded middle, and is named classic in Coq.
Check classic.
  (* : forall P : Prop, P \/ ~ P *)

Theorem double_neg_elim : P : Prop,
  ¬¬P -> P.
Proof.
  (* WORKED IN CLASS *)
  intros.
  pose proof classic P.
  destruct H0.
  + exact H0.
  + contradiction.
Qed.

Theorem not_False :
  ¬False.
Proof.
  (* WORKED IN CLASS *)
  pose proof classic False.
  destruct H.
  + contradiction.
  + exact H.
Qed.

Theorem double_neg_intro : P : Prop,
  P -> ¬¬P.
Proof.
  (* WORKED IN CLASS *)
  intros.
  pose proof classicP).
  destruct H0.
  + contradiction.
  + exact H0.
Qed.

Existential Quantification

To say that there is some x such that some property P holds of x, we write x, P in Coq and write EXISTS x, P in our assertion language. To prove a statement of the form x, P, we must show that P holds for some specific choice of value for x, known as the witness of the existential. This is done in two steps: First, we explicitly tell Coq which witness t we have in mind by invoking the tactic t. Then we prove that P holds after all occurrences of x are replaced by t.
Lemma four_is_even : n, 4 = n + n.
Proof.
  2.
  lia.
Qed.

Lemma six_is_not_prime: n, 2 ≤ n < 6 ∧ q, n * q = 6.
Proof.
  2.
  split.
  + lia.
  + 3.
    lia.
Qed.
Conversely, if we have an existential hypothesis x, P in Coq, we can destruct it
Theorem exists_example : n,
  (m, n = 4 + m) ->
  (o, n = 2 + o).
Proof.
(* WORKED IN CLASS *)
  intros.
  destruct H.
  (2 + x).
  lia.
Qed.

Theorem dist_exists_and : (X:Type) (P Q : X -> Prop),
  (x, P xQ x) -> (x, P x) ∧ (x, Q x).
Proof.
(* WORKED IN CLASS *)
  intros.
  destruct H as [x [HP HQ]].
  split.
  + x.
    exact HP.
  + x.
    exact HQ.
Qed.
Notice that the reverse direction of this theorem is not true. For example, there obviously exists at least one even number and one odd number but there is not number which is both even and odd.
Module sample_derivation.

Import Assertion_S.
Import Assertion_S_Tac.
Import Assertion_S_Rules.
Import Concrete_Pretty_Printing.

Local Instance X: var := new_var().

Fact der: (m n: Z),
  0 ≤ m AND [[X]] = m  
  EXISTS q, n * q + [[X]] = m AND 0 ≤ [[X]].
Proof.
(* WORKED IN CLASS *)
  intros.
  entailer.
  intros.
  0.
  lia.
Qed.

End sample_derivation.

Universal Quantification

The Coq tactic support for universal quantification is very similar to implication's. In short, we use pose proof (or specialize) for universal quantification in assumptions and we use intros for universal quantification in a conclusion. Also, apply can be used for writing backward proofs.
Lemma forall_comm: (P: Z -> Z -> Prop),
  (x y, P x y) ->
  (y x, P x y).
Proof.
  intros.
  apply H.
Qed.

Lemma forall_example1: (P Q: Z -> Z -> Prop),
  P 0 1 ->
  (x y, P x y -> Q x y) ->
  Q 0 1.
Proof.
  intros.
  pose proof H0 0 1 H.
  exact H1.
Qed.

Lemma forall_example2: (P Q: Z -> Z -> Prop),
  P 0 1 ->
  (x y, P x y -> Q x y) ->
  Q 0 1.
Proof.
  intros.
  specialize (H0 0 1 H).
  exact H0.
Qed.

Lemma forall_example3: (P Q: Z -> Z -> Prop),
  P 0 1 ->
  (x y, P x y -> Q x y) ->
  Q 0 1.
Proof.
  intros.
  apply H0.
  exact H.
Qed.

Lemma forall_example4: (P Q: Z -> Z -> Prop),
  P 0 1 ->
  (x y, P x y -> Q x y) ->
  Q 0 1.
Proof.
  intros.
  apply H0 in H.
  exact H.
Qed.

Exercise: 1 star, standard (dist_not_exists)

Prove that "P holds for all x" implies "there is no x for which P does not hold." (Hint: destruct H as [x E] works on existential assumptions!)
Theorem dist_not_exists : (X:Type) (P : X -> Prop),
  (x, P x) -> ¬(x, ¬P x).
Proof.
(* FILL IN HERE *) Admitted.

First Order Logic for Assertion Derivation


Module assertion_FOL.
The Imp library also supports first order logic proof directly, i.e., in order to verify assertion derivation, you can choose not to reduce it to normal Coq propositions. The following proof rules are provided for you.
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 *)


Check AND_right.
  (* : forall P Q R : Assertion,
       P  ⊢ Q -> P  ⊢ R -> P  ⊢ Q AND R *)


Check OR_left.
  (* : forall P Q R : Assertion,
       P  ⊢ R -> Q  ⊢ R -> P OR Q  ⊢ R *)


Check OR_right1.
  (* : forall P Q R : Assertion,
       P  ⊢ Q -> P  ⊢ Q OR R *)


Check OR_right2.
  (* : forall P Q R : Assertion,
       P  ⊢ R -> P  ⊢ Q OR R *)


Check CONTRA.
  (* : forall P Q : Assertion, P AND NOT P  ⊢ Q *)

Check LEM.
  (* : forall P Q : Assertion, P  ⊢ Q OR NOT Q *)

Check True_right.
  (* : forall P : Assertion, P  ⊢ True *)

Check False_left.
  (* : forall P : Assertion, False  ⊢ P *)

Check EXISTS_left.
  (* : forall P (Q : Assertion),
       (forall x : Z, P x  ⊢ Q) ->
       EXISTS x, P x  ⊢ Q *)


Check EXISTS_right.
  (* : forall (P : Assertion) Q (x : Z),
       P  ⊢ Q x -> P  ⊢ EXISTS x, Q x *)


Check FORALL_left.
  (* : forall P (Q : Assertion) (x : Z),
       P x  ⊢ Q -> FORALL x0, P x0  ⊢ Q *)


Check FORALL_right.
  (* : forall (P : Assertion) Q,
       (forall x : Z, P  ⊢ Q x) ->
       P  ⊢ FORALL x, Q x *)


Check derives_refl.
  (* : forall P : Assertion, P  ⊢ P *)

Check derives_trans.
  (* : forall P Q R : Assertion,
       P  ⊢ Q -> Q  ⊢ R -> P  ⊢ R *)


Local Instance X: var := new_var().

Fact example: 0 ≤ [[ X ]] AND [[ X ]] < 100  [[ X ]] < 100 AND 0 ≤ [[ X ]].
Proof.
  apply AND_right.
  + apply AND_left2.
    apply derives_refl.
  + apply AND_left1.
    apply derives_refl.
Qed.

End assertion_FOL.
The following example is about the slow division program. We will show different proof techniques in this example.
Module slow_div.

Local Instance X: var := new_var().
Local Instance Y: var := new_var().

Lemma derivation1: m n: Z,
  0 ≤ m AND [[X]] = m AND [[Y]] = 0  
  n * [[Y]] + [[X]] = m AND 0 ≤ [[X]].
Proof.
  intros.
  entailer.
  intros.
  nia.
Qed.

Lemma derivation2: m n: Z,
  EXISTS z, n * [[Y]] + z = m AND 0 ≤ z AND nz AND [[X]] = z - n  
  n * [[Y]] + [[X]] + n = m AND 0 ≤ [[X]].
Proof.
  intros.
  apply EXISTS_left.
  intros.
  entailer.
  intros.
  lia.
Qed.

Lemma derivation3: m n: Z,
  EXISTS z, n * z + [[X]] + n = m AND 0 ≤ [[X]] AND [[Y]] = z + 1  
  n * [[Y]] + [[X]] = m AND 0 ≤ [[X]].
Proof.
  intros.
  entailer.
  intros.
  destruct H as [k ?].
  nia.
Qed.

Lemma derivation4: m n: Z,
  n * [[Y]] + [[X]] = m AND 0 ≤ [[X]] AND NOT [[nX]]  
  n * [[Y]] + [[X]] = m AND 0 ≤ [[X]] AND [[X]] < n.
Proof.
  intros.
  apply AND_right.
  + apply AND_left1.
    apply derives_refl.
  + apply AND_left2.
    entailer.
    intros.
    lia.
Qed.

Fact slow_div_correct: m n: Z,
        {{ 0 ≤ m }
       X ::= m;;
       Y ::= 0;;
       While nX 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.
  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 [[ nX ]] )%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.

End slow_div.

(* 2021-03-07 20:15 *)