Skip to content

Commit 49d8074

Browse files
committed
chore(Combinatorics/SimpleGraph/Copy): migrate copyCount/labeledCopyCount to Nat.card
- `copyCount` and `labeledCopyCount` redefined via `Nat.card` instead of `Fintype.card` - `Fintype` hypotheses weakened to `Finite` throughout (`copyCount_eq_zero`, `copyCount_pos`, `labeledCopyCount_eq_zero`, `labeledCopyCount_pos`, `copyCount_le_labeledCopyCount`) - `bot_isContained_iff_card_le`: generalized `[Fintype] → [Finite]`, `Fintype.card → Nat.card` - `le_card_edgeFinset_killCopies`: proof avoids `Fintype.ofFinite` round-trip via `Set.finite_range` + `Set.Finite.toFinset` - `copyCount_eq_card_image_copyToSubgraph`: deprecated in favour of `copyCount_eq_nat_card_range_toSubgraph`; proof via `Nat.subtype_card` - Various proof tightening and private lemma extraction
1 parent 6fc9d09 commit 49d8074

1 file changed

Lines changed: 95 additions & 63 deletions

File tree

Mathlib/Combinatorics/SimpleGraph/Copy.lean

Lines changed: 95 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ module
77

88
public import Mathlib.Algebra.Order.Group.Nat
99
public import Mathlib.Combinatorics.SimpleGraph.Subgraph
10+
public import Mathlib.Data.Finite.Card
11+
public import Mathlib.Data.Set.Finite.Range
1012

1113
/-!
1214
# Containment of graphs
@@ -62,7 +64,6 @@ The following notation is declared in scope `SimpleGraph`:
6264
public section
6365

6466
open Finset Function
65-
open Fintype (card)
6667

6768
namespace SimpleGraph
6869
variable {V W X α β γ : Type*} {G G₁ G₂ G₃ : SimpleGraph V} {H : SimpleGraph W} {I : SimpleGraph X}
@@ -202,6 +203,9 @@ instance [Fintype {f : G →g H // Injective f}] : Fintype (G.Copy H) :=
202203
invFun f := ⟨f.1, f.2
203204
}
204205

206+
instance [Finite V] [Finite W] : Finite (G.Copy H) :=
207+
Finite.of_injective _ DFunLike.coe_injective
208+
205209
/-- A copy of `⊤` gives rise to an embedding of `⊤`. -/
206210
@[expose] def topEmbedding (f : Copy (⊤ : SimpleGraph α) G) : (⊤ : SimpleGraph α) ↪g G :=
207211
{ f.toEmbedding with
@@ -297,10 +301,11 @@ lemma IsContained.of_isEmpty [IsEmpty α] : A ⊑ B :=
297301
⟨⟨isEmptyElim, fun {a} ↦ isEmptyElim a⟩, isEmptyElim⟩
298302

299303
/-- `⊥` is contained in any simple graph having sufficiently many vertices. -/
300-
lemma bot_isContained_iff_card_le [Fintype α] [Fintype β] :
301-
(⊥ : SimpleGraph α) ⊑ B ↔ Fintype.card α ≤ Fintype.card β :=
302-
fun ⟨f⟩ ↦ Fintype.card_le_of_embedding f.toEmbedding,
303-
fun h ↦ ⟨Copy.bot (Function.Embedding.nonempty_of_card_le h).some⟩⟩
304+
lemma bot_isContained_iff_card_le [Finite α] [Finite β] :
305+
(⊥ : SimpleGraph α) ⊑ B ↔ Nat.card α ≤ Nat.card β :=
306+
fun ⟨f⟩ ↦ Finite.card_le_of_embedding f.toEmbedding,
307+
fun h ↦ ⟨Copy.bot (Cardinal.lift_mk_le'.mp (by
308+
simp only [← Nat.cast_card, Cardinal.lift_natCast]; exact_mod_cast h)).some⟩⟩
304309

305310
protected alias IsContained.bot := bot_isContained_iff_card_le
306311

@@ -478,86 +483,113 @@ in `H`.
478483
-/
479484

480485
section LabeledCopyCount
481-
variable [Fintype V] [Fintype W]
482486

483487
/-- `G.labeledCopyCount H` is the number of labeled copies of `H` in `G`, i.e. the number of graph
484488
embeddings from `H` to `G`. See `SimpleGraph.copyCount` for the number of unlabeled copies. -/
485-
noncomputable def labeledCopyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ := by
486-
classical exact Fintype.card (Copy H G)
489+
noncomputable def labeledCopyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ :=
490+
Nat.card (Copy H G)
487491

488492
@[deprecated (since := "2026-04-30")] alias labelledCopyCount := labeledCopyCount
489493

494+
private instance [IsEmpty W] : Nonempty (H.Copy G) := IsContained.of_isEmpty
495+
490496
@[simp] lemma labeledCopyCount_of_isEmpty [IsEmpty W] (G : SimpleGraph V) (H : SimpleGraph W) :
491-
G.labeledCopyCount H = 1 := by
492-
convert Fintype.card_unique
493-
exact { default := ⟨default, isEmptyElim⟩, uniq := fun _ ↦ Subsingleton.elim _ _ }
497+
G.labeledCopyCount H = 1 := Nat.card_unique
494498

495499
@[deprecated (since := "2026-04-30")]
496500
alias labelledCopyCount_of_isEmpty := labeledCopyCount_of_isEmpty
497501

498-
@[simp] lemma labeledCopyCount_eq_zero : G.labeledCopyCount H = 0 ↔ H.Free G := by
499-
simp [labeledCopyCount, Fintype.card_eq_zero_iff]
502+
@[simp] lemma labeledCopyCount_eq_zero [Finite V] [Finite W] :
503+
G.labeledCopyCount H = 0 ↔ H.Free G := by
504+
rw [labeledCopyCount, Nat.card_eq_zero, or_iff_left (Finite.not_infinite inferInstance)]
505+
simp [Free, IsContained]
500506

501507
@[deprecated (since := "2026-04-30")] alias labelledCopyCount_eq_zero := labeledCopyCount_eq_zero
502508

503-
@[simp] lemma labeledCopyCount_pos : 0 < G.labeledCopyCount H ↔ H ⊑ G := by
504-
simp [labeledCopyCount, IsContained, Fintype.card_pos_iff]
509+
@[simp] lemma labeledCopyCount_pos [Finite V] [Finite W] : 0 < G.labeledCopyCount H ↔ H ⊑ G := by
510+
simp [Nat.pos_iff_ne_zero, labeledCopyCount_eq_zero]
505511

506512
@[deprecated (since := "2026-04-30")] alias labelledCopyCount_pos := labeledCopyCount_pos
507513

508514
end LabeledCopyCount
509515

510516
section CopyCount
511-
variable [Fintype V]
512517

513518
/-- `G.copyCount H` is the number of unlabeled copies of `H` in `G`, i.e. the number of subgraphs
514519
of `G` isomorphic to `H`. See `SimpleGraph.labeledCopyCount` for the number of labeled copies. -/
515-
noncomputable def copyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ := by
516-
classical exact #{G' : G.Subgraph | Nonempty (H ≃g G'.coe)}
520+
noncomputable def copyCount (G : SimpleGraph V) (H : SimpleGraph W) : ℕ :=
521+
Nat.card {G' : G.Subgraph // Nonempty (H ≃g G'.coe)}
522+
523+
lemma copyCount_eq_nat_card_range_toSubgraph :
524+
copyCount G H = Nat.card (Set.range (Copy.toSubgraph : H.Copy G → G.Subgraph)) := by
525+
rw [copyCount, Copy.range_toSubgraph]; rfl
517526

518-
lemma copyCount_eq_card_image_copyToSubgraph [Fintype {f : H →g G // Injective f}]
519-
[DecidableEq G.Subgraph] :
520-
copyCount G H = #((Finset.univ : Finset (H.Copy G)).image Copy.toSubgraph) := by
527+
@[deprecated copyCount_eq_nat_card_range_toSubgraph (since := "2026-05-04")]
528+
lemma copyCount_eq_card_image_copyToSubgraph [Fintype (H.Copy G)] [DecidableEq G.Subgraph] :
529+
copyCount G H = (Finset.univ.image (Copy.toSubgraph (A := H) (B := G))).card := by
521530
rw [copyCount]
522-
congr
523-
refine Finset.coe_injective ?_
524-
simpa [-Copy.range_toSubgraph] using Copy.range_toSubgraph.symm
531+
exact Nat.subtype_card _ fun G' => by
532+
simp only [Finset.mem_image, Finset.mem_univ, true_and]
533+
constructor
534+
· rintro ⟨f, rfl⟩; exact ⟨f.isoToSubgraph⟩
535+
· rintro ⟨e⟩
536+
exact ⟨⟨G'.hom.comp e.toHom, Subgraph.hom_injective.comp e.injective⟩,
537+
by simp [Copy.toSubgraph, Subgraph.map_comp]⟩
525538

526-
@[simp] lemma copyCount_eq_zero : G.copyCount H = 0 ↔ H.Free G := by
527-
simp [copyCount, Free, -nonempty_subtype, isContained_iff_exists_iso_subgraph,
528-
filter_eq_empty_iff]
539+
@[simp] lemma copyCount_eq_zero [Finite V] : G.copyCount H = 0 ↔ H.Free G := by
540+
rw [copyCount, Nat.card_eq_zero, or_iff_left (Finite.not_infinite inferInstance), isEmpty_subtype]
541+
simp [Free, isContained_iff_exists_iso_subgraph]
529542

530-
@[simp] lemma copyCount_pos : 0 < G.copyCount H ↔ H ⊑ G := by
531-
simp [copyCount, -nonempty_subtype, isContained_iff_exists_iso_subgraph, card_pos,
532-
filter_nonempty_iff]
543+
@[simp] lemma copyCount_pos [Finite V] : 0 < G.copyCount H ↔ H ⊑ G := by
544+
simp [Nat.pos_iff_ne_zero, copyCount_eq_zero]
533545

534546
/-- There's at least as many labeled copies of `H` in `G` than unlabeled ones. -/
535-
lemma copyCount_le_labeledCopyCount [Fintype W] : G.copyCount H ≤ G.labeledCopyCount H := by
536-
classical rw [copyCount_eq_card_image_copyToSubgraph]; exact card_image_le
547+
lemma copyCount_le_labeledCopyCount [Finite V] [Finite W] :
548+
G.copyCount H ≤ G.labeledCopyCount H :=
549+
copyCount_eq_nat_card_range_toSubgraph ▸ Finite.card_range_le _
537550

538551
@[deprecated (since := "2026-04-30")]
539552
alias copyCount_le_labelledCopyCount := copyCount_le_labeledCopyCount
540553

541-
@[simp] lemma copyCount_bot (G : SimpleGraph V) : copyCount G (⊥ : SimpleGraph V) = 1 := by
554+
private lemma subgraph_iso_bot [Finite V] (Gx : G.Subgraph) (e : (⊥ : SimpleGraph V) ≃g Gx.coe) :
555+
Gx.verts = Set.univ ∧ Gx.Adj = ⊥ := by
556+
constructor
557+
· apply Set.eq_univ_of_forall
558+
intro v
559+
obtain ⟨w, hw⟩ := (Finite.injective_iff_surjective.mp
560+
(Subtype.val_injective.comp e.toEquiv.injective)) v
561+
exact hw ▸ (e.toEquiv w).prop
562+
· exact funext₂ fun a b => eq_false fun hadj =>
563+
absurd (e.symm.map_rel_iff.mpr hadj.coe) (by simp)
564+
565+
private instance [Finite V] :
566+
Nonempty {G' : G.Subgraph // Nonempty ((⊥ : SimpleGraph V) ≃g G'.coe)} :=
567+
⟨{ verts := .univ, Adj := ⊥, adj_sub := False.elim, edge_vert := False.elim },
568+
⟨⟨(Equiv.Set.univ V).symm, by simp⟩⟩⟩
569+
570+
private instance [Finite V] :
571+
Subsingleton {G' : G.Subgraph // Nonempty ((⊥ : SimpleGraph V) ≃g G'.coe)} := by
542572
classical
543-
rw [copyCount]
544-
convert card_singleton (α := G.Subgraph)
545-
{ verts := .univ
546-
Adj := ⊥
547-
adj_sub := False.elim
548-
edge_vert := False.elim }
549-
simp only [eq_singleton_iff_unique_mem, mem_filter_univ, Nonempty.forall]
550-
refine ⟨⟨⟨(Equiv.Set.univ _).symm, by simp⟩⟩, fun H' e ↦
551-
Subgraph.ext ((set_fintype_card_eq_univ_iff _).1 <| Fintype.card_congr e.toEquiv.symm) ?_⟩
552-
ext a b
553-
simp only [Prop.bot_eq_false, Pi.bot_apply, iff_false]
554-
exact fun hab ↦ e.symm.map_rel_iff.2 hab.coe
573+
constructor
574+
rintro ⟨G', ⟨e⟩⟩ ⟨G'', ⟨e'⟩⟩
575+
congr 1
576+
exact Subgraph.ext ((subgraph_iso_bot G' e).1.trans (subgraph_iso_bot G'' e').1.symm)
577+
((subgraph_iso_bot G' e).2.trans (subgraph_iso_bot G'' e').2.symm)
578+
579+
@[simp] lemma copyCount_bot [Finite V] (G : SimpleGraph V) :
580+
copyCount G (⊥ : SimpleGraph V) = 1 := by
581+
rw [copyCount]; exact Nat.card_unique
582+
583+
private instance [IsEmpty W] : Nonempty {G' : G.Subgraph // Nonempty (H ≃g G'.coe)} :=
584+
nonempty_subtype.mpr (isContained_iff_exists_iso_subgraph.mp IsContained.of_isEmpty)
585+
586+
private instance [IsEmpty W] : Subsingleton {G' : G.Subgraph // Nonempty (H ≃g G'.coe)} :=
587+
fun ⟨G', ⟨e⟩⟩ ⟨G'', ⟨e'⟩⟩ => Subtype.ext <|
588+
(G'.eq_bot_iff_verts_eq_empty.mpr (Set.isEmpty_coe_sort.mp e.toEquiv.symm.isEmpty)).trans
589+
(G''.eq_bot_iff_verts_eq_empty.mpr (Set.isEmpty_coe_sort.mp e'.toEquiv.symm.isEmpty)).symm⟩
555590

556591
@[simp] lemma copyCount_of_isEmpty [IsEmpty W] (G : SimpleGraph V) (H : SimpleGraph W) :
557-
G.copyCount H = 1 := by
558-
cases nonempty_fintype W
559-
exact (copyCount_le_labeledCopyCount.trans_eq <| labeledCopyCount_of_isEmpty ..).antisymm <|
560-
copyCount_pos.2 <| .of_isEmpty
592+
G.copyCount H = 1 := by rw [copyCount]; exact Nat.card_unique
561593

562594
end CopyCount
563595

@@ -655,28 +687,28 @@ noncomputable instance killCopies.edgeSet.instFintype : Fintype (G.killCopies H)
655687

656688
/-- Removing an edge from `H` for each subgraph isomorphic to `G` means that the number of edges
657689
we've removed is at most the number of copies of `G` in `H`. -/
658-
lemma le_card_edgeFinset_killCopies [Fintype V] :
690+
lemma le_card_edgeFinset_killCopies [Finite V] :
659691
#G.edgeFinset - G.copyCount H ≤ #(G.killCopies H).edgeFinset := by
660692
classical
661693
obtain rfl | hH := eq_or_ne H ⊥
662694
· simp [← card_edgeSet]
663695
let f (G' : {G' : G.Subgraph // Nonempty (H ≃g G'.coe)}) := (aux hH G'.2).some
664-
calc
665-
_ = #G.edgeFinset - card {G' : G.Subgraph // Nonempty (H ≃g G'.coe)} := ?_
666-
_ ≤ #G.edgeFinset - #(univ.image f) := Nat.sub_le_sub_left card_image_le _
667-
_ = #G.edgeFinset - #(Set.range f).toFinset := by rw [Set.toFinset_range]
668-
_ ≤ #(G.edgeFinset \ (Set.range f).toFinset) := le_card_sdiff ..
669-
_ = #(G.killCopies H).edgeFinset := ?_
670-
· simp only [edgeFinset, Set.toFinset_card]
671-
rw [← Set.toFinset_card, ← edgeFinset, copyCount, ← card_subtype, subtype_univ, card_univ]
672-
congr 1
673-
ext e
674-
induction e using Sym2.inductionOn with | hf v w
675-
simp [mem_edgeSet, killCopies_of_ne_bot hH, f, eq_comm]
696+
have hrf : (Set.range f).Finite := Set.finite_range f
697+
have hle : hrf.toFinset.card ≤ G.copyCount H := by
698+
rw [← Nat.card_eq_card_finite_toFinset hrf, copyCount]
699+
exact Finite.card_range_le f
700+
calc #G.edgeFinset - G.copyCount H
701+
≤ #G.edgeFinset - hrf.toFinset.card := Nat.sub_le_sub_left hle _
702+
_ ≤ #(G.edgeFinset \ hrf.toFinset) := le_card_sdiff ..
703+
_ = #(G.killCopies H).edgeFinset := by
704+
congr 1
705+
ext e
706+
induction e using Sym2.inductionOn with | hf v w
707+
simp [mem_edgeSet, killCopies_of_ne_bot hH, f, eq_comm]
676708

677709
/-- Removing an edge from `H` for each subgraph isomorphic to `G` means that the number of edges
678710
we've removed is at most the number of copies of `G` in `H`. -/
679-
lemma le_card_edgeFinset_killCopies_add_copyCount [Fintype V] :
711+
lemma le_card_edgeFinset_killCopies_add_copyCount [Finite V] :
680712
#G.edgeFinset ≤ #(G.killCopies H).edgeFinset + G.copyCount H :=
681713
tsub_le_iff_right.1 le_card_edgeFinset_killCopies
682714

0 commit comments

Comments
 (0)