Theory Graph

section‹Multigraphs with Partially Ordered Weights›

(*<*)
theory Graph
  imports "HOL-Library.Sublist" Antichain
begin
(*>*)

abbreviation (input) FROM where
  "FROM  λ(s, l, t). s"

abbreviation (input) LBL where
  "LBL  λ(s, l, t). l"

abbreviation (input) TO where
  "TO  λ(s, l, t). t"

notation subseq (infix "" 50)

locale graph =
  fixes weights :: "'vtx :: finite  'vtx  'lbl :: {order, monoid_add} antichain"
  assumes zero_le[simp]: "0  (s::'lbl)"
    and plus_mono: "(s1::'lbl)  s2  s3  s4  s1 + s3  s2 + s4"
    and summary_self: "weights loc loc = {}A"
begin

lemma le_plus: "(s::'lbl)  s + s'" "(s'::'lbl)  s + s'"
  by (intro plus_mono[of s s 0 s', simplified] plus_mono[of 0 s s' s', simplified])+

subsection‹Paths›

inductive path :: "'vtx  'vtx  ('vtx × 'lbl × 'vtx) list  bool" where
  path0: "l1 = l2  path l1 l2 []"
| path: "path l1 l2 xs  lbl A weights l2 l3  path l1 l3 (xs @ [(l2, lbl, l3)])"

inductive_cases path0E: "path l1 l2 []"
inductive_cases path_AppendE: "path l1 l3 (xs @ [(l2,s,l2')])"

lemma path_trans: "path l1 l2 xs  path l2 l3 ys  path l1 l3 (xs @ ys)"
  by (rotate_tac, induct l2 l3 ys rule: path.induct)
    (auto intro: path.path simp flip: append_assoc)

lemma path_take_from: "path l1 l2 xs  m < length xs  FROM (xs ! m) = l2'  path l1 l2' (take m xs)"
proof (induct l1 l2 xs rule: path.induct)
  case (path l1 l2 xs lbl l3)
  then show ?case
    apply (unfold take_append)
    apply simp
    apply (cases "l2=l2'")
     apply (metis linorder_not_less nth_append take_all)
    apply (metis case_prod_conv less_Suc_eq nth_append nth_append_length)
    done
qed simp

lemma path_take_to: "path l1 l2 xs  m < length xs  TO (xs ! m) = l2'  path l1 l2' (take (m+1) xs)"
proof (induct l1 l2 xs rule: path.induct)
  case (path l1 l2 xs lbl l3)
  then show ?case
    apply (cases "m < length xs")
     apply (simp add: nth_append)
    apply clarsimp
    apply (metis case_prod_conv less_antisym nth_append_length path.path)
    done
qed simp

lemma path_determines_loc: "path l1 l2 xs  path l1 l3 xs  l2 = l3"
  by (induct l1 l2 xs rule: path.induct) (auto elim: path.cases)

lemma path_first_loc: "path loc loc' xs  xs  []  FROM (xs ! 0) = loc"
proof (induct rule: path.induct)
  case (path l1 l2 xs lbl l3)
  then show ?case
    by (auto elim: path0E simp: nth_append)
qed simp

lemma path_to_eq_from: "path loc1 loc2 xs  i + 1 < length xs  FROM (xs ! (i+1)) = TO (xs ! i)"
proof (induct rule: path.induct)
  case (path l1 l2 xs lbl l3)
  then show ?case
    apply (cases "i + 1 < length xs")
     apply (simp add: nth_append)
    apply (simp add: nth_append)
    apply (metis add.commute drop_eq_Nil hd_drop_conv_nth id_take_nth_drop linorder_not_less path_determines_loc path_take_to plus_1_eq_Suc take_hd_drop)
    done
qed simp

lemma path_singleton[intro, simp]: "s A weights l1 l2  path l1 l2 [(l1,s,l2)]"
  by (subst path.simps) (auto simp: path.intros)

lemma path_appendE: "path l1 l3 (xs @ ys)  l2. path l2 l3 ys  path l1 l2 xs"
proof (induct l1 l3 "xs@ys" arbitrary: xs ys rule: path.induct)
  case (path0 l1 l2)
  then show ?case by (auto intro: path.intros)
next
  case (path l1 l2 xs lbl l3 xs' ys')
  from path(1,3-) show ?case
    apply -
    apply (subst (asm) append_eq_append_conv2[of xs "[(l2,lbl,l3)]" xs' ys'])
    apply (elim exE conjE disjE)
    subgoal for us
      using path(2)[of xs' us]
      by (auto intro: path.intros)
    subgoal for us
      by (cases "us=[]") (auto intro: path.intros simp: Cons_eq_append_conv)
    done
qed

lemma path_replace_prefix:
  "path l1 l3 (xs @ zs)  path l1 l2 ys  path l1 l2 xs  path l1 l3 (ys @ zs)"
  by (drule path_appendE) (auto elim!: path_trans dest: path_determines_loc)

lemma drop_subseq: "n  length xs  drop n xs  xs"
  by (auto simp: suffix_def intro!: exI[of _ "take n xs"])

lemma take_subseq[simp, intro]: "take n xs  xs"
  by (induct xs) auto

lemma map_take_subseq[simp, intro]: "map f (take n xs)  map f xs"
  by (rule subseq_map, induct xs) auto

lemma path_distinct:
  "path l1 l2 xs  xs'. distinct xs'  path l1 l2 xs'  map LBL xs'  map LBL xs"
proof (induct rule: path.induct)
  case (path0 l1 l2)
  then show ?case
    by (intro exI[of _ "[]"]) (auto intro: path.intros)
next
  case (path l1 l2 xs lbl l3)
  then obtain xs' where ih: "path l1 l2 xs'" "distinct xs'" "map LBL xs'  map LBL xs"
    by blast
  then show ?case
  proof (cases "(l2, lbl, l3)  set xs'")
    case True
    then obtain m where m: "m < length xs'" "xs' ! m = (l2, lbl, l3)"
      unfolding in_set_conv_nth by blast
    from m ih have "path l1 l2 (take m xs')"
      by (auto intro: path_take_from)
    with m ih path show ?thesis
      apply (intro exI[of _ "take m xs' @ [(l2, lbl, l3)]"])
      apply (rule conjI)
       apply (metis distinct_take take_Suc_conv_app_nth)
      apply (rule conjI)
       apply (rule path.intros)
        apply simp
       apply simp
      apply simp
      apply (metis ih(3) subseq_order.trans take_map take_subseq)
      done
  next
    case False
    with ih path(3) show ?thesis
      by (auto intro!: exI[of _ "xs' @ [(l2, lbl, l3)]"] intro: path.intros)
  qed
qed

lemma path_edge: "(l1', lbl, l2')  set xs  path l1 l2 xs  lbl A weights l1' l2'"
  by (rotate_tac, induct rule: path.induct) auto

subsection‹Path Weights›

abbreviation sum_weights :: "'lbl list  'lbl" where
  "sum_weights xs  foldr (+) xs 0"
abbreviation "sum_path_weights xs  sum_weights (map LBL xs)"

definition "path_weightp l1 l2 s  (xs. path l1 l2 xs  s = sum_path_weights xs)"

lemma sum_not_less_zero[simp, dest]: "(s::'lbl) < 0  False"
  by (simp add: less_le_not_le)

lemma sum_le_zero[simp]: "(s::'lbl)  0  s = 0"
  by (simp add: eq_iff)

lemma sum_le_zeroD[dest]: "(x::'lbl)  0  x = 0"
  by simp

lemma foldr_plus_mono: "(n::'lbl)  m  foldr (+) xs n  foldr (+) xs m"
  by (induct xs) (auto simp: plus_mono)

lemma sum_weights_append:
  "sum_weights (ys @ xs) = sum_weights ys + sum_weights xs"
  by (induct ys) (auto simp: add.assoc)

lemma sum_summary_prepend_le: "sum_path_weights ys  sum_path_weights xs  sum_path_weights (zs @ ys)  sum_path_weights (zs @ xs)"
  by (induct zs arbitrary: xs ys) (auto intro: plus_mono)

lemma sum_summary_append_le: "sum_path_weights ys  sum_path_weights xs  sum_path_weights (ys @ zs)  sum_path_weights (xs @ zs)"
proof (induct zs arbitrary: xs ys)
  case (Cons a zs)
  then show ?case
    by (metis plus_mono map_append order_refl sum_weights_append)
qed simp

lemma foldr_plus_zero_le: "foldr (+) xs (0::'lbl)  foldr (+) xs a"
  by (induct xs) (simp_all add: plus_mono)

lemma subseq_sum_weights_le:
  assumes "xs  ys"
  shows "sum_weights xs  sum_weights ys"
  using assms
proof (induct rule: list_emb.induct)
  case (list_emb_Nil ys)
  then show ?case by auto
next
  case (list_emb_Cons xs ys y)
  then show ?case by (auto elim!: order_trans simp: le_plus)
next
  case (list_emb_Cons2 x y xs ys)
  then show ?case by (auto elim!: order_trans simp: plus_mono)
qed

lemma subseq_sum_path_weights_le:
  "map LBL xs  map LBL ys  sum_path_weights xs  sum_path_weights ys"
  by (rule subseq_sum_weights_le)

lemma sum_path_weights_take_le[simp, intro]: "sum_path_weights (take i xs)  sum_path_weights xs"
  by (auto intro!: subseq_sum_path_weights_le)

lemma sum_weights_append_singleton:
  "sum_weights (xs @ [x]) = sum_weights xs + x"
  by (induct xs) (simp_all add: add.assoc)

lemma sum_path_weights_append_singleton:
  "sum_path_weights (xs @ [(l,x,l')]) = sum_path_weights xs + x"
  by (induct xs) (simp_all add: add.assoc)

lemma path_weightp_ex_path:
  "path_weightp l1 l2 s  xs.
  (let s' = sum_path_weights xs in s'  s  path_weightp l1 l2 s'  distinct xs 
  ((l1,s,l2)  set xs. s A weights l1 l2))"
  unfolding path_weightp_def
  apply (erule exE conjE)+
  apply (drule path_distinct)
  apply (erule exE conjE)+
  subgoal for xs xs'
    apply (rule exI[of _ xs'])
    apply (auto simp: Let_def dest!: path_edge intro: subseq_sum_path_weights_le)
    done
  done

lemma finite_set_summaries:
  "finite ((λ((l1,l2),s). (l1,s,l2)) ` (Sigma UNIV (λ(l1,l2). set_antichain (weights l1 l2))))"
  by force

lemma finite_summaries: "finite {xs. distinct xs  ((l1, s, l2)  set xs. s A weights l1 l2)}"
  apply (rule finite_subset[OF _ finite_distinct_bounded[of "((λ((l1,l2),s). (l1,s,l2)) ` (Sigma UNIV (λ(l1,l2). set_antichain (weights l1 l2))))"]])
   apply (force simp: finite_set_summaries)+
  done

lemma finite_minimal_antichain_path_weightp:
  "finite (minimal_antichain {x. path_weightp l1 l2 x})"
  apply (rule finite_surj[OF finite_summaries, where f = sum_path_weights])
  apply (clarsimp simp: minimal_antichain_def image_iff dest!: path_weightp_ex_path)
  apply (fastforce simp: Let_def)
  done

(* antichain of summaries along cycles-less paths (cycle-less = no edge repeated) *)
lift_definition path_weight :: "'vtx  'vtx  'lbl antichain"
  is "λl1 l2. minimal_antichain {x. path_weightp l1 l2 x}"
  using finite_minimal_antichain_path_weightp
  by auto

definition "reachable l1 l2  path_weight l1 l2  {}A"

lemma in_path_weight: "s A path_weight loc1 loc2  s  minimal_antichain {s. path_weightp loc1 loc2 s}"
  by transfer simp

lemma path_weight_refl[simp]: "0 A path_weight loc loc"
proof -
  have *: "path loc loc []"
    by (simp add: path0)
  then have "0 = sum_path_weights []" by auto
  with * have "path_weightp loc loc 0"
    using path_weightp_def by blast
  then show ?thesis
    by (auto simp: in_path_weight in_minimal_antichain)
qed

lemma zero_in_minimal_antichain[simp]: "(0::'lbl)  S  0  minimal_antichain S"
  by (auto simp: in_minimal_antichain intro: sum_not_less_zero)

definition "path_weightp_distinct l1 l2 s  (xs. distinct xs  path l1 l2 xs  s = sum_path_weights xs)"

lemma minimal_antichain_path_weightp_distinct:
  "minimal_antichain {xs. path_weightp l1 l2 xs} = minimal_antichain {xs. path_weightp_distinct l1 l2 xs}"
  unfolding path_weightp_def path_weightp_distinct_def minimal_antichain_def
  apply safe
     apply clarsimp
     apply (metis path_distinct order.strict_iff_order subseq_sum_path_weights_le)
    apply (blast+) [2]
  apply clarsimp
  apply (metis (no_types, lifting) le_less_trans path_distinct subseq_sum_weights_le)
  done

lemma finite_path_weightp_distinct[simp, intro]: "finite {xs. path_weightp_distinct l1 l2 xs}"
  unfolding path_weightp_distinct_def
  apply (rule finite_subset[where B = "sum_path_weights ` {xs. distinct xs  path l1 l2 xs}"])
   apply clarsimp
  apply (rule finite_imageI)
  apply (rule finite_subset[OF _ finite_summaries])
  apply (clarsimp simp: path_edge)
  done

lemma path_weightp_distinct_nonempty:
  "{xs. path_weightp l1 l2 xs}  {}  {xs. path_weightp_distinct l1 l2 xs}  {}"
  by (auto dest: path_distinct simp: path_weightp_def path_weightp_distinct_def)

lemma path_weightp_distinct_member:
  "s  {s. path_weightp l1 l2 s}  u. u  {s. path_weightp_distinct l1 l2 s}  u  s"
  apply (clarsimp simp: path_weightp_def path_weightp_distinct_def)
  apply (drule path_distinct)
  apply (auto dest: subseq_sum_path_weights_le)
  done

lemma minimal_antichain_path_weightp_member:
  "s  {xs. path_weightp l1 l2 xs}  u. u  minimal_antichain {xs. path_weightp l1 l2 xs}  u  s"
proof -
  assume "s  {xs. path_weightp l1 l2 xs}"
  then obtain u where u: "u  {s. path_weightp_distinct l1 l2 s}  u  s"
    using path_weightp_distinct_member by blast
  have finite: "finite {xs. path_weightp_distinct l1 l2 xs}" ..
  from u finite obtain v where "v  minimal_antichain {xs. path_weightp_distinct l1 l2 xs}  v  u"
    by atomize_elim (auto intro: minimal_antichain_member)
  with u show ?thesis
    by (auto simp: minimal_antichain_path_weightp_distinct)
qed

lemma path_path_weight: "path l1 l2 xs  s. s A path_weight l1 l2  s  sum_path_weights xs"
proof -
  assume "path l1 l2 xs"
  then have "sum_path_weights xs  {x. path_weightp l1 l2 x}"
    by (auto simp: path_weightp_def)
  then obtain u where "u  minimal_antichain {x. path_weightp l1 l2 x}  u  sum_path_weights xs"
    apply atomize_elim
    apply (drule minimal_antichain_path_weightp_member)
    apply auto
    done
  then show ?thesis
    by transfer auto
qed

lemma path_weight_conv_path:
  "s A path_weight l1 l2  xs. path l1 l2 xs  s = sum_path_weights xs  (ys. path l1 l2 ys  ¬ sum_path_weights ys < sum_path_weights xs)"
  by transfer (auto simp: in_minimal_antichain path_weightp_def)

abbreviation "optimal_path loc1 loc2 xs  path loc1 loc2 xs 
   (ys. path loc1 loc2 ys  ¬ sum_path_weights ys < sum_path_weights xs)"

lemma path_weight_path: "s A path_weight loc1 loc2 
  (xs. optimal_path loc1 loc2 xs  distinct xs  sum_path_weights xs = s  P)  P"
  apply atomize_elim
  apply transfer
  apply (clarsimp simp: in_minimal_antichain path_weightp_def)
  apply (drule path_distinct)
  apply (erule exE)
  subgoal for loc1 loc2 xs xs'
    apply (rule exI[of _ xs'])
    apply safe
    using order.strict_iff_order subseq_sum_path_weights_le apply metis
    using less_le subseq_sum_path_weights_le apply fastforce
    done
  done

lemma path_weight_elem_trans:
  "s A path_weight l1 l2  s' A path_weight l2 l3  u. u A path_weight l1 l3  u  s + s'"
proof -
  assume ps1: "s  A path_weight l1 l2"
  assume ps2: "s' A path_weight l2 l3"
  from ps1 obtain xs where path1: "path l1 l2 xs" "s = sum_path_weights xs"
    by (auto intro: path_weight_path)
  from ps2 obtain ys where path2: "path l2 l3 ys" "s' = sum_path_weights ys"
    by (auto intro: path_weight_path)
  from path1(1) path2(1) have "path l1 l3 (xs @ ys)"
    by (rule path_trans)
  with path1(2) path2(2) have "s + s'  {s. path_weightp l1 l3 s}"
    by (auto simp: path_weightp_def sum_weights_append[symmetric])
  then show "u. u A path_weight l1 l3  u  s + s'"
    by transfer (simp add: minimal_antichain_path_weightp_member)
qed

end

(*<*)
end
(*>*)