EquivProgram Equivalence
Behavioral Equivalence
Definitions
Definition aequiv (a1 a2 : aexp) : Prop :=
∀ (st : state),
aeval st a1 = aeval st a2.
Definition bequiv (b1 b2 : bexp) : Prop :=
∀ (st : state),
beval st b1 = beval st b2.
Theorem aequiv_example: aequiv <{ X - X }> <{ 0 }>.
Theorem bequiv_example: bequiv <{ X - X = 0 }> <{ true }>.
∀ (st : state),
aeval st a1 = aeval st a2.
Definition bequiv (b1 b2 : bexp) : Prop :=
∀ (st : state),
beval st b1 = beval st b2.
Theorem aequiv_example: aequiv <{ X - X }> <{ 0 }>.
Theorem bequiv_example: bequiv <{ X - X = 0 }> <{ true }>.
Definition cequiv (c1 c2 : com) : Prop :=
∀ (st st' : state),
(st =[ c1 ]=> st') ↔ (st =[ c2 ]=> st').
∀ (st st' : state),
(st =[ c1 ]=> st') ↔ (st =[ c2 ]=> st').
Are these two programs equivalent?
X := 1;
Y := 2 and
Y := 2;
X := 1
(1) Yes
(2) No
(3) Not sure
X := 1;
Y := 2 and
Y := 2;
X := 1
What about these?
X := 1;
Y := 2 and
X := 2;
Y := 1
(1) Yes
(2) No
(3) Not sure
X := 1;
Y := 2 and
X := 2;
Y := 1
What about these?
while 1 ≤ X do
X := X + 1
end and
while 2 ≤ X do
X := X + 1
end
(1) Yes
(2) No
(3) Not sure
while 1 ≤ X do
X := X + 1
end and
while 2 ≤ X do
X := X + 1
end
These?
while true do
while false do X := X + 1 end
end and
while false do
while true do X := X + 1 end
end
(1) Yes
(2) No
(3) Not sure
while true do
while false do X := X + 1 end
end and
while false do
while true do X := X + 1 end
end
Simple Examples
Theorem skip_left : ∀ c,
cequiv
<{ skip; c }>
c.
Proof.
(* WORK IN CLASS *) Admitted.
cequiv
<{ skip; c }>
c.
Proof.
(* WORK IN CLASS *) Admitted.
Theorem if_true_simple : ∀ c1 c2,
cequiv
<{ if true then c1 else c2 end }>
c1.
cequiv
<{ if true then c1 else c2 end }>
c1.
Theorem if_true: ∀ b c1 c2,
bequiv b <{true}> →
cequiv
<{ if b then c1 else c2 end }>
c1.
bequiv b <{true}> →
cequiv
<{ if b then c1 else c2 end }>
c1.
Similarly:
Theorem if_false : ∀ b c1 c2,
bequiv b <{false}> →
cequiv
<{ if b then c1 else c2 end }>
c2.
bequiv b <{false}> →
cequiv
<{ if b then c1 else c2 end }>
c2.
Theorem while_false : ∀ b c,
bequiv b <{false}> →
cequiv
<{ while b do c end }>
<{ skip }>.
bequiv b <{false}> →
cequiv
<{ while b do c end }>
<{ skip }>.
Lemma while_true_nonterm : ∀ b c st st',
bequiv b <{true}> →
~( st =[ while b do c end ]=> st' ).
Proof.
(* WORK IN CLASS *) Admitted.
Theorem while_true : ∀ b c,
bequiv b <{true}> →
cequiv
<{ while b do c end }>
<{ while true do skip end }>.
bequiv b <{true}> →
~( st =[ while b do c end ]=> st' ).
Proof.
(* WORK IN CLASS *) Admitted.
Theorem while_true : ∀ b c,
bequiv b <{true}> →
cequiv
<{ while b do c end }>
<{ while true do skip end }>.
Theorem loop_unrolling : ∀ b c,
cequiv
<{ while b do c end }>
<{ if b then c ; while b do c end else skip end }>.
Proof.
(* WORK IN CLASS *) Admitted.
cequiv
<{ while b do c end }>
<{ if b then c ; while b do c end else skip end }>.
Proof.
(* WORK IN CLASS *) Admitted.
Properties of Behavioral Equivalence
Behavioral Equivalence Is an Equivalence
Lemma refl_aequiv : ∀ (a : aexp), aequiv a a.
Lemma sym_aequiv : ∀ (a1 a2 : aexp),
aequiv a1 a2 → aequiv a2 a1.
Lemma trans_aequiv : ∀ (a1 a2 a3 : aexp),
aequiv a1 a2 → aequiv a2 a3 → aequiv a1 a3.
Lemma refl_bequiv : ∀ (b : bexp), bequiv b b.
Lemma sym_bequiv : ∀ (b1 b2 : bexp),
bequiv b1 b2 → bequiv b2 b1.
Lemma trans_bequiv : ∀ (b1 b2 b3 : bexp),
bequiv b1 b2 → bequiv b2 b3 → bequiv b1 b3.
Lemma refl_cequiv : ∀ (c : com), cequiv c c.
Lemma sym_cequiv : ∀ (c1 c2 : com),
cequiv c1 c2 → cequiv c2 c1.
Lemma trans_cequiv : ∀ (c1 c2 c3 : com),
cequiv c1 c2 → cequiv c2 c3 → cequiv c1 c3.
Lemma sym_aequiv : ∀ (a1 a2 : aexp),
aequiv a1 a2 → aequiv a2 a1.
Lemma trans_aequiv : ∀ (a1 a2 a3 : aexp),
aequiv a1 a2 → aequiv a2 a3 → aequiv a1 a3.
Lemma refl_bequiv : ∀ (b : bexp), bequiv b b.
Lemma sym_bequiv : ∀ (b1 b2 : bexp),
bequiv b1 b2 → bequiv b2 b1.
Lemma trans_bequiv : ∀ (b1 b2 b3 : bexp),
bequiv b1 b2 → bequiv b2 b3 → bequiv b1 b3.
Lemma refl_cequiv : ∀ (c : com), cequiv c c.
Lemma sym_cequiv : ∀ (c1 c2 : com),
cequiv c1 c2 → cequiv c2 c1.
Lemma trans_cequiv : ∀ (c1 c2 c3 : com),
cequiv c1 c2 → cequiv c2 c3 → cequiv c1 c3.
Behavioral Equivalence Is a Congruence
aequiv a a' | |
cequiv (x := a) (x := a') |
cequiv c1 c1' | |
cequiv c2 c2' | |
cequiv (c1;c2) (c1';c2') |
These properties allow us to reason that a large program behaves the same when we replace a small part with something equivalent.
Theorem CAsgn_congruence : ∀ x a a',
aequiv a a' →
cequiv <{x := a}> <{x := a'}>.
Theorem CWhile_congruence : ∀ b b' c c',
bequiv b b' → cequiv c c' →
cequiv <{ while b do c end }> <{ while b' do c' end }>.
Proof.
(* WORK IN CLASS *) Admitted.
Theorem CSeq_congruence : ∀ c1 c1' c2 c2',
cequiv c1 c1' → cequiv c2 c2' →
cequiv <{ c1;c2 }> <{ c1';c2' }>.
Theorem CIf_congruence : ∀ b b' c1 c1' c2 c2',
bequiv b b' → cequiv c1 c1' → cequiv c2 c2' →
cequiv <{ if b then c1 else c2 end }>
<{ if b' then c1' else c2' end }>.
aequiv a a' →
cequiv <{x := a}> <{x := a'}>.
Theorem CWhile_congruence : ∀ b b' c c',
bequiv b b' → cequiv c c' →
cequiv <{ while b do c end }> <{ while b' do c' end }>.
Proof.
(* WORK IN CLASS *) Admitted.
Theorem CSeq_congruence : ∀ c1 c1' c2 c2',
cequiv c1 c1' → cequiv c2 c2' →
cequiv <{ c1;c2 }> <{ c1';c2' }>.
Theorem CIf_congruence : ∀ b b' c1 c1' c2 c2',
bequiv b b' → cequiv c1 c1' → cequiv c2 c2' →
cequiv <{ if b then c1 else c2 end }>
<{ if b' then c1' else c2' end }>.
Example congruence_example:
cequiv
(* Program 1: *)
<{ X := 0;
if (X = 0)
then
Y := 0
else
Y := 42
end }>
(* Program 2: *)
<{ X := 0;
if (X = 0)
then
Y := X - X (* <--- Changed here *)
else
Y := 42
end }>.
Proof.
apply CSeq_congruence.
- apply refl_cequiv.
- apply CIf_congruence.
+ apply refl_bequiv.
+ apply CAsgn_congruence. unfold aequiv. simpl.
symmetry. apply minus_diag.
+ apply refl_cequiv.
Qed.
cequiv
(* Program 1: *)
<{ X := 0;
if (X = 0)
then
Y := 0
else
Y := 42
end }>
(* Program 2: *)
<{ X := 0;
if (X = 0)
then
Y := X - X (* <--- Changed here *)
else
Y := 42
end }>.
Proof.
apply CSeq_congruence.
- apply refl_cequiv.
- apply CIf_congruence.
+ apply refl_bequiv.
+ apply CAsgn_congruence. unfold aequiv. simpl.
symmetry. apply minus_diag.
+ apply refl_cequiv.
Qed.
Program Transformations
Definition atrans_sound (atrans : aexp → aexp) : Prop :=
∀ (a : aexp),
aequiv a (atrans a).
Definition btrans_sound (btrans : bexp → bexp) : Prop :=
∀ (b : bexp),
bequiv b (btrans b).
Definition ctrans_sound (ctrans : com → com) : Prop :=
∀ (c : com),
cequiv c (ctrans c).
∀ (a : aexp),
aequiv a (atrans a).
Definition btrans_sound (btrans : bexp → bexp) : Prop :=
∀ (b : bexp),
bequiv b (btrans b).
Definition ctrans_sound (ctrans : com → com) : Prop :=
∀ (c : com),
cequiv c (ctrans c).
The Constant-Folding Transformation
Fixpoint fold_constants_aexp (a : aexp) : aexp :=
match a with
| ANum n ⇒ ANum n
| AId x ⇒ AId x
| <{ a1 + a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2)
with
| (ANum n1, ANum n2) ⇒ ANum (n1 + n2)
| (a1', a2') ⇒ <{ a1' + a2' }>
end
| <{ a1 - a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2)
with
| (ANum n1, ANum n2) ⇒ ANum (n1 - n2)
| (a1', a2') ⇒ <{ a1' - a2' }>
end
| <{ a1 × a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2)
with
| (ANum n1, ANum n2) ⇒ ANum (n1 × n2)
| (a1', a2') ⇒ <{ a1' × a2' }>
end
end.
match a with
| ANum n ⇒ ANum n
| AId x ⇒ AId x
| <{ a1 + a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2)
with
| (ANum n1, ANum n2) ⇒ ANum (n1 + n2)
| (a1', a2') ⇒ <{ a1' + a2' }>
end
| <{ a1 - a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2)
with
| (ANum n1, ANum n2) ⇒ ANum (n1 - n2)
| (a1', a2') ⇒ <{ a1' - a2' }>
end
| <{ a1 × a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2)
with
| (ANum n1, ANum n2) ⇒ ANum (n1 × n2)
| (a1', a2') ⇒ <{ a1' × a2' }>
end
end.
Example fold_aexp_ex1 :
fold_constants_aexp <{ (1 + 2) × X }>
= <{ 3 × X }>.
fold_constants_aexp <{ (1 + 2) × X }>
= <{ 3 × X }>.
Note that this version of constant folding doesn't eliminate
trivial additions, etc. -- we are focusing attention on a single
optimization for the sake of simplicity. It is not hard to
incorporate other ways of simplifying expressions; the definitions
and proofs just get longer.
Example fold_aexp_ex2 :
fold_constants_aexp <{ X - ((0 × 6) + Y) }> = <{ X - (0 + Y) }>.
fold_constants_aexp <{ X - ((0 × 6) + Y) }> = <{ X - (0 + Y) }>.
Not only can we lift fold_constants_aexp to bexps (in the BEq and BLe cases); we can also look for constant boolean expressions and evaluate them in-place.
Fixpoint fold_constants_bexp (b : bexp) : bexp :=
match b with
| <{true}> ⇒ <{true}>
| <{false}> ⇒ <{false}>
| <{ a1 = a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2) with
| (ANum n1, ANum n2) ⇒
if n1 =? n2 then <{true}> else <{false}>
| (a1', a2') ⇒
<{ a1' = a2' }>
end
| <{ a1 ≤ a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2) with
| (ANum n1, ANum n2) ⇒
if n1 <=? n2 then <{true}> else <{false}>
| (a1', a2') ⇒
<{ a1' ≤ a2' }>
end
| <{ ¬b1 }> ⇒
match (fold_constants_bexp b1) with
| <{true}> ⇒ <{false}>
| <{false}> ⇒ <{true}>
| b1' ⇒ <{ ¬b1' }>
end
| <{ b1 && b2 }> ⇒
match (fold_constants_bexp b1,
fold_constants_bexp b2) with
| (<{true}>, <{true}>) ⇒ <{true}>
| (<{true}>, <{false}>) ⇒ <{false}>
| (<{false}>, <{true}>) ⇒ <{false}>
| (<{false}>, <{false}>) ⇒ <{false}>
| (b1', b2') ⇒ <{ b1' && b2' }>
end
end.
match b with
| <{true}> ⇒ <{true}>
| <{false}> ⇒ <{false}>
| <{ a1 = a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2) with
| (ANum n1, ANum n2) ⇒
if n1 =? n2 then <{true}> else <{false}>
| (a1', a2') ⇒
<{ a1' = a2' }>
end
| <{ a1 ≤ a2 }> ⇒
match (fold_constants_aexp a1,
fold_constants_aexp a2) with
| (ANum n1, ANum n2) ⇒
if n1 <=? n2 then <{true}> else <{false}>
| (a1', a2') ⇒
<{ a1' ≤ a2' }>
end
| <{ ¬b1 }> ⇒
match (fold_constants_bexp b1) with
| <{true}> ⇒ <{false}>
| <{false}> ⇒ <{true}>
| b1' ⇒ <{ ¬b1' }>
end
| <{ b1 && b2 }> ⇒
match (fold_constants_bexp b1,
fold_constants_bexp b2) with
| (<{true}>, <{true}>) ⇒ <{true}>
| (<{true}>, <{false}>) ⇒ <{false}>
| (<{false}>, <{true}>) ⇒ <{false}>
| (<{false}>, <{false}>) ⇒ <{false}>
| (b1', b2') ⇒ <{ b1' && b2' }>
end
end.
Example fold_bexp_ex1 :
fold_constants_bexp <{ true && ~(false && true) }>
= <{ true }>.
Example fold_bexp_ex2 :
fold_constants_bexp <{ (X = Y) && (0 = (2 - (1 + 1))) }>
= <{ (X = Y) && true }>.
fold_constants_bexp <{ true && ~(false && true) }>
= <{ true }>.
Example fold_bexp_ex2 :
fold_constants_bexp <{ (X = Y) && (0 = (2 - (1 + 1))) }>
= <{ (X = Y) && true }>.
To fold constants in a command, we apply the appropriate folding functions on all embedded expressions.
Fixpoint fold_constants_com (c : com) : com :=
match c with
| <{ skip }> ⇒
<{ skip }>
| <{ x := a }> ⇒
<{ x := (fold_constants_aexp a) }>
| <{ c1 ; c2 }> ⇒
<{ fold_constants_com c1 ; fold_constants_com c2 }>
| <{ if b then c1 else c2 end }> ⇒
match fold_constants_bexp b with
| <{true}> ⇒ fold_constants_com c1
| <{false}> ⇒ fold_constants_com c2
| b' ⇒ <{ if b' then fold_constants_com c1
else fold_constants_com c2 end}>
end
| <{ while b do c1 end }> ⇒
match fold_constants_bexp b with
| <{true}> ⇒ <{ while true do skip end }>
| <{false}> ⇒ <{ skip }>
| b' ⇒ <{ while b' do (fold_constants_com c1) end }>
end
end.
match c with
| <{ skip }> ⇒
<{ skip }>
| <{ x := a }> ⇒
<{ x := (fold_constants_aexp a) }>
| <{ c1 ; c2 }> ⇒
<{ fold_constants_com c1 ; fold_constants_com c2 }>
| <{ if b then c1 else c2 end }> ⇒
match fold_constants_bexp b with
| <{true}> ⇒ fold_constants_com c1
| <{false}> ⇒ fold_constants_com c2
| b' ⇒ <{ if b' then fold_constants_com c1
else fold_constants_com c2 end}>
end
| <{ while b do c1 end }> ⇒
match fold_constants_bexp b with
| <{true}> ⇒ <{ while true do skip end }>
| <{false}> ⇒ <{ skip }>
| b' ⇒ <{ while b' do (fold_constants_com c1) end }>
end
end.
Example fold_com_ex1 :
fold_constants_com
(* Original program: *)
<{ X := 4 + 5;
Y := X - 3;
if ((X - Y) = (2 + 4)) then skip
else Y := 0 end;
if (0 ≤ (4 - (2 + 1))) then Y := 0
else skip end;
while (Y = 0) do
X := X + 1
end }>
= (* After constant folding: *)
<{ X := 9;
Y := X - 3;
if ((X - Y) = 6) then skip
else Y := 0 end;
Y := 0;
while (Y = 0) do
X := X + 1
end }>.
fold_constants_com
(* Original program: *)
<{ X := 4 + 5;
Y := X - 3;
if ((X - Y) = (2 + 4)) then skip
else Y := 0 end;
if (0 ≤ (4 - (2 + 1))) then Y := 0
else skip end;
while (Y = 0) do
X := X + 1
end }>
= (* After constant folding: *)
<{ X := 9;
Y := X - 3;
if ((X - Y) = 6) then skip
else Y := 0 end;
Y := 0;
while (Y = 0) do
X := X + 1
end }>.
Theorem fold_constants_aexp_sound :
atrans_sound fold_constants_aexp.
Theorem fold_constants_bexp_sound:
btrans_sound fold_constants_bexp.
Theorem fold_constants_com_sound :
ctrans_sound fold_constants_com.
atrans_sound fold_constants_aexp.
Theorem fold_constants_bexp_sound:
btrans_sound fold_constants_bexp.
Theorem fold_constants_com_sound :
ctrans_sound fold_constants_com.
Proving Inequivalence
c1 = (X := 42 + 53;
Y := Y + X)
c2 = (X := 42 + 53;
Y := Y + (42 + 53)) Clearly, this particular c1 and c2 are equivalent. Is this true in general?
Fixpoint subst_aexp (x : string) (u : aexp) (a : aexp) : aexp :=
match a with
| ANum n ⇒
ANum n
| AId x' ⇒
if eqb_string x x' then u else AId x'
| <{ a1 + a2 }> ⇒
<{ (subst_aexp x u a1) + (subst_aexp x u a2) }>
| <{ a1 - a2 }> ⇒
<{ (subst_aexp x u a1) - (subst_aexp x u a2) }>
| <{ a1 × a2 }> ⇒
<{ (subst_aexp x u a1) × (subst_aexp x u a2) }>
end.
Example subst_aexp_ex :
subst_aexp X <{42 + 53}> <{Y + X}>
= <{ Y + (42 + 53)}>.
match a with
| ANum n ⇒
ANum n
| AId x' ⇒
if eqb_string x x' then u else AId x'
| <{ a1 + a2 }> ⇒
<{ (subst_aexp x u a1) + (subst_aexp x u a2) }>
| <{ a1 - a2 }> ⇒
<{ (subst_aexp x u a1) - (subst_aexp x u a2) }>
| <{ a1 × a2 }> ⇒
<{ (subst_aexp x u a1) × (subst_aexp x u a2) }>
end.
Example subst_aexp_ex :
subst_aexp X <{42 + 53}> <{Y + X}>
= <{ Y + (42 + 53)}>.
Definition subst_equiv_property := ∀ x1 x2 a1 a2,
cequiv <{ x1 := a1; x2 := a2 }>
<{ x1 := a1; x2 := subst_aexp x1 a1 a2 }>.
cequiv <{ x1 := a1; x2 := a2 }>
<{ x1 := a1; x2 := subst_aexp x1 a1 a2 }>.
Sadly, the property does not always hold.
X := X + 1; Y := X If we perform the substitution, we get
X := X + 1; Y := X + 1 which clearly isn't equivalent to the original program. ☐
Theorem subst_inequiv :
¬subst_equiv_property.
¬subst_equiv_property.