Theory FiniteGraph
theory FiniteGraph
imports Main
begin
section ‹Specification of a finite directed graph›
text‹A graph ‹G=(V,E)› consits of a set of vertices ‹V›, also called nodes,
and a set of edges ‹E›. The edges are tuples of vertices. Both,
the set of vertices and edges is finite.›
section ‹Graph›
subsection‹Definitions›
text ‹A graph is represented by a record.›
record 'v graph =
nodes :: "'v set"
edges :: "('v ×'v) set"
text ‹In a well-formed graph, edges only go from nodes to nodes.›
locale wf_graph =
fixes G :: "'v graph"
assumes E_wf: "fst ` (edges G) ⊆ (nodes G)"
"snd ` (edges G) ⊆ (nodes G)"
and finiteE: "finite (edges G)"
and finiteV: "finite (nodes G)"
begin
abbreviation "V ≡ (nodes G)"
abbreviation "E ≡ (edges G)"
lemma E_wfD: assumes "(v,v') ∈ E"
shows "v ∈ V" "v' ∈ V"
apply -
apply (rule subsetD[OF E_wf(1)])
using assms apply force
apply (rule subsetD[OF E_wf(2)])
using assms apply force
done
lemma E_wfD2: "∀e ∈ E. fst e ∈ V ∧ snd e ∈ V"
by (auto simp add: E_wfD)
end
subsection ‹Basic operations on Graphs›
text ‹The empty graph.›
definition empty :: "'v graph" where
"empty ≡ ⦇ nodes = {}, edges = {} ⦈"
text ‹Adds a node to a graph.›
definition add_node :: "'v ⇒ 'v graph ⇒ 'v graph" where
"add_node v G ≡ ⦇ nodes = ({v} ∪ (nodes G)), edges=edges G ⦈"
text ‹Deletes a node from a graph. Also deletes all adjacent edges.›
definition delete_node where "delete_node v G ≡ ⦇
nodes = (nodes G) - {v},
edges = {(e1, e2). (e1, e2) ∈ edges G ∧ e1 ≠ v ∧ e2 ≠ v}
⦈"
text ‹Adds an edge to a graph.›
definition add_edge where
"add_edge v v' G = ⦇nodes = nodes G ∪ {v,v'}, edges = {(v, v')} ∪ edges G ⦈"
text ‹Deletes an edge from a graph.›
definition delete_edge where "delete_edge v v' G ≡ ⦇
nodes = nodes G,
edges = {(e1,e2). (e1, e2) ∈ edges G ∧ (e1,e2) ≠ (v,v')}
⦈"
definition delete_edges::"'v graph ⇒ ('v × 'v) set ⇒ 'v graph" where
"delete_edges G es ≡ ⦇
nodes = nodes G,
edges = {(e1,e2). (e1, e2) ∈ edges G ∧ (e1,e2) ∉ es}
⦈"
fun delete_edges_list::"'v graph ⇒ ('v × 'v) list ⇒ 'v graph" where
"delete_edges_list G [] = G"|
"delete_edges_list G ((v,v')#es) = delete_edges_list (delete_edge v v' G) es"
definition fully_connected :: "'v graph ⇒ 'v graph" where
"fully_connected G ≡ ⦇nodes = nodes G, edges = nodes G × nodes G ⦈"
text ‹Extended graph operations›
text ‹Reflexive transitive successors of a node. Or: All reachable nodes for ‹v› including ‹v›.›
definition succ_rtran :: "'v graph ⇒ 'v ⇒ 'v set" where
"succ_rtran G v = {e2. (v,e2) ∈ (edges G)⇧*}"
text ‹Transitive successors of a node. Or: All reachable nodes for ‹v›.›
definition succ_tran :: "'v graph ⇒ 'v ⇒ 'v set" where
"succ_tran G v = {e2. (v,e2) ∈ (edges G)⇧+}"
lemma succ_tran_finite: "wf_graph G ⟹ finite (succ_tran G v)"
proof -
assume "wf_graph G"
from wf_graph.finiteE[OF this] have "finite ((edges G)⇧+)" using finite_trancl[symmetric, of "edges G"] by metis
from this have "finite {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by simp
from this have finite: "finite (snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+})" by (metis finite_imageI)
have "{(e1,e2). (e1, e2) ∈ (edges G)⇧+ ∧ e1 = v} ⊆ {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by blast
have 1: "snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+ ∧ e1 = v} ⊆ snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by blast
have 2: "snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+ ∧ e1 = v} = {e2. (v,e2) ∈ (edges G)⇧+}" by force
from 1 2 have "{e2. (v,e2) ∈ (edges G)⇧+} ⊆ snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by blast
from this finite have "finite {e2. (v, e2) ∈ (edges G)⇧+}" by (metis finite_subset)
thus "finite (succ_tran G v)" using succ_tran_def by metis
qed
text‹If there is no edge leaving from ‹v›, then ‹v› has no successors›
lemma succ_tran_empty: "⟦ wf_graph G; v ∉ (fst ` edges G) ⟧ ⟹ succ_tran G v = {}"
unfolding succ_tran_def using image_iff tranclD by fastforce
text‹@{const succ_tran} is subset of nodes›
lemma succ_tran_subseteq_nodes: "⟦ wf_graph G ⟧ ⟹ succ_tran G v ⊆ nodes G"
unfolding succ_tran_def using tranclD2 wf_graph.E_wfD(2) by fastforce
text ‹The number of reachable nodes from ‹v››
definition num_reachable :: "'v graph ⇒ 'v ⇒ nat" where
"num_reachable G v = card (succ_tran G v)"
definition num_reachable_norefl :: "'v graph ⇒ 'v ⇒ nat" where
"num_reachable_norefl G v = card (succ_tran G v - {v})"
text‹@{const card} returns @{term 0} for infinite sets.
Here, for a well-formed graph, if @{const num_reachable} is zero, there are actually no nodes reachable.›
lemma num_reachable_zero: "⟦wf_graph G; num_reachable G v = 0⟧ ⟹ succ_tran G v = {}"
unfolding num_reachable_def
apply(subgoal_tac "finite (succ_tran G v)")
apply(simp)
apply(blast intro: succ_tran_finite)
done
lemma num_succtran_zero: "⟦succ_tran G v = {}⟧ ⟹ num_reachable G v = 0"
unfolding num_reachable_def by simp
lemma num_reachable_zero_iff: "⟦wf_graph G⟧ ⟹ (num_reachable G v = 0) ⟷ (succ_tran G v = {})"
by(metis num_succtran_zero num_reachable_zero)
section‹Undirected Graph›
subsection‹undirected graph simulation›
text ‹Create undirected graph from directed graph by adding backward links›
definition backflows :: "('v × 'v) set ⇒ ('v × 'v) set" where
"backflows E ≡ {(r,s). (s,r) ∈ E}"
definition undirected :: "'v graph ⇒ 'v graph"
where "undirected G = ⦇ nodes = nodes G, edges = (edges G) ∪ {(b,a). (a,b) ∈ edges G} ⦈"
section ‹Graph Lemmas›
lemma graph_eq_intro: "(nodes (G::'a graph) = nodes G') ⟹ (edges G = edges G') ⟹ G = G'" by simp
lemma wf_graph_finite_filterE: "wf_graph G ⟹ finite {(e1, e2). (e1, e2) ∈ edges G ∧ P e1 e2}"
by(simp add: wf_graph.finiteE split_def)
lemma wf_graph_finite_filterV: "wf_graph G ⟹ finite {n. n ∈ nodes G ∧ P n}"
by(simp add: wf_graph.finiteV)
lemma empty_wf[simp]: "wf_graph empty"
unfolding empty_def by unfold_locales auto
lemma nodes_empty[simp]: "nodes empty = {}" unfolding empty_def by simp
lemma edges_empty[simp]: "edges empty = {}" unfolding empty_def by simp
lemma add_node_wf[simp]: "wf_graph g ⟹ wf_graph (add_node v g)"
unfolding add_node_def wf_graph_def by (auto)
lemma delete_node_wf[simp]: "wf_graph G ⟹ wf_graph (delete_node v G)"
by(auto simp add: delete_node_def wf_graph_def wf_graph_finite_filterE)
lemma add_edge_wf[simp]: "wf_graph G ⟹ wf_graph (add_edge v v' G)"
by(auto simp add: add_edge_def add_node_def wf_graph_def)
lemma delete_edge_wf[simp]: "wf_graph G ⟹ wf_graph (delete_edge v v' G)"
by(auto simp add: delete_edge_def add_node_def wf_graph_def split_def)
lemma delete_edges_list_wf[simp]: "wf_graph G ⟹ wf_graph (delete_edges_list G E)"
by(induction E arbitrary: G, simp, force)
lemma delete_edges_wf[simp]: "wf_graph G ⟹ wf_graph (delete_edges G E)"
by(auto simp add: delete_edges_def add_node_def wf_graph_def split_def)
lemma delete_edges_list_set: "delete_edges_list G E = delete_edges G (set E)"
proof(induction E arbitrary: G)
case Nil thus ?case by (simp add: delete_edges_def)
next
case (Cons e E) thus ?case by(cases e)(simp add: delete_edge_def delete_edges_def)
qed
lemma delete_edges_list_union: "delete_edges_list G (ff @ keeps) = delete_edges G (set ff ∪ set keeps)"
by(simp add: delete_edges_list_set)
lemma add_edge_delete_edges_list:
"(add_edge (fst a) (snd a) (delete_edges_list G (a # ff))) = (add_edge (fst a) (snd a) (delete_edges G (set ff)))"
by(auto simp add: delete_edges_list_set delete_edges_def add_edge_def add_node_def)
lemma delete_edges_empty[simp]: "delete_edges G {} = G"
by(simp add: delete_edges_def)
lemma delete_edges_simp2: "delete_edges G E = ⦇ nodes = nodes G, edges = edges G - E⦈"
by(auto simp add: delete_edges_def)
lemma delete_edges_set_nodes: "nodes (delete_edges G E) = nodes G"
by(simp add: delete_edges_simp2)
lemma delete_edges_edges_mono: "E' ⊆ E ⟹ edges (delete_edges G E) ⊆ edges (delete_edges G E')"
by(simp add: delete_edges_def, fast)
lemma delete_edges_edges_empty: "(delete_edges G (edges G)) = G⦇edges := {}⦈"
by(simp add: delete_edges_simp2)
lemma add_delete_edge: "wf_graph (G::'a graph) ⟹ (a,b) ∈ edges G ⟹ add_edge a b (delete_edge a b G) = G"
apply(simp add: delete_edge_def add_edge_def wf_graph_def)
apply(intro graph_eq_intro)
by auto
lemma add_delete_edges: "wf_graph (G::'v graph) ⟹ (a,b) ∈ edges G ⟹ (a,b) ∉ fs ⟹
add_edge a b (delete_edges G (insert (a, b) fs)) = (delete_edges G fs)"
by(auto simp add: delete_edges_simp2 add_edge_def wf_graph_def)
lemma fully_connected_simp: "fully_connected ⦇nodes = N, edges = ignore ⦈≡ ⦇nodes = N, edges = N × N ⦈"
by(simp add: fully_connected_def)
lemma fully_connected_wf: "wf_graph G ⟹ wf_graph (fully_connected G)"
by(simp add: fully_connected_def wf_graph_def)
lemma succ_tran_mono:
"wf_graph ⦇nodes=N, edges=E⦈ ⟹ E' ⊆ E ⟹ succ_tran ⦇nodes=N, edges=E'⦈ v ⊆ succ_tran ⦇nodes=N, edges=E⦈ v"
apply(drule wf_graph.finiteE)
apply(frule_tac A="E'" in rev_finite_subset, simp)
apply(simp add: num_reachable_def)
apply(simp add: succ_tran_def)
apply(metis (lifting, full_types) Collect_mono trancl_mono)
done
lemma num_reachable_mono:
"wf_graph ⦇nodes=N, edges=E⦈ ⟹ E' ⊆ E ⟹ num_reachable ⦇nodes=N, edges=E'⦈ v ≤ num_reachable ⦇nodes=N, edges=E⦈ v"
apply(simp add: num_reachable_def)
apply(frule_tac E'="E'" and v="v" in succ_tran_mono, simp)
apply(frule_tac v="v" in succ_tran_finite)
apply(simp add: card_mono)
done
lemma num_reachable_norefl_mono:
"wf_graph ⦇nodes=N, edges=E⦈ ⟹ E' ⊆ E ⟹ num_reachable_norefl ⦇nodes=N, edges=E'⦈ v ≤ num_reachable_norefl ⦇nodes=N, edges=E⦈ v"
apply(simp add: num_reachable_norefl_def)
apply(frule_tac E'="E'" and v="v" in succ_tran_mono, simp)
apply(frule_tac v="v" in succ_tran_finite)
using card_mono by (metis Diff_mono finite_Diff subset_refl)
lemma backflows_wf:
"wf_graph ⦇nodes=N, edges=E⦈ ⟹ wf_graph ⦇nodes=N, edges=backflows E⦈"
using [[simproc add: finite_Collect]] by(auto simp add: wf_graph_def backflows_def)
lemma undirected_backflows:
"undirected G = ⦇ nodes = nodes G, edges = (edges G) ∪ backflows (edges G) ⦈"
by(simp add: backflows_def undirected_def)
lemma backflows_id:
"backflows (backflows E) = E"
by(simp add: backflows_def)
lemma backflows_finite: "finite E ⟹ finite (backflows E)"
using [[simproc add: finite_Collect]] by(simp add: backflows_def)
lemma backflows_minus_backflows: "backflows (X - backflows Y) = (backflows X) - Y"
by(auto simp add: backflows_def)
lemma backflows_subseteq: "X ⊆ Y ⟷ backflows X ⊆ backflows Y"
by(auto simp add: backflows_def)
lemma backflows_un: "backflows (A ∪ B) = (backflows A) ∪ (backflows B)"
by(auto simp add: backflows_def)
lemma backflows_inter: "backflows (A ∩ B) = (backflows A) ∩ (backflows B)"
by(auto simp add: backflows_def)
lemma backflows_alt_fstsnd: "backflows E = (λe. (snd e, fst e)) ` E"
by(auto simp add: backflows_def, force)
lemmas graph_ops=add_node_def delete_node_def add_edge_def delete_edge_def delete_edges_simp2
lemma wf_graph_remove_edges: "wf_graph ⦇ nodes = V, edges = E ⦈ ⟹ wf_graph ⦇ nodes = V, edges=E - X⦈"
by (metis delete_edges_simp2 delete_edges_wf select_convs(1) select_convs(2))
lemma wf_graph_remove_edges_union:
"wf_graph ⦇ nodes = V, edges = E ∪ E' ⦈ ⟹ wf_graph ⦇ nodes = V, edges=E⦈"
by(auto simp add: wf_graph_def)
lemma wf_graph_union_edges: "⟦ wf_graph ⦇ nodes = V, edges = E ⦈; wf_graph ⦇ nodes = V, edges=E'⦈ ⟧ ⟹
wf_graph ⦇ nodes = V, edges=E ∪ E'⦈"
by(auto simp add: wf_graph_def)
lemma wf_graph_add_subset_edges: "⟦ wf_graph ⦇ nodes = V, edges = E ⦈; E' ⊆ E ⟧ ⟹
wf_graph ⦇ nodes = V, edges= E ∪ E'⦈"
by(auto simp add: wf_graph_def) (metis rev_finite_subset)
text ‹Successors of a node.›
definition succ :: "'v graph ⇒ 'v ⇒ 'v set"
where "succ G v ≡ {v'. (v,v')∈edges G}"
lemma succ_finite[simp, intro]: "finite (edges G) ⟹ finite (succ G v)"
unfolding succ_def
by (rule finite_subset[where B="snd`edges G"]) force+
lemma succ_empty: "succ empty v = {}" unfolding empty_def succ_def by auto
lemma (in wf_graph) succ_subset: "succ G v ⊆ V"
unfolding succ_def using E_wf
by (force)
end