Theory Path_Algorithms

(* Title:      Correctness of Path Algorithms
   Author:     Walter Guttmann, Peter Hoefner
   Maintainer: Walter Guttmann <walter.guttmann at canterbury.ac.nz>
               Peter Hoefner <peter at hoefner-online.de>
*)

section ‹Correctness of Path Algorithms›

text ‹
To show that our theory of paths integrates with verification tasks, we verify the correctness of three basic path algorithms.
Algorithms at the presented level are executable and can serve prototyping purposes.
Data refinement can be carried out to move from such algorithms to more efficient programs.
The total-correctness proofs use a library developed in cite"Guttmann2018c".
›

theory Path_Algorithms

imports "HOL-Hoare.Hoare_Logic" Rooted_Paths

begin

unbundle no trancl_syntax

class choose_singleton_point_signature =
  fixes choose_singleton :: "'a  'a"
  fixes choose_point :: "'a  'a"

class relation_algebra_rtc_tarski_choose_point =
  relation_algebra_rtc_tarski + choose_singleton_point_signature +
  assumes choose_singleton_singleton: "x  0  singleton (choose_singleton x)"
  assumes choose_singleton_decreasing: "choose_singleton x  x"
  assumes choose_point_point: "is_vector x  x  0  point (choose_point x)"
  assumes choose_point_decreasing: "choose_point x  x"
begin

no_notation
  composition (infixl ; 75) and
  times (infixl * 70)

notation
  composition (infixl * 75)

subsection ‹Construction of a path›

text ‹
Our first example is a basic greedy algorithm that constructs a path from a vertex $x$ to a different vertex $y$ of a directed acyclic graph $D$.
›

abbreviation "construct_path_inv q x y D W 
    is_acyclic D  point x  point y  point q 
    D * q  DT * x  W  D  terminating_path W 
    (W = 0  q=y)  (W  0  q = start_points W  y = end_points W)"

abbreviation "construct_path_inv_simp q x y D W 
    is_acyclic D  point x  point y  point q 
    D * q  DT * x  W  D  terminating_path W 
    q = start_points W  y = end_points W"

lemma construct_path_pre:
  assumes "is_acyclic D"
      and "point y"
      and "point x"
      and "D * y  DT * x"
    shows "construct_path_inv y x y D 0"
  apply (intro conjI, simp_all add: assms is_inj_def is_p_fun_def path_def)
  using assms(2) cycle_iff by fastforce

text ‹
The following three lemmas are auxiliary lemmas for construct_path_inv›.
They are pulled out of the main proof to have more structure.
›

lemma path_inv_points:
  assumes "construct_path_inv q x y D W  q  x"
  shows "point q"
    and "point (choose_point (D*q))"
 using assms apply blast
by (metis assms choose_point_point comp_assoc is_vector_def point_def reachable_implies_predecessor)

lemma path_inv_choose_point_decrease:
  assumes "construct_path_inv q x y D W  q  x"
    shows "W0  choose_point (D*q)  -((W + choose_point (D*q) * qT)T*1)"
proof -
  let ?q = "choose_point (D*q)"
  let ?W = "W + ?q * qT"
  assume as: "W0"
  hence "q*W  W+" (* "connected_root q W" *)
    by (metis assms conv_contrav conv_invol conv_iso conv_terminating_path
              forward_terminating_path_end_points_1 plus_conv point_def ss423bij
              terminating_path_iff)
  hence "?q  WT*1  D*q  WT+*q"
    using choose_point_decreasing meet_iso meet_isor inf_mono assms connected_root_iff2 by simp
  also have "...  (D  DT+)*q"
    by (metis assms inj_distr point_def conv_contrav conv_invol conv_iso meet_isor
              mult_isol_var mult_isor star_conv star_slide_var star_subdist sup.commute sup.orderE)
  also have "...  0"
    by (metis acyclic_trans assms conv_zero step_has_target order.eq_iff galois_aux ss_p18)
  finally have a: "?q  -(WT*1)"
    using galois_aux le_bot by blast

  have "point ?q"
    using assms by(rule path_inv_points(2))
  hence "?q  -(q*?qT*1)"
    by (metis assms acyclic_imp_one_step_different_points(2) point_is_point
            choose_point_decreasing edge_end end_point_char end_point_no_successor)
  with a show ?thesis
    by (simp add: inf.boundedI)
qed

lemma end_points:
  assumes "construct_path_inv q x y D W  q  x"
    shows "choose_point (D*q) = start_points (W + choose_point (D*q) * qT)"
      and "y = end_points (W + choose_point (D*q) * qT)"
proof -
  let ?q = "choose_point (D*q)"
  let ?W = "W + ?q * qT"
  show 1: "?q = start_points ?W"
  proof (rule order.antisym)
    show" start_points ?W  ?q"
      by (metis assms(1) path_inv_points(2) acyclic_imp_one_step_different_points(2)
                choose_point_decreasing edge_end edge_start sup.commute
                path_concatenation_start_points_approx point_is_point order.eq_iff sup_bot_left)
    show "?q  start_points ?W"
    proof -
      have a: "?q = ?q*qT*1"
        by (metis assms(1) comp_assoc point_equations(1) point_is_point aux4 conv_zero
                  choose_point_decreasing choose_point_point conv_contrav conv_one point_def
                  inf.orderE inf_compl_bot inf_compl_bot_right is_vector_def maddux_142
                  sup_bot_left sur_def_var1)
      hence "?q =(q  -q) + (?q  -q  -(?WT*1))"
        by (metis assms path_inv_points(2) path_inv_choose_point_decrease
                  acyclic_imp_one_step_different_points(1) choose_point_decreasing inf.orderE
                  inf_compl_bot sup_inf_absorb edge_start point_is_point sup_bot_left)
      also have "...  (W*1  -(?WT*1)  -q) + (?q  -q  -(?WT*1))"
        by simp
      also have "... = (W*1 + ?q)  -(q + ?WT*1)"
        by (metis compl_sup inf_sup_distrib2 meet_assoc sup.commute)
      also have "...  ?W*1  -(?WT*1)"
        using a by (metis inf.left_commute distrib_right' compl_sup inf.cobounded2)
      finally show "?q  start_points ?W" .
    qed
  qed
  show "y = end_points ?W"
  proof -
    have point_nq: "point ?q"
      using assms by(rule path_inv_points(2))
    hence yp: "y  -?q"
      using 1 assms
      by (metis acyclic_imp_one_step_different_points(2) choose_point_decreasing cycle_no_points(1)
                finite_iff finite_iff_msc forward_finite_iff_msc path_aux1a path_edge_equals_cycle
                point_is_point point_not_equal(1) terminating_iff1)
    have "y = y + (W*1  -(WT*1)  -(W*1))"
      by (simp add: inf.commute)
    also have "... = y + (q  -(W*1))"
      using assms by fastforce
    also have "... = y + (q  -(W*1)  -?q)"
      by (metis calculation sup_assoc sup_inf_absorb)
    also have "... = (y  -?q) + (q  -(W*1)  -?q)"
      using yp by (simp add: inf.absorb1)
    also have "... = (WT*1  -(W*1)  -?q) + (q  -(W*1)  -?q)"
      using assms by fastforce
    also have "... = (WT*1 + q)  -(W*1)  -?q"
      by (simp add: inf_sup_distrib2)
    also have "... = (WT*1 + q)  -(W*1 + ?q)"
      by (simp add: inf.assoc)
    also have "... = (WT*1 + q*?qT*1)  -(W*1 + ?q*qT*1)"
      using point_nq
      by(metis assms(1) comp_assoc conv_contrav conv_one is_vector_def point_def sur_def_var1)
    also have "... = (?WT)*1  -(?W*1)"
      by simp
    finally show ?thesis .
  qed
qed

lemma construct_path_inv:
  assumes "construct_path_inv q x y D W  q  x"
  shows "construct_path_inv (choose_point (D*q)) x y D (W + choose_point (D*q)*qT)"
proof (intro conjI)
  let ?q = "choose_point (D*q)"
  let ?W = "W + ?q * qT"
  show "is_acyclic D"
    using assms by blast
  show point_y: "point y"
    using assms by blast
  show "point x"
    using assms by blast
  show "?W  D"
    using assms choose_point_decreasing le_sup_iff point_def ss423bij inf.boundedE by blast
  show "D*?q  DT*x"
  proof -
    have "D+*q  DT*x"
      using assms conv_galois_2 order_trans star_1l by blast
    thus ?thesis
      by (metis choose_point_decreasing comp_assoc dual_order.trans mult_isol star_slide_var)
  qed
  show point_nq: "point ?q"
    using assms by(rule path_inv_points(2))
  show pathW: "path ?W"
    proof(cases "W=0")
      assume "W=0"
      thus ?thesis
        using assms edge_is_path point_is_point point_nq by simp
    next
      assume a: "W0"
      have b: "?q*qT  1*?q*qT*-(?q*qT*1)"
      proof -
        have "?q*qT 1" by simp
        thus ?thesis
          using assms point_nq
          by(metis different_points_consequences(1) point_def sur_def_var1
               acyclic_imp_one_step_different_points(2) choose_point_decreasing comp_assoc
               is_vector_def point_def point_equations(3,4) point_is_point)
      qed
      have c: "W  -(1*W)*W*1"
        using assms terminating_path_iff by blast
      have d: "(?q*qT)T*1  -((?q*qT)*1) = W*1  -(WT*1)"
        using a
        by (metis assms path_inv_points(2) acyclic_reachable_points choose_point_decreasing
                  edge_end point_is_point comp_assoc point_def sur_total total_one)
      have e: "?q*qT*1  WT*1 = 0"
      proof -
        have "?q*qT*1  WT*1 = ?q  WT*1"
          using assms point_nq
          by (metis comp_assoc conv_contrav conv_one is_vector_def point_def sur_def_var1)
        also have "...  -(?WT*1)  ?WT*1"
          using assms path_inv_choose_point_decrease
          by (smt a conv_contrav conv_iso conv_one inf_mono less_eq_def subdistl_eq)
        also have "...  0"
          using compl_inf_bot eq_refl by blast
        finally show ?thesis
          using bot_unique by blast
      qed
      show ?thesis
      using b c d e by (metis assms comp_assoc edge_is_path path_concatenation_cycle_free
                                point_is_point sup.commute point_nq)
  qed
  show "?W = 0  ?q = y"
    apply (rule iffI)
    apply (metis assms conv_zero dist_alt edge_start inf_compl_bot_right modular_1_aux' modular_2_aux'
              point_is_point sup.left_idem sup_bot_left point_nq)
    by (smt assms end_points(1) conv_contrav conv_invol cycle_no_points(1) end_point_iff2 has_start_end_points_iff path_aux1b path_edge_equals_cycle point_is_point start_point_iff2 sup_bot_left top_greatest pathW)
  show "?W0  ?q = start_points ?W  y = end_points ?W"
    apply (rule iffI)
    using assms end_points apply blast
    using assms by force
  show "terminating ?W"
    by (smt assms end_points end_point_iff2 has_start_end_points_iff point_is_point start_point_iff2
            terminating_iff1 pathW point_nq)
qed

theorem construct_path_partial: "VARS p q W
  { is_acyclic D  point y  point x  D*y  DT*x }
  W := 0;
  q := y;
  WHILE q  x
    INV { construct_path_inv q x y D W }
     DO p := choose_point (D*q);
        W := W + p*qT;
        q := p
     OD
  { W  D  terminating_path W  (W=0  x=y)  (W0  x = start_points W  y = end_points W) }"
  apply vcg
  using construct_path_pre apply blast
  using construct_path_inv apply blast
  by fastforce

end (* relation_algebra_rtc_tarski_choose_point *)

text ‹For termination, we additionally need finiteness.›

context finite
begin

lemma decrease_set:
  assumes "x::'a . Q x  P x"
      and "P w"
      and "¬ Q w"
    shows "card { x . Q x } < card { x . P x }"
by (metis Collect_mono assms card_seteq finite mem_Collect_eq not_le)

end

class relation_algebra_rtc_tarski_choose_point_finite =
      relation_algebra_rtc_tarski_choose_point + relation_algebra_rtc_tarski_point_finite
begin

lemma decrease_variant:
  assumes "y  z"
      and "w  z"
      and "¬ w  y"
    shows "card { x . x  y } < card { x . x  z }"
by (metis Collect_mono assms card_seteq linorder_not_le dual_order.trans finite_code mem_Collect_eq)

lemma construct_path_inv_termination:
  assumes "construct_path_inv q x y D W  q  x"
    shows "card { z . z  -(W + choose_point (D*q)*qT) } < card { z . z  -W }"
proof -
  let ?q = "choose_point (D*q)"
  let ?W = "W + ?q * qT"
  show ?thesis
  proof (rule decrease_variant)
    show "-?W  -W"
      by simp
    show "?q * qT  -W"
      by (metis assms galois_aux inf_compl_bot_right maddux_142 mult_isor order_trans top_greatest)
    show "¬ (?q * qT  -?W)"
      using assms end_points(1)
      by (smt acyclic_imp_one_step_different_points(2) choose_point_decreasing compl_sup inf.absorb1
              inf_compl_bot_right sup.commute sup_bot.left_neutral conv_zero end_points(2))
  qed
qed

theorem construct_path_total: "VARS p q W
  [ is_acyclic D  point y  point x  D*y  DT*x ]
  W := 0;
  q := y;
  WHILE q  x
    INV { construct_path_inv q x y D W }
    VAR { card { z . z  -W } }
     DO p := choose_point (D*q);
        W := W + p*qT;
        q := p
     OD
  [ W  D  terminating_path W  (W=0  x=y)  (W0  x = start_points W  y = end_points W) ]"
  apply vcg_tc
  using construct_path_pre apply blast
  apply (rule CollectI, rule conjI)
  using construct_path_inv apply blast
  using construct_path_inv_termination apply clarsimp
  by fastforce


end (* relation_algebra_rtc_tarski_choose_point_finite *)

subsection ‹Topological sorting›

text ‹
In our second example we look at topological sorting.
Given a directed acyclic graph, the problem is to construct a linear order of its vertices that contains $x$ before $y$ for each edge $(x,y)$ of the graph.
If the input graph models dependencies between tasks, the output is a linear schedule of the tasks that respects all dependencies.
›

context relation_algebra_rtc_tarski_choose_point
begin

abbreviation topological_sort_inv
  where "topological_sort_inv q v R W 
         regressively_finite R  R  v*vT  W+  terminating_path W  W*1 = v-q 
         (W = 0  q = end_points W)  point q  R*v  v  q  v  is_vector v"

lemma topological_sort_pre:
  assumes "regressively_finite R"
  shows "topological_sort_inv (choose_point (minimum R 1)) (choose_point (minimum R 1)) R 0"
proof (intro conjI,simp_all add:assms)
  let ?q = "choose_point (- (RT * 1))"
  show point_q: "point ?q"
    using assms by (metis (full_types) annir choose_point_point galois_aux2 is_inj_def is_sur_def
                          is_vector_def one_idem_mult point_def ss_p18 inf_top_left one_compl)
  show "R  ?q * ?qT  0"
    by (metis choose_point_decreasing conv_invol end_point_char order.eq_iff inf_bot_left schroeder_2)
  show "path 0"
    by (simp add: is_inj_def is_p_fun_def path_def)
  show "R*?q  ?q"
    by (metis choose_point_decreasing compl_bot_eq conv_galois_1 inf_compl_bot_left2 le_inf_iff)
  show "is_vector ?q"
    using point_q point_def by blast
qed

lemma topological_sort_inv:
  assumes "v  1"
      and "topological_sort_inv q v R W"
    shows "topological_sort_inv (choose_point (minimum R (- v))) (v +
                      choose_point (minimum R (- v))) R (W + q * choose_point (minimum R (- v))T)"
proof (intro conjI)
  let ?p = "choose_point (minimum R (-v))"
  let ?W = "W + q*?pT"
  let ?v = "v + ?p"
  show point_p: "point ?p"
    using assms
    by (metis choose_point_point compl_bot_eq double_compl galois_aux2 comp_assoc is_vector_def
              vector_compl vector_mult)
  hence ep_np: "end_points (q*?pT) = ?p"
    using assms(2)
    by (metis aux4 choose_point_decreasing edge_end le_supI1 point_in_vector_or_complement_iff
              point_is_point)
  hence sp_q: "start_points (q*?pT) = q"
    using assms(2) point_p
    by (metis (no_types, lifting) conv_contrav conv_invol edge_start point_is_point)
  hence ep_sp: "W  0  end_points W = start_points (q*?pT)"
    using assms(2) by force
  have "W*1  (q*?pT)T*1 = v-q?p"
    using assms(2) point_p is_vector_def mult_assoc point_def point_equations(3) point_is_point
    by auto
  hence 1: "W*1  (q*?pT)T*1 = 0"
    by (metis choose_point_decreasing dual_order.trans galois_aux inf.cobounded2 inf.commute)

  show "regressively_finite R"
    using assms(2) by blast
  show "R  ?v*?vT  ?W+"
  proof -
    have a: "R  v*vT  ?W+"
      using assms(2) by (meson mult_isol_var order.trans order_prop star_subdist)
    have b: "R  v*?pT  ?W+"
    proof -
      have "R  v*?pT  W*1*?pT + q*?pT"
        by (metis inf_le2 assms(2) aux4 double_compl inf_absorb2 distrib_right)
      also have "... = W*?pT + q*?pT"
        using point_p by (metis conv_contrav conv_one is_vector_def mult_assoc point_def)
      also have "...  W+*end_points W*?pT + q*?pT"
        using assms(2)
        by (meson forward_terminating_path_end_points_1 join_iso mult_isor terminating_path_iff)
      also have "...  W+*q*?pT + q*?pT"
        using assms(2) by (metis annil eq_refl)
      also have "... = W*q*?pT"
        using conway.dagger_unfoldl_distr mult_assoc sup_commute by fastforce
      also have "...  ?W+"
        by (metis mult_assoc mult_isol_var star_slide_var star_subdist sup_ge2)
      finally show ?thesis .
    qed
    have c: "R  ?p*vT  ?W+"
    proof -
      have "v  -?p"
        using choose_point_decreasing compl_le_swap1 inf_le1 order_trans by blast
      hence "R*v  -?p"
        using assms(2) order.trans by blast
      thus ?thesis
        by (metis galois_aux inf_le2 schroeder_2)
    qed
    have d: "R  ?p*?pT  ?W+"
    proof -
      have "R  ?p*?pT  R  1'"
        using point_p is_inj_def meet_isor point_def by blast
      also have "... = 0"
        using assms(2) regressively_finite_irreflexive galois_aux by blast
      finally show ?thesis
        using bot_least inf.absorb_iff2 by simp
    qed
    have "R  ?v*?vT = (R  v*vT) + (R  v*?pT) + (R  ?p*vT) + (R  ?p*?pT)"
      by (metis conv_add distrib_left distrib_right inf_sup_distrib1 sup.commute sup.left_commute)
    also have "...  ?W+"
      using a b c d by (simp add: le_sup_iff)
    finally show ?thesis .
  qed
  show pathW: "path ?W"
  proof (cases "W = 0")
    assume "W = 0"
    thus ?thesis
      using assms(2) point_p edge_is_path point_is_point sup_bot_left by auto
  next
    assume a1: "W  0"
    have fw_path: "forward_terminating_path W"
      using assms(2) terminating_iff by blast
    have bw_path: "backward_terminating_path (q*?pT)"
      using assms point_p sp_q
      by (metis conv_backward_terminating conv_has_start_points conv_path edge_is_path
                forward_terminating_iff1 point_is_point start_point_iff2)
    show ?thesis
      using fw_path bw_path ep_sp 1 a1 path_concatenation_cycle_free by blast
  qed
  show "terminating ?W"
  proof (rule start_end_implies_terminating)
    show "has_start_points ?W"
      apply (cases "W = 0")
      using assms(2) sp_q pathW
      apply (metis (no_types, lifting) point_is_point start_point_iff2 sup_bot.left_neutral)
      using assms(2) ep_sp 1 pathW
      by (metis has_start_end_points_iff path_concatenation_start_points start_point_iff2
                terminating_iff1)
    show "has_end_points ?W"
      apply (cases "W = 0")
      using point_p ep_np ep_sp pathW end_point_iff2 point_is_point apply force
      using point_p ep_np ep_sp 1 pathW
      by (metis end_point_iff2 path_concatenation_end_points point_is_point)
  qed
  show "?W*1 = ?v-?p"
  proof -
    have "?W*1 = v"
      by (metis assms(2) point_p is_vector_def mult_assoc point_def point_equations(3)
                point_is_point aux4 distrib_right' inf_absorb2 sup.commute)
    also have "... = v-?p"
      by (metis choose_point_decreasing compl_le_swap1 inf.cobounded1 inf.orderE order_trans)
    finally show ?thesis
      by (simp add: inf_sup_distrib2)
  qed
  show "?W = 0  ?p = end_points ?W"
    using ep_np ep_sp 1 by (metis path_concatenation_end_points sup_bot_left)
  show "R*?v  ?v"
    using assms(2)
    by (meson choose_point_decreasing conv_galois_1 inf.cobounded2 order.trans sup.coboundedI1
              sup_least)
  show "?p  ?v"
    by simp
  show "is_vector ?v"
    using assms(2) point_p point_def vector_add by blast
qed

lemma topological_sort_post:
 assumes "¬ v  1"
     and "topological_sort_inv q v R W"
   shows "R  W+  terminating_path W  (W + WT)*1 = -1'*1"
proof (intro conjI,simp_all add:assms)
  show "R  W+"
    using assms by force
  show " backward_terminating W  W  1 * W * (- v + q)"
    using assms by force
  show "v  - q + WT * 1 = - 1' * 1"
    proof (cases "W = 0")
      assume "W = 0"
      thus ?thesis
        using assms
        by (metis compl_bot_eq conv_one conv_zero double_compl inf_top.left_neutral is_inj_def
                  le_bot mult_1_right one_idem_mult point_def ss_p18 star_zero sup.absorb2 top_le)
    next
      assume a1: "W  0"
      hence "-1'  0"
        using assms backward_terminating_path_irreflexive le_bot by fastforce
      hence "1 = 1*-1'*1"
        by (simp add: tarski)
      also have "... = -1'*1"
        by (metis comp_assoc distrib_left mult_1_left sup_top_left distrib_right sup_compl_top)
      finally have a: "1 = -1'*1" .
      have "W*1 + WT*1 = 1"
        using assms a1 by (metis double_compl galois_aux4 inf.absorb_iff2 inf_top.left_neutral)
      thus ?thesis
        using a by (simp add: assms(2))
    qed
qed

theorem topological_sort_partial: "VARS p q v W
  { regressively_finite R }
  W := 0;
  q := choose_point (minimum R 1);
  v := q;
  WHILE v  1
    INV { topological_sort_inv q v R W }
     DO p := choose_point (minimum R (-v));
        W := W + q*pT;
        q := p;
        v := v + p
     OD
  { R  W+  terminating_path W  (W + WT)*1 = -1'*1 }"
  apply vcg
  using topological_sort_pre apply blast
  using topological_sort_inv apply blast
  using topological_sort_post by blast

end (* relation_algebra_rtc_tarski_choose_point *)

context relation_algebra_rtc_tarski_choose_point_finite
begin

lemma topological_sort_inv_termination:
  assumes "v  1"
      and "topological_sort_inv q v R W"
    shows "card {z . z  -(v + choose_point (minimum R (-v)))} < card { z . z  -v }"
proof (rule decrease_variant)
  let ?p = "choose_point (minimum R (-v))"
  let ?v = "v + ?p"
  show "-?v  -v"
    by simp
  show "?p  -v"
    using choose_point_decreasing inf.boundedE by blast
  have "point ?p"
    using assms
    by (metis choose_point_point compl_bot_eq double_compl galois_aux2 comp_assoc is_vector_def
              vector_compl vector_mult)
  thus "¬ (?p  -?v)"
    by (metis annir compl_sup inf.absorb1 inf_compl_bot_right maddux_20 no_end_point_char)
qed

text ‹
Use precondition is_acyclic› instead of regressively_finite›.
They are equivalent for finite graphs.
›

theorem topological_sort_total: "VARS p q v W
  [ is_acyclic R ]
  W := 0;
  q := choose_point (minimum R 1);
  v := q;
  WHILE v  1
    INV { topological_sort_inv q v R W }
    VAR { card { z . z  -v } }
      DO p := choose_point (minimum R (-v));
         W := W + q*pT;
         q := p;
         v := v + p
      OD
   [ R  W+  terminating_path W  (W + WT)*1 = -1'*1 ]"
  apply vcg_tc
  apply (drule acyclic_regressively_finite)
  using topological_sort_pre apply blast
  apply (rule CollectI, rule conjI)
  using topological_sort_inv apply blast
  using topological_sort_inv_termination apply auto[1]
  using topological_sort_post by blast

end (* relation_algebra_rtc_tarski_choose_point_finite *)

subsection ‹Construction of a tree›

text ‹
Our last application is a correctness proof of an algorithm that constructs a non-empty cycle for a given directed graph.
This works in two steps.
The first step is to construct a directed tree from a given root along the edges of the graph.
›

context relation_algebra_rtc_tarski_choose_point
begin

abbreviation construct_tree_pre
  where "construct_tree_pre x y R  y  RT*x  point x"
abbreviation construct_tree_inv
  where "construct_tree_inv v x y D R  construct_tree_pre x y R  is_acyclic D  is_inj D 
                                         D  R  D*x = 0  v = x + DT*1  x*vT  D  D  v*vT 
                                         is_vector v"
abbreviation construct_tree_post
  where "construct_tree_post x y D R  is_acyclic D  is_inj D  D  R  D*x = 0  DT*1  DT*x 
                                        D*y  DT*x"

lemma construct_tree_pre:
  assumes "construct_tree_pre x y R"
    shows "construct_tree_inv x x y 0 R"
using assms by (simp add: is_inj_def point_def)

lemma construct_tree_inv_aux:
  assumes "¬ y  v"
      and "construct_tree_inv v x y D R"
    shows "singleton (choose_singleton (v*-vT  R))"
proof (rule choose_singleton_singleton, rule notI)
  assume "v*-vT  R = 0"
  hence "RT*v  v"
    by (metis galois_aux conv_compl conv_galois_1 conv_galois_2 conv_invol double_compl
              star_inductl_var)
  hence "y = 0"
    using assms by (meson mult_isol order_trans sup.cobounded1)
  thus False
   using assms point_is_point by auto
qed

lemma construct_tree_inv:
  assumes "¬ y  v"
      and "construct_tree_inv v x y D R"
    shows "construct_tree_inv (v + choose_singleton (v*-vT  R)T*1) x y (D +
                               choose_singleton (v*-vT  R)) R"
proof (intro conjI)
  let ?e = "choose_singleton (v*-vT  R)"
  let ?D = "D + ?e"
  let ?v = "v + ?eT*1"
  have 1: "?e  v*-vT"
    using choose_singleton_decreasing inf.boundedE by blast
  show "point x"
    by (simp add: assms)
  show "y  RT*x"
    by (simp add: assms)
  show "is_acyclic ?D"
    using 1 assms acyclic_inv by fastforce
  show "is_inj ?D"
    using 1 construct_tree_inv_aux assms injective_inv by blast
  show "?D  R"
    apply (rule sup.boundedI)
    using assms apply blast
    using choose_singleton_decreasing inf.boundedE by blast
  show "?D*x = 0"
  proof -
    have "?D*x = ?e*x"
      by (simp add: assms)
    also have "...  ?e*v"
      by (simp add: assms mult_isol)
    also have "...  v*-vT*v"
      using 1 mult_isor by blast
    also have "... = 0"
      by (metis assms(2) annir comp_assoc vector_prop1)
    finally show ?thesis
      using le_bot by blast
  qed
  show "?v = x + ?DT*1"
    by (simp add: assms sup_assoc)
  show "x*?vT  ?D"
  proof -
    have "x*?vT = x*vT + x*1*?e"
      by (simp add: distrib_left mult_assoc)
    also have "...  D + x*1*(?e  v*-vT)"
      using 1 by (metis assms(2) inf.absorb1 join_iso)
    also have "... = D + x*1*(?e  v  -vT)"
      by (metis assms(2) comp_assoc conv_compl inf.assoc vector_compl vector_meet_comp)
    also have "...  D + x*1*(?e  v)"
      using join_isol mult_subdistl by fastforce
    also have "... = D + x*(1  vT)*?e"
      by (metis assms(2) inf.commute mult_assoc vector_2)
    also have "... = D + x*vT*?e"
      by simp
    also have "...  D + D*?e"
      using assms join_isol mult_isor by blast
    also have "...  ?D"
      by (meson le_sup_iff prod_star_closure star_ext star_subdist)
    finally show ?thesis .
  qed
  show "?D  ?v*?vT"
  proof (rule sup.boundedI)
    show "D  ?v*?vT"
      using assms
      by (meson conv_add distrib_left le_supI1 conv_iso dual_order.trans mult_isol_var order_prop)
    have "?e  v*(-vT  vT*?e)"
      using 1 inf.absorb_iff2 modular_1' by fastforce
    also have "...  v*1*?e"
      by (simp add: comp_assoc le_infI2 mult_isol_var)
    also have "...  ?v*?vT"
      by (metis conv_contrav conv_invol conv_iso conv_one mult_assoc mult_isol_var sup.cobounded1
                sup_ge2)
    finally show "?e  ?v*?vT"
      by simp
  qed
  show "is_vector ?v"
    using assms comp_assoc is_vector_def by fastforce
qed

lemma construct_tree_post:
  assumes "y  v"
      and "construct_tree_inv v x y D R"
    shows "construct_tree_post x y D R"
proof -
  have "v*xT  DT"
    by (metis (no_types, lifting) assms(2) conv_contrav conv_invol conv_iso star_conv)
  hence 1: "v  DT*x"
    using assms point_def ss423bij by blast
  hence 2: "DT*1  DT*x"
    using assms le_supE by blast
  have "D*y  DT*x"
  proof (rule star_inductl, rule sup.boundedI)
    show "y  DT*x"
      using 1 assms order.trans by blast
  next
    have "D*(DT*x) = D*x + D*DT+*x"
      by (metis conway.dagger_unfoldl_distr distrib_left mult_assoc)
    also have "... = D*DT+*x"
      using assms by simp
    also have "...  1'*DT*x"
      by (metis assms(2) is_inj_def mult_assoc mult_isor)
    finally show "D*(DT*x)  DT*x"
      by simp
  qed
  thus "construct_tree_post x y D R"
    using 2 assms by simp
qed

theorem construct_tree_partial: "VARS e v D
  { construct_tree_pre x y R }
  D := 0;
  v := x;
  WHILE ¬ y  v
    INV { construct_tree_inv v x y D R }
     DO e := choose_singleton (v*-vT  R);
        D := D + e;
        v := v + eT*1
     OD
  { construct_tree_post x y D R }"
 apply vcg
 using construct_tree_pre apply blast
 using construct_tree_inv apply blast
 using construct_tree_post by blast

end (* relation_algebra_rtc_tarski_choose_point *)

context relation_algebra_rtc_tarski_choose_point_finite
begin

lemma construct_tree_inv_termination:
 assumes " ¬ y  v"
     and "construct_tree_inv v x y D R"
   shows "card { z . z  -(v + choose_singleton (v*-vT  R)T*1) } < card { z . z  -v }"
proof (rule decrease_variant)
  let ?e = "choose_singleton (v*-vT  R)"
  let ?v = "v + ?eT*1"
  have 1: "?e  v*-vT"
    using choose_singleton_decreasing inf.boundedE by blast
  have 2: "singleton ?e"
    using construct_tree_inv_aux assms by auto
  show "-?v  -v"
    by simp
  have "?eT  -v*vT"
    using 1 conv_compl conv_iso by force
  also have "...  -v*1"
    by (simp add: mult_isol)
  finally show "?eT*1  -v"
    using assms by (metis is_vector_def mult_isor one_compl)
  thus "¬ (?eT*1  -?v)"
    using 2 by (metis annir compl_sup inf.absorb1 inf_compl_bot_right surj_one tarski)
qed

theorem construct_tree_total: "VARS e v D
 [ construct_tree_pre x y R ]
 D := 0;
 v := x;
 WHILE ¬ y  v
   INV { construct_tree_inv v x y D R }
   VAR { card { z . z  -v } }
    DO e := choose_singleton (v*-vT  R);
       D := D + e;
       v := v + eT*1
    OD
 [ construct_tree_post x y D R ]"
  apply vcg_tc
  using construct_tree_pre apply blast
  apply (rule CollectI, rule conjI)
  using construct_tree_inv apply blast
  using construct_tree_inv_termination apply force
  using construct_tree_post by blast

end (* relation_algebra_rtc_tarski_choose_point_finite *)

subsection ‹Construction of a non-empty cycle›

text ‹
The second step is to construct a path from the root to a given vertex in the tree.
Adding an edge back to the root gives the cycle.
›

context relation_algebra_rtc_tarski_choose_point
begin

abbreviation comment
  where "comment _  SKIP" (* instead of inner comments *)
abbreviation construct_cycle_inv
  where "construct_cycle_inv v x y D R  construct_tree_inv v x y D R  point y  y*xT  R"

lemma construct_cycle_pre:
 assumes " ¬ is_acyclic R"
     and "y = choose_point ((R+  1')*1)"
     and "x = choose_point (R*y  RT*y)"
   shows "construct_cycle_inv x x y 0 R"
proof(rule conjI, rule_tac [2] conjI)
  show point_y: "point y"
    using assms by (simp add: choose_point_point is_vector_def mult_assoc galois_aux ss_p18)
  have "R*y  RT*y  0"
  proof
    have "R+  1' = (R+)T  1'"
      by (metis (mono_tags, opaque_lifting) conv_e conv_times inf.cobounded1 inf.commute
                many_strongly_connected_iff_6_eq mult_oner star_subid)
    also have "... = RT+  1'"
      using plus_conv by fastforce
    also have "...  (RT  R)*RT"
      by (metis conv_contrav conv_e conv_invol modular_2_var mult_oner star_slide_var)
    also have "...  (RT  R)*1"
      by (simp add: mult_isol)
    finally have a: "(R+  1')*1  (RT  R)*1"
      by (metis mult_assoc mult_isor one_idem_mult)
    assume "R*y  RT*y = 0"
    hence "(R  RT)*y = 0"
      using point_y inj_distr point_def by blast
    hence "(R  RT)T*1  -y"
      by (simp add: conv_galois_1)
    hence "y  -((R  RT)T*1)"
      using compl_le_swap1 by blast
    also have "... = -((RT  R)*1)"
      by (simp add: star_conv)
    also have "...  -((R+  1')*1)"
      using a comp_anti by blast
    also have "...  -y"
      by (simp add: assms galois_aux ss_p18 choose_point_decreasing)
    finally have "y = 0"
      using inf.absorb2 by fastforce
    thus False
      using point_y annir point_equations(2) point_is_point tarski by force
  qed
  hence point_x: "point x"
    by (metis point_y assms(3) inj_distr is_vector_def mult_assoc point_def choose_point_point)
  hence "y  RT * x"
    by (metis assms(3) point_y choose_point_decreasing inf_le1 order.trans point_swap star_conv)
  thus tree_inv: "construct_tree_inv x x y 0 R"
    using point_x construct_tree_pre by blast
  show "y * xT  R"
  proof -
    have "x  R*y  RT*y"
      using assms(3) choose_point_decreasing by blast
    also have "... = (R  RT)*y"
      using point_y inj_distr point_def by fastforce
    finally have "x*yT  R  RT"
      using point_y point_def ss423bij by blast
    also have "...  RT"
      by simp
    finally show ?thesis
      using conv_iso by force
  qed
qed

lemma construct_cycle_pre2:
 assumes "y  v"
     and "construct_cycle_inv v x y D R"
   shows "construct_path_inv y x y D 0  D  R  D * x = 0  y * xT  R"
proof(intro conjI, simp_all add: assms)
  show "D * y  DT * x"
    using assms construct_tree_post by blast
  show "path 0"
    by (simp add: is_inj_def is_p_fun_def path_def)
  show "y  0"
    using assms(2) is_point_def point_is_point by blast
qed

lemma construct_cycle_post:
  assumes "¬ q  x"
      and "(construct_path_inv q x y D W  D  R  D * x = 0  y * xT  R)"
    shows "W + y * xT  0  W + y * xT  R  cycle (W + y * xT)"
proof(intro conjI)
  let ?C = "W + y*xT"
  show "?C  0"
    by (metis assms acyclic_imp_one_step_different_points(2) no_trivial_inverse point_def ss423bij
              sup_bot.monoid_axioms monoid.left_neutral)
  show "?C  R"
    using assms(2) order_trans sup.boundedI by blast
  show "path (W + y * xT)"
    by (metis assms construct_tree_pre edge_is_path less_eq_def path_edge_equals_cycle
              point_is_point terminating_iff1)
  show "many_strongly_connected (W + y * xT)"
    by (metis assms construct_tree_pre bot_least conv_zero less_eq_def
              path_edge_equals_cycle star_conv star_subid terminating_iff1)
  qed

theorem construct_cycle_partial: "VARS e p q v x y C D W
  { ¬ is_acyclic R }
  y := choose_point ((R+  1')*1);
  x := choose_point (R*y  RT*y);
  D := 0;
  v := x;
  WHILE ¬ y  v
    INV { construct_cycle_inv v x y D R }
     DO e := choose_singleton (v*-vT  R);
        D := D + e;
        v := v + eT*1
     OD;
  comment { is_acyclic D  point y  point x  D*y  DT*x };
  W := 0;
  q := y;
  WHILE q  x
    INV { construct_path_inv q x y D W  D  R  D*x = 0  y*xT  R }
     DO p := choose_point (D*q);
        W := W + p*qT;
        q := p
     OD;
  comment { W  D  terminating_path W  (W = 0  q=y)  (W  0  q = start_points W  y = end_points W) };
  C := W + y*xT
  { C  0  C  R  cycle C }"
  apply vcg
  using construct_cycle_pre apply blast
  using construct_tree_inv apply blast
  using construct_cycle_pre2 apply blast
  using construct_path_inv apply blast
  using construct_cycle_post by blast

end (* relation_algebra_rtc_tarski_choose_point *)

context relation_algebra_rtc_tarski_choose_point_finite
begin

theorem construct_cycle_total: "VARS e p q v x y C D W
  [ ¬ is_acyclic R ]
  y := choose_point ((R+  1')*1);
  x := choose_point (R*y  RT*y);
  D := 0;
  v := x;
  WHILE ¬ y  v
    INV { construct_cycle_inv v x y D R }
    VAR { card { z . z  -v } }
     DO e := choose_singleton (v*-vT  R);
        D := D + e;
        v := v + eT*1
     OD;
  comment { is_acyclic D  point y  point x  D*y  DT*x };
  W := 0;
  q := y;
  WHILE q  x
    INV { construct_path_inv q x y D W  D  R  D*x = 0  y*xT  R }
    VAR { card { z . z  -W } }
     DO p := choose_point (D*q);
        W := W + p*qT;
        q := p
     OD;
  comment { W  D  terminating_path W  (W = 0  q=y)  (W  0  q = start_points W  y = end_points W)};
  C := W + y*xT
 [ C  0  C  R  cycle C ]"
  apply vcg_tc
  using construct_cycle_pre apply blast
  apply (rule CollectI, rule conjI)
  using construct_tree_inv apply blast
  using construct_tree_inv_termination apply force
  using construct_cycle_pre2 apply blast
  apply (rule CollectI, rule conjI)
  using construct_path_inv apply blast
  using construct_path_inv_termination apply clarsimp
  using construct_cycle_post by blast

end (* relation_algebra_rtc_tarski_choose_point_finite *)

end