Theory OFSM_Tables_Refined
section ‹Alternative OFSM Table Computation›
text ‹The approach to computing OFSM tables presented in the imported theories is easy to use in
proofs but inefficient in practice due to repeated recomputation of the same tables.
Thus, in the following we present a more efficient method for computing and storing tables.›
theory OFSM_Tables_Refined
imports Minimisation Distinguishability
begin
subsection ‹Computing a List of all OFSM Tables›
type_synonym ('a,'b,'c) ofsm_table = "('a, 'a set) mapping"
fun initial_ofsm_table :: "('a::linorder,'b,'c) fsm ⇒ ('a,'b,'c) ofsm_table" where
"initial_ofsm_table M = Mapping.tabulate (states_as_list M) (λq . states M)"
abbreviation "ofsm_lookup ≡ Mapping.lookup_default {}"
lemma initial_ofsm_table_lookup_invar: "ofsm_lookup (initial_ofsm_table M) q = ofsm_table M (λq . states M) 0 q"
proof (cases "q ∈ states M")
case True
then have "q ∈ list.set (states_as_list M)"
using states_as_list_set by auto
then have "Mapping.lookup (initial_ofsm_table M) q = Some (states M)"
unfolding initial_ofsm_table.simps
by (simp add: lookup_tabulate)
then have "ofsm_lookup (initial_ofsm_table M) q = states M"
by (simp add: lookup_default_def)
then show ?thesis
using True by auto
next
case False
then have "q ∉ list.set (states_as_list M)"
using states_as_list_set by auto
then have "Mapping.lookup (initial_ofsm_table M) q = None"
unfolding initial_ofsm_table.simps
by (simp add: lookup_tabulate)
then have "ofsm_lookup (initial_ofsm_table M) q = {}"
by (simp add: lookup_default_def)
then show ?thesis
using False by auto
qed
lemma initial_ofsm_table_keys_invar: "Mapping.keys (initial_ofsm_table M) = states M"
using states_as_list_set[of M]
by simp
fun next_ofsm_table :: "('a::linorder,'b,'c) fsm ⇒ ('a,'b,'c) ofsm_table ⇒ ('a,'b,'c) ofsm_table" where
"next_ofsm_table M prev_table = Mapping.tabulate (states_as_list M) (λ q . {q' ∈ ofsm_lookup prev_table q . ∀ x ∈ inputs M . ∀ y ∈ outputs M . (case h_obs M q x y of Some qT ⇒ (case h_obs M q' x y of Some qT' ⇒ ofsm_lookup prev_table qT = ofsm_lookup prev_table qT' | None ⇒ False) | None ⇒ h_obs M q' x y = None) })"
lemma h_obs_non_state :
assumes "q ∉ states M"
shows "h_obs M q x y = None"
proof -
have *:"⋀ x . h M (q,x) = {}"
using assms fsm_transition_source
unfolding h_simps
by force
show ?thesis
unfolding h_obs_simps Let_def *
by (simp add: Set.filter_def)
qed
lemma next_ofsm_table_lookup_invar:
assumes "⋀ q . ofsm_lookup prev_table q = ofsm_table M (λq . states M) k q"
shows "ofsm_lookup (next_ofsm_table M prev_table) q = ofsm_table M (λq . states M) (Suc k) q"
proof (cases "q ∈ states M")
case True
let ?prev_table = "ofsm_table M (λq . states M) k"
from True have "q ∈ list.set (states_as_list M)"
using states_as_list_set by auto
then have "Mapping.lookup (next_ofsm_table M prev_table) q = Some {q' ∈ ofsm_lookup prev_table q . ∀ x ∈ inputs M . ∀ y ∈ outputs M . (case h_obs M q x y of Some qT ⇒ (case h_obs M q' x y of Some qT' ⇒ ofsm_lookup prev_table qT = ofsm_lookup prev_table qT' | None ⇒ False) | None ⇒ h_obs M q' x y = None) }"
unfolding next_ofsm_table.simps
by (meson lookup_tabulate states_as_list_distinct)
then have "ofsm_lookup (next_ofsm_table M prev_table) q = {q' ∈ ofsm_lookup prev_table q . ∀ x ∈ inputs M . ∀ y ∈ outputs M . (case h_obs M q x y of Some qT ⇒ (case h_obs M q' x y of Some qT' ⇒ ofsm_lookup prev_table qT = ofsm_lookup prev_table qT' | None ⇒ False) | None ⇒ h_obs M q' x y = None) }"
by (simp add: lookup_default_def)
also have "… = {q' ∈ ?prev_table q . ∀ x ∈ inputs M . ∀ y ∈ outputs M . (case h_obs M q x y of Some qT ⇒ (case h_obs M q' x y of Some qT' ⇒ ?prev_table qT = ?prev_table qT' | None ⇒ False) | None ⇒ h_obs M q' x y = None) }"
unfolding assms by presburger
also have "… = ofsm_table M (λq . states M) (Suc k) q"
unfolding ofsm_table.simps Let_def by presburger
finally show ?thesis .
next
case False
then have "q ∉ list.set (states_as_list M)"
using states_as_list_set by auto
then have "Mapping.lookup (next_ofsm_table M prev_table) q = None"
by (simp add: lookup_tabulate)
then have "ofsm_lookup (next_ofsm_table M prev_table) q = {}"
by (simp add: lookup_default_def)
then show ?thesis
unfolding ofsm_table_non_state[OF False] .
qed
lemma next_ofsm_table_keys_invar: "Mapping.keys (next_ofsm_table M prev_table) = states M"
using states_as_list_set[of M]
by simp
fun compute_ofsm_table_list :: "('a::linorder,'b,'c) fsm ⇒ nat ⇒ ('a,'b,'c) ofsm_table list" where
"compute_ofsm_table_list M k = rev (foldr (λ _ prev . (next_ofsm_table M (hd prev)) # prev) [0..<k] [initial_ofsm_table M])"
lemma compute_ofsm_table_list_props:
"length (compute_ofsm_table_list M k) = Suc k"
"⋀ i q . i < Suc k ⟹ ofsm_lookup ((compute_ofsm_table_list M k) ! i) q = ofsm_table M (λq . states M) i q"
"⋀ i . i < Suc k ⟹ Mapping.keys ((compute_ofsm_table_list M k) ! i) = states M"
proof -
define t where "t = (λ k . (foldr (λ _ prev . (next_ofsm_table M (hd prev)) # prev) (rev [0..<k]) [initial_ofsm_table M]))"
have t_props:"length (t k) = Suc k
∧ (∀ i q . i < Suc k ⟶ ofsm_lookup (t k ! (k-i)) q = ofsm_table M (λq . states M) i q)
∧ (∀ i . i < Suc k ⟶ Mapping.keys (t k ! i) = states M)"
proof (induction k)
case 0
have "t 0 = [initial_ofsm_table M]"
unfolding t_def by auto
show ?case
unfolding ‹t 0 = [initial_ofsm_table M]›
using initial_ofsm_table_lookup_invar[of M]
using initial_ofsm_table_keys_invar[of M]
by auto
next
case (Suc k)
have "rev [0..<Suc k] = k # (rev [0..<k])"
by auto
have *: "t (Suc k) = (next_ofsm_table M (hd (t k))) # (t k)"
unfolding t_def ‹rev [0..<Suc k] = k # (rev [0..<k])›
by auto
have IH1: "length (t k) = Suc k"
and IH2: "⋀i q . i < Suc k ⟹ ofsm_lookup (t k ! (k-i)) q = ofsm_table M (λq. FSM.states M) i q"
and IH3: "⋀i . i < Suc k ⟹ Mapping.keys (t k ! i) = FSM.states M"
using Suc.IH by blast+
have "length (t (Suc k)) = Suc (Suc k)"
using IH1 unfolding * by auto
moreover have "⋀i q . i < Suc (Suc k) ⟹ ofsm_lookup (t (Suc k) ! ((Suc k)-i)) q = ofsm_table M (λq. FSM.states M) i q"
proof -
fix i q assume "i < Suc (Suc k)"
then consider "i = Suc k" | "i < Suc k"
using less_Suc_eq by blast
then show "ofsm_lookup (t (Suc k) ! ((Suc k)-i)) q = ofsm_table M (λq. FSM.states M) i q" proof cases
case 1
then have "(t (Suc k) ! ((Suc k)-i)) = hd (t (Suc k))"
by (metis "*" diff_self_eq_0 list.sel(1) nth_Cons_0)
then have "(t (Suc k) ! ((Suc k)-i)) = next_ofsm_table M (hd (t k))"
unfolding * by (metis list.sel(1))
then have "ofsm_lookup (t (Suc k) ! ((Suc k)-i)) q = ofsm_lookup (next_ofsm_table M (hd (t k))) q"
by auto
have "(hd (t k)) = (t k ! (k-k))"
by (metis IH1 diff_self_eq_0 hd_conv_nth list.size(3) nat.simps(3))
moreover have "k < Suc k" by auto
ultimately have "ofsm_lookup (next_ofsm_table M (hd (t k))) q = ofsm_table M (λq. FSM.states M) i q"
by (metis "1" IH2 next_ofsm_table_lookup_invar)
then show ?thesis
unfolding ‹ofsm_lookup (t (Suc k) ! ((Suc k)-i)) q = ofsm_lookup (next_ofsm_table M (hd (t k))) q› .
next
case 2
then have "((Suc k)-i) > 0"
by auto
then have "(t (Suc k) ! ((Suc k)-i)) = t k ! (((Suc k)-i) - 1)"
unfolding * by (meson nth_Cons_pos)
then have "(t (Suc k) ! ((Suc k)-i)) = t k ! (k-i)"
by auto
show "ofsm_lookup (t (Suc k) ! ((Suc k)-i)) q = ofsm_table M (λq. FSM.states M) i q"
using IH2[OF 2]
unfolding ‹(t (Suc k) ! ((Suc k)-i)) = t k ! (k-i)› by metis
qed
qed
moreover have "⋀i . i < Suc (Suc k) ⟹ Mapping.keys (t (Suc k) ! i) = FSM.states M"
by (metis "*" IH3 Suc_diff_1 Suc_less_eq less_Suc_eq_0_disj next_ofsm_table_keys_invar nth_Cons')
ultimately show ?case
by blast
qed
have *:"(compute_ofsm_table_list M k) = rev (t k)"
unfolding compute_ofsm_table_list.simps t_def
using foldr_length_helper[of "rev [0..<k]" "[0..<k]" "(λ prev . (next_ofsm_table M (hd prev)) # prev)", OF length_rev]
by metis
show "length (compute_ofsm_table_list M k) = Suc k"
using t_props unfolding * length_rev by blast
have "⋀ i . i < Suc k ⟹ (rev (t k) ! i) = t k ! (k - i)"
by (simp add: rev_nth t_props)
then show "⋀i q. i < Suc k ⟹
ofsm_lookup (compute_ofsm_table_list M k ! i) q = ofsm_table M (λq. FSM.states M) i q"
unfolding * using t_props
by presburger
show "⋀i. i < Suc k ⟹ Mapping.keys (compute_ofsm_table_list M k ! i) = FSM.states M"
unfolding * using t_props ‹⋀ i . i < Suc k ⟹ (rev (t k) ! i) = t k ! (k - i)›
by simp
qed
fun compute_ofsm_tables :: "('a::linorder,'b,'c) fsm ⇒ nat ⇒ (nat, ('a,'b,'c) ofsm_table) mapping" where
"compute_ofsm_tables M k = Mapping.bulkload (compute_ofsm_table_list M k)"
lemma compute_ofsm_tables_entries :
assumes "i < Suc k"
shows "(the (Mapping.lookup (compute_ofsm_tables M k) i)) = ((compute_ofsm_table_list M k) ! i)"
using assms
unfolding compute_ofsm_tables.simps bulkload_def
by (metis bulkload.rep_eq bulkload_def compute_ofsm_table_list_props(1) lookup.rep_eq option.sel)
lemma compute_ofsm_tables_lookup_invar :
assumes "i < Suc k"
shows "ofsm_lookup (the (Mapping.lookup (compute_ofsm_tables M k) i)) q = ofsm_table M (λq . states M) i q"
using compute_ofsm_table_list_props(2)[OF assms]
unfolding compute_ofsm_tables_entries[OF assms] by metis
lemma compute_ofsm_tables_keys_invar :
assumes "i < Suc k"
shows "Mapping.keys (the (Mapping.lookup (compute_ofsm_tables M k) i)) = states M"
using compute_ofsm_table_list_props(3)[OF assms]
unfolding compute_ofsm_tables_entries[OF assms] by metis
subsection ‹Finding Diverging Tables›
lemma ofsm_table_fix_from_compute_ofsm_tables :
assumes "q ∈ states M"
shows "ofsm_lookup (the (Mapping.lookup (compute_ofsm_tables M (size M - 1)) (size M - 1))) q = ofsm_table_fix M (λq. FSM.states M) 0 q"
proof -
have "((λq. FSM.states M) ` FSM.states M) = {states M}"
using fsm_initial[of M] by auto
then have "card ((λq. FSM.states M) ` FSM.states M) = 1"
by auto
have "ofsm_lookup (the (Mapping.lookup (compute_ofsm_tables M (size M - 1)) (size M - 1))) q = ofsm_table M (λq. FSM.states M) (FSM.size M - 1) q"
using compute_ofsm_tables_lookup_invar[of "(size M - 1)" "(size M - 1)" M q]
by linarith
also have "… = ofsm_table_fix M (λq. FSM.states M) 0 q"
using ofsm_table_fix_partition_fixpoint[OF minimise_initial_partition _ assms(1), of "size M"]
unfolding ‹card ((λq. FSM.states M) ` FSM.states M) = 1›
by blast
finally show ?thesis .
qed
fun find_first_distinct_ofsm_table' :: "('a::linorder,'b,'c) fsm ⇒ 'a ⇒ 'a ⇒ nat" where
"find_first_distinct_ofsm_table' M q1 q2 = (let
tables = (compute_ofsm_tables M (size M - 1))
in if (q1 ∈ states M
∧ q2 ∈ states M
∧ (ofsm_lookup (the (Mapping.lookup tables (size M - 1))) q1
≠ ofsm_lookup (the (Mapping.lookup tables (size M - 1))) q2))
then the (find_index (λ i . ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠ ofsm_lookup (the (Mapping.lookup tables i)) q2) [0..<size M])
else 0)"
lemma find_first_distinct_ofsm_table_is_first' :
assumes "q1 ∈ FSM.states M"
and "q2 ∈ FSM.states M"
and "ofsm_table_fix M (λq . states M) 0 q1 ≠ ofsm_table_fix M (λq . states M) 0 q2"
shows "(find_first_distinct_ofsm_table M q1 q2) = Min {k . ofsm_table M (λq . states M) k q1 ≠ ofsm_table M (λq . states M) k q2
∧ (∀k' . k' < k ⟶ ofsm_table M (λq . states M) k' q1 = ofsm_table M (λq . states M) k' q2)}"
(is "find_first_distinct_ofsm_table M q1 q2 = Min ?ks")
proof -
have "find_first_distinct_ofsm_table M q1 q2 ∈ ?ks"
using find_first_distinct_ofsm_table_is_first[OF assms]
by blast
moreover have "⋀ k . k ∈ ?ks ⟹ k = find_first_distinct_ofsm_table M q1 q2"
using calculation linorder_neqE_nat by blast
ultimately have "?ks = {find_first_distinct_ofsm_table M q1 q2}"
by blast
then show ?thesis
by fastforce
qed
lemma find_first_distinct_ofsm_table'_is_first' :
assumes "q1 ∈ FSM.states M"
and "q2 ∈ FSM.states M"
and "ofsm_table_fix M (λq . states M) 0 q1 ≠ ofsm_table_fix M (λq . states M) 0 q2"
shows "(find_first_distinct_ofsm_table' M q1 q2) = Min {k . ofsm_table M (λq . states M) k q1 ≠ ofsm_table M (λq . states M) k q2
∧ (∀k' . k' < k ⟶ ofsm_table M (λq . states M) k' q1 = ofsm_table M (λq . states M) k' q2)}"
(is "find_first_distinct_ofsm_table' M q1 q2 = Min ?ks")
and "find_first_distinct_ofsm_table' M q1 q2 ≤ size M - 1"
proof -
define tables where "tables = compute_ofsm_tables M (FSM.size M - 1)"
have "ofsm_lookup (the (Mapping.lookup tables (FSM.size M - 1))) q1 ≠
ofsm_lookup (the (Mapping.lookup tables (FSM.size M - 1))) q2"
unfolding tables_def
unfolding ofsm_table_fix_from_compute_ofsm_tables[OF assms(1)]
unfolding ofsm_table_fix_from_compute_ofsm_tables[OF assms(2)]
using assms(3) .
then have "find_first_distinct_ofsm_table' M q1 q2 = the (find_index
(λi. ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠
ofsm_lookup (the (Mapping.lookup tables i)) q2)
[0..<FSM.size M])"
unfolding find_first_distinct_ofsm_table'.simps
using assms(1,2,3)
unfolding Let_def tables_def[symmetric]
by presburger
have "FSM.size M - 1 ∈ set [0..<FSM.size M]"
using fsm_size_Suc[of M] by auto
then have *:"∃ k ∈ set [0..<FSM.size M] . (λi. ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠
ofsm_lookup (the (Mapping.lookup tables i)) q2) k"
using ‹ofsm_lookup (the (Mapping.lookup tables (FSM.size M - 1))) q1 ≠
ofsm_lookup (the (Mapping.lookup tables (FSM.size M - 1))) q2›
by blast
have "find_index
(λi. ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠
ofsm_lookup (the (Mapping.lookup tables i)) q2)
[0..<FSM.size M] ≠ None"
using find_index_exhaustive[OF *] .
then obtain k where *:"find_index
(λi. ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠
ofsm_lookup (the (Mapping.lookup tables i)) q2)
[0..<FSM.size M] = Some k"
by blast
then have "find_first_distinct_ofsm_table' M q1 q2 = k"
unfolding ‹find_first_distinct_ofsm_table' M q1 q2 = the (find_index
(λi. ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠
ofsm_lookup (the (Mapping.lookup tables i)) q2)
[0..<FSM.size M])›
by auto
have "⋀ k' . k' ≤ k ⟹ [0..<FSM.size M] ! k' = k'"
using find_index_index(1)[OF *]
by (metis add.left_neutral diff_zero dual_order.trans length_upt not_le nth_upt)
then have "[0..<FSM.size M] ! k = k" and "⋀ k' . k' < k ⟹ [0..<FSM.size M] ! k' = k'"
by auto
have "k < Suc (size M - 1)"
using find_index_index(1)[OF *]
by auto
have "ofsm_lookup (the (Mapping.lookup tables k)) q1 ≠ ofsm_lookup (the (Mapping.lookup tables k)) q2"
using find_index_index(2)[OF *]
unfolding ‹[0..<FSM.size M] ! k = k› .
then have p1: "ofsm_table M (λq . states M) k q1 ≠ ofsm_table M (λq . states M) k q2"
unfolding tables_def
unfolding compute_ofsm_tables_lookup_invar[OF ‹k < Suc (size M - 1)›] .
have "⋀ k' . k' < k ⟹ ofsm_lookup (the (Mapping.lookup tables k')) q1 = ofsm_lookup (the (Mapping.lookup tables k')) q2"
using ‹⋀ k' . k' < k ⟹ [0..<FSM.size M] ! k' = k'›
using find_index_index(3)[OF *]
by auto
then have p2: "(∀k' . k' < k ⟶ ofsm_table M (λq . states M) k' q1 = ofsm_table M (λq . states M) k' q2)"
unfolding tables_def
using compute_ofsm_tables_lookup_invar[of _ "(size M - 1)" M ] ‹k < Suc (size M - 1)›
using less_trans by blast
have "k ∈ ?ks"
using p1 p2 by blast
moreover have "⋀ k' . k' ∈ ?ks ⟹ k' = k"
using calculation linorder_neqE_nat by blast
ultimately have "?ks = {k}"
by blast
then show "find_first_distinct_ofsm_table' M q1 q2 = Min ?ks"
unfolding ‹find_first_distinct_ofsm_table' M q1 q2 = k›
by fastforce
show "find_first_distinct_ofsm_table' M q1 q2 ≤ FSM.size M - 1"
unfolding ‹find_first_distinct_ofsm_table' M q1 q2 = k›
using ‹k < Suc (size M - 1)›
by auto
qed
lemma find_first_distinct_ofsm_table'_max :
"find_first_distinct_ofsm_table' M q1 q2 ≤ size M - 1"
proof (cases "q1 ∈ states M
∧ q2 ∈ states M
∧ (ofsm_lookup (the (Mapping.lookup (compute_ofsm_tables M (size M - 1)) (size M - 1))) q1
≠ ofsm_lookup (the (Mapping.lookup (compute_ofsm_tables M (size M - 1)) (size M - 1))) q2)")
case True
then show ?thesis using find_first_distinct_ofsm_table'_is_first'(2)[of q1 M q2]
using ofsm_table_fix_from_compute_ofsm_tables by blast
next
case False
then have "find_first_distinct_ofsm_table' M q1 q2 = 0"
unfolding find_first_distinct_ofsm_table'.simps Let_def by meson
then show ?thesis
by linarith
qed
lemma find_first_distinct_ofsm_table_alt_def:
"find_first_distinct_ofsm_table M q1 q2 = find_first_distinct_ofsm_table' M q1 q2"
proof (cases "q1 ∈ states M ∧ q2 ∈ states M ∧ ((ofsm_table_fix M (λq . states M) 0 q1 ≠ ofsm_table_fix M (λq . states M) 0 q2))")
case True
then have **: "q1 ∈ states M"
and ***: "q2 ∈ states M"
and ****: "(ofsm_table_fix M (λq . states M) 0 q1 ≠ ofsm_table_fix M (λq . states M) 0 q2)"
by blast+
show ?thesis
unfolding find_first_distinct_ofsm_table'_is_first'[OF ** *** ****]
unfolding find_first_distinct_ofsm_table_is_first'[OF ** *** ****]
by presburger
next
case False
have "find_first_distinct_ofsm_table M q1 q2 = 0"
by (meson False find_first_distinct_ofsm_table_gt.simps)
moreover have "find_first_distinct_ofsm_table' M q1 q2 = 0"
proof (cases "q1 ∈ states M ∧ q2 ∈ states M")
case True
then have **: "q1 ∈ states M"
and ***: "q2 ∈ states M"
by blast+
then have ****:"((ofsm_table_fix M (λq . states M) 0 q1 = ofsm_table_fix M (λq . states M) 0 q2))"
using False by blast
define tables where "tables = compute_ofsm_tables M (FSM.size M - 1)"
have "ofsm_lookup (the (Mapping.lookup tables (FSM.size M - 1))) q1 =
ofsm_lookup (the (Mapping.lookup tables (FSM.size M - 1))) q2"
unfolding tables_def
unfolding ofsm_table_fix_from_compute_ofsm_tables[OF **]
unfolding ofsm_table_fix_from_compute_ofsm_tables[OF ***]
using **** .
then show ?thesis
unfolding find_first_distinct_ofsm_table'.simps Let_def tables_def[symmetric] by auto
next
case False
then show ?thesis
unfolding find_first_distinct_ofsm_table'.simps Let_def
by meson
qed
ultimately show ?thesis
by presburger
qed
subsection ‹Refining the Computation of Distinguishing Traces via OFSM Tables›
fun select_diverging_ofsm_table_io' :: "('a::linorder,'b::linorder,'c::linorder) fsm ⇒ 'a ⇒ 'a ⇒ nat ⇒ ('b × 'c) × ('a option × 'a option)" where
"select_diverging_ofsm_table_io' M q1 q2 k = (let
tables = (compute_ofsm_tables M (size M - 1));
ins = inputs_as_list M;
outs = outputs_as_list M;
table = ofsm_lookup (the (Mapping.lookup tables (k-1)));
f = (λ (x,y) . case (h_obs M q1 x y, h_obs M q2 x y)
of
(Some q1', Some q2') ⇒ if table q1' ≠ table q2'
then Some ((x,y),(Some q1', Some q2'))
else None |
(None,None) ⇒ None |
(Some q1', None) ⇒ Some ((x,y),(Some q1', None)) |
(None, Some q2') ⇒ Some ((x,y),(None, Some q2')))
in
hd (List.map_filter f (List.product ins outs)))"
lemma select_diverging_ofsm_table_io_alt_def :
assumes "k ≤ size M - 1"
shows "select_diverging_ofsm_table_io M q1 q2 k = select_diverging_ofsm_table_io' M q1 q2 k"
proof -
define tables where "tables = compute_ofsm_tables M (FSM.size M - 1)"
define table where "table = ofsm_lookup (the (Mapping.lookup tables (k-1)))"
have "k - 1 < Suc (size M - 1)"
using assms by auto
have "ofsm_table M (λq . states M) (k-1) = table"
unfolding table_def tables_def
unfolding compute_ofsm_tables_lookup_invar[OF ‹k - 1 < Suc (size M - 1)›]
by presburger
show ?thesis
unfolding select_diverging_ofsm_table_io'.simps
select_diverging_ofsm_table_io.simps
Let_def
unfolding tables_def[symmetric] table_def[symmetric]
unfolding ‹ofsm_table M (λq . states M) (k-1) = table›
by meson
qed
fun assemble_distinguishing_sequence_from_ofsm_table' :: "('a::linorder,'b::linorder,'c::linorder) fsm ⇒ 'a ⇒ 'a ⇒ nat ⇒ ('b × 'c) list" where
"assemble_distinguishing_sequence_from_ofsm_table' M q1 q2 0 = []" |
"assemble_distinguishing_sequence_from_ofsm_table' M q1 q2 (Suc k) = (case
select_diverging_ofsm_table_io' M q1 q2 (Suc k)
of
((x,y),(Some q1',Some q2')) ⇒ (x,y) # (assemble_distinguishing_sequence_from_ofsm_table' M q1' q2' k) |
((x,y),_) ⇒ [(x,y)])"
lemma assemble_distinguishing_sequence_from_ofsm_table_alt_def :
assumes "k ≤ size M - 1"
shows "assemble_distinguishing_sequence_from_ofsm_table M q1 q2 k = assemble_distinguishing_sequence_from_ofsm_table' M q1 q2 k"
using assms proof (induction k arbitrary: q1 q2)
case 0
show ?case
unfolding assemble_distinguishing_sequence_from_ofsm_table.simps
unfolding assemble_distinguishing_sequence_from_ofsm_table'.simps
by presburger
next
case (Suc k)
then have "k ≤ FSM.size M - 1"
by auto
show ?case
unfolding assemble_distinguishing_sequence_from_ofsm_table.simps
unfolding assemble_distinguishing_sequence_from_ofsm_table'.simps
unfolding select_diverging_ofsm_table_io_alt_def[OF ‹Suc k ≤ FSM.size M - 1›]
unfolding Suc.IH[OF ‹k ≤ FSM.size M - 1›]
by meson
qed
fun get_distinguishing_sequence_from_ofsm_tables_refined :: "('a::linorder,'b::linorder,'c::linorder) fsm ⇒ 'a ⇒ 'a ⇒ ('b × 'c) list" where
"get_distinguishing_sequence_from_ofsm_tables_refined M q1 q2 = (let
k = find_first_distinct_ofsm_table' M q1 q2
in assemble_distinguishing_sequence_from_ofsm_table' M q1 q2 k)"
lemma get_distinguishing_sequence_from_ofsm_tables_refined_alt_def :
"get_distinguishing_sequence_from_ofsm_tables_refined M q1 q2 = get_distinguishing_sequence_from_ofsm_tables M q1 q2"
proof -
define k where "k = find_first_distinct_ofsm_table' M q1 q2"
then have "k ≤ size M - 1"
using find_first_distinct_ofsm_table'_max by metis
have "find_first_distinct_ofsm_table M q1 q2 = k"
unfolding k_def find_first_distinct_ofsm_table_alt_def
by meson
show ?thesis
unfolding get_distinguishing_sequence_from_ofsm_tables_refined.simps
unfolding get_distinguishing_sequence_from_ofsm_tables.simps
unfolding Let_def
unfolding k_def[symmetric] ‹find_first_distinct_ofsm_table M q1 q2 = k›
unfolding assemble_distinguishing_sequence_from_ofsm_table_alt_def[OF ‹k ≤ size M - 1›]
by meson
qed
lemma get_distinguishing_sequence_from_ofsm_tables_refined_distinguishes :
assumes "observable M"
and "minimal M"
and "q1 ∈ states M"
and "q2 ∈ states M"
and "q1 ≠ q2"
shows "distinguishes M q1 q2 (get_distinguishing_sequence_from_ofsm_tables_refined M q1 q2)"
unfolding get_distinguishing_sequence_from_ofsm_tables_refined_alt_def
using get_distinguishing_sequence_from_ofsm_tables_distinguishes[OF assms] .
fun select_diverging_ofsm_table_io_with_provided_tables :: "(nat, ('a,'b,'c) ofsm_table) mapping ⇒ ('a::linorder,'b::linorder,'c::linorder) fsm ⇒ 'a ⇒ 'a ⇒ nat ⇒ ('b × 'c) × ('a option × 'a option)" where
"select_diverging_ofsm_table_io_with_provided_tables tables M q1 q2 k = (let
ins = inputs_as_list M;
outs = outputs_as_list M;
table = ofsm_lookup (the (Mapping.lookup tables (k-1)));
f = (λ (x,y) . case (h_obs M q1 x y, h_obs M q2 x y)
of
(Some q1', Some q2') ⇒ if table q1' ≠ table q2'
then Some ((x,y),(Some q1', Some q2'))
else None |
(None,None) ⇒ None |
(Some q1', None) ⇒ Some ((x,y),(Some q1', None)) |
(None, Some q2') ⇒ Some ((x,y),(None, Some q2')))
in
hd (List.map_filter f (List.product ins outs)))"
lemma select_diverging_ofsm_table_io_with_provided_tables_simp :
"select_diverging_ofsm_table_io_with_provided_tables (compute_ofsm_tables M (size M - 1)) M = select_diverging_ofsm_table_io' M"
unfolding select_diverging_ofsm_table_io_with_provided_tables.simps
select_diverging_ofsm_table_io'.simps
Let_def
by meson
fun assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables :: "(nat, ('a,'b,'c) ofsm_table) mapping ⇒ ('a::linorder,'b::linorder,'c::linorder) fsm ⇒ 'a ⇒ 'a ⇒ nat ⇒ ('b × 'c) list" where
"assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables tables M q1 q2 0 = []" |
"assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables tables M q1 q2 (Suc k) = (case
select_diverging_ofsm_table_io_with_provided_tables tables M q1 q2 (Suc k)
of
((x,y),(Some q1',Some q2')) ⇒ (x,y) # (assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables tables M q1' q2' k) |
((x,y),_) ⇒ [(x,y)])"
lemma assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables_simp :
"assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables (compute_ofsm_tables M (size M - 1)) M q1 q2 k= assemble_distinguishing_sequence_from_ofsm_table' M q1 q2 k"
proof (induction k arbitrary: q1 q2)
case 0
show ?case
unfolding assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables.simps
assemble_distinguishing_sequence_from_ofsm_table'.simps
Let_def
by meson
next
case (Suc k')
show ?case
unfolding assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables.simps
unfolding assemble_distinguishing_sequence_from_ofsm_table'.simps
unfolding Let_def select_diverging_ofsm_table_io_with_provided_tables_simp Suc.IH
by meson
qed
lemma get_distinguishing_sequence_from_ofsm_tables_refined_code[code] :
"get_distinguishing_sequence_from_ofsm_tables_refined M q1 q2 = (let
tables = (compute_ofsm_tables M (size M - 1));
k = (if (q1 ∈ states M
∧ q2 ∈ states M
∧ (ofsm_lookup (the (Mapping.lookup tables (size M - 1))) q1
≠ ofsm_lookup (the (Mapping.lookup tables (size M - 1))) q2))
then the (find_index (λ i . ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠ ofsm_lookup (the (Mapping.lookup tables i)) q2) [0..<size M])
else 0)
in assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables tables M q1 q2 k)"
unfolding get_distinguishing_sequence_from_ofsm_tables_refined.simps
find_first_distinct_ofsm_table'.simps
Let_def
assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables_simp
by meson
fun get_distinguishing_sequence_from_ofsm_tables_with_provided_tables :: "(nat, ('a,'b,'c) ofsm_table) mapping ⇒ ('a::linorder,'b::linorder,'c::linorder) fsm ⇒ 'a ⇒ 'a ⇒ ('b × 'c) list" where
"get_distinguishing_sequence_from_ofsm_tables_with_provided_tables tables M q1 q2 = (let
k = (if (q1 ∈ states M
∧ q2 ∈ states M
∧ (ofsm_lookup (the (Mapping.lookup tables (size M - 1))) q1
≠ ofsm_lookup (the (Mapping.lookup tables (size M - 1))) q2))
then the (find_index (λ i . ofsm_lookup (the (Mapping.lookup tables i)) q1 ≠ ofsm_lookup (the (Mapping.lookup tables i)) q2) [0..<size M])
else 0)
in assemble_distinguishing_sequence_from_ofsm_table_with_provided_tables tables M q1 q2 k)"
lemma get_distinguishing_sequence_from_ofsm_tables_with_provided_tables_simp :
"get_distinguishing_sequence_from_ofsm_tables_with_provided_tables (compute_ofsm_tables M (size M - 1)) M = get_distinguishing_sequence_from_ofsm_tables_refined M"
unfolding get_distinguishing_sequence_from_ofsm_tables_with_provided_tables.simps
get_distinguishing_sequence_from_ofsm_tables_refined_code
Let_def
by meson
lemma get_distinguishing_sequence_from_ofsm_tables_precomputed:
"get_distinguishing_sequence_from_ofsm_tables M = (let
tables = (compute_ofsm_tables M (size M - 1));
distMap = mapping_of (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables_with_provided_tables tables M q1 q2))
(filter (λ qq . fst qq ≠ snd qq) (List.product (states_as_list M) (states_as_list M))));
distHelper = (λ q1 q2 . if q1 ∈ states M ∧ q2 ∈ states M ∧ q1 ≠ q2 then the (Mapping.lookup distMap (q1,q2)) else get_distinguishing_sequence_from_ofsm_tables M q1 q2)
in distHelper)"
proof -
define distStates where "distStates = (filter (λ qq . fst qq ≠ snd qq) (List.product (states_as_list M) (states_as_list M)))"
define distMap where distMap_orig: "distMap = mapping_of (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables_with_provided_tables (compute_ofsm_tables M (size M - 1)) M q1 q2))
distStates)"
have "distinct distStates"
unfolding distStates_def using states_as_list_distinct
using distinct_filter distinct_product by blast
then have "distinct (map fst (map (λ(q1, q2). ((q1, q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2)) distStates))"
unfolding map_pair_fst_helper .
then have distMap_def: "Mapping.lookup distMap = map_of (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2))
distStates)"
unfolding distMap_orig get_distinguishing_sequence_from_ofsm_tables_with_provided_tables_simp
get_distinguishing_sequence_from_ofsm_tables_refined_alt_def
using mapping_of_map_of
by blast
define distHelper where "distHelper = (λ q1 q2 . if q1 ∈ states M ∧ q2 ∈ states M ∧ q1 ≠ q2 then the (Mapping.lookup distMap (q1,q2)) else get_distinguishing_sequence_from_ofsm_tables M q1 q2)"
have "distHelper = get_distinguishing_sequence_from_ofsm_tables M"
proof -
have "⋀ q1 q2 . distHelper q1 q2 = get_distinguishing_sequence_from_ofsm_tables M q1 q2"
proof -
fix q1 q2
show "distHelper q1 q2 = get_distinguishing_sequence_from_ofsm_tables M q1 q2"
proof (cases "q1 ∈ states M ∧ q2 ∈ states M ∧ q1 ≠ q2")
case False
then show ?thesis
unfolding distHelper_def by metis
next
case True
then have *:"(q1,q2) ∈ list.set distStates"
using states_as_list_set unfolding distStates_def by fastforce
have "distinct (map fst (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2)) distStates))"
proof -
have **: "(map fst (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2)) distStates)) = distStates"
proof (induction distStates)
case Nil
then show ?case by auto
next
case (Cons a distStates)
obtain x y where "a = (x,y)"
using surjective_pairing by blast
show ?case
using Cons unfolding ‹a = (x,y)› by auto
qed
show ?thesis
unfolding **
unfolding distStates_def
by (simp add: distinct_product)
qed
have "((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2) ∈ list.set (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2)) distStates)"
using Util.map_set[OF *, of "(λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2))"]
by force
then have "the (Mapping.lookup distMap (q1,q2)) = get_distinguishing_sequence_from_ofsm_tables M q1 q2"
unfolding distMap_def
unfolding Map.map_of_eq_Some_iff[OF ‹distinct (map fst (map (λ (q1,q2) . ((q1,q2), get_distinguishing_sequence_from_ofsm_tables M q1 q2)) distStates))›, symmetric]
by (metis option.sel)
moreover have "distHelper q1 q2 = the (Mapping.lookup distMap (q1,q2))"
using True unfolding distHelper_def by metis
ultimately show ?thesis
by presburger
qed
qed
then show ?thesis
by blast
qed
then show ?thesis
unfolding distHelper_def distMap_orig distStates_def Let_def
by presburger
qed
lemma get_distinguishing_sequence_from_ofsm_tables_with_provided_tables_distinguishes :
assumes "observable M"
and "minimal M"
and "q1 ∈ states M"
and "q2 ∈ states M"
and "q1 ≠ q2"
shows "distinguishes M q1 q2 (get_distinguishing_sequence_from_ofsm_tables_with_provided_tables (compute_ofsm_tables M (size M - 1)) M q1 q2)"
unfolding get_distinguishing_sequence_from_ofsm_tables_with_provided_tables_simp
using get_distinguishing_sequence_from_ofsm_tables_refined_distinguishes[OF assms] .
subsection ‹Refining Minimisation›
fun minimise_refined :: "('a :: linorder,'b :: linorder,'c :: linorder) fsm ⇒ ('a set,'b,'c) fsm" where
"minimise_refined M = (let
tables = (compute_ofsm_tables M (size M - 1));
eq_class = (ofsm_lookup (the (Mapping.lookup tables (size M - 1))));
ts = (λ t . (eq_class (t_source t), t_input t, t_output t, eq_class (t_target t))) ` (transitions M);
q0 = eq_class (initial M);
eq_states = eq_class |`| fstates M;
M' = create_unconnected_fsm_from_fsets q0 eq_states (finputs M) (foutputs M)
in add_transitions M' ts)"
lemma minimise_refined_is_minimise[code] : "minimise M = minimise_refined M"
proof -
define tables where "tables = compute_ofsm_tables M (FSM.size M - 1)"
define eq_class_refined where "eq_class_refined = (ofsm_lookup (the (Mapping.lookup tables (size M - 1))))"
define eq_class where "eq_class = ofsm_table_fix M (λq . states M) 0"
have "(size M - 1) < Suc (size M - 1)"
by auto
have "⋀ q . q ∈ states M ⟹ eq_class q = eq_class_refined q"
unfolding eq_class_def eq_class_refined_def tables_def
unfolding compute_ofsm_tables_lookup_invar[OF ‹(size M - 1) < Suc (size M - 1)›]
by (metis ofsm_table_fix_partition_fixpoint_trivial_partition)
have ts: "(λ t . (eq_class (t_source t), t_input t, t_output t, eq_class (t_target t))) ` (transitions M)
= (λ t . (eq_class_refined (t_source t), t_input t, t_output t, eq_class_refined (t_target t))) ` (transitions M)"
using ‹⋀ q . q ∈ states M ⟹ eq_class q = eq_class_refined q›[OF fsm_transition_source]
using ‹⋀ q . q ∈ states M ⟹ eq_class q = eq_class_refined q›[OF fsm_transition_target]
by auto
have q0: "eq_class (initial M) = eq_class_refined (initial M)"
using ‹⋀ q . q ∈ states M ⟹ eq_class q = eq_class_refined q›[OF fsm_initial] .
have eq_states: "eq_class |`| fstates M = eq_class_refined |`| fstates M"
using fstates_set[of M]
using ‹⋀ q . q ∈ states M ⟹ eq_class q = eq_class_refined q›
by (metis fset.map_cong)
have M': "create_unconnected_fsm_from_fsets (eq_class (initial M)) (eq_class |`| fstates M) (finputs M) (foutputs M)
= create_unconnected_fsm_from_fsets (eq_class_refined (initial M)) (eq_class_refined |`| fstates M) (finputs M) (foutputs M)"
unfolding q0 eq_states by meson
have res: "add_transitions (create_unconnected_fsm_from_fsets (eq_class (initial M)) (eq_class |`| fstates M) (finputs M) (foutputs M)) ((λ t . (eq_class (t_source t), t_input t, t_output t, eq_class (t_target t))) ` (transitions M))
= add_transitions (create_unconnected_fsm_from_fsets (eq_class_refined (initial M)) (eq_class_refined |`| fstates M) (finputs M) (foutputs M)) ((λ t . (eq_class_refined (t_source t), t_input t, t_output t, eq_class_refined (t_target t))) ` (transitions M))"
unfolding M' ts by meson
show ?thesis
unfolding minimise.simps minimise_refined.simps Let_def
unfolding eq_class_def[symmetric]
unfolding tables_def[symmetric] eq_class_refined_def[symmetric]
unfolding res
by meson
qed
end