Skip to content

feat(GroupTheory/PCore): define the p-core of a group#39771

Draft
kim-em wants to merge 10 commits into
masterfrom
kim-em/p-core
Draft

feat(GroupTheory/PCore): define the p-core of a group#39771
kim-em wants to merge 10 commits into
masterfrom
kim-em/p-core

Conversation

@kim-em
Copy link
Copy Markdown
Contributor

@kim-em kim-em commented May 24, 2026

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 or primality of p.

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.

Core properties: Subgroup.pCore, Subgroup.pCore_normal (instance), Subgroup.isPGroup_pCore, Subgroup.le_pCore, Subgroup.normal_le_pCore (iff version for N normal), Subgroup.mem_pCore_iff, Subgroup.pCore_eq_bot_iff, Subgroup.pCore_eq_top_iff/pCore_eq_top.

Sylow alignment (unconditional — also for non-prime p and infinite G): Subgroup.pCore_le_sylow (pCore p G is contained in every Sylow, by the normal-join argument with maximality), Subgroup.normal_iInf_sylow, Subgroup.isPGroup_iInf_sylow, Subgroup.pCore_eq_iInf_sylowpCore p G = ⨅ P : Sylow p G, (P : Subgroup G).

Homomorphism behaviour for f : G →* H: Subgroup.map_pCore_le_pCore (surjective f), Subgroup.pCore_le_comap_pCore (same fact via the map/comap adjunction), Subgroup.comap_pCore_le_pCore (when f.ker is a p-group), Subgroup.comap_pCore_eq_pCore (both conditions give equality), MulEquiv.map_pCore (isomorphism case).

Also added Subgroup.normal_iSup_normal in Mathlib/Algebra/Group/Subgroup/Pointwise.lean: the supremum of a family of normal subgroups is normal. Marked @[to_additive], so AddSubgroup.normal_iSup_normal is also generated. This is the sibling of the existing normal_iInf_normal in Subgroup/Basic.lean.

Used downstream in leanprover/lean-eval#311 to state the Baer-Suzuki theorem.

🤖 Prepared with Claude Code

@github-actions github-actions Bot added the t-group-theory Group theory label May 24, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 24, 2026

PR summary 9a62cc0831

Import changes for modified files

No significant changes to the import graph

Import changes for all files
Files Import difference
Mathlib.GroupTheory.PCore (new file) 1176

Declarations diff

+ _root_.MulEquiv.map_pCore
+ comap_pCore_eq_pCore
+ comap_pCore_le_pCore
+ directed_pCore
+ instance : Nonempty {N : Subgroup G // N.Normal ∧ IsPGroup p N}
+ isPGroup_iInf_sylow
+ isPGroup_pCore
+ le_pCore
+ map_pCore_le_pCore
+ mem_pCore_iff
+ normal_iInf_sylow
+ normal_iSup_normal
+ normal_le_pCore
+ pCore
+ pCore_eq_bot_iff
+ pCore_eq_iInf_sylow
+ pCore_eq_top
+ pCore_eq_top_iff
+ pCore_le_comap_pCore
+ pCore_le_sylow
+ pCore_normal
+ pCore_one
+ pCore_zero

You can run this locally as follows
## from your `mathlib4` directory:
git clone https://github.com/leanprover-community/mathlib-ci.git ../mathlib-ci

## summary with just the declaration names:
../mathlib-ci/scripts/pr_summary/declarations_diff.sh <optional_commit>

## more verbose report:
../mathlib-ci/scripts/pr_summary/declarations_diff.sh long <optional_commit>

The doc-module for scripts/pr_summary/declarations_diff.sh in the mathlib-ci repository contains some details about this script.


No changes to strong technical debt.
No changes to weak technical debt.

Comment on lines +57 to +58
def pCore (p : ℕ) (G : Type*) [Group G] : Subgroup G :=
⨆ N : {N : Subgroup G // N.Normal ∧ IsPGroup p N}, (N : Subgroup G)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What's the advantage of this over the intersection of all Sylow-p Subgroups?

Copy link
Copy Markdown
Contributor Author

@kim-em kim-em May 25, 2026

Choose a reason for hiding this comment

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

Both definitions agree unconditionally. I had this wrong before, but I've dropped [Fact p.Prime] [Finite G] from pCore_eq_iInf_sylow. I think the iSup definition gives the universal-property and hom-behaviour lemmas more directly.

kim-em added 8 commits May 25, 2026 09:56
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`.
kim-em added 2 commits May 25, 2026 10:04
…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]`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-group-theory Group theory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants