From 69678a02496eddbe880d3d4ddeb8903013d97a72 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 13 Apr 2024 13:30:04 +0900 Subject: [PATCH] Doing some more work on GP modules. Speedup in character-type computations. --- src/sage/combinat/specht_module.py | 6 +- .../symmetric_group_representations.py | 141 ++++++++++++++++-- 2 files changed, 132 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/specht_module.py b/src/sage/combinat/specht_module.py index 127903829f7..6d30ddf9ca0 100644 --- a/src/sage/combinat/specht_module.py +++ b/src/sage/combinat/specht_module.py @@ -123,7 +123,8 @@ def frobenius_image(self): s = SymmetricFunctions(QQ).s() G = self._semigroup CCR = [(elt, elt.cycle_type()) for elt in G.conjugacy_classes_representatives()] - return s(p.sum(QQ(self.representation_matrix(elt).trace()) / la.centralizer_size() * p[la] + B = self.basis() + return s(p.sum(QQ(sum((elt * B[k])[k] for k in B.keys())) / la.centralizer_size() * p[la] for elt, la in CCR)) # TODO: Move these methods up to methods of general representations @@ -208,7 +209,8 @@ def character(self): [ 1 1 1 1 1 1 1] """ G = self._semigroup - chi = [self.representation_matrix(g).trace() + B = self.basis() + chi = [sum((g * B[k])[k] for k in B.keys()) for g in G.conjugacy_classes_representatives()] try: return G.character(chi) diff --git a/src/sage/combinat/symmetric_group_representations.py b/src/sage/combinat/symmetric_group_representations.py index ef36db1abdd..615649aa919 100644 --- a/src/sage/combinat/symmetric_group_representations.py +++ b/src/sage/combinat/symmetric_group_representations.py @@ -16,6 +16,7 @@ """ # **************************************************************************** # Copyright (C) 2009 Franco Saliola +# 2024 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # @@ -1020,10 +1021,18 @@ def partition_to_vector_of_contents(partition, reverse=False): # #### Garsia-Procesi modules ################################################ from sage.rings.quotient_ring import QuotientRing_generic -from sage.combinat.specht_module import SymmetricGroupRepresentation -class GarsiaProcesiModule(UniqueRepresentation, QuotientRing_generic, SymmetricGroupRepresentation): +from sage.combinat.specht_module import SymmetricGroupRepresentation as SymmetricGroupRepresentation_mixin +class GarsiaProcesiModule(UniqueRepresentation, QuotientRing_generic, SymmetricGroupRepresentation_mixin): + """ + A Garsia-Procesi module. + + Isomorphic to the cohomology ring of the Springer fiber. + """ @staticmethod def __classcall_private__(cls, SGA, shape): + """ + Normalize input to ensure a unique representation. + """ return super().__classcall__(cls, SGA, Partition(shape)) def __init__(self, SGA, shape): @@ -1031,7 +1040,7 @@ def __init__(self, SGA, shape): Initialize ``self``. """ self._shape = shape - SymmetricGroupRepresentation.__init__(self, SGA) + SymmetricGroupRepresentation_mixin.__init__(self, SGA) # Construct the Tanisaki ideal from sage.combinat.sf.sf import SymmetricFunctions @@ -1057,26 +1066,44 @@ def p(k): names = tuple([f"gp{i}" for i in range(n)]) from sage.categories.commutative_rings import CommutativeRings from sage.categories.algebras import Algebras - cat = CommutativeRings().Quotients() & Algebras(SGA.base_ring()).WithBasis().FiniteDimensional() + cat = CommutativeRings().Quotients() & Algebras(SGA.base_ring()).Graded().WithBasis().FiniteDimensional() QuotientRing_generic.__init__(self, R, I, names=names, category=cat) def _repr_(self): - """ + r""" Return a string representation of ``self``. """ - return "Garsia-Procesi module of shape {} of {}".format(self._shape, self._semigroup_algebra) + return "Garsia-Procesi module of shape {} over {}".format(self._shape, self.base_ring()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + """ + from sage.misc.latex import latex + return "R_{{{}}}^{{{}}}".format(latex(self._shape), latex(self.base_ring())) def _coerce_map_from_base_ring(self): + r""" + Disable the coercion from the base ring from the category. + """ return None # don't need anything special @cached_method def basis(self): + r""" + Return a basis of ``self``. + """ from sage.sets.family import Family B = self.defining_ideal().normal_basis() return Family([self.retract(b) for b in B]) @cached_method def one_basis(self): + r""" + Return the index of the basis element `1`. + """ B = self.defining_ideal().normal_basis() for i, b in enumerate(B): if b.is_one(): @@ -1094,22 +1121,93 @@ def dimension(self): EXAMPLES:: sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: Sym = SymmetricFunctions(QQ) + sage: s = Sym.s() + sage: Qp = Sym.hall_littlewood(1).Qp() sage: for la in Partitions(5): ....: print(SGA.garsia_procesi_module(la).dimension(), ....: sum(c * StandardTableaux(la).cardinality() ....: for la, c in s(Qp[la]))) - 1, 1 - 5, 5 - 10, 10 - 20, 20 - 30, 30 - 60, 60 - 120, 120 + 1 1 + 5 5 + 10 10 + 20 20 + 30 30 + 60 60 + 120 120 """ return self.defining_ideal().vector_space_dimension() + @cached_method + def graded_frobenius_image(self): + r""" + Return the graded Frobenius image of ``self``. + + The graded Frobenius image is the sum of the :meth:`frobenius_image` + of each graded component, which is known to result in the modified + Hall-Littlewood polynomial `\widetilde{H}_{\lambda}(x; q)`. + + EXAMPLES:: + + We verify that the result is the modified Hall-Littlewood polynomial + for `n = 5`:: + + sage: R. = QQ[] + sage: Sym = SymmetricFunctions(R) + sage: s = Sym.s() + sage: Qp = Sym.hall_littlewood(q).Qp() + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: for la in Partitions(5): + ....: f = SGA.garsia_procesi_module(la).graded_frobenius_image() + ....: d = f[la].degree() + ....: assert f.map_coefficients(lambda c: R(c(~q)*q^d)) == s(Qp[la]) + """ + from sage.combinat.sf.sf import SymmetricFunctions + R = QQ['q'] + q = R.gen() + Sym = SymmetricFunctions(R) + p = Sym.p() + s = Sym.s() + G = self._semigroup + CCR = [(elt, elt.cycle_type()) for elt in G.conjugacy_classes_representatives()] + B = self.basis() + return s(p._from_dict({la: coeff / la.centralizer_size() for elt, la in CCR + if (coeff := sum(q**b.degree() * (elt * b).lift().monomial_coefficient(b.lift()) + for b in B))}, + remove_zeros=False)) + + @cached_method + def graded_character(self): + r""" + Return the graded character of ``self``. + + EXAMPLES:: + """ + q = QQ['q'].gen() + G = self._semigroup + from sage.modules.free_module_element import vector + return vector([sum(q**b.degree() * (g * b).lift().monomial_coefficient(b.lift()) for b in B) + for g in G.conjugacy_classes_representatives()], + immutable=True) + + def graded_representation_matrix(self, elt, q=None): + r""" + Return the matrix corresponding to the left action of the symmetric + group (algebra) element ``elt`` on ``self``. + + EXAMPLES:: + """ + if q is None: + q = self.base_ring()['q'] + R = q.parent() + return matrix(R, [q**b.degree() * (elt * b).to_vector().change_ring(R) + for b in self.basis()]) + class Element(QuotientRing_generic.Element): def _acted_upon_(self, scalar, self_on_left=True): + """ + Return the action of ``scalar`` on ``self``. + """ P = self.parent() if scalar in P.base_ring(): return super()._acted_upon_(scalar, self_on_left) @@ -1124,6 +1222,9 @@ def _acted_upon_(self, scalar, self_on_left=True): return super()._acted_upon_(scalar, self_on_left) def to_vector(self): + """ + Return ``self`` as a (dense) free module vector. + """ P = self.parent() B = P.basis() FM = P._dense_free_module() @@ -1133,6 +1234,20 @@ def to_vector(self): _vector_ = to_vector def monomial_coefficients(self, copy=None): + """ + Return the monomial coefficients of ``self``. + """ B = self.parent().basis() + f = self.lift() return {i: f.monomial_coefficient(b.lift()) for i, b in enumerate(B)} + def degree(self): + return self.lift().degree() + + def homogeneous_degree(self): + if not self: + raise ValueError("the zero element does not have a well-defined degree") + f = self.lift() + if not f.is_homogeneous(): + raise ValueError("element is not homogeneous") + return f.degree()