Theory Graph_Theory.Shortest_Path
theory Shortest_Path imports
Arc_Walk
Weighted_Graph
"HOL-Library.Extended_Real"
begin
section ‹Shortest Paths›
context wf_digraph begin
definition μ where
"μ f u v ≡ INF p∈ {p. awalk u p v}. ereal (awalk_cost f p)"
lemma shortest_path_inf:
assumes "¬(u →⇧* v)"
shows "μ f u v = ∞"
proof -
have *: "{p. awalk u p v} = {}"
using assms by (auto simp: reachable_awalk)
show "μ f u v = ∞" unfolding μ_def *
by (simp add: top_ereal_def)
qed
lemma min_cost_le_walk_cost:
assumes "awalk u p v"
shows "μ c u v ≤ awalk_cost c p"
using assms unfolding μ_def by (auto intro: INF_lower2)
lemma pos_cost_pos_awalk_cost:
assumes "awalk u p v"
assumes pos_cost: "⋀e. e ∈ arcs G ⟹ c e ≥ 0"
shows "awalk_cost c p ≥ 0"
using assms by (induct p arbitrary: u) (auto simp: awalk_Cons_iff)
fun mk_cycles_path :: "nat
⇒ 'b awalk ⇒ 'b awalk" where
"mk_cycles_path 0 c = []"
| "mk_cycles_path (Suc n) c = c @ (mk_cycles_path n c)"
lemma mk_cycles_path_awalk:
assumes "awalk u c u"
shows "awalk u (mk_cycles_path n c) u"
using assms by (induct n) (auto simp: awalk_Nil_iff)
lemma mk_cycles_awalk_cost:
assumes "awalk u p u"
shows "awalk_cost c (mk_cycles_path n p) = n * awalk_cost c p"
using assms proof (induct rule: mk_cycles_path.induct)
case 1 show ?case by simp
next
case (2 n p)
have "awalk_cost c (mk_cycles_path (Suc n) p)
= awalk_cost c (p @ (mk_cycles_path n p))"
by simp
also have "… = awalk_cost c p + real n * awalk_cost c p"
proof (cases n)
case 0 then show ?thesis by simp
next
case (Suc n') then show ?thesis
using 2 by simp
qed
also have "… = real (Suc n) * awalk_cost c p"
by (simp add: algebra_simps)
finally show ?case .
qed
lemma inf_over_nats:
fixes a c :: real
assumes "c < 0"
shows "(INF (i :: nat). ereal (a + i * c)) = - ∞"
proof (rule INF_eqI)
fix i :: nat show "- ∞ ≤ a + real i * c" by simp
next
fix y :: ereal
assume "⋀(i :: nat). i ∈ UNIV ⟹ y ≤ a + real i * c"
then have l_assm: "⋀i::nat. y ≤ a + real i * c" by simp
show "y ≤ - ∞"
proof (subst ereal_infty_less_eq, rule ereal_bot)
fix B :: real
obtain real_x where "a + real_x * c ≤ B" using ‹c < 0›
by atomize_elim
(rule exI[where x="(- abs B -a)/c"], auto simp: field_simps)
obtain x :: nat where "a + x * c ≤ B"
proof (atomize_elim, intro exI[where x="nat(ceiling real_x)"] conjI)
have "real (nat(ceiling real_x)) * c ≤ real_x * c"
using ‹c < 0› by (simp add: real_nat_ceiling_ge)
then show "a + nat(ceiling real_x) * c ≤ B"
using ‹a + real_x * c ≤ B› by simp
qed
then show "y ≤ ereal B"
proof -
have "ereal (a + x * c) ≤ ereal B"
using ‹a + x * c ≤ B› by simp
with l_assm show ?thesis by (rule order_trans)
qed
qed
qed
lemma neg_cycle_imp_inf_μ:
assumes walk_p: "awalk u p v"
assumes walk_c: "awalk w c w"
assumes w_in_p: "w ∈ set (awalk_verts u p)"
assumes "awalk_cost f c < 0"
shows "μ f u v = -∞"
proof -
from w_in_p obtain xs ys where pv_decomp: "awalk_verts u p = xs @ w # ys"
by (auto simp: in_set_conv_decomp)
define q r where "q = take (length xs) p" and "r = drop (length xs) p"
define ext_p where "ext_p n = q @ mk_cycles_path n c @ r" for n
have ext_p_cost: "⋀n. awalk_cost f (ext_p n)
= (awalk_cost f q + awalk_cost f r) + n * awalk_cost f c"
using ‹awalk w c w›
by (auto simp: ext_p_def intro: mk_cycles_awalk_cost)
from q_def r_def have "awlast u q = w"
using pv_decomp walk_p by (auto simp: awalk_verts_take_conv elim!: awalkE)
moreover
from q_def r_def have "awalk u (q @ r) v"
using walk_p by simp
ultimately
have "awalk u q w" "awalk w r v" "⋀n. awalk w (mk_cycles_path n c) w"
using walk_c
by (auto simp: intro: mk_cycles_path_awalk)
then have "⋀n. awalk u (ext_p n) v"
unfolding ext_p_def by (blast intro: awalk_appendI)
then have "{ext_p i|i. i ∈ UNIV} ⊆ {p. awalk u p v}"
by auto
then have "(INF p∈{p. awalk u p v}. ereal (awalk_cost f p))
≤ (INF p∈ {ext_p i|i. i ∈ UNIV}. ereal (awalk_cost f p))"
by (auto intro: INF_superset_mono)
also have "… = (INF i∈ UNIV. ereal (awalk_cost f (ext_p i)))"
by (rule arg_cong[where f=Inf], auto)
also have "… = - ∞" unfolding ext_p_cost
by (rule inf_over_nats[OF ‹awalk_cost f c < 0›])
finally show ?thesis unfolding μ_def by simp
qed
lemma walk_cheaper_path_imp_neg_cyc:
assumes p_props: "awalk u p v"
assumes less_path_μ: "awalk_cost f p < (INF p∈ {p. apath u p v}. ereal (awalk_cost f p))"
shows "∃w c. awalk w c w ∧ w ∈ set (awalk_verts u p) ∧ awalk_cost f c < 0"
proof -
define path_μ where "path_μ = (INF p∈ {p. apath u p v}. ereal (awalk_cost f p))"
then have "awalk u p v" and "awalk_cost f p < path_μ"
using p_props less_path_μ by simp_all
then show ?thesis
proof (induct rule: awalk_to_apath_induct)
case (path p) then have "apath u p v" by (auto simp: apath_def)
then show ?case using path.prems by (auto simp: path_μ_def dest: not_mem_less_INF)
next
case (decomp p q r s)
then obtain w where p_props: "p = q @ r @ s" "awalk u q w" "awalk w r w" "awalk w s v"
by (auto elim: awalk_cyc_decompE)
then have "awalk u (q @ s) v"
using ‹awalk u p v› by (auto simp: awalk_appendI)
then have verts_ss: "set (awalk_verts u (q @ s)) ⊆ set (awalk_verts u p)"
using ‹awalk u p v› ‹p = q @ r @ s› by (auto simp: set_awalk_verts)
show ?case
proof (cases "ereal (awalk_cost f (q @ s)) < path_μ")
case True then have "∃w c. awalk w c w ∧ w ∈ set (awalk_verts u (q @ s)) ∧ awalk_cost f c < 0"
by (rule decomp)
then show ?thesis using verts_ss by auto
next
case False
note ‹awalk_cost f p < path_μ›
also have "path_μ ≤ awalk_cost f (q @ s)"
using False by simp
finally have "awalk_cost f r < 0" using ‹p = q @ r @ s› by simp
moreover
have "w ∈ set (awalk_verts u q)" using ‹awalk u q w› by auto
then have "w ∈ set (awalk_verts u p)"
using ‹awalk u p v› ‹awalk u q w› ‹p = q @ r @ s›
by (auto simp: set_awalk_verts)
ultimately
show ?thesis using ‹awalk w r w› by auto
qed
qed
qed
lemma (in fin_digraph) neg_inf_imp_neg_cyc:
assumes inf_mu: "μ f u v = - ∞"
shows "∃p. awalk u p v ∧ (∃w c. awalk w c w ∧ w ∈ set (awalk_verts u p) ∧ awalk_cost f c < 0)"
proof -
define path_μ where "path_μ = (INF s∈{p. apath u p v}. ereal (awalk_cost f s))"
have awalks_ne: "{p. awalk u p v} ≠ {}"
using inf_mu unfolding μ_def by safe (simp add: top_ereal_def)
then have paths_ne: "{p. apath u p v} ~= {}"
by (auto intro: apath_awalk_to_apath)
obtain p where "apath u p v" "awalk_cost f p = path_μ"
proof -
obtain p where "p ∈ {p. apath u p v}" "awalk_cost f p = path_μ"
using finite_INF_in[OF apaths_finite paths_ne, of "awalk_cost f"]
by (auto simp: path_μ_def)
then show ?thesis using that by auto
qed
then have "path_μ ≠ -∞" by auto
then have "μ f u v < path_μ" using inf_mu by simp
then obtain pw where p_def: "awalk u pw v" "awalk_cost f pw < path_μ"
by atomize_elim (auto simp: μ_def INF_less_iff)
then have "∃w c. awalk w c w ∧ w ∈ set (awalk_verts u pw) ∧ awalk_cost f c < 0"
by (intro walk_cheaper_path_imp_neg_cyc) (auto simp: path_μ_def)
with ‹awalk u pw v› show ?thesis by auto
qed
lemma (in fin_digraph) no_neg_cyc_imp_no_neg_inf:
assumes no_neg_cyc: "⋀p. awalk u p v
⟹ ¬(∃w c. awalk w c w ∧ w ∈ set (awalk_verts u p) ∧ awalk_cost f c < 0)"
shows "- ∞ < μ f u v"
proof (intro ereal_MInfty_lessI notI)
assume "μ f u v = - ∞"
then obtain p where p_props: "awalk u p v"
and ex_cyc: "∃w c. awalk w c w ∧ w ∈ set (awalk_verts u p) ∧ awalk_cost f c < 0"
by atomize_elim (rule neg_inf_imp_neg_cyc)
then show False using no_neg_cyc by blast
qed
lemma μ_reach_conv:
"μ f u v < ∞ ⟷ u →⇧* v"
proof
assume "μ f u v < ∞"
then have "{p. awalk u p v} ≠ {}"
unfolding μ_def by safe (simp add: top_ereal_def)
then show "u →⇧* v" by (simp add: reachable_awalk)
next
assume "u →⇧* v"
then obtain p where p_props: "apath u p v"
by (metis reachable_awalk apath_awalk_to_apath)
then have "{p} ⊆ {p. apath u p v}" by simp
then have "μ f u v ≤ (INF p∈ {p}. ereal (awalk_cost f p))"
unfolding μ_def by (intro INF_superset_mono) (auto simp: apath_def)
also have "… < ∞" by (simp add: min_def)
finally show "μ f u v < ∞" .
qed
lemma awalk_to_path_no_neg_cyc_cost:
assumes p_props:"awalk u p v"
assumes no_neg_cyc: "¬ (∃w c. awalk w c w ∧ w ∈ set (awalk_verts u p) ∧ awalk_cost f c < 0)"
shows "awalk_cost f (awalk_to_apath p) ≤ awalk_cost f p"
using assms
proof (induct rule: awalk_to_apath_induct)
case path then show ?case by (auto simp: awalk_to_apath.simps)
next
case (decomp p q r s)
from decomp(2,3) have "is_awalk_cyc_decomp p (q,r,s)"
using awalk_cyc_decomp_has_prop[OF decomp(1)] by auto
then have decomp_props: "p = q @ r @ s" "∃w. awalk w r w" by auto
have "awalk_cost f (awalk_to_apath p) = awalk_cost f (awalk_to_apath (q @ s))"
using decomp by (auto simp: step_awalk_to_apath[of _ p _ q r s])
also have "… ≤ awalk_cost f (q @ s)"
proof -
have "awalk u (q @ s) v"
using ‹awalk u p v› decomp_props by (auto dest!: awalk_ends_eqD)
then have "set (awalk_verts u (q @ s)) ⊆ set (awalk_verts u p)"
using ‹awalk u p v› ‹p = q @ r @ s›
by (auto simp add: set_awalk_verts)
then show ?thesis using decomp.prems by (intro decomp.hyps) auto
qed
also have "… ≤ awalk_cost f p"
proof -
obtain w where "awalk u q w" "awalk w r w" "awalk w s v"
using decomp by (auto elim: awalk_cyc_decompE)
then have "w ∈ set (awalk_verts u q)" by auto
then have "w ∈ set (awalk_verts u p)"
using ‹p = q @ r @ s› ‹awalk u p v› ‹awalk u q w›
by (auto simp add: set_awalk_verts)
then have "0 ≤ awalk_cost f r" using ‹awalk w r w›
using decomp.prems by (auto simp: not_less)
then show ?thesis using ‹p = q @ r @ s› by simp
qed
finally show ?case .
qed
lemma (in fin_digraph) no_neg_cyc_reach_imp_path:
assumes reach: "u →⇧* v"
assumes no_neg_cyc: "⋀p. awalk u p v
⟹ ¬(∃w c. awalk w c w ∧ w ∈ set (awalk_verts u p) ∧ awalk_cost f c < 0)"
shows "∃p. apath u p v ∧ μ f u v = awalk_cost f p"
proof -
define set_walks where "set_walks = {p. awalk u p v}"
define set_paths where "set_paths = {p. apath u p v}"
have "set_paths ≠ {}"
proof -
obtain p where "apath u p v"
using reach by (metis apath_awalk_to_apath reachable_awalk)
then show ?thesis unfolding set_paths_def by blast
qed
have "μ f u v = (INF p∈ set_walks. ereal (awalk_cost f p))"
unfolding μ_def set_walks_def by simp
also have "… = (INF p∈ set_paths. ereal (awalk_cost f p))"
proof (rule antisym)
have "awalk_to_apath ` set_walks ⊆ set_paths"
unfolding set_walks_def set_paths_def
by (intro subsetI) (auto elim: apath_awalk_to_apath)
then have "(INF p∈ set_paths. ereal (awalk_cost f p))
≤ (INF p∈ awalk_to_apath ` set_walks. ereal (awalk_cost f p))"
by (rule INF_superset_mono) simp
also have "… = (INF p∈ set_walks. ereal (awalk_cost f (awalk_to_apath p)))"
by (simp add: image_comp)
also have "… ≤ (INF p∈ set_walks. ereal (awalk_cost f p))"
proof -
{ fix p assume "p ∈ set_walks"
then have "awalk u p v" by (auto simp: set_walks_def)
then have "awalk_cost f (awalk_to_apath p) ≤ awalk_cost f p"
using no_neg_cyc
using no_neg_cyc and awalk_to_path_no_neg_cyc_cost
by auto }
then show ?thesis by (intro INF_mono) auto
qed
finally show
"(INF p∈ set_paths. ereal (awalk_cost f p))
≤ (INF p∈ set_walks. ereal (awalk_cost f p))" by simp
have "set_paths ⊆ set_walks"
unfolding set_paths_def set_walks_def by (auto simp: apath_def)
then show "(INF p∈ set_walks. ereal (awalk_cost f p))
≤ (INF p∈ set_paths. ereal (awalk_cost f p))"
by (rule INF_superset_mono) simp
qed
also have "… ∈ (λp. ereal (awalk_cost f p)) ` set_paths"
using apaths_finite ‹set_paths ≠ {}›
by (intro finite_INF_in) (auto simp: set_paths_def)
finally show ?thesis
by (simp add: set_paths_def image_def)
qed
lemma (in fin_digraph) min_cost_awalk:
assumes reach: "u →⇧* v"
assumes pos_cost: "⋀e. e ∈ arcs G ⟹ c e ≥ 0"
shows "∃p. apath u p v ∧ μ c u v = awalk_cost c p"
proof -
have pc: "⋀u p v. awalk u p v ⟹ 0 ≤ awalk_cost c p"
using pos_cost_pos_awalk_cost pos_cost by auto
from reach show ?thesis
by (rule no_neg_cyc_reach_imp_path) (auto simp: not_less intro: pc)
qed
lemma (in fin_digraph) pos_cost_mu_triangle:
assumes pos_cost: "⋀e. e ∈ arcs G ⟹ c e ≥ 0"
assumes e_props: "arc_to_ends G e = (u,v)" "e ∈ arcs G"
shows "μ c s v ≤ μ c s u + c e"
proof cases
assume "μ c s u = ∞" then show ?thesis by simp
next
assume "μ c s u ≠ ∞"
then have "{p. awalk s p u} ≠ {}"
unfolding μ_def by safe (simp add: top_ereal_def)
then have "s →⇧* u" by (simp add: reachable_awalk)
with pos_cost
obtain p where p_props: "apath s p u"
and p_cost: "μ c s u = awalk_cost c p"
by (metis min_cost_awalk)
have "awalk u [e] v"
using e_props by (auto simp: arc_to_ends_def awalk_simps)
with ‹apath s p u›
have "awalk s (p @ [e]) v"
by (auto simp: apath_def awalk_appendI)
then have "μ c s v ≤ awalk_cost c (p @ [e])"
by (rule min_cost_le_walk_cost)
also have "… ≤ awalk_cost c p + c e" by simp
also have "… ≤ μ c s u + c e" using p_cost by simp
finally show ?thesis .
qed
lemma (in fin_digraph) mu_exact_triangle:
assumes "v ≠ s"
assumes "s →⇧* v"
assumes nonneg_arcs: "⋀e. e ∈ arcs G ⟹ 0 ≤ c e"
obtains u e where "μ c s v = μ c s u + c e" and "arc e (u,v)"
proof -
obtain p where p_path: "apath s p v"
and p_cost: "μ c s v = awalk_cost c p"
using assms by (metis min_cost_awalk)
then obtain e p' where p'_props: "p = p' @ [e]" using ‹v ≠ s›
by (cases p rule: rev_cases) (auto simp: apath_def)
then obtain u where "awalk s p' u" "awalk u [e] v"
using ‹apath s p v› by (auto simp: apath_def)
then have mu_le: "μ c s v ≤ μ c s u + c e" and arc: "arc e (u,v)"
using nonneg_arcs by (auto intro!: pos_cost_mu_triangle simp: arc_to_ends_def arc_def)
have "μ c s u + c e ≤ ereal (awalk_cost c p') + ereal (c e)"
using ‹awalk s p' u›
by (fast intro: add_right_mono min_cost_le_walk_cost)
also have "… = awalk_cost c p" using p'_props by simp
also have "… = μ c s v" using p_cost by simp
finally
have "μ c s v = μ c s u + c e" using mu_le by auto
then show ?thesis using arc ..
qed
lemma (in fin_digraph) mu_exact_triangle_Ex:
assumes "v ≠ s"
assumes "s →⇧* v"
assumes "⋀e. e ∈ arcs G ⟹ 0 ≤ c e"
shows "∃u e. μ c s v = μ c s u + c e ∧ arc e (u,v)"
using assms by (metis mu_exact_triangle)
lemma (in fin_digraph) mu_Inf_triangle:
assumes "v ≠ s"
assumes "⋀e. e ∈ arcs G ⟹ 0 ≤ c e"
shows "μ c s v = Inf {μ c s u + c e | u e. arc e (u, v)}" (is "_ = Inf ?S")
proof cases
assume "s →⇧* v"
then obtain u e where "μ c s v = μ c s u + c e" "arc e (u,v)"
using assms by (metis mu_exact_triangle)
then have "Inf ?S ≤ μ c s v" by (auto intro: Complete_Lattices.Inf_lower)
also have "… ≤ Inf ?S" using assms(2)
by (auto intro!: Complete_Lattices.Inf_greatest pos_cost_mu_triangle
simp: arc_def arc_to_ends_def)
finally show ?thesis by simp
next
assume "¬s →⇧* v"
then have "μ c s v = ∞" by (metis shortest_path_inf)
define S where "S = ?S"
show "μ c s v = Inf S"
proof cases
assume "S = {}"
then show ?thesis unfolding ‹μ c s v = ∞›
by (simp add: top_ereal_def)
next
assume "S ≠ {}"
{ fix x assume "x ∈ S"
then obtain u e where "arc e (u,v)" and x_val: "x = μ c s u + c e"
unfolding S_def by auto
then have "¬s →⇧* u" using ‹¬ s →⇧* v› by (metis reachable_arc_trans)
then have "μ c s u + c e= ∞" by (simp add: shortest_path_inf)
then have "x = ∞" using x_val by simp }
then have "S = {∞}" using ‹S ≠ {}› by auto
then show ?thesis using ‹μ c s v = ∞› by (simp add: min_def)
qed
qed
end
end