Skip to content

Commit

Permalink
Trac #34495: Construction for invariant/equivariant submodules
Browse files Browse the repository at this point in the history
We introduce a construction functor for invariant and equivariant
submodules.

`sage.modules.with_basis.invariant.FiniteDimensionalInvariantModule`
gets a `construction` method.

In follow-up ticket #34499, also the tensor modules with prescribed
monoterm symmetries from #30229 will get a `construction` method. An
illustration of this application, capturing symmetric and antisymmetric
matrices, is included as an example. (See #32029, #30276.)

URL: https://trac.sagemath.org/34495
Reported by: mkoeppe
Ticket author(s): Matthias Koeppe
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Sep 20, 2022
2 parents 46f4f2c + 7fe5763 commit 7ebd052
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 4 deletions.
20 changes: 18 additions & 2 deletions src/sage/algebras/orlik_solomon.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ class OrlikSolomonInvariantAlgebra(FiniteDimensionalInvariantModule):
.. NOTE::
The algebra structure only exists when the action on the
groundset yeilds an equivariant matroid, in the sense that
groundset yields an equivariant matroid, in the sense that
`g \cdot I \in \mathcal{I}` for every `g \in G` and for
every `I \in \mathcal{I}`.
"""
Expand Down Expand Up @@ -638,9 +638,25 @@ def action(g, m):
*args, **kwargs)

# To subclass FiniteDimensionalInvariant module, we also need a
# self._semigroup method.
# self._semigroup attribute.
self._semigroup = G

def construction(self):
r"""
Return the functorial construction of ``self``.
This implementation of the method only returns ``None``.
TESTS::
sage: M = matroids.Wheel(3)
sage: from sage.algebras.orlik_solomon import OrlikSolomonAlgebra
sage: OS1 = OrlikSolomonAlgebra(QQ, M)
sage: OS1.construction() is None
True
"""
return None

def _basis_action(self, g, f):
r"""
Return the action of the group element ``g`` on the n.b.c. set ``f``
Expand Down
19 changes: 19 additions & 0 deletions src/sage/algebras/orlik_terao.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,25 @@ def action(g, m):

self._semigroup = G

def construction(self):
r"""
Return the functorial construction of ``self``.
This implementation of the method only returns ``None``.
TESTS::
sage: A = matrix([[1,1,0],[-1,0,1],[0,-1,-1]])
sage: M = Matroid(A)
sage: G = SymmetricGroup(3)
sage: def on_groundset(g,x):
....: return g(x+1)-1
sage: OTG = M.orlik_terao_algebra(QQ, invariant=(G,on_groundset))
sage: OTG.construction() is None
True
"""
return None

def _basis_action(self, g, f):
r"""
Let ``f`` be an n.b.c. set so that it indexes a basis
Expand Down
154 changes: 154 additions & 0 deletions src/sage/categories/pushout.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@
Coercion via construction functors
"""

# ****************************************************************************
# Copyright (C) 2007-2014 Robert Bradshaw
# 2007-2018 David Roe
# 2009-2013 Simon King
# 2010 John Cremona
# 2010-2011 Mike Hansen
# 2012 Julian Rueth
# 2013-2016 Peter Bruin
# 2014 Wilfried Luebbe
# 2015 Benjamin Hackl
# 2015 Daniel Krenn
# 2016-2020 Frédéric Chapoton
# 2017 Jori Mäntysalo
# 2018 Vincent Delecroix
# 2020 Marc Mezzarobba
# 2020-2022 Matthias Koeppe
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************

import operator

from sage.misc.lazy_import import lazy_import
from sage.structure.coerce_exceptions import CoercionException
from .functor import Functor, IdentityFunctor_generic
Expand Down Expand Up @@ -3679,6 +3705,134 @@ def merge(self, other):
new_domain)


class EquivariantSubobjectConstructionFunctor(ConstructionFunctor):
r"""
Constructor for subobjects invariant or equivariant under given semigroup actions.
Let `S` be a semigroup that
- acts on a parent `X` as `s \cdot x` (``action``, ``side='left'``) or
- acts on `X` as `x \cdot s` (``action``, ``side='right'``),
and (possibly trivially)
- acts on `X` as `s * x` (``other_action``, ``other_side='left'``) or
- acts on `X` as `x * s` (``other_action``, ``other_side='right'``).
The `S`-equivariant subobject is the subobject
.. MATH::
X^S := \{x \in X : s \cdot x = s * x,\, \forall s \in S \}
when ``side = other_side = 'left'`` and mutatis mutandis for the other values
of ``side`` and ``other_side``.
When ``other_action`` is trivial, `X^S` is called the `S`-invariant subobject.
EXAMPLES:
Monoterm symmetries of a tensor, here only for matrices: row (index 0),
column (index 1); the order of the extra element 2 in a permutation determines
whether it is a symmetry or an antisymmetry::
sage: GSym01 = PermutationGroup([[(0,1),(2,),(3,)]]); GSym01
Permutation Group with generators [(0,1)]
sage: GASym01 = PermutationGroup([[(0,1),(2,3)]]); GASym01
Permutation Group with generators [(0,1)(2,3)]
sage: from sage.categories.action import Action
sage: from sage.structure.element import Matrix
sage: class TensorIndexAction(Action):
....: def _act_(self, g, x):
....: if isinstance(x, Matrix):
....: if g(0) == 1:
....: if g(2) == 2:
....: return x.transpose()
....: else:
....: return -x.transpose()
....: else:
....: return x
....: raise NotImplementedError
sage: M = matrix([[1, 2], [3, 4]]); M
[1 2]
[3 4]
sage: GSym01_action = TensorIndexAction(GSym01, M.parent())
sage: GASym01_action = TensorIndexAction(GASym01, M.parent())
sage: GSym01_action.act(GSym01.0, M)
[1 3]
[2 4]
sage: GASym01_action.act(GASym01.0, M)
[-1 -3]
[-2 -4]
sage: Sym01 = M.parent().invariant_module(GSym01, action=GSym01_action); Sym01
(Permutation Group with generators [(0,1)])-invariant submodule
of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
sage: list(Sym01.basis())
[B[0], B[1], B[2]]
sage: list(Sym01.basis().map(Sym01.lift))
[
[1 0] [0 1] [0 0]
[0 0], [1 0], [0 1]
]
sage: ASym01 = M.parent().invariant_module(GASym01, action=GASym01_action); ASym01
(Permutation Group with generators [(0,1)(2,3)])-invariant submodule
of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
sage: list(ASym01.basis())
[B[0]]
sage: list(ASym01.basis().map(ASym01.lift))
[
[ 0 1]
[-1 0]
]
sage: from sage.categories.pushout import pushout
sage: pushout(Sym01, QQ)
(Permutation Group with generators [(0,1)])-invariant submodule
of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
"""
def __init__(self, S, action=operator.mul, side='left',
other_action=None, other_side='left'):
"""
EXAMPLES::
sage: G = SymmetricGroup(3); G.rename('S3')
sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M')
sage: action = lambda g, x: M.term(g(x))
sage: I = M.invariant_module(G, action_on_basis=action); I
(S3)-invariant submodule of M
sage: I.construction()
(EquivariantSubobjectConstructionFunctor,
Representation of S3 indexed by {1, 2, 3} over Integer Ring)
"""
from sage.categories.sets_cat import Sets
super().__init__(Sets(), Sets())
self.S = S
self.action = action
self.side = side
self.other_action = other_action
self.other_side = other_side

def _apply_functor(self, X):
"""
Apply the functor to an object of ``self``'s domain.
TESTS::
sage: from sage.categories.pushout import EquivariantSubobjectConstructionFunctor
sage: M2 = MatrixSpace(QQ, 2); M2
Full MatrixSpace of 2 by 2 dense matrices over Rational Field
sage: F = EquivariantSubobjectConstructionFunctor(M2,
....: operator.mul, 'left',
....: operator.mul, 'right'); F
EquivariantSubobjectConstructionFunctor
sage: F(M2)
Traceback (most recent call last):
...
NotImplementedError: non-trivial other_action=<built-in function mul> is not implemented
"""
other_action = self.other_action
if other_action is not None:
raise NotImplementedError(f'non-trivial {other_action=} is not implemented')
# Currently only implemented for FiniteDimensionalModulesWithBasis
return X.invariant_module(self.S, action=self.action, side=self.side)


class BlackBoxConstructionFunctor(ConstructionFunctor):
"""
Construction functor obtained from any callable object.
Expand Down
25 changes: 23 additions & 2 deletions src/sage/modules/with_basis/invariant.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

# ****************************************************************************
# Copyright (C) 2021 Trevor K. Karn <karnx018 at umn.edu>
# Travis Scrimshaw
# 2021 Travis Scrimshaw
# 2022 Matthias Koeppe
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -262,9 +263,29 @@ def _invariant_map(g, x):
category=category,
*args, **kwargs)

def construction(self):
r"""
Return the functorial construction of ``self``.
EXAMPLES::
sage: G = CyclicPermutationGroup(3)
sage: R = G.regular_representation(); R
Left Regular Representation of Cyclic group of order 3 as a permutation group over Integer Ring
sage: I = R.invariant_module()
sage: I.construction()
(EquivariantSubobjectConstructionFunctor,
Left Regular Representation of Cyclic group of order 3 as a permutation group over Integer Ring)
"""
from sage.categories.pushout import EquivariantSubobjectConstructionFunctor
return (EquivariantSubobjectConstructionFunctor(self._semigroup,
self._action,
self._side),
self.ambient())

def _repr_(self):
r"""
Return a string representaion of ``self``.
Return a string representation of ``self``.
EXAMPLES::
Expand Down

0 comments on commit 7ebd052

Please sign in to comment.