Lecture notes 20190409

Denotational Semantics 3

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

Review: Denotations


Module Expression_Denotation.

Fixpoint aeval (a : aexp) (st : state) : Z :=
  match a with
  | ANum nn
  | AId Xst X
  | APlus a1 a2 ⇒ (aeval a1 st) + (aeval a2 st)
  | AMinus a1 a2 ⇒ (aeval a1 st) - (aeval a2 st)
  | AMult a1 a2 ⇒ (aeval a1 st) * (aeval a2 st)
  end.

Fixpoint beval (b : bexp) (st : state) : Prop :=
  match b with
  | BTrueTrue
  | BFalseFalse
  | BEq a1 a2 ⇒ (aeval a1 st) = (aeval a2 st)
  | BLe a1 a2 ⇒ (aeval a1 st) ≤ (aeval a2 st)
  | BNot b1 ⇒ ¬(beval b1 st)
  | BAnd b1 b2 ⇒ (beval b1 st) ∧ (beval b2 st)
  end.

End Expression_Denotation.

Module Relation_Operators.

Definition id {A: Type}: AAProp := fun a ba = b.

Definition empty {A B: Type}: ABProp := fun a bFalse.

Definition concat {A B C: Type} (r1: ABProp) (r2: BCProp): ACProp :=
  fun a cb, r1 a br2 b c.

Definition filter1 {A B: Type} (f: AProp): ABProp :=
  fun a bf a.

Definition filter2 {A B: Type} (f: BProp): ABProp :=
  fun a bf b.

Definition union {A B: Type} (r1 r2: ABProp): ABProp :=
  fun a br1 a br2 a b.

Definition intersection {A B: Type} (r1 r2: ABProp): ABProp :=
  fun a br1 a br2 a b.

Definition omega_union {A B: Type} (rs: natABProp): ABProp :=
  fun st1 st2n, rs n st1 st2.

End Relation_Operators.

Module Command_Denotation_As_State_Relation.

Import Expression_Denotation.
Import Relation_Operators.

Definition if_sem
  (b: bexp)
  (then_branch else_branch: statestateProp)
  : statestateProp
:=
  union
    (intersection then_branch (filter1 (beval b)))
    (intersection else_branch (filter1 (beval (BNot b)))).

Fixpoint iter_loop_body
  (b: bexp)
  (loop_body: statestateProp)
  (n: nat)
  : statestateProp
:=
  match n with
  | O
         intersection
           id
           (filter1 (beval (BNot b)))
  | S n'
            intersection
              (concat
                loop_body
                (iter_loop_body b loop_body n'))
              (filter1 (beval b))
  end.

Definition loop_sem (b: bexp) (loop_body: statestateProp)
  : statestateProp
:=
  omega_union (iter_loop_body b loop_body).

Fixpoint ceval (c: com): statestateProp :=
  match c with
  | CSkipid
  | CAss X E
      fun st1 st2
        st2 X = aeval E st1
        Y, XYst1 Y = st2 Y
  | CSeq c1 c2concat (ceval c1) (ceval c2)
  | CIf b c1 c2if_sem b (ceval c1) (ceval c2)
  | CWhile b cloop_sem b (ceval c)
  end.

Definition com_dequiv (d1 d2: statestateProp): Prop :=
  st1 st2, d1 st1 st2d2 st1 st2.

Theorem loop_recur: b loop_body,
  com_dequiv
    (loop_sem b loop_body)
    (union
      (intersection
        (concat loop_body
          (loop_sem b loop_body))
        (filter1 (beval b)))
      (intersection
        id
        (filter1 (beval (BNot b))))).
Proof.
  intros.
  unfold com_dequiv.
  intros.
  split.
  + intros.
    unfold loop_sem, omega_union in H.
    unfold union.
    destruct H as [n H].
    destruct n as [| n'].
    - right.
      simpl in H.
      exact H.
    - left.
      simpl in H.
      unfold concat, intersection in H.
      unfold concat, intersection.
      destruct H as [[st' [? ?]] ?].
      split.
      * st'.
        split.
        { exact H. }
        unfold loop_sem, omega_union.
        n'.
        exact H0.
      * exact H1.
  + intros.
    unfold loop_sem, omega_union.
    unfold union in H.
    destruct H.
    - unfold intersection, concat in H.
      destruct H as [[st' [? ?]] ?].
      unfold loop_sem, omega_union in H0.
      destruct H0 as [n ?].
      (S n).
      simpl.
      unfold intersection, concat.
      split.
      * st'.
        split.
        { exact H. }
        { exact H0. }
      * exact H1.
    - O.
      simpl.
      exact H.
Qed.

End Command_Denotation_As_State_Relation.

32-bit Evaluation


Module Bounded_Evaluation.

Import Expression_Denotation.
In this course, the expression evaluation of integer expressions is unbounded, unlike the situation of normal programming languages like C and Java. But still, we can define "whether evaluating an expression a" is within the range of signed 32-bit integers. We define this property as an inductive predicate in Coq.
Definition max32: Z := 2^31 -1.
Definition min32: Z := - 2^31.

Inductive signed32_eval: aexpstateZProp :=
  | S32_ANum: (n: Z) st,
      min32nmax32
      signed32_eval (ANum n) st n
  | S32_AId: (X: var) st,
      min32st Xmax32
      signed32_eval (AId X) st (st X)
  | S32_APlus: a1 a2 st v1 v2,
      signed32_eval a1 st v1
      signed32_eval a2 st v2
      min32v1 + v2max32
      signed32_eval (APlus a1 a2) st (v1 + v2)
  | S32_AMinus: a1 a2 st v1 v2,
      signed32_eval a1 st v1
      signed32_eval a2 st v2
      min32v1 - v2max32
      signed32_eval (AMinus a1 a2) st (v1 - v2)
  | S32_AMult: a1 a2 st v1 v2,
      signed32_eval a1 st v1
      signed32_eval a2 st v2
      min32v1 * v2max32
      signed32_eval (AMult a1 a2) st (v1 * v2).
In short, signed32_eval a st v says that evaluating a on state st is within the range of signed 32-bit integers (including all intermediate results) the final result is v. Obviously, the evaluation result must coincide with the expressions' denotations defined by aeval.
Theorem signed32_eval_correct: a st v,
  signed32_eval a st v
  aeval a st = v.
Proof.
(* WORKED IN CLASS *)
  intros.
  induction H.
  + simpl.
    reflexivity.
  + simpl.
    reflexivity.
  + simpl.
    rewrite IHsigned32_eval1.
    rewrite IHsigned32_eval2.
    reflexivity.
  + simpl.
    rewrite IHsigned32_eval1.
    rewrite IHsigned32_eval2.
    reflexivity.
  + simpl.
    rewrite IHsigned32_eval1.
    rewrite IHsigned32_eval2.
    reflexivity.
Qed.
Similarly, we can defined 16-bit evaluation.
Definition max16: Z := 2^15 -1.
Definition min16: Z := - 2^15.

Inductive signed16_eval: aexpstateZProp :=
  | S16_ANum: (n: Z) st,
      min16nmax16
      signed16_eval (ANum n) st n
  | S16_AId: (X: var) st,
      min16st Xmax16
      signed16_eval (AId X) st (st X)
  | S16_APlus: a1 a2 st v1 v2,
      signed16_eval a1 st v1
      signed16_eval a2 st v2
      min16v1 + v2max16
      signed16_eval (APlus a1 a2) st (v1 + v2)
  | S16_AMinus: a1 a2 st v1 v2,
      signed16_eval a1 st v1
      signed16_eval a2 st v2
      min16v1 - v2max16
      signed16_eval (AMinus a1 a2) st (v1 - v2)
  | S16_AMult: a1 a2 st v1 v2,
      signed16_eval a1 st v1
      signed16_eval a2 st v2
      min16v1 * v2max16
      signed16_eval (AMult a1 a2) st (v1 * v2).
Of course, 16-bit evaluation defines only a subset of 32-bit evaluation. Second half of the proof is left as exercise.
Lemma range16_range32: v,
  min16vmax16
  min32vmax32.
Proof.
  intros.
  unfold min16, max16 in H.
  unfold min32, max32.
  simpl in H.
  simpl.
  omega.
Qed.
EX2 (signed16_signed32)
Theorem signed16_signed32: a st v,
  signed16_eval a st v
  signed32_eval a st v.
Proof.
  intros.
  induction H.
  + apply S32_ANum.
    apply range16_range32.
    exact H.
  (* FILL IN HERE *) Admitted.
Moreover, from the definition of signed32_eval, we know that if all intermediate results of evaluating a on st fails in the range of 32-bit, the same property is also true for any a's subexpression a'. We first define sub_aexp.
Inductive sub_aexp: aexpaexpProp :=
| sub_aexp_refl: e: aexp,
    sub_aexp e e
| sub_aexp_APlus1: e e1 e2: aexp,
    sub_aexp e e1
    sub_aexp e (APlus e1 e2)
| sub_aexp_APlus2: e e1 e2: aexp,
    sub_aexp e e2
    sub_aexp e (APlus e1 e2)
| sub_aexp_AMinus1: e e1 e2: aexp,
    sub_aexp e e1
    sub_aexp e (AMinus e1 e2)
| sub_aexp_AMinus2: e e1 e2: aexp,
    sub_aexp e e2
    sub_aexp e (AMinus e1 e2)
| sub_aexp_AMult1: e e1 e2: aexp,
    sub_aexp e e1
    sub_aexp e (AMult e1 e2)
| sub_aexp_AMult2: e e1 e2: aexp,
    sub_aexp e e2
    sub_aexp e (AMult e1 e2).

Exercise: 3 stars, standard (signed32_eval_sub_aexp)

Theorem signed32_eval_sub_aexp: e1 e2 st,
  sub_aexp e1 e2
  (v2, signed32_eval e2 st v2) →
  (v1, signed32_eval e1 st v1).
Proof.
  intros.
(* FILL IN HERE *) Admitted.
End Bounded_Evaluation.

Loop-free Programs


Module Loop_Free.

Import Expression_Denotation.

Import Relation_Operators.

Import Command_Denotation_As_State_Relation.
Now, we prove that a loop free program always terminate.
Inductive loop_free: comProp :=
  | loop_free_skip:
      loop_free Skip
  | loop_free_asgn: X E,
      loop_free (CAss X E)
  | loop_free_seq: c1 c2,
      loop_free c1
      loop_free c2
      loop_free (c1 ;; c2)
  | loop_free_if: b c1 c2,
      loop_free c1
      loop_free c2
      loop_free (If b Then c1 Else c2 EndIf).

Theorem loop_free_terminate: c,
  loop_free c
  (st1, st2, ceval c st1 st2).
Proof.
  intros.
Try to understand why we need to strengthen the induction hypothesis here.
  revert st1.
  induction H; intros.
  + st1.
    unfold ceval.
    unfold id.
    reflexivity.
  + unfold ceval.
Oops! We need to construct such a program state st2.
Abort.

Definition state_update (st: state) (X: var) (v: Z): state :=
  fun Yif (Nat.eq_dec X Y) then v else st Y.
Here, we define a program state, i.e. a new function from program variables to integers. This program state is almost the same as st but only differs with st on X's value. X's new value will be v.
The Coq expression if (Nat.eq_dec X Y) then _ else _ says, if X and Y are the same variable, do the calculation described by then; otherwise do the calculation defined by else. In proofs, we can write destruct (Nat.eq_dec X Y) to do case analysis on whether X and Y are the same variable.
It is worth one more sentence emphasizing that Nat.eq_dec X Y is not to tell whether their values are the same; it tells whether they are the same variable.
You might be curious why Nat appears here. Here is some explanation. You do not need understand all of this. You can freely skip it if you want.
If you take a look at how "variables" are formalized in Imp, you would find that they are just names! And different variables are just represented by different natural numbers, i.e. 1st variable, 2nd variable, etc. That is why to determine whether two variable names are the same is to determine whether two natural numbers are the same.
End of the contents which you can skip.
Lemma state_update_spec: st X v,
  (state_update st X v) X = v
  (Y, XYst Y = (state_update st X v) Y).
Proof.
  intros.
  unfold state_update.
  split.
  + destruct (Nat.eq_dec X X).
    - reflexivity.
    - assert (X = X). { reflexivity. }
      tauto.
  + intros.
    destruct (Nat.eq_dec X Y).
    - tauto.
    - reflexivity.
Qed.
Now we are ready to prove loop_free_terminate.

Exercise: 2 stars, standard (loop_free_terminate)

Theorem loop_free_terminate: c,
  loop_free c
  (st1, st2, ceval c st1 st2).
Proof.
  intros.
  revert st1.
  induction H; intros.
  + st1.
    unfold ceval.
    unfold id.
    reflexivity.
  + unfold ceval.
    (state_update st1 X (aeval E st1)).
    apply state_update_spec.
The other two cases are left as exercise.
  (* FILL IN HERE *) Admitted.
You migh wonder why we choose not to define "loop_free" by a recursive function. For example:
Fixpoint loop_free_fun (c: com): Prop :=
  match c with
  | CSkipTrue
  | CAss _ _True
  | CSeq c1 c2loop_free_fun c1loop_free_fun c2
  | CIf b c1 c2loop_free_fun c1loop_free_fun c2
  | CWhile _ _False
  end.
It is no problem. And you can try to prove a similar theorem about termination.

Exercise: 2 stars, standard (loop_free_fun_terminate)

Theorem loop_free_fun_terminate: c,
  loop_free_fun c
  (st1, st2, ceval c st1 st2).
Proof.
(* FILL IN HERE *) Admitted.
If we compare loop_free and loop_free_fun, the latter one is less flexible to extend.
Inductive loop_free': comProp :=
  | loop_free'_skip:
      loop_free' Skip
  | loop_free'_asgn: X E,
      loop_free' (CAss X E)
  | loop_free'_seq: c1 c2,
      loop_free' c1
      loop_free' c2
      loop_free' (c1 ;; c2)
  | loop_free'_if: b c1 c2,
      loop_free' c1
      loop_free' c2
      loop_free' (If b Then c1 Else c2 EndIf)
  | loop_free'_if_then: b c1 c2,
      (st, beval b st) →
      loop_free' c1
      loop_free' (If b Then c1 Else c2 EndIf)
  | loop_free'_if_else: b c1 c2,
      (st, ¬beval b st) →
      loop_free' c2
      loop_free' (If b Then c1 Else c2 EndIf)
  | loop_free'_while_false: b c,
      (st, ¬beval b st) →
      loop_free' (While b Do c EndWhile).
This definition says: if an if-condition is always true, then the loops in its else-branch should not be considered as real loops. Similarly, if an if-condition is always false, then the loops in its then-branch should not be considered as real loops. Also, if a while-loop's loop condition is always false, the loop body will never be executed — that is not a real loop either.

Exercise: 2 stars, standard (loop_free'_terminate)

Theorem loop_free'_terminate: c,
  loop_free' c
  (st1, st2, ceval c st1 st2).
Proof.
(* FILL IN HERE *) Admitted.
End Loop_Free.

Other Denotational Semantics

It is the most common way to define a program's denotation as a binary relation between program states. But there are also other choices. We discuss three alternative definitions in this section.
Module Command_Denotation_Inductively_Defined.

Import Expression_Denotation.
Import Relation_Operators.
The following semantic definition states the same definition by a Coq inductive predicate.
Inductive ceval : comstatestateProp :=
  | E_Skip : st,
      ceval CSkip st st
  | E_Ass : st1 st2 X E,
      st2 X = aeval E st1
      (Y, XYst1 Y = st2 Y) →
      ceval (CAss X E) st1 st2
  | E_Seq : c1 c2 st st' st'',
      ceval c1 st st'
      ceval c2 st' st''
      ceval (c1 ;; c2) st st''
  | E_IfTrue : st st' b c1 c2,
      beval b st
      ceval c1 st st'
      ceval (If b Then c1 Else c2 EndIf) st st'
  | E_IfFalse : st st' b c1 c2,
      ¬beval b st
      ceval c2 st st'
      ceval (If b Then c1 Else c2 EndIf) st st'
  | E_WhileFalse : b st c,
      ¬beval b st
      ceval (While b Do c EndWhile) st st
  | E_WhileTrue : st st' st'' b c,
      beval b st
      ceval c st st'
      ceval (While b Do c EndWhile) st' st''
      ceval (While b Do c EndWhile) st st''.

Definition com_dequiv (d1 d2: statestateProp): Prop :=
  st1 st2, d1 st1 st2d2 st1 st2.
And we can prove that it has all same properties as our original definition. Here is only an example.

Exercise: 2 stars, standard (ceval_Cseq_spec)

Lemma ceval_CSeq_spec: c1 c2,
  com_dequiv (ceval (c1;; c2)) (concat (ceval c1) (ceval c2)).
Proof.
(* FILL IN HERE *) Admitted.
End Command_Denotation_Inductively_Defined.

Module Command_Denotation_With_Steps.

Import Expression_Denotation.
This time, a program's denotation is defined as a trinary relation. Specifically, st1, t, st2 belongs to the denotation of program c if and only if executing c from st1 may take time t and stop at state st2.
Definition skip_sem: stateZstateProp :=
  fun st1 t st2
    st1 = st2t = 0.

Definition asgn_sem (X: var) (E: aexp): stateZstateProp :=
  fun st1 t st2
    st2 X = aeval E st1
    Y, XYst1 Y = st2 Y
    t = 1.
Here we assume every assignment command takes one unit of time. We can write a more realistic definition here so that different assignment commands may take different amount of time.
Definition seq_sem (d1 d2: stateZstateProp)
  : stateZstateProp
:=
  fun st1 t st3
    t1 t2 st2,
      d1 st1 t1 st2d2 st2 t2 st3t = t1 + t2.

Definition if_sem (b: bexp) (d1 d2: stateZstateProp)
  : stateZstateProp
:=
  fun st1 t st2
    (d1 st1 (t - 1) st2beval b st1) ∨
    (d2 st1 (t - 1) st2 ∧ ¬beval b st1).
Here we assume that testing an if-condition will take one unit of time.
Fixpoint iter_loop_body
  (b: bexp)
  (loop_body: stateZstateProp)
  (n: nat)
  : stateZstateProp
:=
  match n with
  | O
     fun st1 t st2
       (st1 = st2t = 1) ∧ ¬beval b st1
  | S n'
     fun st1 t st3
       (t1 t2 st2,
         (loop_body) st1 t1 st2
         (iter_loop_body b loop_body n') st2 t2 st3
         t = t1 + t2) ∧
       beval b st1
  end.
Here we assume that testing a loop condition will take one unit of time.
Definition loop_sem (b: bexp) (loop_body: stateZstateProp)
  : stateZstateProp
:=
  fun st1 t st2
    n, (iter_loop_body b loop_body n) st1 t st2.

Fixpoint ceval (c: com): stateZstateProp :=
  match c with
  | CSkipskip_sem
  | CAss X Easgn_sem X E
  | CSeq c1 c2seq_sem (ceval c1) (ceval c2)
  | CIf b c1 c2if_sem b (ceval c1) (ceval c2)
  | CWhile b cloop_sem b (ceval c)
  end.

End Command_Denotation_With_Steps.

Module Command_Denotation_State_Trace.

Import Expression_Denotation.
This time, a program's denotation is defined as a different trinary relation — the relation of beginning states, intermediate traces and ending states. For conciseness, we assume that an intermediate state trace does not include the beginning state but includes the ending state.
Definition skip_sem: statelist statestateProp :=
  fun st1 tr st2
    st1 = st2tr = nil.

Definition asgn_sem (X: var) (E: aexp): statelist statestateProp :=
  fun st1 tr st2
    st2 X = aeval E st1
    Y, XYst1 Y = st2 Y
    tr = st2 :: nil.

Definition seq_sem (d1 d2: statelist statestateProp)
  : statelist statestateProp
:=
  fun st1 tr st3
    tr1 tr2 st2,
      d1 st1 tr1 st2d2 st2 tr2 st3tr = tr1 ++ tr2.

Definition if_sem (b: bexp) (d1 d2: statelist statestateProp)
  : statelist statestateProp
:=
  fun st1 tr st2
    (tr', tr = st1 :: tr'd1 st1 tr' st2beval b st1) ∨
    (tr', tr = st1 :: tr'd2 st1 tr' st2 ∧ ¬beval b st1).

Fixpoint iter_loop_body
  (b: bexp)
  (loop_body: statelist statestateProp)
  (n: nat)
  : statelist statestateProp
:=
  match n with
  | O
     fun st1 tr st2
       (st1 = st2tr = nil) ∧ ¬beval b st1
  | S n'
     fun st1 tr st3
       (tr1 tr2 st2,
         (loop_body) st1 tr1 st2
         (iter_loop_body b loop_body n') st2 tr2 st3
         tr = st1 :: tr1 ++ tr2) ∧
       beval b st1
  end.
Here we assume that testing an if-condition or a loop condition will have a footprint in program execution's state traces.
Definition loop_sem (b: bexp) (loop_body: statelist statestateProp)
  : statelist statestateProp
:=
  fun st1 tr st2
    n, (iter_loop_body b loop_body n) st1 tr st2.

Fixpoint ceval (c: com): statelist statestateProp :=
  match c with
  | CSkipskip_sem
  | CAss X Easgn_sem X E
  | CSeq c1 c2seq_sem (ceval c1) (ceval c2)
  | CIf b c1 c2if_sem b (ceval c1) (ceval c2)
  | CWhile b cloop_sem b (ceval c)
  end.

Definition com_dequiv (d1 d2: statelist statestateProp): Prop :=
  st1 tr st2, d1 st1 tr st2d2 st1 tr st2.

Exercise: 2 stars, standard (seq_sem_assoc)

Lemma seq_sem_assoc: d1 d2 d3,
  com_dequiv (seq_sem (seq_sem d1 d2) d3) (seq_sem d1 (seq_sem d2 d3)).
Proof.
(* FILL IN HERE *) Admitted.
End Command_Denotation_State_Trace.

(* Tue Apr 9 02:48:59 UTC 2019 *)