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