Ticket #14234: trac_14234-folded-ts.patch

File trac_14234-folded-ts.patch, 53.9 KB (added by tscrim, 8 years ago)
  • doc/en/reference/combinat/algebra.rst

    # HG changeset patch
    # User George H. Seelinger <ghseeli@gmail.com>
    # Date 1374545893 14400
    # Node ID f68629c712ceb87dcdc1e1fc6d5043700713a8eb
    # Parent ded71e91170efd601704211e56d2e3fb5308373c
    Restructuring Diagram/Partition Algebras to match category structure
    
    diff --git a/doc/en/reference/combinat/algebra.rst b/doc/en/reference/combinat/algebra.rst
    a b Combinatorial Algebras 
    66
    77   ../sage/combinat/free_module
    88   ../sage/combinat/combinatorial_algebra
     9   ../sage/combinat/diagram_algebras
    910   ../sage/combinat/symmetric_group_algebra
    1011   ../sage/combinat/symmetric_group_representations
    1112   ../sage/combinat/schubert_polynomial
  • sage/combinat/all.py

    diff --git a/sage/combinat/all.py b/sage/combinat/all.py
    a b from skew_partition import SkewPartition 
    5656#Partition algebra
    5757from partition_algebra import SetPartitionsAk, SetPartitionsPk, SetPartitionsTk, SetPartitionsIk, SetPartitionsBk, SetPartitionsSk, SetPartitionsRk, SetPartitionsRk, SetPartitionsPRk
    5858
     59#Diagram algebra
     60from diagram_algebras import PartitionAlgebra, BrauerAlgebra, TemperleyLiebAlgebra, PlanarAlgebra, PropagatingIdeal
     61
    5962#Vector Partitions
    6063from vector_partition import VectorPartition, VectorPartitions
    6164
  • new file sage/combinat/diagram_algebras.py

    diff --git a/sage/combinat/diagram_algebras.py b/sage/combinat/diagram_algebras.py
    new file mode 100644
    - +  
     1r"""
     2Diagram and Partition Algebras
     3
     4AUTHORS:
     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
     21from sage.categories.all import FiniteDimensionalAlgebrasWithBasis
     22from sage.structure.element import generic_power
     23from sage.combinat.free_module import (CombinatorialFreeModule,
     24    CombinatorialFreeModuleElement)
     25from sage.combinat.set_partition import SetPartitions, SetPartition
     26from sage.sets.set import Set
     27from sage.graphs.graph import Graph
     28from sage.misc.cachefunc import cached_method
     29from sage.rings.all import ZZ
     30import math
     31
     32def 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
     73def 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
     104def 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
     130def 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
     157def 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
     182class 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
     472class 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
     653class 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
     737class 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
     845class 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
     953class 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
     1042class 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
     1180def 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
     1260def 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
     1285def 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
     1339def 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
     1362def 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
     1397def 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        if len(i) >= 3:
     1422            raise ValueError("blocks must have size at most 2, but {0} has {1}".format(i, len(i)))
     1423        if (len(i) == 2):
     1424            paired.append(i)
     1425        if (len(i) == 1):
     1426            not_paired.append(i)
     1427    if any(i[0] in j or -1*i[0] in j for i in not_paired for j in paired):
     1428        raise ValueError("unable to convert {0} to a Brauer partition due to the invalid block {1}".format(l, i))
     1429    for i in not_paired:
     1430        if [-1*i[0]] in not_paired:
     1431            not_paired.remove([-1*i[0]])
     1432        paired.append([i[0], -1*i[0]])
     1433    return to_set_partition(paired)
     1434
     1435def identity_set_partition(k):
     1436    """
     1437    Return the identity set partition `\{\{1, -1\}, \ldots, \{k, -k\}\}`
     1438
     1439    EXAMPLES::
     1440
     1441        sage: import sage.combinat.diagram_algebras as da
     1442        sage: da.identity_set_partition(2)
     1443        {{2, -2}, {1, -1}}
     1444    """
     1445    if k in ZZ:
     1446        return SetPartition( [[i,-i] for i in range(1, k + 1)] )
     1447    # Else k in 1/2 ZZ
     1448    return SetPartition( [[i, -i] for i in range(1, k + ZZ(3)/ZZ(2))] )
     1449
     1450def set_partition_composition(sp1, sp2):
     1451    r"""
     1452    Return a tuple consisting of the composition of the set partitions
     1453    ``sp1`` and ``sp2`` and the number of components removed from the middle
     1454    rows of the graph.
     1455
     1456    EXAMPLES::
     1457
     1458        sage: import sage.combinat.diagram_algebras as da
     1459        sage: sp1 = da.to_set_partition([[1,-2],[2,-1]])
     1460        sage: sp2 = da.to_set_partition([[1,-2],[2,-1]])
     1461        sage: da.set_partition_composition(sp1, sp2) == (da.identity_set_partition(2), 0)
     1462        True
     1463    """
     1464    g = pair_to_graph(sp1, sp2)
     1465    connected_components = g.connected_components()
     1466
     1467    res = []
     1468    total_removed = 0
     1469    for cc in connected_components:
     1470        #Remove the vertices that live in the middle two rows
     1471        new_cc = filter(lambda x: not ( (x[0]<0 and x[1] == 1) or (x[0]>0 and x[1]==2)), cc)
     1472
     1473        if new_cc == []:
     1474            if len(cc) > 1:
     1475                total_removed += 1
     1476        else:
     1477            res.append( Set(map(lambda x: x[0], new_cc)) )
     1478
     1479    return (SetPartition(Set(res)), total_removed)
     1480
     1481##########################################################################
     1482# END BORROWED CODE
     1483##########################################################################
     1484