Theory BMSSP_Find_Pivots_Core

theory BMSSP_Find_Pivots_Core
  imports BMSSP_Unique_Shortest_Tree
begin

section ‹FindPivots Core Relaxation Lemmas›

text ‹
  The concrete FindPivots procedure performs bounded Bellman-Ford-style
  relaxations.  This theory packages the generic relaxation facts in the form
  needed for that proof: vertices reached by a bounded tight path from a
  complete source are complete after the corresponding path relaxations, and
  remain complete through additional valid relaxations.
›

context unique_shortest_digraph
begin

lemma finite_E [simp]: "finite E"
proof -
  have "E  V × V"
    using edge_in_V by auto
  moreover have "finite (V × V)"
    using finite_V by simp
  ultimately show ?thesis
    by (rule finite_subset)
qed

definition outgoing_edges where
  "outgoing_edges F = {(u, v)  E. u  F}"

lemma finite_outgoing_edges [simp]:
  "finite (outgoing_edges F)"
proof -
  have "outgoing_edges F  E"
    unfolding outgoing_edges_def by blast
  then show ?thesis
    by (rule finite_subset) simp
qed

definition edge_list_of where
  "edge_list_of A = (SOME xs. set xs = A  distinct xs)"

lemma edge_list_of_properties:
  assumes "finite A"
  shows "set (edge_list_of A) = A"
    and "distinct (edge_list_of A)"
proof -
  have "xs. set xs = A  distinct xs"
    using finite_distinct_list[OF assms] .
  then have props: "set (edge_list_of A) = A  distinct (edge_list_of A)"
    unfolding edge_list_of_def by (rule someI_ex)
  then show "set (edge_list_of A) = A"
    by blast
  from props show "distinct (edge_list_of A)"
    by blast
qed

definition relax_frontier where
  "relax_frontier d F = relax_edges d (edge_list_of (outgoing_edges F))"

lemma relax_frontier_le:
  "relax_frontier d F x  d x"
  unfolding relax_frontier_def by (rule relax_edges_le)

lemma relax_frontier_sound:
  assumes sound: "sound_label d"
    and frontier_reaches: "u. u  F  reachable s u"
  shows "sound_label (relax_frontier d F)"
proof -
  let ?es = "edge_list_of (outgoing_edges F)"
  have set_es: "set ?es = outgoing_edges F"
    using edge_list_of_properties(1)[OF finite_outgoing_edges] .
  have edge: "u v. (u, v)  set ?es  (u, v)  E"
    using set_es unfolding outgoing_edges_def by blast
  have reach: "u v. (u, v)  set ?es  reachable s u"
    using set_es frontier_reaches unfolding outgoing_edges_def by blast
  show ?thesis
    unfolding relax_frontier_def using relax_edges_sound[OF sound edge reach] .
qed

fun relax_rounds where
  "relax_rounds d [] = d"
| "relax_rounds d (r # rs) = relax_rounds (relax_edges d r) rs"

definition within_k_edges where
  "within_k_edges p k  p  []  length (path_edges p)  k"

lemma relax_rounds_concat:
  "relax_rounds d rounds = relax_edges d (concat rounds)"
  by (induction rounds arbitrary: d) (simp_all add: relax_edges_append)

lemma relax_rounds_sound:
  assumes sound: "sound_label d"
    and edges: "u v. (u, v)  set (concat rounds)  (u, v)  E"
    and reaches: "u v. (u, v)  set (concat rounds)  reachable s u"
  shows "sound_label (relax_rounds d rounds)"
proof -
  have "sound_label (relax_edges d (concat rounds))"
    by (rule relax_edges_sound[OF sound edges reaches])
  then show ?thesis
    by (simp add: relax_rounds_concat)
qed

lemma relax_rounds_preserves_complete_sound:
  assumes sound: "sound_label d"
    and complete_x: "d x = dist s x"
    and edges: "u v. (u, v)  set (concat rounds)  (u, v)  E"
    and reaches: "u v. (u, v)  set (concat rounds)  reachable s u"
  shows "relax_rounds d rounds x = dist s x"
proof -
  have "relax_edges d (concat rounds) x = dist s x"
    by (rule relax_edges_preserves_complete_sound[OF sound complete_x edges reaches])
  then show ?thesis
    by (simp add: relax_rounds_concat)
qed

lemma path_edges_length:
  "length (path_edges p) = length p - 1"
  by (induction p rule: path_edges.induct) auto

lemma within_k_edgesD:
  assumes "within_k_edges p k"
  shows "p  []" "length (path_edges p)  k"
  using assms unfolding within_k_edges_def by auto

lemma find_pivots_tight_path_prefix_completion:
  assumes bounded: "within_k_edges p k"
    and sound: "sound_label d"
    and complete_source: "d (hd p) = dist s (hd p)"
    and tight: "successively tight_edge_step p"
    and es: "es = path_edges p @ extra"
    and extra_edges: "u v. (u, v)  set extra  (u, v)  E"
    and extra_reaches: "u v. (u, v)  set extra  reachable s u"
  shows "sound_label (relax_edges d es)"
    and "relax_edges d es (last p) = dist s (last p)"
proof -
  have nonempty: "p  []"
    using within_k_edgesD(1)[OF bounded] .
  show "sound_label (relax_edges d es)"
    using relax_edges_with_tight_path_prefix_complete(1)
      [OF nonempty sound complete_source tight es extra_edges extra_reaches] .
  show "relax_edges d es (last p) = dist s (last p)"
    using relax_edges_with_tight_path_prefix_complete(2)
      [OF nonempty sound complete_source tight es extra_edges extra_reaches] .
qed

lemma find_pivots_tight_path_block_completion:
  assumes bounded: "within_k_edges p k"
    and sound: "sound_label d"
    and complete_source: "d (hd p) = dist s (hd p)"
    and tight: "successively tight_edge_step p"
    and es: "es = pre @ path_edges p @ extra"
    and pre_edges: "u v. (u, v)  set pre  (u, v)  E"
    and pre_reaches: "u v. (u, v)  set pre  reachable s u"
    and extra_edges: "u v. (u, v)  set extra  (u, v)  E"
    and extra_reaches: "u v. (u, v)  set extra  reachable s u"
  shows "sound_label (relax_edges d es)"
    and "relax_edges d es (last p) = dist s (last p)"
proof -
  let ?d0 = "relax_edges d pre"
  have nonempty: "p  []"
    using within_k_edgesD(1)[OF bounded] .
  have sound0: "sound_label ?d0"
    using relax_edges_sound[OF sound pre_edges pre_reaches] .
  have complete0: "?d0 (hd p) = dist s (hd p)"
    using relax_edges_preserves_complete_sound[OF sound complete_source pre_edges pre_reaches] .
  have tail_sound: "sound_label (relax_edges ?d0 (path_edges p @ extra))"
    using relax_edges_with_tight_path_prefix_complete(1)
      [OF nonempty sound0 complete0 tight refl extra_edges extra_reaches] .
  have tail_complete: "relax_edges ?d0 (path_edges p @ extra) (last p) = dist s (last p)"
    using relax_edges_with_tight_path_prefix_complete(2)
      [OF nonempty sound0 complete0 tight refl extra_edges extra_reaches] .
  have unfold_es: "relax_edges d es = relax_edges ?d0 (path_edges p @ extra)"
    using es relax_edges_append[of d pre "path_edges p @ extra"] by simp
  show "sound_label (relax_edges d es)"
    using tail_sound unfold_es by simp
  show "relax_edges d es (last p) = dist s (last p)"
    using tail_complete unfold_es by simp
qed

lemma find_pivots_rounds_block_completion:
  assumes bounded: "within_k_edges p k"
    and sound: "sound_label d"
    and complete_source: "d (hd p) = dist s (hd p)"
    and tight: "successively tight_edge_step p"
    and rounds: "concat rounds = pre @ path_edges p @ extra"
    and pre_edges: "u v. (u, v)  set pre  (u, v)  E"
    and pre_reaches: "u v. (u, v)  set pre  reachable s u"
    and extra_edges: "u v. (u, v)  set extra  (u, v)  E"
    and extra_reaches: "u v. (u, v)  set extra  reachable s u"
  shows "sound_label (relax_rounds d rounds)"
    and "relax_rounds d rounds (last p) = dist s (last p)"
proof -
  have edge_sound: "sound_label (relax_edges d (concat rounds))"
    using find_pivots_tight_path_block_completion(1)
      [OF bounded sound complete_source tight rounds pre_edges pre_reaches extra_edges extra_reaches] .
  have endpoint: "relax_edges d (concat rounds) (last p) = dist s (last p)"
    using find_pivots_tight_path_block_completion(2)
      [OF bounded sound complete_source tight rounds pre_edges pre_reaches extra_edges extra_reaches] .
  show "sound_label (relax_rounds d rounds)"
    using edge_sound by (simp add: relax_rounds_concat)
  show "relax_rounds d rounds (last p) = dist s (last p)"
    using endpoint by (simp add: relax_rounds_concat)
qed

lemma relax_frontier_tight_successor_complete:
  assumes sound: "sound_label d"
    and uF: "u  F"
    and frontier_reaches: "x. x  F  reachable s x"
    and complete_u: "d u = dist s u"
    and tight: "tight_edge_step u v"
  shows "sound_label (relax_frontier d F)"
    and "relax_frontier d F v = dist s v"
proof -
  let ?es = "edge_list_of (outgoing_edges F)"
  have set_es: "set ?es = outgoing_edges F"
    using edge_list_of_properties(1)[OF finite_outgoing_edges] .
  have edge_uv: "(u, v)  E"
    using tight unfolding tight_edge_step_def by blast
  have mem_uv: "(u, v)  set ?es"
    using set_es edge_uv uF unfolding outgoing_edges_def by blast
  obtain pre extra where es_split: "?es = pre @ (u, v) # extra"
    using split_list[OF mem_uv] by blast
  have bounded: "within_k_edges [u, v] (Suc 0)"
    unfolding within_k_edges_def by simp
  have tight_path: "successively tight_edge_step [u, v]"
    using tight by simp
  have es_path: "?es = pre @ path_edges [u, v] @ extra"
    using es_split by simp
  have pre_edges: "a b. (a, b)  set pre  (a, b)  E"
    using set_es es_split unfolding outgoing_edges_def by auto
  have pre_reaches: "a b. (a, b)  set pre  reachable s a"
    using set_es es_split frontier_reaches unfolding outgoing_edges_def by auto
  have extra_edges: "a b. (a, b)  set extra  (a, b)  E"
    using set_es es_split unfolding outgoing_edges_def by auto
  have extra_reaches: "a b. (a, b)  set extra  reachable s a"
    using set_es es_split frontier_reaches unfolding outgoing_edges_def by auto
  have core1_path: "sound_label (relax_edges d (pre @ path_edges [u, v] @ extra))"
  proof (rule find_pivots_tight_path_block_completion(1)
      [where p = "[u, v]" and k = "Suc 0" and pre = pre and extra = extra])
    show "within_k_edges [u, v] (Suc 0)"
      using bounded .
    show "sound_label d"
      using sound .
    show "d (hd [u, v]) = dist s (hd [u, v])"
      using complete_u by simp
    show "successively tight_edge_step [u, v]"
      using tight_path .
    show "pre @ path_edges [u, v] @ extra = pre @ path_edges [u, v] @ extra"
      by simp
    show "a b. (a, b)  set pre  (a, b)  E"
      using pre_edges .
    show "a b. (a, b)  set pre  reachable s a"
      using pre_reaches .
    show "a b. (a, b)  set extra  (a, b)  E"
      using extra_edges .
    show "a b. (a, b)  set extra  reachable s a"
      using extra_reaches .
  qed
  have core2_path: "relax_edges d (pre @ path_edges [u, v] @ extra) v = dist s v"
  proof -
    have "relax_edges d (pre @ path_edges [u, v] @ extra) (last [u, v]) =
        dist s (last [u, v])"
    proof (rule find_pivots_tight_path_block_completion(2)
        [where p = "[u, v]" and k = "Suc 0" and pre = pre and extra = extra])
      show "within_k_edges [u, v] (Suc 0)"
        using bounded .
      show "sound_label d"
        using sound .
      show "d (hd [u, v]) = dist s (hd [u, v])"
        using complete_u by simp
      show "successively tight_edge_step [u, v]"
        using tight_path .
      show "pre @ path_edges [u, v] @ extra = pre @ path_edges [u, v] @ extra"
        by simp
      show "a b. (a, b)  set pre  (a, b)  E"
        using pre_edges .
      show "a b. (a, b)  set pre  reachable s a"
        using pre_reaches .
      show "a b. (a, b)  set extra  (a, b)  E"
        using extra_edges .
      show "a b. (a, b)  set extra  reachable s a"
        using extra_reaches .
    qed
    then show ?thesis
      by simp
  qed
  have core1: "sound_label (relax_edges d ?es)"
    using core1_path es_path by simp
  have core2: "relax_edges d ?es v = dist s v"
    using core2_path es_path by simp
  show "sound_label (relax_frontier d F)"
    using core1 unfolding relax_frontier_def .
  show "relax_frontier d F v = dist s v"
    using core2 unfolding relax_frontier_def .
qed

end

end