Theory Path_Algorithms
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 ≤ D⇧T⇧⋆ * 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 ≤ D⇧T⇧⋆ * 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 ≤ D⇧T⇧⋆ * 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 "W≠0 ⟹ choose_point (D*q) ≤ -((W + choose_point (D*q) * q⇧T)⇧T*1)"
proof -
let ?q = "choose_point (D*q)"
let ?W = "W + ?q * q⇧T"
assume as: "W≠0"
hence "q*W ≤ 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 ⋅ W⇧T*1 ≤ D*q ⋅ W⇧T⇧+*q"
using choose_point_decreasing meet_iso meet_isor inf_mono assms connected_root_iff2 by simp
also have "... ≤ (D ⋅ D⇧T⇧+)*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 ≤ -(W⇧T*1)"
using galois_aux le_bot by blast
have "point ?q"
using assms by(rule path_inv_points(2))
hence "?q ≤ -(q*?q⇧T*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) * q⇧T)"
and "y = end_points (W + choose_point (D*q) * q⇧T)"
proof -
let ?q = "choose_point (D*q)"
let ?W = "W + ?q * q⇧T"
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*q⇧T*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 ⋅ -(?W⇧T*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 ⋅ -(?W⇧T*1) ⋅ -q) + (?q ⋅ -q ⋅ -(?W⇧T*1))"
by simp
also have "... = (W*1 + ?q) ⋅ -(q + ?W⇧T*1)"
by (metis compl_sup inf_sup_distrib2 meet_assoc sup.commute)
also have "... ≤ ?W*1 ⋅ -(?W⇧T*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 ⋅ -(W⇧T*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 "... = (W⇧T*1 ⋅ -(W*1) ⋅ -?q) + (q ⋅ -(W*1) ⋅ -?q)"
using assms by fastforce
also have "... = (W⇧T*1 + q) ⋅ -(W*1) ⋅ -?q"
by (simp add: inf_sup_distrib2)
also have "... = (W⇧T*1 + q) ⋅ -(W*1 + ?q)"
by (simp add: inf.assoc)
also have "... = (W⇧T*1 + q*?q⇧T*1) ⋅ -(W*1 + ?q*q⇧T*1)"
using point_nq
by(metis assms(1) comp_assoc conv_contrav conv_one is_vector_def point_def sur_def_var1)
also have "... = (?W⇧T)*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)*q⇧T)"
proof (intro conjI)
let ?q = "choose_point (D*q)"
let ?W = "W + ?q * q⇧T"
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 ≤ D⇧T⇧⋆*x"
proof -
have "D⇧+*q ≤ D⇧T⇧⋆*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: "W≠0"
have b: "?q*q⇧T ≤ 1*?q*q⇧T*-(?q*q⇧T*1)"
proof -
have "?q*q⇧T ≤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*q⇧T)⇧T*1 ⋅ -((?q*q⇧T)*1) = W*1 ⋅ -(W⇧T*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*q⇧T*1 ⋅ W⇧T*1 = 0"
proof -
have "?q*q⇧T*1 ⋅ W⇧T*1 = ?q ⋅ W⇧T*1"
using assms point_nq
by (metis comp_assoc conv_contrav conv_one is_vector_def point_def sur_def_var1)
also have "... ≤ -(?W⇧T*1) ⋅ ?W⇧T*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 "?W≠0 ⟷ ?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 ≤ D⇧T⇧⋆*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*q⇧T;
q := p
OD
{ W ≤ D ∧ terminating_path W ∧ (W=0 ⟷ x=y) ∧ (W≠0 ⟷ 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
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)*q⇧T) } < card { z . z ≤ -W }"
proof -
let ?q = "choose_point (D*q)"
let ?W = "W + ?q * q⇧T"
show ?thesis
proof (rule decrease_variant)
show "-?W ≤ -W"
by simp
show "?q * q⇧T ≤ -W"
by (metis assms galois_aux inf_compl_bot_right maddux_142 mult_isor order_trans top_greatest)
show "¬ (?q * q⇧T ≤ -?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 ≤ D⇧T⇧⋆*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*q⇧T;
q := p
OD
[ W ≤ D ∧ terminating_path W ∧ (W=0 ⟷ x=y) ∧ (W≠0 ⟷ 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
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*v⇧T ≤ 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 (- (R⇧T * 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 * ?q⇧T ≤ 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*?p⇧T"
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*?p⇧T) = ?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*?p⇧T) = 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*?p⇧T)"
using assms(2) by force
have "W*1 ⋅ (q*?p⇧T)⇧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*?p⇧T)⇧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*?v⇧T ≤ ?W⇧+"
proof -
have a: "R ⋅ v*v⇧T ≤ ?W⇧+"
using assms(2) by (meson mult_isol_var order.trans order_prop star_subdist)
have b: "R ⋅ v*?p⇧T ≤ ?W⇧+"
proof -
have "R ⋅ v*?p⇧T ≤ W*1*?p⇧T + q*?p⇧T"
by (metis inf_le2 assms(2) aux4 double_compl inf_absorb2 distrib_right)
also have "... = W*?p⇧T + q*?p⇧T"
using point_p by (metis conv_contrav conv_one is_vector_def mult_assoc point_def)
also have "... ≤ W⇧+*end_points W*?p⇧T + q*?p⇧T"
using assms(2)
by (meson forward_terminating_path_end_points_1 join_iso mult_isor terminating_path_iff)
also have "... ≤ W⇧+*q*?p⇧T + q*?p⇧T"
using assms(2) by (metis annil eq_refl)
also have "... = W⇧⋆*q*?p⇧T"
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*v⇧T ≤ ?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*?p⇧T ≤ ?W⇧+"
proof -
have "R ⋅ ?p*?p⇧T ≤ 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*?v⇧T = (R ⋅ v*v⇧T) + (R ⋅ v*?p⇧T) + (R ⋅ ?p*v⇧T) + (R ⋅ ?p*?p⇧T)"
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*?p⇧T)"
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 + W⇧T)*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 + W⇧T * 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 + W⇧T*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*p⇧T;
q := p;
v := v + p
OD
{ R ≤ W⇧+ ∧ terminating_path W ∧ (W + W⇧T)*1 = -1'*1 }"
apply vcg
using topological_sort_pre apply blast
using topological_sort_inv apply blast
using topological_sort_post by blast
end
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*p⇧T;
q := p;
v := v + p
OD
[ R ≤ W⇧+ ∧ terminating_path W ∧ (W + W⇧T)*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
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 ≤ R⇧T⇧⋆*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 + D⇧T*1 ∧ x*v⇧T ≤ D⇧⋆ ∧ D ≤ v*v⇧T ∧
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 ∧ D⇧T*1 ≤ D⇧T⇧⋆*x ∧
D⇧⋆*y ≤ D⇧T⇧⋆*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*-v⇧T ⋅ R))"
proof (rule choose_singleton_singleton, rule notI)
assume "v*-v⇧T ⋅ R = 0"
hence "R⇧T⇧⋆*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*-v⇧T ⋅ R)⇧T*1) x y (D +
choose_singleton (v*-v⇧T ⋅ R)) R"
proof (intro conjI)
let ?e = "choose_singleton (v*-v⇧T ⋅ R)"
let ?D = "D + ?e"
let ?v = "v + ?e⇧T*1"
have 1: "?e ≤ v*-v⇧T"
using choose_singleton_decreasing inf.boundedE by blast
show "point x"
by (simp add: assms)
show "y ≤ R⇧T⇧⋆*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*-v⇧T*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 + ?D⇧T*1"
by (simp add: assms sup_assoc)
show "x*?v⇧T ≤ ?D⇧⋆"
proof -
have "x*?v⇧T = x*v⇧T + x*1*?e"
by (simp add: distrib_left mult_assoc)
also have "... ≤ D⇧⋆ + x*1*(?e ⋅ v*-v⇧T)"
using 1 by (metis assms(2) inf.absorb1 join_iso)
also have "... = D⇧⋆ + x*1*(?e ⋅ v ⋅ -v⇧T)"
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 ⋅ v⇧T)*?e"
by (metis assms(2) inf.commute mult_assoc vector_2)
also have "... = D⇧⋆ + x*v⇧T*?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*?v⇧T"
proof (rule sup.boundedI)
show "D ≤ ?v*?v⇧T"
using assms
by (meson conv_add distrib_left le_supI1 conv_iso dual_order.trans mult_isol_var order_prop)
have "?e ≤ v*(-v⇧T ⋅ v⇧T*?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*?v⇧T"
by (metis conv_contrav conv_invol conv_iso conv_one mult_assoc mult_isol_var sup.cobounded1
sup_ge2)
finally show "?e ≤ ?v*?v⇧T"
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*x⇧T ≤ D⇧T⇧⋆"
by (metis (no_types, lifting) assms(2) conv_contrav conv_invol conv_iso star_conv)
hence 1: "v ≤ D⇧T⇧⋆*x"
using assms point_def ss423bij by blast
hence 2: "D⇧T*1 ≤ D⇧T⇧⋆*x"
using assms le_supE by blast
have "D⇧⋆*y ≤ D⇧T⇧⋆*x"
proof (rule star_inductl, rule sup.boundedI)
show "y ≤ D⇧T⇧⋆*x"
using 1 assms order.trans by blast
next
have "D*(D⇧T⇧⋆*x) = D*x + D*D⇧T⇧+*x"
by (metis conway.dagger_unfoldl_distr distrib_left mult_assoc)
also have "... = D*D⇧T⇧+*x"
using assms by simp
also have "... ≤ 1'*D⇧T⇧⋆*x"
by (metis assms(2) is_inj_def mult_assoc mult_isor)
finally show "D*(D⇧T⇧⋆*x) ≤ D⇧T⇧⋆*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*-v⇧T ⋅ R);
D := D + e;
v := v + e⇧T*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
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*-v⇧T ⋅ R)⇧T*1) } < card { z . z ≤ -v }"
proof (rule decrease_variant)
let ?e = "choose_singleton (v*-v⇧T ⋅ R)"
let ?v = "v + ?e⇧T*1"
have 1: "?e ≤ v*-v⇧T"
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 "?e⇧T ≤ -v*v⇧T"
using 1 conv_compl conv_iso by force
also have "... ≤ -v*1"
by (simp add: mult_isol)
finally show "?e⇧T*1 ≤ -v"
using assms by (metis is_vector_def mult_isor one_compl)
thus "¬ (?e⇧T*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*-v⇧T ⋅ R);
D := D + e;
v := v + e⇧T*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
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
where "comment _ ≡ SKIP"
abbreviation construct_cycle_inv
where "construct_cycle_inv v x y D R ≡ construct_tree_inv v x y D R ∧ point y ∧ y*x⇧T ≤ R"
lemma construct_cycle_pre:
assumes " ¬ is_acyclic R"
and "y = choose_point ((R⇧+ ⋅ 1')*1)"
and "x = choose_point (R⇧⋆*y ⋅ R⇧T*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 ⋅ R⇧T*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 "... = R⇧T⇧+ ⋅ 1'"
using plus_conv by fastforce
also have "... ≤ (R⇧T⇧⋆ ⋅ R)*R⇧T"
by (metis conv_contrav conv_e conv_invol modular_2_var mult_oner star_slide_var)
also have "... ≤ (R⇧T⇧⋆ ⋅ R)*1"
by (simp add: mult_isol)
finally have a: "(R⇧+ ⋅ 1')*1 ≤ (R⇧T⇧⋆ ⋅ R)*1"
by (metis mult_assoc mult_isor one_idem_mult)
assume "R⇧⋆*y ⋅ R⇧T*y = 0"
hence "(R⇧⋆ ⋅ R⇧T)*y = 0"
using point_y inj_distr point_def by blast
hence "(R⇧⋆ ⋅ R⇧T)⇧T*1 ≤ -y"
by (simp add: conv_galois_1)
hence "y ≤ -((R⇧⋆ ⋅ R⇧T)⇧T*1)"
using compl_le_swap1 by blast
also have "... = -((R⇧T⇧⋆ ⋅ 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 ≤ R⇧T⇧⋆ * 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 * x⇧T ≤ R"
proof -
have "x ≤ R⇧⋆*y ⋅ R⇧T*y"
using assms(3) choose_point_decreasing by blast
also have "... = (R⇧⋆ ⋅ R⇧T)*y"
using point_y inj_distr point_def by fastforce
finally have "x*y⇧T ≤ R⇧⋆ ⋅ R⇧T"
using point_y point_def ss423bij by blast
also have "... ≤ R⇧T"
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 * x⇧T ≤ R"
proof(intro conjI, simp_all add: assms)
show "D⇧⋆ * y ≤ D⇧T⇧⋆ * 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 * x⇧T ≤ R)"
shows "W + y * x⇧T ≠ 0 ∧ W + y * x⇧T ≤ R ∧ cycle (W + y * x⇧T)"
proof(intro conjI)
let ?C = "W + y*x⇧T"
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 * x⇧T)"
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 * x⇧T)"
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 ⋅ R⇧T*y);
D := 0;
v := x;
WHILE ¬ y ≤ v
INV { construct_cycle_inv v x y D R }
DO e := choose_singleton (v*-v⇧T ⋅ R);
D := D + e;
v := v + e⇧T*1
OD;
comment { is_acyclic D ∧ point y ∧ point x ∧ D⇧⋆*y ≤ D⇧T⇧⋆*x };
W := 0;
q := y;
WHILE q ≠ x
INV { construct_path_inv q x y D W ∧ D ≤ R ∧ D*x = 0 ∧ y*x⇧T ≤ R }
DO p := choose_point (D*q);
W := W + p*q⇧T;
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*x⇧T
{ 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
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 ⋅ R⇧T*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*-v⇧T ⋅ R);
D := D + e;
v := v + e⇧T*1
OD;
comment { is_acyclic D ∧ point y ∧ point x ∧ D⇧⋆*y ≤ D⇧T⇧⋆*x };
W := 0;
q := y;
WHILE q ≠ x
INV { construct_path_inv q x y D W ∧ D ≤ R ∧ D*x = 0 ∧ y*x⇧T ≤ R }
VAR { card { z . z ≤ -W } }
DO p := choose_point (D*q);
W := W + p*q⇧T;
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*x⇧T
[ 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
end