feat(GroupTheory/PCore): define the p-core of a subgroup#39771
Conversation
PR summary b1fc61ea28Import changes for modified filesNo significant changes to the import graph Import changes for all files
|
This PR adds `Subgroup.pCore p G`, the largest normal `p`-subgroup of `G`, classically denoted `O_p(G)`. Defined as the supremum of all normal `p`-subgroups; proved to be itself a normal `p`-group, without any finiteness hypothesis on `G`. The proof that `pCore p G` is a `p`-group uses the fact that the family of normal `p`-subgroups is directed under `≤` (the join of two normal `p`-subgroups is again a normal `p`-subgroup, by the existing `Subgroup.sup_normal` and `IsPGroup.to_sup_of_normal_left`), so `mem_iSup_of_directed` gives: every element of the supremum lies in one of the summands, and therefore has `p`-power order. Used downstream in `leanprover/lean-eval` to state the Baer-Suzuki theorem (leanprover/lean-eval#311). Follow-up work (filed in module TODO): * `pCore p G = ⨅ P : Sylow p G, (P : Subgroup G)` for finite `G` and prime `p`. * Behaviour of `pCore` under group homomorphisms. * Interaction with `IsSolvable`.
For a finite group `G` and a prime `p`, the `p`-core equals the intersection of all Sylow `p`-subgroups: `pCore p G = ⨅ P : Sylow p G, (P : Subgroup G)` New lemmas: * `Subgroup.pCore_le_sylow` — `pCore p G` is contained in every Sylow `p`-subgroup (uses that `pCore` is a normal `p`-subgroup so equals its conjugate, plus Sylow conjugacy). * `Subgroup.normal_iInf_sylow` — the intersection of all Sylow `p`-subgroups is normal (conjugation permutes Sylows). * `Subgroup.isPGroup_iInf_sylow` — the intersection is a `p`-group (contained in any one Sylow). * `Subgroup.pCore_eq_iInf_sylow` — the main result. Changes the import from `PGroup` to `Sylow` to access Sylow theory. Proof contributed via Codex pair-programming.
Cleanup pass: * Extract `Nonempty` subtype instance to top-level (was duplicated as `haveI` in `isPGroup_pCore` and `mem_pCore_iff`). * Replace `@`-instance threading in `directed_normal_isPGroup` with `haveI`-based instance synthesis. * Collapse the `1` case in `pCore_normal` to `by simp`. * Simplify the final `Subtype.ext` in `isPGroup_pCore` to `ext + simpa`. * Unify the two branches in `normal_iInf_sylow` to a single direction (one-sided argument suffices via `mem_pointwise_smul_iff_inv_smul_mem`). * Drop unnecessary `classical` calls and `MulAut.conj_apply` hint. * Replace `(isPGroup_pCore (G := G) (p := p))` with a clean `have hpg : IsPGroup p (pCore p G) := isPGroup_pCore` step. * Switch `normal_iInf_sylow` from `refine ⟨...⟩` to `where conj_mem` to match the style used by `pCore_normal`.
Codex review pass: * Add `Subgroup.normal_iSup_normal` to `Pointwise.lean` (sibling of the existing `normal_iInf_normal` in `Basic.lean`; needed `iSup_induction` which lives in `Pointwise.lean`). Generalizes the supremum-of-normals-is-normal proof previously inlined in `pCore_normal`. * Simplify `pCore_normal` to a one-liner using the new general lemma. * Add `pCore_eq_bot_iff` (the `p`-core is trivial iff `G` has no non-trivial normal `p`-subgroup). * Add `pCore_eq_top` (if `G` is itself a `p`-group, the `p`-core is the whole group). * Rename module-doc "Notation" section to "Terminology" (the section describes the textbook name `O_p(G)`, but introduces no notation). Kept `directed_normal_isPGroup` public (per user preference): it is genuinely useful API for anyone working with `pCore`, not just a private implementation lemma. The `to_additive` translation of `normal_iSup_normal` is left for a follow-up; the multiplicative proof routes through `MulAut.conj`, which translates but its `map_mul` step needs a small workaround in the additive setting.
Mirrors the API for `Subgroup.normalCore`: * `normal_le_pCore` — for `N` normal in `G`, `N ≤ pCore p G ↔ IsPGroup p N`. Previously only the (←) direction was available (`le_pCore`). * `pCore_eq_top_iff` — `pCore p G = ⊤ ↔ IsPGroup p (⊤ : Subgroup G)`. Previously only the (←) direction (`pCore_eq_top`); the (→) direction follows from `isPGroup_pCore.to_le`.
For a homomorphism `f : G →* H`: * `map_pCore_le_pCore (hf : Surjective f)`: `(pCore p G).map f ≤ pCore p H` — the image of the `p`-core under a surjective hom is a normal `p`-subgroup of `H`, hence in its `p`-core. * `pCore_le_comap_pCore (hf : Surjective f)`: the same fact restated via the map-comap adjunction. * `comap_pCore_le_pCore (hker : IsPGroup p f.ker)`: when the kernel is a `p`-group, the preimage of `pCore p H` is a normal `p`-subgroup of `G`, hence in `pCore p G`. * `comap_pCore_eq_pCore (hf : Surjective f) (hker : IsPGroup p f.ker)`: both conditions together give equality. * `MulEquiv.map_pCore`: a group isomorphism preserves the `p`-core (immediate from the equality above with `e.symm`). These cover the classical hom-behaviour story for `O_p(G)`. Removes the "Behaviour of `pCore` under group homomorphisms" item from the module TODO.
…pCore Shorter, more idiomatic name tied to the pCore definition.
The original proof used `MulAut.conj g` and `map_mul`. The `to_additive` translation of `MulAut.conj` is `AddAut.conj`, whose codomain is `Additive (AddAut G)` (wrapping the inherently multiplicative automorphism group), so the proof did not translate. Replace with the same `simp only [mul_assoc, inv_mul_cancel_left]` manipulation that the existing `sup_normal` (immediately above) uses for the analogous step. Both `mul_assoc` and `inv_mul_cancel_left` have `@[to_additive]`, so the proof now translates cleanly. The lemma `AddSubgroup.normal_iSup_normal` is now also exported and mirrors the existing `AddSubgroup.normal_iInf_normal` in `Basic.lean`.
…lignment The Sylow alignment `pCore p G = ⨅ P : Sylow p G, (P : Subgroup G)` is unconditional, not specific to finite groups or prime `p`: * `pCore_le_sylow` (no longer needs `[Fact p.Prime] [Finite G]`): for any Sylow `P`, the join `P ⊔ pCore p G` is a `p`-subgroup (by `IsPGroup.to_sup_of_normal_right`), so maximality of `P` forces `P ⊔ pCore = P`, hence `pCore ≤ P`. * `normal_iInf_sylow` (no hypotheses): conjugation permutes the Sylows, so the intersection is invariant. * `isPGroup_iInf_sylow` (no hypotheses): the intersection lies in any one Sylow, hence is a `p`-group. `Sylow.nonempty` is unconditional. * `pCore_eq_iInf_sylow` (no hypotheses): le_antisymm of the above. The new proof of `pCore_le_sylow` is also shorter — it avoids the Sylow-II conjugacy machinery (`MulAction.exists_smul_eq`) entirely.
Edge cases: * `pCore_zero`: `pCore 0 G = ⊤`. Every group is a `0`-group via `g ^ 0 ^ 1 = g ^ 0 = 1`. * `pCore_one`: `pCore 1 G = ⊥`. A `1`-group must be trivial, since `g ^ 1 ^ k = g ^ 1 = g`. Both marked `@[simp]`.
…opyright * Replace the `change` + `have` + `rw` block in the mul case of `Subgroup.normal_iSup_normal` with a single `simpa only` step using `mul_mem hy hz`: `simp only` handles beta-reduction plus the `[mul_assoc, inv_mul_cancel_left]` normalisation of the conjugation identity in one go. * Update copyright holder in `PCore.lean` to Lean FRO, LLC.
* Cut "Main results" down to the four headline statements (normality + isPGroup, universal property, Sylow alignment, hom behaviour). The individual API lemmas are documented at their declarations. * Remove "Terminology" section — the docstring of `pCore` already notes the `O_p(G)` notation.
The `Nonempty {N : Subgroup G // N.Normal ∧ IsPGroup p N}` instance is
an implementation detail used only in this file (by `isPGroup_pCore`
and `mem_pCore_iff`). External users shouldn't need it.
Replace the file-wide `@[expose] public section` with per-declaration `public` (and `@[expose]` on `pCore` itself), so only the definition's body is exposed and theorem proofs stay private. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ore only Per-declaration `public` is too noisy. Restore a `public section` for visibility and keep `@[expose]` only on the `pCore` definition, where the body needs to be unfoldable downstream. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Oh, great! This might be relevant for some stuff @Whysoserioushah has been thinking about. |
Following the convention used for `Sylow p H`, the `p`-core of a subgroup `H : Subgroup G` is now a `Subgroup H` rather than a `Subgroup G`. This matches finite group theory usage, where `O_p` is applied to subgroups like `N_G(P)` or `C_G(x)` and the result is compared inside `H`. Intrinsic API (`pCore_normal`, `isPGroup_pCore`, `le_pCore`, `mem_pCore_iff`, `pCore_eq_iInf_sylow`) becomes cleaner — no `subgroupOf` ceremony for normality. The hom API uses `MonoidHom.subgroupMap` and `MonoidHom.subgroupComap` for natural restrictions; `map_pCore_le_pCore` drops its surjectivity hypothesis because `subgroupMap` is intrinsically surjective. Also adds `ker_subgroupComap` (alongside the existing `ker_subgroupMap`) in `Mathlib/Algebra/Group/Subgroup/Ker.lean`, used in the proof of `comap_pCore_le_pCore`. For the classical `O_p(G)`, take `pCore p (⊤ : Subgroup G)`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
p-core of a groupp-core of a subgroup
… lemmas Follow-up to the subgroup redesign, addressing review feedback: * `pCore_characteristic`: `pCore p H` is characteristic in `H`. * `pCore_eq_top_iff` now states `↔ IsPGroup p H` (predicate on the type, per the `Group → Prop` convention) rather than `IsPGroup p (⊤ : Subgroup H)`. * `map_pCore_eq_pCore`: the image-equals form, with the minimal `IsPGroup p (f.subgroupMap H).ker` hypothesis. `MulEquiv.map_pCore` now derives from it. * `comap_pCore_le_pCore` / `comap_pCore_eq_pCore` use the minimal restricted hypotheses (`IsPGroup p (f.subgroupComap H').ker`, surjectivity of the restriction) instead of the stronger whole-map versions. * `sylow_eq_pCore_of_normal`, `pCore_eq_sylow_iff_normal`: a Sylow `p`-subgroup of `H` coincides with the `p`-core exactly when it is normal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hello! I just found this PR when I went to open my own PR defining p-cores 😅 I'll try to review this soon to see if I find stuff worth keeping from my take on this, and in the meantime I opened it as a draft PR if people are interested to play spot-the-difference until I get to it: #40036 |
|
@tb65536, I see you marked this back as I'm going to remove |
|
The status quo is def pCore (p : ℕ) (H : Subgroup G) : Subgroup H :=
⨆ N : {N : Subgroup H // N.Normal ∧ IsPGroup p N}, (N : Subgroup H)but we've suggested def pCore (p : ℕ) (H : Subgroup G) : Subgroup G :=
⨆ N ≤ H, ⨆ _ : (N.subgroupOf H).Normal, ⨆ _ : IsPGroup p N, NSpecifically the issue is that |
# Conflicts: # Mathlib/Algebra/Group/Subgroup/Pointwise.lean
Drop the duplicate normal_iSup_normal (master gained Subgroup.iSup_normal in #40011) and derive pCore_normal from it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ubgroupOf Per reviewer discussion, pCore p H now returns a Subgroup G (lying inside H, see pCore_le) so it composes with ambient-group constructions without .map H.subtype clutter. Since O_p(H) is only normal in H, normality/characteristicity are stated for (pCore p H).subgroupOf H. Drop @[expose] (pCore_subgroupOf is the characterisation lemma); simplify isPGroup_pCore via Sylow.iSup_of_normal; convert the Sylow-alignment and homomorphism-behaviour lemmas to the Subgroup G API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add pCore_eq_iSup, ambient mem_pCore_iff, @[simp] pCore_bot; make the in-H universal property le_pCore_subgroupOf_iff primary (map_subtype_le_pCore_iff its corollary); add map_pCore_eq_pCore_of_ker_isPGroup; split the docstring so the ambient isPGroup result is distinguished from the relative normality/ characteristic results. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…via subgroupMap Add Subgroup.subgroupOf_map_subgroupMap and Subgroup.subgroupOf_comap_subgroupComap (with to_additive), promoting two helpers from the p-core development to general subgroup API (named after the existing subgroupOf_map_subtype), and use them in GroupTheory/PCore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| def pCore (p : ℕ) (H : Subgroup G) : Subgroup G := | ||
| ⨆ N : {N : Subgroup H // N.Normal ∧ IsPGroup p N}, (N : Subgroup H).map H.subtype |
There was a problem hiding this comment.
This seems really awkward to me, could you use
⨆ N ≤ H, ⨆ _ : (N.subgroupOf H).Normal, ⨆ _ : IsPGroup p N, N
instead?
If it makes the proofs worse for some reason, then could you instead add versions of pCore_eq_iSup and mem_pCore_iff with this def?
|
I'm not sure if this should be a Zulip post, so let me know. I've been refamiliarizing myself with mathlib's group theory api and have noticed |
rosborn
left a comment
There was a problem hiding this comment.
directed_pCore can be deleted with the suggested changes to mem_pCore_subgroupOf_iff / mem_pCore_iff / pCore_eq_bot_iff.
normal_iInf_sylow / isPGroup_iInf_sylow no longer have any pCore-specific neighbors. Might be worth moving to Sylow.lean?
| map_le_iff_le_comap.mp (map_pCore_le_pCore f) | ||
|
|
||
| /-- A group isomorphism `e : G ≃* G'` carries the `p`-core of `H` to the `p`-core of its image. -/ | ||
| theorem _root_.MulEquiv.map_pCore (e : G ≃* G') : |
There was a problem hiding this comment.
This is a corollary of map_pCore_eq_pCore_of_ker_isPGroup
map_pCore_eq_pCore_of_ker_isPGroup (e : G →* G') (by
rw [(e : G →* G').ker_eq_bot e.injective]
exact IsPGroup.of_bot)
There was a problem hiding this comment.
Also requires import Mathlib.Algebra.Group.Subgroup.Ker
| rw [pCore, ← Subgroup.map_iSup] | ||
| haveI : ∀ N : {N : Subgroup H // N.Normal ∧ IsPGroup p N}, ((N : Subgroup H)).Normal := | ||
| fun N => N.2.1 | ||
| exact (Sylow.iSup_of_normal _ fun N => N.2.2).map H.subtype |
There was a problem hiding this comment.
| rw [pCore, ← Subgroup.map_iSup] | |
| haveI : ∀ N : {N : Subgroup H // N.Normal ∧ IsPGroup p N}, ((N : Subgroup H)).Normal := | |
| fun N => N.2.1 | |
| exact (Sylow.iSup_of_normal _ fun N => N.2.2).map H.subtype | |
| isPGroup_pCore_subgroupOf.of_equiv (subgroupOfEquivOfLe pCore_le) |
| /-- The subtype of normal `p`-subgroups of `H` is nonempty (it contains `⊥`). -/ | ||
| private instance : Nonempty {N : Subgroup H // N.Normal ∧ IsPGroup p N} := | ||
| ⟨⟨⊥, inferInstance, IsPGroup.of_bot⟩⟩ | ||
|
|
There was a problem hiding this comment.
Can also add an instance for normality
private instance (N : {N : Subgroup H // N.Normal ∧ IsPGroup p N}) : (N : Subgroup H).Normal :=
N.2.1
Will get rid of the haveIs in isPGroup_pCore_subgroupOf and pCore_subgroupOf_normal.
| /-- A normal Sylow `p`-subgroup of `H` coincides with the `p`-core inside `H`. -/ | ||
| theorem sylow_eq_pCore_subgroupOf_of_normal (P : Sylow p H) [(P : Subgroup H).Normal] : | ||
| (P : Subgroup H) = (pCore p H).subgroupOf H := | ||
| le_antisymm (le_pCore_subgroupOf inferInstance P.2) (pCore_subgroupOf_le_sylow P) | ||
|
|
||
| /-- A Sylow `p`-subgroup of `H` equals the `p`-core (inside `H`) iff it is normal. -/ | ||
| theorem pCore_subgroupOf_eq_sylow_iff_normal (P : Sylow p H) : | ||
| (pCore p H).subgroupOf H = (P : Subgroup H) ↔ (P : Subgroup H).Normal := by | ||
| refine ⟨fun h => h ▸ pCore_subgroupOf_normal, fun h => ?_⟩ | ||
| haveI := h | ||
| exact (sylow_eq_pCore_subgroupOf_of_normal P).symm |
There was a problem hiding this comment.
Is there a reason these are flipped?
| x ∈ (pCore p H).subgroupOf H ↔ ∃ N : Subgroup H, N.Normal ∧ IsPGroup p N ∧ x ∈ N := by | ||
| rw [pCore_subgroupOf, mem_iSup_of_directed directed_pCore] | ||
| exact ⟨fun ⟨N, hxN⟩ => ⟨N, N.2.1, N.2.2, hxN⟩, | ||
| fun ⟨N, hN, hP, hxN⟩ => ⟨⟨N, hN, hP⟩, hxN⟩⟩ |
There was a problem hiding this comment.
| x ∈ (pCore p H).subgroupOf H ↔ ∃ N : Subgroup H, N.Normal ∧ IsPGroup p N ∧ x ∈ N := by | |
| rw [pCore_subgroupOf, mem_iSup_of_directed directed_pCore] | |
| exact ⟨fun ⟨N, hxN⟩ => ⟨N, N.2.1, N.2.2, hxN⟩, | |
| fun ⟨N, hN, hP, hxN⟩ => ⟨⟨N, hN, hP⟩, hxN⟩⟩ | |
| x ∈ (pCore p H).subgroupOf H ↔ ∃ N : Subgroup H, N.Normal ∧ IsPGroup p N ∧ x ∈ N := | |
| ⟨fun hx => ⟨(pCore p H).subgroupOf H, pCore_subgroupOf_normal, isPGroup_pCore_subgroupOf, hx⟩, | |
| fun ⟨_, hN, hP, hxN⟩ => le_pCore_subgroupOf hN hP hxN⟩ |
| x ∈ pCore p H ↔ ∃ N : Subgroup H, N.Normal ∧ IsPGroup p N ∧ x ∈ N.map H.subtype := by | ||
| rw [pCore_eq_iSup, mem_iSup_of_directed fun a b => | ||
| (directed_pCore a b).imp fun _ h => ⟨map_mono h.1, map_mono h.2⟩] | ||
| exact ⟨fun ⟨N, hxN⟩ => ⟨N, N.2.1, N.2.2, hxN⟩, | ||
| fun ⟨N, hN, hP, hxN⟩ => ⟨⟨N, hN, hP⟩, hxN⟩⟩ |
There was a problem hiding this comment.
| x ∈ pCore p H ↔ ∃ N : Subgroup H, N.Normal ∧ IsPGroup p N ∧ x ∈ N.map H.subtype := by | |
| rw [pCore_eq_iSup, mem_iSup_of_directed fun a b => | |
| (directed_pCore a b).imp fun _ h => ⟨map_mono h.1, map_mono h.2⟩] | |
| exact ⟨fun ⟨N, hxN⟩ => ⟨N, N.2.1, N.2.2, hxN⟩, | |
| fun ⟨N, hN, hP, hxN⟩ => ⟨⟨N, hN, hP⟩, hxN⟩⟩ | |
| x ∈ pCore p H ↔ ∃ N : Subgroup H, N.Normal ∧ IsPGroup p N ∧ x ∈ N.map H.subtype := | |
| ⟨fun hx => ⟨(pCore p H).subgroupOf H, pCore_subgroupOf_normal, isPGroup_pCore_subgroupOf, | |
| by rwa [map_subgroupOf_eq_of_le pCore_le]⟩, | |
| fun ⟨_, hN, hP, hxN⟩ => le_pCore hN hP hxN⟩ |
| have key : pCore p H = ⊥ ↔ (pCore p H).subgroupOf H = ⊥ := | ||
| ⟨fun h => by rw [h, bot_subgroupOf], | ||
| fun h => by rw [← map_subgroupOf_eq_of_le (pCore_le (H := H)), h, map_bot]⟩ | ||
| rw [key, pCore_subgroupOf] | ||
| refine ⟨fun h N hN hP => le_bot_iff.mp (h ▸ le_iSup | ||
| (fun N : {N : Subgroup H // N.Normal ∧ IsPGroup p N} => (N : Subgroup H)) ⟨N, hN, hP⟩), | ||
| fun h => ?_⟩ | ||
| rw [eq_bot_iff_forall] | ||
| intro x hx | ||
| obtain ⟨N, hxN⟩ := (mem_iSup_of_directed directed_pCore).mp hx | ||
| simpa [h N N.2.1 N.2.2] using hxN |
There was a problem hiding this comment.
| have key : pCore p H = ⊥ ↔ (pCore p H).subgroupOf H = ⊥ := | |
| ⟨fun h => by rw [h, bot_subgroupOf], | |
| fun h => by rw [← map_subgroupOf_eq_of_le (pCore_le (H := H)), h, map_bot]⟩ | |
| rw [key, pCore_subgroupOf] | |
| refine ⟨fun h N hN hP => le_bot_iff.mp (h ▸ le_iSup | |
| (fun N : {N : Subgroup H // N.Normal ∧ IsPGroup p N} => (N : Subgroup H)) ⟨N, hN, hP⟩), | |
| fun h => ?_⟩ | |
| rw [eq_bot_iff_forall] | |
| intro x hx | |
| obtain ⟨N, hxN⟩ := (mem_iSup_of_directed directed_pCore).mp hx | |
| simpa [h N N.2.1 N.2.2] using hxN | |
| refine ⟨fun h N hN hP => ?_, fun h => ?_⟩ | |
| · have hle := le_pCore hN hP | |
| rwa [h, le_bot_iff, map_eq_bot_iff_of_injective _ H.subtype_injective] at hle | |
| · rw [← map_subgroupOf_eq_of_le (pCore_le (H := H)), | |
| h _ pCore_subgroupOf_normal isPGroup_pCore_subgroupOf, Subgroup.map_bot] |
| theorem pCore_subgroupOf_le_sylow (P : Sylow p H) : (pCore p H).subgroupOf H ≤ P := by | ||
| have hpsup : IsPGroup p ((P : Subgroup H) ⊔ (pCore p H).subgroupOf H : Subgroup H) := | ||
| P.2.to_sup_of_normal_right isPGroup_pCore_subgroupOf | ||
| have heq : (P : Subgroup H) ⊔ (pCore p H).subgroupOf H = P := P.3 hpsup le_sup_left | ||
| exact le_sup_right.trans heq.le |
There was a problem hiding this comment.
Since #40012 merged, this can be shortened.
| theorem pCore_subgroupOf_le_sylow (P : Sylow p H) : (pCore p H).subgroupOf H ≤ P := by | |
| have hpsup : IsPGroup p ((P : Subgroup H) ⊔ (pCore p H).subgroupOf H : Subgroup H) := | |
| P.2.to_sup_of_normal_right isPGroup_pCore_subgroupOf | |
| have heq : (P : Subgroup H) ⊔ (pCore p H).subgroupOf H = P := P.3 hpsup le_sup_left | |
| exact le_sup_right.trans heq.le | |
| theorem pCore_subgroupOf_le_sylow (P : Sylow p H) : (pCore p H).subgroupOf H ≤ P := | |
| isPGroup_pCore_subgroupOf.le_sylow_of_normal P |
This PR adds
Subgroup.pCore p H, the largest normalp-subgroup of a subgroupH : Subgroup G(classicallyO_p(H)), defined as the supremum of all normalp-subgroups. Even without finiteness or primality hypotheses, this is itself a normalp-group (the family of normalp-subgroups is directed under≤, so every element of the supremum lies in some specific summand). For the classicalO_p(G), takepCore p (⊤ : Subgroup G).API covers the universal-property characterisations (
le_pCore,normal_le_pCore,mem_pCore_iff,pCore_eq_bot_iff,pCore_eq_top_iff,pCore_zero,pCore_one), the Sylow alignmentpCore p H = ⨅ P : Sylow p H, (P : Subgroup H), and behaviour under group homomorphisms viaMonoidHom.subgroupMapandMonoidHom.subgroupComap(map_pCore_le_pCore,comap_pCore_eq_pCoreand friends,MulEquiv.map_pCore).Also adds
Subgroup.normal_iSup_normalinMathlib/Algebra/Group/Subgroup/Pointwise.lean(the supremum of a family of normal subgroups is normal) as the sibling of the existingnormal_iInf_normal, andker_subgroupComapinMathlib/Algebra/Group/Subgroup/Ker.leanalongside the existingker_subgroupMap. Both marked@[to_additive].Used downstream in leanprover/lean-eval#311 to state the Baer-Suzuki theorem as a lean-eval problem.
🤖 Prepared with Claude Code