Ticket #14234: 17710.patch

File 17710.patch, 61.0 KB (added by ghseeli, 8 years ago)
  • sage/combinat/all.py

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