diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 47e3a9d1d8e..4779ec98c43 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -5491,7 +5491,7 @@ def specht_module(self, base_ring=None): EXAMPLES:: sage: SM = Partition([2,2,1]).specht_module(QQ); SM # optional - sage.modules - Specht module of [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0)] over Rational Field + Specht module of [2, 2, 1] over Rational Field sage: s = SymmetricFunctions(QQ).s() # optional - sage.modules sage: s(SM.frobenius_image()) # optional - sage.modules s[2, 2, 1] diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 459364f6121..df68f6526f0 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1262,7 +1262,7 @@ def __mul__(self, rp): sage: p213 * SGA.an_element() # optional - sage.combinat sage.modules 3*[1, 2, 3] + [1, 3, 2] + [2, 1, 3] + 2*[3, 1, 2] sage: p213 * SM.an_element() # optional - sage.combinat sage.modules - 2*B[0] - 4*B[1] + 2*S[[1, 2], [3]] - 4*S[[1, 3], [2]] """ if not isinstance(rp, Permutation) and isinstance(rp, Element): return get_coercion_model().bin_op(self, rp, operator.mul) diff --git a/src/sage/combinat/specht_module.py b/src/sage/combinat/specht_module.py index d7051d7e3e2..cbef8440828 100644 --- a/src/sage/combinat/specht_module.py +++ b/src/sage/combinat/specht_module.py @@ -4,6 +4,13 @@ AUTHORS: - Travis Scrimshaw (2023-1-22): initial version +- Travis Scrimshaw (2023-11-23): added simple modules based on code + from Sacha Goldman + +.. TODO:: + + Integrate this with the implementations in + :mod:`sage.modules.with_basis.representation`. """ # **************************************************************************** @@ -25,10 +32,10 @@ from sage.matrix.constructor import matrix from sage.rings.rational_field import QQ from sage.modules.with_basis.subquotient import SubmoduleWithBasis, QuotientModuleWithBasis +from sage.modules.free_module_element import vector from sage.categories.modules_with_basis import ModulesWithBasis - -class SymmetricGroupRepresentation(): +class SymmetricGroupRepresentation: """ Mixin class for symmetric group (algebra) representations. """ @@ -41,48 +48,9 @@ def __init__(self, SGA): sage: SM = Partition([3,1,1]).specht_module(GF(3)) sage: TestSuite(SM).run() """ + self._semigroup = SGA.group() self._SGA = SGA - def representation_matrix(self, elt): - r""" - Return the matrix corresponding to the left action of the symmetric - group (algebra) element ``elt`` on ``self``. - - .. SEEALSO:: - - :class:`~sage.combinat.symmetric_group_representations.SpechtRepresentation` - - EXAMPLES:: - - sage: SM = Partition([3,1,1]).specht_module(QQ) - sage: SM.representation_matrix(Permutation([2,1,3,5,4])) - [-1 0 0 0 0 0] - [ 0 0 0 -1 0 0] - [ 1 0 0 -1 1 0] - [ 0 -1 0 0 0 0] - [ 1 -1 1 0 0 0] - [ 0 -1 0 1 0 -1] - - sage: SGA = SymmetricGroupAlgebra(QQ, 5) - sage: SM = SGA.specht_module([(0,0), (0,1), (0,2), (1,0), (2,0)]) - sage: SM.representation_matrix(Permutation([2,1,3,5,4])) - [-1 0 0 1 -1 0] - [ 0 0 1 0 -1 1] - [ 0 1 0 -1 0 1] - [ 0 0 0 0 -1 0] - [ 0 0 0 -1 0 0] - [ 0 0 0 0 0 -1] - sage: SGA = SymmetricGroupAlgebra(QQ, 5) - sage: SM.representation_matrix(SGA([3,1,5,2,4])) - [ 0 -1 0 1 0 -1] - [ 0 0 0 0 0 -1] - [ 0 0 0 -1 0 0] - [ 0 0 -1 0 1 -1] - [ 1 0 0 -1 1 0] - [ 0 0 0 0 1 0] - """ - return matrix(self.base_ring(), [(elt * b).to_vector() for b in self.basis()]) - @cached_method def frobenius_image(self): r""" @@ -139,11 +107,142 @@ def frobenius_image(self): from sage.combinat.sf.sf import SymmetricFunctions BR = self.base_ring() p = SymmetricFunctions(BR).p() - G = self._SGA.group() + G = self._semigroup CCR = [(elt, elt.cycle_type()) for elt in G.conjugacy_classes_representatives()] return p.sum(self.representation_matrix(elt).trace() / la.centralizer_size() * p[la] for elt, la in CCR) + # TODO: Move these methods up to methods of general representations + + def representation_matrix(self, elt): + r""" + Return the matrix corresponding to the left action of the symmetric + group (algebra) element ``elt`` on ``self``. + + EXAMPLES:: + + sage: SM = Partition([3,1,1]).specht_module(QQ) + sage: SM.representation_matrix(Permutation([2,1,3,5,4])) + [-1 0 0 0 0 0] + [ 0 0 0 -1 0 0] + [ 1 0 0 -1 1 0] + [ 0 -1 0 0 0 0] + [ 1 -1 1 0 0 0] + [ 0 -1 0 1 0 -1] + + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: SM = SGA.specht_module([(0,0), (0,1), (0,2), (1,0), (2,0)]) + sage: SM.representation_matrix(Permutation([2,1,3,5,4])) + [-1 0 0 1 -1 0] + [ 0 0 1 0 -1 1] + [ 0 1 0 -1 0 1] + [ 0 0 0 0 -1 0] + [ 0 0 0 -1 0 0] + [ 0 0 0 0 0 -1] + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: SM.representation_matrix(SGA([3,1,5,2,4])) + [ 0 -1 0 1 0 -1] + [ 0 0 0 0 0 -1] + [ 0 0 0 -1 0 0] + [ 0 0 -1 0 1 -1] + [ 1 0 0 -1 1 0] + [ 0 0 0 0 1 0] + """ + return matrix(self.base_ring(), [(elt * b).to_vector() for b in self.basis()]) + + @cached_method + def character(self): + """ + Return the character of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: SM = SGA.specht_module([3,2]) + sage: SM.character() + (5, 1, 1, -1, 1, -1, 0) + sage: matrix(SGA.specht_module(la).character() for la in Partitions(5)) + [ 1 1 1 1 1 1 1] + [ 4 2 0 1 -1 0 -1] + [ 5 1 1 -1 1 -1 0] + [ 6 0 -2 0 0 0 1] + [ 5 -1 1 -1 -1 1 0] + [ 4 -2 0 1 1 0 -1] + [ 1 -1 1 1 -1 -1 1] + + sage: SGA = SymmetricGroupAlgebra(QQ, SymmetricGroup(5)) + sage: SM = SGA.specht_module([3,2]) + sage: SM.character() + Character of Symmetric group of order 5! as a permutation group + sage: SM.character().values() + [5, 1, 1, -1, 1, -1, 0] + sage: matrix(SGA.specht_module(la).character().values() for la in reversed(Partitions(5))) + [ 1 -1 1 1 -1 -1 1] + [ 4 -2 0 1 1 0 -1] + [ 5 -1 1 -1 -1 1 0] + [ 6 0 -2 0 0 0 1] + [ 5 1 1 -1 1 -1 0] + [ 4 2 0 1 -1 0 -1] + [ 1 1 1 1 1 1 1] + sage: SGA.group().character_table() + [ 1 -1 1 1 -1 -1 1] + [ 4 -2 0 1 1 0 -1] + [ 5 -1 1 -1 -1 1 0] + [ 6 0 -2 0 0 0 1] + [ 5 1 1 -1 1 -1 0] + [ 4 2 0 1 -1 0 -1] + [ 1 1 1 1 1 1 1] + """ + G = self._semigroup + chi = [self.representation_matrix(g).trace() + for g in G.conjugacy_classes_representatives()] + try: + return G.character(chi) + except AttributeError: + return vector(chi, immutable=True) + + @cached_method + def brauer_character(self): + """ + Return the Brauer character of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(2), 5) + sage: SM = SGA.specht_module([3,2]) + sage: SM.brauer_character() + (5, -1, 0) + sage: SM.simple_module().brauer_character() + (4, -2, -1) + """ + from sage.rings.number_field.number_field import CyclotomicField + from sage.arith.functions import lcm + G = self._semigroup + p = self.base_ring().characteristic() + # We manually compute the order since a Permutation does not implement order() + chi = [] + for g in G.conjugacy_classes_representatives(): + if p.divides(lcm(g.cycle_type())): + # ignore the non-p-regular elements + continue + evals = self.representation_matrix(g).eigenvalues() + K = evals[0].parent() + val = 0 + for la in evals: + if la == K.one(): + val += 1 + continue + o = la.multiplicative_order() + zeta = CyclotomicField(o).gen() + prim = K.zeta(o) + for deg in range(o): + if prim**deg == la: + val += zeta ** deg + break + chi.append(val) + + return vector(chi, immutable=True) + class SpechtModule(SubmoduleWithBasis, SymmetricGroupRepresentation): r""" @@ -542,6 +641,13 @@ class SpechtModuleTableauxBasis(SpechtModule): This is constructed as a `S_n`-submodule of the :class:`TabloidModule` (also referred to as the standard module). + + .. SEEALSO:: + + - :class:`SpechtModule` for the generic diagram implementation + constructed as a left ideal of the group algebra + - :class:`~sage.combinat.symmetric_group_representations.SpechtRepresentation` + for an implementation of the representation by matrices. """ def __init__(self, ambient): r""" @@ -697,6 +803,10 @@ def simple_module(self): r""" Return the simple (or irreducible) `S_n`-submodule of ``self``. + .. SEEALSO:: + + :class:`~sage.combinat.specht_module.SimpleModule` + EXAMPLES:: sage: SGA = SymmetricGroupAlgebra(GF(3), 5) @@ -704,7 +814,14 @@ def simple_module(self): sage: L = SM.simple_module() sage: L.dimension() 1 + + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: SM = SGA.specht_module([3,2]) + sage: SM.simple_module() is SM + True """ + if self.base_ring().characteristic() == 0: + return self return SimpleModule(self) @@ -741,13 +858,22 @@ def __init__(self, specht_module): sage: SM = SGA.specht_module([3,2]) sage: U = SM.maximal_submodule() sage: TestSuite(U).run() + + sage: SM = SGA.specht_module([2,1,1,1]) + sage: SM.maximal_submodule() + Traceback (most recent call last): + ... + NotImplementedError: only implemented for 3-regular partitions """ SymmetricGroupRepresentation.__init__(self, specht_module._SGA) - if specht_module.base_ring().characteristic() == 0: + p = specht_module.base_ring().characteristic() + if p == 0: basis = [] else: TM = specht_module._ambient + if not TM._shape.is_regular(p): + raise NotImplementedError(f"only implemented for {p}-regular partitions") TV = TM._dense_free_module() SV = TV.submodule(specht_module.lift.matrix().columns()) basis = (SV & SV.complement()).basis() @@ -781,7 +907,7 @@ class SimpleModule(QuotientModuleWithBasis, SymmetricGroupRepresentation): r""" The simgle `S_n`-module associated with a partition `\lambda`. - The simple module `L^{\lambda}` is the quotient of the Specht module + The simple module `D^{\lambda}` is the quotient of the Specht module `S^{\lambda}` by its :class:`maximal submodule ` `U^{\lambda}`. @@ -789,30 +915,70 @@ class SimpleModule(QuotientModuleWithBasis, SymmetricGroupRepresentation): sage: SGA = SymmetricGroupAlgebra(GF(3), 5) sage: SM = SGA.specht_module([3,1,1]) - sage: L = SM.simple_module() - sage: v = L.an_element(); v - 2*L[[[1, 3, 5], [2], [4]]] + 2*L[[[1, 4, 5], [2], [3]]] + sage: D = SM.simple_module() + sage: v = D.an_element(); v + 2*D[[[1, 3, 5], [2], [4]]] + 2*D[[[1, 4, 5], [2], [3]]] sage: SGA.an_element() * v - 2*L[[[1, 2, 4], [3], [5]]] + 2*L[[[1, 3, 5], [2], [4]]] + 2*D[[[1, 2, 4], [3], [5]]] + 2*D[[[1, 3, 5], [2], [4]]] + + We give an example on how to construct the decomposition matrix + (the Specht modules are a complete set of irreducible projective + modules) and the Cartan matrix of a symmetric group algebra:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 4) + sage: BM = matrix(SGA.simple_module(la).brauer_character() + ....: for la in Partitions(4, regular=3)) + sage: SBT = matrix(SGA.specht_module(la).brauer_character() + ....: for la in Partitions(4)) + sage: D = SBT * ~BM; D + [1 0 0 0] + [0 1 0 0] + [1 0 1 0] + [0 0 0 1] + [0 0 1 0] + sage: D.transpose() * D + [2 0 1 0] + [0 1 0 0] + [1 0 2 0] + [0 0 0 1] + + We verify this against the direct computation (up to reindexing the + rows and columns):: + + sage: SGA.cartan_invariants_matrix() # long time + [1 0 0 0] + [0 1 0 0] + [0 0 2 1] + [0 0 1 2] """ def __init__(self, specht_module): - """ + r""" Initialize ``self``. EXAMPLES:: sage: SGA = SymmetricGroupAlgebra(GF(3), 5) sage: SM = SGA.specht_module([3,1,1]) - sage: L = SM.simple_module() - sage: TestSuite(L).run() + sage: D = SM.simple_module() + sage: TestSuite(D).run() + + sage: SGA = SymmetricGroupAlgebra(GF(3), 5) + sage: SM = SGA.specht_module([2,1,1,1]) + sage: SM.simple_module() + Traceback (most recent call last): + ... + ValueError: the partition must be 3-regular """ self._diagram = specht_module._diagram + p = specht_module.base_ring().characteristic() + if not self._diagram.is_regular(p): + raise ValueError(f"the partition must be {p}-regular") SymmetricGroupRepresentation.__init__(self, specht_module._SGA) cat = specht_module.category() - QuotientModuleWithBasis.__init__(self, specht_module.maximal_submodule(), cat, prefix='L') + QuotientModuleWithBasis.__init__(self, specht_module.maximal_submodule(), cat, prefix='D') def _repr_(self): - """ + r""" Return a string representation of ``self``. EXAMPLES:: diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index aed87d371e0..0f827d59525 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -1568,9 +1568,13 @@ def specht_module(self, D): return SpechtModule(self, D) def tabloid_module(self, D): - """ + r""" Return the module of tabloids with the natural action of ``self``. + .. SEEALSO:: + + :class:`~sage.combinat.specht_module.TabloidModule` + EXAMPLES:: sage: SGA = SymmetricGroupAlgebra(QQ, 5) @@ -1601,6 +1605,29 @@ def specht_module_dimension(self, D): span_set = specht_module_spanning_set(D, self) return matrix(self.base_ring(), [v.to_vector() for v in span_set]).rank() + def simple_module(self, la): + r""" + Return the simple module of ``self`` indexed by the partition ``la``. + + Over a field of characteristic `0`, this simply returns the Specht + module. + + .. SEEALSO:: + + :class:`sage.combinat.specht_module.SimpleModule` + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 5) + sage: D = SGA.simple_module(Partition([3,1,1])) + sage: D + Simple module of [3, 1, 1] over Finite Field of size 3 + sage: D.brauer_character() + (6, 0, -2, 0, 1) + """ + from sage.combinat.specht_module import SpechtModule + return SpechtModule(self, la).simple_module() + def simple_module_dimension(self, la): r""" Return the dimension of the simple module of ``self`` indexed by the diff --git a/src/sage/combinat/tabloid.py b/src/sage/combinat/tabloid.py index ed41c8e6db9..72a45f0ebfc 100644 --- a/src/sage/combinat/tabloid.py +++ b/src/sage/combinat/tabloid.py @@ -25,7 +25,7 @@ class Tabloid(ClonableArray): integer partition). """ def __init__(self, parent, tableaux, check=True): - """ + r""" Initialize ``self``. EXAMPLES:: @@ -37,7 +37,7 @@ def __init__(self, parent, tableaux, check=True): super().__init__(parent, map(frozenset, tableaux), check=check) def check(self): - """ + r""" Check that ``self`` is a valid tabloid. EXAMPLES:: @@ -70,7 +70,7 @@ def check(self): assert not X def _repr_(self): - """ + r""" Return a string representation of ``self``. EXAMPLES:: @@ -84,7 +84,7 @@ def _repr_(self): return ret + "]" def _ascii_art_(self): - """ + r""" Return a string representation of ``self``. EXAMPLES:: @@ -124,7 +124,7 @@ def _latex_(self): return ret.replace("|", "") def symmetric_group_action(self, permutation): - """ + r""" Return the left action of ``permutation`` on ``self``. EXAMPLES:: @@ -151,7 +151,7 @@ def symmetric_group_action(self, permutation): return P.element_class(P, [[permutation(val) for val in row] for row in self]) def _acted_upon_(self, sigma, self_on_left=True): - """ + r""" Return the action of ``sigma`` on ``self``. EXAMPLES:: @@ -179,12 +179,12 @@ def _acted_upon_(self, sigma, self_on_left=True): class Tabloids(UniqueRepresentation, Parent): - """ + r""" Tabloids of a fixed shape. """ @staticmethod def __classcall_private__(cls, partition): - """ + r""" Normalize input to ensure a unique representation. EXAMPLES:: @@ -199,7 +199,7 @@ def __classcall_private__(cls, partition): return super().__classcall__(cls, partition) def __init__(self, partition): - """ + r""" Initialize ``self``. EXAMPLES:: @@ -223,7 +223,7 @@ def __init__(self, partition): Parent.__init__(self, category=FiniteEnumeratedSets()) def __iter__(self): - """ + r""" Iterate over ``self``. EXAMPLES:: @@ -290,7 +290,7 @@ def __iter__(self): i += 1 def from_tableau(self, T): - """ + r""" Construct a tabloid from the tableau ``T``. EXAMPLES:: @@ -302,7 +302,7 @@ def from_tableau(self, T): return self.element_class(self, T) def cardinality(self): - """ + r""" Return the cardinality of ``self``. EXAMPLES:: @@ -314,4 +314,3 @@ def cardinality(self): return ZZ(multinomial(list(self._shape))) Element = Tabloid -