Theory Default_St_Algebra
theory Default_St_Algebra
imports Default_St_Base "Voblint_Domain.State_Concretization"
begin
unbundle default_st_carrier_syntax
section ‹Pointwise abstract-domain operations›
text ‹
Join, widening and narrowing are the same construction three times: apply an
element operation to both dictionaries of a state, at the default and at
every name either argument lists. This theory gives that construction once,
instantiates it three times, and installs the lattice and warrowing classes
the solver needs.
The combinator's support is the two argument supports with duplicate keys
removed. Keeping both copies would denote the same lookup function, but every
join would concatenate, and a solver iteration would then grow the
materialized list without bound while each lookup scans it.
›
subsection ‹The pointwise combinator›
fun map2_default_dict ::
"('a => 'a => 'a) => 'a default_dict => 'a default_dict => 'a default_dict"
where
"map2_default_dict f (d1, ps1) (d2, ps2) =
(f d1 d2,
map (λx. (x, f (default_dict_get (d1, ps1) x) (default_dict_get (d2, ps2) x)))
(remdups (map fst ps1 @ map fst ps2)))"
fun map2_default_st_rep ::
"('a => 'a => 'a) => ('a::bot) default_st_rep => 'a default_st_rep =>
'a default_st_rep"
where
"map2_default_st_rep f (l1, g1) (l2, g2) =
(map2_default_dict f l1 l2, map2_default_dict f g1 g2)"
lemma map_of_map_key_list:
"map_of (map (λk. (k, g k)) ks) x =
(if x ∈ set ks then Some (g x) else None)"
by (induction ks) auto
lemma default_dict_get_map2 [simp]:
"default_dict_get (map2_default_dict f m1 m2) x =
f (default_dict_get m1 x) (default_dict_get m2 x)"
by (cases m1; cases m2)
(auto simp: map_of_map_key_list map_of_resolved_none_iff[THEN iffD2])
text ‹
From here on the combinator is read through its lookup equation. Its
defining equation would otherwise unfold first and hide it from
@{thm [source] default_dict_get_map2}.
›
declare map2_default_dict.simps [simp del]
lemma default_st_rep_get_map2 [simp]:
"default_st_rep_get (map2_default_st_rep f s t) loc =
f (default_st_rep_get s loc) (default_st_rep_get t loc)"
by (cases s; cases t; cases loc) simp_all
lemma eq_default_st_rep_map2:
assumes "eq_default_st_rep s1 s2"
and "eq_default_st_rep t1 t2"
shows "eq_default_st_rep (map2_default_st_rep f s1 t1)
(map2_default_st_rep f s2 t2)"
by (rule eq_default_st_repI)
(simp add: eq_default_st_repD[OF assms(1)] eq_default_st_repD[OF assms(2)])
subsection ‹Join and semilattice structure›
text ‹
Join is the pointwise join of both dictionaries, which respects extensional
equality and so lifts to ‹default_st› as a bounded join-semilattice.
›
definition merge_default_st_rep ::
"('a::bounded_semilattice_sup_bot) default_st_rep =>
'a default_st_rep => 'a default_st_rep"
where
"merge_default_st_rep s t = map2_default_st_rep (⊔) s t"
lemma default_st_rep_get_merge [simp]:
"default_st_rep_get (merge_default_st_rep s t) loc =
default_st_rep_get s loc ⊔ default_st_rep_get t loc"
by (simp add: merge_default_st_rep_def)
lemma eq_default_st_rep_merge:
assumes "eq_default_st_rep s1 s2"
and "eq_default_st_rep t1 t2"
shows "eq_default_st_rep (merge_default_st_rep s1 t1)
(merge_default_st_rep s2 t2)"
using assms unfolding merge_default_st_rep_def
by (rule eq_default_st_rep_map2)
instantiation default_st ::
(bounded_semilattice_sup_bot) sup
begin
lift_definition sup_default_st ::
"('a::bounded_semilattice_sup_bot) default_st =>
'a default_st => 'a default_st"
is merge_default_st_rep
by (rule eq_default_st_rep_merge)
instance ..
end
lemma default_st_get_sup [simp]:
"(s ⊔ t)⟨loc⟩ = s⟨loc⟩ ⊔ t⟨loc⟩"
by transfer (rule default_st_rep_get_merge)
instance default_st ::
(bounded_semilattice_sup_bot) semilattice_sup
proof intro_classes
fix s t u :: "('a::bounded_semilattice_sup_bot) default_st"
show "s ≤ s ⊔ t"
by (simp add: le_default_st_iff)
show "t ≤ s ⊔ t"
by (simp add: le_default_st_iff)
show "s ≤ u ⟹ t ≤ u ⟹ s ⊔ t ≤ u"
by (simp add: le_default_st_iff)
qed
instance default_st ::
(bounded_semilattice_sup_bot) bounded_semilattice_sup_bot ..
subsection ‹Widening and narrowing›
text ‹
Widening and narrowing act pointwise as well, inheriting the element domain's
‹warrowing› laws location by location.
›
definition widen_default_st_rep ::
"('a::{bounded_semilattice_sup_bot, warrowing}) default_st_rep =>
'a default_st_rep => 'a default_st_rep"
where
"widen_default_st_rep s t = map2_default_st_rep (∇) s t"
lemma default_st_rep_get_widen [simp]:
"default_st_rep_get (widen_default_st_rep s t) loc =
default_st_rep_get s loc ∇ default_st_rep_get t loc"
by (simp add: widen_default_st_rep_def)
lemma eq_default_st_rep_widen:
assumes "eq_default_st_rep s1 s2"
and "eq_default_st_rep t1 t2"
shows "eq_default_st_rep (widen_default_st_rep s1 t1)
(widen_default_st_rep s2 t2)"
using assms unfolding widen_default_st_rep_def
by (rule eq_default_st_rep_map2)
text ‹
Widening and narrowing are lifted to a named constant first and only then
installed as the class operation, unlike \<^const>‹sup› which is lifted inside
its own ‹instantiation›. The detour is forced: the ‹widening› and
‹narrowing› classes carry axioms, so their ‹instance› proofs need the lookup
equation, and inside an ‹instantiation› block the class operation is not yet
the same term as a constant lifted there -- a lookup lemma stated on the
latter fails to refine a goal about the former, and one proved before
‹instance› abstracts over the operation instead of fixing it. \<^const>‹sup›
escapes this because the bare ‹sup› class has no axioms to discharge.
›
lift_definition widen_on_default_st ::
"('a::{bounded_semilattice_sup_bot, warrowing}) default_st =>
'a default_st => 'a default_st"
is widen_default_st_rep
by (rule eq_default_st_rep_widen)
lemma default_st_get_widen_on [simp]:
"(widen_on_default_st s t)⟨loc⟩ =
s⟨loc⟩ ∇ t⟨loc⟩"
by transfer (rule default_st_rep_get_widen)
instantiation default_st :: ("{bounded_semilattice_sup_bot, warrowing}") widening
begin
definition widen_default_st ::
"('a::{bounded_semilattice_sup_bot, warrowing}) default_st =>
'a default_st => 'a default_st"
where
"widen_default_st s t = widen_on_default_st s t"
instance
proof
fix a b :: "('a::{bounded_semilattice_sup_bot, warrowing}) default_st"
show "a ≤ (a ∇ b)"
by (simp add: le_default_st_iff widen_default_st_def widen_ge1)
show "b ≤ (a ∇ b)"
by (simp add: le_default_st_iff widen_default_st_def widen_ge2)
qed
end
lemma default_st_get_widen [simp]:
"(s ∇ t)⟨loc⟩ = s⟨loc⟩ ∇ t⟨loc⟩"
by (simp add: widen_default_st_def)
definition narrow_default_st_rep ::
"('a::{bounded_semilattice_sup_bot, warrowing}) default_st_rep =>
'a default_st_rep => 'a default_st_rep"
where
"narrow_default_st_rep s t = map2_default_st_rep (Δ) s t"
lemma default_st_rep_get_narrow [simp]:
"default_st_rep_get (narrow_default_st_rep s t) loc =
default_st_rep_get s loc Δ default_st_rep_get t loc"
by (simp add: narrow_default_st_rep_def)
lemma eq_default_st_rep_narrow:
assumes "eq_default_st_rep s1 s2"
and "eq_default_st_rep t1 t2"
shows "eq_default_st_rep (narrow_default_st_rep s1 t1)
(narrow_default_st_rep s2 t2)"
using assms unfolding narrow_default_st_rep_def
by (rule eq_default_st_rep_map2)
lift_definition narrow_on_default_st ::
"('a::{bounded_semilattice_sup_bot, warrowing}) default_st =>
'a default_st => 'a default_st"
is narrow_default_st_rep
by (rule eq_default_st_rep_narrow)
lemma default_st_get_narrow_on [simp]:
"(narrow_on_default_st s t)⟨loc⟩ =
s⟨loc⟩ Δ t⟨loc⟩"
by transfer (rule default_st_rep_get_narrow)
instantiation default_st :: ("{bounded_semilattice_sup_bot, warrowing}") narrowing
begin
definition narrow_default_st ::
"('a::{bounded_semilattice_sup_bot, warrowing}) default_st =>
'a default_st => 'a default_st"
where
"narrow_default_st s t = narrow_on_default_st s t"
instance
by standard
(auto simp add: le_default_st_iff narrow_default_st_def
intro: narrow_ge narrow_le)
end
lemma default_st_get_narrow [simp]:
"(s Δ t)⟨loc⟩ = s⟨loc⟩ Δ t⟨loc⟩"
by (simp add: narrow_default_st_def)
instance default_st :: ("{bounded_semilattice_sup_bot, warrowing}") warrowing ..
unbundle no default_st_carrier_syntax
end