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