Ticket #14347: trac_14347_group_cycle_index_series.patch

File trac_14347_group_cycle_index_series.patch, 14.7 KB (added by agd, 7 years ago)

Patch to implement group cycle index series

  • doc/en/reference/combinat/species.rst

    # HG changeset patch
    # User Andrew Gainer-Dewar <andrew dot gainer dot dewar at gmail.com>
    # Date 1367082850 18000
    # Node ID dd0f57e5a58dab5d628eee49d40f0220fd9c0e98
    # Parent  d0943a747f6dd975386c499f666609c9b1e3d8c5
    Trac 14347: Implement group cycle index series
    
    diff --git a/doc/en/reference/combinat/species.rst b/doc/en/reference/combinat/species.rst
    a b  
    55Power Series
    66------------
    77
    8 .. toctree:: 
     8.. toctree::
    99   :maxdepth: 2
    1010
    1111   ../sage/combinat/species/stream
    1212   ../sage/combinat/species/series_order
    1313   ../sage/combinat/species/series
    1414   ../sage/combinat/species/generating_series
     15   ../sage/combinat/species/group_cycle_index_series
    1516
    1617Basic Species
    1718-------------
     
    3637
    3738.. toctree::
    3839   :maxdepth: 2
    39    
     40
    4041   ../sage/combinat/species/sum_species
    4142   ../sage/combinat/species/product_species
    4243   ../sage/combinat/species/composition_species
  • new file sage/combinat/species/group_cycle_index_series.py

    diff --git a/sage/combinat/species/group_cycle_index_series.py b/sage/combinat/species/group_cycle_index_series.py
    new file mode 100644
    - +  
     1"""
     2Group Cycle Indices
     3
     4This file implements the group cycle indices of Henderson and Gainer-Dewar.
     5
     6For a group `\Gamma` and a ring `R`, a `\Gamma`-cycle index over `R`
     7is an element of the free module over the ring of cycle index series
     8over `R` with basis `\Gamma`.
     9
     10In other words, a `\Gamma`-cycle index series over `R` is a formal sum
     11
     12.. MATH::
     13
     14    F = \\sum_{\gamma \\in \Gamma} F[\gamma] \cdot \gamma,
     15
     16where each coefficient `F[\gamma]` is a cycle index series over `R`.
     17
     18These objects are of interest because they can be used to enumerate `\Gamma`-species;
     19they serve the same role in that theory as ordinary cycle indices do for classical
     20species.
     21
     22AUTHORS:
     23
     24- Andrew Gainer-Dewar (2013): initial version
     25
     26EXAMPLES::
     27
     28    sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     29    sage: GCISR = GroupCycleIndexSeriesRing(SymmetricGroup(4))
     30    sage: loads(dumps(GCISR))
     31    Ring of (Symmetric group of order 4! as a permutation group)-Cycle Index Series over Rational Field
     32
     33"""
     34#*****************************************************************************
     35#       Copyright (C) 2013 Andrew Gainer-Dewar <andrew.gainer.dewar@gmail.com>
     36#
     37#  Distributed under the terms of the GNU General Public License (GPL)
     38#  as published by the Free Software Foundation; either version 2 of
     39#  the License, or (at your option) any later version.
     40#                  http://www.gnu.org/licenses/
     41#*****************************************************************************
     42from sage.rings.rational_field import RationalField
     43from sage.misc.cachefunc import cached_function
     44from sage.combinat.free_module import CombinatorialFreeModule,CombinatorialFreeModuleElement
     45
     46@cached_function
     47def GroupCycleIndexSeriesRing(G, R = RationalField()):
     48    """
     49    Return the ring of group cycle index series.
     50
     51    EXAMPLES::
     52
     53        sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     54        sage: GCISR = GroupCycleIndexSeriesRing(SymmetricGroup(4)); GCISR
     55        Ring of (Symmetric group of order 4! as a permutation group)-Cycle Index Series over Rational Field
     56
     57    TESTS: We test to make sure that caching works. ::
     58
     59        sage: GCISR is GroupCycleIndexSeriesRing(SymmetricGroup(4))
     60        True
     61    """
     62    return GroupCycleIndexSeriesRing_class(G, R)
     63
     64class GroupCycleIndexSeriesRing_class(CombinatorialFreeModule):
     65    def __init__(self, G, R = RationalField()):
     66        """
     67        EXAMPLES::
     68
     69            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     70            sage: GCISR = GroupCycleIndexSeriesRing(SymmetricGroup(4)); GCISR
     71            Ring of (Symmetric group of order 4! as a permutation group)-Cycle Index Series over Rational Field
     72            sage: GCISR == loads(dumps(GCISR))
     73            True
     74        """
     75        from sage.combinat.species.generating_series import CycleIndexSeriesRing
     76        from sage.categories.algebras_with_basis import AlgebrasWithBasis
     77       
     78        self._coeff_ring = R
     79        CISR = CycleIndexSeriesRing(R)
     80        self._cisr = CISR
     81        self._group = G
     82
     83        CombinatorialFreeModule.__init__(self, CISR, G, element_class = GroupCycleIndexSeries, category = AlgebrasWithBasis(CISR), prefix = 'G')
     84   
     85    def product_on_basis(self, left, right):
     86        """
     87        Return the product of two basis elements ``left`` and ``right`` of ``self``.
     88       
     89        Multiplication of `\Gamma`-cycle indices is defined componentwise.
     90        That is, if `F` and `G` are two `\Gamma`-cycle indices, then `(F \cdot G) [\gamma] = F [\gamma] \cdot G [\gamma]`,
     91        where the multiplication the right-hand side is ordinary multiplication of cycle indices.
     92
     93        This is handled in Sage by defining multiplication on the basis of monomials induced
     94        by elements of `\Gamma`.
     95
     96        EXAMPLES::
     97
     98            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     99            sage: GCISR = GroupCycleIndexSeriesRing(SymmetricGroup(4))
     100            sage: e = SymmetricGroup(4).identity()
     101            sage: t = SymmetricGroup(4)([4,3,2,1])
     102            sage: GCISR.product_on_basis(t,t) == GCISR(t)
     103            True
     104       
     105        """
     106        if left == right:
     107            return self.monomial(left)
     108        else:
     109            return self(0)
     110
     111    def one(self):
     112        """
     113        Return the multiplicative identity element of this algebra.
     114
     115        EXAMPLES::
     116
     117            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     118            sage: GCISR = GroupCycleIndexSeriesRing(SymmetricGroup(2))
     119            sage: GCISR.one()
     120            p[]*G[()] + p[]*G[(1,2)]
     121
     122        """
     123        basis = self.basis()
     124        return self.sum(basis[k] for k in basis.keys())
     125
     126    def _repr_(self):
     127        """
     128        EXAMPLES::
     129
     130            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     131            sage: GroupCycleIndexSeriesRing(SymmetricGroup(4))
     132            Ring of (Symmetric group of order 4! as a permutation group)-Cycle Index Series over Rational Field
     133        """
     134        return "Ring of (%s)-Cycle Index Series over %s" %(self._group, self._coeff_ring)
     135
     136class GroupCycleIndexSeries(CombinatorialFreeModuleElement):
     137    """
     138   
     139    EXAMPLES::
     140
     141        sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     142        sage: GCISR = GroupCycleIndexSeriesRing(SymmetricGroup(4))
     143        sage: GCISR.an_element()
     144        p[]*G[()] + 2*p[]*G[(3,4)] + 3*p[]*G[(2,3)] + p[]*G[(1,2,3,4)]
     145   
     146    """
     147   
     148    def quotient(self):
     149        """
     150        Return the quotient of this group cycle index.
     151
     152        This is defined to be the ordinary cycle index `F / \Gamma` obtained from a
     153        `\Gamma`-cycle index `F` by:
     154
     155        .. MATH::
     156            F / \Gamma = \\frac{1}{\\lvert \Gamma \\rvert} \sum_{\gamma \in \Gamma} F [\gamma].
     157
     158        By [AGdiss]_, if `F` is the `\Gamma`-cycle index of a `\Gamma`-species, `F / \Gamma` is the ordinary
     159        cycle index of orbits of structures under the action of `\Gamma`.
     160
     161        EXAMPLES::
     162
     163            sage: S4 = SymmetricGroup(4)
     164            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     165            sage: GCISR = GroupCycleIndexSeriesRing(S4)
     166            sage: GCISR.an_element()
     167            p[]*G[()] + 2*p[]*G[(3,4)] + 3*p[]*G[(2,3)] + p[]*G[(1,2,3,4)]
     168            sage: GCISR.an_element().quotient().coefficients(4)
     169            [7/24*p[], 0, 0, 0]
     170
     171        REFERENCES:
     172
     173        .. [AGdiss] Andrew Gainer. "`\Gamma`-species, quotients, and graph enumeration". Ph.D. diss. Brandeis University, 2012.
     174        """
     175        return 1/self.parent()._group.cardinality() * sum(self.coefficients())
     176
     177    def composition(self, y, test_constant_term_is_zero = True):
     178        """
     179        Return the group-cycle-index plethysm of ``self`` with ``y``.
     180       
     181        Plethysm of group cycle index series is defined by a sort of 'mixing' operation in [Hend]_:
     182
     183        .. MATH::
     184            (F \circ G) [\gamma] (p_{1}, p_{2}, p_{3}, \dots) =
     185            F [\gamma] \left( G [\gamma] (p_{1}, p_{2}, p_{3}, \dots),
     186            G [\gamma^{2}] (p_{2}, p_{4}, p_{6}, \dots), \dots \\right).
     187
     188        It is shown in [Hend]_ that this operation on $\Gamma$-cycle indices corresponds to the
     189        'composition' operation on $\Gamma$-species.
     190
     191        It is required that each of the components of `y` has zero constant term.
     192        However, testing this may not be possible if `y` is of an exotic class.
     193        Set ``test_constant_term_is_zero`` to ``False`` to suppress any testing.
     194
     195        EXAMPLES::
     196
     197            sage: S2 = SymmetricGroup(2)
     198            sage: Eplus = sage.combinat.species.set_species.SetSpecies(min=1).cycle_index_series()
     199            sage: E = sage.combinat.species.set_species.SetSpecies().cycle_index_series()
     200            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     201            sage: GCISR = GroupCycleIndexSeriesRing(S2)
     202            sage: e = S2.identity()
     203            sage: t = S2.gen()
     204            sage: GCIS = GCISR(e)*Eplus*E + GCISR(t)*Eplus
     205            sage: example = GCIS(GCIS)
     206            sage: example[e].coefficients(4)
     207            [0, p[1], 3*p[1, 1] + p[2], 41/6*p[1, 1, 1] + 9/2*p[2, 1] + 2/3*p[3]]
     208            sage: example[t].coefficients(4)
     209            [0, p[1], p[1, 1] + p[2], 5/6*p[1, 1, 1] + 3/2*p[2, 1] + 2/3*p[3]]
     210
     211        REFERENCES:
     212
     213        .. [Hend] Anthony Henderson. "Species over a finite field". J. Algebraic Combin., 21(2):147-161, 2005.
     214        """
     215        from sage.combinat.species.stream import Stream,_integers_from
     216        from sage.misc.misc_c import prod
     217
     218        assert self.parent() == y.parent()
     219
     220        parent = self.parent()
     221        cisr = parent._cisr
     222        sfa = cisr.base_ring()
     223        group = parent._group
     224
     225        if test_constant_term_is_zero:
     226            for ycis in y.coefficients():
     227                assert ycis.coefficient(0) == 0
     228
     229        ypowers = { g: Stream(y[g]._power_gen()) for g in group }
     230
     231        def monomial_composer( partition, g ):
     232            partmults = ( (i+1, j) for i,j in enumerate(partition.to_exp()) if j != 0 )
     233            ycontrib = lambda part, mult: ypowers[g**part][mult-1].stretch(part)
     234            res = cisr.one()*prod(ycontrib(part, mult) for part,mult in partmults)
     235            return res
     236       
     237        def term_map( term, g ):
     238            if test_constant_term_is_zero and term == 0:
     239                return cisr.zero()
     240            else:
     241                res = sum(coeff*monomial_composer(part, g) for part,coeff in term)
     242                return res
     243
     244        def component_builder( g ):
     245            if test_constant_term_is_zero and self[g] == 0:
     246                res = cisr.zero()
     247            #elif g == group.identity(): #Save time by using existing CIS composition code
     248            #    res = self[g](y[g])
     249            else:
     250                res = cisr.sum_generator(term_map(self[g].coefficient(i), g) for i in _integers_from(0))
     251            return res
     252
     253        components_dict = { g: component_builder(g) for g in group }
     254        res = parent._from_dict(components_dict)
     255        return res
     256
     257    __call__ = composition
     258
     259    def derivative(self):
     260        """
     261        Return the cycle-index derivative of ``self``.
     262       
     263        Differentiation of group cycle index series is defined termwise:
     264
     265        .. MATH::
     266            (F')[\gamma] = (F [\gamma])'
     267
     268        EXAMPLES::
     269
     270            sage: S4 = SymmetricGroup(4)
     271            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     272            sage: GCISR = GroupCycleIndexSeriesRing(S4)
     273            sage: G = GCISR(SymmetricGroup(4).an_element())*sage.combinat.species.library.SimpleGraphSpecies().cycle_index_series()
     274            sage: G.derivative()[SymmetricGroup(4).an_element()].coefficients(4)
     275            [p[1], 2*p[1, 1] + 2*p[2], 4*p[1, 1, 1] + 6*p[2, 1] + 2*p[3], 32/3*p[1, 1, 1, 1] + 16*p[2, 1, 1] + 8*p[2, 2] + 16/3*p[3, 1] + 4*p[4]]
     276
     277        """
     278        return self.map_coefficients(lambda x: x.derivative())
     279
     280    def define(self, x):
     281        """
     282        Set ``self`` equal to ``x``, possibly recursively.
     283
     284        EXAMPLES:
     285
     286        Consider the `\mathfrak{S}_{2}`-species `\mathcal{A}` of rooted ordered trees,
     287        where the action of the nontrivial element of `\mathfrak{S}_{2}` inverts all the
     288        orderings on subtrees.
     289
     290        Then we have the recursive functional equation
     291
     292        .. MATH::
     293            \mathcal{A} = X \cdot \mathcal{L} (\mathcal{A})
     294
     295        where `X` is the trivial singleton `\mathfrak{S}_{2}`-species and `\mathcal{L}` is the
     296        `\mathfrak{S}_{2}`-species of linear orderings with the reversal action
     297        described previously.
     298
     299        To enumerate this species using Sage, we first need to set up an environment::
     300       
     301            sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing
     302            sage: from sage.combinat.species.group_cycle_index_series import GroupCycleIndexSeriesRing
     303            sage: from sage.combinat.species.stream import _integers_from
     304            sage: S2 = SymmetricGroup(2)
     305            sage: GCISR = GroupCycleIndexSeriesRing(S2)
     306            sage: CISR = CycleIndexSeriesRing(QQ)
     307            sage: e,t = GCISR.basis().keys()
     308
     309        Next, we define the `\mathfrak{S}_{2}`-species `X` and `\mathcal{L}`.
     310        `X` is straightforward, since the `\mathfrak{S}_{2}`-action is trivial,
     311        but `\mathcal{L}` requires some work. ::
     312       
     313            sage: X = GCISR(species.SingletonSpecies().cycle_index_series())
     314            sage: def Ltgen():
     315            ....:     p = SymmetricFunctions(QQ).power()
     316            ....:     for n in _integers_from(0):
     317            ....:         yield p([2]*n)
     318            ....:         yield p([2]*n+[1])
     319            sage: L = GCISR(e)*species.LinearOrderSpecies().cycle_index_series() + GCISR(t)*CISR(Ltgen())
     320
     321        Finally, we can use ``define`` to set up `\mathcal{A}`::
     322       
     323            sage: A = GCISR(e)*0 + GCISR(t)*0
     324            sage: A.define(X*L(A))
     325            sage: A.quotient().isotype_generating_series().counts(8)
     326            [0, 1, 1, 2, 4, 10, 26, 76]
     327
     328        (Compare :oeis:`A007123`.)
     329       
     330        """       
     331       
     332        keys = self.parent().basis().keys()
     333        for key in keys:
     334            self[key].define(x[key])