Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Burnside ring of a group #37991

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

Newtech66
Copy link
Contributor

@Newtech66 Newtech66 commented May 12, 2024

Resolves #35475.

We implement the Burnside ring for a group $G$. We provide a few ways to construct elements, either directly as a formal sum of orbit types $[G/H]$ (where $H$ is a subgroup of $G$), or by providing a group action $\alpha: G \times X \rightarrow X$ on some set $X$. Finally, we implement addition and multiplication of ring elements.

Depends on #38371.

📝 Checklist

  • The title is concise and informative.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation and checked the documentation preview.

@mantepse

Copy link

github-actions bot commented May 15, 2024

Documentation preview for this PR (built with commit dccdfbf; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

@mantepse mantepse changed the title Implementing the Burnside ring of a group The Burnside ring of a group May 16, 2024
@mantepse
Copy link
Contributor

@trevorkarn, I'm adding you here because of your request at #35475 (comment)

The current branch is a prototype, not yet ready for review.

@trevorkarn
Copy link
Contributor

@trevorkarn, I'm adding you here because of your request at #35475 (comment)

The current branch is a prototype, not yet ready for review.

Thanks so much @mantepse! Just let me know when it is ready for review.

@mantepse
Copy link
Contributor

You are down to 250 lines with more functionality!

You can now use

sage -tox -- src/sage/rings/burnside.py

to run most (perhaps even all) checks locally.

I think it would be good to have one test checking that the cache is populated only as necessary. Since BurnsideRing inerits from UniqueRepresentation, I guess it's best to use a group which doesn't occur in the tests otherwise, and maybe a big one. For example:

sage: G = SymmetricGroup(6)
sage: B = BurnsideRing(G)
sage: X = Subsets(6, 2)
sage: b = B.construct_from_action(lambda g, x: X([g(e) for e in x]), X)
sage: B.basis().keys()._cache
{720: [Symmetric group of order 6! as a permutation group],
 48: [Subgroup generated by [(3,5,4,6), (1,2)(4,5,6)] of (Symmetric group of order 6! as a permutation group)]}
sage: b^2
B[((3,5,4,6), (1,2)(4,5,6))] + B[((5,6), (4,6,5))] + B[((5,6), (3,4), (1,2))]
sage: B.basis().keys()._cache
{720: [Symmetric group of order 6! as a permutation group],
 48: [Subgroup generated by [(3,5,4,6), (1,2)(4,5,6)] of (Symmetric group of order 6! as a permutation group)],
 6: [Subgroup generated by [(5,6), (4,6,5)] of (Symmetric group of order 6! as a permutation group)],
 8: [Subgroup generated by [(5,6), (3,4), (1,2)] of (Symmetric group of order 6! as a permutation group)]}

@mantepse
Copy link
Contributor

One other test:

sage: G = SymmetricGroup(4)
sage: B = BurnsideRing(G)
sage: X = Subsets(4, 2)
sage: b = B.construct_from_action(lambda g, x: X([g(e) for e in x]), X)
sage: b.tensor(b)
B[((3,4), (1,2), (1,2)(3,4))] # B[((3,4), (1,2), (1,2)(3,4))]
sage: (b.tensor(b))^2
4*B[((3,4), (1,2), (1,2)(3,4))] # B[((3,4), (1,2), (1,2)(3,4))] + 2*B[((),)] # B[((3,4), (1,2), (1,2)(3,4))] + 2*B[((3,4), (1,2), (1,2)(3,4))] # B[((),)] + B[((),)] # B[((),)]

@mantepse
Copy link
Contributor

I just realised that you can remove BurnsideRing._element_constructor_ completely, and instead implement ConjugacyClassesOfSubgroups.__contains__ (which we should provide anyway):

    def __contains__(self, H):
        return H.is_subgroup(self._G)

@mantepse
Copy link
Contributor

Here are the next steps (in this order), as I see them:

  1. add docstrings and doctests to make sage -tox -- src/sage/rings/burnside.py pass
  2. create a class MolecularDecompositionPolynomialSymmetricGroupAction (for lack of a better name), which is the graded algebra $\bigoplus_{n\geq 0} B(\mathfrak S_n)$, where $B$ denotes the Burnside ring, and the multiplication is the Cauchy product.
  3. optimize the Burnside ring, most likely by using the functionality in https://docs.gap-system.org/doc/ref/chap41.html
  4. define MolecularDecompositionSymmetricGroupAction (for lack of a better name) as the LazyCompletionGradedAlgebra(MolecularDecompositionPolynomialSymmetricGroupAction)

I'm not completely sure, but I think that MolecularDecompositionPolynomialSymmetricGroupAction will again inherit from CombinatorialFreeModule. The basis might be the set of pairs (n, H) such that H is a subgroup of $\mathfrak S_n$ - I think it is safer to remember n.

I am hesitant to call MolecularDecompositionPolynomialSymmetricGroupAction just PolynomialSpecies, although maybe we should do just that. The reason for my doubt is that we can do very many things without knowing the decomposition of a group action, that is, its representation in the Burnside ring. For example:

sage: Ep = species.SetSpecies(min=1)
sage: C = species.CycleSpecies()
sage: P = Ep(C)
sage: P.isotype_generating_series()
z + 2*z^2 + 3*z^3 + 5*z^4 + 7*z^5 + 11*z^6 + 15*z^7 + O(z^8)

should not create any conjugacy classes of subgroups - this would take forever.

@Newtech66
Copy link
Contributor Author

Newtech66 commented May 20, 2024

I just realised that you can remove BurnsideRing._element_constructor_ completely, and instead implement ConjugacyClassesOfSubgroups.__contains__ (which we should provide anyway):

    def __contains__(self, H):
        return H.is_subgroup(self._G)

How is that working under the hood?

@mantepse
Copy link
Contributor

I just realised that you can remove BurnsideRing._element_constructor_ completely, and instead implement ConjugacyClassesOfSubgroups.__contains__ (which we should provide anyway):

    def __contains__(self, H):
        return H.is_subgroup(self._G)

How is that working under the hood?

Not sure what exactly you mean here.

  1. __contains__ is python magic https://docs.python.org/3/reference/datamodel.html#object.__contains__
  2. CombinatorialFreeModule._element_constructor_ uses __contains__ here:
    elif x in self._indices:
    return self.monomial(self._indices(x))

@Newtech66
Copy link
Contributor Author

  1. __contains__ is python magic https://docs.python.org/3/reference/datamodel.html#object.__contains__
  2. CombinatorialFreeModule._element_constructor_ uses __contains__ here:
    elif x in self._indices:
    return self.monomial(self._indices(x))

Ah, I see, that makes sense.

@Newtech66
Copy link
Contributor Author

I propose we implement the conjugacy class renaming like B[Z3].rename_index("Z3"). We can make it fail if we try to call it on something that is not a basis element. Any alternative suggestions? Can the function be named better?

@mantepse
Copy link
Contributor

I propose we implement the conjugacy class renaming like B[Z3].rename_index("Z3"). We can make it fail if we try to call it on something that is not a basis element. Any alternative suggestions? Can the function be named better?

Maybe, I don't know. I think we can delay thinking about this until we start to play with species.

@Newtech66
Copy link
Contributor Author

I meant to say, in the Factorisation example I gave, a and b have identical values. Are we going to cache and return an object that we already constructed? That is, say we made a, and when we try to construct b, we will see if a already exists and we will just return that?

@mantepse
Copy link
Contributor

I meant to say, in the Factorisation example I gave, a and b have identical values. Are we going to cache and return an object that we already constructed? That is, say we made a, and when we try to construct b, we will see if a already exists and we1 will just return that?

Factorization does not inerit from UniqueRepresentation, whereas our atomic species should. I don't think we are going to rename the product, but rather the individual factors, however.

I can't find an element class inheriting from UniqueRepresentation right now, though.

@Newtech66
Copy link
Contributor Author

I think we should actually use CachedRepresentation, because our AtomicConjugacyClass can have several different representatives in the conjugacy class. Then we can do some argument normalization like here: https://doc.sagemath.org/html/en/reference/structure/sage/structure/unique_representation.html#normalising-the-arguments

@mantepse
Copy link
Contributor

Sorry, I just realized we need neither, because the 'element_constructor' of the parent can make sure it always returns the same object.

@Newtech66
Copy link
Contributor Author

What other work needs to be done here?

@mantepse
Copy link
Contributor

I'm slightly short of time this week, as announced, I'm sorry for that!

Could you double check whether the classes UnivariateAtomicConjugacyClasses and perhaps also UnivariateMolecularConjugacyClasses are really necessary?

The reason I'm asking is as follows: being directly indecomposable is an intrinsic property of a permutation group - it does not depend on an ambient symmetric group.

In particular:

  • the "univariate" directly indecomposable permutation groups are precisely all directly indecomposable permutation groups,
  • the "d-ary" directly indecomposable permutation groups are precisely pairs, consisting of
    • a set partition of the domain $\{X_1,\dots,X_d\}$ with $X_1\cup\dots\cup X_d = \{1,\dots,n\}$, and
    • a directly indecomposable permutation group, which is a subgroup of $S_{X_1}\times\dots\times S_{X_d}$.

This approach should also generalize to variants of species which are not part of this project.

@Newtech66
Copy link
Contributor Author

You're right...we can do better. (that is a brilliant observation...how do I make such observations myself...)

AtomicConjugacyClass can store 2 things: the permgroup and the multicardinality (we will normalize any input beforehand such that the domains are of the form [1...a] [a+1...b] ... [...n]). That takes care of the multivariate aspect.

We can get rid of UnivariateMolecularConjugacyClasses and merge its functionality into other classes.

Then, we do need AtomicConjugacyClasses for basis keys of PolynomialMolecularDecomposition, but then do we want to allow mixing $k$-variate species together? Like having univariate and bivariate species in the same sum? That doesn't feel like it makes sense, so for both classes we should add a variable like k as in P=PolynomialMolecularDecomposition(k=2) to denote that this is the ring of $k$-variate species. Input can be of the form (permgroup, domains_list) where domains_list must be something that can be turned into a valid SetPartition of [1..n] for some $n \geq$ permgroup.degree().

@mantepse
Copy link
Contributor

You're right...we can do better. (that is a brilliant observation...how do I make such observations myself...)

Thank you - maybe do math for 25 years or so?

AtomicConjugacyClass can store 2 things: the permgroup and the multicardinality (we will normalize any input beforehand such that the domains are of the form [1...a] [a+1...b] ... [...n]). That takes care of the multivariate aspect.

No! That's a misunderstanding!

  • AtomicConjugacyClass should just store a directly indecomposable permutation group, normalized. More precisely, the parent class AtomicConjugacyClasses has a cache and an _element_constructor_, that normalizes the input and puts it into the cache if it's not already there
  • in the unary case MolecularConjugacyClass represents a product of conjugacy classes of atomic subgroups.
  • in the $d$-ary case (which may actually be done in the same class), we additionally need a set partition of the domain $X$ into $d$ parts.

but then do we want to allow mixing k-variate species together? Like having univariate and bivariate species in the same sum?

No, (strictly speaking) you cannot add and multiply species of different arity. (In principle, one could regard any species as a species of larger arity, but I'm not sure we want to do that). Composition takes a species $F$ of arity $m$ and $m$ species $G_1,\dots,G_m$ of (the same) arity $k$, and produces a species of arity $k$.

That doesn't feel like it makes sense, so for both classes we should add a variable like k as in P=PolynomialMolecularDecomposition(k=2) to denote that this is the ring of k-variate species.

Yes!

Input can be of the form (permgroup, domains_list) where domains_list must be something that can be turned into a valid SetPartition of [1..n] for some n≥ permgroup.degree().

Hm, domains_list should be a set partition of the domain into precisely $k$ parts, $X_1,\dots,X_k$ and permgroup should then be a subgroup of $\mathfrak S_{X_1}\times\mathfrak S_{X_k}$.

@Newtech66
Copy link
Contributor Author

AtomicConjugacyClass should just store a directly indecomposable permutation group, normalized. More precisely, the parent class AtomicConjugacyClasses has a cache and an element_constructor, that normalizes the input and puts it into the cache if it's not already there

Maintaining a cache here doesn't sound useful: we can only interpret it as a species and have some naming functionality and caching if we also have the set partition of its domain (how else can we distinguish the singleton species $X$ and $Y$ in the bivariate case, for example)? Instead we should have a cache in MolecularConjugacyClasses.

@mantepse
Copy link
Contributor

AtomicConjugacyClass should just store a directly indecomposable permutation group, normalized. More precisely, the parent class AtomicConjugacyClasses has a cache and an element_constructor, that normalizes the input and puts it into the cache if it's not already there

Maintaining a cache here doesn't sound useful: we can only interpret it as a species and have some naming functionality and caching if we also have the set partition of its domain (how else can we distinguish the singleton species X and Y in the bivariate case, for example)? Instead we should have a cache in MolecularConjugacyClasses.

I probably misled you with the name: I would not have used AtomicConjugacyClass for species, but rather for permutation groups. So, I probably should have called it ConjugacyClassOfDirectlyIndecomposableSubgroups or whatever. Thinking about it, probably the class ConjugacyClassOfSubgroups will also not be necessary.

Only the class PolynomialMolecularDecomposition should "interpret" a subgroup as a species, as follows: For simplicity, suppose that we consider bivariate species, and suppose that the species F is atomic. It is represented as a pair (G_F, P) where G_F is an instance of ConjugacyClassOfDirectlyIndecomposableSubgroups(normalized, in particular with domain $1,\dots,n$) andP` is an assignment of each element in the domain to one of the two "variables". It was wrong of me to call this a set partition, because we need to remember which elements belong to which variable.

For example, X could be represented as $(\mathfrak S_1, (\{1\}, \{\}))$, and Y as $(\mathfrak S_1, (\{\}, \{1\}))$, and $\mathcal E_2(XY)$ as $([(1,3)(2,4)], (\{1,3\}, \{2,4\}))$.

For a further example, suppose now that we look at univariate species, then $([(1,3)(2,4)], (\{1,2,3,4\}))$ would instead describe the species $\mathcal E_2(X^2)$, which is (of course!) also atomic.

@mantepse
Copy link
Contributor

Slightly puzzled as to which time of the day you happen to work :-)

@Newtech66
Copy link
Contributor Author

Newtech66 commented Jul 24, 2024

I created a class ConjugacyClassOfDirectlyIndecomposableSubgroups, and AtomicSpecies (which pairs the conjugacy class with an integer vector) and MultivariateAtomicSpecies (the set of $k$-variate atomic species), and PolynomialMolecularDecomposition (now the ring of $k$-variate virtual species) now has basis_keys=IndexedFreeAbelianMonoid(MultivariateAtomicSpecies) (no more explicit molecular species class).

Still several bugs, need to fix.

Update: All done for now, I hope.

@Newtech66
Copy link
Contributor Author

  • The Burnside ring functionality seems completely independent of the rest of the things we are doing here. I still think we should split this PR and open the Burnside ring part of it for review.
  • The current implementation using IndexedFreeAbelianMonoid for basis_keys feels a bit awkward but it works. As a next step, we can define class PolynomialSpecies(IndexedFreeModuleElement) as the element class for PolynomialMolecularDecomposition and add some functions to it like composition(self, others), derivative(self), pointed(self), __call__(self, U) (return structures on $U$?). Though I don't know how we would extend this to an infinite series. Like, for example, $S = E \circ C$ (where $S$ = species of permutations, $E$ = species of sets, $C$ = species of cycles).

@Newtech66 Newtech66 marked this pull request as ready for review July 29, 2024 17:57
@Newtech66
Copy link
Contributor Author

@trevorkarn You can review this PR.

@mantepse
Copy link
Contributor

I think we should not rush to move this into sagemath, I would rather wait until the design of #38446 is fixed.

@trevorkarn
Copy link
Contributor

@trevorkarn You can review this PR.

Thanks @Newtech66 I'll take a look!

@trevorkarn
Copy link
Contributor

I'm having trouble viewing the documentation. Did you add the file as described in https://doc.sagemath.org/html/en/developer/sage_manuals.html#section-add-file?


EXAMPLES::

sage: G = SymmetricGroup(6)
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be good to have some examples where G is not a SymmetricGroup just as a test

@@ -0,0 +1,526 @@
from sage.misc.cachefunc import cached_method
Copy link
Contributor

@trevorkarn trevorkarn Aug 1, 2024

Choose a reason for hiding this comment

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

It could be nice to have a little more documentation for the whole file (as opposed to documentation for each class or method only)

@Newtech66
Copy link
Contributor Author

I'm having trouble viewing the documentation. Did you add the file as described in https://doc.sagemath.org/html/en/developer/sage_manuals.html#section-add-file?

Oops, added it now. en/reference/rings/sage/rings/burnside

@trevorkarn
Copy link
Contributor

Documentation preview for this PR

Thanks! It looks like there is still a "missing title". (Check out https://doc-pr-37991--sagemath.netlify.app/html/en/reference/rings/sage/rings/burnside.)

I am also wondering if there is a better place to put this. Of course I think Burnside rings are important, but probably they are not as important as (say) general quotient rings. Maybe it would be better to put it inside the combinatreference? Just a thought.

Could you also add some tests for cases where the base ring is not ZZ? Maybe QQ and ZZ.quo(6) just as an example?

@dimpase
Copy link
Member

dimpase commented Aug 3, 2024

Burnside rings belong to group theory (or indeed rings). It's more of a coincidence that they have applications in combinatorics

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Burnside ring of permutation representations
4 participants