Theory BMSSP_Base_Case
theory BMSSP_Base_Case
imports BMSSP_Algorithm_Correctness BMSSP_Shortest_Path_Lemmas
begin
section ‹Concrete Base Case›
text ‹
The algorithm's level-0 BMSSP call is a bounded Dijkstra-style search from a
singleton source. It explores the bounded tree below the input bound and
stops once the next extracted distance would exceed the first ‹k› vertices.
Operationally, the paper describes this with a priority queue. For
correctness, only one property of that queue matters here: vertices are
extracted in nondecreasing true distance order.
This theory turns that observation into a compact semantic base case. Since
the graph is finite, the bounded tree can be listed in sorted distance order.
If the list has at most ‹k› vertices, the base case completes the whole
bounded tree and returns the original bound. If the list is longer, the
‹k›-th distance becomes the returned finite bound and the completed set is
exactly the part of the bounded tree strictly below that bound. The final
lemma packages this as a BMSSP postcondition without assuming any recursive
postcondition for the base case itself.
›
context finite_weighted_digraph
begin
definition min_dist_vertex :: "'v set ⇒ 'v" where
"min_dist_vertex A = (SOME x. x ∈ A ∧ dist s x = Min (dist s ` A))"
lemma finite_bound_tree [simp]: "finite (bound_tree S B)"
unfolding bound_tree_def using finite_V by auto
lemma min_dist_vertex_prop:
assumes "finite A" "A ≠ {}"
shows "min_dist_vertex A ∈ A ∧ dist s (min_dist_vertex A) = Min (dist s ` A)"
proof -
obtain a where "a ∈ A"
using assms by auto
then have "Min (dist s ` A) ∈ dist s ` A"
using assms by (intro Min_in) auto
then have "∃x. x ∈ A ∧ dist s x = Min (dist s ` A)"
by auto
then have "(SOME x. x ∈ A ∧ dist s x = Min (dist s ` A)) ∈ A ∧
dist s (SOME x. x ∈ A ∧ dist s x = Min (dist s ` A)) = Min (dist s ` A)"
by (rule someI_ex)
then show ?thesis
unfolding min_dist_vertex_def .
qed
lemma min_dist_vertex_in:
assumes "finite A" "A ≠ {}"
shows "min_dist_vertex A ∈ A"
using min_dist_vertex_prop[OF assms] by blast
lemma min_dist_vertex_min:
assumes "finite A" "A ≠ {}" "y ∈ A"
shows "dist s (min_dist_vertex A) ≤ dist s y"
proof -
have "dist s (min_dist_vertex A) = Min (dist s ` A)"
using min_dist_vertex_prop[OF assms(1,2)] by blast
also have "… ≤ dist s y"
using assms by auto
finally show ?thesis .
qed
lemma :
assumes "finite A"
shows "∃xs. set xs = A ∧ distinct xs ∧ sorted_wrt (λu v. dist s u ≤ dist s v) xs"
proof -
obtain xs where xs: "set xs = A" "distinct xs"
using finite_distinct_list[OF assms] by blast
let ?ys = "sort_key (dist s) xs"
have "set ?ys = A"
using xs(1) by simp
moreover have "distinct ?ys"
using xs(2) by simp
moreover have "sorted_wrt (λu v. dist s u ≤ dist s v) ?ys"
proof -
have "sorted (map (dist s) ?ys)"
by (rule sorted_sort_key)
then show ?thesis
by (simp add: sorted_map)
qed
ultimately show ?thesis
by blast
qed
definition closest_vertices :: "'v set ⇒ 'v list" where
"closest_vertices A =
(SOME xs. set xs = A ∧ distinct xs ∧ sorted_wrt (λu v. dist s u ≤ dist s v) xs)"
lemma closest_vertices_properties:
assumes "finite A"
shows "set (closest_vertices A) = A"
and "distinct (closest_vertices A)"
and "sorted_wrt (λu v. dist s u ≤ dist s v) (closest_vertices A)"
proof -
have "∃xs. set xs = A ∧ distinct xs ∧
sorted_wrt (λu v. dist s u ≤ dist s v) xs"
using ordered_extraction_exists[OF assms] .
then have cv_props: "set (closest_vertices A) = A ∧
distinct (closest_vertices A) ∧
sorted_wrt (λu v. dist s u ≤ dist s v) (closest_vertices A)"
unfolding closest_vertices_def by (rule someI_ex)
then show "set (closest_vertices A) = A"
by blast
from cv_props show "distinct (closest_vertices A)"
by blast
from cv_props show "sorted_wrt (λu v. dist s u ≤ dist s v) (closest_vertices A)"
by blast
qed
definition base_case_order :: "'v ⇒ bound ⇒ 'v list" where
"base_case_order x B = closest_vertices (bound_tree {x} B)"
definition base_case_vertices :: "nat ⇒ 'v ⇒ bound ⇒ 'v set" where
"base_case_vertices k x B =
(let xs = base_case_order x B in
if length xs ≤ k then set xs
else {v ∈ set (take (Suc k) xs). dist s v < dist s (xs ! k)})"
definition base_case_bound :: "nat ⇒ 'v ⇒ bound ⇒ bound" where
"base_case_bound k x B =
(let xs = base_case_order x B in
if length xs ≤ k then B else Fin (dist s (xs ! k)))"
definition base_case_result :: "nat ⇒ 'v ⇒ bound ⇒ bound × 'v set" where
"base_case_result k x B =
(base_case_bound k x B, base_case_vertices k x B)"
text ‹
The helper @{const closest_vertices} is a choice of a distinct list sorted by
true distance. It is intentionally non-executable: the base-case proof is
semantic, and later executable theories provide their own finite procedure.
The public base-case result is described by @{const base_case_result}, whose
components are @{const base_case_bound} and @{const base_case_vertices}. The
strict inequality in @{const base_case_vertices} mirrors the main BMSSP
convention for finite bounds.
›
lemma base_case_order_set:
"set (base_case_order x B) = bound_tree {x} B"
unfolding base_case_order_def
using closest_vertices_properties(1)[OF finite_bound_tree] .
lemma base_case_order_sorted:
"sorted_wrt (λu v. dist s u ≤ dist s v) (base_case_order x B)"
unfolding base_case_order_def
using closest_vertices_properties(3)[OF finite_bound_tree] .
lemma base_case_order_distinct:
"distinct (base_case_order x B)"
unfolding base_case_order_def
using closest_vertices_properties(2)[OF finite_bound_tree] .
lemma in_set_take_dist_lt_nth:
assumes sorted: "sorted_wrt (λu v. dist s u ≤ dist s v) xs"
and y: "y ∈ set xs"
and lt: "dist s y < dist s (xs ! k)"
and k: "k < length xs"
shows "y ∈ set (take k xs)"
proof -
obtain i where i: "i < length xs" "xs ! i = y"
using y by (auto simp: in_set_conv_nth)
have "i < k"
proof (rule ccontr)
assume "¬ i < k"
then have k_le_i: "k ≤ i"
by simp
show False
proof (cases "k = i")
case True
then show ?thesis
using lt i(2) by simp
next
case False
with k_le_i have "k < i"
by simp
then have "dist s (xs ! k) ≤ dist s (xs ! i)"
using sorted_wrt_nth_less[OF sorted, of k i] i(1) by simp
then show ?thesis
using lt i(2) by simp
qed
qed
then have "i < length (take k xs)" "take k xs ! i = y"
using i by auto
then show ?thesis
using nth_mem by metis
qed
lemma base_case_success:
assumes "length (base_case_order x B) ≤ k"
shows "base_case_vertices k x B = bound_tree {x} B"
using assms base_case_order_set[of x B]
unfolding base_case_vertices_def by (simp add: Let_def)
lemma below_bound_less_trans:
assumes "a < b" "below_bound b B"
shows "below_bound a B"
using assms by (cases B) auto
lemma Fin_below_bound_le:
assumes "below_bound a B"
shows "bound_le (Fin a) B"
using assms by (cases B) auto
lemma base_case_partial:
assumes len: "k < length (base_case_order x B)"
shows "base_case_vertices k x B = bound_tree {x} (Fin (dist s ((base_case_order x B) ! k)))"
proof -
let ?xs = "base_case_order x B"
let ?b = "dist s (?xs ! k)"
have set_xs: "set ?xs = bound_tree {x} B"
using base_case_order_set[of x B] .
have sorted: "sorted_wrt (λu v. dist s u ≤ dist s v) ?xs"
using base_case_order_sorted[of x B] .
have kth_in: "?xs ! k ∈ bound_tree {x} B"
using len set_xs nth_mem by metis
show ?thesis
proof
show "base_case_vertices k x B ⊆ bound_tree {x} (Fin ?b)"
proof
fix v
assume "v ∈ base_case_vertices k x B"
moreover have "base_case_vertices k x B =
{v ∈ set (take (Suc k) ?xs). dist s v < ?b}"
using len unfolding base_case_vertices_def by (simp add: Let_def)
ultimately have v: "v ∈ set (take (Suc k) ?xs)" "dist s v < ?b"
by auto
have "v ∈ set ?xs"
using v(1) by (meson in_set_takeD)
then have "v ∈ bound_tree {x} B"
using set_xs by simp
then show "v ∈ bound_tree {x} (Fin ?b)"
using v(2) unfolding bound_tree_def by auto
qed
next
show "bound_tree {x} (Fin ?b) ⊆ base_case_vertices k x B"
proof
fix v
assume v: "v ∈ bound_tree {x} (Fin ?b)"
then have lt: "dist s v < ?b"
unfolding bound_tree_def by auto
have kth_below: "below_bound (dist s (?xs ! k)) B"
using kth_in unfolding bound_tree_def by auto
have "below_bound (dist s v) B"
using below_bound_less_trans[OF lt kth_below] .
then have "v ∈ bound_tree {x} B"
using v unfolding bound_tree_def by auto
then have "v ∈ set ?xs"
using set_xs by simp
then have "v ∈ set (take k ?xs)"
using in_set_take_dist_lt_nth[OF sorted _ lt len] by blast
then have "v ∈ set (take (Suc k) ?xs)"
using set_take_subset_set_take[of k "Suc k" ?xs] by auto
then show "v ∈ base_case_vertices k x B"
using len lt unfolding base_case_vertices_def by (simp add: Let_def)
qed
qed
qed
lemma base_case_result_correct:
assumes "S = {x}"
shows "case base_case_result k x B of (B', U) ⇒
U = bound_tree S B' ∧ complete_on (λv. if v ∈ U then dist s v else d v) U"
proof (cases "length (base_case_order x B) ≤ k")
case True
then have U: "base_case_vertices k x B = bound_tree {x} B"
using base_case_success by blast
have B': "base_case_bound k x B = B"
using True unfolding base_case_bound_def by (simp add: Let_def)
have "complete_on (λv. if v ∈ base_case_vertices k x B then dist s v else d v)
(base_case_vertices k x B)"
unfolding complete_on_def by simp
then show ?thesis
using assms U B' unfolding base_case_result_def by simp
next
case False
then have len: "k < length (base_case_order x B)"
by simp
let ?B = "Fin (dist s ((base_case_order x B) ! k))"
have U: "base_case_vertices k x B = bound_tree {x} ?B"
using base_case_partial[OF len] .
have B': "base_case_bound k x B = ?B"
using False unfolding base_case_bound_def by (simp add: Let_def)
have "complete_on (λv. if v ∈ base_case_vertices k x B then dist s v else d v)
(base_case_vertices k x B)"
unfolding complete_on_def by simp
then show ?thesis
using assms U B' unfolding base_case_result_def by simp
qed
lemma base_case_bound_le:
"bound_le (base_case_bound k x B) B"
proof (cases "length (base_case_order x B) ≤ k")
case True
then show ?thesis
unfolding base_case_bound_def by (cases B) (simp_all add: Let_def)
next
case False
then have len: "k < length (base_case_order x B)"
by simp
let ?xs = "base_case_order x B"
have "?xs ! k ∈ bound_tree {x} B"
using len base_case_order_set[of x B] nth_mem by metis
then have "below_bound (dist s (?xs ! k)) B"
unfolding bound_tree_def by auto
then show ?thesis
using False Fin_below_bound_le unfolding base_case_bound_def by (simp add: Let_def)
qed
lemma base_case_result_bound_le:
"case base_case_result k x B of (B', U) ⇒ bound_le B' B"
unfolding base_case_result_def using base_case_bound_le by simp
lemma base_case_result_bmssp_post:
assumes "S = {x}"
shows "case base_case_result k x B of (B', U) ⇒
bmssp_post d S B (λv. if v ∈ U then dist s v else d v) B' U"
proof -
have corr: "case base_case_result k x B of (B', U) ⇒
U = bound_tree S B' ∧ complete_on (λv. if v ∈ U then dist s v else d v) U"
using base_case_result_correct[OF assms, where k=k and B=B and d=d] .
have le: "case base_case_result k x B of (B', U) ⇒ bound_le B' B"
using base_case_result_bound_le[where k=k and x=x and B=B] .
show ?thesis
using corr le unfolding bmssp_post_def by (cases "base_case_result k x B") auto
qed
text ‹
The proof splits on whether the sorted bounded tree has already fit inside
the base-case budget. In the success case, @{thm base_case_success} says the
returned set is the entire input tree. In the partial case,
@{thm base_case_partial} identifies the returned set with the same tree under
the newly returned finite bound. Lemma @{thm base_case_result_bmssp_post}
is the result consumed by the recursive relation: updating labels to
@{const dist} on the returned set establishes the BMSSP postcondition.
›
lemma base_case_label_sound:
assumes sound: "sound_label d"
shows "sound_label (λv. if v ∈ base_case_vertices k x B then dist s v else d v)"
using sound unfolding sound_label_def by auto
lemma finite_base_case_vertices [simp]:
"finite (base_case_vertices k x B)"
proof (cases "length (base_case_order x B) ≤ k")
case True
then show ?thesis
using base_case_success[of x B k] by simp
next
case False
then show ?thesis
unfolding base_case_vertices_def by (simp add: Let_def)
qed
lemma card_base_case_vertices_le:
"card (base_case_vertices k x B) ≤ k"
proof (cases "length (base_case_order x B) ≤ k")
case True
have "card (base_case_vertices k x B) = length (base_case_order x B)"
using True base_case_order_distinct[of x B]
unfolding base_case_vertices_def by (simp add: Let_def distinct_card)
then show ?thesis
using True by simp
next
case False
then have len: "k < length (base_case_order x B)"
by simp
let ?xs = "base_case_order x B"
let ?U = "base_case_vertices k x B"
have U_subset: "?U ⊆ set (take k ?xs)"
proof
fix v
assume "v ∈ ?U"
moreover have "?U = {v ∈ set (take (Suc k) ?xs). dist s v < dist s (?xs ! k)}"
using len unfolding base_case_vertices_def by (simp add: Let_def)
ultimately have v: "v ∈ set (take (Suc k) ?xs)" "dist s v < dist s (?xs ! k)"
by auto
then have v_set: "v ∈ set ?xs"
by (meson in_set_takeD)
then show "v ∈ set (take k ?xs)"
using in_set_take_dist_lt_nth[OF base_case_order_sorted[of x B] v_set v(2) len] by blast
qed
have card_U_le: "card ?U ≤ card (set (take k ?xs))"
using U_subset by (simp add: card_mono)
have card_take_le: "card (set (take k ?xs)) ≤ length (take k ?xs)"
by (rule card_length)
have "length (take k ?xs) = k"
using len by simp
then show ?thesis
using card_U_le card_take_le by linarith
qed
text ‹
The remaining facts are bookkeeping for the costed and recursive layers. The
base-case label update preserves @{const sound_label}, the returned set is
finite, and @{thm card_base_case_vertices_le} records the intended cap. The
strict cutoff in the partial case is what makes this cardinality bound hold:
ties at the ‹k›-th distance are not included.
›
end
end