Ticket #15029: trac_15029_similarity_local_rings-ap.patch

File trac_15029_similarity_local_rings-ap.patch, 34.4 KB (added by amri, 6 years ago)
  • doc/en/reference/combinat/index.rst

    # HG changeset patch
    # User Amritanshu Prasad <amri@imsc.res.in>
    # Date 1376108154 -19800
    # Node ID 900bf6ea6cd7c69a56bff118ba63071a4b4f9e78
    # Parent  d8c6493924b2aa6a7d299cb73b752ede49c16845
    Implemented similarity classes over principal ideal local rings
    [mq]: trac_15029_similarity_local_rings-ap.patch
    
    diff --git a/doc/en/reference/combinat/index.rst b/doc/en/reference/combinat/index.rst
    a b  
    5454   sage/combinat/ordered_tree
    5555   sage/combinat/binary_tree
    5656   sage/combinat/similarity_class_type
     57   sage/combinat/similarity_over_rings_of_length_two
    5758   sage/combinat/skew_partition
    5859   sage/combinat/subset
    5960   sage/combinat/subsets_pairwise
  • new file sage/combinat/similarity_over_rings_of_length_two.py

    diff --git a/sage/combinat/similarity_over_rings_of_length_two.py b/sage/combinat/similarity_over_rings_of_length_two.py
    new file mode 100644
    - +  
     1r"""
     2Similarity classes of matrcies over a principal ideal local ring of length two
     3
     4Implements a partial algorithm for formal counting of similarity classes of
     5matrices with entries in a finite principal ideal local ring of length two.
     6
     7Let `R` be a principal ideal local ring of lengh two with residue field of order
     8`q`. In [PSS13]_ it is shown that the problem of computing the cardinalities of
     9similarity classes in `M_n(R)` or `GL_n(R)` can be reduced to calculating the
     10number of orbits for the action of the group `G_\lambda :=
     11Z_{GL_m(\mathbf{F}_q)}N_\lambda` on `E_\lambda :=
     12\mathrm{Ext}^1_{\mathbf{F}_q[t]}(\mathbf{F}_q^m \mathbf{F}_q^m)`, where `t` acts
     13on `\mathbf{F}_q^m` by a nilpotent matrix whose Jordan block sizes are given by
     14a partition `\lambda`  of `m` for every partition `\lambda` of an integer
     15`m\leq n`.
     16
     17Calculations are done as formal polynomials in `q`.
     18
     19REFERENCES:
     20
     21.. [PSS13] Amritanshu Prasad, Pooja Singla, and Steven Spallone. Similarity of
     22   matrices over local rings of length two. http://arxiv.org/abs/1212.6157
     23
     24AUTHOR:
     25
     26- Amritanshu Prasad (2013-08-10)
     27
     28"""
     29#*****************************************************************************
     30#       Copyright (C) 2013 Amritanshu Prasad <amri@imsc.res.in>
     31#
     32#  Distributed under the terms of the GNU General Public License (GPL)
     33#
     34#    This code is distributed in the hope that it will be useful, but
     35#    WITHOUT ANY WARRANTY; without even the implied warranty of
     36#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     37#    General Public License for more details.
     38#
     39#  The full text of the GPL is available at:
     40#
     41#                  http://www.gnu.org/licenses/
     42#*****************************************************************************
     43
     44from sage.combinat.cartesian_product import CartesianProduct
     45from sage.combinat.misc import IterableFunctionCall
     46from sage.combinat.partition import Partitions, Partition
     47from sage.combinat.similarity_class_type import SimilarityClassType, SimilarityClassTypes, order_of_general_linear_group, PrimarySimilarityClassType
     48from sage.misc.misc import prod
     49from sage.rings.all import QQ
     50from sage.rings.fraction_field import FractionField
     51from sage.structure.sage_object import SageObject
     52
     53def dictionary_from_generator(gen):
     54    """
     55    Given a generator for a list of pairs `(c,f)` construct a dictionary whose
     56    keys are the distinct values for `c` and whose value at `c` is the sum of
     57    `f` over all pairs of the form `(c',f)` such that `c=c'`.
     58
     59    EXAMPLES::
     60
     61        sage: from sage.combinat.similarity_over_rings_of_length_two import dictionary_from_generator
     62        sage: dictionary_from_generator(((floor(x/2), x) for x in xrange(10)))
     63        {0: 1, 1: 5, 2: 9, 3: 13, 4: 17}
     64
     65    It also works with lists::
     66
     67        sage: dictionary_from_generator([(floor(x/2),x) for x in range(10)])
     68        {0: 1, 1: 5, 2: 9, 3: 13, 4: 17}
     69
     70    .. NOTE::
     71
     72        Since the generator is first converted to a list, memory usage could be
     73        high.
     74    """
     75    L = list(gen)
     76    setofkeys = list(set([item[0] for item in L]))
     77    return dict([(key, sum([entry[1] for entry in filter(lambda pair: pair[0] == key, L)])) for key in setofkeys])
     78
     79class MatrixSimilarityClasses(SageObject):
     80    r"""
     81    Class of similarity classes of `n\times n` matrices with entries in a finite
     82    field of order `q`.
     83    """
     84    def __init__(self, n, q = None):
     85        """
     86        Initialize ``self``,
     87
     88        EXAMPLES::
     89
     90            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     91            sage: M = MatrixSimilarityClasses(2)
     92            sage: TestSuite(M).run()
     93        """
     94        self._n = n
     95        if q is None:
     96            q = FractionField(QQ['q']).gen()
     97        self._q = q
     98
     99    def __repr__(self):
     100        """
     101        Return string representation of self.
     102
     103        EXAMPLES::
     104
     105            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     106            sage: MatrixSimilarityClasses(2)
     107            Similarity classes of 2 by 2 matrices over a finite field of order q
     108        """
     109        return "Similarity classes of %s by %s matrices over a finite field of order %s"%(self.n(), self.n(), self.q())
     110
     111    def __eq__(self, other):
     112        """
     113        Test if ``self`` is equal to ``other``.
     114
     115        EXAMPLES::
     116
     117            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     118            sage: M1 = MatrixSimilarityClasses(2)
     119            sage: M2 = MatrixSimilarityClasses(3)
     120            sage: M3 = MatrixSimilarityClasses(2, q = 2)
     121            sage: M1 == M2
     122            False
     123            sage: M1 == M3
     124            False
     125            sage: M1 == M1
     126            True
     127        """
     128        return self.q() == other.q() and self.n() == other.n()
     129
     130    def n(self):
     131        """
     132        Return the size of ``self``.
     133
     134        EXAMPLES::
     135
     136            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     137            sage: MatrixSimilarityClasses(2).n()
     138            2
     139        """
     140        return self._n
     141
     142    def q(self):
     143        """
     144        Return the cardinality of field of ``self``.
     145
     146            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     147            sage: MatrixSimilarityClasses(2).q()
     148            q
     149            sage: MatrixSimilarityClasses(2, q = 2).q()
     150            2
     151        """
     152        return self._q
     153
     154    def classes(self, invertible = False):
     155        """
     156        Return the total number of similarity classes.
     157
     158        INPUT:
     159
     160        - ``invertible`` -- return number of invertible classes if set.
     161
     162        OUTPUT:
     163
     164        a polynomial in ``q``.
     165
     166        EXAMPLES::
     167
     168            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     169            sage: C = MatrixSimilarityClasses(2)
     170            sage: C.classes()
     171            q^2 + q
     172            sage: C.classes(invertible = True)
     173            q^2 - 1
     174        """
     175        n = self.n()
     176        q = self.q()
     177        if n == 0:
     178            return 1
     179        if invertible:
     180            return sum([q**max(la)*((1-q**(-1))**map(lambda x: x>0, la.to_exp()).count(True)) for la in Partitions(n)])
     181        else:
     182            return sum([q**max(la) for la in Partitions(n)])
     183
     184    def class_centralizers(self, invertible = False):
     185        """
     186        Generate a sequence of pairs `(c, f)`, where for each `c`, there are `f`
     187        classes of centralizer cardinality `c`. Both `c` and `f` are polynomials
     188        in `q`.
     189
     190        .. NOTE::
     191
     192            The same value for `c` may be repeated more than once in pairs `(c,f)`,
     193            in which case the total number of classes of cardinality `c` is the sum
     194            of `f` over all pairs of the form `(c,f)`.
     195
     196        EXAMPLES::
     197
     198            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses
     199            sage: C = MatrixSimilarityClasses(2)
     200            sage: list(C.class_centralizers())
     201            [(q^2 - 2*q + 1, 1/2*q^2 - 1/2*q),
     202            (q^2 - q, q),
     203            (q^4 - q^3 - q^2 + q, q),
     204            (q^2 - 1, 1/2*q^2 - 1/2*q)]
     205            sage: list(C.class_centralizers(invertible = True))
     206            [(q^2 - 2*q + 1, 1/2*q^2 - 3/2*q + 1),
     207            (q^2 - q, q - 1),
     208            (q^4 - q^3 - q^2 + q, q - 1),
     209            (q^2 - 1, 1/2*q^2 - 1/2*q)]
     210        """
     211        q = self.q()
     212        n = self.n()
     213        for tau in SimilarityClassTypes(n):
     214            yield (tau.centralizer_group_card(q = q), tau.number_of_classes(invertible = invertible, q = q))
     215
     216class ExtOrbitClasses(SageObject):
     217    r"""
     218    The class of orbits for the action of the centralizer group
     219    `\mathrm{Aut}_{\mathbf{F}_q[t]}\mathbf{F}_q^n` on
     220    `\mathrm{Ext}^1_{\mathbf{F}_q[t]} (\mathbf{Fq}^n, \mathbf{Fq}^n)`, where the
     221    action of `t` on `\mathbf{F}_q^n` is determined by ``data``.
     222
     223    If ``data`` is a partition, then `t` acts as a nilpotent matrix whose
     224    Jordan block sizes are the parts of this partition.
     225
     226    If ``data`` is a primary similarity class type, then `t` acts as a primary
     227    matrix of this type.
     228
     229    If ``data`` is a similarity class type, then `t` acts a matrix of this
     230    type.
     231
     232    """
     233    def __init__(self, data, q = None):
     234        """
     235        Initialize ``self``
     236
     237        EXAMPLES::
     238
     239            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     240            sage: E = ExtOrbitClasses(Partition([2, 1]))
     241            sage: TestSuite(E).run()
     242
     243        There are three cases handled by the constructor: partitions, primary
     244        similarity class types, and similarity class types. Depending on the
     245        input ``data``, the correct case is identified.
     246
     247        EXAMPLES::
     248
     249            sage: ExtOrbitClasses(Partition([2, 1]))
     250            ExtOrbitClasses for Partition [2, 1]
     251            sage: ExtOrbitClasses(PrimarySimilarityClassType(2, [2, 1]))
     252            ExtOrbitClasses for PrimarySimilarityClassType [2, [2, 1]]
     253            sage: ExtOrbitClasses(SimilarityClassType([[2, [2, 1]]]))
     254            ExtOrbitClasses for SimilarityClassType [[2, [2, 1]]]
     255
     256            The constructor tries to construct the correct input from data provided::
     257
     258            sage: ExtOrbitClasses([2, 1])
     259            ExtOrbitClasses for Partition [2, 1]
     260            sage: ExtOrbitClasses((2, [2, 1]))
     261            ExtOrbitClasses for PrimarySimilarityClassType [2, [2, 1]]
     262            sage: ExtOrbitClasses([[2, [2, 1]]])
     263            ExtOrbitClasses for SimilarityClassType [[2, [2, 1]]]
     264        """
     265        if q is None:
     266            q = FractionField(QQ['q']).gen()
     267        self._q = q
     268        if isinstance(data, SimilarityClassType):
     269            self._case = 'sim'
     270            self._tau = data
     271        elif isinstance(data, PrimarySimilarityClassType):
     272            self._case = 'pri'
     273            self._tau = data
     274        elif isinstance(data, Partition):
     275            self._case = 'par'
     276            self._tau = data
     277        else:
     278            try:
     279                data = Partition(data)
     280                self._case = 'par'
     281                self._tau = data
     282            except (TypeError, ValueError):
     283                try:
     284                    data = SimilarityClassType(data)
     285                    self._case = 'sim'
     286                    self._tau = data
     287                except (TypeError, ValueError):
     288                    try:
     289                        data = PrimarySimilarityClassType(*data)
     290                        self._case = 'pri'
     291                        self._tau = data
     292                    except (TypeError, ValueError):
     293                        raise ValueError("Expected a Partition, a SimiliarityClassType or a PrimarySimilarityClassType, got a %s"%(type(data)))
     294
     295    def __repr__(self):
     296        """
     297        Return a string representation of ``self``.
     298
     299        EXAMPLES::
     300
     301            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     302            sage: ExtOrbitClasses(Partition([2, 1]))
     303            ExtOrbitClasses for Partition [2, 1]
     304        """
     305        return "ExtOrbitClasses for %s %s"%(self.case(), self._tau)
     306
     307    def __eq__(self, other):
     308        """
     309        Test equality of ``self`` and ``other``
     310
     311        EXAMPLES::
     312
     313            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     314            sage: E1 = ExtOrbitClasses(Partition([2, 1]))
     315            sage: E2 = ExtOrbitClasses(Partition([2, 1, 1]))
     316            sage: E1 == E2
     317            False
     318            sage: E3 = ExtOrbitClasses(PrimarySimilarityClassType(2, [2, 1]))
     319            sage: E2 == E3
     320            False
     321            sage: E4 = ExtOrbitClasses(SimilarityClassType([[2, [2, 1]]]))
     322            sage: E3 == E4
     323            False
     324            sage: E5 = ExtOrbitClasses([2, 1])
     325            sage: E1 == E5
     326            True
     327        """
     328        return self._case == other._case and self._tau == other._tau
     329
     330    def case(self):
     331        """
     332        Return the case of ``self``.
     333
     334        This can be "Partition", "PrimarySimilarityClassType", or "SimilarityClassType"
     335
     336        EXAMPLES::
     337
     338            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     339            sage: ExtOrbitClasses(Partition([2, 1])).case()
     340            'Partition'
     341            sage: ExtOrbitClasses(PrimarySimilarityClassType(2, [2, 1])).case()
     342            'PrimarySimilarityClassType'
     343            sage: ExtOrbitClasses(SimilarityClassType([[2, [2, 1]]])).case()
     344            'SimilarityClassType'
     345        """
     346        if self._case == 'par':
     347            return "Partition"
     348        if self._case == 'pri':
     349            return "PrimarySimilarityClassType"
     350        if self._case == 'sim':
     351            return "SimilarityClassType"
     352
     353
     354    def q(self):
     355        """
     356        Return the order of the residue field of ``self``
     357
     358        EXAMPLES::
     359
     360            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     361            sage: ExtOrbitClasses(Partition([2, 1])).q()
     362            q
     363            sage: ExtOrbitClasses(Partition([2, 1]), q =2).q()
     364            2
     365        """
     366
     367        return self._q
     368
     369    def partition(self):
     370        """
     371        Return the partition associated to ``self``.
     372
     373        EXAMPLES::
     374
     375            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     376            sage: ExtOrbitClasses(Partition([2, 1])).partition()
     377            [2, 1]
     378        """
     379        if self._case == 'par':
     380            return self._tau
     381        else:
     382            raise AttributeError("%s has no attribute 'partition'"%(self))
     383
     384    def primary_similarity_class_type(self):
     385        """
     386        Return primary similarity class type associated to ``self``.
     387
     388            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     389            sage: ExtOrbitClasses(PrimarySimilarityClassType(2, [2, 1])).primary_similarity_class_type()
     390            [2, [2, 1]]
     391        """
     392        if self._case == 'pri':
     393            return self._tau
     394        else:
     395            raise AttributeError("%s has no attribute 'primary_similarity_class_type'"%(self))
     396
     397    def similarity_class_type(self):
     398        """
     399        Return similarity class type associated to ``self``.
     400
     401            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     402            sage: ExtOrbitClasses(SimilarityClassType([[2, [2, 1]]])).similarity_class_type()
     403            [[2, [2, 1]]]
     404        """
     405        if self._case == 'sim':
     406            return self._tau
     407        else:
     408            raise AttributeError("%s has no attribute 'similarity_class_type'"%(self))
     409
     410    def orbits(self, selftranspose = False):
     411        r"""
     412        Return the number of orbits in ``self``.
     413
     414        INPUT:
     415
     416        - ``selftranspose`` -- count only selftranspose classes if set.
     417
     418        EXAMPLES::
     419
     420            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     421            sage: ExtOrbitClasses(Partition([6, 1])).orbits()
     422            q^7 + q^6 + q^5
     423            sage: ExtOrbitClasses(Partition([6, 1])).orbits(selftranspose = True)
     424            q^7 + q^6 - q^5
     425            sage: ExtOrbitClasses(Partition([6, 1, 1])).orbits()
     426            q^8 + 2*q^7 + 2*q^6 + 2*q^5
     427            sage: ExtOrbitClasses(Partition([6, 1, 1])).orbits(selftranspose = True)
     428            q^8 + 2*q^7
     429            sage: ExtOrbitClasses(Partition([2, 2])).orbits()
     430            q^4 + q^3 + q^2
     431            sage: ExtOrbitClasses(Partition([2, 2, 2])).orbits()
     432            q^6 + q^5 + 2*q^4 + q^3 + 2*q^2
     433            sage: ExtOrbitClasses(Partition([2, 2, 2, 2])).orbits()
     434            q^8 + q^7 + 3*q^6 + 3*q^5 + 5*q^4 + 3*q^3 + 3*q^2
     435            sage: ExtOrbitClasses(Partition([2, 2])).orbits(selftranspose = True)
     436            q^4 + q^3 + q^2
     437            sage: ExtOrbitClasses(Partition([2, 2, 2])).orbits(selftranspose = True)
     438            q^6 + q^5 + 2*q^4 + q^3
     439            sage: ExtOrbitClasses(Partition([2, 2, 2, 2])).orbits(selftranspose = True)
     440            q^8 + q^7 + 3*q^6 + 3*q^5 + 3*q^4 + q^3 + q^2
     441
     442        This function is not implemented for partitions other than those considered
     443        in [PSS13]_, namely partitions `(1^n)`, `(n)` for `n\geq 0`, `(m+1, 1)`,
     444        `(m+1, 1, 1)` for `m\geq 1`, and `(2^n)` for `0\leq n\leq 4`::
     445
     446            sage: ExtOrbitClasses(Partition([2, 2, 1, 1])).orbits(selftranspose = True)
     447            Traceback (most recent call last):
     448            ...
     449            ValueError: partition [2, 2, 1, 1] not implemented for ExtOrbitClasses.orbits
     450
     451        It works for a PrimarySimilarityClassType::
     452
     453            sage: ExtOrbitClasses(PrimarySimilarityClassType(2, [2, 1])).orbits()
     454            q^6 + q^4 + q^2
     455
     456        And also for a SimilarityClassType::
     457
     458            sage: ExtOrbitClasses(SimilarityClassType([[2, [2, 1]], [1, [1]]])).orbits()
     459            q^7 + q^5 + q^3
     460        """
     461        q = self.q()
     462        if self._case == 'par':
     463            la = self._tau
     464            if la.size() == 0:
     465                return q.parent()(1)
     466            if max(la) == 1:
     467                return MatrixSimilarityClasses(len(la), q = q).classes()
     468            elif len(la) == 1:
     469                return q**la.size()
     470            elif len(la) == 2 and list(la).count(1) == 1: # see Table 3
     471                m = max(la) - 1
     472                if selftranspose:
     473                    return q**(m + 2) + q**(m + 1) - q**m
     474                else:
     475                    return q**(m + 2) + q**(m + 1) + q**m
     476            elif len(la) == 3 and list(la).count(1) == 2: # see Table 4
     477                m = max(la) - 1
     478                if not selftranspose:
     479                    return q**m*(q**3 + 2*q**2 + 2*q + 2)
     480                else:
     481                    return q**m*(q**3 + 2*q**2)
     482            elif min(la) == 2 and max(la) == 2:
     483                return MatrixSimilarityClasses2(len(la), q = q).classes(selftranspose = selftranspose)
     484            else:
     485                raise ValueError('partition %s not implemented for ExtOrbitClasses.orbits'%(la))
     486        elif self._case == 'pri':
     487            tau = self._tau
     488            return ExtOrbitClasses(tau.partition(), q = q).orbits(selftranspose = selftranspose).substitute(q = q**tau.degree())
     489        elif self._case == 'sim':
     490            tau = self._tau
     491            return prod([ExtOrbitClasses(PT, q = q).orbits(selftranspose = selftranspose) for PT in tau])
     492
     493    def orbit_centralizers(self, selftranspose = False):
     494        """
     495        Generate a list of pairs `(c, f)` where for each `c` there are `f`
     496        orbits in ``self`` with centralizer of cardinality `c`.
     497
     498        .. NOTE::
     499
     500            The same calue of `c` may occur for multiple pairs `(c, f)` in this
     501            list. If this happens, the number of orbits with centralizer of
     502            cardinality `c` is the sum of `f` over all pairs `(c',f)` with
     503            `c'=c`. To get this number use the method
     504            ``orbit_centralizers_dict``.
     505
     506        EXAMPLES::
     507
     508            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     509            sage: list(ExtOrbitClasses(Partition([6, 1])).orbit_centralizers())
     510            [(q^9 - 2*q^8 + q^7, q^6),
     511            (q^7 - 2*q^6 + q^5, q^7 - q^6),
     512            (q^7 - q^6, q^6 + q^5)]
     513            sage: list(ExtOrbitClasses(Partition([6, 1])).orbit_centralizers(selftranspose = True))
     514            [(q^9 - 2*q^8 + q^7, q^6),
     515            (q^7 - 2*q^6 + q^5, q^7 - q^6),
     516            (q^7 - q^6, q^6 - q^5)]
     517            sage: list(ExtOrbitClasses(Partition([6, 1, 1])).orbit_centralizers())
     518            [(q^12 - 3*q^11 + 3*q^10 - q^9, 1/2*q^7 - 1/2*q^6),
     519            (q^8 - 3*q^7 + 3*q^6 - q^5, 1/2*q^8 - q^7 + 1/2*q^6),
     520            (q^12 - 2*q^11 + q^10, q^6),
     521            (q^8 - 2*q^7 + q^6, q^7 - q^6),
     522            (q^14 - 2*q^13 + 2*q^11 - q^10, q^6),
     523            (q^10 - 2*q^9 + 2*q^7 - q^6, q^7 - q^6),
     524            (q^12 - q^11 - q^10 + q^9, 1/2*q^7 - 1/2*q^6),
     525            (q^8 - q^7 - q^6 + q^5, 1/2*q^8 - q^7 + 1/2*q^6),
     526            (q^8 - 2*q^7 + q^6, q^7 - q^6),
     527            (q^8 - q^7, q^6 + 2*q^5),
     528            (q^10 - 2*q^9 + q^8, 2*q^6)]
     529            sage: list(ExtOrbitClasses(Partition([6, 1, 1])).orbit_centralizers(selftranspose = True))
     530            [(q^12 - 3*q^11 + 3*q^10 - q^9, 1/2*q^7 - 1/2*q^6),
     531            (q^8 - 3*q^7 + 3*q^6 - q^5, 1/2*q^8 - q^7 + 1/2*q^6),
     532            (q^12 - 2*q^11 + q^10, q^6),
     533            (q^8 - 2*q^7 + q^6, q^7 - q^6),
     534            (q^14 - 2*q^13 + 2*q^11 - q^10, q^6),
     535            (q^10 - 2*q^9 + 2*q^7 - q^6, q^7 - q^6),
     536            (q^12 - q^11 - q^10 + q^9, 1/2*q^7 - 1/2*q^6),
     537            (q^8 - q^7 - q^6 + q^5, 1/2*q^8 - q^7 + 1/2*q^6),
     538            (q^8 - 2*q^7 + q^6, q^7 - q^6),
     539            (q^8 - q^7, q^6)]
     540            sage: list(ExtOrbitClasses(PrimarySimilarityClassType(2, [6, 1, 1])).orbit_centralizers(selftranspose = True))
     541            [(q^24 - 3*q^22 + 3*q^20 - q^18, 1/2*q^14 - 1/2*q^12),
     542            (q^16 - 3*q^14 + 3*q^12 - q^10, 1/2*q^16 - q^14 + 1/2*q^12),
     543            (q^24 - 2*q^22 + q^20, q^12),
     544            (q^16 - 2*q^14 + q^12, q^14 - q^12),
     545            (q^28 - 2*q^26 + 2*q^22 - q^20, q^12),
     546            (q^20 - 2*q^18 + 2*q^14 - q^12, q^14 - q^12),
     547            (q^24 - q^22 - q^20 + q^18, 1/2*q^14 - 1/2*q^12),
     548            (q^16 - q^14 - q^12 + q^10, 1/2*q^16 - q^14 + 1/2*q^12),
     549            (q^16 - 2*q^14 + q^12, q^14 - q^12),
     550            (q^16 - q^14, q^12)]
     551            sage: list(ExtOrbitClasses(SimilarityClassType([[2, [6, 1, 1]]])).orbit_centralizers(selftranspose = True))
     552            [(q^24 - 3*q^22 + 3*q^20 - q^18, 1/2*q^14 - 1/2*q^12),
     553            (q^16 - 3*q^14 + 3*q^12 - q^10, 1/2*q^16 - q^14 + 1/2*q^12),
     554            (q^24 - 2*q^22 + q^20, q^12),
     555            (q^16 - 2*q^14 + q^12, q^14 - q^12),
     556            (q^28 - 2*q^26 + 2*q^22 - q^20, q^12),
     557            (q^20 - 2*q^18 + 2*q^14 - q^12, q^14 - q^12),
     558            (q^24 - q^22 - q^20 + q^18, 1/2*q^14 - 1/2*q^12),
     559            (q^16 - q^14 - q^12 + q^10, 1/2*q^16 - q^14 + 1/2*q^12),
     560            (q^16 - 2*q^14 + q^12, q^14 - q^12),
     561            (q^16 - q^14, q^12)]
     562        """
     563        q = self.q()
     564        if self._case == 'par':
     565            la = self._tau
     566            if len(la) == 0:
     567                yield (1, 1)
     568                return
     569            elif max(la) == 1:
     570                for item in MatrixSimilarityClasses(len(la), q = q).class_centralizers():
     571                    yield item
     572                return
     573            elif len(la) == 1:
     574                yield (q**la[0] - q**(la[0]-1), q**la[0])
     575                return
     576            elif len(la) == 2 and list(la).count(1) == 1: # see Table 3
     577                m = max(la) - 1
     578                yield (q**(m + 4) - 2*q**(m + 3) + q**(m + 2), q**(m + 1)) # (8.5.1)
     579                yield (q**(m + 2) - 2*q**(m + 1) + q**m, q**(m + 2) - q**(m + 1)) # (8.5.2)
     580                if selftranspose:
     581                    yield (q**(m + 2) - q**(m + 1), q**(m+1) - q**m) # (8.5.3) and (8.5.4)
     582                else:
     583                    yield (q**(m + 2) - q**(m + 1), q**(m + 1) + q**m) # (8.5.3) and (8.5.4)
     584                return
     585            elif len(la) == 3 and list(la).count(1) == 2: # see Table 4
     586                m = max(la) - 1
     587                for item in MatrixSimilarityClasses(2, q = q).class_centralizers():
     588                    yield (item[0]*(q**(m + 5) - q**(m + 4)), item[1]*q**m) # (8.6.1)
     589                    yield (item[0]*(q**(m + 1) - q**m), item[1]*(q**(m + 1) - q**m)) # (8.6.2)
     590                yield (q**(m + 3) - 2*q**(m + 2) + q**(m+1), q**(m + 2) - q**(m + 1)) # (8.6.3)
     591                if selftranspose:
     592                    yield (q**(m + 3) - q**(m+2), q**(m+1))
     593                else:
     594                    yield (q**(m + 3) - q**(m+2), q**(m + 1) + 2*q**m)
     595                    yield (q**(m + 5) - 2*q**(m + 4) + q**(m + 3), 2*q**(m + 1))
     596                return
     597            elif max(la) == 2 and min(la) == 2:
     598                for item in MatrixSimilarityClasses2(len(la), q = q).class_centralizers(selftranspose = selftranspose):
     599                    yield item
     600            else:
     601                raise ValueError('partition %s not implemented for ExtOrbitClasses.orbit_centralizers'%(la))
     602        elif self._case == 'pri':
     603            tau = self._tau
     604            for item in ExtOrbitClasses(tau.partition()).orbit_centralizers(selftranspose = selftranspose):
     605                yield (item[0].substitute(q = q**tau.degree()), item[1].substitute(q = q**tau.degree()))
     606        elif self._case == 'sim':
     607            tau = self._tau
     608            for item in CartesianProduct(*[IterableFunctionCall(lambda x: ExtOrbitClasses(x, q = q).orbit_centralizers(selftranspose = selftranspose), PT) for PT in tau]):
     609                size = prod([list(entry)[0] for entry in item])
     610                freq = prod([list(entry)[1] for entry in item])
     611                yield(size, freq)
     612
     613    def orbit_centralizers_dict(self, selftranspose = False):
     614        """
     615        Return a dictionary whose keys are the cardinalities of orbit
     616        centralizers in ``self``. The value is the number of orbits with
     617        centralizer of that cardinality.
     618
     619
     620        EXAMPLES::
     621
     622            sage: from sage.combinat.similarity_over_rings_of_length_two import ExtOrbitClasses
     623            sage: ExtOrbitClasses(PrimarySimilarityClassType(2, [2, 1]), q =2).orbit_centralizers_dict(selftranspose = True)
     624            {576: 16, 48: 12, 36: 48}
     625        """
     626        return dictionary_from_generator(self.orbit_centralizers(selftranspose = selftranspose))
     627
     628
     629class MatrixSimilarityClasses2():
     630    r"""
     631    Class of similarity classes of `n\times n` matrices with entries in a
     632    principal ideal local ring of length two with residue field of order `q`.
     633    """
     634    def __init__(self, n, q = None):
     635        """
     636        INPUT:
     637
     638        - ``n`` -- nonnegative integer
     639        - ``q`` -- integer or indeterminate
     640
     641            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     642            sage: M = MatrixSimilarityClasses2(2)
     643            sage: TestSuite(M).run()
     644        """
     645        if q is None:
     646            q = FractionField(QQ['q']).gen()
     647        self._q = q
     648        self._n = n
     649
     650    def __repr__(self):
     651        """
     652        Return string representation of ``self``.
     653
     654        EXAMPLES::
     655
     656            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     657            sage: MatrixSimilarityClasses2(2)
     658            Similarity classes of 2 by 2 matrices over a principal ideal local ring with residue field of order q
     659        """
     660        return "Similarity classes of %s by %s matrices over a principal ideal local ring with residue field of order %s"%(self.n(), self.n(), self.q())
     661
     662    def __eq__(self, other):
     663        """
     664        Test equality of ``self`` and ``other``.
     665
     666        EXAMPLES::
     667
     668            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     669            sage: M1 = MatrixSimilarityClasses2(2)
     670            sage: M2 = MatrixSimilarityClasses2(3)
     671            sage: M3 = MatrixSimilarityClasses2(2, q = 2)
     672            sage: M1 == M2
     673            False
     674            sage: M1 == M3
     675            False
     676            sage: M1 == M1
     677            True
     678        """
     679        return self.n() == other.n() and self.q() == other.q()
     680
     681    def n(self):
     682        """
     683        Return size of ``self``.
     684
     685        EXAMPLES::
     686
     687            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     688            sage: MatrixSimilarityClasses2(2).n()
     689            2
     690        """
     691        return self._n
     692
     693    def q(self):
     694        """
     695        Return order of residue field of ``self``.
     696
     697        EXAMPLES::
     698
     699            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     700            sage: MatrixSimilarityClasses2(2, q = 2).q()
     701            2
     702        """
     703        return self._q
     704
     705    def classes(self, selftranspose = False, invertible = False):
     706        """
     707        Return the number of classes in ``self``.
     708
     709        EXAMPLES:
     710
     711        We can generate Table 6 of [PSS13]_::
     712
     713            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     714            sage: MatrixSimilarityClasses2(2).classes()
     715            q^4 + q^3 + q^2
     716            sage: MatrixSimilarityClasses2(2).classes(invertible = True)
     717            q^4 - q
     718            sage: MatrixSimilarityClasses2(3).classes()
     719            q^6 + q^5 + 2*q^4 + q^3 + 2*q^2
     720            sage: MatrixSimilarityClasses2(3).classes(invertible = True)
     721            q^6 - q^3 + 2*q^2 - 2*q
     722            sage: MatrixSimilarityClasses2(4).classes()
     723            q^8 + q^7 + 3*q^6 + 3*q^5 + 5*q^4 + 3*q^3 + 3*q^2
     724            sage: MatrixSimilarityClasses2(4).classes(invertible = True)
     725            q^8 + q^6 - q^5 + 2*q^4 - 2*q^3 + 2*q^2 - 3*q
     726
     727        And also Table 7::
     728
     729            sage: MatrixSimilarityClasses2(2).classes(selftranspose = True)
     730            q^4 + q^3 + q^2
     731            sage: MatrixSimilarityClasses2(2).classes(selftranspose = True, invertible = True)
     732            q^4 - q
     733            sage: MatrixSimilarityClasses2(3).classes(selftranspose = True)
     734            q^6 + q^5 + 2*q^4 + q^3
     735            sage: MatrixSimilarityClasses2(3).classes(selftranspose = True, invertible = True)
     736            q^6 - q^3
     737            sage: MatrixSimilarityClasses2(4).classes(selftranspose = True)
     738            q^8 + q^7 + 3*q^6 + 3*q^5 + 3*q^4 + q^3 + q^2
     739            sage: MatrixSimilarityClasses2(4).classes(selftranspose = True, invertible = True)
     740            q^8 + q^6 - q^5 - q
     741        """
     742        return sum([tau.number_of_classes(invertible = invertible, q = self.q())*ExtOrbitClasses(tau, q = self.q()).orbits(selftranspose = selftranspose) for tau in SimilarityClassTypes(self.n())])
     743
     744    def class_centralizers(self, selftranspose = False, invertible = False):
     745        """
     746        Generate a list of pairs `(c, f)` where for each `c` there are `f`
     747        classes in ``self`` with centralizer of cardinality `c`.
     748
     749        .. NOTE::
     750
     751            The same calue of `c` may occur for multiple pairs `(c, f)` in this
     752            list. If this happens, the number of orbits with centralizer of
     753            cardinality `c` is the sum of `f` over all pairs `(c',f)` with
     754            `c'=c`. To get this number use the method
     755            ``orbit_centralizers_dict``.
     756
     757        EXAMPLES::
     758
     759            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     760            sage: list(MatrixSimilarityClasses2(2).class_centralizers())
     761            [(q^4 - 2*q^3 + q^2, 1/2*q^4 - 1/2*q^3),
     762            (q^4 - q^3, q^3),
     763            (q^6 - 2*q^5 + q^4, 1/2*q^3 - 1/2*q^2),
     764            (q^6 - q^5, q^2),
     765            (q^8 - q^7 - q^6 + q^5, q^2),
     766            (q^6 - q^4, 1/2*q^3 - 1/2*q^2),
     767            (q^4 - q^2, 1/2*q^4 - 1/2*q^3)]
     768        """
     769        q = self.q()
     770        for tau in SimilarityClassTypes(self.n()):
     771            for pair in ExtOrbitClasses(tau, q = q).orbit_centralizers(selftranspose = selftranspose):
     772                yield (q**tau.centralizer_algebra_dim()*pair[0], tau.number_of_classes(invertible = invertible, q = q)*pair[1])
     773
     774    def class_centralizers_dict(self, selftranspose = False, invertible = False):
     775        """
     776        Return a dictionary whose keys are cardinalities, and values the number
     777        of classes with centralizers of that cardinality.
     778
     779        EXAMPLES::
     780
     781            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     782            sage: MatrixSimilarityClasses2(2, q = 2).class_centralizers_dict()
     783            {96: 4, 32: 4, 4: 4, 16: 2, 8: 8, 12: 4, 48: 2}
     784        """
     785        return dictionary_from_generator(self.class_centralizers(selftranspose = selftranspose, invertible = invertible))
     786
     787    def class_cardinalities(self, selftranspose = False, invertible = False):
     788        """
     789        Generate a list of pairs `(c, f)` where for each `c` there are `f`
     790        classes in ``self`` with cardinality `c`.
     791
     792        .. NOTE::
     793
     794            The same calue of `c` may occur for multiple pairs `(c, f)` in this
     795            list. If this happens, the number of orbits with centralizer of
     796            cardinality `c` is the sum of `f` over all pairs `(c',f)` with
     797            `c'=c`. To get this number use the method
     798            ``orbit_centralizers_dict``.
     799
     800        EXAMPLES::
     801
     802            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     803            sage: list(MatrixSimilarityClasses2(2, q = 2).class_cardinalities())
     804            [(24, 4), (12, 8), (6, 2), (3, 4), (1, 4), (2, 2), (8, 4)]
     805        """
     806        q = self.q()
     807        n = self.n()
     808        g = order_of_general_linear_group(n, q = q)*q**(n**2)
     809        for item in self.class_centralizers(selftranspose = selftranspose, invertible = invertible):
     810            yield (g/item[0], item[1])
     811
     812    def class_cardinalities_dict(self, selftranspose = False, invertible = False):
     813        """
     814        Return a dictionary whose keys are cardinalities, and values the number
     815        of classes of that cardinality.
     816
     817        EXAMPLES::
     818
     819            sage: from sage.combinat.similarity_over_rings_of_length_two import MatrixSimilarityClasses2
     820            sage: MatrixSimilarityClasses2(2, q = 2).class_cardinalities_dict()
     821            {1: 4, 2: 2, 3: 4, 6: 2, 8: 4, 12: 8, 24: 4}
     822        """
     823        return dictionary_from_generator(self.class_cardinalities(selftranspose = selftranspose, invertible = invertible))