Ticket #7797: trac7797-full_letterplace_wrapper_combined.patch

File trac7797-full_letterplace_wrapper_combined.patch, 117.6 KB (added by SimonKing, 7 years ago)

A full wrapper for Singular's letterplace functionality, plus positive integral degree weights, plus complete Groebner bases of weighted homogeneous two-sided ideals, plus coercion. Rel #12988

  • doc/en/reference/algebras.rst

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1301320222 -7200
    # Node ID b386895e038bfb07e5acf4658eabc61258b98d0b
    # Parent  a837878923f7fcf4745cb616b3e35cd3e57a6569
    #7797: A full wrapper for Singular's letterplace, rel #12988
    An alternative implementation of free algebras (faster arithmetic),
    introducing a UniqueFactory for free algebras.
    Degree-wise Groebner bases for twosided weighted homogeneous ideals in free algebras.
    Complete Groebner bases for weighted homogeneous twosided ideals of
    free algebras, provided they exist.
    
    diff --git a/doc/en/reference/algebras.rst b/doc/en/reference/algebras.rst
    a b  
    99   sage/algebras/free_algebra
    1010   sage/algebras/free_algebra_element
    1111
     12   sage/algebras/letterplace/free_algebra_letterplace
     13   sage/algebras/letterplace/free_algebra_element_letterplace
     14   sage/algebras/letterplace/letterplace_ideal
     15
    1216   sage/algebras/free_algebra_quotient
    1317   sage/algebras/free_algebra_quotient_element
    1418
  • module_list.py

    diff --git a/module_list.py b/module_list.py
    a b  
    114114               include_dirs = [SAGE_INC + 'FLINT/'],
    115115               depends = flint_depends),
    116116
     117    Extension('sage.algebras.letterplace.free_algebra_letterplace',
     118              sources = ['sage/algebras/letterplace/free_algebra_letterplace.pyx'],
     119              language="c++",
     120              include_dirs = [SAGE_INC +'singular/'],
     121              depends = singular_depends),
     122
     123    Extension('sage.algebras.letterplace.free_algebra_element_letterplace',
     124              sources = ['sage/algebras/letterplace/free_algebra_element_letterplace.pyx'],
     125              language="c++",
     126              include_dirs = [SAGE_INC +'singular/'],
     127              depends = singular_depends),
     128
     129    Extension('sage.algebras.letterplace.letterplace_ideal',
     130              sources = ['sage/algebras/letterplace/letterplace_ideal.pyx'],
     131              language="c++",
     132              include_dirs = [SAGE_INC +'singular/'],
     133              depends = singular_depends),
     134
    117135    Extension('sage.algebras.quatalg.quaternion_algebra_cython',
    118136               sources = ['sage/algebras/quatalg/quaternion_algebra_cython.pyx'],
    119137               language='c++',
  • sage/algebras/free_algebra.py

    diff --git a/sage/algebras/free_algebra.py b/sage/algebras/free_algebra.py
    a b  
    99  things.
    1010
    1111- Simon King (2011-04): Put free algebras into the category framework.
     12  Reimplement free algebra constructor, using a
     13  :class:`~sage.structure.factory.UniqueFactory` for handling
     14  different implementations of free algebras. Allow degree weights
     15  for free algebras in letterplace implementation.
    1216
    1317EXAMPLES::
    1418
     
    2024    sage: G.base_ring()
    2125    Free Algebra on 3 generators (x, y, z) over Integer Ring
    2226
     27The above free algebra is based on a generic implementation. By
     28:trac:`7797`, there is a different implementation
     29:class:`~sage.algebras.letterplace.free_algebra_letterplace.FreeAlgebra_letterplace`
     30based on Singular's letterplace rings. It is currently restricted to
     31weighted homogeneous elements and is therefore not the default. But the
     32arithmetic is much faster than in the generic implementation.
     33Moreover, we can compute Groebner bases with degree bound for its
     34two-sided ideals, and thus provide ideal containment tests::
     35
     36    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     37    sage: F
     38    Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     39    sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     40    sage: I.groebner_basis(degbound=4)
     41    Twosided Ideal (y*z*y*y - y*z*y*z + y*z*z*y - y*z*z*z, y*z*y*x + y*z*y*z + y*z*z*x + y*z*z*z, y*y*z*y - y*y*z*z + y*z*z*y - y*z*z*z, y*y*z*x + y*y*z*z + y*z*z*x + y*z*z*z, y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     42    sage: y*z*y*y*z*z + 2*y*z*y*z*z*x + y*z*y*z*z*z - y*z*z*y*z*x + y*z*z*z*z*x in I
     43    True
     44
     45Positive integral degree weights for the letterplace implementation
     46was introduced in trac ticket #...::
     47
     48    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     49    sage: x.degree()
     50    2
     51    sage: y.degree()
     52    1
     53    sage: z.degree()
     54    3
     55    sage: I = F*[x*y-y*x, x^2+2*y*z, (x*y)^2-z^2]*F
     56    sage: Q.<a,b,c> = F.quo(I)
     57    sage: TestSuite(Q).run()
     58    sage: a^2*b^2
     59    c*c
     60
    2361TESTS::
    2462
    2563    sage: F = FreeAlgebra(GF(5),3,'x')
    2664    sage: TestSuite(F).run()
     65    sage: F is loads(dumps(F))
     66    True
     67    sage: F = FreeAlgebra(GF(5),3,'x', implementation='letterplace')
     68    sage: TestSuite(F).run()
     69    sage: F is loads(dumps(F))
     70    True
    2771
    2872::
    2973
    3074    sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
    3175    sage: TestSuite(F).run()
     76    sage: F is loads(dumps(F))
     77    True
     78    sage: F.<x,y,z> = FreeAlgebra(GF(5),3, implementation='letterplace')
     79    sage: TestSuite(F).run()
     80    sage: F is loads(dumps(F))
     81    True
    3282
    3383::
    3484
    3585    sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'])
    3686    sage: TestSuite(F).run()
     87    sage: F is loads(dumps(F))
     88    True
     89    sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'], implementation='letterplace')
     90    sage: TestSuite(F).run()
     91    sage: F is loads(dumps(F))
     92    True
    3793
    3894::
    3995
    4096    sage: F = FreeAlgebra(GF(5),3, 'abc')
    4197    sage: TestSuite(F).run()
     98    sage: F is loads(dumps(F))
     99    True
     100    sage: F = FreeAlgebra(GF(5),3, 'abc', implementation='letterplace')
     101    sage: TestSuite(F).run()
     102    sage: F is loads(dumps(F))
     103    True
    42104
    43105::
    44106
    45     sage: F = FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x')
     107    sage: F = FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x')
    46108    sage: TestSuite(F).run()
     109    sage: F is loads(dumps(F))
     110    True
     111
     112Note that the letterplace implementation can only be used if the corresponding
     113(multivariate) polynomial ring has an implementation in Singular::
     114
     115    sage: FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x', implementation='letterplace')
     116    Traceback (most recent call last):
     117    ...
     118    NotImplementedError: The letterplace implementation is not available for the free algebra you requested
    47119
    48120"""
    49121
     
    66138
    67139import sage.structure.parent_gens
    68140
    69 def FreeAlgebra(R, n, names):
     141from sage.structure.factory import UniqueFactory
     142from sage.all import PolynomialRing
     143from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular
     144
     145class FreeAlgebraFactory(UniqueFactory):
    70146    """
    71     Return the free algebra over the ring `R` on `n`
    72     generators with given names.
    73    
    74     INPUT:
    75    
    76     -  ``R`` - ring
    77     -  ``n`` - integer
    78     -  ``names`` - string or list/tuple of n strings
    79    
    80     OUTPUT:
    81    
    82     A free algebra.
    83    
     147    A constructor of free algebras.
     148
     149    See :mod:`~sage.algebras.free_algebra` for examples and corner cases.
     150
    84151    EXAMPLES::
    85    
     152
    86153        sage: FreeAlgebra(GF(5),3,'x')
    87154        Free Algebra on 3 generators (x0, x1, x2) over Finite Field of size 5
    88155        sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     
    105172        sage: G = FreeAlgebra(ZZ,3,'x,y,z')
    106173        sage: F is G
    107174        True
     175        sage: F.<x,y,z> = FreeAlgebra(GF(5),3)  # indirect doctest
     176        sage: F is loads(dumps(F))
     177        True
     178        sage: F is FreeAlgebra(GF(5),['x','y','z'])
     179        True
     180        sage: copy(F) is F is loads(dumps(F))
     181        True
     182        sage: TestSuite(F).run()
     183
     184    By :trac:`7797`, we provide a different implementation of free
     185    algebras, based on Singular's "letterplace rings". Our letterplace
     186    wrapper allows for chosing positive integral degree weights for the
     187    generators of the free algebra. However, only (weighted) homogenous
     188    elements are supported. Of course, isomorphic algebras in different
     189    implementations are not identical::
     190
     191        sage: G = FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace')
     192        sage: F == G
     193        False
     194        sage: G is FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace')
     195        True
     196        sage: copy(G) is G is loads(dumps(G))
     197        True
     198        sage: TestSuite(G).run()
     199
     200    ::
     201
     202        sage: H = FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace', degrees=[1,2,3])
     203        sage: F != H != G
     204        True
     205        sage: H is FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace', degrees=[1,2,3])
     206        True
     207        sage: copy(H) is H is loads(dumps(H))
     208        True
     209        sage: TestSuite(H).run()
    108210   
    109211    Free algebras commute with their base ring.
    110212    ::
     
    122224        sage: c^3 * a * b^2
    123225        a*b^2*c^3
    124226    """
    125     names = sage.structure.parent_gens.normalize_names(n, names)
    126     return cache(R, n, names)
     227    def create_key(self,base_ring, arg1=None, arg2=None,
     228                                      sparse=False, order='degrevlex',
     229                                      names=None, name=None,
     230                                      implementation=None, degrees=None):
     231        """
     232        Create the key under which a free algebra is stored.
     233
     234        TESTS::
     235
     236            sage: FreeAlgebra.create_key(GF(5),['x','y','z'])
     237            (Finite Field of size 5, ('x', 'y', 'z'))
     238            sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3)
     239            (Finite Field of size 5, ('x', 'y', 'z'))
     240            sage: FreeAlgebra.create_key(GF(5),3,'xyz')
     241            (Finite Field of size 5, ('x', 'y', 'z'))
     242            sage: FreeAlgebra.create_key(GF(5),['x','y','z'], implementation='letterplace')
     243            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     244            sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3, implementation='letterplace')
     245            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     246            sage: FreeAlgebra.create_key(GF(5),3,'xyz', implementation='letterplace')
     247            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     248            sage: FreeAlgebra.create_key(GF(5),3,'xyz', implementation='letterplace', degrees=[1,2,3])
     249            ((1, 2, 3), Multivariate Polynomial Ring in x, y, z, x_ over Finite Field of size 5)
     250
     251        """
     252        if arg1 is None and arg2 is None and names is None:
     253            # this is used for pickling
     254            if degrees is None:
     255                return (base_ring,)
     256            return tuple(degrees),base_ring
     257        PolRing = None
     258        # test if we can use libSingular/letterplace
     259        if implementation is not None and implementation != 'generic':
     260            try:
     261                PolRing = PolynomialRing(base_ring, arg1, arg2,
     262                                   sparse=sparse, order=order,
     263                                   names=names, name=name,
     264                                   implementation=implementation if implementation!='letterplace' else None)
     265                if not isinstance(PolRing,MPolynomialRing_libsingular):
     266                    if PolRing.ngens() == 1:
     267                        PolRing = PolynomialRing(base_ring,1,PolRing.variable_names())
     268                        if not isinstance(PolRing,MPolynomialRing_libsingular):
     269                            raise TypeError
     270                    else:
     271                        raise TypeError
     272            except (TypeError, NotImplementedError),msg:
     273                raise NotImplementedError, "The letterplace implementation is not available for the free algebra you requested"
     274        if PolRing is not None:
     275            if degrees is None:
     276                return (PolRing,)
     277            from sage.all import TermOrder
     278            T = PolRing.term_order()+TermOrder('lex',1)
     279            varnames = list(PolRing.variable_names())
     280            newname = 'x'
     281            while newname in varnames:
     282                newname += '_'
     283            varnames.append(newname)
     284            return tuple(degrees),PolynomialRing(PolRing.base(), varnames,
     285                    sparse=sparse, order=T,
     286                    implementation=implementation if implementation!='letterplace'  else None)
     287        # normalise the generator names
     288        from sage.all import Integer
     289        if isinstance(arg1, (int, long, Integer)):
     290            arg1, arg2 = arg2, arg1
     291        if not names is None:
     292            arg1 = names
     293        elif not name is None:
     294            arg1 = name
     295        if arg2 is None:
     296            arg2 = len(arg1)
     297        names = sage.structure.parent_gens.normalize_names(arg2,arg1)
     298        return base_ring, names
     299
     300    def create_object(self, version, key):
     301        """
     302        Construct the free algebra that belongs to a unique key.
     303
     304        NOTE:
     305
     306        Of course, that method should not be called directly,
     307        since it does not use the cache of free algebras.
     308
     309        TESTS::
     310
     311            sage: FreeAlgebra.create_object('4.7.1', (QQ['x','y'],))
     312            Free Associative Unital Algebra on 2 generators (x, y) over Rational Field
     313            sage: FreeAlgebra.create_object('4.7.1', (QQ['x','y'],)) is FreeAlgebra(QQ,['x','y'])
     314            False
     315
     316        """
     317        if len(key)==1:
     318            from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     319            return FreeAlgebra_letterplace(key[0])
     320        if isinstance(key[0],tuple):
     321            from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     322            return FreeAlgebra_letterplace(key[1],degrees=key[0])
     323        return FreeAlgebra_generic(key[0],len(key[1]),key[1])
     324
     325FreeAlgebra = FreeAlgebraFactory('FreeAlgebra')
     326
    127327
    128328def is_FreeAlgebra(x):
    129329    """
     
    138338        False
    139339        sage: is_FreeAlgebra(FreeAlgebra(ZZ,100,'x'))
    140340        True
     341        sage: is_FreeAlgebra(FreeAlgebra(ZZ,10,'x',implementation='letterplace'))
     342        True
     343        sage: is_FreeAlgebra(FreeAlgebra(ZZ,10,'x',implementation='letterplace', degrees=range(1,11)))
     344        True
     345
    141346    """
    142     return isinstance(x, FreeAlgebra_generic)
     347    from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     348    return isinstance(x, (FreeAlgebra_generic,FreeAlgebra_letterplace))
    143349
    144350
    145351class FreeAlgebra_generic(Algebra):
     
    158364        x*y*x*y*x*y*x*y*x*y*x*y + x*y*z*x*y*z*x*y*z*x*y*z
    159365        sage: (2 + x*z + x^2)^2 + (x - y)^2
    160366        4 + 5*x^2 - x*y + 4*x*z - y*x + y^2 + x^4 + x^3*z + x*z*x^2 + x*z*x*z
     367
     368    TESTS:
     369
     370    Free algebras commute with their base ring.
     371    ::
     372
     373        sage: K.<a,b> = FreeAlgebra(QQ)
     374        sage: K.is_commutative()
     375        False
     376        sage: L.<c,d> = FreeAlgebra(K)
     377        sage: L.is_commutative()
     378        False
     379        sage: s = a*b^2 * c^3; s
     380        a*b^2*c^3
     381        sage: parent(s)
     382        Free Algebra on 2 generators (c, d) over Free Algebra on 2 generators (a, b) over Rational Field
     383        sage: c^3 * a * b^2
     384        a*b^2*c^3
     385
    161386    """
    162387    Element = FreeAlgebraElement
    163388    def __init__(self, R, n, names):
     
    174399   
    175400            sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet
    176401            Free Algebra on 3 generators (x, y, z) over Rational Field
     402
     403        TEST:
     404
     405        Note that the following is *not* the recommended way to create
     406        a free algebra.
     407        ::
     408
     409            sage: from sage.algebras.free_algebra import FreeAlgebra_generic
     410            sage: FreeAlgebra_generic(ZZ,3,'abc')
     411            Free Algebra on 3 generators (a, b, c) over Integer Ring
     412
    177413        """
    178414        if not isinstance(R, Ring):
    179415            raise TypeError("Argument R must be a ring.")
     
    218454    def __cmp__(self, other):
    219455        """
    220456        Two free algebras are considered the same if they have the same
    221         base ring, number of generators and variable names.
     457        base ring, number of generators and variable names, and the same
     458        implementation.
    222459       
    223460        EXAMPLES::
    224461       
     
    233470            False
    234471            sage: F == FreeAlgebra(QQ,3,'y')
    235472            False
     473
     474        Note that since :trac:`7797` there is a different
     475        implementation of free algebras. Two corresponding free
     476        algebras in different implementations are not equal, but there
     477        is a coercion::
     478
     479
    236480        """
    237481        if not isinstance(other, FreeAlgebra_generic):
    238482            return -1
    239483        c = cmp(self.base_ring(), other.base_ring())
    240484        if c: return c
    241         c = cmp(self.__ngens, other.__ngens)
     485        c = cmp(self.__ngens, other.ngens())
    242486        if c: return c
    243487        c = cmp(self.variable_names(), other.variable_names())
    244488        if c: return c
     
    251495        EXAMPLES::
    252496       
    253497            sage: F = FreeAlgebra(QQ,3,'x')
    254             sage: print F  # indirect doctest
     498            sage: F  # indirect doctest
    255499            Free Algebra on 3 generators (x0, x1, x2) over Rational Field
    256500            sage: F.rename('QQ<<x0,x1,x2>>')
    257             sage: print F #indirect doctest
     501            sage: F #indirect doctest
    258502            QQ<<x0,x1,x2>>
    259503            sage: FreeAlgebra(ZZ,1,['a'])
    260504            Free Algebra on 1 generators (a,) over Integer Ring
     
    264508
    265509    def _element_constructor_(self, x):
    266510        """
    267        Coerce x into self.
     511       Convert x into self.
    268512       
    269513       EXAMPLES::
    270514       
    271515           sage: R.<x,y> = FreeAlgebra(QQ,2)
    272516           sage: R(3) # indirect doctest
    273517           3
     518
     519       TESTS::
     520
     521           sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     522           sage: L.<x,y,z> = FreeAlgebra(ZZ,3,implementation='letterplace')
     523           sage: F(x)     # indirect doctest
     524           x
     525           sage: F.1*L.2
     526           y*z
     527           sage: (F.1*L.2).parent() is F
     528           True
     529
     530       ::
     531
     532           sage: K.<z> = GF(25)
     533           sage: F.<a,b,c> = FreeAlgebra(K,3)
     534           sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace')
     535           sage: F.1+(z+1)*L.2
     536           b + (z+1)*c
     537
    274538       """
    275539        if isinstance(x, FreeAlgebraElement):
    276540            P = x.parent()
     
    278542                return x
    279543            if not (P is self.base_ring()):
    280544                return self.element_class(self, x)
     545        elif hasattr(x,'letterplace_polynomial'):
     546            P = x.parent()
     547            if self.has_coerce_map_from(P): # letterplace versus generic
     548                ngens = P.ngens()
     549                M = self.__monoid
     550                def exp_to_monomial(T):
     551                    out = []
     552                    for i in xrange(len(T)):
     553                        if T[i]:
     554                            out.append((i%ngens,T[i]))
     555                    return M(out)
     556                return self.element_class(self, dict([(exp_to_monomial(T),c) for T,c in x.letterplace_polynomial().dict().iteritems()]))
    281557        # ok, not a free algebra element (or should not be viewed as one).
     558        if isinstance(x, basestring):
     559            from sage.all import sage_eval
     560            return sage_eval(x,locals=self.gens_dict())
    282561        F = self.__monoid
    283562        R = self.base_ring()
    284563        # coercion from free monoid
     
    299578
    300579        - this free algebra
    301580
     581        - a free algebra in letterplace implementation that has
     582          the same generator names and whose base ring coerces
     583          into self's base ring
     584
    302585        - the underlying monoid
    303586
    304587        - anything that coerces to the base ring of this free algebra
     
    366649            TypeError: no canonical coercion from Free Algebra on 3 generators
    367650            (x, y, z) over Finite Field of size 7 to Free Algebra on 3
    368651            generators (x, y, z) over Integer Ring
     652
     653        TESTS::
     654
     655           sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     656           sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace')
     657           sage: F(x)
     658           x
     659           sage: F.1*L.2     # indirect doctest
     660           y*z
     661
    369662        """
    370663        try:
    371664            R = x.parent()
     
    419712            True
    420713            sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z'))
    421714            False
     715            sage: K.<z> = GF(25)
     716            sage: F.<a,b,c> = FreeAlgebra(K,3)
     717            sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace')
     718            sage: F.1+(z+1)*L.2      # indirect doctest
     719            b + (z+1)*c
     720
    422721        """
    423722        if self.__monoid.has_coerce_map_from(R):
    424723            return True
  • sage/algebras/free_algebra_element.py

    diff --git a/sage/algebras/free_algebra_element.py b/sage/algebras/free_algebra_element.py
    a b  
    7878        EXAMPLES::
    7979       
    8080            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    81             sage: repr(-x+3*y*z)
     81            sage: repr(-x+3*y*z)    # indirect doctest
    8282            '-x + 3*y*z'
    8383
    8484        Trac ticket #11068 enables the use of local variable names::
     
    105105        EXAMPLES::
    106106       
    107107            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    108             sage: latex(-x+3*y^20*z)
     108            sage: latex(-x+3*y^20*z)   # indirect doctest
    109109            \left(-1\right)x + 3y^{20}z
    110110            sage: alpha,beta,gamma=FreeAlgebra(ZZ,3,'alpha,beta,gamma').gens()
    111111            sage: latex(alpha-beta)
     
    197197        EXAMPLES::
    198198       
    199199            sage: R.<x,y> = FreeAlgebra(QQ,2)
    200             sage: x + y
     200            sage: x + y    # indirect doctest
    201201            x + y
    202202        """
    203203        A = self.parent()
     
    234234        EXAMPLES::
    235235       
    236236            sage: R.<x,y> = FreeAlgebra(QQ,2)
    237             sage: -(x+y)
     237            sage: -(x+y)    # indirect doctest
    238238            -x - y
    239239        """
    240240        y = self.parent()(0)
     
    252252        EXAMPLES::
    253253       
    254254            sage: R.<x,y> = FreeAlgebra(QQ,2)
    255             sage: x - y
     255            sage: x - y    # indirect doctest
    256256            x - y
    257257        """
    258258        A = self.parent()
     
    290290        EXAMPLES::
    291291       
    292292            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    293             sage: (x+y+x*y)*(x+y+1)
     293            sage: (x+y+x*y)*(x+y+1)    # indirect doctest
    294294            x + y + x^2 + 2*x*y + y*x + y^2 + x*y*x + x*y^2
    295295        """
    296296        A = self.parent()
  • sage/algebras/free_algebra_quotient.py

    diff --git a/sage/algebras/free_algebra_quotient.py b/sage/algebras/free_algebra_quotient.py
    a b  
    11"""
    2 Free algebra quotients
     2Finite dimensional free algebra quotients
     3
     4REMARK:
     5
     6This implementation only works for finite dimensional quotients, since
     7a list of basis monomials and the multiplication matrices need to be
     8explicitly provided.
     9
     10The homogeneous part of a quotient of a free algebra over a field by a
     11finitely generated homogeneous twosided ideal is available in a
     12different implementation. See
     13:mod:`~sage.algebras.letterplace.free_algebra_letterplace` and
     14:mod:`~sage.rings.quotient_ring`.
    315
    416TESTS::
    517
  • new file sage/algebras/letterplace/__init__.py

    diff --git a/sage/algebras/letterplace/__init__.py b/sage/algebras/letterplace/__init__.py
    new file mode 100644
    - +  
     1# tell Python that letterplace exists
  • new file sage/algebras/letterplace/free_algebra_element_letterplace.pxd

    diff --git a/sage/algebras/letterplace/free_algebra_element_letterplace.pxd b/sage/algebras/letterplace/free_algebra_element_letterplace.pxd
    new file mode 100644
    - +  
     1###############################################################################
     2#
     3#       Copyright (C) 2011 Simon King <simon.king@uni-jena.de>
     4#  Distributed under the terms of the GNU General Public License (GPL),
     5#  version 2 or any later version.  The full text of the GPL is available at:
     6#                  http://www.gnu.org/licenses/
     7#
     8###############################################################################
     9
     10cdef class FreeAlgebraElement_letterplace
     11
     12from sage.structure.element cimport AlgebraElement, ModuleElement, RingElement, Element
     13from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_libsingular, MPolynomial_libsingular
     14from sage.algebras.letterplace.free_algebra_letterplace cimport FreeAlgebra_letterplace
     15include "../../ext/stdsage.pxi"
     16
     17cdef class FreeAlgebraElement_letterplace(AlgebraElement):
     18    cdef MPolynomial_libsingular _poly
     19
  • new file sage/algebras/letterplace/free_algebra_element_letterplace.pyx

    diff --git a/sage/algebras/letterplace/free_algebra_element_letterplace.pyx b/sage/algebras/letterplace/free_algebra_element_letterplace.pyx
    new file mode 100644
    - +  
     1###############################################################################
     2#
     3#       Copyright (C) 2011 Simon King <simon.king@uni-jena.de>
     4#  Distributed under the terms of the GNU General Public License (GPL),
     5#  version 2 or any later version.  The full text of the GPL is available at:
     6#                  http://www.gnu.org/licenses/
     7#
     8###############################################################################
     9
     10"""
     11Weighted homogeneous elements of free algebras, in letterplace implementation.
     12
     13AUTHOR:
     14
     15- Simon King (2011-03-23): Trac ticket :trac:`7797`
     16
     17"""
     18
     19from sage.libs.singular.function import lib, singular_function
     20from sage.misc.misc import repr_lincomb
     21from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal
     22
     23# Define some singular functions
     24lib("freegb.lib")
     25poly_reduce = singular_function("NF")
     26singular_system=singular_function("system")
     27
     28#####################
     29# Free algebra elements
     30cdef class FreeAlgebraElement_letterplace(AlgebraElement):
     31    """
     32    Weighted homogeneous elements of a free associative unital algebra (letterplace implementation)
     33
     34    EXAMPLES::
     35
     36        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     37        sage: x+y
     38        x + y
     39        sage: x*y !=y*x
     40        True
     41        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     42        sage: (y^3).reduce(I)
     43        y*y*y
     44        sage: (y^3).normal_form(I)
     45        y*y*z - y*z*y + y*z*z
     46
     47    Here is an example with nontrivial degree weights::
     48
     49        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     50        sage: I = F*[x*y-y*x, x^2+2*y*z, (x*y)^2-z^2]*F
     51        sage: x.degree()
     52        2
     53        sage: y.degree()
     54        1
     55        sage: z.degree()
     56        3
     57        sage: (x*y)^3
     58        x*y*x*y*x*y
     59        sage: ((x*y)^3).normal_form(I)
     60        z*z*y*x
     61        sage: ((x*y)^3).degree()
     62        9
     63
     64    """
     65    def __init__(self, A, x, check=True):
     66        """
     67        INPUT:
     68
     69        - A free associative unital algebra in letterplace implementation, `A`.
     70        - A homogeneous polynomial that can be coerced into the currently
     71          used polynomial ring of `A`.
     72        - ``check`` (optional bool, default ``True``): Do not attempt the
     73          above coercion (for internal use only).
     74
     75        TEST::
     76
     77            sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace
     78            sage: F.<x,y,z> = FreeAlgebra(GF(3), implementation='letterplace')
     79            sage: F.set_degbound(2)
     80            sage: P = F.current_ring()
     81            sage: F.set_degbound(4)
     82            sage: P == F.current_ring()
     83            False
     84            sage: p = FreeAlgebraElement_letterplace(F,P.1*P.3+2*P.0*P.4); p
     85            -x*y + y*x
     86            sage: loads(dumps(p)) == p
     87            True
     88
     89        """
     90        cdef FreeAlgebra_letterplace P = A
     91        if check:
     92            if not x.is_homogeneous():
     93                raise ValueError, "Free algebras based on Letterplace can currently only work with weighted homogeneous elements"
     94            P.set_degbound(x.degree())
     95            x = P._current_ring(x)
     96        AlgebraElement.__init__(self,P)
     97        self._poly = x
     98    def __reduce__(self):
     99        """
     100        Pickling.
     101
     102        TEST::
     103
     104            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     105            sage: loads(dumps(x*y*x)) == x*y*x   # indirect doctest
     106            True
     107
     108        """
     109        return self.__class__, (self._parent,self._poly)
     110    def __copy__(self):
     111        """
     112        TEST::
     113
     114            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     115            sage: copy(x*y*z+z*y*x) == x*y*z+z*y*x   # indirect doctest
     116            True
     117
     118        """
     119        self._poly = (<FreeAlgebra_letterplace>self._parent)._current_ring(self._poly)
     120        return self.__class__(self._parent,self._poly,check=False)
     121    def __hash__(self):
     122        """
     123        TEST::
     124
     125            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     126            sage: set([x*y*z, z*y+x*z,x*y*z])  # indirect doctest
     127            set([x*z + z*y, x*y*z])
     128
     129        """
     130        return hash(self._poly)
     131    def __iter__(self):
     132        """
     133        Iterates over the pairs "tuple of exponents, coefficient".
     134
     135        EXAMPLE::
     136
     137            sage: F.<w,x,y,z> = FreeAlgebra(GF(3), implementation='letterplace')
     138            sage: p = x*y-z^2
     139            sage: list(p)   # indirect doctest
     140            [((0, 0, 0, 1, 0, 0, 0, 1), 2), ((0, 1, 0, 0, 0, 0, 1, 0), 1)]
     141
     142        """
     143        return self._poly.dict().iteritems()
     144    def _repr_(self):
     145        """
     146        TEST::
     147
     148            sage: K.<z> = GF(25)
     149            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     150            sage: -(a+b*(z+1)-c)^2     # indirect doctest
     151            -a*a + (4*z + 4)*a*b + a*c + (4*z + 4)*b*a + (2*z + 1)*b*b + (z + 1)*b*c + c*a + (z + 1)*c*b - c*c
     152
     153        It is possible to change the names temporarily::
     154
     155            sage: from sage.structure.parent_gens import localvars
     156            sage: with localvars(F, ['w', 'x','y']):
     157            ...     print a+b*(z+1)-c
     158            w + (z + 1)*x - y
     159            sage: print a+b*(z+1)-c
     160            a + (z + 1)*b - c
     161
     162        """
     163        cdef list L = []
     164        cdef FreeAlgebra_letterplace P = self._parent
     165        cdef int ngens = P.__ngens
     166        if P._base.is_atomic_repr():
     167            for E,c in zip(self._poly.exponents(),self._poly.coefficients()):
     168                monstr = P.exponents_to_string(E)
     169                if monstr:
     170                    if c==1:
     171                        if L:
     172                            L.extend(['+',monstr])
     173                        else:
     174                            L.append(monstr)
     175                    elif c==-1:
     176                        if L:
     177                            L.extend(['-',monstr])
     178                        else:
     179                            L.append('-'+monstr)
     180                    else:
     181                        if L:
     182                            if c>=0:
     183                                L.extend(['+',repr(c)+'*'+monstr])
     184                            else:
     185                                L.extend(['-',repr(-c)+'*'+monstr])
     186                        else:
     187                            L.append(repr(c)+'*'+monstr)
     188                else:
     189                    if c>=0:
     190                        if L:
     191                            L.extend(['+',repr(c)])
     192                        else:
     193                            L.append(repr(c))
     194                    else:
     195                        if L:
     196                            L.extend(['-',repr(-c)])
     197                        else:
     198                            L.append(repr(c))
     199        else:
     200            for E,c in zip(self._poly.exponents(),self._poly.coefficients()):
     201                monstr = P.exponents_to_string(E)
     202                if monstr:
     203                    if c==1:
     204                        if L:
     205                            L.extend(['+',monstr])
     206                        else:
     207                            L.append(monstr)
     208                    elif c==-1:
     209                        if L:
     210                            L.extend(['-',monstr])
     211                        else:
     212                            L.append('-'+monstr)
     213                    else:
     214                        if L:
     215                            L.extend(['+','('+repr(c)+')*'+monstr])
     216                        else:
     217                            L.append('('+repr(c)+')*'+monstr)
     218                else:
     219                    if L:
     220                        L.extend(['+',repr(c)])
     221                    else:
     222                        L.append(repr(c))
     223        if L:
     224            return ' '.join(L)
     225        return '0'
     226
     227    def _latex_(self):
     228        """
     229        TEST::
     230
     231            sage: K.<z> = GF(25)
     232            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace', degrees=[1,2,3])
     233            sage: -(a*b*(z+1)-c)^2
     234            (2*z + 1)*a*b*a*b + (z + 1)*a*b*c + (z + 1)*c*a*b - c*c
     235            sage: latex(-(a*b*(z+1)-c)^2)     # indirect doctest
     236            \left(2 z + 1\right) a b a b + \left(z + 1\right) a b c + \left(z + 1\right) c a b - c c
     237
     238        """
     239        cdef list L = []
     240        cdef FreeAlgebra_letterplace P = self._parent
     241        cdef int ngens = P.__ngens
     242        from sage.all import latex
     243        if P._base.is_atomic_repr():
     244            for E,c in zip(self._poly.exponents(),self._poly.coefficients()):
     245                monstr = P.exponents_to_latex(E)
     246                if monstr:
     247                    if c==1:
     248                        if L:
     249                            L.extend(['+',monstr])
     250                        else:
     251                            L.append(monstr)
     252                    elif c==-1:
     253                        if L:
     254                            L.extend(['-',monstr])
     255                        else:
     256                            L.append('-'+monstr)
     257                    else:
     258                        if L:
     259                            if c>=0:
     260                                L.extend(['+',repr(latex(c))+' '+monstr])
     261                            else:
     262                                L.extend(['-',repr(latex(-c))+' '+monstr])
     263                        else:
     264                            L.append(repr(latex(c))+' '+monstr)
     265                else:
     266                    if c>=0:
     267                        if L:
     268                            L.extend(['+',repr(latex(c))])
     269                        else:
     270                            L.append(repr(latex(c)))
     271                    else:
     272                        if L:
     273                            L.extend(['-',repr(latex(-c))])
     274                        else:
     275                            L.append(repr(c))
     276        else:
     277            for E,c in zip(self._poly.exponents(),self._poly.coefficients()):
     278                monstr = P.exponents_to_latex(E)
     279                if monstr:
     280                    if c==1:
     281                        if L:
     282                            L.extend(['+',monstr])
     283                        else:
     284                            L.append(monstr)
     285                    elif c==-1:
     286                        if L:
     287                            L.extend(['-',monstr])
     288                        else:
     289                            L.append('-'+monstr)
     290                    else:
     291                        if L:
     292                            L.extend(['+','\\left('+repr(latex(c))+'\\right) '+monstr])
     293                        else:
     294                            L.append('\\left('+repr(latex(c))+'\\right) '+monstr)
     295                else:
     296                    if L:
     297                        L.extend(['+',repr(latex(c))])
     298                    else:
     299                        L.append(repr(latex(c)))
     300        if L:
     301            return ' '.join(L)
     302        return '0'
     303
     304    def degree(self):
     305        """
     306        Return the degree of this element.
     307
     308        NOTE:
     309
     310        Generators may have a positive integral degree weight. All
     311        elements must be weighted homogeneous.
     312
     313        EXAMPLE::
     314
     315            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     316            sage: ((x+y+z)^3).degree()
     317            3
     318            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     319            sage: ((x*y+z)^3).degree()
     320            9
     321
     322        """
     323        return self._poly.degree()
     324
     325    def letterplace_polynomial(self):
     326        """
     327        Return the commutative polynomial that is used internally to represent this free algebra element.
     328
     329        EXAMPLE::
     330
     331            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     332            sage: ((x+y-z)^2).letterplace_polynomial()
     333            x*x_1 + x*y_1 - x*z_1 + y*x_1 + y*y_1 - y*z_1 - z*x_1 - z*y_1 + z*z_1
     334
     335        If degree weights are used, the letterplace polynomial is
     336        homogenized by slack variables::
     337
     338            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     339            sage: ((x*y+z)^2).letterplace_polynomial()
     340            x*x__1*y_2*x_3*x__4*y_5 + x*x__1*y_2*z_3*x__4*x__5 + z*x__1*x__2*x_3*x__4*y_5 + z*x__1*x__2*z_3*x__4*x__5
     341
     342        """
     343        return self._poly
     344
     345    def lm(self):
     346        """
     347        The leading monomial of this free algebra element.
     348
     349        EXAMPLE::
     350
     351            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     352            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lm()
     353            x*x*y
     354            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     355            sage: ((2*x*y+z)^2).lm()
     356            x*y*x*y
     357
     358        """
     359        return FreeAlgebraElement_letterplace(self._parent, self._poly.lm())
     360
     361    def lt(self):
     362        """
     363        The leading term (monomial times coefficient) of this free algebra
     364        element.
     365
     366        EXAMPLE::
     367
     368            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     369            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lt()
     370            20*x*x*y
     371            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     372            sage: ((2*x*y+z)^2).lt()
     373            4*x*y*x*y
     374
     375        """
     376        return FreeAlgebraElement_letterplace(self._parent, self._poly.lt())
     377
     378    def lc(self):
     379        """
     380        The leading coefficient of this free algebra element, as element
     381        of the base ring.
     382
     383        EXAMPLE::
     384
     385            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     386            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lc()
     387            20
     388            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lc().parent() is F.base()
     389            True
     390            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     391            sage: ((2*x*y+z)^2).lc()
     392            4
     393
     394        """
     395        return self._poly.lc()
     396
     397    def __nonzero__(self):
     398        """
     399        TEST::
     400
     401            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     402            sage: bool(x)      # indirect doctest
     403            True
     404            sage: bool(F.zero())
     405            False
     406
     407        """
     408        return bool(self._poly)
     409
     410    def lm_divides(self, FreeAlgebraElement_letterplace p):
     411        """
     412        Tell whether or not the leading monomial of self devides the
     413        leading monomial of another element.
     414
     415        NOTE:
     416
     417        A free algebra element `p` divides another one `q` if there are
     418        free algebra elements `s` and `t` such that `spt = q`.
     419
     420        EXAMPLE::
     421
     422            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     423            sage: ((2*x*y+z)^2*z).lm()
     424            x*y*x*y*z
     425            sage: (y*x*y-y^4).lm()
     426            y*x*y
     427            sage: (y*x*y-y^4).lm_divides((2*x*y+z)^2*z)
     428            True
     429
     430        """
     431        if self._parent is not p._parent:
     432            raise TypeError, "The two arguments must be elements in the same free algebra."
     433        cdef FreeAlgebra_letterplace A = self._parent
     434        P = A._current_ring
     435        p_poly = p._poly = P(p._poly)
     436        s_poly = self._poly = P(self._poly)
     437        cdef int p_d = p_poly.degree()
     438        cdef int s_d = s_poly.degree()
     439        if s_d>p_d:
     440            return False
     441        cdef int i
     442        if P.monomial_divides(s_poly,p_poly):
     443            return True
     444        for i from 0 <= i < p_d-s_d:
     445            s_poly = singular_system("stest",s_poly,1,
     446                                     A._degbound,A.__ngens,ring=P)
     447            if P.monomial_divides(s_poly,p_poly):
     448                return True
     449        return False
     450
     451    def __richcmp__(left, right, int op):
     452        """
     453        TEST::
     454
     455            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     456            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     457            sage: p-p.lt()<p    # indirect doctest
     458            True
     459
     460        """
     461        return (<Element>left)._richcmp(right, op)
     462    def __cmp__(left, right):
     463        """
     464        TEST::
     465
     466            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     467            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     468            sage: cmp(p,p-p.lt())    # indirect doctest
     469            1
     470
     471        """
     472        return (<Element>left)._cmp(right)
     473
     474    cdef int _cmp_c_impl(self, Element other) except -2:
     475        """
     476        Auxiliary method for comparison.
     477
     478        TEST::
     479
     480            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     481            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     482            sage: p-p.lt()<p    # indirect doctest
     483            True
     484
     485        """
     486        cdef int c = cmp(type(self),type(other))
     487        if c: return c
     488        return cmp((<FreeAlgebraElement_letterplace>self)._poly,(<FreeAlgebraElement_letterplace>other)._poly)
     489
     490    ################################
     491    ## Arithmetic
     492    cpdef ModuleElement _neg_(self):
     493        """
     494        TEST::
     495
     496            sage: K.<z> = GF(25)
     497            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     498            sage: -((z+2)*a^2*b+3*c^3)  # indirect doctest
     499            (4*z + 3)*a*a*b + (2)*c*c*c
     500            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     501            sage: -(3*x*y+2*z^2)
     502            -3*x*y - 2*z*z
     503
     504        """
     505        return FreeAlgebraElement_letterplace(self._parent,-self._poly,check=False)
     506    cpdef ModuleElement _add_(self, ModuleElement other):
     507        """
     508        Addition, under the side condition that either one summand
     509        is zero, or both summands have the same degree.
     510
     511        TEST::
     512
     513            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     514            sage: x+y    # indirect doctest
     515            x + y
     516            sage: x+1
     517            Traceback (most recent call last):
     518            ...
     519            ArithmeticError: Can only add elements of the same weighted degree
     520            sage: x+0
     521            x
     522            sage: 0+x
     523            x
     524
     525        """
     526        if not other:
     527            return self
     528        if not self:
     529            return other
     530        cdef FreeAlgebraElement_letterplace right = other
     531        if right._poly.degree()!=self._poly.degree():
     532            raise ArithmeticError, "Can only add elements of the same weighted degree"
     533        # update the polynomials
     534        cdef FreeAlgebra_letterplace A = self._parent
     535        self._poly = A._current_ring(self._poly)
     536        right._poly = A._current_ring(right._poly)
     537        return FreeAlgebraElement_letterplace(self._parent,self._poly+right._poly,check=False)
     538
     539    cpdef ModuleElement _sub_(self, ModuleElement other):
     540        """
     541        Difference, under the side condition that either one summand
     542        is zero or both have the same weighted degree.
     543
     544        TEST::
     545
     546            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     547            sage: x*y-y*x     # indirect doctest
     548            x*y - y*x
     549            sage: x-1
     550            Traceback (most recent call last):
     551            ...
     552            ArithmeticError: Can only subtract elements of the same degree
     553            sage: x-0
     554            x
     555            sage: 0-x
     556            -x
     557
     558        Here is an example with non-trivial degree weights::
     559
     560            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     561            sage: x*y+z
     562            x*y + z
     563
     564        """
     565        if not other:
     566            return self
     567        if not self:
     568            return -other
     569        cdef FreeAlgebraElement_letterplace right = other
     570        if right._poly.degree()!=self._poly.degree():
     571            raise ArithmeticError, "Can only subtract elements of the same degree"
     572        # update the polynomials
     573        cdef FreeAlgebra_letterplace A = self._parent
     574        self._poly = A._current_ring(self._poly)
     575        right._poly = A._current_ring(right._poly)
     576        return FreeAlgebraElement_letterplace(self._parent,self._poly-right._poly,check=False)
     577
     578    cpdef ModuleElement _lmul_(self, RingElement right):
     579        """
     580        Multiplication from the right with an element of the base ring.
     581
     582        TEST::
     583
     584            sage: K.<z> = GF(25)
     585            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     586            sage: (a+b)*(z+1)    # indirect doctest
     587            (z + 1)*a + (z + 1)*b
     588
     589        """
     590        return FreeAlgebraElement_letterplace(self._parent,self._poly._lmul_(right),check=False)
     591
     592    cpdef ModuleElement _rmul_(self, RingElement left):
     593        """
     594        Multiplication from the left with an element of the base ring.
     595
     596        TEST::
     597
     598            sage: K.<z> = GF(25)
     599            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     600            sage: (z+1)*(a+b)   # indirect doctest
     601            (z + 1)*a + (z + 1)*b
     602
     603        """
     604        return FreeAlgebraElement_letterplace(self._parent,self._poly._rmul_(left),check=False)
     605
     606    cpdef RingElement _mul_(self, RingElement other):
     607        """
     608        Product of two free algebra elements in letterplace implementation.
     609
     610        TEST::
     611
     612            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     613            sage: (x*y+z)*z   # indirect doctest
     614            x*y*z + z*z
     615
     616        """
     617        cdef FreeAlgebraElement_letterplace left = self
     618        cdef FreeAlgebraElement_letterplace right = other
     619        cdef FreeAlgebra_letterplace A = left._parent
     620        A.set_degbound(left._poly.degree()+right._poly.degree())
     621        # we must put the polynomials into the same ring
     622        left._poly = A._current_ring(left._poly)
     623        right._poly = A._current_ring(right._poly)
     624        rshift = singular_system("stest",right._poly,left._poly.degree(),A._degbound,A.__ngens, ring=A._current_ring)
     625        return FreeAlgebraElement_letterplace(A,left._poly*rshift, check=False)
     626
     627    def __pow__(FreeAlgebraElement_letterplace self, int n, k):
     628        """
     629        TEST::
     630
     631            sage: K.<z> = GF(25)
     632            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     633            sage: (a+z*b)^3    # indirect doctest
     634            a*a*a + (z)*a*a*b + (z)*a*b*a + (z + 3)*a*b*b + (z)*b*a*a + (z + 3)*b*a*b + (z + 3)*b*b*a + (4*z + 3)*b*b*b
     635
     636        """
     637        cdef FreeAlgebra_letterplace A = self._parent
     638        if n<0:
     639            raise ValueError, "Negative exponents are not allowed"
     640        if n==0:
     641            return FreeAlgebraElement_letterplace(A, A._current_ring(1),
     642                                                  check=False)
     643        if n==1:
     644            return self
     645        A.set_degbound(self._poly.degree()*n)
     646        cdef MPolynomial_libsingular p,q
     647        self._poly = A._current_ring(self._poly)
     648        cdef int d = self._poly.degree()
     649        q = p = self._poly
     650        cdef int i
     651        for i from 0<i<n:
     652            q = singular_system("stest",q,d,A._degbound,A.__ngens,
     653                                     ring=A._current_ring)
     654            p *= q
     655        return FreeAlgebraElement_letterplace(A, p, check=False)
     656
     657    ## Groebner related stuff
     658    def reduce(self, G):
     659        """
     660        Reduce this element by a list of elements or by a
     661        twosided weighted homogeneous ideal.
     662
     663        INPUT:
     664
     665        Either a list or tuple of weighted homogeneous elements of the
     666        free algebra, or an ideal of the free algebra, or an ideal in
     667        the commutative polynomial ring that is currently used to
     668        implement the multiplication in the free algebra.
     669
     670        OUTPUT:
     671
     672        The twosided reduction of this element by the argument.
     673
     674        NOTE:
     675
     676        This may not be the normal form of this element, unless
     677        the argument is a twosided Groebner basis up to the degree
     678        of this element.
     679
     680        EXAMPLE::
     681
     682            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     683            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     684            sage: p = y^2*z*y^2+y*z*y*z*y
     685
     686        We compute the letterplace version of the Groebneer basis
     687        of `I` with degree bound 4::
     688
     689            sage: G = F._reductor_(I.groebner_basis(4).gens(),4)
     690            sage: G.ring() is F.current_ring()
     691            True
     692
     693        Since the element `p` is of degree 5, it is no surrprise
     694        that its reductions with respect to the original generators
     695        of `I` (of degree 2), or with respect to `G` (Groebner basis
     696        with degree bound 4), or with respect to the Groebner basis
     697        with degree bound 5 (which yields its normal form) are
     698        pairwise different::
     699
     700            sage: p.reduce(I)
     701            y*y*z*y*y + y*z*y*z*y
     702            sage: p.reduce(G)
     703            y*y*z*z*y + y*z*y*z*y - y*z*z*y*y + y*z*z*z*y
     704            sage: p.normal_form(I)
     705            y*y*z*z*z + y*z*y*z*z - y*z*z*y*z + y*z*z*z*z
     706            sage: p.reduce(I) != p.reduce(G) != p.normal_form(I) != p.reduce(I)
     707            True
     708
     709        """
     710        cdef FreeAlgebra_letterplace P = self._parent
     711        if not isinstance(G,(list,tuple)):
     712            if G==P:
     713                return P.zero_element()
     714            if not (isinstance(G,MPolynomialIdeal) and G.ring()==P._current_ring):
     715                G = G.gens()
     716        C = P.current_ring()
     717        cdef int selfdeg = self._poly.degree()
     718        if isinstance(G,MPolynomialIdeal):
     719            gI = G
     720        else:
     721            gI = P._reductor_(G,selfdeg) #C.ideal(g,coerce=False)
     722        from sage.libs.singular.option import LibSingularOptions
     723        libsingular_options = LibSingularOptions()
     724        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     725        libsingular_options['redTail'] = True
     726        libsingular_options['redSB'] = True
     727        poly = poly_reduce(C(self._poly),gI, ring=C,
     728                           attributes={gI:{"isSB":1}})
     729        libsingular_options['redTail'] = bck[0]
     730        libsingular_options['redSB'] = bck[1]
     731        return FreeAlgebraElement_letterplace(P,poly,check=False)
     732
     733    def normal_form(self,I):
     734        """
     735        Return the normal form of this element with respect to
     736        a twosided weighted homogeneous ideal.
     737
     738        INPUT:
     739
     740        A twosided homogeneous ideal `I` of the parent `F` of
     741        this element, `x`.
     742
     743        OUTPUT:
     744
     745        The normal form of `x` wrt. `I`.
     746
     747        NOTE:
     748
     749        The normal form is computed by reduction with respect
     750        to a Groebnerbasis of `I` with degree bound `deg(x)`.
     751
     752        EXAMPLE::
     753
     754            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     755            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     756            sage: (x^5).normal_form(I)
     757            -y*z*z*z*x - y*z*z*z*y - y*z*z*z*z
     758
     759        We verify two basic properties of normal forms: The
     760        difference of an element and its normal form is contained
     761        in the ideal, and if two elements of the free algebra
     762        differ by an element of the ideal then they have the same
     763        normal form::
     764
     765            sage: x^5 - (x^5).normal_form(I) in I
     766            True
     767            sage: (x^5+x*I.0*y*z-3*z^2*I.1*y).normal_form(I) == (x^5).normal_form(I)
     768            True
     769
     770        Here is an example with non-trivial degree weights::
     771
     772            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[1,2,3])
     773            sage: I = F*[x*y-y*x+z, y^2+2*x*z, (x*y)^2-z^2]*F
     774            sage: ((x*y)^3).normal_form(I)
     775            z*z*y*x - z*z*z
     776            sage: (x*y)^3-((x*y)^3).normal_form(I) in I
     777            True
     778            sage: ((x*y)^3+2*z*I.0*z+y*I.1*z-x*I.2*y).normal_form(I) == ((x*y)^3).normal_form(I)
     779            True
     780
     781        """
     782        if self._parent != I.ring():
     783            raise ValueError, "Can not compute normal form wrt an ideal that does not belong to %s"%self._parent
     784        sdeg = self._poly.degree()
     785        return self.reduce(self._parent._reductor_(I.groebner_basis(degbound=sdeg).gens(), sdeg))
  • new file sage/algebras/letterplace/free_algebra_letterplace.pxd

    diff --git a/sage/algebras/letterplace/free_algebra_letterplace.pxd b/sage/algebras/letterplace/free_algebra_letterplace.pxd
    new file mode 100644
    - +  
     1###############################################################################
     2#
     3#       Copyright (C) 2011 Simon King <simon.king@uni-jena.de>
     4#  Distributed under the terms of the GNU General Public License (GPL),
     5#  version 2 or any later version.  The full text of the GPL is available at:
     6#                  http://www.gnu.org/licenses/
     7#
     8###############################################################################
     9
     10cdef class FreeAlgebra_letterplace
     11
     12from sage.rings.ring cimport Algebra
     13from sage.structure.element cimport AlgebraElement, ModuleElement, RingElement, Element
     14from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_libsingular, MPolynomial_libsingular
     15from sage.algebras.letterplace.free_algebra_element_letterplace cimport FreeAlgebraElement_letterplace
     16
     17include "../../ext/stdsage.pxi"
     18
     19cdef class FreeAlgebra_letterplace(Algebra):
     20    cdef MPolynomialRing_libsingular _commutative_ring
     21    cdef MPolynomialRing_libsingular _current_ring
     22    cdef int _degbound
     23    cdef int __ngens
     24    cdef int _nb_slackvars
     25    cdef object __monoid
     26    cdef public object __custom_name
     27    cdef str exponents_to_string(self, E)
     28    cdef str exponents_to_latex(self, E)
     29    cdef tuple _degrees
  • new file sage/algebras/letterplace/free_algebra_letterplace.pyx

    diff --git a/sage/algebras/letterplace/free_algebra_letterplace.pyx b/sage/algebras/letterplace/free_algebra_letterplace.pyx
    new file mode 100644
    - +  
     1###############################################################################
     2#
     3#       Copyright (C) 2011 Simon King <simon.king@uni-jena.de>
     4#  Distributed under the terms of the GNU General Public License (GPL),
     5#  version 2 or any later version.  The full text of the GPL is available at:
     6#                  http://www.gnu.org/licenses/
     7#
     8###############################################################################
     9
     10"""
     11Free associative unital algebras, implemented via Singular's letterplace rings
     12
     13AUTHOR:
     14
     15- Simon King (2011-03-21): :trac:`7797`
     16
     17With this implementation, Groebner bases out to a degree bound and
     18normal forms can be computed for twosided weighted homogeneous ideals
     19of free algebras. For now, all computations are restricted to weighted
     20homogeneous elements, i.e., other elements can not be created by
     21arithmetic operations.
     22
     23EXAMPLES::
     24
     25    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     26    sage: F
     27    Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     28    sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     29    sage: I
     30    Twosided Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     31    sage: x*(x*I.0-I.1*y+I.0*y)-I.1*y*z
     32    x*y*x*y + x*y*y*y - x*y*y*z + x*y*z*y + y*x*y*z + y*y*y*z
     33    sage: x^2*I.0-x*I.1*y+x*I.0*y-I.1*y*z in I
     34    True
     35
     36The preceding containment test is based on the computation of Groebner
     37bases with degree bound::
     38
     39    sage: I.groebner_basis(degbound=4)
     40    Twosided Ideal (y*z*y*y - y*z*y*z + y*z*z*y - y*z*z*z, y*z*y*x + y*z*y*z + y*z*z*x + y*z*z*z, y*y*z*y - y*y*z*z + y*z*z*y - y*z*z*z, y*y*z*x + y*y*z*z + y*z*z*x + y*z*z*z, y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     41
     42When reducing an element by `I`, the original generators are chosen::
     43
     44    sage: (y*z*y*y).reduce(I)
     45    y*z*y*y
     46
     47However, there is a method for computing the normal form of an
     48element, which is the same as reduction by the Groebner basis out to
     49the degree of that element::
     50
     51    sage: (y*z*y*y).normal_form(I)
     52    y*z*y*z - y*z*z*y + y*z*z*z
     53    sage: (y*z*y*y).reduce(I.groebner_basis(4))
     54    y*z*y*z - y*z*z*y + y*z*z*z
     55
     56The default term order derives from the degree reverse lexicographic
     57order on the commutative version of the free algebra::
     58
     59    sage: F.commutative_ring().term_order()
     60    Degree reverse lexicographic term order
     61
     62A different term order can be chosen, and of course may yield a
     63different normal form::
     64
     65    sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace', order='lex')
     66    sage: L.commutative_ring().term_order()
     67    Lexicographic term order
     68    sage: J = L*[a*b+b*c,a^2+a*b-b*c-c^2]*L
     69    sage: J.groebner_basis(4)
     70    Twosided Ideal (2*b*c*b - b*c*c + c*c*b, a*c*c - 2*b*c*a - 2*b*c*c - c*c*a, a*b + b*c, a*a - 2*b*c - c*c) of Free Associative Unital Algebra on 3 generators (a, b, c) over Rational Field
     71    sage: (b*c*b*b).normal_form(J)
     72    1/2*b*c*c*b - 1/2*c*c*b*b
     73
     74Here is an example with degree weights::
     75
     76    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[1,2,3])
     77    sage: (x*y+z).degree()
     78    3
     79
     80TEST::
     81
     82    sage: TestSuite(F).run()
     83    sage: TestSuite(L).run()
     84    sage: loads(dumps(F)) is F
     85    True
     86
     87TODO:
     88
     89The computation of Groebner bases only works for global term
     90orderings, and all elements must be weighted homogeneous with respect
     91to positive integral degree weights. It is ongoing work in Singular to
     92lift these restrictions.
     93
     94We support coercion from the letterplace wrapper to the corresponding
     95generic implementation of a free algebra
     96(:class:`~sage.algebras.free_algebra.FreeAlgebra_generic`), but there
     97is no coercion in the opposite direction, since the generic
     98implementation also comprises non-homogeneous elements.
     99
     100We also do not support coercion from a subalgebra, or between free
     101algebras with different term orderings, yet.
     102
     103"""
     104
     105from sage.all import PolynomialRing, prod
     106from sage.libs.singular.function import lib, singular_function
     107from sage.rings.polynomial.term_order import TermOrder
     108from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
     109from sage.categories.algebras import Algebras
     110from sage.rings.noncommutative_ideals import IdealMonoid_nc
     111
     112#####################
     113# Define some singular functions
     114lib("freegb.lib")
     115poly_reduce = singular_function("NF")
     116singular_system=singular_function("system")
     117
     118# unfortunately we can not set Singular attributes for MPolynomialRing_libsingular
     119# Hence, we must constantly work around Letterplace's sanity checks,
     120# and can not use the following library functions:
     121#set_letterplace_attributes = singular_function("setLetterplaceAttributes")
     122#lpMult = singular_function("lpMult")
     123
     124#####################
     125# Auxiliar functions
     126
     127cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks):
     128    """
     129    Create a polynomial ring in block order.
     130
     131    INPUT:
     132
     133    - ``base_ring``: A multivariate polynomial ring.
     134    - ``blocks``: The number of blocks to be formed.
     135
     136    OUTPUT:
     137
     138    A multivariate polynomial ring in block order, all blocks
     139    isomorphic (as ordered rings) with the given ring, and the
     140    variable names of the `n`-th block (`n>0`) ending with
     141    ``"_%d"%n``.
     142
     143    TEST:
     144
     145    Note that, since the algebras are cached, we need to choose
     146    a different base ring, since other doctests could have a
     147    side effect on the atteined degree bound::
     148
     149        sage: F.<x,y,z> = FreeAlgebra(GF(17), implementation='letterplace')
     150        sage: L.<a,b,c> = FreeAlgebra(GF(17), implementation='letterplace', order='lex')
     151        sage: F.set_degbound(4)
     152        sage: F.current_ring()  # indirect doctest
     153        Multivariate Polynomial Ring in x, y, z, x_1, y_1, z_1, x_2, y_2, z_2, x_3, y_3, z_3 over Finite Field of size 17
     154        sage: F.current_ring().term_order()
     155        Block term order with blocks:
     156        (Degree reverse lexicographic term order of length 3,
     157         Degree reverse lexicographic term order of length 3,
     158         Degree reverse lexicographic term order of length 3,
     159         Degree reverse lexicographic term order of length 3)
     160        sage: L.set_degbound(2)
     161        sage: L.current_ring().term_order()
     162        Block term order with blocks:
     163        (Lexicographic term order of length 3,
     164         Lexicographic term order of length 3)
     165
     166    """
     167    n = base_ring.ngens()
     168    T0 = base_ring.term_order()
     169    T = T0
     170    cdef i
     171    cdef tuple names0 = base_ring.variable_names()
     172    cdef list names = list(names0)
     173    for i from 1<=i<blocks:
     174        T += T0
     175        names.extend([x+'_'+str(i) for x in names0])
     176    return PolynomialRing(base_ring.base_ring(),len(names),names,order=T)
     177
     178#####################
     179# The free algebra
     180
     181cdef class FreeAlgebra_letterplace(Algebra):
     182    """
     183    Finitely generated free algebra, with arithmetic restricted to weighted homogeneous elements.
     184
     185    NOTE:
     186
     187    The restriction to weighted homogeneous elements should be lifted
     188    as soon as the restriction to homogeneous elements is lifted in
     189    Singular's "Letterplace algebras".
     190
     191    EXAMPLE::
     192
     193        sage: K.<z> = GF(25)
     194        sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     195        sage: F
     196        Free Associative Unital Algebra on 3 generators (a, b, c) over Finite Field in z of size 5^2
     197        sage: P = F.commutative_ring()
     198        sage: P
     199        Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2
     200
     201    We can do arithmetic as usual, as long as we stay (weighted) homogeneous::
     202
     203        sage: (z*a+(z+1)*b+2*c)^2
     204        (z + 3)*a*a + (2*z + 3)*a*b + (2*z)*a*c + (2*z + 3)*b*a + (3*z + 4)*b*b + (2*z + 2)*b*c + (2*z)*c*a + (2*z + 2)*c*b - c*c
     205        sage: a+1
     206        Traceback (most recent call last):
     207        ...
     208        ArithmeticError: Can only add elements of the same weighted degree
     209
     210    """
     211    # It is not really a free algebra over the given generators. Rather,
     212    # it is a free algebra over the commutative monoid generated by the given generators.
     213    def __init__(self, R, degrees=None):
     214        """
     215        INPUT:
     216
     217        A multivariate polynomial ring of type :class:`~sage.rings.polynomial.multipolynomial_libsingular.MPolynomialRing_libsingular`.
     218
     219        OUTPUT:
     220
     221        The free associative version of the given commutative ring.
     222
     223        NOTE:
     224
     225        One is supposed to use the `FreeAlgebra` constructor, in order to use the cache.
     226
     227        TEST::
     228
     229            sage: from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     230            sage: FreeAlgebra_letterplace(QQ['x','y'])
     231            Free Associative Unital Algebra on 2 generators (x, y) over Rational Field
     232            sage: FreeAlgebra_letterplace(QQ['x'])
     233            Traceback (most recent call last):
     234            ...
     235            TypeError: A letterplace algebra must be provided by a polynomial ring of type <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomialRing_libsingular'>
     236
     237        ::
     238
     239            sage: K.<z> = GF(25)
     240            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     241            sage: TestSuite(F).run(verbose=True)
     242            running ._test_additive_associativity() . . . pass
     243            running ._test_an_element() . . . pass
     244            running ._test_associativity() . . . pass
     245            running ._test_category() . . . pass
     246            running ._test_characteristic() . . . pass
     247            running ._test_distributivity() . . . pass
     248            running ._test_elements() . . .
     249              Running the test suite of self.an_element()
     250              running ._test_category() . . . pass
     251              running ._test_eq() . . . pass
     252              running ._test_not_implemented_methods() . . . pass
     253              running ._test_pickling() . . . pass
     254              pass
     255            running ._test_elements_eq() . . . pass
     256            running ._test_eq() . . . pass
     257            running ._test_not_implemented_methods() . . . pass
     258            running ._test_one() . . . pass
     259            running ._test_pickling() . . . pass
     260            running ._test_prod() . . . pass
     261            running ._test_some_elements() . . . pass
     262            running ._test_zero() . . . pass
     263
     264        """
     265        if not isinstance(R,MPolynomialRing_libsingular):
     266            raise TypeError, "A letterplace algebra must be provided by a polynomial ring of type %s"%MPolynomialRing_libsingular
     267        self.__ngens = R.ngens()
     268        if degrees is None:
     269            varnames = R.variable_names()
     270            self._nb_slackvars = 0
     271        else:
     272            varnames = R.variable_names()[:-1]
     273            self._nb_slackvars = 1
     274        base_ring = R.base_ring()
     275        Algebra.__init__(self, base_ring, varnames,
     276                         normalize=False, category=Algebras(base_ring))
     277        self._commutative_ring = R
     278        self._current_ring = make_letterplace_ring(R,1)
     279        self._degbound = 1
     280        if degrees is None:
     281            self._degrees = tuple([int(1)]*self.__ngens)
     282        else:
     283            if (not isinstance(degrees,(tuple,list))) or len(degrees)!=self.__ngens-1 or any([i<=0 for i in degrees]):
     284                raise TypeError, "The generator degrees must be given by a list or tuple of %d positive integers"%(self.__ngens-1)
     285            self._degrees = tuple([int(i) for i in degrees])
     286            self.set_degbound(max(self._degrees))
     287        self._populate_coercion_lists_(coerce_list=[base_ring])
     288    def __reduce__(self):
     289        """
     290        TEST::
     291
     292            sage: K.<z> = GF(25)
     293            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     294            sage: loads(dumps(F)) is F    # indirect doctest
     295            True
     296
     297        """
     298        from sage.algebras.free_algebra import FreeAlgebra
     299        if self._nb_slackvars==0:
     300            return FreeAlgebra,(self._commutative_ring,)
     301        return FreeAlgebra,(self._commutative_ring,None,None,None,None,None,None,None,self._degrees)
     302    # Small methods
     303    def ngens(self):
     304        """
     305        Return the number of generators.
     306
     307        EXAMPLE::
     308
     309            sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
     310            sage: F.ngens()
     311            3
     312
     313        """
     314        return self.__ngens-self._nb_slackvars
     315    def gen(self,i):
     316        """
     317        Return the `i`-th generator.
     318
     319        INPUT:
     320
     321        `i` -- an integer.
     322
     323        OUTPUT:
     324
     325        Generator number `i`.
     326
     327        EXAMPLE::
     328
     329            sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
     330            sage: F.1 is F.1  # indirect doctest
     331            True
     332            sage: F.gen(2)
     333            c
     334
     335        """
     336        if i>=self.__ngens-self._nb_slackvars:
     337            raise ValueError, "This free algebra only has %d generators"%(self.__ngens-self._nb_slackvars)
     338        if self._gens is not None:
     339            return self._gens[i]
     340        deg = self._degrees[i]
     341        #self.set_degbound(deg)
     342        p = self._current_ring.gen(i)
     343        cdef int n
     344        cdef int j = self.__ngens-1
     345        for n from 1<=n<deg:
     346            j += self.__ngens
     347            p *= self._current_ring.gen(j)
     348        return FreeAlgebraElement_letterplace(self, p)
     349    def current_ring(self):
     350        """
     351        Return the commutative ring that is used to emulate
     352        the non-commutative multiplication out to the current degree.
     353
     354        EXAMPLE::
     355
     356            sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
     357            sage: F.current_ring()
     358            Multivariate Polynomial Ring in a, b, c over Rational Field
     359            sage: a*b
     360            a*b
     361            sage: F.current_ring()
     362            Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1 over Rational Field
     363            sage: F.set_degbound(3)
     364            sage: F.current_ring()
     365            Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1, a_2, b_2, c_2 over Rational Field
     366
     367        """
     368        return self._current_ring
     369    def commutative_ring(self):
     370        """
     371        Return the commutative version of this free algebra.
     372
     373        NOTE:
     374
     375        This commutative ring is used as a unique key of the free algebra.
     376
     377        EXAMPLE::
     378
     379            sage: K.<z> = GF(25)
     380            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     381            sage: F
     382            Free Associative Unital Algebra on 3 generators (a, b, c) over Finite Field in z of size 5^2
     383            sage: F.commutative_ring()
     384            Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2
     385            sage: FreeAlgebra(F.commutative_ring()) is F
     386            True
     387
     388        """
     389        return self._commutative_ring
     390    def term_order_of_block(self):
     391        """
     392        Return the term order that is used for the commutative version of this free algebra.
     393
     394        EXAMPLE::
     395
     396            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     397            sage: F.term_order_of_block()
     398            Degree reverse lexicographic term order
     399            sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace',order='lex')
     400            sage: L.term_order_of_block()
     401            Lexicographic term order
     402
     403        """
     404        return self._commutative_ring.term_order()
     405
     406    def generator_degrees(self):
     407        return self._degrees
     408
     409    # Some basic properties of this ring
     410    def is_commutative(self):
     411        """
     412        Tell whether this algebra is commutative, i.e., whether the generator number is one.
     413
     414        EXAMPLE::
     415
     416            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     417            sage: F.is_commutative()
     418            False
     419            sage: FreeAlgebra(QQ, implementation='letterplace', names=['x']).is_commutative()
     420            True
     421
     422        """
     423        return self.__ngens-self._nb_slackvars <= 1
     424
     425    def is_field(self):
     426        """
     427        Tell whether this free algebra is a field.
     428
     429        NOTE:
     430
     431        This would only be the case in the degenerate case of no generators.
     432        But such an example can not be constructed in this implementation.
     433
     434        TEST::
     435
     436            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     437            sage: F.is_field()
     438            False
     439
     440        """
     441        return (not (self.__ngens-self._nb_slackvars)) and self._base.is_field()
     442
     443    def _repr_(self):
     444        """
     445        EXAMPLE::
     446
     447            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     448            sage: F     # indirect doctest
     449            Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     450
     451        The degree weights are not part of the string representation::
     452
     453            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
     454            sage: F
     455            Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     456
     457
     458        """
     459        return "Free Associative Unital Algebra on %d generators %s over %s"%(self.__ngens-self._nb_slackvars,self.gens(),self._base)
     460
     461    def _latex_(self):
     462        """
     463        Representation of this free algebra in LaTeX.
     464
     465        EXAMPLE::
     466
     467            sage: F.<bla,alpha,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[1,2,3])
     468            sage: latex(F)
     469            \Bold{Q}\langle \mbox{bla}, \alpha, z\rangle
     470
     471        """
     472        from sage.all import latex
     473        return "%s\\langle %s\\rangle"%(latex(self.base_ring()),', '.join(self.latex_variable_names()))
     474
     475    def degbound(self):
     476        """
     477        Return the degree bound that is currently used.
     478
     479        NOTE:
     480
     481        When multiplying two elements of this free algebra, the degree
     482        bound will be dynamically adapted. It can also be set by
     483        :meth:`set_degbound`.
     484
     485        EXAMPLE:
     486
     487        In order to avoid we get a free algebras from the cache that
     488        was created in another doctest and has a different degree
     489        bound, we choose a base ring that does not appear in other tests::
     490
     491            sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
     492            sage: F.degbound()
     493            1
     494            sage: x*y
     495            x*y
     496            sage: F.degbound()
     497            2
     498            sage: F.set_degbound(4)
     499            sage: F.degbound()
     500            4
     501
     502        """
     503        return self._degbound
     504    def set_degbound(self,d):
     505        """
     506        Increase the degree bound that is currently in place.
     507
     508        NOTE:
     509
     510        The degree bound can not be decreased.
     511
     512        EXAMPLE:
     513
     514        In order to avoid we get a free algebras from the cache that
     515        was created in another doctest and has a different degree
     516        bound, we choose a base ring that does not appear in other tests::
     517
     518            sage: F.<x,y,z> = FreeAlgebra(GF(251), implementation='letterplace')
     519            sage: F.degbound()
     520            1
     521            sage: x*y
     522            x*y
     523            sage: F.degbound()
     524            2
     525            sage: F.set_degbound(4)
     526            sage: F.degbound()
     527            4
     528            sage: F.set_degbound(2)
     529            sage: F.degbound()
     530            4
     531
     532        """
     533        if d<=self._degbound:
     534            return
     535        self._degbound = d
     536        self._current_ring = make_letterplace_ring(self._commutative_ring,d)
     537
     538#    def base_extend(self, R):
     539#        if self._base.has_coerce_map_from(R):
     540#            return self
     541
     542    ################################################
     543    ## Ideals
     544
     545    def _ideal_class_(self, n=0):
     546        """
     547        Return the class :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`.
     548
     549        EXAMPLE::
     550
     551            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     552            sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*F
     553            sage: I
     554            Right Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     555            sage: type(I) is F._ideal_class_()
     556            True
     557
     558        """
     559        from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
     560        return LetterplaceIdeal
     561
     562    def ideal_monoid(self):
     563        """
     564        Return the monoid of ideals of this free algebra.
     565
     566        EXAMPLE::
     567
     568            sage: F.<x,y> = FreeAlgebra(GF(2), implementation='letterplace')
     569            sage: F.ideal_monoid()
     570            Monoid of ideals of Free Associative Unital Algebra on 2 generators (x, y) over Finite Field of size 2
     571            sage: F.ideal_monoid() is F.ideal_monoid()
     572            True
     573
     574        """
     575        if self.__monoid is None:
     576            self.__monoid = IdealMonoid_nc(self)
     577        return self.__monoid
     578
     579    # Auxiliar methods
     580    cdef str exponents_to_string(self, E):
     581        """
     582        This auxiliary method is used for the string representation of elements of this free algebra.
     583
     584        EXAMPLE::
     585
     586            sage: F.<x,y,z> = FreeAlgebra(GF(2), implementation='letterplace')
     587            sage: x*y*x*z   # indirect doctest
     588            x*y*x*z
     589
     590        It should be possible to use the letterplace algebra to implement the
     591        free algebra generated by the elements of a finitely generated free abelian
     592        monoid. However, we can not use it, yet. So, for now, we raise an error::
     593
     594            sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace
     595            sage: P = F.commutative_ring()
     596            sage: FreeAlgebraElement_letterplace(F, P.0*P.1^2+P.1^3) # indirect doctest
     597            Traceback (most recent call last):
     598            ...
     599            NotImplementedError:
     600              Apparently you tried to view the letterplace algebra with
     601              shift-multiplication as the free algebra over a finitely
     602              generated free abelian monoid.
     603              In principle, this is correct, but it is not implemented, yet.
     604
     605        """
     606        cdef int ngens = self.__ngens
     607        cdef int nblocks = len(E)/ngens
     608        cdef int i,j,base, exp, var_ind
     609        cdef list out = []
     610        cdef list tmp
     611        for i from 0<=i<nblocks:
     612            base = i*ngens
     613            tmp = [(j,E[base+j]) for j in xrange(ngens) if E[base+j]]
     614            if not tmp:
     615                continue
     616            var_ind, exp = tmp[0]
     617            if len(tmp)>1 or exp>1:
     618                raise NotImplementedError, "\n  Apparently you tried to view the letterplace algebra with\n  shift-multiplication as the free algebra over a finitely\n  generated free abelian monoid.\n  In principle, this is correct, but it is not implemented, yet."
     619
     620            out.append(self._names[var_ind])
     621            i += (self._degrees[var_ind]-1)
     622            ### This was the original implementation, with "monoid hack" but without generator degrees
     623            #s = '.'.join([('%s^%d'%(x,e) if e>1 else x) for x,e in zip(self._names,E[i*ngens:(i+1)*ngens]) if e])
     624            #if s:
     625            #    out.append(s)
     626        return '*'.join(out)
     627
     628    # Auxiliar methods
     629    cdef str exponents_to_latex(self, E):
     630        """
     631        This auxiliary method is used for the representation of elements of this free algebra as a latex string.
     632
     633        EXAMPLE::
     634
     635            sage: K.<z> = GF(25)
     636            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace', degrees=[1,2,3])
     637            sage: -(a*b*(z+1)-c)^2
     638            (2*z + 1)*a*b*a*b + (z + 1)*a*b*c + (z + 1)*c*a*b - c*c
     639            sage: latex(-(a*b*(z+1)-c)^2)     # indirect doctest
     640            \left(2 z + 1\right) a b a b + \left(z + 1\right) a b c + \left(z + 1\right) c a b - c c
     641
     642        """
     643        cdef int ngens = self.__ngens
     644        cdef int nblocks = len(E)/ngens
     645        cdef int i,j,base, exp, var_ind
     646        cdef list out = []
     647        cdef list tmp
     648        cdef list names = self.latex_variable_names()
     649        for i from 0<=i<nblocks:
     650            base = i*ngens
     651            tmp = [(j,E[base+j]) for j in xrange(ngens) if E[base+j]]
     652            if not tmp:
     653                continue
     654            var_ind, exp = tmp[0]
     655            if len(tmp)>1 or exp>1:
     656                raise NotImplementedError, "\n  Apparently you tried to view the letterplace algebra with\n  shift-multiplication as the free algebra over a finitely\n  generated free abelian monoid.\n  In principle, this is correct, but it is not implemented, yet."
     657
     658            out.append(names[var_ind])
     659            i += (self._degrees[var_ind]-1)
     660        return ' '.join(out)
     661
     662    def _reductor_(self, g, d):
     663        """
     664        Return a commutative ideal that can be used to compute the normal
     665        form of a free algebra element of a given degree.
     666
     667        INPUT:
     668
     669        ``g`` - a list of elements of this free algebra.
     670        ``d`` - an integer.
     671
     672        OUTPUT:
     673
     674        An ideal such that reduction of a letterplace polynomial by that ideal corresponds
     675        to reduction of an element of degree at most ``d`` by ``g``.
     676
     677        EXAMPLE::
     678
     679            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     680            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     681            sage: p = y*x*y + y*y*y + y*z*y - y*z*z
     682            sage: p.reduce(I)
     683            y*y*y - y*y*z + y*z*y - y*z*z
     684            sage: G = F._reductor_(I.gens(),3); G
     685            Ideal (x*y_1 + y*z_1, x_1*y_2 + y_1*z_2, x*x_1 + x*y_1 - y*x_1 - y*y_1, x_1*x_2 + x_1*y_2 - y_1*x_2 - y_1*y_2) of Multivariate Polynomial Ring in x, y, z, x_1, y_1, z_1, x_2, y_2, z_2... over Rational Field
     686
     687        We do not use the usual reduction method for polynomials in
     688        Sage, since it does the reductions in a different order
     689        compared to Singular. Therefore, we call the original Singular
     690        reduction method, and prevent a warning message by asserting
     691        that `G` is a Groebner basis.
     692
     693            sage: from sage.libs.singular.function import singular_function
     694            sage: poly_reduce = singular_function("NF")
     695            sage: q = poly_reduce(p.letterplace_polynomial(), G, ring=F.current_ring(), attributes={G:{"isSB":1}}); q
     696            y*y_1*y_2 - y*y_1*z_2 + y*z_1*y_2 - y*z_1*z_2
     697            sage: p.reduce(I).letterplace_polynomial() == q
     698            True
     699
     700        """
     701        cdef list out = []
     702        C = self.current_ring()
     703        cdef FreeAlgebraElement_letterplace x
     704        ngens = self.__ngens
     705        degbound = self._degbound
     706        cdef list G = [C(x._poly) for x in g]
     707        for y in G:
     708            out.extend([y]+[singular_system("stest",y,n+1,degbound,ngens,ring=C) for n in xrange(d-y.degree())])
     709        return C.ideal(out)
     710
     711    ###########################
     712    ## Coercion
     713    cpdef _coerce_map_from_(self,S):
     714        """
     715        A ring ``R`` coerces into self, if
     716
     717        - it coerces into the current polynomial ring, or
     718        - it is a free graded algebra in letterplace implementation,
     719          the generator names of ``R`` are a proper subset of the
     720          generator names of self, the degrees of equally named
     721          generators are equal, and the base ring of ``R`` coerces
     722          into the base ring of self.
     723
     724        TEST:
     725
     726        Coercion from the base ring::
     727
     728            sage: F.<x,y,z> = FreeAlgebra(GF(5), implementation='letterplace')
     729            sage: 5 == F.zero()    # indirect doctest
     730            True
     731
     732        Coercion from another free graded algebra::
     733
     734            sage: F.<t,y,z> = FreeAlgebra(ZZ, implementation='letterplace', degrees=[4,2,3])
     735            sage: G = FreeAlgebra(GF(5), implementation='letterplace', names=['x','y','z','t'], degrees=[1,2,3,4])
     736            sage: t*G.0       # indirect doctest
     737            t*x
     738
     739        """
     740        if self==S or self._current_ring.has_coerce_map_from(S):
     741            return True
     742        cdef int i
     743        # Do we have another letterplace algebra?
     744        if not isinstance(S, FreeAlgebra_letterplace):
     745            return False
     746        # Do the base rings coerce?
     747        if not self.base_ring().has_coerce_map_from(S.base_ring()):
     748            return False
     749        # Do the names match?
     750        cdef tuple degs, Sdegs, names, Snames
     751        names = self.variable_names()
     752        Snames = S.variable_names()
     753        if not set(names).issuperset(Snames):
     754            return False
     755        # Do the degrees match
     756        degs = self._degrees
     757        Sdegs = (<FreeAlgebra_letterplace>S)._degrees
     758        for i from 0<=i<S.ngens():
     759            if degs[names.index(Snames[i])] != Sdegs[i]:
     760                return False
     761        return True
     762
     763    def _an_element_(self):
     764        """
     765        Return an element.
     766
     767        EXAMPLE::
     768
     769            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     770            sage: F.an_element()   # indirect doctest
     771            x
     772
     773        """
     774        return FreeAlgebraElement_letterplace(self, self._current_ring.an_element(), check=False)
     775
     776#    def random_element(self, degree=2, terms=5):
     777#        """
     778#        Return a random element of a given degree and with a given number of terms.
     779#
     780#        INPUT:
     781#
     782#        - ``degree`` -- the maximal degree of the output (default 2).
     783#        - ``terms`` -- the maximal number of terms of the output (default 5).
     784#
     785#        NOTE:
     786#
     787#        This method is currently not useful at all.
     788#
     789#        Not tested.
     790#        """
     791#        self.set_degbound(degree)
     792#        while(1):
     793#            p = self._current_ring.random_element(degree=degree,terms=terms)
     794#            if p.is_homogeneous():
     795#                break
     796#        return FreeAlgebraElement_letterplace(self, p, check=False)
     797
     798    def _from_dict_(self, D, check=True):
     799        """
     800        Create an element from a dictionary.
     801
     802        INPUT:
     803
     804        - A dictionary. Keys: tuples of exponents. Values:
     805          The coefficients of the corresponding monomial
     806          in the to-be-created element.
     807        - ``check`` (optional bool, default ``True``):
     808          This is forwarded to the initialisation of
     809          :class:`~sage.algebas.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace`.
     810
     811        TEST:
     812
     813        This method applied to the dictionary of any element must
     814        return the same element. This must hold true even if the
     815        underlying letterplace ring has been extended in the meantime.
     816        ::
     817
     818            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     819            sage: p = 3*x*y+2*z^2
     820            sage: F.set_degbound(10)
     821            sage: p == F._from_dict_(dict(p))
     822            True
     823
     824        For the empty dictionary, zero is returned::
     825
     826            sage: F._from_dict_({})
     827            0
     828
     829        """
     830        if not D:
     831            return self.zero_element()
     832        cdef int l
     833        for e in D.iterkeys():
     834            l = len(e)
     835            break
     836        cdef dict out = {}
     837        self.set_degbound(l/self.__ngens)
     838        cdef int n = self._current_ring.ngens()
     839        for e,c in D.iteritems():
     840            out[tuple(e)+(0,)*(n-l)] = c
     841        return FreeAlgebraElement_letterplace(self,self._current_ring(out),
     842                                              check=check)
     843
     844    def _element_constructor_(self, x):
     845        """
     846        Return an element of this free algebra.
     847
     848        INPUT:
     849
     850        An element of a free algebra with a proper subset of generator
     851        names, or anything that can be interpreted in the polynomial
     852        ring that is used to implement the letterplace algebra out to
     853        the current degree bound, or a string that can be interpreted
     854        as an expression in the algebra (provided that the
     855        coefficients are numerical).
     856
     857        EXAMPLE::
     858
     859            sage: F.<t,y,z> = FreeAlgebra(ZZ, implementation='letterplace', degrees=[4,2,3])
     860
     861        Conversion of a number::
     862
     863            sage: F(3)
     864            3
     865
     866        Interpretation of a string as an algebra element::
     867
     868            sage: F('t*y+3*z^2')
     869            t*y + 3*z*z
     870
     871        Conversion from the currently underlying polynomial ring::
     872
     873            sage: F.set_degbound(3)
     874            sage: P = F.current_ring()
     875            sage: F(P.0*P.7*P.11*P.15*P.17*P.23 - 2*P.2*P.7*P.11*P.14*P.19*P.23)
     876            t*y - 2*z*z
     877
     878        Conversion from a graded sub-algebra::
     879
     880            sage: G = FreeAlgebra(GF(5), implementation='letterplace', names=['x','y','z','t'], degrees=[1,2,3,4])
     881            sage: G(t*y + 2*y^3 - 4*z^2)   # indirect doctest
     882            (2)*y*y*y + z*z + t*y
     883
     884        """
     885        if isinstance(x, basestring):
     886            from sage.all import sage_eval
     887            return sage_eval(x,locals=self.gens_dict())
     888        try:
     889            P = x.parent()
     890        except AttributeError:
     891            P = None
     892        if P is self:
     893            (<FreeAlgebraElement_letterplace>x)._poly = self._current_ring((<FreeAlgebraElement_letterplace>x)._poly)
     894            return x
     895        if isinstance(P, FreeAlgebra_letterplace):
     896            self.set_degbound(P.degbound())
     897            Ppoly = (<FreeAlgebra_letterplace>P)._current_ring
     898            Gens = self._current_ring.gens()
     899            Names = self._current_ring.variable_names()
     900            PNames = list(Ppoly.variable_names())
     901            # translate the slack variables
     902            PNames[P.ngens(): len(PNames): P.ngens()+1] = list(Names[self.ngens(): len(Names): self.ngens()+1])[:P.degbound()]
     903            x = Ppoly.hom([Gens[Names.index(asdf)] for asdf in PNames])(x.letterplace_polynomial())
     904        return FreeAlgebraElement_letterplace(self,self._current_ring(x))
  • new file sage/algebras/letterplace/letterplace_ideal.pyx

    diff --git a/sage/algebras/letterplace/letterplace_ideal.pyx b/sage/algebras/letterplace/letterplace_ideal.pyx
    new file mode 100644
    - +  
     1###############################################################################
     2#
     3#       Copyright (C) 2011 Simon King <simon.king@uni-jena.de>
     4#  Distributed under the terms of the GNU General Public License (GPL),
     5#  version 2 or any later version.  The full text of the GPL is available at:
     6#                  http://www.gnu.org/licenses/
     7#
     8###############################################################################
     9
     10"""
     11Homogeneous ideals of free algebras.
     12
     13For twosided ideals and when the base ring is a field, this
     14implementation also provides Groebner bases and ideal containment
     15tests.
     16
     17EXAMPLES::
     18
     19    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     20    sage: F
     21    Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     22    sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     23    sage: I
     24    Twosided Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     25
     26One can compute Groebner bases out to a finite degree, can compute normal
     27forms and can test containment in the ideal::
     28
     29    sage: I.groebner_basis(degbound=3)
     30    Twosided Ideal (y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     31    sage: (x*y*z*y*x).normal_form(I)
     32    y*z*z*y*z + y*z*z*z*x + y*z*z*z*z
     33    sage: x*y*z*y*x - (x*y*z*y*x).normal_form(I) in I
     34    True
     35
     36AUTHOR:
     37
     38- Simon King (2011-03-22):  See :trac:`7797`.
     39
     40"""
     41
     42from sage.rings.noncommutative_ideals import Ideal_nc
     43from sage.libs.singular.function import lib, singular_function
     44from sage.algebras.letterplace.free_algebra_letterplace cimport FreeAlgebra_letterplace
     45from sage.algebras.letterplace.free_algebra_element_letterplace cimport FreeAlgebraElement_letterplace
     46from sage.all import Infinity
     47
     48#####################
     49# Define some singular functions
     50lib("freegb.lib")
     51singular_system=singular_function("system")
     52poly_reduce=singular_function("NF")
     53
     54class LetterplaceIdeal(Ideal_nc):
     55    """
     56    Graded homogeneous ideals in free algebras.
     57
     58    In the two-sided case over a field, one can compute Groebner bases
     59    up to a degree bound, normal forms of graded homogeneous elements
     60    of the free algebra, and ideal containment.
     61
     62    EXAMPLES::
     63
     64        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     65        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     66        sage: I
     67        Twosided Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     68        sage: I.groebner_basis(2)
     69        Twosided Ideal (x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     70        sage: I.groebner_basis(4)
     71        Twosided Ideal (y*z*y*y - y*z*y*z + y*z*z*y - y*z*z*z, y*z*y*x + y*z*y*z + y*z*z*x + y*z*z*z, y*y*z*y - y*y*z*z + y*z*z*y - y*z*z*z, y*y*z*x + y*y*z*z + y*z*z*x + y*z*z*z, y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     72
     73    Groebner bases are cached. If one has computed a Groebner basis
     74    out to a high degree then it will also be returned if a Groebner
     75    basis with a lower degree bound is requested::
     76
     77        sage: I.groebner_basis(2)
     78        Twosided Ideal (y*z*y*y - y*z*y*z + y*z*z*y - y*z*z*z, y*z*y*x + y*z*y*z + y*z*z*x + y*z*z*z, y*y*z*y - y*y*z*z + y*z*z*y - y*z*z*z, y*y*z*x + y*y*z*z + y*z*z*x + y*z*z*z, y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     79
     80    Of course, the normal form of any element has to satisfy the following::
     81
     82        sage: x*y*z*y*x - (x*y*z*y*x).normal_form(I) in I
     83        True
     84
     85    Left and right ideals can be constructed, but only twosided ideals provide
     86    Groebner bases::
     87
     88        sage: JL = F*[x*y+y*z,x^2+x*y-y*x-y^2]; JL
     89        Left Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     90        sage: JR = [x*y+y*z,x^2+x*y-y*x-y^2]*F; JR
     91        Right Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     92        sage: JR.groebner_basis(2)
     93        Traceback (most recent call last):
     94        ...
     95        TypeError: This ideal is not two-sided. We can only compute two-sided Groebner bases
     96        sage: JL.groebner_basis(2)
     97        Traceback (most recent call last):
     98        ...
     99        TypeError: This ideal is not two-sided. We can only compute two-sided Groebner bases
     100
     101    Also, it is currently not possible to compute a Groebner basis when the base
     102    ring is not a field::
     103
     104        sage: FZ.<a,b,c> = FreeAlgebra(ZZ, implementation='letterplace')
     105        sage: J = FZ*[a^3-b^3]*FZ
     106        sage: J.groebner_basis(2)
     107        Traceback (most recent call last):
     108        ...
     109        TypeError: Currently, we can only compute Groebner bases if the ring of coefficients is a field
     110
     111    The letterplace implementation of free algebras also provides integral degree weights
     112    for the generators, and we can compute Groebner bases for twosided graded homogeneous
     113    ideals::
     114
     115        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace',degrees=[1,2,3])
     116        sage: I = F*[x*y+z-y*x,x*y*z-x^6+y^3]*F
     117        sage: I.groebner_basis(Infinity)
     118        Twosided Ideal (x*z*z - y*x*x*z - y*x*y*y + y*x*z*x + y*y*y*x + z*x*z + z*y*y - z*z*x,
     119        x*y - y*x + z,
     120        x*x*x*x*z*y*y + x*x*x*z*y*y*x - x*x*x*z*y*z - x*x*z*y*x*z + x*x*z*y*y*x*x +
     121        x*x*z*y*y*y - x*x*z*y*z*x - x*z*y*x*x*z - x*z*y*x*z*x +
     122        x*z*y*y*x*x*x + 2*x*z*y*y*y*x - 2*x*z*y*y*z - x*z*y*z*x*x -
     123        x*z*y*z*y + y*x*z*x*x*x*x*x - 4*y*x*z*x*x*z - 4*y*x*z*x*z*x +
     124        4*y*x*z*y*x*x*x + 3*y*x*z*y*y*x - 4*y*x*z*y*z + y*y*x*x*x*x*z +
     125        y*y*x*x*x*z*x - 3*y*y*x*x*z*x*x - y*y*x*x*z*y +
     126        5*y*y*x*z*x*x*x + 4*y*y*x*z*y*x - 4*y*y*y*x*x*z +
     127        4*y*y*y*x*z*x + 3*y*y*y*y*z + 4*y*y*y*z*x*x + 6*y*y*y*z*y +
     128        y*y*z*x*x*x*x + y*y*z*x*z + 7*y*y*z*y*x*x + 7*y*y*z*y*y -
     129        7*y*y*z*z*x - y*z*x*x*x*z - y*z*x*x*z*x + 3*y*z*x*z*x*x +
     130        y*z*x*z*y + y*z*y*x*x*x*x - 3*y*z*y*x*z + 7*y*z*y*y*x*x +
     131        3*y*z*y*y*y - 3*y*z*y*z*x - 5*y*z*z*x*x*x - 4*y*z*z*y*x +
     132        4*y*z*z*z - z*y*x*x*x*z - z*y*x*x*z*x - z*y*x*z*x*x -
     133        z*y*x*z*y + z*y*y*x*x*x*x - 3*z*y*y*x*z + 3*z*y*y*y*x*x +
     134        z*y*y*y*y - 3*z*y*y*z*x - z*y*z*x*x*x - 2*z*y*z*y*x +
     135        2*z*y*z*z - z*z*x*x*x*x*x + 4*z*z*x*x*z + 4*z*z*x*z*x -
     136        4*z*z*y*x*x*x - 3*z*z*y*y*x + 4*z*z*y*z + 4*z*z*z*x*x +
     137        2*z*z*z*y,
     138        x*x*x*x*x*z + x*x*x*x*z*x + x*x*x*z*x*x + x*x*z*x*x*x + x*z*x*x*x*x +
     139        y*x*z*y - y*y*x*z + y*z*z + z*x*x*x*x*x - z*z*y,
     140        x*x*x*x*x*x - y*x*z - y*y*y + z*z)
     141        of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     142
     143    Again, we can compute normal forms::
     144
     145        sage: (z*I.0-I.1).normal_form(I)
     146        0
     147        sage: (z*I.0-x*y*z).normal_form(I)
     148        -y*x*z + z*z
     149
     150    """
     151    def __init__(self, ring, gens, coerce=True, side = "twosided"):
     152        """
     153        INPUT:
     154
     155        - ``ring``: A free algebra in letterplace implementation.
     156        - ``gens``: List, tuple or sequence of generators.
     157        - ``coerce`` (optional bool, default ``True``):
     158          Shall ``gens`` be coerced first?
     159        - ``side``: optional string, one of ``"twosided"`` (default),
     160          ``"left"`` or ``"right"``. Determines whether the ideal
     161          is a left, right or twosided ideal. Groebner bases or
     162          only supported in the twosided case.
     163
     164        TEST::
     165
     166            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     167            sage: from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
     168            sage: LetterplaceIdeal(F,x)
     169            Twosided Ideal (x) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     170            sage: LetterplaceIdeal(F,[x,y],side='left')
     171            Left Ideal (x, y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     172
     173        It is not correctly detected that this class inherits from an
     174        extension class. Therefore, we have to skip one item of the
     175        test suite::
     176
     177            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     178            sage: TestSuite(I).run(skip=['_test_category'],verbose=True)
     179            running ._test_eq() . . . pass
     180            running ._test_not_implemented_methods() . . . pass
     181            running ._test_pickling() . . . pass
     182
     183        """
     184        Ideal_nc.__init__(self, ring, gens, coerce=coerce, side=side)
     185        self.__GB = self
     186        self.__uptodeg = 0
     187    def groebner_basis(self, degbound=None):
     188        """
     189        Twosided Groebner basis with degree bound.
     190
     191        INPUT:
     192
     193        - ``degbound`` (optional integer, or Infinity): If it is provided,
     194          a Groebner basis at least out to that degree is returned. By
     195          default, the current degree bound of the underlying ring is used.
     196
     197        ASSUMPTIONS:
     198
     199        Currently, we can only compute Groebner bases for twosided
     200        ideals, and the ring of coefficients must be a field. A
     201        `TypeError` is raised if one of these conditions is violated.
     202
     203        NOTES:
     204
     205        - The result is cached. The same Groebner basis is returned
     206          if a smaller degree bound than the known one is requested.
     207        - If the degree bound Infinity is requested, it is attempted to
     208          compute a complete Groebner basis. But we can not guarantee
     209          that the computation will terminate, since not all twosided
     210          homogeneous ideals of a free algebra have a finite Groebner
     211          basis.
     212
     213        EXAMPLES::
     214
     215            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     216            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     217
     218        Since `F` was cached and since its degree bound can not be
     219        decreased, it may happen that, as a side effect of other tests,
     220        it already has a degree bound bigger than 3. So, we can not
     221        test against the output of ``I.groebner_basis()``::
     222
     223            sage: F.set_degbound(3)
     224            sage: I.groebner_basis()   # not tested
     225            Twosided Ideal (y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     226            sage: I.groebner_basis(4)
     227            Twosided Ideal (y*z*y*y - y*z*y*z + y*z*z*y - y*z*z*z, y*z*y*x + y*z*y*z + y*z*z*x + y*z*z*z, y*y*z*y - y*y*z*z + y*z*z*y - y*z*z*z, y*y*z*x + y*y*z*z + y*z*z*x + y*z*z*z, y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     228            sage: I.groebner_basis(2) is I.groebner_basis(4)
     229            True
     230            sage: G = I.groebner_basis(4)
     231            sage: G.groebner_basis(3) is G
     232            True
     233
     234        If a finite complete Groebner basis exists, we can compute
     235        it as follows::
     236
     237            sage: I = F*[x*y-y*x,x*z-z*x,y*z-z*y,x^2*y-z^3,x*y^2+z*x^2]*F
     238            sage: I.groebner_basis(Infinity)
     239            Twosided Ideal (z*z*z*y*y + z*z*z*z*x, z*x*x*x + z*z*z*y, y*z - z*y, y*y*x + z*x*x, y*x*x - z*z*z, x*z - z*x, x*y - y*x) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     240
     241        Since the commutators of the generators are contained in the ideal,
     242        we can verify the above result by a computation in a polynomial ring
     243        in negative lexicographic order::
     244
     245            sage: P.<c,b,a> = PolynomialRing(QQ,order='neglex')
     246            sage: J = P*[a^2*b-c^3,a*b^2+c*a^2]
     247            sage: J.groebner_basis()
     248            [b*a^2 - c^3, b^2*a + c*a^2, c*a^3 + c^3*b, c^3*b^2 + c^4*a]
     249
     250        Aparently, the results are compatible, by sending `a` to `x`, `b`
     251        to `y` and `c` to `z`.
     252
     253        """
     254        cdef FreeAlgebra_letterplace A = self.ring()
     255        cdef FreeAlgebraElement_letterplace x
     256        if degbound is None:
     257            degbound = A.degbound()
     258        if self.__uptodeg >= degbound:
     259            return self.__GB
     260        if not A.base().is_field():
     261            raise TypeError, "Currently, we can only compute Groebner bases if the ring of coefficients is a field"
     262        if self.side()!='twosided':
     263            raise TypeError, "This ideal is not two-sided. We can only compute two-sided Groebner bases"
     264        if degbound == Infinity:
     265            while self.__uptodeg<Infinity:
     266                test_bound = 2*max([x._poly.degree() for x in self.__GB.gens()])
     267                self.groebner_basis(test_bound)
     268            return self.__GB
     269        # Set the options required by letterplace
     270        from sage.libs.singular.option import LibSingularOptions
     271        libsingular_options = LibSingularOptions()
     272        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     273        libsingular_options['redTail'] = True
     274        libsingular_options['redSB'] = True
     275        A.set_degbound(degbound)
     276        P = A._current_ring
     277        out = [FreeAlgebraElement_letterplace(A,X,check=False) for X in
     278               singular_system("freegb",P.ideal([x._poly for x in self.__GB.gens()]),
     279                               degbound,A.__ngens, ring = P)]
     280        libsingular_options['redTail'] = bck[0]
     281        libsingular_options['redSB'] = bck[1]
     282        self.__GB = A.ideal(out,side='twosided',coerce=False)
     283        if degbound >= 2*max([x._poly.degree() for x in out]):
     284            degbound = Infinity
     285        self.__uptodeg = degbound
     286        self.__GB.__uptodeg = degbound
     287        return self.__GB
     288
     289    def __contains__(self,x):
     290        """
     291        The containment test is based on a normal form computation.
     292
     293        EXAMPLES::
     294
     295            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     296            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     297            sage: x*I.0-I.1*y+I.0*y in I    # indirect doctest
     298            True
     299            sage: 1 in I
     300            False
     301
     302        """
     303        R = self.ring()
     304        return (x in R) and R(x).normal_form(self).is_zero()
     305
     306    def reduce(self, G):
     307        """
     308        Reduction of this ideal by another ideal,
     309        or normal form of an algebra element with respect to this ideal.
     310
     311        INPUT:
     312
     313        - ``G``: A list or tuple of elements, an ideal,
     314          the ambient algebra, or a single element.
     315
     316        OUTPUT:
     317
     318        - The normal form of ``G`` with respect to this ideal, if
     319          ``G`` is an element of the algebra.
     320        - The reduction of this ideal by the elements resp. generators
     321          of ``G``, if ``G`` is a list, tuple or ideal.
     322        - The zero ideal, if ``G`` is the algebra containing this ideal.
     323
     324        EXAMPLES::
     325
     326            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     327            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     328            sage: I.reduce(F)
     329            Twosided Ideal (0) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     330            sage: I.reduce(x^3)
     331            -y*z*x - y*z*y - y*z*z
     332            sage: I.reduce([x*y])
     333            Twosided Ideal (y*z, x*x - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     334            sage: I.reduce(F*[x^2+x*y,y^2+y*z]*F)
     335            Twosided Ideal (x*y + y*z, -y*x + y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     336
     337        """
     338        P = self.ring()
     339        if not isinstance(G,(list,tuple)):
     340            if G==P:
     341                return P.ideal([P.zero_element()])
     342            if G in P:
     343                return G.normal_form(self)
     344            G = G.gens()
     345        C = P.current_ring()
     346        sI = C.ideal([C(X.letterplace_polynomial()) for X in self.gens()], coerce=False)
     347        selfdeg = max([x.degree() for x in sI.gens()])
     348        gI = P._reductor_(G, selfdeg)
     349        from sage.libs.singular.option import LibSingularOptions
     350        libsingular_options = LibSingularOptions()
     351        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     352        libsingular_options['redTail'] = True
     353        libsingular_options['redSB'] = True
     354        sI = poly_reduce(sI,gI, ring=C, attributes={gI:{"isSB":1}})
     355        libsingular_options['redTail'] = bck[0]
     356        libsingular_options['redSB'] = bck[1]
     357        return P.ideal([FreeAlgebraElement_letterplace(P,x,check=False) for x in sI], coerce=False)
  • sage/categories/rings.py

    diff --git a/sage/categories/rings.py b/sage/categories/rings.py
    a b  
    108108            rings also inherits from the base class of
    109109            rings. Therefore, we implemented a ``__mul__``
    110110            method for parents, that calls a ``_mul_``
    111             method implemented here. See trac ticket #11068.
     111            method implemented here. See :trac:`7797`.
    112112
    113113            INPUT:
    114114
     
    190190            The code is copied from the base class of rings.
    191191            This is since there are rings that do not inherit
    192192            from that class, such as matrix algebras.  See
    193             trac ticket #11068.
     193            :trac:`7797`.
    194194
    195195            EXAMPLE::
    196196
     
    267267            :class:`~sage.rings.ring.Ring`. This is
    268268            because there are rings that do not inherit
    269269            from that class, such as matrix algebras.
    270             See trac ticket #11068.
     270            See :trac:`7797`.
    271271
    272272            INPUT:
    273273
     
    439439            - ``names``: a list of strings to be used as names
    440440              for the variables in the quotient ring.
    441441
    442             EXAMPLES::
     442            EXAMPLES:
    443443
    444                 sage: F.<x,y,z> = FreeAlgebra(QQ, 3)
    445                 sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     444            Usually, a ring inherits a method :meth:`sage.rings.ring.Ring.quotient`.
     445            So, we need a bit of effort to make the following example work with the
     446            category framework::
     447
     448                sage: F.<x,y,z> = FreeAlgebra(QQ)
     449                sage: from sage.rings.noncommutative_ideals import Ideal_nc
     450                sage: class PowerIdeal(Ideal_nc):
     451                ...    def __init__(self, R, n):
     452                ...        self._power = n
     453                ...        self._power = n
     454                ...        Ideal_nc.__init__(self,R,[R.prod(m) for m in CartesianProduct(*[R.gens()]*n)])
     455                ...    def reduce(self,x):
     456                ...        R = self.ring()
     457                ...        return add([c*R(m) for c,m in x if len(m)<self._power],R(0))
     458                ...
     459                sage: I = PowerIdeal(F,3)
    446460                sage: Q = Rings().parent_class.quotient(F,I); Q
    447                 Quotient of Free Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x*y + y*z, x^2 + x*y - y*x - y^2)
     461                Quotient of Free Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x^3, x^2*y, x^2*z, x*y*x, x*y^2, x*y*z, x*z*x, x*z*y, x*z^2, y*x^2, y*x*y, y*x*z, y^2*x, y^3, y^2*z, y*z*x, y*z*y, y*z^2, z*x^2, z*x*y, z*x*z, z*y*x, z*y^2, z*y*z, z^2*x, z^2*y, z^3)
    448462                sage: Q.0
    449463                xbar
    450464                sage: Q.1
    451465                ybar
    452466                sage: Q.2
    453467                zbar
     468                sage: Q.0*Q.1
     469                xbar*ybar
     470                sage: Q.0*Q.1*Q.0
     471                0
    454472
    455473            """
    456474            from sage.rings.quotient_ring import QuotientRing
  • sage/rings/noncommutative_ideals.pyx

    diff --git a/sage/rings/noncommutative_ideals.pyx b/sage/rings/noncommutative_ideals.pyx
    a b  
    1212
    1313AUTHOR:
    1414
    15 - Simon King (2011-03-28), <simon.king@uni-jena.de>: Trac ticket #11068.
     15- Simon King (2011-03-21), <simon.king@uni-jena.de>, :trac:`7797`.
    1616
    1717EXAMPLES::
    1818
     
    3939    )
    4040     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
    4141
     42See :mod:`~sage.algebras.letterplace.letterplace_ideal` for a more
     43elaborate implementation in the special case of ideals in free
     44algebras.
     45
    4246TEST::
    4347
    4448    sage: A = SteenrodAlgebra(2)
     
    137141    Generic non-commutative ideal.
    138142
    139143    All fancy stuff such as the computation of Groebner bases must be
    140     implemented in sub-classes.
     144    implemented in sub-classes. See :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`
     145    for an example.
    141146
    142147    EXAMPLE::
    143148
  • sage/rings/quotient_ring.py

    diff --git a/sage/rings/quotient_ring.py b/sage/rings/quotient_ring.py
    a b  
    2121provides a ``reduce`` method so that ``I.reduce(x)`` is the normal
    2222form of an element `x` with respect to `I` (i.e., we have
    2323``I.reduce(x)==I.reduce(y)`` if `x-y\\in I`, and
    24 ``x-I.reduce(x) in I``). It is planned (trac ticket #7797) to
    25 provide this for the case of homogeneous twosided ideals in free
    26 algebras. By now, we only have the following toy example::
     24``x-I.reduce(x) in I``). Here is a toy example::
    2725
    2826    sage: from sage.rings.noncommutative_ideals import Ideal_nc
    2927    sage: class PowerIdeal(Ideal_nc):
     
    7573    sage: (a+b+2)^4
    7674    16 + 32*a + 32*b
    7775
     76Since trac ticket #7797, there is an implementation of free algebras
     77based on Singular's implementation of the Letterplace Algebra. Our
     78letterplace wrapper allows to provide the above toy example more
     79easily::
     80
     81    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     82    sage: Q3 = F.quo(F*[F.prod(m) for m in CartesianProduct(*[F.gens()]*3)]*F)
     83    sage: Q3
     84    Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x*x*x, x*x*y, x*x*z, x*y*x, x*y*y, x*y*z, x*z*x, x*z*y, x*z*z, y*x*x, y*x*y, y*x*z, y*y*x, y*y*y, y*y*z, y*z*x, y*z*y, y*z*z, z*x*x, z*x*y, z*x*z, z*y*x, z*y*y, z*y*z, z*z*x, z*z*y, z*z*z)
     85    sage: Q3.0*Q3.1-Q3.1*Q3.0
     86    xbar*ybar - ybar*xbar
     87    sage: Q3.0*(Q3.1*Q3.2)-(Q3.1*Q3.2)*Q3.0
     88    0
     89    sage: Q2 = F.quo(F*[F.prod(m) for m in CartesianProduct(*[F.gens()]*2)]*F)
     90    sage: Q2.is_commutative()
     91    True
     92
    7893"""
    7994
    8095###########################################################################
     
    209224        sage: R.quotient(I)
    210225        Ring of integers modulo 2
    211226
     227    Here is an example of the quotient of a free algebra by a
     228    twosided homogeneous ideal (see :trac:`7797`)::
     229
     230        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     231        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     232        sage: Q.<a,b,c> = F.quo(I); Q
     233        Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x*y + y*z, x*x + x*y - y*x - y*y)
     234        sage: a*b
     235        -b*c
     236        sage: a^3
     237        -b*c*a - b*c*b - b*c*c
     238        sage: J = Q*[a^3-b^3]*Q
     239        sage: R.<i,j,k> = Q.quo(J); R
     240        Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field by the ideal (-y*y*z - y*z*x - 2*y*z*z, x*y + y*z, x*x + x*y - y*x - y*y)
     241        sage: i^3
     242        -j*k*i - j*k*j - j*k*k
     243        sage: j^3
     244        -j*k*i - j*k*j - j*k*k
     245
    212246    """
    213247    # 1. Not all rings inherit from the base class of rings.
    214248    # 2. We want to support quotients of free algebras by homogeneous two-sided ideals.
     
    269303        sage: is_QuotientRing(R)
    270304        False
    271305
     306    ::
     307
     308        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     309        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     310        sage: Q = F.quo(I)
     311        sage: is_QuotientRing(Q)
     312        True
     313        sage: is_QuotientRing(F)
     314        False
     315
    272316    """
    273317    return isinstance(x, QuotientRing_nc)
    274318
     
    283327    """
    284328    The quotient ring of `R` by a twosided ideal `I`.
    285329   
    286     This base class is for rings that do not inherit from :class:`~sage.rings.ring.CommutativeRing`.
    287     Real life examples will be available with trac ticket #7797.
     330    EXAMPLES:
     331
     332    This class is for rings that do not inherit from :class:`~sage.rings.ring.CommutativeRing`.
     333    Here is a quotient of a free algebra by a twosided homogeneous ideal::
     334
     335        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     336        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     337        sage: Q.<a,b,c> = F.quo(I); Q
     338        Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x*y + y*z, x*x + x*y - y*x - y*y)
     339        sage: a*b
     340        -b*c
     341        sage: a^3
     342        -b*c*a - b*c*b - b*c*c
     343
     344    A quotient of a quotient is just the quotient of the original top
     345    ring by the sum of two ideals::
     346
     347        sage: J = Q*[a^3-b^3]*Q
     348        sage: R.<i,j,k> = Q.quo(J); R
     349        Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field by the ideal (-y*y*z - y*z*x - 2*y*z*z, x*y + y*z, x*x + x*y - y*x - y*y)
     350        sage: i^3
     351        -j*k*i - j*k*j - j*k*k
     352        sage: j^3
     353        -j*k*i - j*k*j - j*k*k
    288354
    289355    For rings that *do* inherit from :class:`~sage.rings.ring.CommutativeRing`, we provide
    290356    a subclass :class:`QuotientRing_generic`, for backwards compatibility.
     
    305371        sage: S(0) == a^2 + b^2
    306372        True
    307373   
    308     A quotient of a quotient is just the quotient of the original top
     374    Again, a quotient of a quotient is just the quotient of the original top
    309375    ring by the sum of two ideals.
    310376   
    311377    ::
     
    328394        -  ``R`` - a ring.
    329395        -  ``I`` - a twosided ideal of `R`.
    330396        - ``names`` - a list of generator names.
    331        
     397
     398        EXAMPLES::
     399
     400            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     401            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     402            sage: Q.<a,b,c> = F.quo(I); Q
     403            Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x*y + y*z, x*x + x*y - y*x - y*y)
     404            sage: a*b
     405            -b*c
     406            sage: a^3
     407            -b*c*a - b*c*b - b*c*c
     408
    332409        """
    333410        if R not in _Rings:
    334411            raise TypeError, "The first argument must be a ring, but %s is not"%R
     
    365442            sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
    366443            sage: R.quotient_ring(I).construction()
    367444            (QuotientFunctor, Univariate Polynomial Ring in x over Integer Ring)
     445            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     446            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     447            sage: Q = F.quo(I)
     448            sage: Q.construction()
     449            (QuotientFunctor, Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field)
    368450
    369451        TESTS::
    370452       
     
    422504
    423505        AUTHOR:
    424506
    425         - Simon King (2011-03-23): See trac ticket #11068.
     507        - Simon King (2011-03-23): See :trac:`7797`.
    426508
    427509        EXAMPLES:
    428510
     
    432514            sage: P.quo(P.random_element()).is_commutative()
    433515            True
    434516
    435         The non-commutative case is more interesting, but it
    436         will only be available once trac ticket #7797 is merged.
     517        The non-commutative case is more interesting::
     518
     519            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     520            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     521            sage: Q = F.quo(I)
     522            sage: Q.is_commutative()
     523            False
     524            sage: Q.1*Q.2==Q.2*Q.1
     525            False
     526
     527        In the next example, the generators apparently commute::
     528
     529            sage: J = F*[x*y-y*x,x*z-z*x,y*z-z*y,x^3-y^3]*F
     530            sage: R = F.quo(J)
     531            sage: R.is_commutative()
     532            True
    437533
    438534        """
    439535        try:
  • sage/rings/quotient_ring_element.py

    diff --git a/sage/rings/quotient_ring_element.py b/sage/rings/quotient_ring_element.py
    a b  
    533533            0
    534534            sage: a.__cmp__(b)
    535535            1
     536
     537        See :trac:`7797`:
     538
     539            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     540            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     541            sage: Q = F.quo(I)
     542            sage: Q.0^4    # indirect doctest
     543            ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar
     544
    536545        """
    537546        #if self.__rep == other.__rep or ((self.__rep - other.__rep) in self.parent().defining_ideal()):
    538547        #    return 0
  • sage/rings/ring.pyx

    diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx
    a b  
    409409            sage: Q = sage.rings.ring.Ring.quotient(F,I)
    410410            sage: Q.ideal_monoid()
    411411            Monoid of ideals of Quotient of Free Algebra on 3 generators (x, y, z) over Integer Ring by the ideal (x*y + y*z, x^2 + x*y - y*x - y^2)
     412            sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
     413            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     414            sage: Q = F.quo(I)
     415            sage: Q.ideal_monoid()
     416            Monoid of ideals of Quotient of Free Associative Unital Algebra on 3 generators (x, y, z) over Integer Ring by the ideal (x*y + y*z, x*x + x*y - y*x - y*y)
    412417
    413418        """
    414419        if self.__ideal_monoid is not None:
     
    536541            sage: (x+y,z+y^3)*R
    537542            Ideal (x + y, y^3 + z) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 7
    538543
    539         The following was implemented in trac ticket #11068::
     544        The following was implemented in :trac:`7797`::
    540545
    541546            sage: A = SteenrodAlgebra(2)
    542547            sage: A*[A.1+A.2,A.1^2]
     
    609614            sage: RR._ideal_class_()
    610615            <class 'sage.rings.ideal.Ideal_pid'>
    611616
    612         Since #11068, non-commutative rings have ideals as well::
     617        Since :trac:`7797`, non-commutative rings have ideals as well::
    613618
    614619            sage: A = SteenrodAlgebra(2)
    615620            sage: A._ideal_class_()
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    814814        is because ``__mul__`` can not be implemented via inheritance
    815815        from the parent methods of the category, but ``_mul_`` can
    816816        be inherited. This is, e.g., used when creating twosided
    817         ideals of matrix algebras. See trac ticket #11068.
     817        ideals of matrix algebras. See :trac:`7797`.
    818818
    819819        EXAMPLE::
    820820
     821            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
    821822            sage: MS = MatrixSpace(QQ,2,2)
    822823
    823824        This matrix space is in fact an algebra, and in particular
  • setup.py

    diff --git a/setup.py b/setup.py
    a b  
    855855      packages    = ['sage',
    856856                     
    857857                     'sage.algebras',
     858                     'sage.algebras.letterplace',
    858859                     'sage.algebras.quatalg',
    859860                     'sage.algebras.steenrod',
    860861