@@ -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
6466open Finset Function
6567open 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]
152151theorem 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
213210end 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
392389subgraph 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`
475472in `H`.
476473
477474#### Not necessarily induced copies
478475-/
479476
480- section LabelledCopyCount
477+ section CopyCount
481478variable [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
645657we've removed is at most the number of copies of `G` in `H`. -/
646658lemma 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
666678we'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