(** Remark. Some material in this lecture is from << Software Foundation >>
volume 1 and volume 2. *)
Require Import Coq.micromega.Psatz.
Require Import PL.Imp.
Require Import PL.ImpExt.
Require Import PL.RTClosure.
Import Assertion_D.
Import Abstract_Pretty_Printing.
Local Open Scope imp.
(* ################################################################# *)
(** * Review: Small Step *)
(** We learnt to describe program behavior via steps last time. Specifically,
we defined a "smallstep" relation between pairs of residue programs and
program states: [cstep (c1, st1) (c2, st2)]. *)
(* ################################################################# *)
(** * Simulation for Program Equivalence *)
(* ================================================================= *)
(** ** Associativity of Sequential Composition *)
(** We have defined program equivalence via denotational semantics weeks ago.
We will consider a new approach based on small step semantics. Specifically,
we can surely say two programs [c1] and [c2] have the same behavior if their
execution processes are stepwise corresponding to each other.
For example, we know that [c1 ;; (c2 ;; c3)] and [(c1 ;; c2);; c3] have the
same behavior for any [c1], [c2] and [c3], not only in the sense that they
have the same pairs of initial states and ending states, but also in the
sense that each one of them can simulate the other's execution step by step.
Here is a brief illustration:
[[
( c1 ;; (c2 ;; c3), st1 ) ( (c1 ;; c2) ;; c3, st1 )
 
 
v v
( c1' ;; (c2 ;; c3), st1' ) ( (c1' ;; c2) ;; c3, st1' )
 
[...] [...]
 
v v
( Skip ;; (c2 ;; c3), st2 ) ( (Skip ;; c2) ;; c3, st2 )
 
 
v v
> ( c2 ;; c3, st2 ) <


v
( c2' ;; c3, st2' )

...

v
( Skip ;; c3, st3 )


v
( c3 , st3 )


v
( c3' , st3' )

...

v
( Skip, st4 )
]]
We can formally define this correspondence.
*)
Module SeqAssoc.
Inductive match_com: com > com > Prop :=
 MatSeqAssoc: forall c1 c2 c3,
match_com (c1 ;; (c2 ;; c3)) ((c1 ;; c2);; c3)
 MatRefl: forall c,
match_com c c.
(** And we can proof the simulation between such two programs. Here, simulation
mean: whenever one side takes a step, the other side can take a
corresponding step.
[[
step
A > B
 .
match  . match
 .
v v
A'     > B' (exists)
step
]]
*)
Lemma L_simulate_R: forall c1 c1' c2' st1 st2,
match_com c1 c1' >
cstep (c1', st1) (c2', st2) >
exists c2,
match_com c2 c2' /\ cstep (c1, st1) (c2, st2).
(** The proof is straightforward. We can prove it by a case analysis over
[[
match_com c1 c2.
]]
Case 1: [MatRefl] case, i.e. [c1 = c2]. The conclusion is obviously true.
Case 2: [MatSeqAssoc] case, i.e. [c1 = c1_1 ;; (c1_2 ;; c1_3)]
Case 21: [cstep (c1_1, st1) (c2_1, st2)]. New simulation is still based on
[MatSeqAssoc].
Case 21: [c1_1 = CSkip]. New simulation is based [MatRefl]. *)
Proof.
intros.
inversion H as [c1_1 c1_2 c1_3 ]; subst.
2: { exists c2'; split; [constructor  exact H0]. }
inversion H0; subst.
inversion H2 as [  ? ? c2_1     ]; subst; clear H0 H2.
+ exists (c2_1 ;; (c1_2 ;; c1_3)).
split.
 apply MatSeqAssoc.
 constructor.
apply H1.
+ rename c1' into c1_2.
exists (c1_2 ;; c1_3).
split.
 apply MatRefl.
 constructor.
Qed.
(** The other direction is simular. *)
Lemma R_simulate_L: forall c1 c2 c1' st st',
match_com c1 c2 >
cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\ cstep (c2, st) (c2', st').
Proof.
intros.
inversion H; subst.
2: { exists c1'; split; [constructor  exact H0]. }
inversion H0; subst.
+ exists ((c1'0 ;; c3) ;; c4).
split.
 apply MatSeqAssoc.
 constructor.
constructor.
apply H2.
+ exists (c3 ;; c4).
split.
 apply MatRefl.
 constructor.
constructor.
Qed.
End SeqAssoc.
(* ================================================================= *)
(** ** Removing Skips On the Left *)
(** We know that [Skip] does nothing and can be removed. We proved that [c],
[c;; Skip] and [Skip;; c] behave the same based on denotational semantics.
Today, we first define a function to remove all [Skip] on the left side
of sequential composition. *)
Fixpoint remove_skip (c: com): com :=
match c with
 CSkip =>
CSkip
 CAss X E =>
CAss X E
 CSeq c1 c2 =>
match remove_skip c1 with
 CSkip => remove_skip c2
 _ => CSeq (remove_skip c1) (remove_skip c2)
end
 CIf b c1 c2 =>
CIf b (remove_skip c1) (remove_skip c2)
 CWhile b c1 =>
CWhile b (remove_skip c1)
end.
Example remove_skip_example_1:
remove_skip (Skip ;; (Skip ;; While BTrue Do Skip EndWhile)) =
While BTrue Do Skip EndWhile.
Proof. reflexivity. Qed.
Example remove_skip_example_2:
remove_skip ((Skip ;; Skip) ;; While BTrue Do Skip EndWhile) =
While BTrue Do Skip EndWhile.
Proof. reflexivity. Qed.
Example remove_skip_example_3: forall (X: var),
remove_skip (Skip ;; X ::= 0 ;; Skip) =
(X ::= 0 ;; Skip).
Proof. intros. reflexivity. Qed.
(** It is easy to prove that [c] and [remove_skip c] always have the same
denotation. But we can prove stronger claims based on small step semantics.
That is, the steps of executing [c] will be simulated by the steps of
executing [remove_skip c], though some redundant skipstep can be removed.
This conclusion can be formally stated as follows. *)
Definition cstep_or_not (X Y: com * state): Prop :=
X = Y \/ cstep X Y.
Definition SimulationStatement_01 (match_com: com > com > Prop): Prop :=
forall c1 c2 c1' st st',
match_com c1 c2 >
cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\ cstep_or_not (c2, st) (c2', st').
(** Let's start with an naive definition of [match_com]. *)
Module RemoveSkip_First_Attempt.
Definition match_com (c1 c2: com): Prop := c2 = remove_skip c1.
(** The following statement says: when [c] takes one step, [remove_skip c]
will take zero step or one corresponding step. *)
Lemma R_simulate_L: forall c1 c2 c1' st st',
match_com c1 c2 >
cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\
((c2, st) = (c2', st') \/ cstep (c2, st) (c2', st')).
(** Unfortunately, this statement is not true! Consider the execution of
[[
X ::= 1;; Y ::= 0.
]]
According to small step semantics, it takes three steps.
[[
X ::= 1;; Y ::= 0 >
Skip;; Y ::= 0 >
Y ::= 0 >
Skip
]]
But [remove_skip (Skip;; Y ::= 0) = (Y ::= 0)]. Thus, the first step above
does not have a correspondence when the [remove_skip] transformation is
taken. You can find where a Coq proof attempt will fail in the following
scripts. *)
Proof.
intros.
unfold match_com in *.
subst c2.
exists (remove_skip c1').
split; [reflexivity ].
induction_cstep H0; simpl; intros.
+ simpl.
right.
constructor; tauto.
+ simpl.
right.
constructor; tauto.
+
Abort.
End RemoveSkip_First_Attempt.
Module RemoveSkip.
(** In order to establish a simulation relation for [remove_skip], we define
a weaker version of [match_com]. The main idea is to reserve some leftside
skips. *)
Fixpoint match_com (c1 c2: com): Prop :=
match c1 with
 c1_1 ;; c1_2 =>
remove_skip c1_1 = CSkip /\ c2 = remove_skip c1_2 \/
exists c2_1, match_com c1_1 c2_1 /\ c2 = (c2_1 ;; remove_skip c1_2)
 CIf b c1_1 c1_2 =>
exists c2_1, match_com c1_1 c2_1 /\ c2 = CIf b c2_1 (remove_skip c1_2)
 _ =>
c2 = remove_skip c1
end.
(** For example, consider [X ::= 1;; (Skip;; Y ::= 2)], which will be transfered
to [X ::= 1;; Y ::=2] by [remove_skip].
[[
X ::= 1;; (Skip;; Y ::= 2) X ::= 1;; Y ::= 2
 
 
v v
Skip;; (Skip;; Y ::= 2) Skip;; Y ::= 2
  
   (no step)
v  
Skip;; Y ::= 2 Skip;; Y ::= 2
 
 
v v
Y ::= 2 Y ::= 2
 
 
v v
Skip Skip
]]
This new definition of [match_com] allows [Skip;; (Skip;; Y ::= 2)] to match
[Skip;; Y ::= 2], but not only restricted to [Y ::= 2].
Here we prove this [match_com] relation is indeed a weaker one comparing to
[remove_skip]. *)
Lemma match_com_remove_skip: forall c, match_com c (remove_skip c).
Proof.
intros.
induction c; simpl; try tauto.
+ destruct (remove_skip c1); try eauto.
+ eauto.
Qed.
(** Our proof strategy for simulation (defined as follows)
[[
forall c1 c2 c1' st st',
match_com c1 c2 >
cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\
cstep_or_not (c2, st) (c2', st')
]]
is based on an induction over its assumption
[[
cstep (c1, st) (c1', st').
]]
Corresponding to [cstep]'s definition, our inductive proof includes 8 cases.
For example, the proof step for the [CS_AssStep] case is to prove:
[[
astep st a a' >
match_com (CAss X a) c2 >
exists c2',
match_com (CAss X a') c2' /\
cstep_or_not (c2, st) (c2', st)
]]
which will be proved later (Lemma [R_simulate_L_CS_AssStep]).
Among all 8 cases, the most intersting case is the [CS_SeqStep] case, and we
need two auxiliary lemmas for establishing the proof.
The following lemma [remove_skip_skip_step] says, if [c1] can be transformed
to [Skip] by [remove_skip] (i.e. it is a sequential composition of many
skips), then taking one step from [c1] results in another command [c2] s.t.
[remove_skip c2 = Skip] as well. The main proof idea is to apply an
induction over [c1]'s structure. *)
Lemma remove_skip_skip_step: forall c1 c2 st1 st2,
remove_skip c1 = CSkip >
cstep (c1, st1) (c2, st2) >
st1 = st2 /\ remove_skip c2 = CSkip.
Proof.
intros.
revert st1 c2 st2 H0.
induction c1; try solve [inversion H]; intros.
+ inversion H0.
+ simpl in H.
destruct (remove_skip c1_1) eqn:?H; try solve [inversion H].
specialize (IHc1_1 eq_refl).
inversion H0; subst.
 specialize (IHc1_1 _ _ _ H3).
destruct IHc1_1.
split; [auto  simpl].
rewrite H4, H.
reflexivity.
 auto.
Qed.
(** The following lemma says, if one or zero step takes [ (c1, st) ] to [ (c1',
st') ], then one or zero step can also take [ (c1;; c2, st) ] to [ (c1';;c2,
st') ]. Its proof is straightforward. *)
Lemma cstep_or_not_congr_CSeq: forall c1 st c1' st' c2,
cstep_or_not (c1, st) (c1', st') >
cstep_or_not (c1;; c2, st) (c1';; c2, st').
Proof.
intros.
destruct H; [left  right].
+ injection H as ? ?; subst; reflexivity.
+ apply CS_SeqStep, H.
Qed.
(** The following lemma states the induction step of [CS_SeqStep]. Since
[[
match_com (c1_1;; c1_2) c2,
]]
either [c2] has a form of [c2_1 ;; c2_2] s.t.
 [match_com c1_1 c2_1];
 [remove_skip c1_2 = c2_2];
or [c2 = remove_skip c1_2] and [remove_skip c1_1 = CSkip]. We prove the
following lemma by such a case analysis. *)
Lemma R_simulate_L_CS_SeqStep: forall c1_1 c1_2 st c1_1' st' c2
(IH:
forall c2_1,
match_com c1_1 c2_1 >
exists c2_1',
match_com c1_1' c2_1' /\
cstep_or_not (c2_1, st) (c2_1', st')),
cstep (c1_1, st) (c1_1', st') >
match_com (c1_1;; c1_2) c2 >
exists c2',
match_com (c1_1';; c1_2) c2' /\
cstep_or_not (c2, st) (c2', st').
Proof.
intros.
simpl.
simpl in H0.
destruct H0 as [[? ?]  [c2_1 [? ?]]].
+ pose proof remove_skip_skip_step _ _ _ _ H0 H as [? ?].
subst st'.
exists c2.
split; [ left; reflexivity].
subst c2; simpl; rewrite H3.
auto.
+ specialize (IH _ H0) as [c2_1' [? ?]].
exists (c2_1' ;; remove_skip c1_2).
split; [right; eauto ].
subst c2.
apply cstep_or_not_congr_CSeq, H3.
Qed.
(** The following lemma proves the [CS_Seq] case. Its assumption
[[
match_com (CSkip;; c1_2) c2
]]
also means two possibilities:
 [ c2 = CSkip;; remove_skip c1_2];
 [ c2 = remove_skip c1_2]. *)
Lemma R_simulate_L_CS_Seq: forall c1_2 c2 st,
match_com (CSkip;; c1_2) c2 >
exists c2',
match_com c1_2 c2' /\
cstep_or_not (c2, st) (c2', st).
Proof.
intros.
destruct H as [[? ?]  [c2_1 [? ?]]].
+ clear H.
subst c2.
exists (remove_skip c1_2).
split; [apply match_com_remove_skip  left; reflexivity].
+ inversion H; subst.
clear H.
simpl (remove_skip Skip).
exists (remove_skip c1_2).
split; [apply match_com_remove_skip ].
right; apply CS_Seq.
Qed.
(** We then list other proof steps for building [R_simulate_L]. *)
Lemma R_simulate_L_CS_AssStep: forall X a a' c2 st,
astep st a a' >
match_com (CAss X a) c2 >
exists c2',
match_com (CAss X a') c2' /\
cstep_or_not (c2, st) (c2', st).
Proof.
intros.
inversion H0; subst; clear H0.
exists (X ::= a').
split; [constructor ].
right; constructor; tauto.
Qed.
Lemma R_simulate_L_CS_Ass: forall X n c2 st1 st2,
st2 X = n >
(forall Y : var, X <> Y > st1 Y = st2 Y) >
match_com (CAss X (ANum n)) c2 >
exists c2',
match_com CSkip c2' /\
cstep_or_not (c2, st1) (c2', st2).
Proof.
intros.
inversion H1; subst; clear H1.
exists CSkip.
split; [constructor ].
right; apply CS_Ass; tauto.
Qed.
Lemma R_simulate_L_CS_IfStep: forall b b' c1_1 c1_2 c2 st,
bstep st b b' >
match_com (If b Then c1_1 Else c1_2 EndIf) c2 >
exists c2',
match_com (If b' Then c1_1 Else c1_2 EndIf) c2' /\
cstep_or_not (c2, st) (c2', st).
Proof.
intros.
simpl.
simpl in H0.
destruct H0 as [c2_1 [? ?]].
subst c2.
exists (If b' Then c2_1 Else (remove_skip c1_2) EndIf).
split; [eauto ].
right; apply CS_IfStep, H.
Qed.
Lemma R_simulate_L_CS_IfTrue: forall c1_1 c1_2 c2 st,
match_com (If BTrue Then c1_1 Else c1_2 EndIf) c2 >
exists c2',
match_com c1_1 c2' /\
cstep_or_not (c2, st) (c2', st).
Proof.
intros.
simpl.
simpl in H.
destruct H as [c2_1 [? ?]].
subst c2.
exists c2_1.
split; [exact H ].
right; apply CS_IfTrue.
Qed.
Lemma R_simulate_L_CS_IfFalse: forall c1_1 c1_2 c2 st,
match_com (If BFalse Then c1_1 Else c1_2 EndIf) c2 >
exists c2',
match_com c1_2 c2' /\
cstep_or_not (c2, st) (c2', st).
Proof.
intros.
simpl.
simpl in H.
destruct H as [c2_1 [? ?]].
subst c2.
exists (remove_skip c1_2).
split; [apply match_com_remove_skip ].
right; apply CS_IfFalse.
Qed.
Lemma R_simulate_L_CS_While: forall b c1_1 c2 st,
match_com (While b Do c1_1 EndWhile) c2 >
exists c2',
match_com (If b Then c1_1;; While b Do c1_1 EndWhile Else Skip EndIf) c2' /\
cstep_or_not (c2, st) (c2', st).
Proof.
intros.
simpl in H.
subst c2.
exists (CIf b (remove_skip c1_1;; CWhile b (remove_skip c1_1)) Skip).
split.
+ exists (remove_skip c1_1;; CWhile b (remove_skip c1_1)).
split.
 right.
exists (remove_skip c1_1).
split; [apply match_com_remove_skip  reflexivity].
 reflexivity.
+ right.
apply CS_While.
Qed.
(** Adding these proof steps together, we can prove the simulation. *)
Theorem R_simulate_L: SimulationStatement_01 match_com.
(**
[[
forall c1 c2 c1' st st',
match_com c1 c2 >
cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\ cstep_or_not (c2, st) (c2', st').
]]
*)
Proof.
unfold SimulationStatement_01.
intros.
revert c2 H; induction_cstep H0; intros.
+ eapply R_simulate_L_CS_AssStep; eassumption.
+ eapply R_simulate_L_CS_Ass; eassumption.
+ eapply R_simulate_L_CS_SeqStep; eassumption.
+ eapply R_simulate_L_CS_Seq; eassumption.
+ eapply R_simulate_L_CS_IfStep; eassumption.
+ eapply R_simulate_L_CS_IfTrue; eassumption.
+ eapply R_simulate_L_CS_IfFalse; eassumption.
+ eapply R_simulate_L_CS_While; eassumption.
Qed.
End RemoveSkip.
(* ################################################################# *)
(** * Properties of Simulation Relations *)
(** Simulation relations describes correspondences between single steps.
Naturally, we would like to extend such discussion to multistep relations.
*)
Definition SimulationStatement_1 (match_com: com > com > Prop): Prop :=
forall c1 c2 c1' st st',
match_com c1 c2 >
cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\ cstep (c2, st) (c2', st').
Lemma multi_cstep_Simulation_1: forall match_com,
SimulationStatement_1 match_com >
forall c1 c2 c1' st st',
match_com c1 c2 >
multi_cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\ multi_cstep (c2, st) (c2', st').
Proof.
intros.
revert c2 H0; induction_1n H1; intros.
+ exists c2.
split; [exact H0  reflexivity].
+ pose proof H _ _ _ _ _ H2 H0.
destruct H3 as [c' [? ?]].
specialize (IHrt c' H3).
destruct IHrt as [c2' [? ?]].
exists c2'.
split; [exact H5 ].
etransitivity_1n; [exact H4  exact H6].
Qed.
(** The following lemma says, such properties about multistep relation also
holds for [SimulationStatement_01]. *)
Lemma multi_cstep_Simulation_01: forall match_com,
SimulationStatement_01 match_com >
forall c1 c2 c1' st st',
match_com c1 c2 >
multi_cstep (c1, st) (c1', st') >
exists c2',
match_com c1' c2' /\ multi_cstep (c2, st) (c2', st').
Proof.
intros.
revert c2 H0; induction_1n H1; intros.
+ exists c2.
split; [exact H0  reflexivity].
+ pose proof H _ _ _ _ _ H2 H0.
destruct H3 as [c' [? ?]].
specialize (IHrt c' H3).
destruct IHrt as [c2' [? ?]].
exists c2'.
split; [exact H5 ].
destruct H4.
 injection H4 as ? ?; subst.
exact H6.
 etransitivity_1n; [exact H4  exact H6].
Qed.
(* ################################################################# *)
(** * More Reading: Unused Assignments *)
(** Here we discuss another issue, unused assignments. For example,
[[
X ::= 1;; Y ::= 2;; X ::= 3
]]
can be optimized to
[[
Y ::= 2;; X ::= 3
]]
since the first assignment [X ::= 1] is an unused assignment. Every step in
the former program's execution will be simulated by the latter one, execept
those steps for the redundant assignment. However, we cannot prove a similar
simulation relation for it like [remove_skip].
[[
X ::= 1;; Y ::= 2;; X ::= 3 (State Match) Y ::= 2;; X ::= 3
  
   (no step)
v  
Skip;; Y ::= 2;; X ::= 3 (State Mismatch) Y ::= 2;; X ::= 3
  
   (no step)
v  
Y ::= 2;; X ::= 3 (State Mismatch) Y ::= 2;; X ::= 3
 
 
v v
Skip;; X ::= 3 (State Mismatch) Skip;; X ::= 3
 
 
v v
X ::= 3 (State Mismatch) X ::= 3
 
 
v v
Skip (State Match) Skip
]]
Thus, we need a different simulation statement.
*)
Definition SimulatationStatement_01'
(match_com_state: com * state > com * state > Prop) :=
forall cst1 cst2 cst1',
match_com_state cst1 cst2 >
cstep cst1 cst1' >
exists cst2',
match_com_state cst1' cst2' /\
cstep_or_not cst2 cst2'.
(* 20210329 18:49 *)