diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 7bbb286778d..1d23e1b7b00 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -936,22 +936,27 @@ def simple_projections(self, side='right', length_increasing=True): from sage.sets.family import Family return Family(self.index_set(), lambda i: self.simple_projection(i, side=side, length_increasing=length_increasing)) - def sign_representation(self, base_ring=None, side="twosided"): + def sign_representation(self, base_ring=None): r""" Return the sign representation of ``self`` over ``base_ring``. INPUT: - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` - - ``side`` -- ignored EXAMPLES:: - sage: W = WeylGroup(["A", 1, 1]) # needs sage.combinat sage.groups - sage: W.sign_representation() # needs sage.combinat sage.groups + sage: W = WeylGroup(['D', 4]) # needs sage.combinat sage.groups + sage: W.sign_representation(QQ) # needs sage.combinat sage.groups Sign representation of - Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space) - over Integer Ring + Weyl Group of type ['D', 4] (as a matrix group acting on the ambient space) + over Rational Field + + sage: # optional - gap3 + sage: W = CoxeterGroup(['B',3], implementation="coxeter3") + sage: W.sign_representation() + Sign representation of Coxeter group of type ['B', 3] + implemented by Coxeter3 over Integer Ring """ if base_ring is None: from sage.rings.integer_ring import ZZ diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index b0214063e3b..aedce1286f1 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -2184,6 +2184,72 @@ def _row_stabilizer(self, la): G = self.group() return self.sum_of_monomials(G(list(w.tuple())) for w in la.young_subgroup()) + @cached_method + def _column_antistabilizer(self, la): + """ + Return the column antistabilizer element of a canonical standard tableau + of shape ``la``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: for la in Partitions(3): + ....: print(la, SGA._column_antistabilizer(la)) + [3] [1, 2, 3] + [2, 1] [1, 2, 3] - [3, 2, 1] + [1, 1, 1] [1, 2, 3] - [1, 3, 2] - [2, 1, 3] + [2, 3, 1] + [3, 1, 2] - [3, 2, 1] + """ + T = [] + total = 1 # make it 1-based + for r in la: + T.append(list(range(total, total+r))) + total += r + T = Tableau(T) + G = self.group() + R = self.base_ring() + return self._from_dict({G(list(w.tuple())): R(w.sign()) for w in T.column_stabilizer()}, + remove_zeros=False) + + @cached_method + def _young_symmetrizer(self, la): + """ + Return the Young symmetrizer of shape ``la`` of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: for la in Partitions(3): + ....: print(la, SGA._young_symmetrizer(la)) + [3] [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + [2, 1] [1, 2, 3] + [2, 1, 3] - [3, 1, 2] - [3, 2, 1] + [1, 1, 1] [1, 2, 3] - [1, 3, 2] - [2, 1, 3] + [2, 3, 1] + [3, 1, 2] - [3, 2, 1] + """ + return self._column_antistabilizer(la) * self._row_stabilizer(la) + + def young_symmetrizer(self, la): + """ + Return the Young symmetrizer of shape ``la`` of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, SymmetricGroup(3)) + sage: SGA.young_symmetrizer([2,1]) + () + (1,2) - (1,3,2) - (1,3) + sage: SGA = SymmetricGroupAlgebra(QQ, 4) + sage: SGA.young_symmetrizer([2,1,1]) + [1, 2, 3, 4] - [1, 2, 4, 3] + [2, 1, 3, 4] - [2, 1, 4, 3] + - [3, 1, 2, 4] + [3, 1, 4, 2] - [3, 2, 1, 4] + [3, 2, 4, 1] + + [4, 1, 2, 3] - [4, 1, 3, 2] + [4, 2, 1, 3] - [4, 2, 3, 1] + sage: SGA.young_symmetrizer([5,1,1]) + Traceback (most recent call last): + ... + ValueError: the partition [5, 1, 1] is not of size 4 + """ + la = _Partitions(la) + if la.size() != self.n: + raise ValueError("the partition {} is not of size {}".format(la, self.n)) + return self._young_symmetrizer(la) + def kazhdan_lusztig_cellular_basis(self): r""" Return the Kazhdan-Lusztig basis (at `q = 1`) of ``self`` diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 2b87493fbc5..0326566ce49 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -343,18 +343,14 @@ def _latex_(self): gens = ', '.join(latex(x) for x in self.gens()) return '\\left\\langle %s \\right\\rangle' % gens - def sign_representation(self, base_ring=None, side="twosided"): + def sign_representation(self, base_ring=None): r""" Return the sign representation of ``self`` over ``base_ring``. - .. WARNING:: - - Assumes ``self`` is a matrix group over a field which has embedding over real numbers. - INPUT: - - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` - - ``side`` -- ignored + - ``base_ring`` -- (optional) the base ring; the default is the base + ring of ``self`` EXAMPLES:: @@ -364,8 +360,6 @@ def sign_representation(self, base_ring=None, side="twosided"): sage: e [1 0] [0 1] - sage: V._default_sign(e) - 1 sage: m2 = V.an_element() sage: m2 2*B['v'] @@ -373,13 +367,58 @@ def sign_representation(self, base_ring=None, side="twosided"): 2*B['v'] sage: m2*e*e 2*B['v'] + + sage: W = WeylGroup(["A", 1, 1]) + sage: W.sign_representation() + Sign representation of + Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space) + over Rational Field + + sage: G = GL(4, 2) + sage: G.sign_representation() == G.trivial_representation() + True """ if base_ring is None: - from sage.rings.integer_ring import ZZ - base_ring = ZZ + base_ring = self.base_ring() + if base_ring.characteristic() == 2: # characteristic 2 + return self.trivial_representation() from sage.modules.with_basis.representation import SignRepresentationMatrixGroup return SignRepresentationMatrixGroup(self, base_ring) + def natural_representation(self, base_ring=None): + r""" + Return the natural representation of ``self`` over ``base_ring``. + + INPUT: + + - ``base_ring`` -- (optional) the base ring; the default is the base + ring of ``self`` + + EXAMPLES:: + + sage: G = groups.matrix.SL(6, 3) + sage: V = G.natural_representation() + sage: V + Natural representation of Special Linear Group of degree 6 + over Finite Field of size 3 + sage: e = prod(G.gens()) + sage: e + [2 0 0 0 0 1] + [2 0 0 0 0 0] + [0 2 0 0 0 0] + [0 0 2 0 0 0] + [0 0 0 2 0 0] + [0 0 0 0 2 0] + sage: v = V.an_element() + sage: v + 2*e[0] + 2*e[1] + sage: e * v + e[0] + e[1] + e[2] + """ + from sage.modules.with_basis.representation import NaturalMatrixRepresentation + return NaturalMatrixRepresentation(self, base_ring) + + ################################################################### # # Matrix group over a generic ring diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index f001126ed84..3453301fd26 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -4981,14 +4981,13 @@ def upper_central_series(self): from sage.groups.generic import structure_description - def sign_representation(self, base_ring=None, side="twosided"): + def sign_representation(self, base_ring=None): r""" Return the sign representation of ``self`` over ``base_ring``. INPUT: - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` - - ``side`` -- ignored EXAMPLES:: diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 0e1e081c671..caafadf6cf3 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -22,6 +22,7 @@ from sage.misc.cachefunc import cached_method from sage.structure.element import Element from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModule_Tensor +from sage.modules.with_basis.subquotient import SubmoduleWithBasis from sage.categories.modules import Modules from sage.matrix.constructor import matrix @@ -329,6 +330,25 @@ def symmetric_power(self, degree=None): """ return Representation_Symmetric(self, degree) + def schur_functor(self, la): + r""" + Return the :class:`Schur functor + ` + with shape ``la`` applied to ``self``. + + EXAMPLES:: + + sage: W = CoxeterGroup(['H', 3]) + sage: R = W.reflection_representation() + sage: S111 = R.schur_functor([1,1,1]) + sage: S111.dimension() + 1 + sage: S3 = R.schur_functor([3]) + sage: S3.dimension() + 10 + """ + return SchurFunctorRepresentation(self, la) + @abstract_method(optional=True) def _semigroup_action(self, g, vec, vec_on_left): """ @@ -1801,7 +1821,7 @@ def _default_sign(self, elem): class SignRepresentationCoxeterGroup(SignRepresentation_abstract): - """ + r""" The sign representation for a Coxeter group. EXAMPLES:: @@ -1809,6 +1829,11 @@ class SignRepresentationCoxeterGroup(SignRepresentation_abstract): sage: G = WeylGroup(["A", 1, 1]) sage: V = G.sign_representation() sage: TestSuite(V).run() + + sage: # optional - gap3 + sage: W = CoxeterGroup(['B', 3], implementation="coxeter3") + sage: S = W.sign_representation() + sage: TestSuite(S).run() """ def _default_sign(self, elem): """ @@ -1825,6 +1850,13 @@ def _default_sign(self, elem): sage: V = G.sign_representation() sage: V._default_sign(elem) 1 + + sage: # optional - gap3 + sage: W = CoxeterGroup(['B', 3], implementation="coxeter3") + sage: S = W.sign_representation() + sage: elem = W.an_element() + sage: S._default_sign(elem) + 1 """ return -1 if elem.length() % 2 else 1 @@ -1920,3 +1952,316 @@ def _semigroup_action(self, g, vec, vec_on_left): if vec_on_left: g = ~g return self.from_vector(g.canonical_matrix() * vec.to_vector()) + +class NaturalMatrixRepresentation(Representation): + r""" + The natural representation of a matrix semigroup. + + A matrix semigroup is defined by its representation on a (finite + dimensional) vector space `V`, which is called the *natural + representation*. + + INPUT: + + - ``matrix_semigroup`` -- a matrix semigroup + - ``base_ring`` -- (optional) the base ring; the default is the base ring + of the semigroup + """ + @staticmethod + def __classcall_private__(cls, semigroup, base_ring=None): + r""" + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: G = groups.matrix.SO(5, 2) + sage: N1 = G.natural_representation() + sage: N2 = G.natural_representation(GF(2)) + sage: N1 is N2 + True + """ + if base_ring is None: + base_ring = semigroup.base_ring() + return super().__classcall__(cls, semigroup, base_ring) + + def __init__(self, semigroup, base_ring): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: G = groups.matrix.SU(2, 2) + sage: N = G.natural_representation() + sage: TestSuite(N).run() + + sage: G = groups.matrix.Sp(4, 2) + sage: N = G.natural_representation() + sage: TestSuite(N).run() # long time + """ + base_ring = semigroup.base_ring() + n = semigroup.degree() + module = CombinatorialFreeModule(base_ring, list(range(n)), prefix='e') + Representation.__init__(self, semigroup, module, None, "left") + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = groups.matrix.GL(3, 2) + sage: G.natural_representation() + Natural representation of General Linear Group of degree 3 + over Finite Field of size 2 + """ + return "Natural representation of {}".format(self._semigroup) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: G = groups.matrix.GL(3, 2) + sage: latex(G.natural_representation()) + \Bold{F}_{2} ^{3} + """ + from sage.misc.latex import latex + return latex(self.base_ring()) + "^{{{}}}".format(self.dimension()) + + def _semigroup_action(self, g, vec, vec_on_left): + r""" + Return the action of the semigroup element ``g`` on the + vector ``vec`` of ``self``. + + EXAMPLES:: + + sage: G = groups.matrix.SL(2, 3) + sage: N = G.natural_representation() + sage: N._test_representation() # indirect doctest + """ + if self._left_repr == vec_on_left: + g = ~g + # TODO: currently, the slowest part is getting the matrix from g + return self.from_vector(g.matrix() * vec.to_vector()) + + +class SchurFunctorRepresentation(Representation_abstract, SubmoduleWithBasis): + r""" + The representation constructed by the Schur functor. + + Let `G` be a semigroup and let `V` be a representation of `G`. The + *Schur functor* for a partition `\lambda` of size `k` is the functor + `\mathbb{S}_{\lambda}` that sends `V` to the `G`-subrepresentation of + `V^{\otimes k}` spanned by `(v_1 \otimes \cdots \otimes v_k) c_{\lambda}`, + where `c_{\lambda}` is the :meth:`Young symmetrizer + ` + corresponding to `\lambda`. When `G = GL_n(F)`, the Schur functor image + `\mathbb{S}_{\lambda} F^n` is the (irreducible when `F` has characteristic + `0`) highest representation of shape `\lambda`. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(3) + sage: V = G.regular_representation() + sage: S21V = V.schur_functor([2, 1]) + sage: S21V.dimension() + 70 + sage: SemistandardTableaux([2,1], max_entry=V.dimension()).cardinality() + 70 + sage: chi = G.character([S21V.representation_matrix(g).trace() + ....: for g in G.conjugacy_classes_representatives()]) + sage: chi.values() + [70, 0, -2] + sage: G.character_table() + [ 1 -1 1] + [ 2 0 -1] + [ 1 1 1] + sage: for m, phi in chi.decompose(): + ....: print(m, phi.values()) + 11 [1, -1, 1] + 11 [1, 1, 1] + 24 [2, 0, -1] + + sage: # long time + sage: S211V = V.schur_functor([2, 1, 1]) + sage: S211V.dimension() + 105 + sage: SemistandardTableaux([2, 1, 1], max_entry=V.dimension()).cardinality() + 105 + sage: chi = G.character([S211V.representation_matrix(g).trace() + ....: for g in G.conjugacy_classes_representatives()]) + sage: chi.values() + [105, -3, 0] + sage: for m, phi in chi.decompose(): + ....: print(m, phi.values()) + 19 [1, -1, 1] + 16 [1, 1, 1] + 35 [2, 0, -1] + + An example with the cyclic group:: + + sage: C3 = groups.permutation.Cyclic(3) + sage: V3 = C3.regular_representation() + sage: S31V3 = V3.schur_functor([3, 1]) + sage: S31V3.dimension() + 15 + sage: chi = C3.character([S31V3.representation_matrix(g).trace() + ....: for g in C3.conjugacy_classes_representatives()]) + sage: chi.values() + [15, 0, 0] + sage: C3.character_table() + [ 1 1 1] + [ 1 zeta3 -zeta3 - 1] + [ 1 -zeta3 - 1 zeta3] + sage: for m, phi in chi.decompose(): + ....: print(m, phi.values()) + 5 [1, 1, 1] + 5 [1, -zeta3 - 1, zeta3] + 5 [1, zeta3, -zeta3 - 1] + + An example of `GL_3(\GF{2})`:: + + sage: G = groups.matrix.GL(3, 2) + sage: from sage.modules.with_basis.representation import Representation + sage: M = CombinatorialFreeModule(GF(2), [0, 1, 2]) + sage: N = Representation(G, M, lambda g, i: M.from_vector(g.matrix().column(i))) + sage: N.rename("Natural representation of {}".format(G)) + sage: N + Natural representation of General Linear Group of degree 3 over Finite Field of size 2 + sage: S21N = N.schur_functor([2, 1]) + sage: S21N.dimension() + 8 + """ + @staticmethod + def __classcall_private__(cls, V, shape): + r""" + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: from sage.modules.with_basis.representation import SchurFunctorRepresentation + sage: G = groups.permutation.Cyclic(3) + sage: V = G.regular_representation() + sage: S1 = SchurFunctorRepresentation(V, Partition([2, 1, 1])) + sage: S2 = SchurFunctorRepresentation(V, (2, 1, 1)) + sage: S1 is S2 + True + """ + from sage.combinat.partition import _Partitions + return super().__classcall__(cls, V, _Partitions(shape)) + + def __init__(self, V, shape): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(3) + sage: V = G.regular_representation() + sage: S = V.schur_functor([2, 1]) + sage: TestSuite(S).run() + + sage: G = CoxeterGroup(['B', 3]) + sage: R = G.reflection_representation() + sage: S = R.schur_functor([3, 2, 1]) + sage: TestSuite(S).run() + + sage: G = groups.matrix.GL(3, 2) + sage: N = G.natural_representation() + sage: S = N.schur_functor([2, 1]) + sage: TestSuite(S).run() + """ + from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + from sage.categories.tensor import tensor + from sage.matrix.matrix_space import MatrixSpace + + R = V.base_ring() + self._shape = shape + self._module = V + d = sum(shape) + + if not isinstance(V, CombinatorialFreeModule): + keys = list(range(V.dimension())) + V = CombinatorialFreeModule(R, keys) + else: + keys = list(V.basis().keys()) + + ambient = tensor([V]*d) + I = ambient.indices() + cla = SymmetricGroupAlgebra(R, SymmetricGroup(d)).young_symmetrizer(shape) + mc = cla.monomial_coefficients(copy=False) + gens = [ambient.sum_of_terms((tuple([k[i-1] for i in p.tuple()]), coeff) + for p, coeff in mc.items()) + for k in ambient.basis().keys()] + support_order = ambient._compute_support_order(gens, None) + from sage.sets.family import Family + gens = Family(ambient.echelon_form(gens, order=support_order)) + cat = Modules(ambient.category().base_ring()).WithBasis().Subobjects() + SubmoduleWithBasis.__init__(self, gens, support_order, ambient, unitriangular=False, category=cat, prefix='S') + # Copied from Representation_abstract.__init__ + self._semigroup = V.semigroup() + self._semigroup_algebra = V.semigroup_algebra() + self._side = V.side() + if self._side not in ["left", "right", "twosided"]: + raise ValueError("the side must be either 'left', 'right', or 'twosided'") + self._left_repr = bool(self._side == "left" or self._side == "twosided") + self._right_repr = bool(self._side == "right" or self._side == "twosided") + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = groups.matrix.GL(4, 2) + sage: N = G.natural_representation() + sage: N.schur_functor([2, 1]) + Schur functor of shape [2, 1] image of Natural representation of + General Linear Group of degree 4 over Finite Field of size 2 + """ + return "Schur functor of shape {} image of {}".format(self._shape, self._module) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: G = groups.matrix.GL(4, 2) + sage: N = G.natural_representation() + sage: latex(N.schur_functor([2, 1])) + \mathbb{S}_{{\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} + \lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{1-2} + \lr{\phantom{x}}\\\cline{1-1} + \end{array}$} + }}(\Bold{F}_{2} ^{4}) + """ + from sage.misc.latex import latex + return "\\mathbb{{S}}_{{{}}}({})".format(latex(self._shape), latex(self._module)) + + def _semigroup_action(self, g, vec, vec_on_left): + r""" + Return the action of the Coxeter group element ``g`` on the + vector ``vec`` of ``self``. + + EXAMPLES:: + + sage: G = WeylGroup(['C', 3], prefix='s') + sage: R = G.reflection_representation() + sage: S = R.schur_functor([3, 2, 1]) + sage: g = G.an_element(); g + s1*s2*s3 + sage: v = S.an_element(); v + 2*S[0] + 2*S[1] + 3*S[2] + sage: S._semigroup_action(g, v, True) + -(2*a+1)*S[0] - (a+2)*S[1] - (2*a-2)*S[2] + (2*a-2)*S[3] + - (-2*a+4)*S[4] + (-2*a+4)*S[5] + 2*S[6] + 2*a*S[7] + sage: S._semigroup_action(g, v, False) + 3*S[0] + (-2*a+5)*S[2] + 3*a*S[4] - (5*a-2)*S[6] - 6*S[7] + """ + if vec_on_left: + return self.retract(vec.lift() * g) + return self.retract(g * vec.lift())