| 1 | r""" |
| 2 | Similarity classes of matrcies over a principal ideal local ring of length two |
| 3 | |
| 4 | Implements a partial algorithm for formal counting of similarity classes of |
| 5 | matrices with entries in a finite principal ideal local ring of length two. |
| 6 | |
| 7 | Let `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 |
| 9 | similarity classes in `M_n(R)` or `GL_n(R)` can be reduced to calculating the |
| 10 | number of orbits for the action of the group `G_\lambda := |
| 11 | Z_{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 |
| 13 | on `\mathbf{F}_q^m` by a nilpotent matrix whose Jordan block sizes are given by |
| 14 | a partition `\lambda` of `m` for every partition `\lambda` of an integer |
| 15 | `m\leq n`. |
| 16 | |
| 17 | Calculations are done as formal polynomials in `q`. |
| 18 | |
| 19 | REFERENCES: |
| 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 | |
| 24 | AUTHOR: |
| 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 | |
| 44 | from sage.combinat.cartesian_product import CartesianProduct |
| 45 | from sage.combinat.misc import IterableFunctionCall |
| 46 | from sage.combinat.partition import Partitions, Partition |
| 47 | from sage.combinat.similarity_class_type import SimilarityClassType, SimilarityClassTypes, order_of_general_linear_group, PrimarySimilarityClassType |
| 48 | from sage.misc.misc import prod |
| 49 | from sage.rings.all import QQ |
| 50 | from sage.rings.fraction_field import FractionField |
| 51 | from sage.structure.sage_object import SageObject |
| 52 | |
| 53 | def 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 | |
| 79 | class 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 | |
| 216 | class 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 | |
| 629 | class 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)) |