Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

feat(combinatorics/simple_graph): Construct a tripartite graph from its triangles #19202

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 247 additions & 7 deletions src/combinatorics/simple_graph/triangle/basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ Copyright (c) 2022 Yaël Dillies, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Bhavik Mehta
-/
import combinatorics.double_counting
import combinatorics.simple_graph.clique
import data.nat.parity
import data.sym.card

/-!
# Triangles in graphs
Expand All @@ -18,6 +21,8 @@ This module defines and proves properties about triangles in simple graphs.

## Main declarations

* `simple_graph.edge_disjoint_triangles`: Predicate for a graph whose triangles are edge-disjoint.
* `simple_graph.locally_linear`: Predicate for each edge in a graph to be in a unique triangle.
* `simple_graph.far_from_triangle_free`: Predicate for a graph to have enough triangles that, to
remove all of them, one must one must remove a lot of edges. This is the crux of the Triangle
Removal lemma.
Expand All @@ -28,21 +33,183 @@ This module defines and proves properties about triangles in simple graphs.
* Find a better name for `far_from_triangle_free`. Added 4/26/2022. Remove this TODO if it gets old.
-/

open finset fintype nat
open_locale classical
namespace finset
variables {α : Type*} [decidable_eq α]

lemma insert_comm (a b : α) (s : finset α) : insert a (insert b s) = insert b (insert a s) :=
coe_injective $ by { push_cast, exact set.insert_comm _ _ _ }

end finset
YaelDillies marked this conversation as resolved.
Show resolved Hide resolved

namespace simple_graph
variables {V : Type*}

@[simp] lemma edge_set_top : (⊤ : simple_graph V).edge_set = {e : sym2 V | ¬ e.is_diag} :=
by ext x; induction x using sym2.ind; simp

@[simp] lemma edge_finset_top [fintype V] [decidable_eq V] [fintype (⊤ : simple_graph V).edge_set] :
(⊤ : simple_graph V).edge_finset = finset.univ.filter (λ e, ¬ e.is_diag) :=
by ext x; induction x using sym2.ind; simp

end simple_graph
YaelDillies marked this conversation as resolved.
Show resolved Hide resolved

open finset fintype (card) nat

namespace simple_graph
variables {α 𝕜 : Type*} [fintype α] [linear_ordered_field 𝕜] {G H : simple_graph α} {ε δ : 𝕜}
{n : ℕ} {s : finset α}
variables {α β 𝕜 : Type*} [linear_ordered_field 𝕜] {G H : simple_graph α} {ε δ : 𝕜} {n : ℕ}
{s : finset α}

section locally_linear

/-- A graph has edge-disjoint triangles if each edge belongs to at most one triangle. -/
def edge_disjoint_triangles (G : simple_graph α) : Prop :=
(G.clique_set 3).pairwise $ λ x y, (x ∩ y : set α).subsingleton
YaelDillies marked this conversation as resolved.
Show resolved Hide resolved

/-- A graph is locally linear if each edge belongs to exactly one triangle. -/
def locally_linear (G : simple_graph α) : Prop :=
G.edge_disjoint_triangles ∧ ∀ ⦃x y⦄, G.adj x y → ∃ s, G.is_n_clique 3 s ∧ x ∈ s ∧ y ∈ s

protected lemma locally_linear.edge_disjoint_triangles :
G.locally_linear → G.edge_disjoint_triangles :=
and.left

lemma edge_disjoint_triangles.mono (h : G ≤ H) (hH : H.edge_disjoint_triangles) :
G.edge_disjoint_triangles :=
hH.mono $ clique_set_mono h

@[simp] lemma edge_disjoint_triangles_bot : (⊥ : simple_graph α).edge_disjoint_triangles :=
by simp [edge_disjoint_triangles]

@[simp] lemma locally_linear_bot : (⊥ : simple_graph α).locally_linear := by simp [locally_linear]

lemma edge_disjoint_triangles.map (f : α ↪ β) (hG : G.edge_disjoint_triangles) :
(G.map f).edge_disjoint_triangles :=
begin
rw [edge_disjoint_triangles, clique_set_map (bit1_lt_bit1.2 zero_lt_one),
((finset.map_injective f).inj_on _).pairwise_image],
classical,
rintro s hs t ht hst,
dsimp [function.on_fun],
rw [←coe_inter, ←map_inter, coe_map, coe_inter],
exact (hG hs ht hst).image _,
end

lemma locally_linear.map (f : α ↪ β) (hG : G.locally_linear) : (G.map f).locally_linear :=
begin
refine ⟨hG.1.map _, _⟩,
rintro _ _ ⟨a, b, h, rfl, rfl⟩,
obtain ⟨s, hs, ha, hb⟩ := hG.2 h,
exact ⟨s.map f, hs.map, mem_map_of_mem _ ha, mem_map_of_mem _ hb⟩,
end

@[simp] lemma locally_linear_comap {G : simple_graph β} {e : α ≃ β} :
(G.comap e).locally_linear ↔ G.locally_linear :=
begin
refine ⟨λ h, _, _⟩,
{ rw [←comap_map_eq e.symm.to_embedding G, comap_symm, map_symm],
exact h.map _ },
{ rw [←equiv.coe_to_embedding, ←map_symm],
exact locally_linear.map _ }
end
Comment on lines +105 to +113
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is one direction of this true for embeddings?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment belongs to #19201.

I cannot really work on it because I need #19203 to be in, but I suspect the only implication that can be weakened is G.locally_linear → (G.map f).locally_linear.


variables [decidable_eq α]

lemma edge_disjoint_triangles_iff_mem_sym2_subsingleton :
G.edge_disjoint_triangles ↔
∀ ⦃e : sym2 α⦄, ¬e.is_diag → {s ∈ G.clique_set 3 | e ∈ (s : finset α).sym2}.subsingleton :=
begin
have : ∀ a b, a ≠ b → {s ∈ (G.clique_set 3 : set (finset α)) | ⟦(a, b)⟧ ∈ (s : finset α).sym2}
= {s | G.adj a b ∧ ∃ c, G.adj a c ∧ G.adj b c ∧ s = {a, b, c}},
{ rintro a b hab,
ext s,
simp only [mem_sym2_iff, sym2.mem_iff, forall_eq_or_imp, forall_eq, set.sep_and,
set.mem_inter_iff, set.mem_sep_iff, mem_clique_set_iff, set.mem_set_of_eq,
and_and_and_comm _ (_ ∈ _), and_self, is_3_clique_iff],
split,
{ rintro ⟨⟨c, d, e, hcd, hce, hde, rfl⟩, hab⟩,
simp only [mem_insert, mem_singleton] at hab,
obtain ⟨rfl | rfl | rfl, rfl | rfl | rfl⟩ := hab;
simp only [*, adj_comm, true_and, ne.def, eq_self_iff_true, not_true] at *;
refine ⟨c, _⟩ <|> refine ⟨d, _⟩ <|> refine ⟨e, _⟩; simp [*, pair_comm, insert_comm] },
{ rintro ⟨hab, c, hac, hbc, rfl⟩,
refine ⟨⟨a, b, c, _⟩, _⟩; simp [*] } },
split,
{ rw sym2.forall,
rintro hG a b hab,
simp only [sym2.is_diag_iff_proj_eq] at hab,
rw this _ _ (sym2.mk_is_diag_iff.not.2 hab),
rintro _ ⟨hab, c, hac, hbc, rfl⟩ _ ⟨-, d, had, hbd, rfl⟩,
refine hG.eq _ _ (set.nontrivial.not_subsingleton ⟨a, _, b, _, hab.ne⟩);
simp [is_3_clique_triple_iff, *] },
{ simp only [edge_disjoint_triangles, is_3_clique_iff, set.pairwise, mem_clique_set_iff, ne.def,
forall_exists_index, and_imp, ←@set.not_nontrivial_iff _ (_ ∩ _), not_imp_not, set.nontrivial,
set.mem_inter_iff, mem_coe],
rintro hG _ a b c hab hac hbc rfl _ d e f hde hdf hef rfl g hg₁ hg₂ h hh₁ hh₂ hgh,
refine hG (sym2.mk_is_diag_iff.not.2 hgh)_ _; simp [is_3_clique_triple_iff, *] }
end

alias edge_disjoint_triangles_iff_mem_sym2_subsingleton ↔
edge_disjoint_triangles.mem_sym2_subsingleton _

variables [fintype α] [decidable_rel G.adj]

instance : decidable G.edge_disjoint_triangles :=
decidable_of_iff ((G.clique_finset 3 : set (finset α)).pairwise $ λ x y, (x ∩ y).card ≤ 1) $
by simpa only [coe_clique_finset, edge_disjoint_triangles, finset.card_le_one, ←coe_inter]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be worth extracting as a lemma.


instance : decidable G.locally_linear := and.decidable

lemma edge_disjoint_triangles.card_edge_finset_le (hG : G.edge_disjoint_triangles) :
3 * (G.clique_finset 3).card ≤ G.edge_finset.card :=
begin
rw [mul_comm, ←mul_one G.edge_finset.card],
refine card_mul_le_card_mul (λ s e, e ∈ s.sym2) _ (λ e he, _),
{ simp only [is_3_clique_iff, mem_clique_finset_iff, mem_sym2_iff,
forall_exists_index, and_imp],
rintro _ a b c hab hac hbc rfl,
have : finset.card ({⟦(a, b)⟧, ⟦(a, c)⟧, ⟦(b, c)⟧} : finset $ sym2 α) = 3,
{ refine card_eq_three.2 ⟨_, _, _, _, _, _, rfl⟩; simp [hab.ne, hac.ne, hbc.ne] },
rw ←this,
refine card_mono _,
simp [insert_subset, *] },
{ simpa only [card_le_one, mem_bipartite_below, and_imp, set.subsingleton, set.mem_sep_iff,
mem_clique_finset_iff, mem_clique_set_iff]
using hG.mem_sym2_subsingleton (G.not_is_diag_of_mem_edge_set $ mem_edge_finset.1 he) }
end

lemma locally_linear.card_edge_finset (hG : G.locally_linear) :
G.edge_finset.card = 3 * (G.clique_finset 3).card :=
begin
refine hG.edge_disjoint_triangles.card_edge_finset_le.antisymm' _,
rw [←mul_comm, ←mul_one (finset.card _)],
refine card_mul_le_card_mul (λ e s, e ∈ s.sym2) _ _,
{ simpa [sym2.forall, nat.succ_le_iff, card_pos, finset.nonempty] using hG.2 },
simp only [mem_clique_finset_iff, is_3_clique_iff, forall_exists_index, and_imp],
rintro _ a b c hab hac hbc rfl,
refine (card_mono _).trans _,
{ exact {⟦(a, b)⟧, ⟦(a, c)⟧, ⟦(b, c)⟧} },
{ simp only [subset_iff, sym2.forall, mem_sym2_iff, le_eq_subset, mem_bipartite_below, mem_insert,
mem_edge_finset, mem_singleton, and_imp, mem_edge_set, sym2.mem_iff, forall_eq_or_imp,
forall_eq, quotient.eq, sym2.rel_iff],
rintro d e hde (rfl | rfl | rfl) (rfl | rfl | rfl); simp [*] at * },
{ exact (card_insert_le _ _).trans (succ_le_succ $ (card_insert_le _ _).trans_eq $
by rw card_singleton) }
end

end locally_linear

variables (G ε) [fintype α] [decidable_eq α] [decidable_rel G.adj] [decidable_rel H.adj]

/-- A simple graph is *`ε`-triangle-free far* if one must remove at least `ε * (card α)^2` edges to
make it triangle-free. -/
def far_from_triangle_free (G : simple_graph α) (ε : 𝕜) : Prop :=
G.delete_far (λ H, H.clique_free 3) $ ε * (card α^2 : ℕ)
def far_from_triangle_free : Prop := G.delete_far (λ H, H.clique_free 3) $ ε * (card α^2 : ℕ)

variables {G ε}

lemma far_from_triangle_free_iff :
G.far_from_triangle_free ε ↔
∀ ⦃H⦄, H ≤ G → H.clique_free 3 → ε * (card α^2 : ℕ) ≤ G.edge_finset.card - H.edge_finset.card :=
∀ ⦃H : simple_graph _⦄ [decidable_rel H.adj], by exactI
H ≤ G → H.clique_free 3 → ε * (card α^2 : ℕ) ≤ G.edge_finset.card - H.edge_finset.card :=
delete_far_iff

alias far_from_triangle_free_iff ↔ far_from_triangle_free.le_card_sub_card _
Expand All @@ -51,14 +218,87 @@ lemma far_from_triangle_free.mono (hε : G.far_from_triangle_free ε) (h : δ
G.far_from_triangle_free δ :=
hε.mono $ mul_le_mul_of_nonneg_right h $ cast_nonneg _

@[simp] lemma far_from_triangle_free_of_nonpos (hε : ε ≤ 0) : G.far_from_triangle_free ε :=
λ H _ _, (mul_nonpos_of_nonpos_of_nonneg hε $ nat.cast_nonneg _).trans $ nat.cast_nonneg _

lemma far_from_triangle_free.clique_finset_nonempty' (hH : H ≤ G) (hG : G.far_from_triangle_free ε)
(hcard : (G.edge_finset.card - H.edge_finset.card : 𝕜) < ε * (card α ^ 2 : ℕ)) :
(H.clique_finset 3).nonempty :=
nonempty_of_ne_empty $ H.clique_finset_eq_empty_iff.not.2 $ λ hH',
(hG.le_card_sub_card hH hH').not_lt hcard

private lemma far_from_triangle_free_of_disjoint_triangles_aux {tris : finset (finset α)}
(htris : tris ⊆ G.clique_finset 3)
(pd : (tris : set (finset α)).pairwise (λ x y, (x ∩ y : set α).subsingleton)) (hHG : H ≤ G)
(hH : H.clique_free 3) : tris.card ≤ G.edge_finset.card - H.edge_finset.card :=
begin
rw [←card_sdiff (edge_finset_mono hHG), ←card_attach],
by_contra' hG,
have : ∀ ⦃t⦄, t ∈ tris → ∃ x y, x ∈ t ∧ y ∈ t ∧ x ≠ y ∧ ⟦(x, y)⟧ ∈ G.edge_finset \ H.edge_finset,
{ intros t ht,
by_contra' h,
refine hH t _,
simp only [not_and, mem_sdiff, not_not, mem_edge_finset, mem_edge_set] at h,
obtain ⟨x, y, z, xy, xz, yz, rfl⟩ := is_3_clique_iff.1 (G.mem_clique_finset_iff.1 $ htris ht),
rw is_3_clique_triple_iff,
refine ⟨h _ _ _ _ xy.ne xy, h _ _ _ _ xz.ne xz, h _ _ _ _ yz.ne yz⟩; simp },
choose fx fy hfx hfy hfne fmem using this,
let f : {x // x ∈ tris} → sym2 α := λ t, ⟦(fx t.2, fy t.2)⟧,
have hf : ∀ x, x ∈ tris.attach → f x ∈ G.edge_finset \ H.edge_finset := λ x hx, fmem _,
obtain ⟨⟨t₁, ht₁⟩, -, ⟨t₂, ht₂⟩, -, tne, t : ⟦_⟧ = ⟦_⟧⟩ :=
exists_ne_map_eq_of_card_lt_of_maps_to hG hf,
dsimp at t,
have i := pd ht₁ ht₂ (subtype.val_injective.ne tne),
rw sym2.eq_iff at t,
cases t,
{ exact hfne _ (i ⟨hfx ht₁, t.1.symm ▸ hfx ht₂⟩ ⟨hfy ht₁, t.2.symm ▸ hfy ht₂⟩) },
{ exact hfne _ (i ⟨hfx ht₁, t.1.symm ▸ hfy ht₂⟩ ⟨hfy ht₁, t.2.symm ▸ hfx ht₂⟩) }
end

/-- If there are `ε * (card α)^2` disjoint triangles, then the graph is `ε`-far from being
triangle-free. -/
lemma far_from_triangle_free_of_disjoint_triangles (tris : finset (finset α))
(htris : tris ⊆ G.clique_finset 3)
(pd : (tris : set (finset α)).pairwise (λ x y, (x ∩ y : set α).subsingleton))
(tris_big : ε * (card α ^ 2 : ℕ) ≤ tris.card) :
G.far_from_triangle_free ε :=
begin
rw far_from_triangle_free_iff,
introsI H _ hG hH,
rw ←nat.cast_sub (card_le_of_subset $ edge_finset_mono hG),
exact tris_big.trans
(nat.cast_le.2 $ far_from_triangle_free_of_disjoint_triangles_aux htris pd hG hH),
end

protected lemma edge_disjoint_triangles.far_from_triangle_free (hG : G.edge_disjoint_triangles)
(tris_big : ε * (card α ^ 2 : ℕ) ≤ (G.clique_finset 3).card) : G.far_from_triangle_free ε :=
far_from_triangle_free_of_disjoint_triangles _ subset.rfl (by simpa using hG) tris_big

variables [nonempty α]

lemma far_from_triangle_free.lt_half (hG : G.far_from_triangle_free ε) : ε < 2⁻¹ :=
begin
by_contra' hε,
have := hG.le_card_sub_card bot_le (clique_free_bot $ by norm_num),
simp only [set.to_finset_card (edge_set ⊥), fintype.card_of_finset, edge_set_bot, cast_zero,
finset.card_empty, tsub_zero] at this,
have hε₀ : 0 < ε := hε.trans_lt' (by norm_num),
rw inv_pos_le_iff_one_le_mul (zero_lt_two' 𝕜) at hε,
refine (this.trans $ le_mul_of_one_le_left (by positivity) hε).not_lt _,
rw [mul_assoc, mul_lt_mul_left hε₀],
norm_cast,
refine (mul_le_mul_left' (card_mono $ edge_finset_mono le_top) _).trans_lt _,
rw [edge_finset_top, filter_not, card_sdiff (subset_univ _), card_univ, sym2.card],
simp_rw [sym2.is_diag_iff_mem_range_diag, univ_filter_mem_range, mul_tsub,
nat.mul_div_cancel' (card α).even_mul_succ_self.two_dvd],
rw [card_image_of_injective _ sym2.diag_injective, card_univ, mul_add_one, two_mul, sq,
add_tsub_add_eq_tsub_right],
exact tsub_lt_self (mul_pos fintype.card_pos fintype.card_pos) fintype.card_pos,
end

lemma far_from_triangle_free.lt_one (hG : G.far_from_triangle_free ε) : ε < 1 :=
hG.lt_half.trans $ by norm_num

lemma far_from_triangle_free.nonpos (h₀ : G.far_from_triangle_free ε) (h₁ : G.clique_free 3) :
ε ≤ 0 :=
begin
Expand Down
Loading
Loading