Theory BMSSP_Path_Family
theory BMSSP_Path_Family
imports BMSSP_Top_Level_Bounds BMSSP_Executable_Base_Case
begin
section ‹A Size-Indexed Family of Runtime Instances›
text ‹
The runtime headline
@{thm bmssp_runtime_headline_instance.bmssp_runtime_bigo_target} is a theorem
of the @{locale bounded_reduced_positive_instance} locale. The theory
🗏‹BMSSP_Runtime_Instance.thy› interprets that locale on the single fixed
graph ‹0 → 1 → 2 → 3›, which certifies non-vacuity at one size only, and
whose edge count is therefore a constant.
This theory removes that limitation. For every ‹n› it builds the unit-weight
directed path ‹0 → 1 → … → n›, proves that it satisfies all assumptions of
@{locale bounded_reduced_positive_instance} \emph{uniformly in ‹n›}, and
obtains the deterministic running-time bound for a family whose vertex count
‹n + 1› and edge count ‹n› grow without bound. The only non-routine
assumption, uniqueness of shortest walks, is discharged by the structural fact
that in such a path the only walk starting at ‹0› is an initial segment
‹[0, 1, …, k]›. Unlike the fixed instance, no step uses code evaluation; the
argument is a single induction valid for all ‹n›.
›
subsection ‹The Path Graph of Size ‹n››
definition pf_V :: "nat ⇒ nat set" where
"pf_V n = {0..n}"
definition pf_E :: "nat ⇒ (nat × nat) set" where
"pf_E n = {(i, Suc i) | i. i < n}"
definition pf_w :: "nat ⇒ nat ⇒ real" where
"pf_w u v = 1"
lemma pf_E_iff: "(a, b) ∈ pf_E n ⟷ a < n ∧ b = Suc a"
unfolding pf_E_def by auto
lemma pf_finite_weighted_digraph:
"finite_weighted_digraph (pf_V n) (pf_E n) pf_w 0"
proof
show "finite (pf_V n)"
unfolding pf_V_def by simp
show "0 ∈ pf_V n"
unfolding pf_V_def by simp
show "⋀u v. (u, v) ∈ pf_E n ⟹ u ∈ pf_V n ∧ v ∈ pf_V n"
unfolding pf_V_def by (auto simp: pf_E_iff)
show "⋀u v. (u, v) ∈ pf_E n ⟹ 0 ≤ pf_w u v"
unfolding pf_w_def by simp
qed
subsection ‹The Path Family Locale›
text ‹
Fixing the size ‹n› in a locale lets us interpret the graph locales for
‹pf_V n›, ‹pf_E n›, while keeping ‹n› schematic across the whole development.
›
locale path_family =
fixes n :: nat
begin
interpretation pf: finite_weighted_digraph "pf_V n" "pf_E n" pf_w 0
by (rule pf_finite_weighted_digraph)
subsubsection ‹The Only Walk From the Source Is an Initial Segment›
text ‹
Vertex ‹i› has the single out-edge ‹(i, Suc i)›, so any walk starting at ‹0›
is forced to visit ‹0, 1, 2, …› in order: its ‹i›-th element is ‹i›.
›
lemma pf_walk_nth:
assumes walk: "pf.walk p"
and hd0: "hd p = 0"
and i: "i < length p"
shows "p ! i = i"
using i
proof (induction i)
case 0
have "p ≠ []" using assms(3) by auto
then show ?case using hd0 by (simp add: hd_conv_nth)
next
case (Suc i)
have i_lt: "i < length p" using Suc.prems by simp
have ih: "p ! i = i" using Suc.IH[OF i_lt] .
have edge: "(p ! i, p ! Suc i) ∈ pf_E n"
by (rule pf.walk_nth_edge[OF walk Suc.prems])
have "p ! Suc i = Suc (p ! i)"
using edge by (simp add: pf_E_iff)
then show ?case using ih by simp
qed
lemma pf_walk_from_0_eq_upt:
assumes walk: "pf.walk p"
and hd0: "hd p = 0"
shows "p = [0..<length p]"
proof (rule nth_equalityI)
show "length p = length [0..<length p]" by simp
fix i assume "i < length p"
then show "p ! i = [0..<length p] ! i"
using pf_walk_nth[OF walk hd0] by simp
qed
lemma pf_simple_walk_betw_eq:
assumes "pf.simple_walk_betw 0 p v"
shows "p = [0..<Suc v]"
proof -
have walk: "pf.walk p" and ne: "p ≠ []"
and hd0: "hd p = 0" and last_v: "last p = v"
using assms unfolding pf.simple_walk_betw_def pf.walk_betw_def by auto
have p_eq: "p = [0..<length p]"
by (rule pf_walk_from_0_eq_upt[OF walk hd0])
have len_pos: "0 < length p" using ne by auto
have "p ! (length p - 1) = length p - 1"
using pf_walk_nth[OF walk hd0] len_pos by simp
moreover have "last p = p ! (length p - 1)"
using ne by (simp add: last_conv_nth)
ultimately have "v = length p - 1" using last_v by simp
then have "length p = Suc v" using len_pos by simp
then show ?thesis using p_eq by simp
qed
subsubsection ‹Reachability and Uniqueness›
lemma pf_upt_simple_walk:
assumes "v ≤ n"
shows "pf.simple_walk_betw 0 [0..<Suc v] v"
proof -
have walk: "pf.walk [0..<Suc v]"
using assms
proof (induction v)
case 0
have "[0..<Suc 0] = [0]" by simp
moreover have "pf.walk [0]"
using pf.walk.simps(2)[of 0] unfolding pf_V_def by simp
ultimately show ?case by simp
next
case (Suc v)
have v_le: "v ≤ n" using Suc.prems by simp
have wv: "pf.walk [0..<Suc v]" by (rule Suc.IH[OF v_le])
have upt_eq: "[0..<Suc (Suc v)] = [0..<Suc v] @ [Suc v]"
by (simp add: upt_Suc_append)
have edge: "(v, Suc v) ∈ pf_E n"
using Suc.prems by (simp add: pf_E_iff)
have last_wv: "last [0..<Suc v] = v" by simp
have ne: "[0..<Suc v] ≠ []" by simp
have hd_wv: "hd [0..<Suc v] = 0"
using upt_conv_Cons[of 0 "Suc v"] by simp
have sb: "pf.simple_walk_betw 0 [0..<Suc v] v"
unfolding pf.simple_walk_betw_def pf.walk_betw_def
using wv last_wv ne hd_wv by simp
have fresh: "Suc v ∉ set [0..<Suc v]" by simp
have sw: "pf.simple_walk_betw 0 ([0..<Suc v] @ [Suc v]) (Suc v)"
by (rule pf.simple_walk_snoc[OF sb edge fresh])
then have "pf.walk ([0..<Suc v] @ [Suc v])"
unfolding pf.simple_walk_betw_def pf.walk_betw_def by simp
then show ?case
unfolding upt_eq .
qed
have hd0: "hd [0..<Suc v] = 0"
using upt_conv_Cons[of 0 "Suc v"] by simp
have last_v: "last [0..<Suc v] = v" by simp
show ?thesis
unfolding pf.simple_walk_betw_def pf.walk_betw_def
using walk hd0 last_v by simp
qed
lemma pf_all_reachable:
assumes "v ∈ pf_V n"
shows "pf.reachable 0 v"
proof -
have "v ≤ n" using assms unfolding pf_V_def by simp
then have "pf.simple_walk_betw 0 [0..<Suc v] v"
by (rule pf_upt_simple_walk)
then show ?thesis unfolding pf.reachable_def by blast
qed
lemma pf_unique_shortest_walk:
assumes "pf.shortest_walk 0 p v" and "pf.shortest_walk 0 q v"
shows "p = q"
proof -
have sp: "pf.simple_walk_betw 0 p v" and sq: "pf.simple_walk_betw 0 q v"
using assms unfolding pf.shortest_walk_def by blast+
have "p = [0..<Suc v]" by (rule pf_simple_walk_betw_eq[OF sp])
moreover have "q = [0..<Suc v]" by (rule pf_simple_walk_betw_eq[OF sq])
ultimately show ?thesis by simp
qed
text ‹
With uniqueness in hand we may interpret the @{locale unique_shortest_digraph}
layer, which is where @{const unique_shortest_digraph.edge_outdegree_le} and
@{const unique_shortest_digraph.outgoing_edges} live.
›
interpretation pf_usd: unique_shortest_digraph "pf_V n" "pf_E n" pf_w 0
by unfold_locales (rule pf_unique_shortest_walk)
subsubsection ‹Bounded Out-Degree and Positive Weights›
lemma pf_positive_weight: "(u, v) ∈ pf_E n ⟹ 0 < pf_w u v"
unfolding pf_w_def by simp
lemma pf_edge_outdegree: "pf_usd.edge_outdegree_le 1"
unfolding pf_usd.edge_outdegree_le_def
proof
fix u :: nat
assume "u ∈ pf_V n"
have sub: "pf_usd.outgoing_edges {u} ⊆ {(u, Suc u)}"
unfolding pf_usd.outgoing_edges_def by (auto simp: pf_E_iff)
have "card (pf_usd.outgoing_edges {u}) ≤ card {(u, Suc u)}"
by (rule card_mono[OF _ sub]) simp
then show "card (pf_usd.outgoing_edges {u}) ≤ 1" by simp
qed
subsubsection ‹The Reduced Positive Instance and Its Running-Time Bound›
sublocale pf_bri: bounded_reduced_positive_instance "pf_V n" "pf_E n" pf_w 0 1
proof unfold_locales
show "⋀u v. (u, v) ∈ pf_E n ⟹ 0 < pf_w u v"
by (rule pf_positive_weight)
show "⋀v. v ∈ pf_V n ⟹ pf.reachable 0 v"
by (rule pf_all_reachable)
show "pf_usd.edge_outdegree_le 1"
by (rule pf_edge_outdegree)
qed
text ‹
The vertex count is ‹n + 1› and the edge count is exactly ‹n›: both grow with
the size parameter, unlike the constant edge count of the fixed instance.
›
lemma pf_vertex_count: "pf_usd.vertex_count = Suc n"
proof -
have "pf_usd.vertex_count = card (pf_V n)"
by (rule pf_usd.vertex_count_def)
also have "… = card {0..n}"
unfolding pf_V_def by (rule refl)
also have "… = Suc n"
by simp
finally show ?thesis .
qed
lemma pf_edge_count: "pf_usd.edge_count = n"
proof -
have inj: "inj_on (λi. (i, Suc i)) {i. i < n}"
by (rule inj_onI) auto
have "pf_E n = (λi. (i, Suc i)) ` {i. i < n}"
unfolding pf_E_def by auto
then have "pf_usd.edge_count = card ((λi. (i, Suc i)) ` {i. i < n})"
unfolding pf_usd.edge_count_def by simp
also have "… = card {i. i < n}"
using inj by (simp add: card_image)
also have "… = n" by simp
finally show ?thesis .
qed
text ‹
The closed running-time bound for the path of size ‹n›, with all locale
hypotheses discharged. This is the deterministic
‹O(m * (ln N) powr (2/3))› envelope of the headline, now holding for a member
of the family of \emph{every} size. The time and size functions are named
locally so the re-exported statement below is free of deep qualified names.
›
definition pf_T :: "nat ⇒ nat" where
"pf_T = pf_bri.runtime_headline.T_bmssp"
definition pf_m :: "nat ⇒ nat" where
"pf_m = pf_bri.runtime_headline.m"
lemma pf_runtime_bigo_target:
"(λN. real (pf_T N)) ∈
O(λN. real (pf_m N) * (ln (real N + 2)) powr (2 / 3))"
unfolding pf_T_def pf_m_def
by (rule pf_bri.runtime_headline.bmssp_runtime_bigo_target)
text ‹
Locale-level names for the graph-size measures, so the external re-exports
avoid deep interpretation-qualified constants.
›
definition pf_verts :: nat where
"pf_verts = pf_usd.vertex_count"
definition pf_edges :: nat where
"pf_edges = pf_usd.edge_count"
lemma pf_verts_eq: "pf_verts = Suc n"
unfolding pf_verts_def by (rule pf_vertex_count)
lemma pf_edges_eq: "pf_edges = n"
unfolding pf_edges_def by (rule pf_edge_count)
end
subsection ‹The Bound Holds at Every Size›
text ‹
Re-exported outside the locale: for every size parameter ‹n›, the path graph
on ‹n + 1› vertices and ‹n› edges satisfies the deterministic BMSSP
running-time bound, and its edge count really is ‹n›. Because ‹n› is
universally quantified, this is a single statement witnessing non-vacuity of
the runtime locale across all sizes, not merely at one fixed graph.
›
theorem path_family_runtime_bigo_target:
fixes n :: nat
shows "(λN. real (path_family.pf_T n N)) ∈
O(λN. real (path_family.pf_m n N) * (ln (real N + 2)) powr (2 / 3))"
proof -
interpret path_family n .
show ?thesis
by (rule pf_runtime_bigo_target)
qed
theorem path_family_edge_count:
fixes n :: nat
shows "path_family.pf_edges n = n"
proof -
interpret path_family n .
show ?thesis
by (rule pf_edges_eq)
qed
theorem path_family_vertex_count:
fixes n :: nat
shows "path_family.pf_verts n = Suc n"
proof -
interpret path_family n .
show ?thesis
by (rule pf_verts_eq)
qed
end