# Theory Designs_And_Graphs

(* Title: Designs_And_Graphs.thy
Author: Chelsea Edmonds
*)

section ‹Graphs and Designs›
text ‹There are many links between graphs and design - most fundamentally that graphs are designs›

theory Designs_And_Graphs imports Block_Designs Graph_Theory.Digraph Graph_Theory.Digraph_Component
begin

subsection ‹Non-empty digraphs›
text ‹First, we define the concept of a non-empty digraph. This mirrors the idea of a "proper design"
in the design theory library›
locale non_empty_digraph = wf_digraph +
assumes arcs_not_empty: "arcs G ≠ {}"

begin

lemma verts_not_empty: "verts G ≠ {}"
using wf arcs_not_empty head_in_verts by fastforce

end

subsection ‹Arcs to Blocks›
text ‹A digraph uses a pair of points to define an ordered edge. In the case of simple graphs,
both possible orderings will be in the arcs set. Blocks are inherently unordered, and as such
a method is required to convert between the two representations›
context graph
begin

definition arc_to_block :: "'b ⇒ 'a set" where
"arc_to_block e ≡ {tail G e, head G e}"

lemma arc_to_block_to_ends: "{fst (arc_to_ends G e), snd (arc_to_ends G e)} = arc_to_block e"

lemma arc_to_block_to_ends_swap: "{snd (arc_to_ends G e), fst (arc_to_ends G e)} = arc_to_block e"
using arc_to_block_to_ends

lemma arc_to_ends_to_block: "arc_to_block e = {x, y} ⟹
arc_to_ends G e = (x, y) ∨ arc_to_ends G e = (y, x)"
by (metis arc_to_block_def arc_to_ends_def doubleton_eq_iff)

lemma arc_to_block_sym: "arc_to_ends G e1 = (u, v) ⟹ arc_to_ends G e2 = (v, u) ⟹
arc_to_block e1 = arc_to_block e2"
by (simp add: arc_to_block_def arc_to_ends_def insert_commute)

definition arcs_blocks :: "'a set multiset" where
"arcs_blocks ≡ mset_set (arc_to_block  (arcs G))"

lemma  arcs_blocks_ends: "(x, y) ∈ arcs_ends G ⟹ {x, y} ∈# arcs_blocks"
proof (auto simp add: arcs_ends_def arcs_blocks_def )
fix xa
assume assm1: "(x, y) = arc_to_ends G xa" and assm2: "xa ∈ arcs G"
obtain z where zin: "z ∈ (arc_to_block  (arcs G))" and "z = arc_to_block xa"
using assm2 by blast
thus "{x, y} ∈ arc_to_block  (arcs G)" using assm1 arc_to_block_to_ends
by (metis fst_conv snd_conv)
qed

lemma arc_ends_blocks_subset: "E ⊆ arcs G ⟹ (x, y) ∈ ((arc_to_ends G)  E) ⟹
{x, y} ∈ (arc_to_block  E)"
by (auto simp add: arc_to_ends_def arc_to_block_def )

lemma arc_blocks_end_subset: assumes "E ⊆ arcs G"  and "{x, y} ∈ (arc_to_block  E)"
shows "(x, y) ∈ ((arc_to_ends G)  E) ∨ (y, x) ∈ ((arc_to_ends G)  E)"
proof -
obtain e where "e ∈ E" and "arc_to_block e = {x,y}" using assms
by fastforce
then have "arc_to_ends G e = (x, y) ∨ arc_to_ends G e = (y, x)"
using arc_to_ends_to_block by simp
thus ?thesis
by (metis ‹e ∈ E› image_iff)
qed

lemma arcs_ends_blocks: "{x, y} ∈# arcs_blocks ⟹ (x, y) ∈ arcs_ends G ∧ (y, x) ∈ arcs_ends G"
proof (auto simp add: arcs_ends_def arcs_blocks_def )
fix xa
assume assm1: "{x, y} = arc_to_block  xa" and assm2: "xa ∈ (arcs G)"
obtain z where zin: "z ∈ (arc_to_ends G  (arcs G))" and "z = arc_to_ends G xa"
using assm2 by blast
then have "z = (x, y) ∨ z = (y, x)" using arc_to_block_to_ends assm1
by (metis arc_to_ends_def doubleton_eq_iff fst_conv snd_conv) (* Slow *)
thus "(x, y) ∈ arc_to_ends G  (arcs G)" using assm2
by (metis arcs_ends_def arcs_ends_symmetric sym_arcs zin)
next
fix xa
assume assm1: "{x, y} = arc_to_block  xa" and assm2: "xa ∈ (arcs G)"
thus "(y, x) ∈ arc_to_ends G  arcs G" using arcs_ends_def
by (metis dual_order.refl graph.arc_blocks_end_subset graph_axioms graph_symmetric imageI)
qed

lemma arcs_blocks_iff: "{x, y} ∈# arcs_blocks ⟷ (x, y) ∈ arcs_ends G ∧ (y, x) ∈ arcs_ends G"
using arcs_ends_blocks arcs_blocks_ends by blast

lemma arcs_ends_wf: "(x, y) ∈ arcs_ends G ⟹ x ∈ verts G ∧ y ∈ verts G"
by auto

lemma arcs_blocks_elem: "bl ∈# arcs_blocks ⟹ ∃ x y . bl = {x, y}"
by (meson arc_to_block_def)

lemma arcs_ends_blocks_wf:
assumes "bl ∈# arcs_blocks"
shows "bl ⊆ verts G"
proof -
obtain x y where blpair: "bl = {x, y}" using arcs_blocks_elem assms
by fastforce
then have "(x, y) ∈ arcs_ends G" using arcs_ends_blocks assms by simp
thus ?thesis using arcs_ends_wf blpair by auto
qed

lemma arcs_blocks_simple: "bl ∈# arcs_blocks ⟹ count (arcs_blocks) bl = 1"

lemma arcs_blocks_ne: "arcs G ≠ {} ⟹ arcs_blocks ≠ {#}"
by (simp add: arcs_blocks_iff arcs_blocks_def mset_set_empty_iff)

end

subsection ‹Graphs are designs›

text ‹Prove that a graph is a number of different types of designs›
sublocale graph ⊆ design "verts G" "arcs_blocks"
using arcs_ends_blocks_wf arcs_blocks_elem by (unfold_locales) (auto)

sublocale graph ⊆ simple_design "verts G" "arcs_blocks"
using arcs_ends_blocks_wf arcs_blocks_elem arcs_blocks_simple by (unfold_locales) (auto)

locale non_empty_graph = graph + non_empty_digraph

sublocale non_empty_graph ⊆ proper_design "verts G" "arcs_blocks"
using arcs_blocks_ne arcs_not_empty by (unfold_locales) simp

lemma (in graph) graph_block_size: assumes "bl ∈# arcs_blocks" shows "card bl = 2"
proof -
obtain x y where blrep: "bl = {x, y}" using assms arcs_blocks_elem
by fastforce
then have "(x, y) ∈ arcs_ends G" using arcs_ends_blocks assms by simp
then have "x ≠ y" using no_loops using adj_not_same by blast
thus ?thesis using blrep by simp
qed

sublocale non_empty_graph ⊆ block_design "verts G" "arcs_blocks" 2
using arcs_not_empty graph_block_size arcs_blocks_ne by (unfold_locales) simp_all

subsection ‹R-regular graphs›
text ‹To reason on r-regular graphs and their link to designs, we require a number of extensions to
lemmas reasoning around the degrees of vertices›
context sym_digraph
begin

lemma in_out_arcs_reflexive: "v ∈ verts G ⟹ (e ∈ (in_arcs G v) ⟹
∃ e' . (e' ∈ (out_arcs G v) ∧ head G e' = tail G e))"
using symmetric_conv sym_arcs by fastforce

lemma out_in_arcs_reflexive: "v ∈ verts G ⟹ (e ∈ (out_arcs G v) ⟹
∃ e' . (e' ∈ (in_arcs G v) ∧ tail G e' = head G e))"
using symmetric_conv sym_arcs by fastforce

end

context nomulti_digraph
begin

lemma in_arcs_single_per_vert:
assumes "v ∈ verts G" and "u ∈ verts G"
assumes "e1 ∈ in_arcs G v" and " e2 ∈ in_arcs G v"
assumes "tail G e1 = u" and "tail G e2 = u"
shows "e1 = e2"
proof -
have in_arcs1: "e1 ∈ arcs G" and in_arcs2: "e2 ∈ arcs G" using assms by auto
have "arc_to_ends G e1 = arc_to_ends G e2" using assms arc_to_ends_def
by (metis in_in_arcs_conv)
thus ?thesis using in_arcs1 in_arcs2 no_multi_arcs by simp
qed

lemma out_arcs_single_per_vert:
assumes "v ∈ verts G" and "u ∈ verts G"
assumes "e1 ∈ out_arcs G v" and " e2 ∈ out_arcs G v"
assumes "head G e1 = u" and "head G e2 = u"
shows "e1 = e2"
proof -
have in_arcs1: "e1 ∈ arcs G" and in_arcs2: "e2 ∈ arcs G" using assms by auto
have "arc_to_ends G e1 = arc_to_ends G e2" using assms arc_to_ends_def
by (metis in_out_arcs_conv)
thus ?thesis using in_arcs1 in_arcs2 no_multi_arcs by simp
qed

end

text ‹Some helpers on the transformation arc definition›
context graph
begin

lemma arc_to_block_is_inj_in_arcs: "inj_on arc_to_block (in_arcs G v)"
apply (auto simp add: arc_to_block_def inj_on_def)
by (metis arc_to_ends_def doubleton_eq_iff no_multi_arcs)

lemma arc_to_block_is_inj_out_arcs: "inj_on arc_to_block (out_arcs G v)"
apply (auto simp add: arc_to_block_def inj_on_def)
by (metis arc_to_ends_def doubleton_eq_iff no_multi_arcs)

lemma in_out_arcs_reflexive_uniq: "v ∈ verts G ⟹ (e ∈ (in_arcs G v) ⟹
∃! e' . (e' ∈ (out_arcs G v) ∧ head G e' = tail G e))"
apply auto
using symmetric_conv apply fastforce
using out_arcs_single_per_vert by (metis head_in_verts in_out_arcs_conv)

lemma out_in_arcs_reflexive_uniq: "v ∈ verts G ⟹ e ∈ (out_arcs G v) ⟹
∃! e' . (e' ∈ (in_arcs G v) ∧ tail G e' = head G e)"
apply auto
using symmetric_conv apply fastforce
using in_arcs_single_per_vert by (metis tail_in_verts in_in_arcs_conv)

lemma in_eq_out_arc_ends: "(u, v) ∈ ((arc_to_ends G)  (in_arcs G v)) ⟷
(v, u) ∈ ((arc_to_ends G)  (out_arcs G v))"
using arc_to_ends_def in_in_arcs_conv in_out_arcs_conv
by (smt (z3) Pair_inject adj_in_verts(1) dominatesI image_iff out_in_arcs_reflexive_uniq)

lemma in_degree_eq_card_arc_ends: "in_degree G v = card ((arc_to_ends G)  (in_arcs G v))"
using  no_multi_arcs by (metis card_image in_arcs_in_arcs inj_onI)

lemma in_degree_eq_card_arc_blocks: "in_degree G v = card (arc_to_block  (in_arcs G v))"
using no_multi_arcs arc_to_block_is_inj_in_arcs by (simp add: card_image)

lemma out_degree_eq_card_arc_blocks: "out_degree G v = card (arc_to_block  (out_arcs G v))"
using no_multi_arcs arc_to_block_is_inj_out_arcs by (simp add: card_image)

lemma out_degree_eq_card_arc_ends: "out_degree G v = card ((arc_to_ends G)  (out_arcs G v))"
using no_multi_arcs by (metis card_image out_arcs_in_arcs inj_onI)

lemma bij_betw_in_out_arcs: "bij_betw (λ (u, v) . (v, u)) ((arc_to_ends G)  (in_arcs G v))
((arc_to_ends G)  (out_arcs G v))"
apply (metis Pair_inject arc_to_ends_def image_eqI in_eq_out_arc_ends in_in_arcs_conv)
by (metis arc_to_ends_def imageI in_eq_out_arc_ends in_out_arcs_conv pair_imageI)

lemma in_eq_out_degree: "in_degree G v = out_degree G v"
using bij_betw_in_out_arcs bij_betw_same_card in_degree_eq_card_arc_ends
out_degree_eq_card_arc_ends by auto

lemma in_out_arcs_blocks: "arc_to_block  (in_arcs G v) = arc_to_block  (out_arcs G v)"
proof (auto)
fix xa
assume a1: "xa ∈ arcs G" and a2: "v = head G xa"
then have "xa ∈ in_arcs G v" by simp
then obtain e where out: "e ∈ out_arcs G v" and "head G e = tail G xa"
using out_in_arcs_reflexive_uniq by force
then have "arc_to_ends G e = (v, tail G xa)"
then have "arc_to_block xa = arc_to_block e"
using arc_to_block_sym by (metis a2 arc_to_ends_def)
then show "arc_to_block xa ∈ arc_to_block  out_arcs G (head G xa)"
using out a2 by blast
next
fix xa
assume a1: "xa ∈ arcs G" and a2: "v = tail G xa"
then have "xa ∈ out_arcs G v" by simp
then obtain e where ina: "e ∈ in_arcs G v" and "tail G e = head G xa"
using out_in_arcs_reflexive_uniq by force
then have "arc_to_ends G e = (head G xa, v)"
then have "arc_to_block xa = arc_to_block e"
using arc_to_block_sym by (metis a2 arc_to_ends_def)
then show "arc_to_block xa ∈ arc_to_block  in_arcs G (tail G xa)"
using ina a2 by blast
qed

end

text ‹A regular digraph is defined as one where the in degree equals the out degree which in turn
equals some fixed integer $\mathrm{r}$›
locale regular_digraph = wf_digraph +
fixes 𝗋 :: nat
assumes in_deg_r: "v ∈ verts G ⟹ in_degree G v = 𝗋"
assumes out_deg_r: "v ∈ verts G ⟹ out_degree G v = 𝗋"

locale regular_graph = graph + regular_digraph
begin

lemma rep_vertices_in_blocks [simp]:
assumes "x ∈ verts G"
shows "size {# e ∈# arcs_blocks . x ∈ e #} = 𝗋"
proof -
have "⋀ e . e ∈ (arc_to_block  (arcs G)) ⟹ x ∈ e ⟹ e ∈ (arc_to_block  in_arcs G x)"
using arc_to_block_def in_in_arcs_conv insert_commute insert_iff singleton_iff sym_arcs
symmetric_conv by fastforce
then have "{ e ∈ (arc_to_block  (arcs G)) . x ∈ e} =  (arc_to_block  (in_arcs G x))"
using arc_to_block_def by auto
then have "card { e ∈ (arc_to_block  (arcs G)) . x ∈ e} = 𝗋"
using in_deg_r in_degree_eq_card_arc_blocks assms by auto
thus ?thesis
using arcs_blocks_def finite_arcs by force
qed

end

text ‹Intro rules for regular graphs›
lemma graph_in_degree_r_imp_reg[intro]: assumes "graph G"
assumes "(⋀ v . v ∈ (verts G) ⟹ in_degree G v = 𝗋)"
shows "regular_graph G 𝗋"
proof -
interpret g: graph G using assms by simp
interpret wf: wf_digraph G by (simp add: g.wf_digraph_axioms)
show ?thesis
using assms(2) g.in_eq_out_degree by (unfold_locales) simp_all
qed

lemma graph_out_degree_r_imp_reg[intro]: assumes "graph G"
assumes "(⋀ v . v ∈ (verts G) ⟹ out_degree G v = 𝗋)"
shows "regular_graph G 𝗋"
proof -
interpret g: graph G using assms by simp
interpret wf: wf_digraph G by (simp add: g.wf_digraph_axioms)
show ?thesis
using assms(2) g.in_eq_out_degree by (unfold_locales) simp_all
qed

text ‹Regular graphs (non-empty) can be shown to be a constant rep design›
locale non_empty_regular_graph = regular_graph + non_empty_digraph

sublocale non_empty_regular_graph ⊆ non_empty_graph
by unfold_locales

sublocale non_empty_regular_graph ⊆ constant_rep_design "verts G" "arcs_blocks" 𝗋
using arcs_blocks_ne arcs_not_empty
end