Theory Equivalence_Checking2
section ‹Deciding Equivalence of Extended Regular Expressions›
theory Equivalence_Checking2
imports Regular_Exp2 "HOL-Library.While_Combinator"
begin
subsection ‹Term ordering›
fun le_rexp :: "nat rexp ⇒ nat rexp ⇒ bool"
where
"le_rexp Zero _ = True"
| "le_rexp _ Zero = False"
| "le_rexp One _ = True"
| "le_rexp _ One = False"
| "le_rexp (Atom a) (Atom b) = (a <= b)"
| "le_rexp (Atom _) _ = True"
| "le_rexp _ (Atom _) = False"
| "le_rexp (Star r) (Star s) = le_rexp r s"
| "le_rexp (Star _) _ = True"
| "le_rexp _ (Star _) = False"
| "le_rexp (Not r) (Not s) = le_rexp r s"
| "le_rexp (Not _) _ = True"
| "le_rexp _ (Not _) = False"
| "le_rexp (Plus r r') (Plus s s') =
(if r = s then le_rexp r' s' else le_rexp r s)"
| "le_rexp (Plus _ _) _ = True"
| "le_rexp _ (Plus _ _) = False"
| "le_rexp (Times r r') (Times s s') =
(if r = s then le_rexp r' s' else le_rexp r s)"
| "le_rexp (Times _ _) _ = True"
| "le_rexp _ (Times _ _) = False"
| "le_rexp (Inter r r') (Inter s s') =
(if r = s then le_rexp r' s' else le_rexp r s)"
subsection ‹Normalizing operations›
text ‹associativity, commutativity, idempotence, zero›
fun nPlus :: "nat rexp ⇒ nat rexp ⇒ nat rexp"
where
"nPlus Zero r = r"
| "nPlus r Zero = r"
| "nPlus (Plus r s) t = nPlus r (nPlus s t)"
| "nPlus r (Plus s t) =
(if r = s then (Plus s t)
else if le_rexp r s then Plus r (Plus s t)
else Plus s (nPlus r t))"
| "nPlus r s =
(if r = s then r
else if le_rexp r s then Plus r s
else Plus s r)"
lemma lang_nPlus[simp]: "lang S (nPlus r s) = lang S (Plus r s)"
by (induct r s rule: nPlus.induct) auto
text ‹associativity, zero, one›
fun nTimes :: "nat rexp ⇒ nat rexp ⇒ nat rexp"
where
"nTimes Zero _ = Zero"
| "nTimes _ Zero = Zero"
| "nTimes One r = r"
| "nTimes r One = r"
| "nTimes (Times r s) t = Times r (nTimes s t)"
| "nTimes r s = Times r s"
lemma lang_nTimes[simp]: "lang S (nTimes r s) = lang S (Times r s)"
by (induct r s rule: nTimes.induct) (auto simp: conc_assoc)
text ‹more optimisations:›
fun nInter :: "nat rexp ⇒ nat rexp ⇒ nat rexp"
where
"nInter Zero _ = Zero"
| "nInter _ Zero = Zero"
| "nInter r s = Inter r s"
lemma lang_nInter[simp]: "lang S (nInter r s) = lang S (Inter r s)"
by (induct r s rule: nInter.induct) (auto)
primrec norm :: "nat rexp ⇒ nat rexp"
where
"norm Zero = Zero"
| "norm One = One"
| "norm (Atom a) = Atom a"
| "norm (Plus r s) = nPlus (norm r) (norm s)"
| "norm (Times r s) = nTimes (norm r) (norm s)"
| "norm (Star r) = Star (norm r)"
| "norm (Not r) = Not (norm r)"
| "norm (Inter r1 r2) = nInter (norm r1) (norm r2)"
lemma lang_norm[simp]: "lang S (norm r) = lang S r"
by (induct r) auto
subsection ‹Derivative›
primrec nderiv :: "nat ⇒ nat rexp ⇒ nat rexp"
where
"nderiv _ Zero = Zero"
| "nderiv _ One = Zero"
| "nderiv a (Atom b) = (if a = b then One else Zero)"
| "nderiv a (Plus r s) = nPlus (nderiv a r) (nderiv a s)"
| "nderiv a (Times r s) =
(let r's = nTimes (nderiv a r) s
in if nullable r then nPlus r's (nderiv a s) else r's)"
| "nderiv a (Star r) = nTimes (nderiv a r) (Star r)"
| "nderiv a (Not r) = Not (nderiv a r)"
| "nderiv a (Inter r1 r2) = nInter (nderiv a r1) (nderiv a r2)"
lemma lang_nderiv: "a:S ⟹ lang S (nderiv a r) = Deriv a (lang S r)"
by (induct r) (auto simp: Let_def nullable_iff[where S=S])
lemma atoms_nPlus[simp]: "atoms (nPlus r s) = atoms r ∪ atoms s"
by (induct r s rule: nPlus.induct) auto
lemma atoms_nTimes: "atoms (nTimes r s) ⊆ atoms r ∪ atoms s"
by (induct r s rule: nTimes.induct) auto
lemma atoms_nInter: "atoms (nInter r s) ⊆ atoms r ∪ atoms s"
by (induct r s rule: nInter.induct) auto
lemma atoms_norm: "atoms (norm r) ⊆ atoms r"
by (induct r) (auto dest!:subsetD[OF atoms_nTimes]subsetD[OF atoms_nInter])
lemma atoms_nderiv: "atoms (nderiv a r) ⊆ atoms r"
by (induct r) (auto simp: Let_def dest!:subsetD[OF atoms_nTimes]subsetD[OF atoms_nInter])
subsection ‹Bisimulation between languages and regular expressions›
context
fixes S :: "'a set"
begin
coinductive bisimilar :: "'a lang ⇒ 'a lang ⇒ bool" where
"K ⊆ lists S ⟹ L ⊆ lists S
⟹ ([] ∈ K ⟷ [] ∈ L)
⟹ (⋀x. x:S ⟹ bisimilar (Deriv x K) (Deriv x L))
⟹ bisimilar K L"
lemma equal_if_bisimilar:
assumes "K ⊆ lists S" "L ⊆ lists S" "bisimilar K L" shows "K = L"
proof (rule set_eqI)
fix w
from assms show "w ∈ K ⟷ w ∈ L"
proof (induction w arbitrary: K L)
case Nil thus ?case by (auto elim: bisimilar.cases)
next
case (Cons a w K L)
show ?case
proof cases
assume "a : S"
with ‹bisimilar K L› have "bisimilar (Deriv a K) (Deriv a L)"
by (auto elim: bisimilar.cases)
then have "w ∈ Deriv a K ⟷ w ∈ Deriv a L"
by (metis Cons.IH bisimilar.cases)
thus ?case by (auto simp: Deriv_def)
next
assume "a ∉ S"
thus ?case using Cons.prems by auto
qed
qed
qed
lemma language_coinduct:
fixes R (infixl "∼" 50)
assumes "⋀K L. K ∼ L ⟹ K ⊆ lists S ∧ L ⊆ lists S"
assumes "K ∼ L"
assumes "⋀K L. K ∼ L ⟹ ([] ∈ K ⟷ [] ∈ L)"
assumes "⋀K L x. K ∼ L ⟹ x : S ⟹ Deriv x K ∼ Deriv x L"
shows "K = L"
apply (rule equal_if_bisimilar)
apply (metis assms(1) assms(2))
apply (metis assms(1) assms(2))
apply (rule bisimilar.coinduct[of R, OF ‹K ∼ L›])
apply (auto simp: assms)
done
end
type_synonym rexp_pair = "nat rexp * nat rexp"
type_synonym rexp_pairs = "rexp_pair list"
definition is_bisimulation :: "nat list ⇒ rexp_pairs ⇒ bool"
where
"is_bisimulation as ps =
(∀(r,s)∈ set ps. (atoms r ∪ atoms s ⊆ set as) ∧ (nullable r ⟷ nullable s) ∧
(∀a∈set as. (nderiv a r, nderiv a s) ∈ set ps))"
lemma bisim_lang_eq:
assumes bisim: "is_bisimulation as ps"
assumes "(r, s) ∈ set ps"
shows "lang (set as) r = lang (set as) s"
proof -
let ?R = "λK L. (∃(r,s)∈set ps. K = lang (set as) r ∧ L = lang (set as) s)"
show ?thesis
proof (rule language_coinduct[where R="?R" and S = "set as"])
from ‹(r, s) ∈ set ps› show "?R (lang (set as) r) (lang (set as) s)"
by auto
next
fix K L assume "?R K L"
then obtain r s where rs: "(r, s) ∈ set ps"
and KL: "K = lang (set as) r" "L = lang (set as) s" by auto
with bisim have "nullable r ⟷ nullable s"
by (auto simp: is_bisimulation_def)
thus "[] ∈ K ⟷ [] ∈ L" by (auto simp: nullable_iff[where S="set as"] KL)
txt‹next case, but shared context›
from bisim rs KL lang_subset_lists[of _ "set as"]
show "K ⊆ lists (set as) ∧ L ⊆ lists (set as)"
unfolding is_bisimulation_def by blast
txt‹next case, but shared context›
fix a assume "a ∈ set as"
with rs bisim
have "(nderiv a r, nderiv a s) ∈ set ps"
by (auto simp: is_bisimulation_def)
thus "?R (Deriv a K) (Deriv a L)" using ‹a ∈ set as›
by (force simp: KL lang_nderiv)
qed
qed
subsection ‹Closure computation›
fun test :: "rexp_pairs * rexp_pairs ⇒ bool"
where "test (ws, ps) = (case ws of [] ⇒ False | (p,q)#_ ⇒ nullable p = nullable q)"
fun step :: "nat list ⇒ rexp_pairs * rexp_pairs ⇒ rexp_pairs * rexp_pairs"
where "step as (ws,ps) =
(let
(r, s) = hd ws;
ps' = (r, s) # ps;
succs = map (λa. (nderiv a r, nderiv a s)) as;
new = filter (λp. p ∉ set ps' ∪ set ws) succs
in (new @ tl ws, ps'))"
definition closure ::
"nat list ⇒ rexp_pairs * rexp_pairs
⇒ (rexp_pairs * rexp_pairs) option" where
"closure as = while_option test (step as)"
definition pre_bisim :: "nat list ⇒ nat rexp ⇒ nat rexp ⇒
rexp_pairs * rexp_pairs ⇒ bool"
where
"pre_bisim as r s = (λ(ws,ps).
((r, s) ∈ set ws ∪ set ps) ∧
(∀(r,s)∈ set ws ∪ set ps. atoms r ∪ atoms s ⊆ set as) ∧
(∀(r,s)∈ set ps. (nullable r ⟷ nullable s) ∧
(∀a∈set as. (nderiv a r, nderiv a s) ∈ set ps ∪ set ws)))"
theorem closure_sound:
assumes result: "closure as ([(r,s)],[]) = Some([],ps)"
and atoms: "atoms r ∪ atoms s ⊆ set as"
shows "lang (set as) r = lang (set as) s"
proof-
{ fix st have "pre_bisim as r s st ⟹ test st ⟹ pre_bisim as r s (step as st)"
unfolding pre_bisim_def
by (cases st) (auto simp: split_def split: list.splits prod.splits
dest!: subsetD[OF atoms_nderiv]) }
moreover
from atoms
have "pre_bisim as r s ([(r,s)],[])" by (simp add: pre_bisim_def)
ultimately have pre_bisim_ps: "pre_bisim as r s ([],ps)"
by (rule while_option_rule[OF _ result[unfolded closure_def]])
then have "is_bisimulation as ps" "(r, s) ∈ set ps"
by (auto simp: pre_bisim_def is_bisimulation_def)
thus "lang (set as) r = lang (set as) s" by (rule bisim_lang_eq)
qed
subsection ‹The overall procedure›
primrec add_atoms :: "nat rexp ⇒ nat list ⇒ nat list"
where
"add_atoms Zero = id"
| "add_atoms One = id"
| "add_atoms (Atom a) = List.insert a"
| "add_atoms (Plus r s) = add_atoms s o add_atoms r"
| "add_atoms (Times r s) = add_atoms s o add_atoms r"
| "add_atoms (Not r) = add_atoms r"
| "add_atoms (Inter r s) = add_atoms s o add_atoms r"
| "add_atoms (Star r) = add_atoms r"
lemma set_add_atoms: "set (add_atoms r as) = atoms r ∪ set as"
by (induct r arbitrary: as) auto
definition check_eqv :: "nat list ⇒ nat rexp ⇒ nat rexp ⇒ bool"
where
"check_eqv as r s ⟷ set(add_atoms r (add_atoms s [])) ⊆ set as ∧
(case closure as ([(norm r, norm s)], []) of
Some([],_) ⇒ True | _ ⇒ False)"
lemma soundness:
assumes "check_eqv as r s" shows "lang (set as) r = lang (set as) s"
proof -
obtain ps where cl: "closure as ([(norm r,norm s)],[]) = Some([],ps)"
and at: "atoms r ∪ atoms s ⊆ set as"
using assms
by (auto simp: check_eqv_def set_add_atoms split:option.splits list.splits)
hence "atoms(norm r) ∪ atoms(norm s) ⊆ set as"
using atoms_norm by blast
hence "lang (set as) (norm r) = lang (set as) (norm s)"
by (rule closure_sound[OF cl])
thus "lang (set as) r = lang (set as) s" by simp
qed
lemma "check_eqv [0] (Plus One (Times (Atom 0) (Star(Atom 0)))) (Star(Atom 0))"
by eval
lemma "check_eqv [0,1] (Not(Atom 0))
(Plus One (Times (Plus (Atom 1) (Times (Atom 0) (Plus (Atom 0) (Atom 1))))
(Star(Plus (Atom 0) (Atom 1)))))"
by eval
lemma "check_eqv [0] (Atom 0) (Inter (Star (Atom 0)) (Atom 0))"
by eval
end