Ticket #7797: trac7797-full_letterplace_wrapper_combined.patch

File trac7797-full_letterplace_wrapper_combined.patch, 117.6 KB (added by SimonKing, 6 months 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