| 1 | r""" |
| 2 | Diagram/Partition Algebras |
| 3 | """ |
| 4 | #***************************************************************************** |
| 5 | # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, |
| 6 | # 2012 Stephen Doty <doty@math.luc.edu>, |
| 7 | # Aaron Lauve <lauve@math.luc.edu>, |
| 8 | # George H. Seelinger <ghseeli@gmail.com> |
| 9 | # |
| 10 | # Distributed under the terms of the GNU General Public License (GPL) |
| 11 | # http://www.gnu.org/licenses/ |
| 12 | #***************************************************************************** |
| 13 | from sage.categories.all import FiniteDimensionalAlgebrasWithBasis |
| 14 | from sage.combinat.family import Family |
| 15 | from sage.combinat.free_module import (CombinatorialFreeModule, |
| 16 | CombinatorialFreeModuleElement) |
| 17 | from sage.combinat.set_partition import SetPartitions |
| 18 | from sage.sets.set import Set |
| 19 | from sage.graphs.graph import Graph |
| 20 | from sage.misc.cachefunc import cached_method |
| 21 | import collections |
| 22 | import functools, math |
| 23 | ##these four imports are for drawing diagrams; these may be removed in the final version. |
| 24 | #import networkx |
| 25 | #import pylab |
| 26 | #import matplotlib.pyplot as plt |
| 27 | #import itertools |
| 28 | ## |
| 29 | |
| 30 | def partition_diagrams(k): |
| 31 | r""" |
| 32 | partition_diagrams(k) returns a list of all partition diagrams of order `k`, |
| 33 | where we define a partition diagram of order `k` to be any set partition |
| 34 | of the set `\{1, \dots, k, -1, \dots, -k\}`. |
| 35 | |
| 36 | INPUT: |
| 37 | |
| 38 | - ``k`` - integer or integer + 1/2. This is the order of the partition diagrams. |
| 39 | |
| 40 | OUTPUT: |
| 41 | |
| 42 | set partitions -- if k is an integer, set partitions of the set |
| 43 | `\{1, \dots, k, -1, \dots, -k\}`. If k is an integer + 1/2, set |
| 44 | partitions `\{1, \dots, k-0.5, -1, \dots, -(k-0.5)\}` with |
| 45 | `\{k+0.5, -(k+0.5)\}` appended to each set partition. |
| 46 | |
| 47 | EXAMPLES:: |
| 48 | |
| 49 | sage: partition_diagrams(2) |
| 50 | Set partitions of [1, 2, -1, -2] |
| 51 | sage: partition_diagrams(1.5) #random order |
| 52 | [{{2, -2}, {1, -1}}, {{-1}, {2, -2}, {1}}] |
| 53 | |
| 54 | """ |
| 55 | if int(k) == k: |
| 56 | return SetPartitions( range(1,k+1) + [-j for j in range(1,k+1)] ) |
| 57 | if k - math.floor(k) == 0.5: |
| 58 | L = [] |
| 59 | counter = 0 |
| 60 | for i in SetPartitions( range(1,int(k+0.5)) + [-j for j in range(1,int(k+0.5))] ).list(): |
| 61 | L.append(i.list()) |
| 62 | (L[counter]).append(Set([int(k)+1, -(int(k)+1)])) |
| 63 | L[counter] = Set(L[counter]) |
| 64 | counter = counter+1 |
| 65 | return L |
| 66 | |
| 67 | def brauer_diagrams(k): |
| 68 | r""" |
| 69 | brauer_diagrams(k) returns a list of all Brauer diagrams of order `k`, |
| 70 | where we define a Brauer diagram of order `k` to be any set partition |
| 71 | of the set`\{1, \dots, k, -1, \dots, -k\}` with block size 2. |
| 72 | |
| 73 | INPUT: |
| 74 | |
| 75 | - ``k`` - integer. This is the order of the Brauer diagrams. |
| 76 | |
| 77 | OUTPUT: |
| 78 | |
| 79 | set partitions -- set partitions of the set |
| 80 | `\{1, \dots, k, -1, \dots, -k\}` with block size 2. If k is an |
| 81 | integer + 1/2, set partitions |
| 82 | `\{1, \dots, k-0.5, -1, \dots, -(k-0.5)\}` of block size 2 with |
| 83 | `\{k+0.5, -(k+0.5)\}` appended to each set partition. |
| 84 | |
| 85 | EXAMPLES:: |
| 86 | |
| 87 | sage: brauer_diagrams(2) |
| 88 | Set partitions of [1, 2, -1, -2] with sizes in [[2, 2]] |
| 89 | sage: brauer_diagrams(2.5) #random order |
| 90 | [{{1, 2}, {3, -3}, {-1, -2}}, {{2, -2}, {3, -3}, {1, -1}}, {{2, -1}, {3, -3}, {1, -2}}] |
| 91 | |
| 92 | """ |
| 93 | if int(k) == k: |
| 94 | return SetPartitions( range(1,k+1) + [-j for j in range(1,k+1)], [2 for j in range(1,k+1)] ) |
| 95 | if k - math.floor(k) == 0.5: |
| 96 | L = [] |
| 97 | counter = 0 |
| 98 | for i in SetPartitions( range(1,int(k+0.5)) + [-j for j in range(1,int(k+0.5))], [2 for j in range(1,k+0.5)] ).list(): |
| 99 | L.append(i.list()) |
| 100 | (L[counter]).append(Set([int(k)+1, -(int(k)+1)])) |
| 101 | L[counter] = Set(L[counter]) |
| 102 | counter = counter+1 |
| 103 | return L |
| 104 | |
| 105 | def temperley_lieb_diagrams(k): |
| 106 | r""" |
| 107 | temperley_lieb_diagrams(k) returns a list of all Temperley-Lieb |
| 108 | diagrams of order `k`, where we define a Temperley-Lieb diagram of |
| 109 | order `k` to be any set partition of the set |
| 110 | `\{1, \dots, k, -1, \dots, -k\}` with block size 2 and that is |
| 111 | planar. |
| 112 | |
| 113 | INPUT: |
| 114 | |
| 115 | - ``k`` - integer or integer + 1/2. This is the order of the |
| 116 | Temperley-Lieb diagrams. |
| 117 | |
| 118 | OUTPUT: |
| 119 | |
| 120 | set partitions -- set partitions of the set |
| 121 | `\{1, \dots, k, -1, \dots, -k\}` with block size 2 that are planar. |
| 122 | If k is an integer + 1/2, set partitions |
| 123 | `\{1, \dots, k-0.5, -1, \dots, -(k-0.5)\}` of block size 2 that are |
| 124 | planar with `\{k+0.5, -(k+0.5)\}` appended to each set partition. |
| 125 | |
| 126 | EXAMPLES:: |
| 127 | |
| 128 | sage: temperley_lieb_diagrams(2) #random order |
| 129 | [{{1, 2}, {-1, -2}}, {{2, -2}, {1, -1}}] |
| 130 | sage: temperley_lieb_diagrams(2.5) #random order |
| 131 | [{{1, 2}, {3, -3}, {-1, -2}}, {{2, -2}, {3, -3}, {1, -1}}] |
| 132 | """ |
| 133 | B = brauer_diagrams(k) |
| 134 | T = [] |
| 135 | for i in B: |
| 136 | if is_planar(i) == True: |
| 137 | T.append(i) |
| 138 | return T |
| 139 | |
| 140 | def planar_diagrams(k): |
| 141 | A = partition_diagrams(k) |
| 142 | P = [] |
| 143 | for i in A: |
| 144 | if is_planar(i) == True: |
| 145 | P.append(i) |
| 146 | return P |
| 147 | |
| 148 | def ideal_diagrams(k): |
| 149 | A = partition_diagrams(k) |
| 150 | I = [] |
| 151 | for i in A: |
| 152 | if propagating_number(i) < k: |
| 153 | I.append(i) |
| 154 | return I |
| 155 | |
| 156 | def is_partition_diagram(s,k): |
| 157 | r""" |
| 158 | is_partition_diagram(s,k) returns True if `s` is a partition diagram of order |
| 159 | exactly `k` (i.e., a set partition on `\{1, \dots, k, -1, \dots ,-k\}`) and returns False |
| 160 | otherwise. |
| 161 | INPUT: |
| 162 | |
| 163 | - ``s`` - set partition. This is the set partition being tested. |
| 164 | - ``k`` - integer. This is the test order. |
| 165 | |
| 166 | OUTPUT: |
| 167 | |
| 168 | boolean -- this is true if `s` has order `k` and false otherwise. |
| 169 | |
| 170 | EXAMPLES:: |
| 171 | |
| 172 | sage: s = partition_diagrams(2) |
| 173 | sage: is_partition_diagram(s.list()[2], 2) |
| 174 | True |
| 175 | sage: is_partition_diagram(s.list()[2], 3) |
| 176 | False |
| 177 | |
| 178 | TODO: SHOULD BE MADE A METHOD OF THE SetPartitions CLASS ? |
| 179 | """ |
| 180 | return s in partition_diagrams(k) |
| 181 | |
| 182 | class PartitionAlgebra(CombinatorialFreeModule): |
| 183 | r""" |
| 184 | INPUT: |
| 185 | |
| 186 | -``k``-- rank of the algebra (equals half the number of nodes in the diagrams) |
| 187 | -``q``-- a parameter. |
| 188 | |
| 189 | OPTIONAL ARGUMENTS: |
| 190 | |
| 191 | -``base_ring``-- a ring containing q (default q.parent()) |
| 192 | -``prefix``-- a label for the basis elements (default "P") |
| 193 | |
| 194 | The partition algebra of rank k is an algebra with basis indexed by the |
| 195 | collection of set partitions of `\{1, \dots, k, -1, \dots, -k\}`. Each such set |
| 196 | partition is regarded as a graph on nodes `\{1, \dots, k, -1, \dots, -k\}` |
| 197 | arranged in two rows, with nodes `1, \dots, k` in the top row from left to |
| 198 | right and with nodes `-1, \dots, -k` in the bottom row from left to right, |
| 199 | and an edge connecting two nodes if and only if the nodes lie in the same |
| 200 | subset of the set partition. |
| 201 | |
| 202 | The partition algebra is regarded as an example of a "diagram algebra" due |
| 203 | to the fact that its natural basis is given by certain graphs often called |
| 204 | diagrams. |
| 205 | |
| 206 | The product of two basis elements is given by the rule a.b = q^N (a o b), |
| 207 | where a o b is the composite set partition obtained by placing diagram |
| 208 | a above diagram b, identifying the bottom row nodes of a with the top row |
| 209 | nodes of b, and omitting any closed "loops" in the middle. The number N |
| 210 | is the number of connected components of the omitted loops. |
| 211 | |
| 212 | The parameter q is a deformation parameter. Taking `q=1` produces the |
| 213 | semigroup algebra (over the base ring) of the partition monoid, in which |
| 214 | the product of two set partitions is simply given by their composition. |
| 215 | |
| 216 | The Iwahori-Hecke algebra of type A (with a single parameter) is naturally |
| 217 | a subalgebra of the partition algebra. |
| 218 | |
| 219 | An excellent reference for partition algebras and its various subalgebras |
| 220 | (Brauer algebra, Temperley-Lieb algebra, etc) is the paper [HR]_. |
| 221 | |
| 222 | REFERENCES: |
| 223 | |
| 224 | .. [HR] Tom Halverson and Arun Ram, Partition algebras. European Journal of |
| 225 | Combinatorics 26 (2005), 869--921. |
| 226 | |
| 227 | EXAMPLES: |
| 228 | |
| 229 | The following shorthand simultaneously define the univariate polynomial |
| 230 | ring over the rationals as well as the variable `x`:: |
| 231 | |
| 232 | sage: R.<x> = PolynomialRing(QQ) |
| 233 | sage: R |
| 234 | Univariate Polynomial Ring in x over Rational Field |
| 235 | sage: x |
| 236 | x |
| 237 | sage: x.parent() is R |
| 238 | True |
| 239 | |
| 240 | We now define the partition algebra of rank `2` with parameter `x` over the Integer ring:: |
| 241 | |
| 242 | sage: P = PartitionAlgebra(2,x,ZZ['x']) |
| 243 | sage: P |
| 244 | Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring, with prefix P |
| 245 | sage: P.basis() |
| 246 | Lazy family (Term map from Set partitions of [1, 2, -1, -2] to Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring, with prefix P(i))_{i in Set partitions of [1, 2, -1, -2]} |
| 247 | sage: b = P.basis().list() |
| 248 | sage: b #random order |
| 249 | [P[{{1, 2, -1, -2}}], P[{{2, -1, -2}, {1}}], P[{{2}, {1, -1, -2}}], P[{{-1}, {1, 2, -2}}], P[{{-2}, {1, 2, -1}}], P[{{2, -1}, {1, -2}}], P[{{2, -2}, {1, -1}}], P[{{1, 2}, {-2, -1}}], P[{{2}, {-1, -2}, {1}}], P[{{-1}, {2, -2}, {1}}], P[{{-2}, {2, -1}, {1}}], P[{{-1}, {2}, {1, -2}}], P[{{-2}, {2}, {1, -1}}], P[{{-1}, {-2}, {1, 2}}], P[{{-1}, {-2}, {2}, {1}}]] |
| 250 | sage: E = P([[1,2],[-2,-1]]); E |
| 251 | P[{{1, 2}, {-2, -1}}] |
| 252 | sage: E in b |
| 253 | True |
| 254 | sage: E^2 |
| 255 | x*P[{{1, 2}, {-2, -1}}] |
| 256 | sage: E^5 |
| 257 | x^4*P[{{1, 2}, {-2, -1}}] |
| 258 | sage: (P([[2,-2],[-1,1]]) - 2*P([[1,2],[-1,-2]]))^2 |
| 259 | (4*x-4)*P[{{1, 2}, {-2, -1}}] + P[{{2, -2}, {1, -1}}] |
| 260 | |
| 261 | One can work with partition algebras using a symbol for the parameter, |
| 262 | leaving the base ring unspecified. This implies that the underlying |
| 263 | base ring is Sage's Symbolic Ring. |
| 264 | |
| 265 | :: |
| 266 | |
| 267 | sage: q = var('q') |
| 268 | sage: PA = PartitionAlgebra(2,q); PA |
| 269 | Partition Algebra of rank 2 with parameter q over Symbolic Ring, with prefix P |
| 270 | sage: P = PA.basis().list() |
| 271 | sage: P[7], P[7]*P[7] |
| 272 | (P[{{1, 2}, {-2, -1}}], q*P[{{1, 2}, {-2, -1}}]) |
| 273 | sage: (P[6] - 2*P[7])^2 |
| 274 | (4*q-4)*P[{{1, 2}, {-2, -1}}] + P[{{2, -2}, {1, -1}}] |
| 275 | |
| 276 | The identity element of the partition algebra is the diagram whose set |
| 277 | partition is `\{\{1,-1\}, \{2,-2\}, ..., \{k,-k\}\}`. |
| 278 | |
| 279 | :: |
| 280 | |
| 281 | sage: PA.one() #random order from SetPartitions |
| 282 | P[{{2, -2}, {1, -1}}] |
| 283 | sage: PA.one()*P[7] |
| 284 | P[{{1, 2}, {-2, -1}}] |
| 285 | sage: P[7]*PA.one() |
| 286 | P[{{1, 2}, {-2, -1}}] |
| 287 | |
| 288 | We now give some further examples of the use of the other arguments. |
| 289 | One may wish to "specialize" the parameter to a chosen element of |
| 290 | the base ring. |
| 291 | |
| 292 | :: |
| 293 | |
| 294 | sage: PA = PartitionAlgebra(2, q, RR['q'], prefix='B') |
| 295 | sage: PA |
| 296 | Partition Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Real Field with 53 bits of precision, with prefix B |
| 297 | sage: PA.basis().list()[7] |
| 298 | 1.00000000000000*B[{{1, 2}, {-2, -1}}] |
| 299 | sage: PA = PartitionAlgebra(2, 5, base_ring=ZZ, prefix='B') |
| 300 | sage: PA |
| 301 | Partition Algebra of rank 2 with parameter 5 over Integer Ring, with prefix B |
| 302 | sage: P = PA.basis().list() |
| 303 | sage: (P[6] - 2*P[7])^2 |
| 304 | 16*B[{{1, 2}, {-2, -1}}] + B[{{2, -2}, {1, -1}}] |
| 305 | """ |
| 306 | |
| 307 | @staticmethod |
| 308 | def __classcall_private__(cls, k, q, base_ring=None, prefix="P"): |
| 309 | r""" |
| 310 | This method gets the base_ring from the parent of the parameter q |
| 311 | if no base_ring is given. |
| 312 | """ |
| 313 | if base_ring is None: |
| 314 | base_ring = q.parent() |
| 315 | return super(PartitionAlgebra, cls).__classcall__( |
| 316 | cls, k, q=q, base_ring = base_ring, prefix=prefix) |
| 317 | |
| 318 | |
| 319 | # The following is the basic constructor method for the class. |
| 320 | # The purpose of the "prefix" is to label the basis elements |
| 321 | def __init__(self, k, q, base_ring, prefix): |
| 322 | r""" |
| 323 | See :class:`PartitionAlgebra` for full documentation. |
| 324 | |
| 325 | TESTS:: |
| 326 | |
| 327 | sage: q = var('q') |
| 328 | sage: PA = PartitionAlgebra(2, q, RR['q']) |
| 329 | sage: TestSuite(PA).run() |
| 330 | """ |
| 331 | |
| 332 | self._k = k |
| 333 | self._prefix = prefix |
| 334 | self._q = base_ring(q) |
| 335 | category = FiniteDimensionalAlgebrasWithBasis(base_ring) |
| 336 | CombinatorialFreeModule.__init__(self, base_ring, partition_diagrams(k), |
| 337 | category = category, prefix=prefix) |
| 338 | |
| 339 | # The following constructs a basis element from a given set partition. |
| 340 | # It should never be called directly. |
| 341 | def _element_constructor_(self, set_partition): |
| 342 | r""" |
| 343 | If PA is a particular partition algebra (previously defined) then |
| 344 | PA( sp ) returns the element of the algebra PA corresponding to a |
| 345 | given set partition sp. |
| 346 | |
| 347 | EXAMPLES:: |
| 348 | |
| 349 | sage: q = var('q') |
| 350 | sage: PA = PartitionAlgebra(2,q,QQ['q']) |
| 351 | sage: PA |
| 352 | Partition Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field, with prefix P |
| 353 | sage: sp = to_set_partition( [[1,2], [-1,-2]] ) # create a set partition |
| 354 | sage: sp |
| 355 | {{1, 2}, {-1, -2}} |
| 356 | sage: PA(sp) # construct a basis element from it |
| 357 | P[{{1, 2}, {-1, -2}}] |
| 358 | sage: PA(sp) in PA |
| 359 | True |
| 360 | sage: PA([[1,2],[-1,-2]]) |
| 361 | P[{{1, 2}, {-1, -2}}] |
| 362 | sage: PA([{1,2},{-1,-2}]) |
| 363 | P[{{1, 2}, {-1, -2}}] |
| 364 | """ |
| 365 | if (set_partition in self.basis().keys()) and isinstance(set_partition, collections.Hashable): |
| 366 | return self.monomial(set_partition) |
| 367 | elif isinstance(set_partition, list): |
| 368 | sp = to_set_partition(set_partition) # attempt coercion |
| 369 | assert sp in self.basis().keys() # did it work? |
| 370 | return self.monomial(sp) |
| 371 | else: |
| 372 | raise AssertionError, "%s invalid form" % set_partition |
| 373 | |
| 374 | @cached_method |
| 375 | def one_basis(self): |
| 376 | r""" |
| 377 | The following constructs the identity element of the partition algebra. |
| 378 | It is not called directly; instead one should use PA.one() if PA is a |
| 379 | previously defined partition algebra instance. |
| 380 | |
| 381 | EXAMPLES:: |
| 382 | |
| 383 | sage: PA = PartitionAlgebra(2, x); |
| 384 | sage: PA.one_basis() |
| 385 | {{2, -2}, {1, -1}} |
| 386 | |
| 387 | """ |
| 388 | return identity_set_partition(self._k) |
| 389 | |
| 390 | def _repr_(self): |
| 391 | msg = "Partition Algebra of rank %s with parameter %s over %s, with prefix %s" |
| 392 | return msg % (self._k, self._q, self.base_ring(), self._prefix) |
| 393 | |
| 394 | def order(self): |
| 395 | r""" |
| 396 | The method PA.order() returns the 'order' of the given partition algebra PA. |
| 397 | This is defined as half of the number of nodes in the diagrams. |
| 398 | |
| 399 | EXAMPLES:: |
| 400 | |
| 401 | sage: q = var('q') |
| 402 | sage: PA = PartitionAlgebra(2,q) |
| 403 | sage: PA.order() |
| 404 | 2 |
| 405 | """ |
| 406 | return self._k |
| 407 | |
| 408 | def set_partitions(self): |
| 409 | r""" |
| 410 | The method PA.set_partitions() returns the collection of underlying |
| 411 | set partitions indexing the basis elements of the given partition |
| 412 | algebra PA. |
| 413 | |
| 414 | EXAMPLES:: |
| 415 | |
| 416 | sage: PA = PartitionAlgebra(2, x) |
| 417 | sage: PA.set_partitions() |
| 418 | Set partitions of [1, 2, -1, -2] |
| 419 | sage: PA.set_partitions().list() #random order |
| 420 | [{{1, 2, -1, -2}}, {{2, -1, -2}, {1}}, {{2}, {1, -1, -2}}, {{-1}, {1, 2, -2}}, {{-2}, {1, 2, -1}}, {{2, -1}, {1, -2}}, {{2, -2}, {1, -1}}, {{1, 2}, {-2, -1}}, {{2}, {-1, -2}, {1}}, {{-1}, {2, -2}, {1}}, {{-2}, {2, -1}, {1}}, {{-1}, {2}, {1, -2}}, {{-2}, {2}, {1, -1}}, {{-1}, {-2}, {1, 2}}, {{-1}, {-2}, {2}, {1}}] |
| 421 | """ |
| 422 | return self.basis().keys() |
| 423 | |
| 424 | def product_on_basis(self, d1, d2): |
| 425 | r""" |
| 426 | This method defines the product of two basis diagrams. Usually |
| 427 | it is not called directly, but indirectly through usualy arithmetic |
| 428 | operators. |
| 429 | |
| 430 | TESTS:: |
| 431 | sage: P = PartitionAlgebra(2, x); |
| 432 | sage: P.product_on_basis(P([[1,2],[-1,-2]]), P([[1,2],[-1,-2]])) |
| 433 | x*P[{{1, {{1, 2}, {-1, -2}}}}] |
| 434 | """ |
| 435 | (composite_diagram, loops_removed) = set_partition_composition(d1, d2) |
| 436 | return self.term(composite_diagram, self._q**loops_removed) |
| 437 | |
| 438 | |
| 439 | # The following subclass provides a few additional methods for |
| 440 | # partition algebra elements. NOTE: Most element methods we need are |
| 441 | # ALREADY implemented! Use the __dir__() builtin python function |
| 442 | # to figure out what methods are defined. |
| 443 | |
| 444 | class Element(CombinatorialFreeModuleElement): |
| 445 | r""" |
| 446 | This subclass provides a few additional methods for |
| 447 | partition algebra elements. Most element methods are |
| 448 | already implemented elsewhere. |
| 449 | """ |
| 450 | |
| 451 | def diagram(self): |
| 452 | r""" |
| 453 | This method returns the underlying diagram of a given |
| 454 | basis element (or an error if not a basis element.) |
| 455 | """ |
| 456 | if len(self) != 1: |
| 457 | raise NotImplementedError("this works only for basis elements") |
| 458 | PA = self.parent() |
| 459 | ans = self.support_of_term() |
| 460 | if is_partition_diagram(ans,PA.order()): |
| 461 | return ans |
| 462 | raise NotImplementedError("element should be keyed by a diagram") |
| 463 | |
| 464 | def diagrams(self): |
| 465 | r""" |
| 466 | this method returns the diagrams in the support of any |
| 467 | given element. It is just a rename of "support" which already exists. |
| 468 | """ |
| 469 | return self.support() |
| 470 | # def show(self): |
| 471 | # diagram = self.diagram() |
| 472 | # sp = to_set_partition(diagram) |
| 473 | # sageGraph = to_graph(sp) |
| 474 | # G = sageGraph.networkx_graph() |
| 475 | # flatlist = itertools.chain(*diagram) |
| 476 | # k = max(flatlist) |
| 477 | # pos=dict(zip(range(1,k+1),zip(range(1,k+1),[1]*k))) |
| 478 | # pos.update(dict(zip(reversed(range(-k,0)),zip(range(1,k+1),[0.5]*k)))) |
| 479 | # plt.clf() |
| 480 | # networkx.draw(G,pos) |
| 481 | # pylab.xlim(-1,9) |
| 482 | # pylab.ylim(-1,2) |
| 483 | # plt.savefig("tmp.png", bbox_inches='tight', pad_inches=-1) |
| 484 | # plt.show() |
| 485 | |
| 486 | class BrauerAlgebra(CombinatorialFreeModule): |
| 487 | r""" |
| 488 | INPUT: |
| 489 | |
| 490 | -``k``-- rank of the algebra (equals half the number of nodes in the diagrams) |
| 491 | -``q``-- a parameter. |
| 492 | |
| 493 | OPTIONAL ARGUMENTS: |
| 494 | |
| 495 | -``base_ring``-- a ring containing q (default q.parent()) |
| 496 | -``prefix``-- a label for the basis elements (default "B") |
| 497 | |
| 498 | The Brauer algebra of rank k is an algebra with basis indexed by the |
| 499 | collection of set partitions of `\{1, \dots, k, -1, \dots, -k\}` |
| 500 | with block size 2. This algebra is thus a subalgebra of the |
| 501 | partition algebra. For more information, see |
| 502 | :class:`PartitionAlgebra` |
| 503 | |
| 504 | EXAMPLES: |
| 505 | |
| 506 | The following shorthand simultaneously define the univariate polynomial |
| 507 | ring over the rationals as well as the variable `x`:: |
| 508 | |
| 509 | sage: R.<x> = PolynomialRing(QQ) |
| 510 | sage: R |
| 511 | Univariate Polynomial Ring in x over Rational Field |
| 512 | sage: x |
| 513 | x |
| 514 | sage: x.parent() is R |
| 515 | True |
| 516 | |
| 517 | We now define the Brauer algebra of rank `2` with parameter `x` over the Integer ring:: |
| 518 | |
| 519 | sage: B = BrauerAlgebra(2,x,ZZ['x']) |
| 520 | sage: B |
| 521 | Brauer Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring, with prefix B |
| 522 | sage: B.basis() |
| 523 | Lazy family (Term map from Set partitions of [1, 2, -1, -2] with sizes in [[2, 2]] to Brauer Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring, with prefix B(i))_{i in Set partitions of [1, 2, -1, -2] with sizes in [[2, 2]]} |
| 524 | sage: b = B.basis().list() |
| 525 | sage: b |
| 526 | [B[{{2, -1}, {1, -2}}], B[{{2, -2}, {1, -1}}], B[{{1, 2}, {-2, -1}}]] |
| 527 | sage: b[2] |
| 528 | B[{{1, 2}, {-2, -1}}] |
| 529 | sage: b[2]^2 |
| 530 | x*B[{{1, 2}, {-2, -1}}] |
| 531 | sage: b[2]^5 |
| 532 | x^4*B[{{1, 2}, {-2, -1}}] |
| 533 | """ |
| 534 | @staticmethod |
| 535 | def __classcall_private__(cls, k, q, base_ring=None, prefix="B"): |
| 536 | r""" |
| 537 | This method gets the base_ring from the parent of the parameter q |
| 538 | if no base_ring is given. |
| 539 | """ |
| 540 | if base_ring is None: |
| 541 | base_ring = q.parent() |
| 542 | return super(BrauerAlgebra, cls).__classcall__( |
| 543 | cls, k, q=q, base_ring = base_ring, prefix=prefix) |
| 544 | def __init__(self, k, q, base_ring, prefix = "B"): |
| 545 | r""" |
| 546 | See :class:`BrauerAlgebra` for full documentation. |
| 547 | |
| 548 | TESTS:: |
| 549 | |
| 550 | sage: q = var('q') |
| 551 | sage: BA = BrauerAlgebra(2, q, RR['q']) |
| 552 | sage: TestSuite(BA).run() |
| 553 | """ |
| 554 | self._k = k |
| 555 | self._prefix = prefix |
| 556 | self._q = base_ring(q) |
| 557 | category = FiniteDimensionalAlgebrasWithBasis(base_ring) |
| 558 | CombinatorialFreeModule.__init__(self, base_ring, brauer_diagrams(k), category = category, prefix=prefix) |
| 559 | |
| 560 | @cached_method |
| 561 | def one_basis(self): |
| 562 | r""" |
| 563 | The following constructs the identity element of the Brauer algebra. |
| 564 | It is not called directly; instead one should use BA.one() if BA is a |
| 565 | previously defined Brauer algebra instance. |
| 566 | |
| 567 | EXAMPLES:: |
| 568 | |
| 569 | sage: BA = BrauerAlgebra(2, x); |
| 570 | sage: BA.one_basis() |
| 571 | {{2, -2}, {1, -1}} |
| 572 | sage: BA.one() |
| 573 | B[{{2, -2}, {1, -1}}] |
| 574 | |
| 575 | """ |
| 576 | return identity_set_partition(self._k) |
| 577 | |
| 578 | def _repr_(self): |
| 579 | msg = "Brauer Algebra of rank %s with parameter %s over %s, with prefix %s" |
| 580 | return msg % (self._k, self._q, self.base_ring(), self._prefix) |
| 581 | |
| 582 | def _element_constructor_(self, set_partition): |
| 583 | r""" |
| 584 | If BA is a particular Brauer algebra (previously defined) then |
| 585 | BA( sp ) returns the element of the algebra BA corresponding to a |
| 586 | given set partition sp. |
| 587 | |
| 588 | EXAMPLES:: |
| 589 | |
| 590 | sage: q = var('q') |
| 591 | sage: BA = BrauerAlgebra(2,q,QQ['q']) |
| 592 | sage: BA |
| 593 | Brauer Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field, with prefix B |
| 594 | sage: sp = to_set_partition( [[1,2], [-1,-2]] ) # create a set partition |
| 595 | sage: sp |
| 596 | {{1, 2}, {-1, -2}} |
| 597 | sage: BA(sp) # construct a basis element from it |
| 598 | B[{{1, 2}, {-1, -2}}] |
| 599 | sage: BA(sp) in BA |
| 600 | True |
| 601 | sage: BA([[1,2],[-1,-2]]) |
| 602 | B[{{1, 2}, {-1, -2}}] |
| 603 | sage: BA([{1,2},{-1,-2}]) |
| 604 | B[{{1, 2}, {-1, -2}}] |
| 605 | """ |
| 606 | if (set_partition in self.basis().keys()) and isinstance(set_partition, collections.Hashable): |
| 607 | return self.monomial(set_partition) |
| 608 | elif isinstance(set_partition, list): |
| 609 | sp = to_set_partition(set_partition) # attempt coercion |
| 610 | assert sp in self.basis().keys() # did it work? |
| 611 | return self.monomial(sp) |
| 612 | else: |
| 613 | raise AssertionError, "%s invalid form" % set_partition |
| 614 | |
| 615 | #These methods allow for BrauerAlgebra to be correctly identified as a sub-algebra of PartitionAlgebra |
| 616 | def ambient(self): |
| 617 | r""" |
| 618 | ambient returns the partition algebra the Brauer algebra is a sub-algebra of. |
| 619 | Generally, this method is not called directly. |
| 620 | |
| 621 | EXAMPLES:: |
| 622 | sage: BA = BrauerAlgebra(2, x); |
| 623 | sage: P = BA.ambient() |
| 624 | sage: P |
| 625 | Partition Algebra of rank 2 with parameter x over Symbolic Ring, with prefix B |
| 626 | """ |
| 627 | return PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix) |
| 628 | def lift(self, x): |
| 629 | r""" |
| 630 | lift maps a Brauer algebra element to the corresponding element |
| 631 | in the ambient space. This method is not intended |
| 632 | to be called directly. |
| 633 | |
| 634 | EXAMPLES:: |
| 635 | sage: BA = BrauerAlgebra(2, x); |
| 636 | sage: E = BA([[1,2],[-1,-2]]); E |
| 637 | B[{{1, 2}, {-1, -2}}] |
| 638 | sage: BA.lift(E) in BA.ambient() |
| 639 | True |
| 640 | sage: BA.lift(E) in BA |
| 641 | False |
| 642 | """ |
| 643 | assert x in self |
| 644 | monomial_indices = x.support() |
| 645 | monomial_coefficients = x.coefficients() |
| 646 | result = 0 |
| 647 | for i in xrange(len(monomial_coefficients)): |
| 648 | result += monomial_coefficients[i]*self.ambient().monomial(monomial_indices[i]) |
| 649 | return result |
| 650 | def retract(self, x): |
| 651 | r""" |
| 652 | retract maps an appropriate partition algebra element to the |
| 653 | corresponding element in the Brauer algebra. This method |
| 654 | is not intended to be called directly. |
| 655 | |
| 656 | EXAMPLES:: |
| 657 | sage: BA = BrauerAlgebra(2, x) |
| 658 | sage: PA = BA.ambient() |
| 659 | sage: E = PA([[1,2],[-1,-2]]) |
| 660 | sage: BA.retract(E) in PA |
| 661 | False |
| 662 | sage: BA.retract(E) in BA |
| 663 | True |
| 664 | """ |
| 665 | assert x in self.ambient() and set(x.support()).issubset(set(self.basis().keys())) |
| 666 | monomial_indices = x.support() |
| 667 | monomial_coefficients = x.coefficients() |
| 668 | result = self.zero() |
| 669 | for i in xrange(len(monomial_coefficients)): |
| 670 | result += monomial_coefficients[i]*self.monomial(monomial_indices[i]) |
| 671 | return result |
| 672 | |
| 673 | #These methods depend on the above three methods |
| 674 | |
| 675 | def product_on_basis(self, left, right): |
| 676 | r""" |
| 677 | This method is not made to be called directly but by using |
| 678 | arithematic operators on elements. |
| 679 | EXAMPLES:: |
| 680 | sage: BA = BrauerAlgebra(2, x) |
| 681 | sage: E = BA([[1,2],[-1,-2]]) |
| 682 | sage: E*E |
| 683 | x*B[{{1, 2}, {-2, -1}}] |
| 684 | sage: E^4 |
| 685 | x^3*B[{{1, 2}, {-2, -1}}] |
| 686 | """ |
| 687 | return self.ambient().product_on_basis(left,right) |
| 688 | def order(self): |
| 689 | r""" |
| 690 | Returns the order of the Brauer Algebra |
| 691 | EXAMPLES:: |
| 692 | sage: BA = BrauerAlgebra(2, x) |
| 693 | sage: BA.order() |
| 694 | 2 |
| 695 | """ |
| 696 | return self.ambient().order() |
| 697 | |
| 698 | class TemperleyLiebAlgebra(CombinatorialFreeModule): |
| 699 | r""" |
| 700 | INPUT: |
| 701 | |
| 702 | -``k``-- rank of the algebra (equals half the number of nodes in the diagrams) |
| 703 | -``q``-- a parameter. |
| 704 | |
| 705 | OPTIONAL ARGUMENTS: |
| 706 | |
| 707 | -``base_ring``-- a ring containing q (default q.parent()) |
| 708 | -``prefix``-- a label for the basis elements (default "T") |
| 709 | |
| 710 | The Temperley-Lieb algebra of rank k is an algebra with basis indexed by the |
| 711 | collection of set partitions of `\{1, \dots, k, -1, \dots, -k\}` |
| 712 | with block size 2 and each set partition is planar. |
| 713 | This algebra is thus a subalgebra of the partition algebra. For more |
| 714 | information, see :class:`PartitionAlgebra` |
| 715 | |
| 716 | EXAMPLES: |
| 717 | |
| 718 | The following shorthand simultaneously define the univariate polynomial |
| 719 | ring over the rationals as well as the variable `x`:: |
| 720 | |
| 721 | sage: R.<x> = PolynomialRing(QQ); R |
| 722 | Univariate Polynomial Ring in x over Rational Field |
| 723 | sage: x |
| 724 | x |
| 725 | sage: x.parent() is R |
| 726 | True |
| 727 | |
| 728 | We now define the Temperley-Lieb algebra of rank `2` with parameter |
| 729 | `x` over the Integer ring:: |
| 730 | |
| 731 | sage: T = TemperleyLiebAlgebra(2, x); T |
| 732 | Temperley-Lieb Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field, with prefix T |
| 733 | sage: T.basis() |
| 734 | Finite family {{{2, -2}, {1, -1}}: T[{{2, -2}, {1, -1}}], {{1, 2}, {-2, -1}}: T[{{1, 2}, {-2, -1}}]} |
| 735 | sage: b = T.basis().list() |
| 736 | sage: b |
| 737 | [T[{{2, -2}, {1, -1}}], T[{{1, 2}, {-2, -1}}]] |
| 738 | sage: b[1] |
| 739 | T[{{1, 2}, {-2, -1}}] |
| 740 | sage: b[1]^2 |
| 741 | x*T[{{1, 2}, {-2, -1}}] |
| 742 | sage: b[1]^5 |
| 743 | x^4*T[{{1, 2}, {-2, -1}}] |
| 744 | |
| 745 | """ |
| 746 | @staticmethod |
| 747 | def __classcall_private__(cls, k, q, base_ring=None, prefix="T"): |
| 748 | r""" |
| 749 | This method gets the base_ring from the parent of the parameter q |
| 750 | if no base_ring is given. |
| 751 | """ |
| 752 | if base_ring is None: |
| 753 | base_ring = q.parent() |
| 754 | return super(TemperleyLiebAlgebra, cls).__classcall__(cls, k, q=q, base_ring = base_ring, prefix=prefix) |
| 755 | |
| 756 | def __init__(self, k, q, base_ring, prefix = "T"): |
| 757 | r""" |
| 758 | See :class:`TemperleyLiebAlgebra` for full documentation. |
| 759 | |
| 760 | TESTS:: |
| 761 | |
| 762 | sage: q = var('q') |
| 763 | sage: TL = TemperleyLiebAlgebra(2, q, RR['q']) |
| 764 | sage: TestSuite(TL).run() |
| 765 | """ |
| 766 | self._k = k |
| 767 | self._prefix = prefix |
| 768 | self._q = base_ring(q) |
| 769 | category = FiniteDimensionalAlgebrasWithBasis(base_ring) |
| 770 | CombinatorialFreeModule.__init__(self, base_ring, temperley_lieb_diagrams(k), category = category, prefix=prefix) |
| 771 | |
| 772 | @cached_method |
| 773 | def one_basis(self): |
| 774 | r""" |
| 775 | The following constructs the identity element of the |
| 776 | Temperley-Lieb algebra. It is not called directly; |
| 777 | instead one should use TL.one() if TL is a |
| 778 | previously defined Temperley-Lieb algebra instance. |
| 779 | |
| 780 | EXAMPLES:: |
| 781 | |
| 782 | sage: TL = TemperleyLiebAlgebra(2, x); |
| 783 | sage: TL.one_basis() |
| 784 | {{2, -2}, {1, -1}} |
| 785 | sage: TL.one() |
| 786 | T[{{2, -2}, {1, -1}}] |
| 787 | |
| 788 | """ |
| 789 | return identity_set_partition(self._k) |
| 790 | |
| 791 | def _repr_(self): |
| 792 | msg = "Temperley-Lieb Algebra of rank %s with parameter %s over %s, with prefix %s" |
| 793 | return msg % (self._k, self._q, self.base_ring(), self._prefix) |
| 794 | |
| 795 | def _element_constructor_(self, set_partition): |
| 796 | r""" |
| 797 | If TL is a particular Temperley-Lieb algebra (previously defined) |
| 798 | then TL( sp ) returns the element of the algebra TL corresponding |
| 799 | to a given set partition sp. |
| 800 | |
| 801 | EXAMPLES:: |
| 802 | |
| 803 | sage: q = var('q') |
| 804 | sage: TL = TemperleyLiebAlgebra(2,q,QQ['q']) |
| 805 | sage: TL |
| 806 | Temperley-Lieb Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field, with prefix T |
| 807 | sage: sp = to_set_partition( [[1,2], [-1,-2]] ) # create a set partition |
| 808 | sage: sp |
| 809 | {{1, 2}, {-1, -2}} |
| 810 | sage: TL(sp) # construct a basis element from it |
| 811 | T[{{1, 2}, {-1, -2}}] |
| 812 | sage: TL(sp) in TL |
| 813 | True |
| 814 | sage: TL([[1,2],[-1,-2]]) |
| 815 | T[{{1, 2}, {-1, -2}}] |
| 816 | sage: TL([{1,2},{-1,-2}]) |
| 817 | T[{{1, 2}, {-1, -2}}] |
| 818 | """ |
| 819 | if (set_partition in self.basis().keys()) and isinstance(set_partition, collections.Hashable): |
| 820 | return self.monomial(set_partition) |
| 821 | elif isinstance(set_partition, list): |
| 822 | sp = to_set_partition(set_partition) # attempt coercion |
| 823 | assert sp in self.basis().keys() # did it work? |
| 824 | return self.monomial(sp) |
| 825 | else: |
| 826 | raise AssertionError, "%s invalid form" % set_partition |
| 827 | |
| 828 | def ambient(self): |
| 829 | r""" |
| 830 | ambient returns the partition algebra the Temperley-Lieb algebra |
| 831 | is a sub-algebra of. Generally, this method is not called |
| 832 | directly. |
| 833 | |
| 834 | EXAMPLES:: |
| 835 | sage: TL = TemperleyLiebAlgebra(2, x); |
| 836 | sage: P = TL.ambient() |
| 837 | sage: P |
| 838 | Partition Algebra of rank 2 with parameter x over Symbolic Ring, with prefix T |
| 839 | """ |
| 840 | return PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix) |
| 841 | def lift(self, x): |
| 842 | r""" |
| 843 | lift maps a Temperley-Lieb algebra element to the corresponding |
| 844 | element in the ambient space. This method is not intended |
| 845 | to be called directly. |
| 846 | |
| 847 | EXAMPLES:: |
| 848 | sage: TL = TemperleyLiebAlgebra(2, x); |
| 849 | sage: E = TL([[1,2],[-1,-2]]); E |
| 850 | T[{{1, 2}, {-1, -2}}] |
| 851 | sage: TL.lift(E) in TL.ambient() |
| 852 | True |
| 853 | sage: TL.lift(E) in TL |
| 854 | False |
| 855 | """ |
| 856 | assert x in self |
| 857 | monomial_indices = x.support() |
| 858 | monomial_coefficients = x.coefficients() |
| 859 | result = 0 |
| 860 | for i in xrange(len(monomial_coefficients)): |
| 861 | result += monomial_coefficients[i]*self.ambient().monomial(monomial_indices[i]) |
| 862 | return result |
| 863 | |
| 864 | def retract(self, x): |
| 865 | r""" |
| 866 | retract maps an appropriate partition algebra element to the |
| 867 | corresponding element in the Temperley-Lieb algebra. This method |
| 868 | is not intended to be called directly. |
| 869 | |
| 870 | EXAMPLES:: |
| 871 | sage: TL = TemperleyLiebAlgebra(2, x) |
| 872 | sage: PA = TL.ambient() |
| 873 | sage: E = PA([[1,2],[-1,-2]]) |
| 874 | sage: TL.retract(E) in PA |
| 875 | False |
| 876 | sage: TL.retract(E) in TL |
| 877 | True |
| 878 | """ |
| 879 | assert x in self.ambient() and set(x.support()).issubset(set(self.basis().keys())) |
| 880 | monomial_indices = x.support() |
| 881 | monomial_coefficients = x.coefficients() |
| 882 | result = self.zero() |
| 883 | for i in xrange(len(monomial_coefficients)): |
| 884 | result += monomial_coefficients[i]*self.monomial(monomial_indices[i]) |
| 885 | return result |
| 886 | |
| 887 | def product_on_basis(self, left, right): |
| 888 | r""" |
| 889 | This method is not made to be called directly but by using |
| 890 | arithematic operators on elements. |
| 891 | EXAMPLES:: |
| 892 | sage: TL = TemperleyLiebAlgebra(2, x) |
| 893 | sage: E = TL([[1,2],[-1,-2]]) |
| 894 | sage: E*E |
| 895 | x*T[{{1, 2}, {-2, -1}}] |
| 896 | sage: E^4 |
| 897 | x^3*T[{{1, 2}, {-2, -1}}] |
| 898 | """ |
| 899 | return self.ambient().product_on_basis(left,right) |
| 900 | |
| 901 | def order(self): |
| 902 | r""" |
| 903 | Returns the order of the Temperley-Lieb Algebra |
| 904 | EXAMPLES:: |
| 905 | sage: TL = TemperleyLiebAlgebra(2, x) |
| 906 | sage: TL.order() |
| 907 | 2 |
| 908 | """ |
| 909 | return self.ambient().order() |
| 910 | |
| 911 | class PlanarAlgebra(CombinatorialFreeModule): |
| 912 | r""" |
| 913 | INPUT: |
| 914 | |
| 915 | -``k``-- rank of the algebra (equals half the number of nodes in the diagrams) |
| 916 | -``q``-- a parameter. |
| 917 | |
| 918 | OPTIONAL ARGUMENTS: |
| 919 | |
| 920 | -``base_ring``-- a ring containing q (default q.parent()) |
| 921 | -``prefix``-- a label for the basis elements (default "Pl") |
| 922 | |
| 923 | The Planar algebra of rank k is an algebra with basis indexed by the |
| 924 | collection of set partitions of `\{1, \dots, k, -1, \dots, -k\}` |
| 925 | where each set partition is planar. |
| 926 | This algebra is thus a subalgebra of the partition algebra. For more |
| 927 | information, see :class:`PartitionAlgebra` |
| 928 | |
| 929 | EXAMPLES: |
| 930 | |
| 931 | The following shorthand simultaneously define the univariate polynomial |
| 932 | ring over the rationals as well as the variable `x`:: |
| 933 | |
| 934 | sage: R.<x> = PolynomialRing(QQ); R |
| 935 | Univariate Polynomial Ring in x over Rational Field |
| 936 | sage: x |
| 937 | x |
| 938 | sage: x.parent() is R |
| 939 | True |
| 940 | |
| 941 | We now define the Temperley-Lieb algebra of rank `2` with parameter |
| 942 | `x` over the Integer ring:: |
| 943 | |
| 944 | sage: Pl = PlanarAlgebra(2, x); Pl |
| 945 | Planar Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field, with prefix Pl |
| 946 | sage: Pl.basis() #random order |
| 947 | Finite family {{{1, 2, -1, -2}}: Pl[{{1, 2, -1, -2}}], {{-1}, {2, -2}, {1}}: Pl[{{-1}, {2, -2}, {1}}], {{2, -1, -2}, {1}}: Pl[{{2, -1, -2}, {1}}], {{2}, {1, -1, -2}}: Pl[{{2}, {1, -1, -2}}], {{-2}, {1, 2, -1}}: Pl[{{-2}, {1, 2, -1}}], {{-1}, {2}, {1, -2}}: Pl[{{-1}, {2}, {1, -2}}], {{-2}, {2}, {1, -1}}: Pl[{{-2}, {2}, {1, -1}}], {{1, 2}, {-1, -2}}: Pl[{{1, 2}, {-1, -2}}], {{-1}, {-2}, {2}, {1}}: Pl[{{-1}, {-2}, {2}, {1}}], {{2}, {-1, -2}, {1}}: Pl[{{2}, {-1, -2}, {1}}], {{-1}, {1, 2, -2}}: Pl[{{-1}, {1, 2, -2}}], {{2, -2}, {1, -1}}: Pl[{{2, -2}, {1, -1}}], {{-1}, {-2}, {1, 2}}: Pl[{{-1}, {-2}, {1, 2}}], {{-2}, {2, -1}, {1}}: Pl[{{-2}, {2, -1}, {1}}]} |
| 948 | sage: b = Pl.basis().list() |
| 949 | sage: b #random order |
| 950 | [Pl[{{1, 2, -1, -2}}], Pl[{{2, -1, -2}, {1}}], Pl[{{2}, {1, -1, -2}}], Pl[{{-1}, {1, 2, -2}}], Pl[{{-2}, {1, 2, -1}}], Pl[{{1, 2}, {-1, -2}}], Pl[{{2, -2}, {1, -1}}], Pl[{{2}, {-1, -2}, {1}}], Pl[{{-1}, {2, -2}, {1}}], Pl[{{-2}, {2, -1}, {1}}], Pl[{{-1}, {2}, {1, -2}}], Pl[{{-2}, {2}, {1, -1}}], Pl[{{-1}, {-2}, {1, 2}}], Pl[{{-1}, {-2}, {2}, {1}}]] |
| 951 | sage: b[6] |
| 952 | Pl[{{1, 2}, {-2, -1}}] |
| 953 | sage: b[6]^2 |
| 954 | x*Pl[{{1, 2}, {-2, -1}}] |
| 955 | sage: b[6]^5 |
| 956 | x^4*Pl[{{1, 2}, {-2, -1}}] |
| 957 | |
| 958 | """ |
| 959 | @staticmethod |
| 960 | def __classcall_private__(cls, k, q, base_ring=None, prefix="Pl"): |
| 961 | r""" |
| 962 | This method gets the base_ring from the parent of the parameter q |
| 963 | if no base_ring is given. |
| 964 | """ |
| 965 | if base_ring is None: |
| 966 | base_ring = q.parent() |
| 967 | return super(PlanarAlgebra, cls).__classcall__(cls, k, q=q, base_ring = base_ring, prefix=prefix) |
| 968 | |
| 969 | def __init__(self, k, q, base_ring, prefix = "Pl"): |
| 970 | r""" |
| 971 | See :class:`PlanarAlgebra` for full documentation. |
| 972 | |
| 973 | TESTS:: |
| 974 | |
| 975 | sage: q = var('q') |
| 976 | sage: PlA = PlanarAlgebra(2, q, RR['q']) |
| 977 | sage: TestSuite(PlA).run() |
| 978 | """ |
| 979 | self._k = k |
| 980 | self._prefix = prefix |
| 981 | self._q = base_ring(q) |
| 982 | category = FiniteDimensionalAlgebrasWithBasis(base_ring) |
| 983 | CombinatorialFreeModule.__init__(self, base_ring, planar_diagrams(k), category = category, prefix=prefix) |
| 984 | |
| 985 | @cached_method |
| 986 | def one_basis(self): |
| 987 | r""" |
| 988 | The following constructs the identity element of the |
| 989 | Temperley-Lieb algebra. It is not called directly; |
| 990 | instead one should use TL.one() if TL is a |
| 991 | previously defined Temperley-Lieb algebra instance. |
| 992 | |
| 993 | EXAMPLES:: |
| 994 | |
| 995 | sage: PlA = PlanarAlgebra(2, x); |
| 996 | sage: PlA.one_basis() |
| 997 | {{2, -2}, {1, -1}} |
| 998 | sage: PlA.one() |
| 999 | Pl[{{2, -2}, {1, -1}}] |
| 1000 | |
| 1001 | """ |
| 1002 | return identity_set_partition(self._k) |
| 1003 | |
| 1004 | def _repr_(self): |
| 1005 | msg = "Planar Algebra of rank %s with parameter %s over %s, with prefix %s" |
| 1006 | return msg % (self._k, self._q, self.base_ring(), self._prefix) |
| 1007 | |
| 1008 | def _element_constructor_(self, set_partition): |
| 1009 | r""" |
| 1010 | If PlA is a particular Planar algebra (previously defined) |
| 1011 | then PlA( sp ) returns the element of the algebra PlA corresponding |
| 1012 | to a given set partition sp. |
| 1013 | |
| 1014 | EXAMPLES:: |
| 1015 | |
| 1016 | sage: q = var('q') |
| 1017 | sage: PlA = PlanarAlgebra(2,q,QQ['q']) |
| 1018 | sage: PlA |
| 1019 | Planar Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field, with prefix Pl |
| 1020 | sage: sp = to_set_partition( [[1,2], [-1,-2]] ) # create a set partition |
| 1021 | sage: sp |
| 1022 | {{1, 2}, {-1, -2}} |
| 1023 | sage: PlA(sp) # construct a basis element from it |
| 1024 | Pl[{{1, 2}, {-1, -2}}] |
| 1025 | sage: PlA(sp) in PlA |
| 1026 | True |
| 1027 | sage: PlA([[1,2],[-1,-2]]) |
| 1028 | Pl[{{1, 2}, {-1, -2}}] |
| 1029 | sage: PlA([{1,2},{-1,-2}]) |
| 1030 | Pl[{{1, 2}, {-1, -2}}] |
| 1031 | """ |
| 1032 | if (set_partition in self.basis().keys()) and isinstance(set_partition, collections.Hashable): |
| 1033 | return self.monomial(set_partition) |
| 1034 | elif isinstance(set_partition, list): |
| 1035 | sp = to_set_partition(set_partition) # attempt coercion |
| 1036 | assert sp in self.basis().keys() # did it work? |
| 1037 | return self.monomial(sp) |
| 1038 | else: |
| 1039 | raise AssertionError, "%s invalid form" % set_partition |
| 1040 | |
| 1041 | def ambient(self): |
| 1042 | r""" |
| 1043 | ambient returns the partition algebra the Planar algebra |
| 1044 | is a sub-algebra of. Generally, this method is not called |
| 1045 | directly. |
| 1046 | |
| 1047 | EXAMPLES:: |
| 1048 | sage: PlA = PlanarAlgebra(2, x); |
| 1049 | sage: P = PlA.ambient() |
| 1050 | sage: P |
| 1051 | Partition Algebra of rank 2 with parameter x over Symbolic Ring, with prefix Pl |
| 1052 | """ |
| 1053 | return PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix) |
| 1054 | def lift(self, x): |
| 1055 | r""" |
| 1056 | lift maps a Planar algebra element to the corresponding |
| 1057 | element in the ambient space. This method is not intended |
| 1058 | to be called directly. |
| 1059 | |
| 1060 | EXAMPLES:: |
| 1061 | sage: PlA = PlanarAlgebra(2, x); |
| 1062 | sage: E = PlA([[1,2],[-1,-2]]); E |
| 1063 | Pl[{{1, 2}, {-1, -2}}] |
| 1064 | sage: PlA.lift(E) in PlA.ambient() |
| 1065 | True |
| 1066 | sage: PlA.lift(E) in PlA |
| 1067 | False |
| 1068 | """ |
| 1069 | assert x in self |
| 1070 | monomial_indices = x.support() |
| 1071 | monomial_coefficients = x.coefficients() |
| 1072 | result = 0 |
| 1073 | for i in xrange(len(monomial_coefficients)): |
| 1074 | result += monomial_coefficients[i]*self.ambient().monomial(monomial_indices[i]) |
| 1075 | return result |
| 1076 | |
| 1077 | def retract(self, x): |
| 1078 | r""" |
| 1079 | retract maps an appropriate partition algebra element to the |
| 1080 | corresponding element in the planar algebra. This method |
| 1081 | is not intended to be called directly. |
| 1082 | |
| 1083 | EXAMPLES:: |
| 1084 | sage: PlA = PlanarAlgebra(2, x) |
| 1085 | sage: PA = PlA.ambient() |
| 1086 | sage: E = PA([[1,2],[-1,-2]]) |
| 1087 | sage: PlA.retract(E) in PA |
| 1088 | False |
| 1089 | sage: PlA.retract(E) in PlA |
| 1090 | True |
| 1091 | """ |
| 1092 | assert x in self.ambient() and set(x.support()).issubset(set(self.basis().keys())) |
| 1093 | monomial_indices = x.support() |
| 1094 | monomial_coefficients = x.coefficients() |
| 1095 | result = self.zero() |
| 1096 | for i in xrange(len(monomial_coefficients)): |
| 1097 | result += monomial_coefficients[i]*self.monomial(monomial_indices[i]) |
| 1098 | return result |
| 1099 | |
| 1100 | def product_on_basis(self, left, right): |
| 1101 | r""" |
| 1102 | This method is not made to be called directly but by using |
| 1103 | arithematic operators on elements. |
| 1104 | EXAMPLES:: |
| 1105 | sage: PlA = PlanarAlgebra(2, x) |
| 1106 | sage: E = PlA([[1,2],[-1,-2]]) |
| 1107 | sage: E*E |
| 1108 | x*Pl[{{1, 2}, {-2, -1}}] |
| 1109 | sage: E^4 |
| 1110 | x^3*Pl[{{1, 2}, {-2, -1}}] |
| 1111 | """ |
| 1112 | return self.ambient().product_on_basis(left,right) |
| 1113 | |
| 1114 | def order(self): |
| 1115 | r""" |
| 1116 | Returns the order of the Planar Algebra |
| 1117 | EXAMPLES:: |
| 1118 | sage: PlA = PlanarAlgebra(2, x) |
| 1119 | sage: PlA.order() |
| 1120 | 2 |
| 1121 | """ |
| 1122 | return self.ambient().order() |
| 1123 | |
| 1124 | class IdealAlgebra(CombinatorialFreeModule): |
| 1125 | r""" |
| 1126 | INPUT: |
| 1127 | |
| 1128 | -``k``-- rank of the algebra (equals half the number of nodes in the diagrams) |
| 1129 | -``q``-- a parameter. |
| 1130 | |
| 1131 | OPTIONAL ARGUMENTS: |
| 1132 | |
| 1133 | -``base_ring``-- a ring containing q (default q.parent()) |
| 1134 | -``prefix``-- a label for the basis elements (default "I") |
| 1135 | |
| 1136 | The ideal algebra of rank k is an algebra with basis indexed by the |
| 1137 | collection of set partitions of `\{1, \dots, k, -1, \dots, -k\}` |
| 1138 | where each set partition has propogating number less than `k`. |
| 1139 | This algebra is thus a subalgebra of the partition algebra. For more |
| 1140 | information, see :class:`PartitionAlgebra` |
| 1141 | |
| 1142 | EXAMPLES: |
| 1143 | |
| 1144 | The following shorthand simultaneously define the univariate polynomial |
| 1145 | ring over the rationals as well as the variable `x`:: |
| 1146 | |
| 1147 | sage: R.<x> = PolynomialRing(QQ); R |
| 1148 | Univariate Polynomial Ring in x over Rational Field |
| 1149 | sage: x |
| 1150 | x |
| 1151 | sage: x.parent() is R |
| 1152 | True |
| 1153 | |
| 1154 | We now define the Temperley-Lieb algebra of rank `2` with parameter |
| 1155 | `x` over the Integer ring:: |
| 1156 | |
| 1157 | sage: I = IdealAlgebra(2, x); I |
| 1158 | Ideal Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field, with prefix I |
| 1159 | sage: I.basis() #random order |
| 1160 | Finite family {{{1, 2, -1, -2}}: I[{{1, 2, -1, -2}}], {{-1}, {2, -2}, {1}}: I[{{-1}, {2, -2}, {1}}], {{2, -1, -2}, {1}}: I[{{2, -1, -2}, {1}}], {{2}, {1, -1, -2}}: I[{{2}, {1, -1, -2}}], {{-2}, {1, 2, -1}}: I[{{-2}, {1, 2, -1}}], {{-1}, {2}, {1, -2}}: I[{{-1}, {2}, {1, -2}}], {{-2}, {2}, {1, -1}}: I[{{-2}, {2}, {1, -1}}], {{1, 2}, {-1, -2}}: I[{{1, 2}, {-1, -2}}], {{-1}, {-2}, {2}, {1}}: I[{{-1}, {-2}, {2}, {1}}], {{2}, {-1, -2}, {1}}: I[{{2}, {-1, -2}, {1}}], {{-1}, {1, 2, -2}}: I[{{-1}, {1, 2, -2}}], {{-1}, {-2}, {1, 2}}: I[{{-1}, {-2}, {1, 2}}], {{-2}, {2, -1}, {1}}: I[{{-2}, {2, -1}, {1}}]} |
| 1161 | sage: b = I.basis().list() |
| 1162 | sage: b #random order |
| 1163 | [I[{{1, 2, -1, -2}}], I[{{2, -1, -2}, {1}}], I[{{2}, {1, -1, -2}}], I[{{-1}, {1, 2, -2}}], I[{{-2}, {1, 2, -1}}], I[{{1, 2}, {-1, -2}}], I[{{2}, {-1, -2}, {1}}], I[{{-1}, {2, -2}, {1}}], I[{{-2}, {2, -1}, {1}}], I[{{-1}, {2}, {1, -2}}], I[{{-2}, {2}, {1, -1}}], I[{{-1}, {-2}, {1, 2}}], I[{{-1}, {-2}, {2}, {1}}]] |
| 1164 | sage: E = I([[1,2],[-1,-2]]); E |
| 1165 | I[{{1, 2}, {-1, -2}}] |
| 1166 | sage: E^2 |
| 1167 | x*I[{{1, 2}, {-2, -1}}] |
| 1168 | sage: E^5 |
| 1169 | x^4*I[{{1, 2}, {-2, -1}}] |
| 1170 | |
| 1171 | """ |
| 1172 | @staticmethod |
| 1173 | def __classcall_private__(cls, k, q, base_ring=None, prefix="I"): |
| 1174 | r""" |
| 1175 | This method gets the base_ring from the parent of the parameter q |
| 1176 | if no base_ring is given. |
| 1177 | """ |
| 1178 | if base_ring is None: |
| 1179 | base_ring = q.parent() |
| 1180 | return super(IdealAlgebra, cls).__classcall__(cls, k, q=q, base_ring = base_ring, prefix=prefix) |
| 1181 | |
| 1182 | def __init__(self, k, q, base_ring, prefix = "I"): |
| 1183 | r""" |
| 1184 | See :class:`IdealAlgebra` for full documentation. |
| 1185 | |
| 1186 | TESTS:: |
| 1187 | |
| 1188 | sage: q = var('q') |
| 1189 | sage: I = IdealAlgebra(2, q, RR['q']) |
| 1190 | sage: TestSuite(I).run() |
| 1191 | """ |
| 1192 | self._k = k |
| 1193 | self._prefix = prefix |
| 1194 | self._q = base_ring(q) |
| 1195 | category = FiniteDimensionalAlgebrasWithBasis(base_ring) |
| 1196 | CombinatorialFreeModule.__init__(self, base_ring, ideal_diagrams(k), category = category, prefix=prefix) |
| 1197 | |
| 1198 | @cached_method |
| 1199 | def one_basis(self): |
| 1200 | r""" |
| 1201 | The following constructs the identity element of the |
| 1202 | Ideal algebra. It is not called directly; |
| 1203 | instead one should use I.one() if I is a |
| 1204 | previously defined Ideal algebra instance. |
| 1205 | |
| 1206 | TODO: |
| 1207 | Make this method correct. |
| 1208 | |
| 1209 | EXAMPLES:: |
| 1210 | |
| 1211 | sage: I = IdealAlgebra(2, x); |
| 1212 | sage: I.one_basis() |
| 1213 | {{2, -2}, {1, -1}} |
| 1214 | sage: I.one() |
| 1215 | I[{{2, -2}, {1, -1}}] |
| 1216 | |
| 1217 | """ |
| 1218 | return identity_set_partition(self._k) |
| 1219 | |
| 1220 | def _repr_(self): |
| 1221 | msg = "Ideal Algebra of rank %s with parameter %s over %s, with prefix %s" |
| 1222 | return msg % (self._k, self._q, self.base_ring(), self._prefix) |
| 1223 | |
| 1224 | def _element_constructor_(self, set_partition): |
| 1225 | r""" |
| 1226 | If I is a particular Ideal algebra (previously defined) |
| 1227 | then I( sp ) returns the element of the algebra I corresponding |
| 1228 | to a given set partition sp. |
| 1229 | |
| 1230 | EXAMPLES:: |
| 1231 | |
| 1232 | sage: q = var('q') |
| 1233 | sage: I = IdealAlgebra(2,q,QQ['q']) |
| 1234 | sage: I |
| 1235 | Ideal Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field, with prefix I |
| 1236 | sage: sp = to_set_partition( [[1,2], [-1,-2]] ) # create a set partition |
| 1237 | sage: sp |
| 1238 | {{1, 2}, {-1, -2}} |
| 1239 | sage: I(sp) # construct a basis element from it |
| 1240 | I[{{1, 2}, {-1, -2}}] |
| 1241 | sage: I(sp) in I |
| 1242 | True |
| 1243 | sage: I([[1,2],[-1,-2]]) |
| 1244 | I[{{1, 2}, {-1, -2}}] |
| 1245 | sage: I([{1,2},{-1,-2}]) |
| 1246 | I[{{1, 2}, {-1, -2}}] |
| 1247 | """ |
| 1248 | if (set_partition in self.basis().keys()) and isinstance(set_partition, collections.Hashable): |
| 1249 | return self.monomial(set_partition) |
| 1250 | elif isinstance(set_partition, list): |
| 1251 | sp = to_set_partition(set_partition) # attempt coercion |
| 1252 | assert sp in self.basis().keys() # did it work? |
| 1253 | return self.monomial(sp) |
| 1254 | else: |
| 1255 | raise AssertionError, "%s invalid form" % set_partition |
| 1256 | |
| 1257 | def ambient(self): |
| 1258 | r""" |
| 1259 | ambient returns the partition algebra the Ideal algebra |
| 1260 | is a sub-algebra of. Generally, this method is not called |
| 1261 | directly. |
| 1262 | |
| 1263 | EXAMPLES:: |
| 1264 | sage: I = IdealAlgebra(2, x); |
| 1265 | sage: P = I.ambient() |
| 1266 | sage: P |
| 1267 | Partition Algebra of rank 2 with parameter x over Symbolic Ring, with prefix I |
| 1268 | """ |
| 1269 | return PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix) |
| 1270 | def lift(self, x): |
| 1271 | r""" |
| 1272 | lift maps a Ideal algebra element to the corresponding |
| 1273 | element in the ambient space. This method is not intended |
| 1274 | to be called directly. |
| 1275 | |
| 1276 | EXAMPLES:: |
| 1277 | sage: I = IdealAlgebra(2, x); |
| 1278 | sage: E = I([[1,2],[-1,-2]]); E |
| 1279 | I[{{1, 2}, {-1, -2}}] |
| 1280 | sage: I.lift(E) in I.ambient() |
| 1281 | True |
| 1282 | sage: I.lift(E) in I |
| 1283 | False |
| 1284 | """ |
| 1285 | assert x in self |
| 1286 | monomial_indices = x.support() |
| 1287 | monomial_coefficients = x.coefficients() |
| 1288 | result = 0 |
| 1289 | for i in xrange(len(monomial_coefficients)): |
| 1290 | result += monomial_coefficients[i]*self.ambient().monomial(monomial_indices[i]) |
| 1291 | return result |
| 1292 | |
| 1293 | def retract(self, x): |
| 1294 | r""" |
| 1295 | retract maps an appropriate partition algebra element to the |
| 1296 | corresponding element in the Ideal algebra. This method |
| 1297 | is not intended to be called directly. |
| 1298 | |
| 1299 | EXAMPLES:: |
| 1300 | sage: I = IdealAlgebra(2, x) |
| 1301 | sage: PA = I.ambient() |
| 1302 | sage: E = PA([[1,2],[-1,-2]]) |
| 1303 | sage: I.retract(E) in PA |
| 1304 | False |
| 1305 | sage: I.retract(E) in I |
| 1306 | True |
| 1307 | """ |
| 1308 | assert x in self.ambient() and set(x.support()).issubset(set(self.basis().keys())) |
| 1309 | monomial_indices = x.support() |
| 1310 | monomial_coefficients = x.coefficients() |
| 1311 | result = self.zero() |
| 1312 | for i in xrange(len(monomial_coefficients)): |
| 1313 | result += monomial_coefficients[i]*self.monomial(monomial_indices[i]) |
| 1314 | return result |
| 1315 | |
| 1316 | def product_on_basis(self, left, right): |
| 1317 | r""" |
| 1318 | This method is not made to be called directly but by using |
| 1319 | arithematic operators on elements. |
| 1320 | EXAMPLES:: |
| 1321 | sage: I = IdealAlgebra(2, x) |
| 1322 | sage: E = I([[1,2],[-1,-2]]) |
| 1323 | sage: E*E |
| 1324 | x*I[{{1, 2}, {-2, -1}}] |
| 1325 | sage: E^4 |
| 1326 | x^3*I[{{1, 2}, {-2, -1}}] |
| 1327 | """ |
| 1328 | return self.ambient().product_on_basis(left,right) |
| 1329 | |
| 1330 | def order(self): |
| 1331 | r""" |
| 1332 | Returns the order of the Temperley-Lieb Algebra |
| 1333 | EXAMPLES:: |
| 1334 | sage: I = IdealAlgebra(2, x) |
| 1335 | sage: I.order() |
| 1336 | 2 |
| 1337 | """ |
| 1338 | return self.ambient().order() |
| 1339 | |
| 1340 | ######################################################################### |
| 1341 | # START BORROWED CODE |
| 1342 | ######################################################################### |
| 1343 | # Borrowed from Mike Hansen's original code -- global methods for dealing |
| 1344 | # with partition diagrams, compositions of partition diagrams, and so on. |
| 1345 | # --> CHANGED 'identity' to 'identity_set_partition' for enhanced clarity. |
| 1346 | ######################################################################### |
| 1347 | |
| 1348 | def is_planar(sp): |
| 1349 | r""" |
| 1350 | Returns True if the diagram corresponding to the set partition is |
| 1351 | planar; otherwise, it returns False. |
| 1352 | |
| 1353 | EXAMPLES:: |
| 1354 | |
| 1355 | sage: import sage.combinat.partition_algebra as pa |
| 1356 | sage: pa.is_planar( pa.to_set_partition([[1,-2],[2,-1]])) |
| 1357 | False |
| 1358 | sage: pa.is_planar( pa.to_set_partition([[1,-1],[2,-2]])) |
| 1359 | True |
| 1360 | """ |
| 1361 | to_consider = map(list, sp) |
| 1362 | |
| 1363 | #Singletons don't affect planarity |
| 1364 | to_consider = filter(lambda x: len(x) > 1, to_consider) |
| 1365 | n = len(to_consider) |
| 1366 | |
| 1367 | for i in range(n): |
| 1368 | #Get the positive and negative entries of this |
| 1369 | #part |
| 1370 | ap = filter(lambda x: x>0, to_consider[i]) |
| 1371 | an = filter(lambda x: x<0, to_consider[i]) |
| 1372 | an = map(abs, an) |
| 1373 | #print a, ap, an |
| 1374 | |
| 1375 | |
| 1376 | #Check if a includes numbers in both the top and bottom rows |
| 1377 | if len(ap) > 0 and len(an) > 0: |
| 1378 | |
| 1379 | for j in range(n): |
| 1380 | if i == j: |
| 1381 | continue |
| 1382 | #Get the positive and negative entries of this part |
| 1383 | bp = filter(lambda x: x>0, to_consider[j]) |
| 1384 | bn = filter(lambda x: x<0, to_consider[j]) |
| 1385 | bn = map(abs, bn) |
| 1386 | |
| 1387 | #Skip the ones that don't involve numbers in both |
| 1388 | #the bottom and top rows |
| 1389 | if len(bn) == 0 or len(bp) == 0: |
| 1390 | continue |
| 1391 | |
| 1392 | #Make sure that if min(bp) > max(ap) |
| 1393 | #then min(bn) > max(an) |
| 1394 | if max(bp) > max(ap): |
| 1395 | if min(bn) < min(an): |
| 1396 | return False |
| 1397 | |
| 1398 | |
| 1399 | #Go through the bottom and top rows |
| 1400 | for row in [ap, an]: |
| 1401 | if len(row) > 1: |
| 1402 | row.sort() |
| 1403 | for s in range(len(row)-1): |
| 1404 | if row[s] + 1 == row[s+1]: |
| 1405 | #No gap, continue on |
| 1406 | continue |
| 1407 | else: |
| 1408 | rng = range(row[s] + 1, row[s+1]) |
| 1409 | |
| 1410 | #Go through and make sure any parts that |
| 1411 | #contain numbers in this range are completely |
| 1412 | #contained in this range |
| 1413 | for j in range(n): |
| 1414 | if i == j: |
| 1415 | continue |
| 1416 | |
| 1417 | #Make sure we make the numbers negative again |
| 1418 | #if we are in the bottom row |
| 1419 | if row is ap: |
| 1420 | sr = Set(rng) |
| 1421 | else: |
| 1422 | sr = Set(map(lambda x: -1*x, rng)) |
| 1423 | |
| 1424 | |
| 1425 | sj = Set(to_consider[j]) |
| 1426 | intersection = sr.intersection(sj) |
| 1427 | if intersection: |
| 1428 | if sj != intersection: |
| 1429 | return False |
| 1430 | |
| 1431 | return True |
| 1432 | |
| 1433 | |
| 1434 | def to_graph(sp): |
| 1435 | r""" |
| 1436 | Returns a graph representing the set partition sp. |
| 1437 | |
| 1438 | EXAMPLES:: |
| 1439 | |
| 1440 | sage: import sage.combinat.partition_algebra as pa |
| 1441 | sage: g = pa.to_graph( pa.to_set_partition([[1,-2],[2,-1]])); g |
| 1442 | Graph on 4 vertices |
| 1443 | |
| 1444 | :: |
| 1445 | |
| 1446 | sage: g.vertices() #random |
| 1447 | [1, 2, -2, -1] |
| 1448 | sage: g.edges() #random |
| 1449 | [(1, -2, None), (2, -1, None)] |
| 1450 | """ |
| 1451 | g = Graph() |
| 1452 | for part in sp: |
| 1453 | part_list = list(part) |
| 1454 | if len(part_list) > 0: |
| 1455 | g.add_vertex(part_list[0]) |
| 1456 | for i in range(1, len(part_list)): |
| 1457 | g.add_vertex(part_list[i]) |
| 1458 | g.add_edge(part_list[i-1], part_list[i]) |
| 1459 | return g |
| 1460 | |
| 1461 | def pair_to_graph(sp1, sp2): |
| 1462 | r""" |
| 1463 | Returns a graph consisting of the graphs of set partitions sp1 and |
| 1464 | sp2 along with edges joining the bottom row (negative numbers) of |
| 1465 | sp1 to the top row (positive numbers) of sp2. |
| 1466 | |
| 1467 | EXAMPLES:: |
| 1468 | |
| 1469 | sage: import sage.combinat.partition_algebra as pa |
| 1470 | sage: sp1 = pa.to_set_partition([[1,-2],[2,-1]]) |
| 1471 | sage: sp2 = pa.to_set_partition([[1,-2],[2,-1]]) |
| 1472 | sage: g = pa.pair_to_graph( sp1, sp2 ); g |
| 1473 | Graph on 8 vertices |
| 1474 | |
| 1475 | :: |
| 1476 | |
| 1477 | sage: g.vertices() #random |
| 1478 | [(1, 2), (-1, 1), (-2, 2), (-1, 2), (-2, 1), (2, 1), (2, 2), (1, 1)] |
| 1479 | sage: g.edges() #random |
| 1480 | [((1, 2), (-1, 1), None), |
| 1481 | ((1, 2), (-2, 2), None), |
| 1482 | ((-1, 1), (2, 1), None), |
| 1483 | ((-1, 2), (2, 2), None), |
| 1484 | ((-2, 1), (1, 1), None), |
| 1485 | ((-2, 1), (2, 2), None)] |
| 1486 | """ |
| 1487 | g = Graph() |
| 1488 | |
| 1489 | #Add the first set partition to the graph |
| 1490 | for part in sp1: |
| 1491 | part_list = list(part) |
| 1492 | if len(part_list) > 0: |
| 1493 | g.add_vertex( (part_list[0],1) ) |
| 1494 | |
| 1495 | #Add the edge to the second part of the graph |
| 1496 | if part_list[0] < 0 and len(part_list) > 1: |
| 1497 | g.add_edge( (part_list[0], 1), (abs(part_list[0]),2) ) |
| 1498 | |
| 1499 | for i in range(1, len(part_list)): |
| 1500 | g.add_vertex( (part_list[i],1) ) |
| 1501 | |
| 1502 | #Add the edge to the second part of the graph |
| 1503 | if part_list[i] < 0: |
| 1504 | g.add_edge( (part_list[i], 1), (abs(part_list[i]),2) ) |
| 1505 | |
| 1506 | #Add the edge between parts |
| 1507 | g.add_edge( (part_list[i-1],1), (part_list[i],1) ) |
| 1508 | |
| 1509 | #Add the second set partition to the graph |
| 1510 | for part in sp2: |
| 1511 | part_list = list(part) |
| 1512 | if len(part_list) > 0: |
| 1513 | g.add_vertex( (part_list[0],2) ) |
| 1514 | for i in range(1, len(part_list)): |
| 1515 | g.add_vertex( (part_list[i],2) ) |
| 1516 | g.add_edge( (part_list[i-1],2), (part_list[i],2) ) |
| 1517 | |
| 1518 | return g |
| 1519 | |
| 1520 | def propagating_number(sp): |
| 1521 | r""" |
| 1522 | Returns the propagating number of the set partition sp. The |
| 1523 | propagating number is the number of blocks with both a positive and |
| 1524 | negative number. |
| 1525 | |
| 1526 | EXAMPLES:: |
| 1527 | |
| 1528 | sage: import sage.combinat.partition_algebra as pa |
| 1529 | sage: sp1 = pa.to_set_partition([[1,-2],[2,-1]]) |
| 1530 | sage: sp2 = pa.to_set_partition([[1,2],[-2,-1]]) |
| 1531 | sage: pa.propagating_number(sp1) |
| 1532 | 2 |
| 1533 | sage: pa.propagating_number(sp2) |
| 1534 | 0 |
| 1535 | """ |
| 1536 | pn = 0 |
| 1537 | for part in sp: |
| 1538 | if min(part) < 0 and max(part) > 0: |
| 1539 | pn += 1 |
| 1540 | return pn |
| 1541 | |
| 1542 | def to_set_partition(l,k=None): |
| 1543 | r""" |
| 1544 | Converts a list of a list of numbers to a set partitions. Each list |
| 1545 | of numbers in the outer list specifies the numbers contained in one |
| 1546 | of the blocks in the set partition. |
| 1547 | |
| 1548 | If k is specified, then the set partition will be a set partition |
| 1549 | of 1, ..., k, -1, ..., -k. Otherwise, k will default to the minimum |
| 1550 | number needed to contain all of the specified numbers. |
| 1551 | |
| 1552 | EXAMPLES:: |
| 1553 | |
| 1554 | sage: import sage.combinat.partition_algebra as pa |
| 1555 | sage: pa.to_set_partition([[1,-1],[2,-2]]) == pa.identity(2) |
| 1556 | True |
| 1557 | """ |
| 1558 | if k == None: |
| 1559 | if l == []: |
| 1560 | return Set([]) |
| 1561 | else: |
| 1562 | k = max( map( lambda x: max( map(abs, x) ), l) ) |
| 1563 | |
| 1564 | to_be_added = Set( range(1, k+1) + map(lambda x: -1*x, range(1, k+1) ) ) |
| 1565 | |
| 1566 | sp = [] |
| 1567 | for part in l: |
| 1568 | spart = Set(part) |
| 1569 | to_be_added -= spart |
| 1570 | sp.append(spart) |
| 1571 | |
| 1572 | for singleton in to_be_added: |
| 1573 | sp.append(Set([singleton])) |
| 1574 | |
| 1575 | return Set(sp) |
| 1576 | |
| 1577 | def identity_set_partition(k): |
| 1578 | """ |
| 1579 | Returns the identity set partition 1, -1, ..., k, -k |
| 1580 | |
| 1581 | EXAMPLES:: |
| 1582 | |
| 1583 | sage: identity_set_partition(2) #random order |
| 1584 | {{2, -2}, {1, -1}} |
| 1585 | """ |
| 1586 | if k == int(k): |
| 1587 | return Set( [Set([i,-i]) for i in range(1, k + 1)] ) |
| 1588 | if k - math.floor(k) == 0.5: |
| 1589 | return Set( [Set([i, -i]) for i in range(1, k + 1.5)] ) |
| 1590 | |
| 1591 | def set_partition_composition(sp1, sp2): |
| 1592 | r""" |
| 1593 | Returns a tuple consisting of the composition of the set partitions |
| 1594 | sp1 and sp2 and the number of components removed from the middle |
| 1595 | rows of the graph. |
| 1596 | |
| 1597 | EXAMPLES:: |
| 1598 | |
| 1599 | sage: import sage.combinat.partition_algebra as pa |
| 1600 | sage: sp1 = pa.to_set_partition([[1,-2],[2,-1]]) |
| 1601 | sage: sp2 = pa.to_set_partition([[1,-2],[2,-1]]) |
| 1602 | sage: pa.set_partition_composition(sp1, sp2) == (pa.identity(2), 0) |
| 1603 | True |
| 1604 | """ |
| 1605 | g = pair_to_graph(sp1, sp2) |
| 1606 | connected_components = g.connected_components() |
| 1607 | |
| 1608 | res = [] |
| 1609 | total_removed = 0 |
| 1610 | for cc in connected_components: |
| 1611 | #Remove the vertices that live in the middle two rows |
| 1612 | new_cc = filter(lambda x: not ( (x[0]<0 and x[1] == 1) or (x[0]>0 and x[1]==2)), cc) |
| 1613 | |
| 1614 | if new_cc == []: |
| 1615 | if len(cc) > 1: |
| 1616 | total_removed += 1 |
| 1617 | else: |
| 1618 | res.append( Set(map(lambda x: x[0], new_cc)) ) |
| 1619 | |
| 1620 | |
| 1621 | return ( Set(res), total_removed ) |
| 1622 | |
| 1623 | ########################################################################## |
| 1624 | # END BORROWED CODE |
| 1625 | ########################################################################## |
| 1626 | |