Theory Path_Automation
section ‹Automation for paths›
theory Path_Automation
imports "HOL-Library.Sublist" Path_Equivalence
begin
text ‹
In this section, we provide some machinery to make certain common arguments about paths easier.
In particular:
▪ Proving the equivalence of some combination of lines and circular arcs modulo
associativity
▪ Proving the equivalence of loops modulo associativity and ``rotation''
▪ Proving subpath relationships
▪ Decomposing a contour integral over a composite path into the contour integrals of its
constituent paths
Equivalence arguments that involve splitting, e.g.\
\<^prop>‹linepath 0 1 +++ linepath 1 2 ≡⇩p linepath 0 2› are not supported.
›
subsection ‹Joining a list of paths together›
text ‹
The following operation takes a non-empty list of paths and joines them together
left-to-right, i.e. it is an ‹n›-ary version of \<^const>‹joinpaths›. Associativity is to the right.
A list of paths is considered well-formed if it is non-empty, each path is indeed a well-formed
path, and each successive pair of paths has compatible ends.
›
fun joinpaths_list :: "(real ⇒ 'a :: real_normed_vector) list ⇒ real ⇒ 'a" where
"joinpaths_list [] = linepath 0 0"
| "joinpaths_list [p] = p"
| "joinpaths_list (p # ps) = p +++ joinpaths_list ps"
lemma joinpaths_list_Cons [simp]: "ps ≠ [] ⟹ joinpaths_list (p # ps) = p +++ joinpaths_list ps"
by (cases ps) auto
fun wf_pathlist :: "(real ⇒ 'a :: real_normed_vector) list ⇒ bool" where
"wf_pathlist [] ⟷ False"
| "wf_pathlist [p] ⟷ path p"
| "wf_pathlist (p # q # ps) ⟷ path p ∧ path q ∧ pathfinish p = pathstart q ∧ wf_pathlist (q # ps)"
fun weak_wf_pathlist :: "(real ⇒ 'a :: real_normed_vector) list ⇒ bool" where
"weak_wf_pathlist [] ⟷ False"
| "weak_wf_pathlist [p] ⟷ True"
| "weak_wf_pathlist (p # q # ps) ⟷ pathfinish p = pathstart q ∧ weak_wf_pathlist (q # ps)"
fun arc_joinpaths_list_aux :: "(real ⇒ 'a :: real_normed_vector) list ⇒ bool" where
"arc_joinpaths_list_aux [] ⟷ False"
| "arc_joinpaths_list_aux [p] ⟷ True"
| "arc_joinpaths_list_aux (p # q # ps) ⟷
path_image p ∩ path_image q ⊆ {pathfinish p} ∧
(∀r∈set ps. path_image p ∩ path_image r = {}) ∧
arc_joinpaths_list_aux (q # ps)"
definition arc_joinpaths_list :: "(real ⇒ 'a :: real_normed_vector) list ⇒ bool" where
"arc_joinpaths_list ps ⟷ arc_joinpaths_list_aux ps ∧ (∀p∈set ps. arc p)"
fun simple_joinpaths_list :: "(real ⇒ 'a :: real_normed_vector) list ⇒ bool" where
"simple_joinpaths_list [] ⟷ False"
| "simple_joinpaths_list [p] ⟷ simple_path p"
| "simple_joinpaths_list [p, q] ⟷
path_image p ∩ path_image q ⊆ {pathfinish p} ∪ ({pathstart p} ∩ {pathfinish q}) ∧ arc p ∧ arc q"
| "simple_joinpaths_list (p # q # ps) ⟷
path_image p ∩ path_image q ⊆ {pathfinish p} ∧
(∀r∈set (butlast ps). path_image p ∩ path_image r = {}) ∧
path_image p ∩ path_image (last ps) ⊆ {pathstart p} ∩ {pathfinish (last ps)} ∧
arc_joinpaths_list_aux (q # ps) ∧ arc p ∧ (∀r∈set (q#ps). arc r)"
lemma simple_joinpaths_list_Cons [simp]:
assumes "ps ≠ []"
shows "simple_joinpaths_list (p # q # ps) ⟷
path_image p ∩ path_image q ⊆ {pathfinish p} ∧
(∀r∈set (butlast ps). path_image p ∩ path_image r = {}) ∧
path_image p ∩ path_image (last ps) ⊆ {pathstart p} ∩ {pathfinish (last ps)} ∧
arc_joinpaths_list_aux (q # ps) ∧ arc p ∧ (∀q∈set (q#ps). arc q)"
using assms by (cases ps rule: simple_joinpaths_list.cases) simp_all
lemma wf_pathlist_Cons:
"wf_pathlist (p # ps) ⟷ path p ∧ (ps = [] ∨ pathfinish p = pathstart (hd ps) ∧ wf_pathlist ps)"
by (induction ps arbitrary: p) auto
lemma weak_wf_pathlist_Cons:
"weak_wf_pathlist (p # ps) ⟷ (ps = [] ∨ pathfinish p = pathstart (hd ps) ∧ weak_wf_pathlist ps)"
by (induction ps arbitrary: p) auto
fun valid_path_pathlist where
"valid_path_pathlist [] ⟷ False"
| "valid_path_pathlist [p] ⟷ valid_path p"
| "valid_path_pathlist (p # ps) ⟷ valid_path p ∧ valid_path_pathlist ps"
lemma valid_path_pathlist_Cons:
"valid_path_pathlist (p # ps) ⟷ valid_path p ∧ (ps = [] ∨ valid_path_pathlist ps)"
by (cases ps) auto
lemma valid_path_pathlist_altdef: "valid_path_pathlist xs ⟷ xs ≠ [] ∧ list_all valid_path xs"
by (induction xs) (auto simp: valid_path_pathlist_Cons)
lemma valid_path_weak_wf_pathlist_imp_wf:
"valid_path_pathlist ps ⟹ weak_wf_pathlist ps ⟹ wf_pathlist ps"
by (induction ps)
(auto dest: valid_path_imp_path simp: valid_path_pathlist_Cons
weak_wf_pathlist_Cons wf_pathlist_Cons)
lemma wf_pathlist_append:
assumes "ps ≠ []" "qs ≠ []"
shows "wf_pathlist (ps @ qs) ⟷
wf_pathlist ps ∧ wf_pathlist qs ∧ pathfinish (last ps) = pathstart (hd qs)"
using assms
by (induction ps arbitrary: qs rule: wf_pathlist.induct) (auto simp: wf_pathlist_Cons)
lemma wf_pathlist_append':
"wf_pathlist (ps @ qs) ⟷ (ps = [] ∧ wf_pathlist qs) ∨ (qs = [] ∧ wf_pathlist ps) ∨
(wf_pathlist ps ∧ wf_pathlist qs ∧ pathfinish (last ps) = pathstart (hd qs))"
using wf_pathlist_append[of ps qs] by (cases "ps = []"; cases "qs = []") auto
lemma weak_wf_pathlist_append:
assumes "ps ≠ []" "qs ≠ []"
shows "weak_wf_pathlist (ps @ qs) ⟷
weak_wf_pathlist ps ∧ weak_wf_pathlist qs ∧ pathfinish (last ps) = pathstart (hd qs)"
using assms
by (induction ps arbitrary: qs rule: weak_wf_pathlist.induct) (auto simp: weak_wf_pathlist_Cons)
lemma weak_wf_pathlist_append':
"weak_wf_pathlist (ps @ qs) ⟷ (ps = [] ∧ weak_wf_pathlist qs) ∨ (qs = [] ∧ weak_wf_pathlist ps) ∨
(weak_wf_pathlist ps ∧ weak_wf_pathlist qs ∧ pathfinish (last ps) = pathstart (hd qs))"
using weak_wf_pathlist_append[of ps qs] by (cases "ps = []"; cases "qs = []") auto
lemma pathstart_joinpaths_list [simp]:
"xs ≠ [] ⟹ pathstart (joinpaths_list xs) = pathstart (hd xs)"
by (induction xs rule: joinpaths_list.induct) auto
lemma pathfinish_joinpaths_list [simp]:
"xs ≠ [] ⟹ pathfinish (joinpaths_list xs) = pathfinish (last xs)"
by (induction xs rule: joinpaths_list.induct) auto
lemma path_joinpaths_list [simp, intro]: "wf_pathlist xs ⟹ path (joinpaths_list xs)"
by (induction xs rule: joinpaths_list.induct) auto
lemma valid_path_joinpaths_list [intro]:
"valid_path_pathlist xs ⟹ weak_wf_pathlist xs ⟹ valid_path (joinpaths_list xs)"
by (induction xs rule: joinpaths_list.induct) (auto intro!: valid_path_join)
lemma path_image_joinpaths_list:
assumes "wf_pathlist ps"
shows "path_image (joinpaths_list ps) = (⋃p∈set ps. path_image p)"
using assms by (induction ps rule: wf_pathlist.induct) (auto simp: path_image_join)
lemma joinpaths_list_append:
assumes "wf_pathlist xs" "wf_pathlist ys" "pathfinish (last xs) = pathstart (hd ys)"
shows "joinpaths_list (xs @ ys) ≡⇩p joinpaths_list xs +++ joinpaths_list ys"
proof -
from assms(1) have "xs ≠ []"
by auto
from assms show ?thesis
proof (induction xs arbitrary: ys rule: joinpaths_list.induct)
case (2 p ys)
have "ys ≠ []"
using 2 by auto
then obtain y ys' where [simp]: "ys = y # ys'"
by (cases ys) auto
show ?case using 2 by auto
next
case (3 p1 p2 ps qs)
obtain q qs' where [simp]: "qs = q # qs'"
using 3 by (cases qs) auto
have "joinpaths_list ((p1 # p2 # ps) @ qs) =
p1 +++ joinpaths_list ((p2 # ps) @ qs)"
by simp
also have "… ≡⇩p p1 +++ joinpaths_list (p2 # ps) +++ joinpaths_list qs"
using 3 by (intro eq_paths_join eq_paths_refl 3) auto
also have "… ≡⇩p (p1 +++ joinpaths_list (p2 # ps)) +++ joinpaths_list qs"
by (intro eq_paths_join_assoc2) (use 3 in auto)
finally show ?case
by simp
qed auto
qed
lemma arc_joinpaths_list_weak_wf_imp_wf:
assumes "weak_wf_pathlist xs" "arc_joinpaths_list xs"
shows "wf_pathlist xs"
using assms
by (induction xs rule: wf_pathlist.induct) (auto intro: arc_imp_path simp: arc_joinpaths_list_def)
lemma arc_joinpaths_aux:
assumes "wf_pathlist xs" "arc_joinpaths_list_aux xs" "∀x∈set xs. arc x"
shows "arc (joinpaths_list xs)"
using assms
proof (induction xs rule: wf_pathlist.induct)
case (3 p q ps)
thus ?case
by (fastforce intro!: arc_join simp: path_image_joinpaths_list)
qed auto
lemma arc_joinpaths_list [intro?]:
assumes "weak_wf_pathlist xs" "arc_joinpaths_list xs"
shows "arc (joinpaths_list xs)"
using assms arc_joinpaths_aux[of xs] arc_joinpaths_list_weak_wf_imp_wf[of xs]
by (auto simp: arc_joinpaths_list_def)
lemma simple_joinpaths_list_weak_wf_imp_wf:
assumes "weak_wf_pathlist xs" "simple_joinpaths_list xs"
shows "wf_pathlist xs"
using arc_joinpaths_list_weak_wf_imp_wf[of "tl xs"] assms
by (cases xs rule: simple_joinpaths_list.cases)
(auto dest: simple_path_imp_path arc_imp_path simp: arc_joinpaths_list_def)
lemma simple_path_joinpaths_list [intro?]:
assumes "weak_wf_pathlist xs" "simple_joinpaths_list xs"
shows "simple_path (joinpaths_list xs)"
proof (cases xs rule: simple_joinpaths_list.cases)
case (3 p q)
thus ?thesis using assms
by (force split: if_splits intro!: simple_path_joinI)
next
case (4 p q r rs)
define rs' where "rs' = r # rs"
have [simp]: "rs' ≠ []"
by (auto simp: rs'_def)
have [simp]: "xs = p # q # rs'"
by (simp add: 4 rs'_def)
note [simp] = wf_pathlist_Cons
have "simple_path (p +++ joinpaths_list (q # rs'))"
proof (rule simple_path_joinI)
show "arc p"
using assms by auto
next
show "arc (joinpaths_list (q # rs'))" using assms
by (intro arc_joinpaths_list) (auto split: if_splits simp: arc_joinpaths_list_def)
next
have *: "set rs' = insert (last rs') (set (butlast rs'))"
by (subst append_butlast_last_id [symmetric]) (auto simp del: append_butlast_last_id)
have "wf_pathlist (q # rs')"
using assms arc_joinpaths_list_weak_wf_imp_wf[of "q # rs'"]
by (auto simp: arc_joinpaths_list_def)
thus "path_image p ∩ path_image (joinpaths_list (q # rs'))
⊆ insert (pathstart (joinpaths_list (q # rs')))
(if pathstart p = pathfinish (joinpaths_list (q # rs')) then {pathstart p} else {})"
using assms by (subst path_image_joinpaths_list) (auto simp: *)
qed (use assms in auto)
thus ?thesis
by (simp add: rs'_def)
qed (use assms in auto)
lemma wf_pathlist_sublist:
assumes "wf_pathlist ys" "sublist xs ys" "xs ≠ []"
shows "wf_pathlist xs"
proof -
from assms(2) obtain as bs where *: "ys = as @ xs @ bs"
by (auto simp: sublist_def)
have **: "wf_pathlist xs" if "wf_pathlist (xs @ bs)"
using that ‹xs ≠ []› by (induction xs rule: wf_pathlist.induct) (auto simp: wf_pathlist_Cons)
show ?thesis
using assms(1) ‹xs ≠ []› unfolding *
by (induction as) (auto simp: ** wf_pathlist_Cons)
qed
lemma is_subpath_joinpaths_list_append_right:
assumes "wf_pathlist (xs @ ys)" "xs ≠ []"
shows "is_subpath (joinpaths_list xs) (joinpaths_list (xs @ ys))"
proof (cases "ys = []")
case False
hence "is_subpath (joinpaths_list xs) (joinpaths_list xs +++ joinpaths_list ys)"
using assms by (intro is_subpath_joinI1 path_joinpaths_list) (auto simp: wf_pathlist_append)
also have "eq_paths … (joinpaths_list (xs @ ys))"
using False assms by (intro eq_paths_sym[OF joinpaths_list_append])
(auto simp: wf_pathlist_append)
finally show ?thesis .
qed (use assms in auto)
lemma is_subpath_joinpaths_list_append_left:
assumes "wf_pathlist (xs @ ys)" "ys ≠ []"
shows "is_subpath (joinpaths_list ys) (joinpaths_list (xs @ ys))"
proof (cases "xs = []")
case False
hence "is_subpath (joinpaths_list ys) (joinpaths_list xs +++ joinpaths_list ys)"
using assms by (intro is_subpath_joinI2 path_joinpaths_list) (auto simp: wf_pathlist_append)
also have "eq_paths … (joinpaths_list (xs @ ys))"
using False assms by (intro eq_paths_sym[OF joinpaths_list_append])
(auto simp: wf_pathlist_append)
finally show ?thesis .
qed (use assms in auto)
lemma is_subpath_joinpaths_list:
assumes "wf_pathlist ys" "sublist xs ys" "xs ≠ []"
shows "is_subpath (joinpaths_list xs) (joinpaths_list ys)"
proof -
from assms(2) obtain as bs where *: "ys = as @ xs @ bs"
by (auto simp: sublist_def)
have "is_subpath (joinpaths_list xs) (joinpaths_list (xs @ bs))"
using assms by (intro is_subpath_joinpaths_list_append_right)
(auto simp: wf_pathlist_append' *)
also have "is_subpath … (joinpaths_list (as @ xs @ bs))"
using assms by (intro is_subpath_joinpaths_list_append_left)
(auto simp: wf_pathlist_append' *)
finally show ?thesis
by (simp add: *)
qed
lemma eq_loops_joinpaths_list_append:
assumes "wf_pathlist (xs @ ys)" "pathfinish (last (xs @ ys)) = pathstart (hd (xs @ ys))"
shows "eq_loops (joinpaths_list (xs @ ys)) (joinpaths_list (ys @ xs))"
proof (cases "xs = [] ∨ ys = []")
case True
have "xs ≠ [] ∨ ys ≠ []"
using assms by auto
with True show ?thesis
using assms by auto
next
case False
have "eq_paths (joinpaths_list (xs @ ys)) (joinpaths_list xs +++ joinpaths_list ys)"
using assms False by (intro joinpaths_list_append) (auto simp: wf_pathlist_append)
also have "eq_loops … (joinpaths_list ys +++ joinpaths_list xs)"
using assms False by (intro eq_loops_joinpaths_commute) (auto simp: wf_pathlist_append)
also have "eq_paths … (joinpaths_list (ys @ xs))"
using assms False
by (intro eq_paths_sym[OF joinpaths_list_append]) (auto simp: wf_pathlist_append)
finally show ?thesis .
qed
lemma eq_loops_rotate:
assumes "wf_pathlist xs" "pathfinish (last xs) = pathstart (hd xs)"
shows "eq_loops (joinpaths_list xs) (joinpaths_list (rotate n xs))"
proof -
define m where "m = n mod length xs"
have "eq_loops (joinpaths_list (take m xs @ drop m xs))
(joinpaths_list (drop m xs @ take m xs))"
using assms by (intro eq_loops_joinpaths_list_append) auto
thus ?thesis
by (simp add: m_def rotate_drop_take)
qed
lemma winding_number_joinpaths_list:
assumes "wf_pathlist ps" "⋀p. p ∈ set ps ⟹ x ∉ path_image p"
shows "winding_number (joinpaths_list ps) x = (∑p←ps. winding_number p x)"
using assms
proof (induction ps rule: wf_pathlist.induct)
case (3 p q ps)
have "winding_number (joinpaths_list (p # q # ps)) x =
winding_number (p +++ joinpaths_list (q # ps)) x"
by simp
also have "… = winding_number p x + winding_number (joinpaths_list (q # ps)) x"
using "3.prems" by (intro winding_number_join) (auto simp: path_image_joinpaths_list)
also have "winding_number (joinpaths_list (q # ps)) x = (∑r←q#ps. winding_number r x)"
by (intro "3.IH") (use "3.prems" in auto)
finally show ?case
by simp
qed auto
lemma contour_integral_joinpaths_list:
assumes "weak_wf_pathlist ps" "valid_path_pathlist ps"
"f contour_integrable_on (joinpaths_list ps)"
shows "contour_integral (joinpaths_list ps) f = (∑p←ps. contour_integral p f)"
using assms
proof (induction ps rule: wf_pathlist.induct)
case (3 p q ps)
have wf: "wf_pathlist (p # q # ps)"
using "3.prems" valid_path_weak_wf_pathlist_imp_wf by blast
have int: "f contour_integrable_on (p +++ joinpaths_list (q # ps))"
using "3.prems" by simp
have int1: "f contour_integrable_on p"
using contour_integrable_joinD1[OF int] "3.prems" by auto
have int2: "f contour_integrable_on joinpaths_list (q # ps)"
using contour_integrable_joinD2[OF int] "3.prems" by auto
have "contour_integral (joinpaths_list (p # q # ps)) f =
contour_integral (p +++ joinpaths_list (q # ps)) f"
by simp
also have "… = contour_integral p f + contour_integral (joinpaths_list (q # ps)) f"
using "3.prems" int1 int2 by (intro contour_integral_join) auto
also have "contour_integral (joinpaths_list (q # ps)) f = (∑r←q#ps. contour_integral r f)"
by (intro "3.IH") (use "3.prems" int2 in auto)
finally show ?case
by simp
qed auto
subsection ‹Representing a sequence of path joins as a tree›
text ‹
To deal with the problem that path joining is not associative, we define an expression tree
to represent all the possible different bracketings of joining ‹n› paths together.
There is also a ``flattening'' operation to convert the tree to a list of paths, since our
eventual goal is to show that the order does not matter (up to path equivalence).
Well-formedness is again defined similarly to the list case.
›