Skip to content

Commit 498eb74

Browse files
committed
refactor(Combinatorics/SimpleGraph/Copy): rename copy counts, introduce Sub subtype
Aligns the `Copy.lean` naming with the four-axis hom/copy/subgraph taxonomy discussed in Zulip (#general > "SimpleGraph.Copy is labeled but copyCount is not"): | notion | before | after | | ------------------------ | ------------------- | ------------ | | labelled copy (inj hom) | `Copy A B` | unchanged | | unlabelled subgraph type | inlined subtype | `Sub H G` | | labelled count | `labelledCopyCount` | `copyCount` | | unlabelled count | `copyCount` | `subCount` | Changes: * Rename `labelledCopyCount → copyCount` (with British deprecation alias `labelledCopyCount`); rename the corresponding `_of_isEmpty`, `_eq_zero`, `_pos` lemmas similarly. * Rename the previous `copyCount → subCount` (no alias — name is reassigned to the labelled count). Rename `_eq_zero`, `_pos`, `_bot`, `_of_isEmpty`, `_le_*` lemmas accordingly. * Add `abbrev Sub (H : SimpleGraph W) (G : SimpleGraph V) := {G' : G.Subgraph // Nonempty (H ≃g G'.coe)}` alongside `subCount`. The corresponding count name `subCount` matches Lovász's blackboard `sub(H, G)`. * Redefine `subCount` as `Fintype.card (H.Sub G)` (was the filter-set form `#{G' : G.Subgraph | Nonempty (H ≃g G'.coe)}`). Proofs of `subCount_eq_zero`, `subCount_pos`, `subCount_bot`, and `subCount_le_copyCount` updated to use type-form lemmas (`Fintype.card_eq_zero_iff`, `Fintype.card_unique`, `Fintype.card_le_of_surjective`). * Drop `copyCount_eq_card_image_copyToSubgraph` (legacy filter-form bridge, unused after the type-form refactor). * Add `instance uniqueSubBot : Unique ((⊥ : SimpleGraph V).Sub G)`, factoring the singleton-empty-subgraph fact out of `subCount_bot`. * Update `le_card_edgeFinset_killCopies` to use `H.Sub G` directly (was inline subtype + `card_subtype`/`subtype_univ`/`card_univ` rewrite chain). * Update module docstring: add `Sub`/`subCount` bullets; clarify that `Sub` references `SimpleGraph.Subgraph` (the labelled non-induced structure); reorder declarations in logical-flow order (`Copy → IsContained → Free → Sub → copyCount → subCount → killCopies`); add TODOs for `homCount` and the three densities (`copyDensity`, `subDensity`, `homDensity`).
1 parent b217bf7 commit 498eb74

1 file changed

Lines changed: 88 additions & 76 deletions

File tree

Mathlib/Combinatorics/SimpleGraph/Copy.lean

Lines changed: 88 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ Containment:
3535
same underlying vertex type.
3636
* `SimpleGraph.Free` is the predicate that `H` is `G`-free, that is, `H` does not contain a copy of
3737
`G`. This is the negation of `SimpleGraph.IsContained` implemented for convenience.
38+
* `SimpleGraph.Sub H G`: Type of `SimpleGraph.Subgraph`s of `G` isomorphic to `H`.
39+
* `SimpleGraph.copyCount G H`: Number of labeled copies of `H` in `G`, i.e. number of injective
40+
graph homomorphisms from `H` to `G`.
41+
* `SimpleGraph.subCount G H`: Number of `SimpleGraph.Subgraph`s of `G` isomorphic to `H`.
3842
* `SimpleGraph.killCopies G H`: Subgraph of `G` that does not contain `H`. Obtained by arbitrarily
3943
removing an edge from each copy of `H` in `G`.
40-
* `SimpleGraph.copyCount G H`: Number of copies of `H` in `G`, i.e. number of subgraphs of `G`
41-
isomorphic to `H`.
42-
* `SimpleGraph.labelledCopyCount G H`: Number of labelled copies of `H` in `G`, i.e. number of
43-
graph embeddings from `H` to `G`.
4444
4545
Induced containment:
4646
* Induced copies of `G` inside `H` are already defined as `G ↪g H`.
@@ -56,10 +56,12 @@ The following notation is declared in scope `SimpleGraph`:
5656
5757
* Relate `⊥ ⊴ H` to there being an independent set in `H`.
5858
* Count induced copies of a graph inside another.
59-
* Make `copyCount`/`labelledCopyCount` computable (not necessarily efficiently).
59+
* Make `copyCount`/`subCount` computable (not necessarily efficiently).
60+
* Add `homCount G H` for the number of graph homomorphisms `H →g G`.
61+
* Add `copyDensity G H`, `subDensity G H` (and `homDensity G H` once `homCount` exists).
6062
-/
6163

62-
public section
64+
@[expose] public section
6365

6466
open Finset Function
6567
open Fintype (card)
@@ -125,12 +127,10 @@ def mapNeighborSet (f : Copy A B) (a : α) :
125127
exact f.injective h
126128

127129
/-- A copy gives rise to an embedding of vertex types. -/
128-
@[expose] def toEmbedding (f : Copy A B) : α ↪ β := ⟨f, f.injective⟩
129-
130-
@[simp] lemma toEmbedding_apply (f : Copy A B) (a : α) : f.toEmbedding a = f a := rfl
130+
def toEmbedding (f : Copy A B) : α ↪ β := ⟨f, f.injective⟩
131131

132132
/-- The identity copy from a simple graph to itself. -/
133-
@[expose, refl] def id (G : SimpleGraph V) : Copy G G := ⟨Hom.id, Function.injective_id⟩
133+
@[refl] def id (G : SimpleGraph V) : Copy G G := ⟨Hom.id, Function.injective_id⟩
134134

135135
@[simp, norm_cast] lemma coe_id : ⇑(id G) = _root_.id := rfl
136136

@@ -145,8 +145,7 @@ theorem comp_apply (g : Copy B C) (f : Copy A B) (a : α) : g.comp f a = g (f a)
145145
RelHom.comp_apply g.toHom f.toHom a
146146

147147
/-- The copy from a subgraph to the supergraph. -/
148-
@[expose] def ofLE (G₁ G₂ : SimpleGraph V) (h : G₁ ≤ G₂) : Copy G₁ G₂ :=
149-
⟨Hom.ofLE h, Function.injective_id⟩
148+
def ofLE (G₁ G₂ : SimpleGraph V) (h : G₁ ≤ G₂) : Copy G₁ G₂ := ⟨Hom.ofLE h, Function.injective_id⟩
150149

151150
@[simp, norm_cast]
152151
theorem coe_comp (g : Copy B C) (f : Copy A B) : ⇑(g.comp f) = g ∘ f := by ext; simp
@@ -203,12 +202,10 @@ instance [Fintype {f : G →g H // Injective f}] : Fintype (G.Copy H) :=
203202
}
204203

205204
/-- A copy of `⊤` gives rise to an embedding of `⊤`. -/
206-
@[expose] def topEmbedding (f : Copy (⊤ : SimpleGraph α) G) : (⊤ : SimpleGraph α) ↪g G :=
205+
@[simps!]
206+
def topEmbedding (f : Copy (⊤ : SimpleGraph α) G) : (⊤ : SimpleGraph α) ↪g G :=
207207
{ f.toEmbedding with
208-
map_rel_iff' := fun {_ _} ↦ ⟨fun h ↦ f.injective.ne_iff.mp h.ne, f.toHom.map_adj⟩ }
209-
210-
@[simp] lemma topEmbedding_apply (f : Copy (⊤ : SimpleGraph α) G) (v : α) :
211-
f.topEmbedding v = f v := rfl
208+
map_rel_iff' := fun {v w} ↦ ⟨fun h ↦ by simpa using h.ne, f.toHom.map_adj⟩}
212209

213210
end Copy
214211

@@ -390,7 +387,7 @@ We denote "`G` is inducingly contained in `H`" by `G ⊴ H` (`\trianglelefteq`).
390387

391388
/-- A simple graph `G` is inducingly contained in a simple graph `H` if there exists an induced
392389
subgraph of `H` isomorphic to `G`. This is denoted by `G ⊴ H`. -/
393-
abbrev IsIndContained (G : SimpleGraph V) (H : SimpleGraph W) : Prop := Nonempty (G ↪g H)
390+
def IsIndContained (G : SimpleGraph V) (H : SimpleGraph W) : Prop := Nonempty (G ↪g H)
394391

395392
@[inherit_doc] scoped infixl:50 " ⊴ " => SimpleGraph.IsIndContained
396393

@@ -471,83 +468,98 @@ theorem isIndContained_iff_exists_comap_eq : H ⊴ G ↔ ∃ (f : W ↪ V), G.co
471468
/-!
472469
### Counting the copies
473470
474-
If `G` and `H` are finite graphs, we can count the number of unlabelled and labelled copies of `G`
471+
If `G` and `H` are finite graphs, we can count the number of unlabeled and labeled copies of `G`
475472
in `H`.
476473
477474
#### Not necessarily induced copies
478475
-/
479476

480-
section LabelledCopyCount
477+
section CopyCount
481478
variable [Fintype V] [Fintype W]
482479

483-
/-- `G.labelledCopyCount H` is the number of labelled copies of `H` in `G`, i.e. the number of graph
484-
embeddings from `H` to `G`. See `SimpleGraph.copyCount` for the number of unlabelled copies. -/
485-
noncomputable def labelledCopyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ := by
480+
/-- `G.copyCount H` is the number of labeled copies of `H` in `G`, i.e. the number of graph
481+
embeddings from `H` to `G`. See `SimpleGraph.subCount` for the number of unlabeled copies. -/
482+
noncomputable def copyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ := by
486483
classical exact Fintype.card (Copy H G)
487484

488-
@[simp] lemma labelledCopyCount_of_isEmpty [IsEmpty W] (G : SimpleGraph V) (H : SimpleGraph W) :
489-
G.labelledCopyCount H = 1 := by
485+
@[deprecated (since := "2026-04-30")] alias labelledCopyCount := copyCount
486+
487+
@[simp] lemma copyCount_of_isEmpty [IsEmpty W] (G : SimpleGraph V) (H : SimpleGraph W) :
488+
G.copyCount H = 1 := by
490489
convert Fintype.card_unique
491490
exact { default := ⟨default, isEmptyElim⟩, uniq := fun _ ↦ Subsingleton.elim _ _ }
492491

493-
@[simp] lemma labelledCopyCount_eq_zero : G.labelledCopyCount H = 0 ↔ H.Free G := by
494-
simp [labelledCopyCount, Fintype.card_eq_zero_iff]
492+
@[deprecated (since := "2026-04-30")]
493+
alias labelledCopyCount_of_isEmpty := copyCount_of_isEmpty
495494

496-
@[simp] lemma labelledCopyCount_pos : 0 < G.labelledCopyCount H ↔ H G := by
497-
simp [labelledCopyCount, IsContained, Fintype.card_pos_iff]
495+
@[simp] lemma copyCount_eq_zero : G.copyCount H = 0 ↔ H.Free G := by
496+
simp [copyCount, Fintype.card_eq_zero_iff]
498497

499-
end LabelledCopyCount
498+
@[deprecated (since := "2026-04-30")] alias labelledCopyCount_eq_zero := copyCount_eq_zero
500499

501-
section CopyCount
502-
variable [Fintype V]
500+
@[simp] lemma copyCount_pos : 0 < G.copyCount H ↔ H ⊑ G := by
501+
simp [copyCount, IsContained, Fintype.card_pos_iff]
503502

504-
/-- `G.copyCount H` is the number of unlabelled copies of `H` in `G`, i.e. the number of subgraphs
505-
of `G` isomorphic to `H`. See `SimpleGraph.labelledCopyCount` for the number of labelled copies. -/
506-
noncomputable def copyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ := by
507-
classical exact #{G' : G.Subgraph | Nonempty (H ≃g G'.coe)}
503+
@[deprecated (since := "2026-04-30")] alias labelledCopyCount_pos := copyCount_pos
508504

509-
lemma copyCount_eq_card_image_copyToSubgraph [Fintype {f : H →g G // Injective f}]
510-
[DecidableEq G.Subgraph] :
511-
copyCount G H = #((Finset.univ : Finset (H.Copy G)).image Copy.toSubgraph) := by
512-
rw [copyCount]
513-
congr
514-
refine Finset.coe_injective ?_
515-
simpa [-Copy.range_toSubgraph] using Copy.range_toSubgraph.symm
505+
end CopyCount
516506

517-
@[simp] lemma copyCount_eq_zero : G.copyCount H = 0 ↔ H.Free G := by
518-
simp [copyCount, Free, -nonempty_subtype, isContained_iff_exists_iso_subgraph,
519-
filter_eq_empty_iff]
507+
section SubCount
508+
variable [Fintype V]
520509

521-
@[simp] lemma copyCount_pos : 0 < G.copyCount H ↔ H ⊑ G := by
522-
simp [copyCount, -nonempty_subtype, isContained_iff_exists_iso_subgraph, card_pos,
523-
filter_nonempty_iff]
510+
/-- `H.Sub G` is the type of `SimpleGraph.Subgraph`s of `G` isomorphic to `H`. The corresponding
511+
count is `SimpleGraph.subCount`. -/
512+
abbrev Sub (H : SimpleGraph W) (G : SimpleGraph V) : Type _ :=
513+
{G' : G.Subgraph // Nonempty (H ≃g G'.coe)}
514+
515+
/-- `G.subCount H` is the number of `SimpleGraph.Subgraph`s of `G` isomorphic to `H`. See
516+
`SimpleGraph.copyCount` for the number of labeled copies. -/
517+
noncomputable def subCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ := by
518+
classical exact Fintype.card (H.Sub G)
519+
520+
@[simp] lemma subCount_eq_zero : G.subCount H = 0 ↔ H.Free G := by
521+
simp [subCount, Free, -nonempty_subtype, isContained_iff_exists_iso_subgraph,
522+
Fintype.card_eq_zero_iff, isEmpty_subtype, not_nonempty_iff]
524523

525-
/-- There's at least as many labelled copies of `H` in `G` than unlabelled ones. -/
526-
lemma copyCount_le_labelledCopyCount [Fintype W] : G.copyCount H ≤ G.labelledCopyCount H := by
527-
classical rw [copyCount_eq_card_image_copyToSubgraph]; exact card_image_le
524+
@[simp] lemma subCount_pos : 0 < G.subCount H ↔ H ⊑ G := by
525+
rw [Nat.pos_iff_ne_zero, ne_eq, subCount_eq_zero, Free, not_not]
528526

529-
@[simp] lemma copyCount_bot (G : SimpleGraph V) : copyCount G (⊥ : SimpleGraph V) = 1 := by
527+
/-- There's at least as many labeled copies of `H` in `G` than unlabeled ones. -/
528+
lemma subCount_le_copyCount [Fintype W] : G.subCount H ≤ G.copyCount H := by
530529
classical
531-
rw [copyCount]
532-
convert card_singleton (α := G.Subgraph)
533-
{ verts := .univ
534-
Adj := ⊥
535-
adj_sub := False.elim
536-
edge_vert := False.elim }
537-
simp only [eq_singleton_iff_unique_mem, mem_filter_univ, Nonempty.forall]
538-
refine ⟨⟨⟨(Equiv.Set.univ _).symm, by simp⟩⟩, fun H' e ↦
539-
Subgraph.ext ((set_fintype_card_eq_univ_iff _).1 <| Fintype.card_congr e.toEquiv.symm) ?_⟩
540-
ext a b
541-
simp only [Prop.bot_eq_false, Pi.bot_apply, iff_false]
542-
exact fun hab ↦ e.symm.map_rel_iff.2 hab.coe
530+
rw [subCount, copyCount]
531+
apply Fintype.card_le_of_surjective
532+
(fun c : Copy H G ↦ (⟨c.toSubgraph, ⟨c.isoToSubgraph⟩⟩ : H.Sub G))
533+
rintro ⟨G', hG'⟩
534+
obtain ⟨c, hc⟩ : ∃ c, Copy.toSubgraph c = G' := by
535+
rwa [← Set.mem_range, Copy.range_toSubgraph]
536+
exact ⟨c, Subtype.ext hc⟩
537+
538+
@[deprecated (since := "2026-04-30")]
539+
alias subCount_le_labelledCopyCount := subCount_le_copyCount
540+
541+
instance uniqueSubBot (G : SimpleGraph V) : Unique ((⊥ : SimpleGraph V).Sub G) where
542+
default := ⟨{ verts := .univ, Adj := ⊥, adj_sub := False.elim, edge_vert := False.elim },
543+
⟨(Equiv.Set.univ _).symm, by simp⟩⟩
544+
uniq := fun ⟨H', ⟨e⟩⟩ ↦ Subtype.ext <| Subgraph.ext
545+
(by classical exact (set_fintype_card_eq_univ_iff _).1 <| Fintype.card_congr e.toEquiv.symm)
546+
(by ext a b
547+
simp only [Prop.bot_eq_false, Pi.bot_apply, iff_false]
548+
exact fun hab ↦ e.symm.map_rel_iff.2 hab.coe)
549+
550+
@[simp] lemma subCount_bot (G : SimpleGraph V) : subCount G (⊥ : SimpleGraph V) = 1 := by
551+
classical
552+
rw [subCount]
553+
convert Fintype.card_unique
554+
exact uniqueSubBot G
543555

544-
@[simp] lemma copyCount_of_isEmpty [IsEmpty W] (G : SimpleGraph V) (H : SimpleGraph W) :
545-
G.copyCount H = 1 := by
556+
@[simp] lemma subCount_of_isEmpty [IsEmpty W] (G : SimpleGraph V) (H : SimpleGraph W) :
557+
G.subCount H = 1 := by
546558
cases nonempty_fintype W
547-
exact (copyCount_le_labelledCopyCount.trans_eq <| labelledCopyCount_of_isEmpty ..).antisymm <|
548-
copyCount_pos.2 <| .of_isEmpty
559+
exact (subCount_le_copyCount.trans_eq <| copyCount_of_isEmpty ..).antisymm <|
560+
subCount_pos.2 <| .of_isEmpty
549561

550-
end CopyCount
562+
end SubCount
551563

552564
/-!
553565
#### Induced copies
@@ -644,28 +656,28 @@ noncomputable instance killCopies.edgeSet.instFintype : Fintype (G.killCopies H)
644656
/-- Removing an edge from `H` for each subgraph isomorphic to `G` means that the number of edges
645657
we've removed is at most the number of copies of `G` in `H`. -/
646658
lemma le_card_edgeFinset_killCopies [Fintype V] :
647-
#G.edgeFinset - G.copyCount H ≤ #(G.killCopies H).edgeFinset := by
659+
#G.edgeFinset - G.subCount H ≤ #(G.killCopies H).edgeFinset := by
648660
classical
649661
obtain rfl | hH := eq_or_ne H ⊥
650662
· simp [← card_edgeSet]
651-
let f (G' : {G' : G.Subgraph // Nonempty (H ≃g G'.coe)}) := (aux hH G'.2).some
663+
let f (G' : H.Sub G) := (aux hH G'.2).some
652664
calc
653-
_ = #G.edgeFinset - card {G' : G.Subgraph // Nonempty (H ≃g G'.coe)} := ?_
665+
_ = #G.edgeFinset - Fintype.card (H.Sub G) := ?_
654666
_ ≤ #G.edgeFinset - #(univ.image f) := Nat.sub_le_sub_left card_image_le _
655667
_ = #G.edgeFinset - #(Set.range f).toFinset := by rw [Set.toFinset_range]
656668
_ ≤ #(G.edgeFinset \ (Set.range f).toFinset) := le_card_sdiff ..
657669
_ = #(G.killCopies H).edgeFinset := ?_
658670
· simp only [edgeFinset, Set.toFinset_card]
659-
rw [← Set.toFinset_card, ← edgeFinset, copyCount, ← card_subtype, subtype_univ, card_univ]
671+
rw [← Set.toFinset_card, ← edgeFinset, subCount]
660672
congr 1
661673
ext e
662674
induction e using Sym2.inductionOn with | hf v w
663675
simp [mem_edgeSet, killCopies_of_ne_bot hH, f, eq_comm]
664676

665677
/-- Removing an edge from `H` for each subgraph isomorphic to `G` means that the number of edges
666678
we've removed is at most the number of copies of `G` in `H`. -/
667-
lemma le_card_edgeFinset_killCopies_add_copyCount [Fintype V] :
668-
#G.edgeFinset ≤ #(G.killCopies H).edgeFinset + G.copyCount H :=
679+
lemma le_card_edgeFinset_killCopies_add_subCount [Fintype V] :
680+
#G.edgeFinset ≤ #(G.killCopies H).edgeFinset + G.subCount H :=
669681
tsub_le_iff_right.1 le_card_edgeFinset_killCopies
670682

671683
/-!

0 commit comments

Comments
 (0)