Theory PAC_Map_Rel
theory PAC_Map_Rel
imports
Refine_Imperative_HOL.IICF Finite_Map_Multiset
begin
section ‹Hash-Map for finite mappings›
text ‹
This function declares hash-maps for \<^typ>‹('a, 'b)fmap›, that are nicer
to use especially here where everything is finite.
›
definition fmap_rel where
[to_relAPP]:
"fmap_rel K V ≡ {(m1, m2).
(∀i j. i |∈| fmdom m2 ⟶ (j, i) ∈ K ⟶ (the (fmlookup m1 j), the (fmlookup m2 i)) ∈ V) ∧
fset (fmdom m1) ⊆ Domain K ∧ fset (fmdom m2) ⊆ Range K ∧
(∀i j. (i, j) ∈ K ⟶ j |∈| fmdom m2 ⟷ i |∈| fmdom m1)}"
lemma fmap_rel_alt_def:
‹⟨K, V⟩fmap_rel ≡
{(m1, m2).
(∀i j. i ∈# dom_m m2 ⟶
(j, i) ∈ K ⟶ (the (fmlookup m1 j), the (fmlookup m2 i)) ∈ V) ∧
fset (fmdom m1) ⊆ Domain K ∧
fset (fmdom m2) ⊆ Range K ∧
(∀i j. (i, j) ∈ K ⟶ (j ∈# dom_m m2) = (i ∈# dom_m m1))}
›
unfolding fmap_rel_def dom_m_def
by auto
lemma fmdom_empty_fmempty_iff[simp]: ‹fmdom m = {||} ⟷ m = fmempty›
by (metis fmdom_empty fmdrop_fset_fmdom fmdrop_fset_null)
lemma fmap_rel_empty1_simp[simp]:
"(fmempty,m)∈⟨K,V⟩fmap_rel ⟷ m=fmempty"
apply (cases ‹fmdom m = {||}›)
apply (auto simp: fmap_rel_def)[]
by (auto simp add: fmap_rel_def simp del: fmdom_empty_fmempty_iff)
lemma fmap_rel_empty2_simp[simp]:
"(m,fmempty)∈⟨K,V⟩fmap_rel ⟷ m=fmempty"
apply (cases ‹fmdom m = {||}›)
apply (auto simp: fmap_rel_def)[]
by (fastforce simp add: fmap_rel_def simp del: fmdom_empty_fmempty_iff)
sepref_decl_intf ('k,'v) f_map is "('k, 'v) fmap"
lemma [synth_rules]: "⟦INTF_OF_REL K TYPE('k); INTF_OF_REL V TYPE('v)⟧
⟹ INTF_OF_REL (⟨K,V⟩fmap_rel) TYPE(('k,'v) f_map)" by simp
subsection ‹Operations›
sepref_decl_op fmap_empty: "fmempty" :: "⟨K,V⟩fmap_rel" .
sepref_decl_op fmap_is_empty: "(=) fmempty" :: "⟨K,V⟩fmap_rel → bool_rel"
apply (rule fref_ncI)
apply parametricity
apply (rule fun_relI; auto)
done
lemma fmap_rel_fmupd_fmap_rel:
‹(A, B) ∈ ⟨K, R⟩fmap_rel ⟹ (p, p') ∈ K ⟹ (q, q') ∈ R ⟹
(fmupd p q A, fmupd p' q' B) ∈ ⟨K, R⟩fmap_rel›
if "single_valued K" "single_valued (K¯)"
using that
unfolding fmap_rel_alt_def
apply (case_tac ‹p' ∈# dom_m B›)
apply (auto simp add: all_conj_distrib IS_RIGHT_UNIQUED dest!: multi_member_split)
done
sepref_decl_op fmap_update: "fmupd" :: "K → V → ⟨K,V⟩fmap_rel → ⟨K,V⟩fmap_rel"
where "single_valued K" "single_valued (K¯)"
apply (rule fref_ncI)
apply parametricity
apply (intro fun_relI)
by (rule fmap_rel_fmupd_fmap_rel)
lemma remove1_mset_eq_add_mset_iff:
‹remove1_mset a A = add_mset a A' ⟷ A = add_mset a (add_mset a A')›
by (metis add_mset_add_single add_mset_diff_bothsides diff_zero remove1_mset_eqE)
lemma fmap_rel_fmdrop_fmap_rel:
‹(fmdrop p A, fmdrop p' B) ∈ ⟨K, R⟩fmap_rel›
if single: "single_valued K" "single_valued (K¯)" and
H0: ‹(A, B) ∈ ⟨K, R⟩fmap_rel› ‹(p, p') ∈ K›
proof -
have H: ‹⋀Aa j.
∀i. i ∈# dom_m B ⟶ (∀j. (j, i) ∈ K ⟶ (the (fmlookup A j), the (fmlookup B i)) ∈ R) ⟹
remove1_mset p' (dom_m B) = add_mset p' Aa ⟹ (j, p') ∈ K ⟹ False›
by (metis dom_m_fmdrop fmlookup_drop in_dom_m_lookup_iff union_single_eq_member)
have H2: ‹⋀i Aa j.
(p, p') ∈ K ⟹
∀i. i ∈# dom_m B ⟶ (∀j. (j, i) ∈ K ⟶ (the (fmlookup A j), the (fmlookup B i)) ∈ R) ⟹
∀i j. (i, j) ∈ K ⟶ (j ∈# dom_m B) = (i ∈# dom_m A) ⟹
remove1_mset p' (dom_m B) = add_mset i Aa ⟹
(j, i) ∈ K ⟹
(the (fmlookup A j), the (fmlookup B i)) ∈ R ∧ j ∈# remove1_mset p (dom_m A) ∧
i ∈# remove1_mset p' (dom_m B)›
‹⋀i j Aa.
(p, p') ∈ K ⟹
single_valued K ⟹
single_valued (K¯) ⟹
∀i. i ∈# dom_m B ⟶ (∀j. (j, i) ∈ K ⟶ (the (fmlookup A j), the (fmlookup B i)) ∈ R) ⟹
fset (fmdom A) ⊆ Domain K ⟹
fset (fmdom B) ⊆ Range K ⟹
∀i j. (i, j) ∈ K ⟶ (j ∈# dom_m B) = (i ∈# dom_m A) ⟹
(i, j) ∈ K ⟹ remove1_mset p (dom_m A) = add_mset i Aa ⟹ j ∈# remove1_mset p' (dom_m B)›
using single
by (metis IS_RIGHT_UNIQUED converse.intros dom_m_fmdrop fmlookup_drop in_dom_m_lookup_iff
union_single_eq_member)+
show ‹(fmdrop p A, fmdrop p' B) ∈ ⟨K, R⟩fmap_rel›
using that
unfolding fmap_rel_alt_def
by (auto simp add: all_conj_distrib IS_RIGHT_UNIQUED
dest!: multi_member_split dest: H H2)
qed
sepref_decl_op fmap_delete: "fmdrop" :: "K → ⟨K,V⟩fmap_rel → ⟨K,V⟩fmap_rel"
where "single_valued K" "single_valued (K¯)"
apply (rule fref_ncI)
apply parametricity
by (auto simp add: fmap_rel_fmdrop_fmap_rel)
lemma fmap_rel_nat_the_fmlookup[intro]:
‹(A, B) ∈ ⟨S, R⟩fmap_rel ⟹ (p, p') ∈ S ⟹ p' ∈# dom_m B ⟹
(the (fmlookup A p), the (fmlookup B p')) ∈ R›
by (auto simp: fmap_rel_alt_def distinct_mset_dom)
lemma fmap_rel_in_dom_iff:
‹(aa, a'a) ∈ ⟨K, V⟩fmap_rel ⟹
(a, a') ∈ K ⟹
a' ∈# dom_m a'a ⟷
a ∈# dom_m aa›
unfolding fmap_rel_alt_def
by auto
lemma fmap_rel_fmlookup_rel:
‹(a, a') ∈ K ⟹ (aa, a'a) ∈ ⟨K, V⟩fmap_rel ⟹
(fmlookup aa a, fmlookup a'a a') ∈ ⟨V⟩option_rel›
using fmap_rel_nat_the_fmlookup[of aa a'a K V a a']
fmap_rel_in_dom_iff[of aa a'a K V a a']
in_dom_m_lookup_iff[of a' a'a]
in_dom_m_lookup_iff[of a aa]
by (cases ‹a' ∈# dom_m a'a›)
(auto simp del: fmap_rel_nat_the_fmlookup)
sepref_decl_op fmap_lookup: "fmlookup" :: "⟨K,V⟩fmap_rel → K → ⟨V⟩option_rel"
apply (rule fref_ncI)
apply parametricity
apply (intro fun_relI)
apply (rule fmap_rel_fmlookup_rel; assumption)
done
lemma in_fdom_alt: "k∈#dom_m m ⟷ ¬is_None (fmlookup m k)"
by (auto split: option.split intro: fmdom_notI fmdomI simp: dom_m_def)
sepref_decl_op fmap_contains_key: "λk m. k∈#dom_m m" :: "K → ⟨K,V⟩fmap_rel → bool_rel"
unfolding in_fdom_alt
apply (rule fref_ncI)
apply parametricity
apply (rule fmap_rel_fmlookup_rel; assumption)
done
subsection ‹Patterns›
lemma pat_fmap_empty[pat_rules]: "fmempty ≡ op_fmap_empty" by simp
lemma pat_map_is_empty[pat_rules]:
"(=) $m$fmempty ≡ op_fmap_is_empty$m"
"(=) $fmempty$m ≡ op_fmap_is_empty$m"
"(=) $(dom_m$m)${#} ≡ op_fmap_is_empty$m"
"(=) ${#}$(dom_m$m) ≡ op_fmap_is_empty$m"
unfolding atomize_eq
by (auto dest: sym)
lemma op_map_contains_key[pat_rules]:
"(∈#) $ k $ (dom_m$m) ≡ op_fmap_contains_key$'k$'m"
by (auto intro!: eq_reflection)
subsection ‹Mapping to Normal Hashmaps›
abbreviation map_of_fmap :: ‹('k ⇒ 'v option) ⇒ ('k, 'v) fmap› where
‹map_of_fmap h ≡ Abs_fmap h›
definition map_fmap_rel where
‹map_fmap_rel = br map_of_fmap (λa. finite (dom a))›
lemma fmdrop_set_None:
‹(op_map_delete, fmdrop) ∈ Id → map_fmap_rel → map_fmap_rel›
apply (auto simp: map_fmap_rel_def br_def)
apply (subst fmdrop.abs_eq)
apply (auto simp: eq_onp_def fmap.Abs_fmap_inject
map_drop_def map_filter_finite
intro!: ext)
apply (auto simp: map_filter_def)
done
lemma map_upd_fmupd:
‹(op_map_update, fmupd) ∈ Id → Id → map_fmap_rel → map_fmap_rel›
apply (auto simp: map_fmap_rel_def br_def)
apply (subst fmupd.abs_eq)
apply (auto simp: eq_onp_def fmap.Abs_fmap_inject
map_drop_def map_filter_finite map_upd_def
intro!: ext)
done
text ‹Technically @{term op_map_lookup} has the arguments in the wrong direction.›
definition fmlookup' where
[simp]: ‹fmlookup' A k = fmlookup k A›
lemma [def_pat_rules]:
‹((∈#)$k$(dom_m$A)) ≡ Not$(is_None$(fmlookup'$k$A))›
by (simp add: fold_is_None in_fdom_alt)
lemma op_map_lookup_fmlookup:
‹(op_map_lookup, fmlookup') ∈ Id → map_fmap_rel → ⟨Id⟩option_rel›
by (auto simp: map_fmap_rel_def br_def fmap.Abs_fmap_inverse)
abbreviation hm_fmap_assn where
‹hm_fmap_assn K V ≡ hr_comp (hm.assn K V) map_fmap_rel›
lemmas fmap_delete_hnr [sepref_fr_rules] =
hm.delete_hnr[FCOMP fmdrop_set_None]
lemmas fmap_update_hnr [sepref_fr_rules] =
hm.update_hnr[FCOMP map_upd_fmupd]
lemmas fmap_lookup_hnr [sepref_fr_rules] =
hm.lookup_hnr[FCOMP op_map_lookup_fmlookup]
lemma fmempty_empty:
‹(uncurry0 (RETURN op_map_empty), uncurry0 (RETURN fmempty)) ∈ unit_rel →⇩f ⟨map_fmap_rel⟩nres_rel›
by (auto simp: map_fmap_rel_def br_def fmempty_def frefI nres_relI)
lemmas [sepref_fr_rules] =
hm.empty_hnr[FCOMP fmempty_empty, unfolded op_fmap_empty_def[symmetric]]
abbreviation iam_fmap_assn where
‹iam_fmap_assn K V ≡ hr_comp (iam.assn K V) map_fmap_rel›
lemmas iam_fmap_delete_hnr [sepref_fr_rules] =
iam.delete_hnr[FCOMP fmdrop_set_None]
lemmas iam_ffmap_update_hnr [sepref_fr_rules] =
iam.update_hnr[FCOMP map_upd_fmupd]
lemmas iam_ffmap_lookup_hnr [sepref_fr_rules] =
iam.lookup_hnr[FCOMP op_map_lookup_fmlookup]
definition op_iam_fmap_empty where
‹op_iam_fmap_empty = fmempty›
lemma iam_fmempty_empty:
‹(uncurry0 (RETURN op_map_empty), uncurry0 (RETURN op_iam_fmap_empty)) ∈ unit_rel →⇩f ⟨map_fmap_rel⟩nres_rel›
by (auto simp: map_fmap_rel_def br_def fmempty_def frefI nres_relI op_iam_fmap_empty_def)
lemmas [sepref_fr_rules] =
iam.empty_hnr[FCOMP fmempty_empty, unfolded op_iam_fmap_empty_def[symmetric]]
definition upper_bound_on_dom where
‹upper_bound_on_dom A = SPEC(λn. ∀i ∈#(dom_m A). i < n)›
lemma [sepref_fr_rules]:
‹((Array.len), upper_bound_on_dom) ∈ (iam_fmap_assn nat_assn V)⇧k →⇩a nat_assn›
proof -
have [simp]: ‹finite (dom b) ⟹ i ∈ fset (fmdom (map_of_fmap b)) ⟷ i ∈ dom b› for i b
by (subst fmdom.abs_eq)
(auto simp: eq_onp_def fset.Abs_fset_inverse)
have 2: ‹nat_rel = the_pure (nat_assn)› and
3: ‹nat_assn = pure nat_rel›
by auto
have [simp]: ‹the_pure (λa c :: nat. ↑ (c = a)) = nat_rel›
apply (subst 2)
apply (subst 3)
apply (subst pure_def)
apply auto
done
have [simp]: ‹(iam_of_list l, b) ∈ the_pure (λa c :: nat. ↑ (c = a)) → ⟨the_pure V⟩option_rel ⟹
b i = Some y ⟹ i < length l› for i b l y
by (auto dest!: fun_relD[of _ _ _ _ i i] simp: option_rel_def
iam_of_list_def split: if_splits)
show ?thesis
by sepref_to_hoare
(sep_auto simp: upper_bound_on_dom_def hr_comp_def iam.assn_def map_rel_def
map_fmap_rel_def is_iam_def br_def dom_m_def)
qed
lemma fmap_rel_nat_rel_dom_m[simp]:
‹(A, B) ∈ ⟨nat_rel, R⟩fmap_rel ⟹ dom_m A = dom_m B›
by (subst distinct_set_mset_eq_iff[symmetric])
(auto simp: fmap_rel_alt_def distinct_mset_dom
simp del: fmap_rel_nat_the_fmlookup)
lemma ref_two_step':
‹A ≤ B ⟹ ⇓ R A ≤ ⇓ R B›
using ref_two_step by auto
end