Theory Directed_Graph
section ‹Weighted Directed Graphs›
theory Directed_Graph
imports Common
begin
text ‹
A weighted graph is represented by a function from edges to weights.
For simplicity, we use @{typ enat} as weights, @{term ‹∞›} meaning
that there is no edge.
›
type_synonym ('v) wgraph = "('v × 'v) ⇒ enat"
text ‹We encapsulate weighted graphs into a locale that fixes a graph›
locale WGraph = fixes w :: "'v wgraph"
begin
text ‹Set of edges with finite weight›
definition "edges ≡ {(u,v) . w (u,v) ≠ ∞}"
subsection ‹Paths›
text ‹A path between nodes ‹u› and ‹v› is a list of edge weights
of a sequence of edges from ‹u› to ‹v›.
Note that a path may also contain edges with weight ‹∞›.
›
fun path :: "'v ⇒ enat list ⇒ 'v ⇒ bool" where
"path u [] v ⟷ u=v"
| "path u (l#ls) v ⟷ (∃uh. l = w (u,uh) ∧ path uh ls v)"
lemma path_append[simp]:
"path u (ls1@ls2) v ⟷ (∃w. path u ls1 w ∧ path w ls2 v)"
by (induction ls1 arbitrary: u) auto
text ‹There is a singleton path between every two nodes
(it's weight might be ‹∞›).›
lemma triv_path: "path u [w (u,v)] v" by auto
text ‹Shortcut for the set of all paths between two nodes›
definition "paths u v ≡ {p . path u p v}"
lemma paths_ne: "paths u v ≠ {}" using triv_path unfolding paths_def by blast
text ‹If there is a path from a node inside a set ‹S›, to a node outside
a set ‹S›, this path must contain an edge from inside ‹S› to outside ‹S›.
›
lemma find_leave_edgeE:
assumes "path u p v"
assumes "u∈S" "v∉S"
obtains p1 x y p2
where "p = p1@w (x,y)#p2" "x∈S" "y∉S" "path u p1 x" "path y p2 v"
proof -
have "∃p1 x y p2. p = p1@w (x,y)#p2 ∧ x∈S ∧ y∉S ∧ path u p1 x ∧ path y p2 v"
using assms
proof (induction p arbitrary: u)
case Nil
then show ?case by auto
next
case (Cons a p)
from Cons.prems obtain x where [simp]: "a=w (u,x)" and PX: "path x p v"
by auto
show ?case proof (cases "x∈S")
case False with PX ‹u∈S› show ?thesis by fastforce
next
case True from Cons.IH[OF PX True ‹v∉S›] show ?thesis
by clarsimp (metis WGraph.path.simps(2) append_Cons)
qed
qed
thus ?thesis by (fast intro: that)
qed
subsection ‹Distance›
text ‹The (minimum) distance between two nodes ‹u› and ‹v› is called ‹δ u v›.›
definition "δ u v ≡ LEAST w::enat. w∈sum_list`paths u v"
lemma obtain_shortest_path:
obtains p where "path s p u" "δ s u = sum_list p"
unfolding δ_def using paths_ne
by (smt Collect_empty_eq LeastI_ex WGraph.paths_def imageI image_iff
mem_Collect_eq paths_def)
lemma shortest_path_least:
"path s p u ⟹ δ s u ≤ sum_list p"
unfolding δ_def paths_def
by (simp add: Least_le)
lemma distance_refl[simp]: "δ s s = 0"
using shortest_path_least[of s "[]" s] by auto
lemma distance_direct: "δ s u ≤ w (s, u)"
using shortest_path_least[of s "[w (s,u)]" u] by auto
text ‹Triangle inequality: The distance from ‹s› to ‹v› is shorter than
the distance from ‹s› to ‹u› and the edge weight from ‹u› to ‹v›. ›
lemma triangle: "δ s v ≤ δ s u + w (u,v)"
proof -
have "path s (p@[w (u,v)]) v" if "path s p u" for p using that by auto
then have "(+) (w (u,v)) ` sum_list ` paths s u ⊆ sum_list ` paths s v"
by (fastforce simp: paths_def image_iff simp del: path.simps path_append)
from least_antimono[OF _ this] paths_ne have
"(LEAST y::enat. y ∈ sum_list ` paths s v)
≤ (LEAST x::enat. x ∈ (+) (w (u,v)) ` sum_list ` paths s u)"
by (auto simp: paths_def)
also have "… = (LEAST x. x ∈ sum_list ` paths s u) + w (u,v)"
apply (subst Least_mono[of "(+) (w (u,v))" "sum_list`paths s u"])
subgoal by (auto simp: mono_def)
subgoal by simp (metis paths_def mem_Collect_eq
obtain_shortest_path shortest_path_least)
subgoal by auto
done
finally show ?thesis unfolding δ_def .
qed
text ‹Any prefix of a shortest path is a shortest path itself.
Note: The ‹< ∞› conditions are required to avoid saturation in adding to ‹∞›!
›
lemma shortest_path_prefix:
assumes "path s p⇩1 x" "path x p⇩2 u"
and DSU: "δ s u = sum_list p⇩1 + sum_list p⇩2" "δ s u < ∞"
shows "δ s x = sum_list p⇩1" "δ s x < ∞"
proof -
have "δ s x ≤ sum_list p⇩1" using assms shortest_path_least by blast
moreover have "¬δ s x < sum_list p⇩1" proof
assume "δ s x < sum_list p⇩1"
then obtain p⇩1' where "path s p⇩1' x" "sum_list p⇩1' < sum_list p⇩1"
by (auto intro: obtain_shortest_path[of s x])
with ‹path x p⇩2 u› shortest_path_least[of s "p⇩1'@p⇩2" u] DSU show False
by fastforce
qed
ultimately show "δ s x = sum_list p⇩1" by auto
with DSU show "δ s x < ∞" using le_iff_add by fastforce
qed
end
end