Theory Gabow_Skeleton
section ‹Skeleton for Gabow's SCC Algorithm›
theory Gabow_Skeleton
imports "../lib/Base_MEC" "../ds/Graph" "../ds/Stack_Set"
begin
text ‹
In this theory, we formalize a skeleton of Gabow's SCC algorithm.
The skeleton serves as a starting point to develop concrete algorithms,
like enumerating the SCCs or checking emptiness of a generalized Büchi automaton.
›
section ‹Abstract Algorithm›
text ‹
In this section, we formalize an abstract version of a path-based SCC algorithm.
Later, this algorithm will be refined to use Gabow's data structure.
›
subsection ‹Preliminaries›
definition path_seg :: "'a set list ⇒ nat ⇒ nat ⇒ 'a set"
where "path_seg p i j ≡ ⋃{p!k|k. i≤k ∧ k<j}"
lemma path_seg_simps[simp]:
"j≤i ⟹ path_seg p i j = {}"
"path_seg p i (Suc i) = p!i"
unfolding path_seg_def
apply auto []
apply (auto simp: le_less_Suc_eq) []
done
lemma path_seg_drop:
"⋃(set (drop i p)) = path_seg p i (length p)"
unfolding path_seg_def
by (fastforce simp: in_set_drop_conv_nth Bex_def)
lemma path_seg_butlast:
"p≠[] ⟹ path_seg p 0 (length p - Suc 0) = ⋃(set (butlast p))"
apply (cases p rule: rev_cases, simp)
apply (fastforce simp: path_seg_def nth_append in_set_conv_nth)
done
definition idx_of :: "'a set list ⇒ 'a ⇒ nat"
where "idx_of p v ≡ THE i. i<length p ∧ v∈p!i"
lemma idx_of_props:
assumes
p_disjoint_sym: "∀i j v. i<length p ∧ j<length p ∧ v∈p!i ∧ v∈p!j ⟶ i=j"
assumes ON_STACK: "v∈⋃(set p)"
shows
"idx_of p v < length p" and
"v ∈ p ! idx_of p v"
proof -
from ON_STACK obtain i where "i<length p" "v ∈ p ! i"
by (auto simp add: in_set_conv_nth)
moreover hence "∀j<length p. v∈p ! j ⟶ i=j"
using p_disjoint_sym by auto
ultimately show "idx_of p v < length p"
and "v ∈ p ! idx_of p v" unfolding idx_of_def
by (metis (lifting) theI')+
qed
lemma idx_of_uniq:
assumes
p_disjoint_sym: "∀i j v. i<length p ∧ j<length p ∧ v∈p!i ∧ v∈p!j ⟶ i=j"
assumes A: "i<length p" "v∈p!i"
shows "idx_of p v = i"
proof -
from A p_disjoint_sym have "∀j<length p. v∈p ! j ⟶ i=j" by auto
with A show ?thesis
unfolding idx_of_def
by (metis (lifting) the_equality)
qed
subsection ‹Invariants›
text ‹The state of the inner loop consists of the path ‹p› of
collapsed nodes, the set ‹D› of finished (done) nodes, the multiset
‹pE› of pending edges, and the set ‹vE› of visited edges.›
type_synonym 'v abs_state = "'v set list × 'v set × ('v × 'v) multiset × ('v × 'v) set"
context fr_graph
begin
definition touched :: "'v set list ⇒ 'v set ⇒ 'v set"
where "touched p D ≡ D ∪ ⋃(set p)"
end
locale outer_invar_loc
= fr_graph E V0 for E :: "'v digraph" and V0 +
fixes it :: "'v set"
fixes D :: "'v set"
assumes it_initial: "it⊆V0"
assumes it_done: "V0 - it ⊆ D"
assumes D_reachable: "D⊆E⇧*``V0"
assumes D_closed: "E``D ⊆ D"
begin
lemma locale_this: "outer_invar_loc E V0 it D" by unfold_locales
definition (in fr_graph) "outer_invar ≡ λit D. outer_invar_loc E V0 it D"
lemma outer_invar_this[simp, intro!]: "outer_invar it D"
unfolding outer_invar_def apply simp by unfold_locales
end
locale invar_loc
=
fr_graph E V0
for E :: "'v digraph" and V0 +
fixes v0 :: "'v"
fixes D0 :: "'v set"
fixes p :: "'v set list"
fixes D :: "'v set"
fixes pE :: "('v × 'v) multiset"
fixes vE :: "('v × 'v) set"
assumes v0_initial[simp, intro!]: "v0∈V0"
assumes D_incr: "D0 ⊆ D"
assumes vE_ss_E: "vE ⊆ E"
assumes pE_E_from_p: "(set_mset pE) ⊆ E ∩ (⋃(set p)) × UNIV"
assumes E_from_p_touched: "E ∩ (⋃(set p) × UNIV) ⊆ (set_mset pE) ∪ UNIV × touched p D"
assumes D_reachable: "D⊆E⇧*``V0"
assumes p_connected: "Suc i<length p ⟹ p!i × p!Suc i ∩ vE ≠ {}"
assumes p_disjoint: "⟦i<j; j<length p⟧ ⟹ p!i ∩ p!j = {}"
assumes p_sc: "U∈set p ⟹ U×U ⊆ (vE ∩ U×U)⇧*"
assumes root_v0: "p≠[] ⟹ v0∈hd p"
assumes p_empty_v0: "p=[] ⟹ v0∈D"
assumes D_closed: "E``D ⊆ D"
assumes vE_no_back: "⟦i<j; j<length p⟧ ⟹ vE ∩ p!j × p!i = {}"
assumes p_not_D: "⋃(set p) ∩ D = {}"
assumes D_vis: "E∩D×UNIV ⊆ vE"
assumes vE_touched: "vE ⊆ touched p D × touched p D"
assumes pE_if_not_visited: "(E ∩ ⋃(set p) × UNIV) - vE ⊆ set_mset pE"
begin
abbreviation ltouched where "ltouched ≡ touched p D"
lemma locale_this: "invar_loc E V0 v0 D0 p D pE vE" by unfold_locales
definition (in fr_graph)
"invar ≡ λv0 D0 (p,D,pE,vE). invar_loc E V0 v0 D0 p D pE vE"
lemma invar_this[simp, intro!]: "invar v0 D0 (p,D,pE,vE)"
unfolding invar_def apply simp by unfold_locales
lemma finite_reachableE_v0[simp, intro!]: "finite (E⇧*``{v0})"
apply (rule finite_subset[OF _ finite_reachableE_V0])
using v0_initial by auto
lemma path_touched: "⋃(set p) ⊆ ltouched" by (auto simp: touched_def)
lemma D_touched: "D ⊆ ltouched" by (auto simp: touched_def)
lemma D_succs_in_D: "E ∩ (D × UNIV) = E ∩ (D × D)"
using D_closed by blast
lemma D_image_closed: "E⇧*``D = D"
using D_closed
by (simp add: Image_closed_trancl)
end
subsubsection ‹Termination›
context fr_graph
begin
text ‹The termination argument is based on unprocessed edges:
Reachable edges from untouched nodes and pending edges.›
definition "reachable_edges v0 ≡ E ∩ (E⇧*``{v0}) × (E⇧*``{v0})"
lemma reachable_edges_alt: "reachable_edges v0 = E ∩ (E⇧*``{v0}) × UNIV"
unfolding reachable_edges_def
by auto
definition "unproc_edges v0 vE ≡ reachable_edges v0 - vE"
text ‹
In each iteration of the loop, either the number of unprocessed edges
decreases, or the path length decreases.›
definition "abs_wf_rel v0 ≡ inv_image (finite_psubset <*lex*> measure size <*lex*> measure length)
(λ(p,D,pE,vE). (unproc_edges v0 vE, pE, p))"
lemma abs_wf_rel_wf[simp, intro!]: "wf (abs_wf_rel v0)"
unfolding abs_wf_rel_def
by auto
end
subsection ‹Abstract Skeleton Algorithm›
context fr_graph
begin
definition out_edges :: "'v ⇒ (('v × 'v) multiset) nres" where
"out_edges v = SPEC (λ r. set_mset r = (E ∩ {v}×UNIV))"
definition initial :: "'v ⇒ 'v set ⇒ ('v abs_state) nres"
where "initial v0 D ≡ do { pE ← out_edges v0; RETURN([{v0}], D, pE, E∩D×UNIV)}"
definition (in -) collapse_aux :: "'a set list ⇒ nat ⇒ 'a set list"
where "collapse_aux p i ≡ take i p @ [⋃(set (drop i p))]"
definition (in -) collapse :: "'a ⇒ 'a abs_state ⇒ 'a abs_state"
where "collapse v PDPE ≡
let
(p,D,pE,vE)=PDPE;
i=idx_of p v;
p = collapse_aux p i
in (p,D,pE,vE)"
definition (in -)
select_edge :: "'a abs_state ⇒ ('a option × 'a abs_state) nres"
where
"select_edge PDPE ≡ do {
let (p,D,pE,vE) = PDPE;
e ← SELECT (λe. e ∈ (set_mset pE) ∩ last p × UNIV);
case e of
None ⇒ RETURN (None,(p,D,pE,vE))
| Some (u,v) ⇒ RETURN (Some v, (p,D,pE - {#(u,v)#},insert (u,v) vE))
}"
definition push :: "'v ⇒ 'v abs_state ⇒ ('v abs_state) nres"
where "push v PDPE ≡
do {
let (p,D,pE,vE) = PDPE;
let p = p@[{v}];
pE' ← out_edges v;
let pE = pE + pE';
RETURN (p,D,pE,vE)
}"
definition (in -) pop :: "'v abs_state ⇒ 'v abs_state"
where "pop PDPE ≡ let
(p,D,pE,vE) = PDPE;
(p,V) = (butlast p, last p);
D = V ∪ D
in
(p,D,pE,vE)"
definition skeleton :: "'v set nres"
where
"skeleton ≡ do {
let D = {};
r ← FOREACHi outer_invar V0 (λv0 D0. do {
if v0∉D0 then do {
ASSERT(v0 ∈ E⇧*``V0);
s ← initial v0 D0;
(p,D,pE,vE) ← WHILEIT (invar v0 D0)
(λ(p,D,pE,vE). p ≠ []) (λ(p,D,pE,vE).
do {
(vo,(p,D,pE,vE)) ← select_edge (p,D,pE,vE);
ASSERT (p≠[]);
case vo of
Some v ⇒ do {
ASSERT (v ∈ E⇧* `` V0);
if v ∈ ⋃(set p) then do {
RETURN (collapse v (p,D,pE,vE))
} else if v∉D then do {
push v (p,D,pE,vE)
} else do {
RETURN (p,D,pE,vE)
}
}
| None ⇒ do {
ASSERT ((set_mset pE) ∩ last p × UNIV = {});
RETURN (pop (p,D,pE,vE))
}
}) s;
ASSERT (p=[] ∧ pE={#});
RETURN D
} else
RETURN D0
}) D;
RETURN r
}"
end
subsection ‹Invariant Preservation›
context fr_graph begin
lemma set_collapse_aux[simp]: "⋃(set (collapse_aux p i)) = ⋃(set p)"
apply (subst (2) append_take_drop_id[of _ p,symmetric])
apply (simp del: append_take_drop_id)
unfolding collapse_aux_def by auto
lemma touched_collapse[simp]: "touched (collapse_aux p i) D = touched p D"
unfolding touched_def by simp
lemma touched_push[simp]: "touched (p @ [V]) D = touched p D ∪ V"
unfolding touched_def by auto
end
subsubsection ‹Corollaries of the invariant›
text ‹In this section, we prove some more corollaries of the invariant,
which are helpful to show invariant preservation›
context invar_loc
begin
lemma cnode_connectedI:
"⟦i<length p; u∈p!i; v∈p!i⟧ ⟹ (u,v)∈(vE ∩ p!i×p!i)⇧*"
using p_sc[of "p!i"] by (auto simp: in_set_conv_nth)
lemma cnode_connectedI': "⟦i<length p; u∈p!i; v∈p!i⟧ ⟹ (u,v)∈(vE)⇧*"
by (metis inf.cobounded1 rtrancl_mono_mp cnode_connectedI)
lemma p_no_empty: "{} ∉ set p"
proof
assume "{}∈set p"
then obtain i where IDX: "i<length p" "p!i={}"
by (auto simp add: in_set_conv_nth)
show False proof (cases i)
case 0 with root_v0 IDX show False by (cases p) auto
next
case [simp]: (Suc j)
from p_connected[of j] IDX show False by simp
qed
qed
corollary p_no_empty_idx: "i<length p ⟹ p!i≠{}"
using p_no_empty by (metis nth_mem)
lemma p_disjoint_sym: "⟦i<length p; j<length p; v∈p!i; v∈p!j⟧ ⟹ i=j"
by (metis disjoint_iff_not_equal linorder_neqE_nat p_disjoint)
lemma pi_ss_path_seg_eq[simp]:
assumes A: "i<length p" "u≤length p"
shows "p!i⊆path_seg p l u ⟷ l≤i ∧ i<u"
proof
assume B: "p!i⊆path_seg p l u"
from A obtain x where "x∈p!i" by (blast dest: p_no_empty_idx)
with B obtain i' where C: "x∈p!i'" "l≤i'" "i'<u"
by (auto simp: path_seg_def)
from p_disjoint_sym[OF ‹i<length p› _ ‹x∈p!i› ‹x∈p!i'›] ‹i'<u› ‹u≤length p›
have "i=i'" by simp
with C show "l≤i ∧ i<u" by auto
qed (auto simp: path_seg_def)
lemma path_seg_ss_eq[simp]:
assumes A: "l1<u1" "u1≤length p" "l2<u2" "u2≤length p"
shows "path_seg p l1 u1 ⊆ path_seg p l2 u2 ⟷ l2≤l1 ∧ u1≤u2"
proof
assume S: "path_seg p l1 u1 ⊆ path_seg p l2 u2"
have "p!l1 ⊆ path_seg p l1 u1" using A by simp
also note S finally have 1: "l2≤l1" using A by simp
have "p!(u1 - 1) ⊆ path_seg p l1 u1" using A by simp
also note S finally have 2: "u1≤u2" using A by auto
from 1 2 show "l2≤l1 ∧ u1≤u2" ..
next
assume "l2≤l1 ∧ u1≤u2" thus "path_seg p l1 u1 ⊆ path_seg p l2 u2"
using A
apply (clarsimp simp: path_seg_def) []
apply (metis dual_order.strict_trans1 dual_order.trans)
done
qed
lemma pathI:
assumes "x∈p!i" "y∈p!j"
assumes "i≤j" "j<length p"
defines "seg ≡ path_seg p i (Suc j)"
shows "(x,y)∈(vE ∩ seg×seg)⇧*"
using assms(3,1,2,4) unfolding seg_def
proof (induction arbitrary: y rule: dec_induct)
case base thus ?case by (auto intro!: cnode_connectedI)
next
case (step j)
let ?seg = "path_seg p i (Suc j)"
let ?seg' = "path_seg p i (Suc (Suc j))"
have SSS: "?seg ⊆ ?seg'"
apply (subst path_seg_ss_eq)
using step.hyps step.prems by auto
from p_connected[OF ‹Suc j < length p›] obtain u v where
UV: "(u,v)∈vE" "u∈p!j" "v∈p!Suc j" by auto
have ISS: "p!j ⊆ ?seg'" "p!Suc j ⊆ ?seg'"
using step.hyps step.prems by simp_all
from p_no_empty_idx[of j] ‹Suc j < length p› obtain x' where "x'∈p!j"
by auto
with step.IH[of x'] ‹x∈p!i› ‹Suc j < length p›
have t: "(x,x')∈(vE∩?seg×?seg)⇧*" by auto
have "(x,x')∈(vE∩?seg'×?seg')⇧*" using SSS
by (auto intro: rtrancl_mono_mp[OF _ t])
also
from cnode_connectedI[OF _ ‹x'∈p!j› ‹u∈p!j›] ‹Suc j < length p› have
t: "(x', u) ∈ (vE ∩ p ! j × p ! j)⇧*" by auto
have "(x', u) ∈ (vE∩?seg'×?seg')⇧*" using ISS
by (auto intro: rtrancl_mono_mp[OF _ t])
also have "(u,v)∈vE∩?seg'×?seg'" using UV ISS by auto
also from cnode_connectedI[OF ‹Suc j < length p› ‹v∈p!Suc j› ‹y∈p!Suc j›]
have t: "(v, y) ∈ (vE ∩ p ! Suc j × p ! Suc j)⇧*" by auto
have "(v, y) ∈ (vE∩?seg'×?seg')⇧*" using ISS
by (auto intro: rtrancl_mono_mp[OF _ t])
finally show "(x,y)∈(vE∩?seg'×?seg')⇧*" .
qed
lemma p_reachable: "⋃(set p) ⊆ E⇧*``{v0}"
proof
fix v
assume A: "v∈⋃(set p)"
then obtain i where "i<length p" and "v∈p!i"
by (metis UnionE in_set_conv_nth)
moreover from A root_v0 have "v0∈p!0" by (cases p) auto
ultimately have
t: "(v0,v)∈(vE ∩ path_seg p 0 (Suc i) × path_seg p 0 (Suc i))⇧*"
by (auto intro: pathI)
from vE_ss_E have "(v0,v)∈E⇧*" by (auto intro: rtrancl_mono_mp[OF _ t])
thus "v∈E⇧*``{v0}" by auto
qed
lemma p_elem_reachable: "X ∈ set p ⟹ X ⊆ E⇧* `` {v0}"
using p_reachable
by blast
lemma touched_reachable: "ltouched ⊆ E⇧*``V0"
unfolding touched_def using p_reachable D_reachable by blast
lemma vE_reachable: "vE ⊆ E⇧*``V0 × E⇧*``V0"
apply (rule order_trans[OF vE_touched])
using touched_reachable by blast
lemma pE_reachable: "(set_mset pE) ⊆ E⇧*``{v0} × E⇧*``{v0}"
proof safe
fix u v
assume E: "(u,v)∈#pE"
with pE_E_from_p p_reachable have "(v0,u)∈E⇧*" "(u,v)∈E" by blast+
thus "(v0,u)∈E⇧*" "(v0,v)∈E⇧*" by auto
qed
lemma D_closed_vE_rtrancl: "vE⇧*``D ⊆ D"
by (metis D_closed Image_closed_trancl eq_iff reachable_mono vE_ss_E)
lemma D_closed_path: "⟦path E u q w; u∈D⟧ ⟹ set q ⊆ D"
proof -
assume a1: "path E u q w"
assume "u ∈ D"
hence f1: "{u} ⊆ D"
using bot.extremum by force
have "set q ⊆ E⇧* `` {u}"
using a1 by (metis insert_subset path_nodes_reachable)
thus "set q ⊆ D"
using f1 by (metis D_closed rtrancl_reachable_induct subset_trans)
qed
lemma D_closed_path_vE: "⟦path vE u q w; u∈D⟧ ⟹ set q ⊆ D"
by (metis D_closed_path path_mono vE_ss_E)
lemma path_in_lastnode:
assumes P: "path vE u q v"
assumes [simp]: "p≠[]"
assumes ND: "u∈last p" "v∈last p"
shows "set q ⊆ last p"
using P ND
proof (induction)
case (path_prepend u v l w)
from ‹(u,v)∈vE› vE_touched have "v∈ltouched" by auto
hence "v∈⋃(set p)"
unfolding touched_def
proof
assume "v∈D"
moreover from ‹path vE v l w› have "(v,w)∈vE⇧*" by (rule path_is_rtrancl)
ultimately have "w∈D" using D_closed_vE_rtrancl by auto
with ‹w∈last p› p_not_D have False
by (metis IntI Misc.last_in_set Sup_inf_eq_bot_iff assms(2)
bex_empty path_prepend.hyps(2))
thus ?thesis ..
qed
then obtain i where "i<length p" "v∈p!i"
by (metis UnionE in_set_conv_nth)
have "i=length p - 1"
proof (rule ccontr)
assume "i≠length p - 1"
with ‹i<length p› have "i < length p - 1" by simp
with vE_no_back[of i "length p - 1"] ‹i<length p›
have "vE ∩ last p × p!i = {}"
by (simp add: last_conv_nth)
with ‹(u,v)∈vE› ‹u∈last p› ‹v∈p!i› show False by auto
qed
with ‹v∈p!i› have "v∈last p" by (simp add: last_conv_nth)
with path_prepend.IH ‹w∈last p› ‹u∈last p› show ?case by auto
qed simp
lemma loop_in_lastnode:
assumes P: "path vE u q u"
assumes [simp]: "p≠[]"
assumes ND: "set q ∩ last p ≠ {}"
shows "u∈last p" and "set q ⊆ last p"
proof -
from ND obtain v where "v∈set q" "v∈last p" by auto
then obtain q1 q2 where [simp]: "q=q1@v#q2"
by (auto simp: in_set_conv_decomp)
from P have "path vE v (v#q2@q1) v"
by (auto simp: path_conc_conv path_cons_conv)
from path_in_lastnode[OF this ‹p≠[]› ‹v∈last p› ‹v∈last p›]
show "set q ⊆ last p" by simp
from P show "u∈last p"
apply (cases q, simp)
apply simp
using ‹set q ⊆ last p›
apply (auto simp: path_cons_conv)
done
qed
lemma no_D_p_edges: "E ∩ D × ⋃(set p) = {}"
using D_closed p_not_D by auto
lemma idx_of_props:
assumes ON_STACK: "v∈⋃(set p)"
shows
"idx_of p v < length p" and
"v ∈ p ! idx_of p v"
using idx_of_props[OF _ assms] p_disjoint_sym by blast+
end
subsubsection ‹Auxiliary Lemmas Regarding the Operations›
context invar_loc
begin
lemma pE_fin: "p=[] ⟹ pE={#}"
using pE_E_from_p by auto
lemma lastp_un_D_closed:
assumes NE: "p ≠ []"
assumes NO': "(set_mset pE) ∩ (last p × UNIV) = {}"
shows "E``(last p ∪ D) ⊆ (last p ∪ D)"
proof (intro subsetI, elim ImageE)
have "(E - vE) ∩ (last p × UNIV) = (E ∩ ⋃ (set p) × UNIV - vE) ∩ (last p × UNIV)"
using NE by blast
also from pE_if_not_visited have "... ⊆ (set_mset pE) ∩ (last p × UNIV)"
by fast
finally have NO: "(E - vE) ∩ (last p × UNIV) = {}" using NO' by blast
let ?i = "length p - 1"
from NE have [simp]: "last p = p!?i" by (metis last_conv_nth)
fix u v
assume E: "(u,v)∈E"
assume UI: "u∈last p ∪ D" hence "u∈p!?i ∪ D" by simp
{
assume "u∈last p" "v∉last p"
moreover from E NO ‹u∈last p› have "(u,v)∈vE" by auto
ultimately have "v∈D ∨ v∈⋃(set p)"
using vE_touched unfolding touched_def by auto
moreover {
assume "v∈⋃(set p)"
then obtain j where V: "j<length p" "v∈p!j"
by (metis UnionE in_set_conv_nth)
with ‹v∉last p› have "j<?i" by (cases "j=?i") auto
from vE_no_back[OF ‹j<?i› _] ‹(u,v)∈vE› V ‹u∈last p› have False by auto
} ultimately have "v∈D" by blast
} with E UI D_closed show "v∈last p ∪ D" by auto
qed
end
subsubsection ‹Preservation of Invariant by Operations›
context fr_graph
begin
lemma (in outer_invar_loc) invar_initial_aux:
assumes "v0∈it - D"
shows "initial v0 D ≤ SPEC (invar v0 D)"
unfolding invar_def initial_def out_edges_def
apply refine_vcg
apply clarsimp
apply unfold_locales
apply simp_all
using assms it_initial apply auto []
using D_reachable it_initial assms apply auto []
using D_closed apply auto []
using assms apply auto []
unfolding touched_def
apply(subgoal_tac "E ∩ D × UNIV ⊆ E ∩ D × D")
apply blast
using D_closed apply blast
apply blast
done
lemma invar_initial:
"⟦outer_invar it D0; v0∈it; v0∉D0⟧ ⟹ initial v0 D0 ≤ SPEC (invar v0 D0)"
unfolding outer_invar_def
apply (drule outer_invar_loc.invar_initial_aux)
by auto
lemma outer_invar_initial[simp, intro!]: "outer_invar V0 {}"
unfolding outer_invar_def
apply unfold_locales
by auto
lemma invar_pop:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes NE[simp]: "p≠[]"
assumes NO': "(set_mset pE) ∩ (last p × UNIV) = {}"
shows "invar v0 D0 (pop (p,D,pE,vE))"
unfolding invar_def pop_def
apply simp
proof -
from INV interpret invar_loc E V0 v0 D0 p D pE unfolding invar_def by simp
have [simp]: "set p = insert (last p) (set (butlast p))"
using NE by (cases p rule: rev_cases) auto
from p_disjoint have lp_dj_blp: "last p ∩ ⋃(set (butlast p)) = {}"
apply (cases p rule: rev_cases)
apply simp
apply (fastforce simp: in_set_conv_nth nth_append)
done
{
fix i
assume A: "Suc i < length (butlast p)"
hence A': "Suc i < length p" by auto
from nth_butlast[of i p] A have [simp]: "butlast p ! i = p ! i" by auto
from nth_butlast[of "Suc i" p] A
have [simp]: "butlast p ! Suc i = p ! Suc i" by auto
from p_connected[OF A']
have "butlast p ! i × butlast p ! Suc i ∩ vE ≠ {}"
by simp
} note AUX_p_connected = this
have AUX_last_p_visited: "E ∩ last p × UNIV ⊆ vE"
proof safe
fix u v
assume UVE: "(u,v) ∈ E"
and ULP: "u ∈ last p"
hence "(u,v) ∉# pE" using NO' by blast
moreover have "(u,v) ∈ E ∩ (⋃(set p)) × UNIV" using ULP UVE by auto
ultimately show "(u,v) ∈ vE" using pE_if_not_visited by blast
qed
show "invar_loc E V0 v0 D0 (butlast p) (last p ∪ D) pE vE"
apply unfold_locales
subgoal by simp
subgoal using D_incr by auto
subgoal using vE_ss_E by simp
subgoal using pE_E_from_p NO' by auto []
subgoal using E_from_p_touched by (auto simp: touched_def) []
subgoal using D_reachable p_reachable NE by auto []
subgoal by (rule AUX_p_connected, assumption+) []
subgoal using p_disjoint by (simp add: nth_butlast)
subgoal using p_sc by simp
subgoal using root_v0 by (cases p rule: rev_cases; auto)
subgoal using root_v0 p_empty_v0 by (cases p rule: rev_cases; auto)
subgoal apply (rule lastp_un_D_closed) using insert NO' by auto
subgoal using vE_no_back by (auto simp: nth_butlast)
subgoal using p_not_D lp_dj_blp by auto
subgoal using D_vis AUX_last_p_visited by blast
subgoal using vE_touched unfolding touched_def by auto
subgoal using pE_if_not_visited by auto
done
qed
lemma set_mset_subtract_cases: obtains "set_mset (m-{#x#}) = set_mset m" | "set_mset (m-{#x#}) = set_mset m - {x}"
by (meson at_most_one_mset_mset_diff more_than_one_mset_mset_diff)
lemma invar_collapse:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes NE[simp]: "p≠[]"
assumes E: "(u,v)∈#pE" and "u∈last p"
assumes BACK: "v∈⋃(set p)"
defines "i ≡ idx_of p v"
defines "p' ≡ collapse_aux p i"
shows "invar v0 D0 (collapse v (p,D,pE - {#(u,v)#},insert (u,v) vE))"
unfolding invar_def collapse_def
apply simp
unfolding i_def[symmetric] p'_def[symmetric]
proof -
from INV interpret invar_loc E V0 v0 D0 p D pE unfolding invar_def by simp
let ?thesis="invar_loc E V0 v0 D0 p' D (pE - {#(u,v)#}) (insert (u,v) vE)"
have SETP'[simp]: "⋃(set p') = ⋃(set p)" unfolding p'_def by simp
have IL: "i < length p" and VMEM: "v∈p!i"
using idx_of_props[OF BACK] unfolding i_def by auto
have [simp]: "length p' = Suc i"
unfolding p'_def collapse_aux_def using IL by auto
have P'_IDX_SS: "∀j<Suc i. p!j ⊆ p'!j"
unfolding p'_def collapse_aux_def using IL
by (auto simp add: nth_append path_seg_drop)
from ‹u∈last p› have "u∈p!(length p - 1)" by (auto simp: last_conv_nth)
have defs_fold:
"touched p' D = ltouched"
by (simp_all add: p'_def E)
{
fix j
assume A: "Suc j < length p'"
hence "Suc j < length p" using IL by simp
note p_connected[OF this]
moreover from P'_IDX_SS A have "p!j⊆p'!j" and "p!Suc j ⊆ p'!Suc j"
by auto
ultimately have "p' ! j × p' ! Suc j ∩ insert (u,v) vE ≠ {}"
by blast
} note AUX_p_connected = this
have P_IDX_EQ[simp]: "∀j. j < i ⟶ p'!j = p!j"
unfolding p'_def collapse_aux_def using IL
by (auto simp: nth_append)
have P'_LAST[simp]: "p'!i = path_seg p i (length p)" (is "_ = ?last_cnode")
unfolding p'_def collapse_aux_def using IL
by (auto simp: nth_append path_seg_drop)
{
fix j k
assume A: "j < k" "k < length p'"
have "p' ! j ∩ p' ! k = {}"
proof (safe, simp)
fix v
assume "v∈p'!j" and "v∈p'!k"
with A have "v∈p!j" by simp
show False proof (cases)
assume "k=i"
with ‹v∈p'!k› obtain k' where "v∈p!k'" "i≤k'" "k'<length p"
by (auto simp: path_seg_def)
hence "p ! j ∩ p ! k' = {}"
using A by (auto intro!: p_disjoint)
with ‹v∈p!j› ‹v∈p!k'› show False by auto
next
assume "k≠i" with A have "k<i" by simp
hence "k<length p" using IL by simp
note p_disjoint[OF ‹j<k› this]
also have "p!j = p'!j" using ‹j<k› ‹k<i› by simp
also have "p!k = p'!k" using ‹k<i› by simp
finally show False using ‹v∈p'!j› ‹v∈p'!k› by auto
qed
qed
} note AUX_p_disjoint = this
{
fix U
assume A: "U∈set p'"
then obtain j where "j<Suc i" and [simp]: "U=p'!j"
by (auto simp: in_set_conv_nth)
hence "U × U ⊆ (insert (u, v) vE ∩ U × U)⇧*"
proof cases
assume [simp]: "j=i"
show ?thesis proof (clarsimp)
fix x y
assume "x∈path_seg p i (length p)" "y∈path_seg p i (length p)"
then obtain ix iy where
IX: "x∈p!ix" "i≤ix" "ix<length p" and
IY: "y∈p!iy" "i≤iy" "iy<length p"
by (auto simp: path_seg_def)
from IX have SS1: "path_seg p ix (length p) ⊆ ?last_cnode"
by (subst path_seg_ss_eq) auto
from IY have SS2: "path_seg p i (Suc iy) ⊆ ?last_cnode"
by (subst path_seg_ss_eq) auto
let ?rE = "λR. (vE ∩ R×R)"
let ?E = "(insert (u,v) vE ∩ ?last_cnode × ?last_cnode)"
from pathI[OF ‹x∈p!ix› ‹u∈p!(length p - 1)›] have
"(x,u)∈(?rE (path_seg p ix (Suc (length p - 1))))⇧*" using IX by auto
hence "(x,u)∈?E⇧*"
apply (rule rtrancl_mono_mp[rotated])
using SS1
by auto
also have "(u,v)∈?E" using ‹i<length p›
apply (clarsimp)
apply (intro conjI)
apply (rule rev_subsetD[OF ‹u∈p!(length p - 1)›])
apply (simp)
apply (rule rev_subsetD[OF VMEM])
apply (simp)
done
also
from pathI[OF ‹v∈p!i› ‹y∈p!iy›] have
"(v,y)∈(?rE (path_seg p i (Suc iy)))⇧*" using IY by auto
hence "(v,y)∈?E⇧*"
apply (rule rtrancl_mono_mp[rotated])
using SS2
by auto
finally show "(x,y)∈?E⇧*" .
qed
next
assume "j≠i"
with ‹j<Suc i› have [simp]: "j<i" by simp
with ‹i<length p› have "p!j∈set p"
by (metis Suc_lessD in_set_conv_nth less_trans_Suc)
thus ?thesis using p_sc[of U] ‹p!j∈set p›
apply (clarsimp)
apply (subgoal_tac "(a,b)∈(vE ∩ p ! j × p ! j)⇧*")
apply (erule rtrancl_mono_mp[rotated])
apply auto
done
qed
} note AUX_p_sc = this
{ fix j k
assume A: "j<k" "k<length p'"
hence "j<i" by simp
have "insert (u, v) vE ∩ p' ! k × p' ! j = {}"
proof -
have "{(u,v)} ∩ p' ! k × p' ! j = {}"
apply auto
by (metis IL P_IDX_EQ Suc_lessD VMEM ‹j < i›
less_irrefl_nat less_trans_Suc p_disjoint_sym)
moreover have "vE ∩ p' ! k × p' ! j = {}"
proof (cases "k<i")
case True thus ?thesis
using vE_no_back[of j k] A ‹i<length p› by auto
next
case False with A have [simp]: "k=i" by simp
show ?thesis proof (rule disjointI, clarsimp simp: ‹j<i›)
fix x y
assume B: "(x,y)∈vE" "x∈path_seg p i (length p)" "y∈p!j"
then obtain ix where "x∈p!ix" "i≤ix" "ix<length p"
by (auto simp: path_seg_def)
moreover with A have "j<ix" by simp
ultimately show False using vE_no_back[of j ix] B by auto
qed
qed
ultimately show ?thesis by blast
qed
} note AUX_vE_no_back = this
have V_TOUCHED: "v∈ltouched" using BACK path_touched by auto
note pE_removed_cases = set_mset_subtract_cases[of pE "(u,v)"]
show ?thesis
apply unfold_locales
unfolding defs_fold
subgoal by simp
subgoal using D_incr by auto []
subgoal using vE_ss_E E pE_E_from_p by blast
subgoal using pE_E_from_p in_diffD SETP' subset_eq by metis
subgoal
apply (simp) using E_from_p_touched V_TOUCHED
apply (cases rule: pE_removed_cases)
by (auto)
subgoal by (rule D_reachable)
subgoal by (rule AUX_p_connected)
subgoal by (rule AUX_p_disjoint)
subgoal for U by (rule AUX_p_sc)
subgoal
using root_v0
apply (cases i)
apply (simp add: p'_def collapse_aux_def)
apply (metis NE hd_in_set)
apply (cases p, simp_all add: p'_def collapse_aux_def) []
done
subgoal by (simp add: p'_def collapse_aux_def)
subgoal by (rule D_closed)
subgoal by (drule (1) AUX_vE_no_back, auto)
subgoal using p_not_D by simp
subgoal using D_vis by auto
subgoal
using V_TOUCHED vE_touched
apply clarsimp
using ‹u∈last p› path_touched
by fastforce
subgoal by (smt (verit) Diff_eq_empty_iff Diff_insert Diff_insert2 E SETP' insert_Diff pE_if_not_visited pE_removed_cases subset_insertI)
done
qed
lemma invar_push:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes NE[simp]: "p≠[]"
assumes E: "(u,v)∈#pE" and UIL: "u∈last p"
assumes VNE: "v∉⋃(set p)" "v∉D"
shows "push v (p,D,pE - {#(u,v)#}, insert (u,v) vE) ≤ SPEC (invar v0 D0)"
unfolding invar_def push_def out_edges_def
apply refine_vcg
apply clarsimp
proof -
fix succE
assume succE_v: "set_mset succE = E ∩ {v} × UNIV"
from INV interpret invar_loc E V0 v0 D0 p D pE unfolding invar_def by simp
let ?thesis
= "invar_loc E V0 v0 D0 (p @ [{v}]) D (pE - {#(u, v)#} + succE) (insert (u, v) vE)"
note defs_fold = touched_push
note pE_removed_cases = set_mset_subtract_cases[of pE "(u,v)"]
{
fix i
assume SILL: "Suc i < length (p @ [{v}])"
have "(p @ [{v}]) ! i × (p @ [{v}]) ! Suc i
∩ insert (u, v) vE ≠ {}"
proof (cases "i = length p - 1")
case True thus ?thesis using SILL E pE_E_from_p UIL VNE
by (simp add: nth_append last_conv_nth)
next
case False
with SILL have SILL': "Suc i < length p" by simp
with SILL' VNE have X1: "v∉p!i" "v∉p!Suc i" by auto
from p_connected[OF SILL'] obtain a b where
"a∈p!i" "b∈p!Suc i" "(a,b)∈vE" by auto
with ‹a∈p!i› ‹b∈p!Suc i›
show ?thesis using SILL'
by (simp add: nth_append; blast)
qed
} note AUX_p_connected = this
{
fix U
assume A: "U ∈ set (p @ [{v}])"
have "U × U ⊆ (insert (u, v) vE ∩ U × U)⇧*"
proof cases
assume "U∈set p"
with p_sc have "U×U ⊆ (vE ∩ U×U)⇧*" .
thus ?thesis
by (metis (lifting, no_types) Int_insert_left_if0 Int_insert_left_if1
in_mono insert_subset rtrancl_mono_mp subsetI)
next
assume "U∉set p" with A have "U={v}" by simp
thus ?thesis by auto
qed
} note AUX_p_sc = this
{
fix i j
assume A: "i < j" "j < length (p @ [{v}])"
have "insert (u, v) vE ∩ (p @ [{v}]) ! j × (p @ [{v}]) ! i = {}"
proof (cases "j=length p")
case False with A have "j<length p" by simp
from vE_no_back ‹i<j› this VNE show ?thesis
by (auto simp add: nth_append)
next
from p_not_D A have PDDJ: "p!i ∩ D = {}"
by (auto simp: Sup_inf_eq_bot_iff)
case True thus ?thesis
using A apply (simp add: nth_append)
apply (rule conjI)
using UIL A p_disjoint_sym
apply (metis Misc.last_in_set NE UnionI VNE(1))
using vE_touched VNE PDDJ apply (auto simp: touched_def) []
done
qed
} note AUX_vE_no_back = this
have U_TOUCHED: "u∈ltouched"
using ‹u∈last p› path_touched by fastforce
show ?thesis
apply unfold_locales
unfolding defs_fold
subgoal by simp
subgoal using D_incr by auto []
subgoal using vE_ss_E E pE_E_from_p by blast
subgoal using pE_E_from_p succE_v by (auto dest: in_diffD)
subgoal
apply (cases rule: pE_removed_cases)
using E_from_p_touched VNE succE_v
by auto
subgoal by (rule D_reachable)
subgoal for i by (rule AUX_p_connected)
subgoal using p_disjoint ‹v∉⋃(set p)› by (auto simp: nth_append)
subgoal for U by (rule AUX_p_sc)
subgoal using root_v0 by simp
subgoal by simp
subgoal by (rule D_closed)
subgoal for i j by (rule AUX_vE_no_back)
subgoal using p_not_D VNE by auto
subgoal using D_vis by auto
subgoal using vE_touched U_TOUCHED by auto
subgoal
apply (cases rule: pE_removed_cases)
using succE_v pE_if_not_visited
by auto
done
qed
lemma invar_pE_is_node:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes E: "(u,v)∈#pE"
shows "v∈E⇧*``V0"
proof -
from INV interpret invar_loc E V0 v0 D0 p D pE unfolding invar_def by simp
from E pE_reachable show ?thesis by blast
qed
lemma invar_skip:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes NE[simp]: "p≠[]"
assumes E: "(u,v)∈#pE" and UIL: "u∈last p"
assumes VNP: "v∉⋃(set p)" and VD: "v∈D"
shows "invar v0 D0 (p,D,pE - {#(u, v)#}, insert (u,v) vE)"
unfolding invar_def
apply simp
proof -
from INV interpret invar_loc E V0 v0 D0 p D pE unfolding invar_def by simp
let ?thesis = "invar_loc E V0 v0 D0 p D (pE - {#(u, v)#}) (insert (u,v) vE)"
note pE_removed_cases = set_mset_subtract_cases[of pE "(u,v)"]
have U_TOUCHED: "u∈ltouched"
using ‹u∈last p› path_touched by fastforce
have V_TOUCHED: "v ∈ ltouched"
using ‹v∈D› D_touched by blast
show ?thesis
apply unfold_locales
subgoal by simp
subgoal using D_incr by auto
subgoal using vE_ss_E E pE_E_from_p by blast
subgoal using pE_E_from_p apply (cases rule:pE_removed_cases) by auto
subgoal using E_from_p_touched VD apply (cases rule:pE_removed_cases) by (auto simp: touched_def)
subgoal by (rule D_reachable)
subgoal using p_connected by auto []
subgoal by (rule p_disjoint)
subgoal
apply (drule p_sc)
apply (erule order_trans)
apply (rule rtrancl_mono)
by blast []
subgoal by (rule root_v0)
subgoal by (rule p_empty_v0)
subgoal by (rule D_closed)
subgoal for i j
using vE_no_back VD p_not_D
apply clarsimp
by (metis Suc_lessD UnionI VNP less_trans_Suc nth_mem)
subgoal by (rule p_not_D)
subgoal using D_vis by auto
subgoal using vE_touched U_TOUCHED V_TOUCHED by auto
subgoal
apply (cases rule: pE_removed_cases)
using pE_if_not_visited
by auto
done
qed
lemma fin_D_is_reachable:
assumes INV: "invar v0 D0 ([], D, pE, vE)"
shows "D ⊇ E⇧*``{v0}"
proof -
from INV interpret invar_loc E V0 v0 D0 "[]" D pE vE unfolding invar_def by auto
from p_empty_v0 rtrancl_reachable_induct[OF order_refl D_closed] D_reachable
show ?thesis by auto
qed
lemma fin_reachable_path:
assumes INV: "invar v0 D0 ([], D, pE, vE)"
assumes UR: "u∈E⇧*``{v0}"
shows "path vE u q v ⟷ path E u q v"
proof -
from INV interpret invar_loc E V0 v0 D0 "[]" D pE vE unfolding invar_def by auto
show ?thesis
proof
assume "path vE u q v"
thus "path E u q v" using path_mono[OF vE_ss_E] by blast
next
assume "path E u q v"
thus "path vE u q v" using UR
proof induction
case (path_prepend u v p w)
with fin_D_is_reachable[OF INV] have "u∈D" by auto
with D_closed ‹(u,v)∈E› have "v∈D" by auto
from path_prepend.prems path_prepend.hyps have "v∈E⇧*``{v0}" by auto
with path_prepend.IH fin_D_is_reachable[OF INV] have "path vE v p w"
by simp
moreover from ‹u∈D› ‹v∈D› ‹(u,v)∈E› D_vis have "(u,v)∈vE" by auto
ultimately show ?case by (auto simp: path_cons_conv)
qed simp
qed
qed
lemma invar_outer_newnode:
assumes A: "v0∉D0" "v0∈it"
assumes OINV: "outer_invar it D0"
assumes INV: "invar v0 D0 ([],D',pE,vE)"
shows "outer_invar (it-{v0}) D'"
proof -
from OINV interpret outer_invar_loc E V0 it D0 unfolding outer_invar_def .
from INV interpret inv: invar_loc E V0 v0 D0 "[]" D' pE vE
unfolding invar_def by simp
from fin_D_is_reachable[OF INV] have [simp]: "v0∈D'" by auto
show ?thesis
unfolding outer_invar_def
apply unfold_locales
using it_initial apply auto []
using it_done inv.D_incr apply auto []
using inv.D_reachable apply assumption
using inv.D_closed apply assumption
done
qed
lemma invar_outer_Dnode:
assumes A: "v0∈D0" "v0∈it"
assumes OINV: "outer_invar it D0"
shows "outer_invar (it-{v0}) D0"
proof -
from OINV interpret outer_invar_loc E V0 it D0 unfolding outer_invar_def .
show ?thesis
unfolding outer_invar_def
apply unfold_locales
using it_initial apply auto []
using it_done A apply auto []
using D_reachable apply assumption
using D_closed apply assumption
done
qed
lemma pE_fin': "invar x σ ([], D, pE, vE) ⟹ pE={#}"
unfolding invar_def by (simp add: invar_loc.pE_fin)
end
subsubsection ‹Termination›
context invar_loc
begin
lemma reachable_finite[simp, intro!]: "finite (reachable_edges v0)"
proof -
have "reachable_edges v0 ⊆ E⇧*``{v0} × E⇧*``{v0}"
unfolding reachable_edges_def
by auto
thus ?thesis
by (rule finite_subset) simp
qed
lemma unproc_finite[simp, intro!]: "finite (unproc_edges v0 vE)"
unfolding unproc_edges_def
by auto
lemma pE_reachable_edges: "set_mset pE ⊆ reachable_edges v0"
using pE_reachable pE_E_from_p unfolding reachable_edges_def
by blast
end
context fr_graph
begin
lemma abs_wf_pop:
assumes NE[simp]: "p≠[]"
shows "(pop (p,D,pE,vE), (p, D, pE,vE)) ∈ abs_wf_rel v0"
unfolding pop_def
by (auto simp: abs_wf_rel_def)
lemma abs_wf_take_edge:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes E: "(u,v) ∈# pE"
shows "((p',D',pE-{#(u,v)#}, insert(u,v) vE), (p, D, pE, vE))∈ abs_wf_rel v0"
proof -
from INV interpret invar_loc E V0 v0 D0 p D pE unfolding invar_def by simp
have "size (pE - {#(u,v)#}) < size pE" using E
by (simp add: size_Diff1_less)
then show ?thesis
using assms
by (clarsimp simp: abs_wf_rel_def unproc_edges_def)
qed
lemma abs_wf_collapse:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes E: "(u,v)∈#pE"
shows "(collapse v (p,D,pE-{#(u,v)#}, insert(u,v) vE), (p, D, pE, vE))∈ abs_wf_rel v0"
using INV E
unfolding collapse_def
by (simp add: abs_wf_take_edge)
lemma abs_wf_push:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes NE[simp]: "p≠[]"
assumes E: "(u,v)∈#pE" "u∈last p" and A: "v∉D" "v∉⋃(set p)"
shows "push v (p,D,pE-{#(u,v)#},insert (u,v) vE) ≤ SPEC (λr. (r, (p, D, pE, vE)) ∈ abs_wf_rel v0)"
unfolding push_def out_edges_def
apply refine_vcg
apply clarsimp
proof -
fix succE
assume succE_v: "set_mset succE = E ∩ {v} × UNIV"
from INV interpret invar_loc E V0 v0 D0 p D pE vE unfolding invar_def by simp
let ?thesis
= "((p@[{v}], D, pE - {#(u, v)#} + succE, insert (u, v) vE), (p, D, pE, vE)) ∈ abs_wf_rel v0"
have "(u,v)∉vE" using vE_touched A unfolding touched_def by blast
hence "unproc_edges v0 (insert (u,v) vE) ⊂ unproc_edges v0 vE"
unfolding unproc_edges_def using pE_reachable_edges E by blast
thus ?thesis
unfolding abs_wf_rel_def by simp
qed
lemma abs_wf_skip:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes E: "(u,v)∈#pE"
shows "((p, D, pE-{#(u,v)#}, insert (u,v) vE), (p, D, pE, vE)) ∈ abs_wf_rel v0"
using assms abs_wf_take_edge by simp
end
subsubsection ‹Main Correctness Theorem›
context fr_graph
begin
lemmas invar_preserve =
invar_pop invar_skip invar_collapse
abs_wf_pop abs_wf_collapse abs_wf_skip
outer_invar_initial invar_outer_newnode invar_outer_Dnode
invar_pE_is_node
lemma invar_rel_push:
assumes INV: "invar v0 D0 (p,D,pE,vE)"
assumes NE[simp]: "p≠[]"
assumes E: "(u,v)∈#pE" and UIL: "u∈last p"
assumes VNE: "v∉⋃(set p)" "v∉D"
shows "push v (p,D,pE - {#(u,v)#}, insert (u,v) vE) ≤ SPEC (λr. invar v0 D0 r ∧ (r,(p,D,pE,vE)) ∈ abs_wf_rel v0)"
apply (rule SPEC_rule_conjI[OF invar_push abs_wf_push])
by fact+
lemmas [refine_vcg] = invar_initial
text ‹The main correctness theorem for the dummy-algorithm just states that
it satisfies the invariant when finished, and the path is empty.
›
theorem skeleton_spec: "skeleton ≤ SPEC (λD. outer_invar {} D)"
proof -
note [simp del] = Union_iff
note [[goals_limit = 7]]
note [refine_vcg del] = WHILEIT_rule
note [simp] = invar_pE_is_node
show ?thesis
unfolding skeleton_def select_edge_def select_def
apply (refine_vcg)
apply (vc_solve solve: invar_preserve simp: pE_fin')
apply auto
apply (refine_vcg WHILEIT_rule[OF abs_wf_rel_wf])
apply (vc_solve solve: invar_preserve simp: pE_fin')
apply auto
apply (refine_vcg invar_rel_push)
done
qed
end
subsection "Consequences of Invariant when Finished"
context fr_graph
begin
lemma fin_outer_D_is_reachable:
assumes INV: "outer_invar {} D"
shows "D = E⇧*``V0"
proof -
from INV interpret outer_invar_loc E V0 "{}" D unfolding outer_invar_def by auto
from it_done rtrancl_reachable_induct[OF order_refl D_closed] D_reachable
show ?thesis by auto
qed
end
section ‹Refinement to Gabow's Data Structure›
text ‹
The implementation due to Gabow \cite{Gabow2000} represents a path as
a stack ‹S› of single nodes, and a stack ‹B› that contains the
boundaries of the collapsed segments. Moreover, a map ‹I› maps nodes
to their stack indices, or SCC index once they are done.
As we use a tail-recursive formulation, we use another stack
‹P :: ('v × 'v list) list› to represent the pending edges.
›
subsection ‹Preliminaries›
primrec find_max_nat :: "nat ⇒ (nat⇒bool) ⇒ nat"
where
"find_max_nat 0 _ = 0"
| "find_max_nat (Suc n) P = (if (P n) then n else find_max_nat n P)"
lemma find_max_nat_correct:
"⟦P 0; 0<u⟧ ⟹ find_max_nat u P = Max {i. i<u ∧ P i}"
apply (induction u)
apply auto
apply (rule Max_eqI[THEN sym])
apply auto [3]
apply (case_tac u)
apply simp
apply clarsimp
by (metis less_SucI less_antisym)
lemma find_max_nat_param[param]:
assumes "(n,n')∈nat_rel"
assumes "⋀j j'. ⟦(j,j')∈nat_rel; j'<n'⟧ ⟹ (P j,P' j')∈bool_rel"
shows "(find_max_nat n P,find_max_nat n' P') ∈ nat_rel"
using assms
by (induction n arbitrary: n') auto
context begin interpretation autoref_syn .
lemma find_max_nat_autoref[autoref_rules]:
assumes "(n,n')∈nat_rel"
assumes "⋀j j'. ⟦(j,j')∈nat_rel; j'<n'⟧ ⟹ (P j,P'$j')∈bool_rel"
shows "(find_max_nat n P,
(OP find_max_nat ::: nat_rel → (nat_rel→bool_rel) → nat_rel) $n'$P'
) ∈ nat_rel"
using find_max_nat_param[OF assms]
by simp
end
subsection ‹Gabow's Datastructure›
subsubsection ‹Definition and Invariant›