Theory Prim_Impl
section ‹Implementation of Prim's Algorithm›
theory Prim_Impl
imports
Prim_Abstract
Undirected_Graph_Impl
"HOL-Library.While_Combinator"
"Priority_Search_Trees.PST_RBT"
"HOL-Data_Structures.RBT_Map"
begin
subsection ‹Implementation using ADT Interfaces\label{sec:prim_data_structs}›
locale Prim_Impl_Adts =
G: adt_wgraph G_αw G_αg G_invar G_adj G_empty G_add_edge
+ M: Map M_empty M_update M_delete M_lookup M_invar
+ Q: PrioMap Q_empty Q_update Q_delete Q_invar Q_lookup Q_is_empty Q_getmin
for typG :: "'g itself" and typM :: "'m itself" and typQ :: "'q itself"
and G_αw and G_αg :: "'g ⇒ ('v) ugraph" and G_invar G_adj G_empty G_add_edge
and M_empty M_update M_delete and M_lookup :: "'m ⇒ 'v ⇒ 'v option" and M_invar
and Q_empty Q_update Q_delete Q_invar and Q_lookup :: "'q ⇒ 'v ⇒ nat option"
and Q_is_empty Q_getmin
begin
text ‹Simplifier setup›
lemmas [simp] = G.wgraph_specs
lemmas [simp] = M.map_specs
lemmas [simp] = Q.prio_map_specs
end
locale Prim_Impl_Defs = Prim_Impl_Adts
where typG = typG and typM = typM and typQ = typQ and G_αw = G_αw and G_αg = G_αg
for typG :: "'g itself" and typM :: "'m itself" and typQ :: "'q itself"
and G_αw and G_αg :: "'g ⇒ ('v::linorder) ugraph" and g :: 'g and r :: 'v
begin
subsubsection ‹Concrete Algorithm›
term M_lookup
definition "foreach_impl_body u ≡ (λ(v,d) (Qi,πi).
if v=r then (Qi,πi)
else
case (Q_lookup Qi v, M_lookup πi v) of
(None,None) ⇒ (Q_update v d Qi, M_update v u πi)
| (Some d',_) ⇒ (if d<d' then (Q_update v d Qi, M_update v u πi) else (Qi,πi))
| (None, Some _) ⇒ (Qi,πi)
)"
definition foreach_impl :: "'q ⇒ 'm ⇒ 'v ⇒ ('v×nat) list ⇒ 'q × 'm" where
"foreach_impl Qi πi u adjs = foldr (foreach_impl_body u) adjs (Qi,πi)"
definition "outer_loop_impl Qi πi ≡ while (λ(Qi,πi). ¬Q_is_empty Qi) (λ(Qi,πi).
let
(u,_) = Q_getmin Qi;
adjs = G_adj g u;
(Qi,πi) = foreach_impl Qi πi u adjs;
Qi = Q_delete u Qi
in (Qi,πi)) (Qi,πi)"
definition "prim_impl = (let
Qi = Q_update r 0 Q_empty;
πi = M_empty;
(Qi,πi) = outer_loop_impl Qi πi
in πi)
"
text ‹The whole algorithm as one function›
lemma prim_impl_alt: "prim_impl = (let
(Q,π) = (Q_update r 0 Q_empty, M_empty);
(Q, π) =
while (λ(Q, π). ¬ Q_is_empty Q) (λ(Q, π). let
(u, _) = Q_getmin Q;
(Q, π) =
foldr ((λ(v, d) (Q, π). let
qv = Q_lookup Q v;
πv = M_lookup π v
in
if v≠r ∧ (qv≠None ∨ πv=None) ∧ enat d < enat_of_option qv
then (Q_update v d Q, M_update v u π)
else (Q, π))
) (G_adj g u) (Q, π);
Q = Q_delete u Q
in (Q, π)) (Q, π)
in π
)"
proof -
have 1: "foreach_impl_body u = (λ(v,d) (Qi,πi). let
qiv = (Q_lookup Qi v);
πiv = M_lookup πi v
in
if v≠r ∧ (qiv≠None ∨ πiv=None) ∧ enat d < enat_of_option qiv
then (Q_update v d Qi, M_update v u πi)
else (Qi, πi))" for u
unfolding foreach_impl_body_def
apply (intro ext)
by (auto split: option.split)
show ?thesis
unfolding prim_impl_def outer_loop_impl_def foreach_impl_def 1
by (simp)
qed
subsubsection ‹Abstraction of Result›
text ‹Invariant for the result, and its interpretation as (minimum spanning) tree:
▪ The map ‹πi› and set ‹Vi› satisfy their implementation invariants
▪ The ‹πi› encodes irreflexive edges consistent with the nodes determined
by ‹Vi›. Note that the edges in ‹πi› will not be symmetric, thus we take
their symmetric closure ‹E∪E¯›.
›
definition "invar_MST πi ≡ M_invar πi"
definition "α_MST πi ≡ graph {r} {(u,v) | u v. M_lookup πi u = Some v}"
end
subsection ‹Refinement of State›
locale Prim_Impl = Prim_Impl_Defs
where typG = typG and typM = typM and typQ = typQ and G_αw = G_αw and G_αg = G_αg
for typG :: "'g itself" and typM :: "'m itself" and typQ :: "'q itself"
and G_αw and G_αg :: "'g ⇒ ('v::linorder) ugraph"
+
assumes G_invar[simp]: "G_invar g"
begin
sublocale Prim2 "G_αw g" "G_αg g" r .
subsubsection ‹Abstraction of ‹Q››
text ‹The priority map implements a function of type @{typ ‹'v⇒enat›},
mapping @{const None} to @{term ∞}.
›
definition "Q_α Qi ≡ enat_of_option o Q_lookup Qi :: 'v ⇒ enat"
lemma Q_α_empty: "Q_α Q_empty = (λ_. ∞)"
unfolding Q_α_def by (auto)
lemma Q_α_update: "Q_invar Q ⟹ Q_α (Q_update u d Q) = (Q_α Q)(u := enat d)"
unfolding Q_α_def by (auto)
lemma Q_α_is_empty: "Q_invar Q ⟹ Q_lookup Q = Map.empty ⟷ Q_α Q = (λ_. ∞)"
unfolding Q_α_def by (auto simp: fun_eq_iff)
lemma Q_α_delete: "Q_invar Q ⟹ Q_α (Q_delete u Q) = (Q_α Q)(u:=∞)"
unfolding Q_α_def by (auto simp: fun_eq_iff)
lemma Q_α_min:
assumes MIN: "Q_getmin Qi = (u, d)"
assumes I: "Q_invar Qi"
assumes NE: "¬ Q_is_empty Qi"
shows "Q_α Qi u = enat d" (is ?G1) and
"∀v. enat d ≤ Q_α Qi v" (is ?G2)
proof -
from Q.map_getmin[OF MIN]
have "Q_lookup Qi u = Some d" "(∀x∈ran (Q_lookup Qi). d ≤ x)"
using NE I by auto
thus "?G1" "?G2"
unfolding Q_α_def apply simp_all
by (metis enat_of_option.elims enat_ord_simps(1) enat_ord_simps(3) ranI)
qed
lemmas Q_α_specs = Q_α_empty Q_α_update Q_α_is_empty Q_α_delete
subsubsection ‹Concrete Invariant›
text ‹The implementation invariants of the concrete state's components,
and the abstract invariant of the state's abstraction›
definition "prim_invar_impl Qi πi ≡
Q_invar Qi ∧ M_invar πi ∧ prim_invar2 (Q_α Qi) (M_lookup πi)"
end
subsection ‹Refinement of Algorithm›
context Prim_Impl
begin
lemma foreach_impl_correct:
fixes Qi Vi πi defines "Q ≡ Q_α Qi" and "π ≡ M_lookup πi"
assumes A: "foreach_impl Qi πi u (G_adj g u) = (Qi',πi')"
assumes I: "prim_invar_impl Qi πi"
shows "Q_invar Qi'" and "M_invar πi'"
and "Q_α Qi' = Qinter Q π u" and "M_lookup πi' = π' Q π u"
proof -
from I have [simp]: "Q_invar Qi" "M_invar πi"
unfolding prim_invar_impl_def Q_def π_def by auto
{
fix Qi πi d v and adjs :: "('v × nat) list"
assume "Q_invar Qi" "M_invar πi" "(v, d) ∈ set adjs"
then have
"(case foreach_impl_body u (v, d) (Qi, πi) of
(Qi, πi) ⇒ Q_invar Qi ∧ M_invar πi)
∧ map_prod Q_α M_lookup (foreach_impl_body u (v, d) (Qi, πi))
= foreach_body u (v, d) (Q_α Qi, M_lookup πi)"
unfolding foreach_impl_body_def foreach_body_def
unfolding Q_α_def
by (auto simp: fun_eq_iff split: option.split)
} note aux=this
from foldr_refine[
where I="λ(Qi,πi). Q_invar Qi ∧ M_invar πi" and α="map_prod Q_α M_lookup",
of "(Qi,πi)" "(G_adj g u)" "foreach_impl_body u" "foreach_body u"
]
and A aux[where ?adjs3="(G_adj g u)"]
have "Q_invar Qi'" "M_invar πi'"
and 1: "foreach u (G_adj g u) (Q_α Qi, M_lookup πi)
= (Q_α Qi', M_lookup πi')"
unfolding foreach_impl_def foreach_def
unfolding Q_def π_def
by (auto split: prod.splits)
then show "Q_invar Qi'" "M_invar πi'" by auto
from 1 foreach_refine[where adjs="G_adj g u" and u=u] show
"Q_α Qi' = Qinter Q π u" and "M_lookup πi' = π' Q π u"
by (auto simp: Q_def π_def)
qed
lemma foreach_impl_correct_presentation:
fixes Qi Vi πi defines "Q ≡ Q_α Qi" and "π ≡ M_lookup πi"
assumes A: "foreach_impl Qi πi u (G_adj g u) = (Qi',πi')"
assumes I: "prim_invar_impl Qi πi"
shows "Q_invar Qi' ∧ M_invar πi'
∧ Q_α Qi' = Qinter Q π u ∧ M_lookup πi' = π' Q π u"
using foreach_impl_correct assms by blast
definition "T_measure_impl ≡ λ(Qi,πi). T_measure2 (Q_α Qi) (M_lookup πi)"
lemma prim_invar_impl_init: "prim_invar_impl (Q_update r 0 Q_empty) M_empty"
using invar2_init
by (auto simp: prim_invar_impl_def Q_α_specs initQ_def initπ_def zero_enat_def)
lemma maintain_prim_invar_impl:
assumes
I: "prim_invar_impl Qi πi" and
NE: "¬ Q_is_empty Qi" and
MIN: "Q_getmin Qi = (u, d)" and
FOREACH: "foreach_impl Qi πi u (G_adj g u) = (Qi', πi')"
shows "prim_invar_impl (Q_delete u Qi') πi'" (is ?G1)
and "T_measure_impl (Q_delete u Qi', πi') < T_measure_impl (Qi,πi)" (is "?G2")
proof -
note II[simp] = I[unfolded prim_invar_impl_def]
note FI[simp] = foreach_impl_correct[OF FOREACH I]
note MIN' = Q_α_min[OF MIN _ NE, simplified]
show ?G1
unfolding prim_invar_impl_def
using Q_α_delete maintain_invar2[OF _ MIN']
by (simp add: Q'_def)
show ?G2
unfolding prim_invar_impl_def T_measure_impl_def
using Q_α_delete maintain_invar2[OF _ MIN']
apply (simp add: Q'_def Q_α_def)
by (metis FI(3) II Q'_def Q_α_def
‹⋀π. prim_invar2 (Q_α Qi) π
⟹ T_measure2 (Q' (Q_α Qi) π u) (π' (Q_α Qi) π u)
< T_measure2 (Q_α Qi) π›)
qed
lemma maintain_prim_invar_impl_presentation:
assumes
I: "prim_invar_impl Qi πi" and
NE: "¬ Q_is_empty Qi" and
MIN: "Q_getmin Qi = (u, d)" and
FOREACH: "foreach_impl Qi πi u (G_adj g u) = (Qi', πi')"
shows "prim_invar_impl (Q_delete u Qi') πi'
∧ T_measure_impl (Q_delete u Qi', πi') < T_measure_impl (Qi,πi)"
using maintain_prim_invar_impl assms by blast
lemma prim_invar_impl_finish:
"⟦Q_is_empty Q; prim_invar_impl Q π⟧
⟹ invar_MST π ∧ is_MST (G_αw g) rg (α_MST π)"
using invar2_finish
by (auto simp: Q_α_specs prim_invar_impl_def invar_MST_def α_MST_def Let_def)
lemma prim_impl_correct:
assumes "prim_impl = πi"
shows
"invar_MST πi" (is ?G1)
"is_MST (G_αw g) (component_of (G_αg g) r) (α_MST πi)" (is ?G2)
proof -
have "let (Qi, πi) = outer_loop_impl (Q_update r 0 Q_empty) M_empty in
invar_MST πi ∧ is_MST (G_αw g) rg (α_MST πi)"
unfolding outer_loop_impl_def
apply (rule while_rule[where
P="λ(Qi,πi). prim_invar_impl Qi πi" and r="measure T_measure_impl"])
apply (all ‹clarsimp split: prod.splits simp: Q_α_specs›)
apply (simp_all add: prim_invar_impl_init maintain_prim_invar_impl
prim_invar_impl_finish)
done
with assms show ?G1 ?G2
unfolding rg_def prim_impl_def by (simp_all split: prod.splits)
qed
lemma prim_impl_correct_presentation:
"invar_MST prim_impl
∧ is_MST (G_αw g) (component_of (G_αg g) r) (α_MST prim_impl)"
using prim_impl_correct by blast
end
subsection ‹Instantiation with Actual Data Structures\label{sec:prim_inst_ds}›
global_interpretation
G: wgraph_by_map RBT_Set.empty RBT_Map.update RBT_Map.delete
Lookup2.lookup RBT_Map.M.invar
defines G_empty = G.empty
and G_add_edge = G.add_edge
and G_add_edge1 = G.add_edge1
and G_adj = G.adj
and G_from_list = G.from_list
and G_valid_wgraph_repr = G.valid_wgraph_repr
by unfold_locales
lemma G_from_list_unfold: "G_from_list = G.from_list"
by (simp add: G_add_edge_def G_empty_def G_from_list_def)
lemma [code]: "G_from_list l = foldr (λ(e, d). G_add_edge e d) l G_empty"
by (simp add: G.from_list_def G_from_list_unfold)
global_interpretation Prim_Impl_Adts _ _ _
G.αw G.αg G.invar G.adj G.empty G.add_edge
RBT_Set.empty RBT_Map.update RBT_Map.delete Lookup2.lookup RBT_Map.M.invar
PST_RBT.empty PST_RBT.update PST_RBT.delete PST_RBT.PM.invar
Lookup2.lookup PST_RBT.rbt_is_empty pst_getmin
..
global_interpretation P: Prim_Impl_Defs G.invar G.adj G.empty G.add_edge
RBT_Set.empty RBT_Map.update RBT_Map.delete Lookup2.lookup RBT_Map.M.invar
PST_RBT.empty PST_RBT.update PST_RBT.delete PST_RBT.PM.invar
Lookup2.lookup PST_RBT.rbt_is_empty pst_getmin
_ _ _ G.αw G.αg g r
for g and r::"'a::linorder"
defines prim_impl = P.prim_impl
and outer_loop_impl = P.outer_loop_impl
and foreach_impl = P.foreach_impl
and foreach_impl_body = P.foreach_impl_body
by unfold_locales
lemmas [code] = P.prim_impl_alt
context
fixes g
assumes [simp]: "G.invar g"
begin
interpretation AUX: Prim_Impl
G.invar G.adj G.empty G.add_edge
RBT_Set.empty RBT_Map.update RBT_Map.delete Lookup2.lookup RBT_Map.M.invar
PST_RBT.empty PST_RBT.update PST_RBT.delete PST_RBT.PM.invar
Lookup2.lookup PST_RBT.rbt_is_empty pst_getmin
g r _ _ _ G.αw G.αg for r::"'a::linorder"
by unfold_locales simp_all
lemmas prim_impl_correct = AUX.prim_impl_correct[folded prim_impl_def]
end
subsubsection ‹Adding a Graph-From-List Parser›
definition "prim_list_impl l r
≡ if G_valid_wgraph_repr l then Some (prim_impl (G_from_list l) r) else None"
subsection ‹Main Correctness Theorem›
text ‹
The @{const prim_list_impl} algorithm returns @{const None}, if the input was
invalid. Otherwise it returns @{term ‹Some (πi,Vi)›}, which satisfy the
map/set invariants and encode a minimum spanning tree of the component of the
graph that contains ‹r›.
Notes:
▪ If ‹r› is n ot a node of the graph, ‹component_of› will return the graph
with the only node ‹r›. (@{thm [source] component_of_not_node})
›
theorem prim_list_impl_correct:
shows "case prim_list_impl l r of
None ⇒ ¬G.valid_wgraph_repr l
| Some πi ⇒
G.valid_wgraph_repr l ∧ (let Gi = G.from_list l in G.invar Gi
∧ P.invar_MST πi
∧ is_MST (G.αw Gi) (component_of (G.αg Gi) r) (P.α_MST r πi)) "
unfolding prim_list_impl_def G_from_list_unfold
using prim_impl_correct[of "G.from_list l" r] G.from_list_correct[of l]
by (auto simp: Let_def)
theorem prim_list_impl_correct_presentation:
shows "case prim_list_impl l r of
None ⇒ ¬G.valid_wgraph_repr l
| Some πi ⇒ let
g=G.αg (G.from_list l);
w=G.αw (G.from_list l);
rg=component_of g r;
t=P.α_MST r πi
in
G.valid_wgraph_repr l
∧ P.invar_MST πi
∧ is_MST w rg t "
using prim_list_impl_correct[of l r] unfolding Let_def
by (auto split: option.splits)
subsection ‹Code Generation and Test\label{sec:prim_exec}›
definition prim_list_impl_int :: "_ ⇒ int ⇒ _"
where "prim_list_impl_int ≡ prim_list_impl"
export_code prim_list_impl prim_list_impl_int checking SML
experiment begin
abbreviation "a ≡ 1"
abbreviation "b ≡ 2"
abbreviation "c ≡ 3"
abbreviation "d ≡ 4"
abbreviation "e ≡ 5"
abbreviation "f ≡ 6"
abbreviation "g ≡ 7"
abbreviation "h ≡ 8"
abbreviation "i ≡ 9"
value "(prim_list_impl_int [
((a,b),4),
((a,h),8),
((b,h),11),
((b,c),8),
((h,i),7),
((h,g),1),
((c,i),2),
((g,i),6),
((c,d),7),
((c,f),4),
((g,f),2),
((d,f),14),
((d,e),9),
((e,f),10)
] 1)"
end
end