Ticket #7797: trac7797-full_letterplace_wrapper_rel11068.patch

File trac7797-full_letterplace_wrapper_rel11068.patch, 100.1 KB (added by SimonKing, 8 years ago)

A full wrapper for Singular's letterplace functionality, plus complete Groebner bases; based on top of 11068

  • doc/en/reference/algebras.rst

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1301320222 -7200
    # Node ID 0f2cfca8e89716c862686a46b589b4cfce256dd1
    # Parent  16f7d9cead4916fe576a9d9ea6c1bc908ea4c698
    #7797: A full wrapper for Singular's letterplace.
    An alternative implementation of free algebras (faster arithmetic).
    Degree-wise Groebner bases for twosided homogeneous ideals in free algebras.
    * * *
    Complete Groebner bases for 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  
    100100               include_dirs = [SAGE_INC + 'FLINT/'],
    101101               depends = flint_depends),
    102102
     103    Extension('sage.algebras.letterplace.free_algebra_letterplace',
     104              sources = ['sage/algebras/letterplace/free_algebra_letterplace.pyx'],
     105              language="c++",
     106              include_dirs = [SAGE_ROOT +'/local/include/singular'],
     107              depends = singular_depends),
     108
     109    Extension('sage.algebras.letterplace.free_algebra_element_letterplace',
     110              sources = ['sage/algebras/letterplace/free_algebra_element_letterplace.pyx'],
     111              language="c++",
     112              include_dirs = [SAGE_ROOT +'/local/include/singular'],
     113              depends = singular_depends),
     114
     115    Extension('sage.algebras.letterplace.letterplace_ideal',
     116              sources = ['sage/algebras/letterplace/letterplace_ideal.pyx'],
     117              language="c++",
     118              include_dirs = [SAGE_ROOT +'/local/include/singular'],
     119              depends = singular_depends),
     120
    103121    Extension('sage.algebras.quatalg.quaternion_algebra_cython',
    104122               sources = ['sage/algebras/quatalg/quaternion_algebra_cython.pyx'],
    105123               language='c++',
  • sage/algebras/free_algebra.py

    diff --git a/sage/algebras/free_algebra.py b/sage/algebras/free_algebra.py
    a b  
    1010
    1111- Simon King (2011-04): Put free algebras into the category framework.
    1212
     13- Simon King (2011-03-21): reimplement free algebra constructor, using
     14  a :class:`~sage.structure.factory.UniqueFactory` for handling
     15  different implementations of free algebras.
     16
    1317EXAMPLES::
    1418
    1519    sage: F = FreeAlgebra(ZZ,3,'x,y,z')
     
    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 trac
     28ticket #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
     31homogeneous 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
    2345TESTS::
    2446
    2547    sage: F = FreeAlgebra(GF(5),3,'x')
    2648    sage: TestSuite(F).run()
     49    sage: F is loads(dumps(F))
     50    True
     51    sage: F = FreeAlgebra(GF(5),3,'x', implementation='letterplace')
     52    sage: TestSuite(F).run()
     53    sage: F is loads(dumps(F))
     54    True
    2755
    2856::
    2957
    3058    sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
    3159    sage: TestSuite(F).run()
     60    sage: F is loads(dumps(F))
     61    True
     62    sage: F.<x,y,z> = FreeAlgebra(GF(5),3, implementation='letterplace')
     63    sage: TestSuite(F).run()
     64    sage: F is loads(dumps(F))
     65    True
    3266
    3367::
    3468
    3569    sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'])
    3670    sage: TestSuite(F).run()
     71    sage: F is loads(dumps(F))
     72    True
     73    sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'], implementation='letterplace')
     74    sage: TestSuite(F).run()
     75    sage: F is loads(dumps(F))
     76    True
    3777
    3878::
    3979
    4080    sage: F = FreeAlgebra(GF(5),3, 'abc')
    4181    sage: TestSuite(F).run()
     82    sage: F is loads(dumps(F))
     83    True
     84    sage: F = FreeAlgebra(GF(5),3, 'abc', implementation='letterplace')
     85    sage: TestSuite(F).run()
     86    sage: F is loads(dumps(F))
     87    True
    4288
    4389::
    4490
    45     sage: F = FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x')
     91    sage: F = FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x')
    4692    sage: TestSuite(F).run()
     93    sage: F is loads(dumps(F))
     94    True
     95
     96Note that the letterplace implementation can only be used if the corresponding
     97(multivariate) polynomial ring has an implementation in Singular::
     98
     99    sage: FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x', implementation='letterplace')
     100    Traceback (most recent call last):
     101    ...
     102    NotImplementedError: The letterplace implementation is not available for the free algebra you requested
    47103
    48104"""
    49105
     
    67123
    68124import sage.structure.parent_gens
    69125
    70        
    71 def FreeAlgebra(R, n, names):
     126from sage.structure.factory import UniqueFactory
     127from sage.all import PolynomialRing
     128from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular
     129
     130class FreeAlgebraFactory(UniqueFactory):
    72131    """
    73     Return the free algebra over the ring `R` on `n`
    74     generators with given names.
    75    
    76     INPUT:
    77    
    78    
    79     -  ``R`` - ring
    80    
    81     -  ``n`` - integer
    82    
    83     -  ``names`` - string or list/tuple of n strings
    84    
    85    
    86     OUTPUT: a free algebra
    87    
     132    A constructor of free algebras.
     133
     134    See :mod:`~sage.algebras.free_algebra` for examples and corner cases.
     135
    88136    EXAMPLES::
    89    
    90         sage: FreeAlgebra(GF(5),3,'x')
    91         Free Algebra on 3 generators (x0, x1, x2) over Finite Field of size 5
    92         sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
    93         sage: (x+y+z)^2
    94         x^2 + x*y + x*z + y*x + y^2 + y*z + z*x + z*y + z^2
    95         sage: FreeAlgebra(GF(5),3, 'xx, zba, Y')
    96         Free Algebra on 3 generators (xx, zba, Y) over Finite Field of size 5
    97         sage: FreeAlgebra(GF(5),3, 'abc')
    98         Free Algebra on 3 generators (a, b, c) over Finite Field of size 5
    99         sage: FreeAlgebra(GF(5),1, 'z')
    100         Free Algebra on 1 generators (z,) over Finite Field of size 5
    101         sage: FreeAlgebra(GF(5),1, ['alpha'])
    102         Free Algebra on 1 generators (alpha,) over Finite Field of size 5
    103         sage: FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x')
    104         Free Algebra on 2 generators (x0, x1) over Free Algebra on 1 generators (a,) over Integer Ring
    105    
    106     Free algebras are globally unique::
    107    
    108         sage: F = FreeAlgebra(ZZ,3,'x,y,z')
    109         sage: G = FreeAlgebra(ZZ,3,'x,y,z')
     137
     138        sage: F.<x,y,z> = FreeAlgebra(GF(5),3)  # indirect doctest
     139        sage: F is loads(dumps(F))
     140        True
     141        sage: F is FreeAlgebra(GF(5),['x','y','z'])
     142        True
     143        sage: G = FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace')
    110144        sage: F is G
     145        False
     146        sage: G is FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace')
    111147        True
    112    
    113     Free algebras commute with their base ring.
    114    
    115     ::
    116    
    117         sage: K.<a,b> = FreeAlgebra(QQ,2)
    118         sage: K.is_commutative()
    119         False
    120         sage: L.<c> = FreeAlgebra(K,1)
    121         sage: L.is_commutative()
    122         False
    123         sage: s = a*b^2 * c^3; s
    124         a*b^2*c^3
    125         sage: parent(s)
    126         Free Algebra on 1 generators (c,) over Free Algebra on 2 generators (a, b) over Rational Field
    127         sage: c^3 * a * b^2
    128         a*b^2*c^3
     148        sage: copy(G) is G
     149        True
     150        sage: copy(F) is F
     151        True
     152
    129153    """
    130     names = sage.structure.parent_gens.normalize_names(n, names)
    131     return cache(R, n, names)
     154    def create_key(self,base_ring, arg1=None, arg2=None,
     155                                      sparse=False, order='degrevlex',
     156                                      names=None, name=None,
     157                                      implementation=None):
     158        """
     159        Create the key under which a free algebra is stored.
     160
     161        TESTS::
     162
     163            sage: FreeAlgebra.create_key(GF(5),['x','y','z'])
     164            (Finite Field of size 5, ('x', 'y', 'z'))
     165            sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3)
     166            (Finite Field of size 5, ('x', 'y', 'z'))
     167            sage: FreeAlgebra.create_key(GF(5),3,'xyz')
     168            (Finite Field of size 5, ('x', 'y', 'z'))
     169            sage: FreeAlgebra.create_key(GF(5),['x','y','z'], implementation='letterplace')
     170            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     171            sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3, implementation='letterplace')
     172            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     173            sage: FreeAlgebra.create_key(GF(5),3,'xyz', implementation='letterplace')
     174            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     175
     176        """
     177        if arg1 is None and arg2 is None and names is None:
     178            # this is used for pickling
     179            return (base_ring,)
     180        PolRing = None
     181        # test if we can use libSingular/letterplace
     182        if implementation is not None and implementation != 'generic':
     183            try:
     184                PolRing = PolynomialRing(base_ring, arg1, arg2,
     185                                   sparse=sparse, order=order,
     186                                   names=names, name=name,
     187                                   implementation=implementation if implementation!='letterplace' else None)
     188                if not isinstance(PolRing,MPolynomialRing_libsingular):
     189                    if PolRing.ngens() == 1:
     190                        PolRing = PolynomialRing(base_ring,1,PolRing.variable_names())
     191                        if not isinstance(PolRing,MPolynomialRing_libsingular):
     192                            raise TypeError
     193                    else:
     194                        raise TypeError
     195            except (TypeError, NotImplementedError),msg:
     196                raise NotImplementedError, "The letterplace implementation is not available for the free algebra you requested"
     197        if PolRing is not None:
     198            return (PolRing,)
     199        # normalise the generator names
     200        from sage.all import Integer
     201        if isinstance(arg1, (int, long, Integer)):
     202            arg1, arg2 = arg2, arg1
     203        if not names is None:
     204            arg1 = names
     205        elif not name is None:
     206            arg1 = name
     207        if arg2 is None:
     208            arg2 = len(arg1)
     209        names = sage.structure.parent_gens.normalize_names(arg2,arg1)
     210        return base_ring, names
     211    def create_object(self, version, key):
     212        """
     213        Construct the free algebra that belongs to a unique key.
     214
     215        NOTE:
     216
     217        Of course, that method should not be called directly,
     218        since it does not use the cache of free algebras.
     219
     220        TESTS::
     221
     222            sage: FreeAlgebra.create_object('4.6.2', (QQ['x','y'],))
     223            Free Associative Unital Algebra on 2 generators (x, y) over Rational Field
     224            sage: FreeAlgebra.create_object('4.6.2', (QQ['x','y'],)) is FreeAlgebra(QQ,['x','y'])
     225            False
     226
     227        """
     228        if len(key)==1:
     229#            if key[0].ngens()<=1:
     230#                return key[0]
     231            from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     232            return FreeAlgebra_letterplace(key[0])
     233#        if len(key[1])<=1:
     234#            return PolynomialRing(key[0],key[1])
     235        return FreeAlgebra_generic(key[0],len(key[1]),key[1])
     236
     237FreeAlgebra = FreeAlgebraFactory('FreeAlgebra')
    132238
    133239def is_FreeAlgebra(x):
    134240    """
     
    143249        False
    144250        sage: is_FreeAlgebra(FreeAlgebra(ZZ,100,'x'))
    145251        True
     252        sage: is_FreeAlgebra(FreeAlgebra(ZZ,10,'x',implementation='letterplace'))
     253        True
     254
    146255    """
    147     return isinstance(x, FreeAlgebra_generic)
     256    from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     257    return isinstance(x, (FreeAlgebra_generic,FreeAlgebra_letterplace))
    148258
    149259
    150260class FreeAlgebra_generic(Algebra):
     
    163273        x*y*x*y*x*y*x*y*x*y*x*y + x*y*z*x*y*z*x*y*z*x*y*z
    164274        sage: (2 + x*z + x^2)^2 + (x - y)^2
    165275        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
     276
     277    TESTS:
     278
     279    Free algebras commute with their base ring.
     280    ::
     281   
     282        sage: K.<a,b> = FreeAlgebra(QQ)
     283        sage: K.is_commutative()
     284        False
     285        sage: L.<c,d> = FreeAlgebra(K)
     286        sage: L.is_commutative()
     287        False
     288        sage: s = a*b^2 * c^3; s
     289        a*b^2*c^3
     290        sage: parent(s)
     291        Free Algebra on 2 generators (c, d) over Free Algebra on 2 generators (a, b) over Rational Field
     292        sage: c^3 * a * b^2
     293        a*b^2*c^3
     294
    166295    """
    167296    Element = FreeAlgebraElement
    168297    def __init__(self, R, n, names):
     
    175304        -  ``n`` - an integer
    176305       
    177306        -  ``names`` - generator names
     307
     308        TEST:
     309
     310        Note that the following is *not* the recommended way to create
     311        a free algebra.
     312        ::
     313
     314            sage: from sage.algebras.free_algebra import FreeAlgebra_generic
     315            sage: FreeAlgebra_generic(ZZ,3,'abc')
     316            Free Algebra on 3 generators (a, b, c) over Integer Ring
     317           
    178318        """
    179319        if not isinstance(R, Ring):
    180320            raise TypeError, "Argument R must be a ring."
     
    219359    def __cmp__(self, other):
    220360        """
    221361        Two free algebras are considered the same if they have the same
    222         base ring, number of generators and variable names.
     362        base ring, number of generators and variable names, and the same
     363        implementation.
    223364       
    224365        EXAMPLES::
    225366       
     
    234375            False
    235376            sage: F == FreeAlgebra(QQ,3,'y')
    236377            False
     378
     379        Note that since trac ticket #7797 there is a different
     380        implementation of free algebras. Two corresponding free
     381        algebras in different implementations are not equal, but there
     382        is a coercion::
     383
     384           
    237385        """
    238386        if not isinstance(other, FreeAlgebra_generic):
    239387            return -1
    240388        c = cmp(self.base_ring(), other.base_ring())
    241389        if c: return c
    242         c = cmp(self.__ngens, other.__ngens)
     390        c = cmp(self.__ngens, other.ngens())
    243391        if c: return c
    244392        c = cmp(self.variable_names(), other.variable_names())
    245393        if c: return c
     
    252400        EXAMPLES::
    253401       
    254402            sage: F = FreeAlgebra(QQ,3,'x')
    255             sage: print F  # indirect doctest
     403            sage: F  # indirect doctest
    256404            Free Algebra on 3 generators (x0, x1, x2) over Rational Field
    257405            sage: F.rename('QQ<<x0,x1,x2>>')
    258             sage: print F
     406            sage: F
    259407            QQ<<x0,x1,x2>>
    260408            sage: FreeAlgebra(ZZ,1,['a'])
    261409            Free Algebra on 1 generators (a,) over Integer Ring
     
    265413
    266414    def _element_constructor_(self, x):
    267415        """
    268        Coerce x into self.
     416       Convert x into self.
    269417       
    270418       EXAMPLES::
    271419       
    272420           sage: R.<x,y> = FreeAlgebra(QQ,2)
    273421           sage: R(3) # indirect doctest
    274422           3
     423
     424       TESTS::
     425
     426           sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     427           sage: L.<x,y,z> = FreeAlgebra(ZZ,3,implementation='letterplace')
     428           sage: F(x)     # indirect doctest
     429           x
     430           sage: F.1*L.2
     431           y*z
     432           sage: (F.1*L.2).parent() is F
     433           True
     434
     435       ::
     436
     437           sage: K.<z> = GF(25)
     438           sage: F.<a,b,c> = FreeAlgebra(K,3)
     439           sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace')
     440           sage: F.1+(z+1)*L.2
     441           b + (z+1)*c
     442         
    275443       """
    276444        if isinstance(x, FreeAlgebraElement):
    277445            P = x.parent()
     
    279447                return x
    280448            if not (P is self.base_ring()):
    281449                return self.element_class(self, x)
     450        elif hasattr(x,'letterplace_polynomial'):
     451            P = x.parent()
     452            if self.has_coerce_map_from(P): # letterplace versus generic
     453                ngens = P.ngens()
     454                M = self.__monoid
     455                def exp_to_monomial(T):
     456                    out = []
     457                    for i in xrange(len(T)):
     458                        if T[i]:
     459                            out.append((i%ngens,T[i]))
     460                    return M(out)
     461                return self.element_class(self, dict([(exp_to_monomial(T),c) for T,c in x.letterplace_polynomial().dict().iteritems()]))
    282462        # ok, not a free algebra element (or should not be viewed as one).
     463        if isinstance(x, basestring):
     464            from sage.all import sage_eval
     465            return sage_eval(x,locals=self.gens_dict())
    283466        F = self.__monoid
    284467        R = self.base_ring()
    285468        # coercion from free monoid
     
    300483
    301484        - this free algebra
    302485
     486        - a free algebra in letterplace implementation that has
     487          the same generator names and whose base ring coerces
     488          into self's base ring
     489
    303490        - the underlying monoid
    304491
    305492        - anything that coerces to the base ring of this free algebra
     
    367554            TypeError: no canonical coercion from Free Algebra on 3 generators
    368555            (x, y, z) over Finite Field of size 7 to Free Algebra on 3
    369556            generators (x, y, z) over Integer Ring
     557
     558        TESTS::
     559
     560           sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     561           sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace')
     562           sage: F(x)
     563           x
     564           sage: F.1*L.2     # indirect doctest
     565           y*z
     566
    370567        """
    371568        try:
    372569            R = x.parent()
     
    390587        return self._coerce_try(x, [self.base_ring()])
    391588
    392589    def _coerce_map_from_(self, R):
     590        """
     591        Test whether there is a coercion from R to self.
     592
     593        TESTS::
     594
     595           sage: K.<z> = GF(25)
     596           sage: F.<a,b,c> = FreeAlgebra(K,3)
     597           sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace')
     598           sage: F.1+(z+1)*L.2      # indirect doctest
     599           b + (z+1)*c
     600
     601        """
    393602        if self.__monoid.has_coerce_map_from(R):
    394603            return True
    395604
  • sage/algebras/free_algebra_element.py

    diff --git a/sage/algebras/free_algebra_element.py b/sage/algebras/free_algebra_element.py
    a b  
    8181        EXAMPLES::
    8282       
    8383            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    84             sage: repr(-x+3*y*z)
     84            sage: repr(-x+3*y*z)    # indirect doctest
    8585            '-x + 3*y*z'
    8686
    8787        Trac ticket #11068 enables the use of local variable names::
     
    114114        EXAMPLES::
    115115       
    116116            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    117             sage: latex(-x+3*y^20*z)
     117            sage: latex(-x+3*y^20*z)   # indirect doctest
    118118            \left(-1\right)x + 3y^{20}z
    119119            sage: alpha,beta,gamma=FreeAlgebra(ZZ,3,'alpha,beta,gamma').gens()
    120120            sage: latex(alpha-beta)
     
    212212        EXAMPLES::
    213213       
    214214            sage: R.<x,y> = FreeAlgebra(QQ,2)
    215             sage: x + y
     215            sage: x + y    # indirect doctest
    216216            x + y
    217217        """
    218218        A = self.parent()
     
    249249        EXAMPLES::
    250250       
    251251            sage: R.<x,y> = FreeAlgebra(QQ,2)
    252             sage: -(x+y)
     252            sage: -(x+y)    # indirect doctest
    253253            -x - y
    254254        """
    255255        y = self.parent()(0)
     
    267267        EXAMPLES::
    268268       
    269269            sage: R.<x,y> = FreeAlgebra(QQ,2)
    270             sage: x - y
     270            sage: x - y    # indirect doctest
    271271            x - y
    272272        """
    273273        A = self.parent()
     
    305305        EXAMPLES::
    306306       
    307307            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    308             sage: (x+y+x*y)*(x+y+1)
     308            sage: (x+y+x*y)*(x+y+1)    # indirect doctest
    309309            x + y + x^2 + 2*x*y + y*x + y^2 + x*y*x + x*y^2
    310310        """
    311311        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"""
     11Homogeneous elements of free algebras, in letterplace implementation
     12
     13AUTHOR:
     14
     15- Simon King (2011-03-23): Trac ticket #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    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    """
     48    def __init__(self, A, x, check=True):
     49        """
     50        INPUT:
     51
     52        - A free associative unital algebra in letterplace implementation, `A`.
     53        - A homogeneous polynomial that can be coerced into the currently
     54          used polynomial ring of `A`.
     55        - ``check`` (optional bool, default ``True``): Do not attempt the
     56          above coercion (for internal use only).
     57
     58        TEST::
     59
     60            sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace
     61            sage: F.<x,y,z> = FreeAlgebra(GF(3), implementation='letterplace')
     62            sage: F.set_degbound(2)
     63            sage: P = F.current_ring()
     64            sage: F.set_degbound(4)
     65            sage: P == F.current_ring()
     66            False
     67            sage: p = FreeAlgebraElement_letterplace(F,P.1*P.3+2*P.0*P.4); p
     68            -x*y + y*x
     69            sage: loads(dumps(p)) == p
     70            True
     71           
     72        """
     73        cdef FreeAlgebra_letterplace P = A
     74        if check:
     75            if not x.is_homogeneous():
     76                raise ValueError, "Free algebras based on Letterplace can currently only work with homogeneous elements"
     77            P.set_degbound(x.degree())
     78            x = P._current_ring(x)
     79        AlgebraElement.__init__(self,P)
     80        self._poly = x
     81    def __reduce__(self):
     82        """
     83        Pickling.
     84
     85        TEST::
     86
     87            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     88            sage: loads(dumps(x*y*x)) == x*y*x   # indirect doctest
     89            True
     90
     91        """
     92        return self.__class__, (self._parent,self._poly)
     93    def __copy__(self):
     94        """
     95        TEST::
     96
     97            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     98            sage: copy(x*y*z+z*y*x) == x*y*z+z*y*x   # indirect doctest
     99            True
     100
     101        """
     102        self._poly = (<FreeAlgebra_letterplace>self._parent)._current_ring(self._poly)
     103        return self.__class__(self._parent,self._poly,check=False)
     104    def __hash__(self):
     105        """
     106        TEST::
     107
     108            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     109            sage: set([x*y*z, z*y+x*z,x*y*z])  # indirect doctest
     110            set([x*z + z*y, x*y*z])
     111
     112        """
     113        return hash(self._poly)
     114    def __iter__(self):
     115        """
     116        Iterates over the pairs "tuple of exponents, coefficient".
     117
     118        EXAMPLE::
     119
     120            sage: F.<w,x,y,z> = FreeAlgebra(GF(3), implementation='letterplace')
     121            sage: p = x*y-z^2
     122            sage: list(p)   # indirect doctest
     123            [((0, 0, 0, 1, 0, 0, 0, 1), 2), ((0, 1, 0, 0, 0, 0, 1, 0), 1)]
     124
     125        """
     126        return self._poly.dict().iteritems()
     127    def _repr_(self):
     128        """
     129        TEST::
     130
     131            sage: K.<z> = GF(25)
     132            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     133            sage: -(a+b*(z+1)-c)^2     # indirect doctest
     134            -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
     135
     136        """
     137        cdef list L = []
     138        cdef FreeAlgebra_letterplace P = self._parent
     139        cdef int ngens = P.__ngens
     140        if P._base.is_atomic_repr():
     141            for E,c in zip(self._poly.exponents(),self._poly.coefficients()):
     142                monstr = P.exponents_to_string(E)
     143                if monstr:
     144                    if c==1:
     145                        if L:
     146                            L.extend(['+',monstr])
     147                        else:
     148                            L.append(monstr)
     149                    elif c==-1:
     150                        if L:
     151                            L.extend(['-',monstr])
     152                        else:
     153                            L.append('-'+monstr)
     154                    else:
     155                        if L:
     156                            if c>=0:
     157                                L.extend(['+',repr(c)+'*'+monstr])
     158                            else:
     159                                L.extend(['-',repr(-c)+'*'+monstr])
     160                        else:
     161                            L.append(repr(c)+'*'+monstr)
     162                else:
     163                    if c>=0:
     164                        if L:
     165                            L.extend(['+',repr(c)])
     166                        else:
     167                            L.append(repr(c))
     168                    else:
     169                        if L:
     170                            L.extend(['-',repr(-c)])
     171                        else:
     172                            L.append(repr(c))
     173        else:
     174            for E,c in zip(self._poly.exponents(),self._poly.coefficients()):
     175                monstr = P.exponents_to_string(E)
     176                if monstr:
     177                    if c==1:
     178                        if L:
     179                            L.extend(['+',monstr])
     180                        else:
     181                            L.append(monstr)
     182                    elif c==-1:
     183                        if L:
     184                            L.extend(['-',monstr])
     185                        else:
     186                            L.append('-'+monstr)
     187                    else:
     188                        if L:
     189                            L.extend(['+','('+repr(c)+')*'+monstr])
     190                        else:
     191                            L.append('('+repr(c)+')*'+monstr)
     192                else:
     193                    if L:
     194                        L.extend(['+',repr(c)])
     195                    else:
     196                        L.append(repr(c))
     197        if L:
     198            return ' '.join(L)
     199        return '0'
     200
     201    def degree(self):
     202        """
     203        Return the degree of this element.
     204
     205        NOTE:
     206
     207        Currently, generators can only have the degree one.
     208
     209        EXAMPLE::
     210
     211            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     212            sage: ((x+y+z)^3).degree()
     213            3
     214        """
     215        return self._poly.degree()
     216
     217    def letterplace_polynomial(self):
     218        """
     219        Return the commutative polynomial that is used internally to represent this free algebra element.
     220
     221        EXAMPLE::
     222
     223            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     224            sage: ((x+y-z)^2).letterplace_polynomial()
     225            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
     226
     227        """
     228        return self._poly
     229
     230    def lm(self):
     231        """
     232        The leading monomial of this free algebra element.
     233
     234        EXAMPLE::
     235
     236            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     237            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lm()
     238            x*x*y
     239           
     240        """
     241        return FreeAlgebraElement_letterplace(self._parent, self._poly.lm())
     242
     243    def lt(self):
     244        """
     245        The leading term (monomial times coefficient) of this free algebra
     246        element.
     247
     248        EXAMPLE::
     249
     250            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     251            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lt()
     252            20*x*x*y
     253
     254        """
     255        return FreeAlgebraElement_letterplace(self._parent, self._poly.lt())
     256
     257    def lc(self):
     258        """
     259        The leading coefficient of this free algebra element, as element
     260        of the base ring.
     261
     262        EXAMPLE::
     263
     264            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     265            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lc()
     266            20
     267            sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lc().parent() is F.base()
     268            True
     269
     270        """
     271        return self._poly.lc()
     272
     273    def __nonzero__(self):
     274        """
     275        TEST::
     276
     277            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     278            sage: bool(x)      # indirect doctest
     279            True
     280            sage: bool(F.zero())
     281            False
     282
     283        """
     284        return bool(self._poly)
     285
     286    def lm_divides(self, FreeAlgebraElement_letterplace p):
     287        if self._parent is not p._parent:
     288            raise TypeError, "The two arguments must be elements in the same free algebra."
     289        cdef FreeAlgebra_letterplace A = self._parent
     290        P = A._current_ring
     291        p_poly = p._poly = P(p._poly)
     292        s_poly = self._poly = P(self._poly)
     293        cdef int p_d = p_poly.degree()
     294        cdef int s_d = s_poly.degree()
     295        if s_d>p_d:
     296            return False
     297        cdef int i
     298        if P.monomial_divides(s_poly,p_poly):
     299            return True
     300        for i from 0 <= i < p_d-s_d:
     301            s_poly = singular_system("stest",s_poly,1,
     302                                     A._degbound,A.__ngens,ring=P)
     303            if P.monomial_divides(s_poly,p_poly):
     304                return True
     305        return False
     306
     307    def __richcmp__(left, right, int op):
     308        """
     309        TEST::
     310
     311            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     312            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     313            sage: p-p.lt()<p    # indirect doctest
     314            True
     315
     316        """
     317        return (<Element>left)._richcmp(right, op)
     318    def __cmp__(left, right):
     319        """
     320        TEST::
     321
     322            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     323            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     324            sage: cmp(p,p-p.lt())    # indirect doctest
     325            1
     326
     327        """
     328        return (<Element>left)._cmp(right)
     329
     330    cdef int _cmp_c_impl(self, Element other) except -2:
     331        """
     332        Auxiliary method for comparison.
     333
     334        TEST::
     335
     336            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     337            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     338            sage: p-p.lt()<p    # indirect doctest
     339            True
     340
     341        """
     342        cdef int c = cmp(type(self),type(other))
     343        if c: return c
     344        return cmp((<FreeAlgebraElement_letterplace>self)._poly,(<FreeAlgebraElement_letterplace>other)._poly)
     345
     346    ################################
     347    ## Arithmetic
     348    cpdef ModuleElement _neg_(self):
     349        """
     350        TEST::
     351
     352            sage: K.<z> = GF(25)
     353            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     354            sage: -((z+2)*a^2*b+3*c^3)  # indirect doctest
     355            (4*z + 3)*a*a*b + (2)*c*c*c
     356            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     357            sage: -(3*x*y+2*z^2)
     358            -3*x*y - 2*z*z
     359
     360        """
     361        return FreeAlgebraElement_letterplace(self._parent,-self._poly,check=False)
     362    cpdef ModuleElement _add_(self, ModuleElement other):
     363        """
     364        Addition, under the side condition that either one summand
     365        is zero, or both summands have the same degree.
     366
     367        TEST::
     368
     369            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     370            sage: x+y    # indirect doctest
     371            x + y
     372            sage: x+1
     373            Traceback (most recent call last):
     374            ...
     375            ArithmeticError: Can only add elements of the same degree
     376            sage: x+0
     377            x
     378            sage: 0+x
     379            x
     380
     381        """
     382        if not other:
     383            return self
     384        if not self:
     385            return other
     386        cdef FreeAlgebraElement_letterplace right = other       
     387        if right._poly.degree()!=self._poly.degree():
     388            raise ArithmeticError, "Can only add elements of the same degree"
     389        # update the polynomials
     390        cdef FreeAlgebra_letterplace A = self._parent
     391        self._poly = A._current_ring(self._poly)
     392        right._poly = A._current_ring(right._poly)
     393        return FreeAlgebraElement_letterplace(self._parent,self._poly+right._poly,check=False)
     394
     395    cpdef ModuleElement _sub_(self, ModuleElement other):
     396        """
     397        Difference, under the side condition that either one summand
     398        is zero or both have the same degree.
     399
     400        TEST::
     401
     402            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     403            sage: x*y-y*x     # indirect doctest
     404            x*y - y*x
     405            sage: x-1
     406            Traceback (most recent call last):
     407            ...
     408            ArithmeticError: Can only subtract elements of the same degree
     409            sage: x-0
     410            x
     411            sage: 0-x
     412            -x
     413
     414        """
     415        if not other:
     416            return self
     417        if not self:
     418            return -other
     419        cdef FreeAlgebraElement_letterplace right = other
     420        if right._poly.degree()!=self._poly.degree():
     421            raise ArithmeticError, "Can only subtract elements of the same degree"
     422        # update the polynomials
     423        cdef FreeAlgebra_letterplace A = self._parent
     424        self._poly = A._current_ring(self._poly)
     425        right._poly = A._current_ring(right._poly)
     426        return FreeAlgebraElement_letterplace(self._parent,self._poly-right._poly,check=False)
     427
     428    cpdef ModuleElement _lmul_(self, RingElement right):
     429        """
     430        Multiplication from the right with an element of the base ring.
     431
     432        TEST::
     433
     434            sage: K.<z> = GF(25)
     435            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     436            sage: (a+b)*(z+1)    # indirect doctest
     437            (z + 1)*a + (z + 1)*b
     438
     439        """
     440        return FreeAlgebraElement_letterplace(self._parent,self._poly._lmul_(right),check=False)
     441
     442    cpdef ModuleElement _rmul_(self, RingElement left):
     443        """
     444        Multiplication from the left with an element of the base ring.
     445
     446        TEST::
     447
     448            sage: K.<z> = GF(25)
     449            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     450            sage: (z+1)*(a+b)   # indirect doctest
     451            (z + 1)*a + (z + 1)*b
     452
     453        """
     454        return FreeAlgebraElement_letterplace(self._parent,self._poly._rmul_(left),check=False)
     455
     456    cpdef RingElement _mul_(self, RingElement other):
     457        cdef FreeAlgebraElement_letterplace left = self
     458        cdef FreeAlgebraElement_letterplace right = other
     459        cdef FreeAlgebra_letterplace A = left._parent
     460        A.set_degbound(left._poly.degree()+right._poly.degree())
     461        # we must put the polynomials into the same ring
     462        left._poly = A._current_ring(left._poly)
     463        right._poly = A._current_ring(right._poly)
     464        rshift = singular_system("stest",right._poly,left._poly.degree(),A._degbound,A.__ngens, ring=A._current_ring)
     465        return FreeAlgebraElement_letterplace(A,left._poly*rshift, check=False)
     466
     467    def __pow__(FreeAlgebraElement_letterplace self, int n, k):
     468        """
     469        TEST::
     470
     471            sage: K.<z> = GF(25)
     472            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     473            sage: (a+z*b)^3    # indirect doctest
     474            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
     475
     476        """
     477        cdef FreeAlgebra_letterplace A = self._parent
     478        if n<0:
     479            raise ValueError, "Negative exponents are not allowed"
     480        if n==0:
     481            return FreeAlgebraElement_letterplace(A, A._current_ring(1),
     482                                                  check=False)
     483        if n==1:
     484            return self
     485        A.set_degbound(self._poly.degree()*n)
     486        cdef MPolynomial_libsingular p,q
     487        self._poly = A._current_ring(self._poly)
     488        cdef int d = self._poly.degree()
     489        q = p = self._poly
     490        cdef int i
     491        for i from 0<i<n:
     492            q = singular_system("stest",q,d,A._degbound,A.__ngens,
     493                                     ring=A._current_ring)
     494            p *= q
     495        return FreeAlgebraElement_letterplace(A, p, check=False)
     496
     497    ## Groebner related stuff
     498    def reduce(self, G):
     499        """
     500        Reduce this element by a list of elements or by a
     501        twosided homogeneous ideal.
     502
     503        INPUT:
     504
     505        Either a list or tuple of homogeneous elements of the
     506        free algebra, or an ideal of the free algebra, or an
     507        ideal in the commutative polynomial ring that is
     508        currently used to implement the multiplication in the
     509        free algebra.
     510
     511        OUTPUT:
     512
     513        The twosided reduction of this element by the argument.
     514       
     515        NOTE:
     516           
     517        This may not be the normal form of this element, unless
     518        the argument is a twosided Groebner basis up to the degree
     519        of this element.
     520
     521        EXAMPLE::
     522
     523            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     524            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     525            sage: p = y^2*z*y^2+y*z*y*z*y
     526
     527        We compute the letterplace version of the Groebneer basis
     528        of `I` with degree bound 4::
     529
     530            sage: G = F._reductor_(I.groebner_basis(4).gens(),4)
     531            sage: G.ring() is F.current_ring()
     532            True
     533
     534        Since the element `p` is of degree 5, it is no surrprise
     535        that its reductions with respect to the original generators
     536        of `I` (of degree 2), or with respect to `G` (Groebner basis
     537        with degree bound 4), or with respect to the Groebner basis
     538        with degree bound 5 (which yields its normal form) are
     539        pairwise different::
     540
     541            sage: p.reduce(I)
     542            y*y*z*y*y + y*z*y*z*y
     543            sage: p.reduce(G)
     544            y*y*z*z*y + y*z*y*z*y - y*z*z*y*y + y*z*z*z*y
     545            sage: p.normal_form(I)
     546            y*y*z*z*z + y*z*y*z*z - y*z*z*y*z + y*z*z*z*z
     547            sage: p.reduce(I) != p.reduce(G) != p.normal_form(I) != p.reduce(I)
     548            True
     549
     550        """
     551        cdef FreeAlgebra_letterplace P = self._parent
     552        if not isinstance(G,(list,tuple)):
     553            if G==P:
     554                return P.zero_element()
     555            if not (isinstance(G,MPolynomialIdeal) and G.ring()==P._current_ring):
     556                G = G.gens()
     557        C = P.current_ring()
     558        cdef int selfdeg = self._poly.degree()
     559        if isinstance(G,MPolynomialIdeal):
     560            gI = G
     561        else:
     562            gI = P._reductor_(G,selfdeg) #C.ideal(g,coerce=False)
     563        from sage.libs.singular.option import LibSingularOptions
     564        libsingular_options = LibSingularOptions()
     565        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     566        libsingular_options['redTail'] = True
     567        libsingular_options['redSB'] = True
     568        poly = poly_reduce(C(self._poly),gI, ring=C,
     569                           attributes={gI:{"isSB":1}})
     570        libsingular_options['redTail'] = bck[0]
     571        libsingular_options['redSB'] = bck[1]
     572        return FreeAlgebraElement_letterplace(P,poly,check=False)
     573
     574    def normal_form(self,I):
     575        """
     576        Return the normal form of this element with respect to
     577        a twosided homogeneous ideal.
     578
     579        INPUT:
     580
     581        A twosided homogeneous ideal `I` of the parent `F` of
     582        this element, `x`.
     583
     584        OUTPUT:
     585
     586        The normal form of `x` wrt. `I`.
     587
     588        NOTE:
     589
     590        The normal form is computed by reduction with respect
     591        to a Groebnerbasis of `I` with degree bound `deg(x)`.
     592
     593        EXAMPLE::
     594
     595            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     596            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     597            sage: (x^5).normal_form(I)
     598            -y*z*z*z*x - y*z*z*z*y - y*z*z*z*z
     599
     600        We verify two basic properties of normal forms: The
     601        difference of an element and its normal form is contained
     602        in the ideal, and if two elements of the free algebra
     603        differ by an element of the ideal then they have the same
     604        normal form::
     605
     606            sage: x^5 - (x^5).normal_form(I) in I
     607            True
     608            sage: (x^5+x*I.0*y*z-3*z^2*I.1*y).normal_form(I) == (x^5).normal_form(I)
     609            True
     610
     611        """
     612        if self._parent != I.ring():
     613            raise ValueError, "Can not compute normal form wrt an ideal that does not belong to %s"%self._parent
     614        sdeg = self._poly.degree()
     615        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 object __monoid
     25    cdef public object __custom_name
     26    cdef str exponents_to_string(self, E)
  • 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 ticket #7797
     16
     17With this implementation, Groebner bases out to a degree bound and
     18normal forms can be computed for twosided homogeneous ideals of free
     19algebras. For now, all computations are restricted to the homogeneous
     20case.
     21
     22EXAMPLES::
     23
     24    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     25    sage: F
     26    Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     27    sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     28    sage: I
     29    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
     30    sage: x*(x*I.0-I.1*y+I.0*y)-I.1*y*z
     31    x*y*x*y + x*y*y*y - x*y*y*z + x*y*z*y + y*x*y*z + y*y*y*z
     32    sage: x^2*I.0-x*I.1*y+x*I.0*y-I.1*y*z in I
     33    True
     34
     35The preceding containment test is based on the computation of Groebner
     36bases with degree bound::
     37
     38    sage: I.groebner_basis(degbound=4)
     39    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
     40
     41When reducing an element by `I`, the original generators are chosen::
     42
     43    sage: (y*z*y*y).reduce(I)
     44    y*z*y*y
     45
     46However, there is a method for computing the normal form of an
     47element, which is the same as reduction by the Groebner basis out to
     48the degree of that element::
     49
     50    sage: (y*z*y*y).normal_form(I)
     51    y*z*y*z - y*z*z*y + y*z*z*z
     52    sage: (y*z*y*y).reduce(I.groebner_basis(4))
     53    y*z*y*z - y*z*z*y + y*z*z*z
     54
     55The default term order derives from the degree reverse lexicographic
     56order on the commutative version of the free algebra::
     57
     58    sage: F.commutative_ring().term_order()
     59    Degree reverse lexicographic term order
     60
     61A different term order can be chosen, and of course may yield a
     62different normal form::
     63
     64    sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace', order='lex')
     65    sage: L.commutative_ring().term_order()
     66    Lexicographic term order
     67    sage: J = L*[a*b+b*c,a^2+a*b-b*c-c^2]*L
     68    sage: J.groebner_basis(4)
     69    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
     70    sage: (b*c*b*b).normal_form(J)
     71    1/2*b*c*c*b - 1/2*c*c*b*b
     72
     73TEST::
     74
     75    sage: TestSuite(F).run()
     76    sage: TestSuite(L).run()
     77    sage: loads(dumps(F)) is F
     78    True
     79
     80TODO:
     81
     82In this first version of a wrapper for the letterplace algebra, we can
     83only support computations with homogeneous elements. Moreover, the
     84computation of Groebner bases only works for global term orderings,
     85and all generators have degree 1. These restrictions are inherited
     86from the letterplace implementation in Singular, but it is ongoing
     87work to lift these restrictions.
     88
     89We support coercion from the letterplace wrapper to the corresponding
     90generic implementation of a free algebra
     91(:class:`~sage.algebras.free_algebra.FreeAlgebra_generic`), but there
     92is no coercion the opposite direction, since the generic
     93implementation also comprises non-homogeneous elements.
     94
     95We also do not support coercion from a subalgebra, or between free
     96algebras with different term orderings, yet.
     97
     98"""
     99
     100from sage.all import PolynomialRing, prod
     101from sage.libs.singular.function import lib, singular_function
     102from sage.rings.polynomial.term_order import TermOrder
     103from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
     104from sage.categories.algebras import Algebras
     105from sage.rings.noncommutative_ideals import IdealMonoid_nc
     106
     107#####################
     108# Define some singular functions
     109lib("freegb.lib")
     110poly_reduce = singular_function("NF")
     111singular_system=singular_function("system")
     112
     113# unfortunately we can not set Singular attributes for MPolynomialRing_libsingular
     114# Hence, we must constantly work around Letterplace's sanity checks,
     115# and can not use the following library functions:
     116#set_letterplace_attributes = singular_function("setLetterplaceAttributes")
     117#lpMult = singular_function("lpMult")
     118
     119#####################
     120# Auxiliar functions
     121
     122cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks):
     123    """
     124    Create a polynomial ring in block order.
     125
     126    INPUT:
     127
     128    - ``base_ring``: A multivariate polynomial ring.
     129    - ``blocks``: The number of blocks to be formed.
     130
     131    OUTPUT:
     132
     133    A multivariate polynomial ring in block order, all blocks
     134    isomorphic (as ordered rings) with the given ring, and the
     135    variable names of the `n`-th block (`n>0`) ending with
     136    ``"_%d"%n``.
     137
     138    TEST:
     139
     140    Note that, since the algebras are cached, we need to choose
     141    a different base ring, since other doctests could have a
     142    side effect on the atteined degree bound::
     143
     144        sage: F.<x,y,z> = FreeAlgebra(GF(17), implementation='letterplace')
     145        sage: L.<a,b,c> = FreeAlgebra(GF(17), implementation='letterplace', order='lex')
     146        sage: F.set_degbound(4)
     147        sage: F.current_ring()  # indirect doctest
     148        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
     149        sage: F.current_ring().term_order()
     150        degrevlex(3),degrevlex(3),degrevlex(3),degrevlex(3) term order
     151        sage: L.set_degbound(2)
     152        sage: L.current_ring().term_order()
     153        lex(3),lex(3) term order
     154
     155    """
     156    n = base_ring.ngens()
     157    T0 = base_ring.term_order()
     158    T = T0
     159    cdef i
     160    cdef tuple names0 = base_ring.variable_names()
     161    cdef list names = list(names0)
     162    for i from 1<=i<blocks:
     163        T += T0
     164        names.extend([x+'_'+str(i) for x in names0])
     165    return PolynomialRing(base_ring.base_ring(),len(names),names,order=T)
     166
     167#####################
     168# The free algebra
     169
     170cdef class FreeAlgebra_letterplace(Algebra):
     171    """
     172    Finitely generated free algebra, with arithmetic restricted to homogeneous elements.
     173
     174    NOTE:
     175
     176    The restriction to homogeneous elements should be lifted as soon
     177    as it is lifted in Singular's "Letterplace algebras".
     178
     179    EXAMPLE::
     180
     181        sage: K.<z> = GF(25)
     182        sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     183        sage: F
     184        Free Associative Unital Algebra on 3 generators (a, b, c) over Finite Field in z of size 5^2
     185        sage: P = F.commutative_ring()
     186        sage: P
     187        Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2
     188
     189    We can do arithmetic as usual, as long as we stay homogeneous::
     190
     191        sage: (z*a+(z+1)*b+2*c)^2
     192        (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
     193        sage: a+1
     194        Traceback (most recent call last):
     195        ...
     196        ArithmeticError: Can only add elements of the same degree
     197
     198    """
     199    # It is not really a free algebra over the given generators. Rather,
     200    # it is a free algebra over the commutative monoid generated by the given generators.
     201    def __init__(self, R):
     202        """
     203        INPUT:
     204
     205        A multivariate polynomial ring of type :class:`~sage.rings.polynomial.multipolynomial_libsingular.MPolynomialRing_libsingular`.
     206
     207        OUTPUT:
     208
     209        The free associative version of the given commutative ring.
     210
     211        NOTE:
     212
     213        One is supposed to use the `FreeAlgebra` constructor, in order to use the cache.
     214
     215        TEST::
     216
     217            sage: from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     218            sage: FreeAlgebra_letterplace(QQ['x','y'])
     219            Free Associative Unital Algebra on 2 generators (x, y) over Rational Field
     220            sage: FreeAlgebra_letterplace(QQ['x'])
     221            Traceback (most recent call last):
     222            ...
     223            TypeError: A letterplace algebra must be provided by a polynomial ring of type <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomialRing_libsingular'>
     224
     225        ::
     226
     227            sage: K.<z> = GF(25)
     228            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     229            sage: TestSuite(F).run(verbose=True)
     230            running ._test_additive_associativity() . . . pass
     231            running ._test_an_element() . . . pass
     232            running ._test_associativity() . . . pass
     233            running ._test_category() . . . pass
     234            running ._test_distributivity() . . . pass
     235            running ._test_elements() . . .
     236              Running the test suite of self.an_element()
     237              running ._test_category() . . . pass
     238              running ._test_eq() . . . pass
     239              running ._test_not_implemented_methods() . . . pass
     240              running ._test_pickling() . . . pass
     241              pass
     242            running ._test_elements_eq() . . . pass
     243            running ._test_eq() . . . pass
     244            running ._test_not_implemented_methods() . . . pass
     245            running ._test_one() . . . pass
     246            running ._test_pickling() . . . pass
     247            running ._test_prod() . . . pass
     248            running ._test_some_elements() . . . pass
     249            running ._test_zero() . . . pass
     250
     251        """
     252        if not isinstance(R,MPolynomialRing_libsingular):
     253            raise TypeError, "A letterplace algebra must be provided by a polynomial ring of type %s"%MPolynomialRing_libsingular
     254        self.__ngens = R.ngens()
     255        base_ring = R.base_ring()
     256        Algebra.__init__(self, base_ring, R.variable_names(),
     257                         normalize=False, category=Algebras(base_ring))
     258        self._commutative_ring = R
     259        self._current_ring = make_letterplace_ring(R,1)
     260        self._degbound = 1
     261        self._populate_coercion_lists_(coerce_list=[base_ring])
     262    def __reduce__(self):
     263        """
     264        TEST::
     265
     266            sage: K.<z> = GF(25)
     267            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     268            sage: loads(dumps(F)) is F    # indirect doctest
     269            True
     270
     271        """
     272        from sage.algebras.free_algebra import FreeAlgebra
     273        return FreeAlgebra,(self._commutative_ring,)
     274    # Small methods
     275    def ngens(self):
     276        """
     277        Return the number of generators.
     278
     279        EXAMPLE::
     280
     281            sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
     282            sage: F.ngens()
     283            3
     284
     285        """
     286        return self.__ngens
     287    def gen(self,i):
     288        """
     289        Return the `i`-th generator.
     290
     291        INPUT:
     292
     293        `i` -- an integer.
     294
     295        OUTPUT:
     296
     297        Generator number `i`.
     298
     299        EXAMPLE::
     300
     301            sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
     302            sage: F.1 is F.1  # indirect doctest
     303            True
     304            sage: F.gen(2)
     305            c
     306
     307        """
     308        if i>=self.__ngens:
     309            raise ValueError, "This free algebra only has %d generators"%self.__ngens
     310        if self._gens is not None:
     311            return self._gens[i]
     312        return FreeAlgebraElement_letterplace(self, self._current_ring.gen(i))
     313    def current_ring(self):
     314        """
     315        Return the commutative ring that is used to emulate
     316        the non-commutative multiplication out to the current degree.
     317
     318        EXAMPLE::
     319
     320            sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
     321            sage: F.current_ring()
     322            Multivariate Polynomial Ring in a, b, c over Rational Field
     323            sage: a*b
     324            a*b
     325            sage: F.current_ring()
     326            Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1 over Rational Field
     327            sage: F.set_degbound(3)
     328            sage: F.current_ring()
     329            Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1, a_2, b_2, c_2 over Rational Field
     330
     331        """
     332        return self._current_ring
     333    def commutative_ring(self):
     334        """
     335        Return the commutative version of this free algebra.
     336
     337        NOTE:
     338
     339        This commutative ring is used as a unique key of the free algebra.
     340
     341        EXAMPLE::
     342
     343            sage: K.<z> = GF(25)
     344            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     345            sage: F
     346            Free Associative Unital Algebra on 3 generators (a, b, c) over Finite Field in z of size 5^2
     347            sage: F.commutative_ring()
     348            Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2
     349            sage: FreeAlgebra(F.commutative_ring()) is F
     350            True
     351
     352        """
     353        return self._commutative_ring
     354    def term_order_of_block(self):
     355        """
     356        Return the term order that is used for the commutative version of this free algebra.
     357
     358        EXAMPLE::
     359
     360            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     361            sage: F.term_order_of_block()
     362            Degree reverse lexicographic term order
     363            sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace',order='lex')
     364            sage: L.term_order_of_block()
     365            Lexicographic term order
     366
     367        """
     368        return self._commutative_ring.term_order()
     369
     370    # Some basic properties of this ring
     371    def is_commutative(self):
     372        """
     373        Tell whether this algebra is commutative, i.e., whether the generator number is one.
     374
     375        EXAMPLE::
     376
     377            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     378            sage: F.is_commutative()
     379            False
     380            sage: FreeAlgebra(QQ, implementation='letterplace', names=['x']).is_commutative()
     381            True
     382
     383        """
     384        return self.__ngens <= 1
     385
     386    def is_field(self):
     387        """
     388        Tell whether this free algebra is a field.
     389
     390        NOTE:
     391
     392        This would only be the case in the degenerate case of no generators.
     393        But such an example can not be constructed in this implementation.
     394
     395        TEST::
     396
     397            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     398            sage: F.is_field()
     399            False
     400
     401        """
     402        return (not self.__ngens) and self._base.is_field()
     403
     404    def _repr_(self):
     405        """
     406        EXAMPLE::
     407
     408            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     409            sage: F     # indirect doctest
     410            Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     411
     412        """
     413        return "Free Associative Unital Algebra on %d generators %s over %s"%(self.__ngens,self.gens(),self._base)
     414    def degbound(self):
     415        """
     416        Return the degree bound that is currently used.
     417
     418        NOTE:
     419
     420        When multiplying two elements of this free algebra, the degree
     421        bound will be dynamically adapted. It can also be set by
     422        :meth:`set_degbound`.
     423
     424        EXAMPLE:
     425
     426        In order to avoid we get a free algebras from the cache that
     427        was created in another doctest and has a different degree
     428        bound, we choose a base ring that does not appear in other tests::
     429
     430            sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
     431            sage: F.degbound()
     432            1
     433            sage: x*y
     434            x*y
     435            sage: F.degbound()
     436            2
     437            sage: F.set_degbound(4)
     438            sage: F.degbound()
     439            4
     440
     441        """
     442        return self._degbound
     443    def set_degbound(self,d):
     444        """
     445        Increase the degree bound that is currently in place.
     446
     447        NOTE:
     448
     449        The degree bound can not be decreased.
     450
     451        EXAMPLE:
     452
     453        In order to avoid we get a free algebras from the cache that
     454        was created in another doctest and has a different degree
     455        bound, we choose a base ring that does not appear in other tests::
     456
     457            sage: F.<x,y,z> = FreeAlgebra(GF(251), implementation='letterplace')
     458            sage: F.degbound()
     459            1
     460            sage: x*y
     461            x*y
     462            sage: F.degbound()
     463            2
     464            sage: F.set_degbound(4)
     465            sage: F.degbound()
     466            4
     467            sage: F.set_degbound(2)
     468            sage: F.degbound()
     469            4         
     470
     471        """
     472        if d<=self._degbound:
     473            return
     474        self._degbound = d
     475        self._current_ring = make_letterplace_ring(self._commutative_ring,d)
     476
     477#    def base_extend(self, R):
     478#        if self._base.has_coerce_map_from(R):
     479#            return self
     480
     481    ################################################
     482    ## Ideals
     483
     484    def _ideal_class_(self, n=0):
     485        """
     486        Return the class :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`.
     487
     488        EXAMPLE::
     489
     490            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     491            sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*F
     492            sage: I
     493            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
     494            sage: type(I) is F._ideal_class_()
     495            True
     496
     497        """
     498        from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
     499        return LetterplaceIdeal
     500
     501    def ideal_monoid(self):
     502        """
     503        Return the monoid of ideals of this free algebra.
     504
     505        EXAMPLE::
     506
     507            sage: F.<x,y> = FreeAlgebra(GF(2), implementation='letterplace')
     508            sage: F.ideal_monoid()
     509            Monoid of ideals of Free Associative Unital Algebra on 2 generators (x, y) over Finite Field of size 2
     510            sage: F.ideal_monoid() is F.ideal_monoid()
     511            True
     512
     513        """
     514        if self.__monoid is None:
     515            self.__monoid = IdealMonoid_nc(self)
     516        return self.__monoid
     517
     518    # Auxiliar methods
     519    cdef str exponents_to_string(self, E):
     520        """
     521        This auxiliary method is used for the string representation of elements of this free algebra.
     522
     523        EXAMPLE::
     524
     525            sage: F.<x,y> = FreeAlgebra(GF(2), implementation='letterplace')
     526            sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace
     527            sage: P = F.commutative_ring()
     528            sage: FreeAlgebraElement_letterplace(F, P.0*P.1^2+P.1^3)   # indirect doctest
     529            x.y^2 + y^3
     530
     531        """
     532        cdef int ngens = self.__ngens
     533        cdef int nblocks = len(E)/ngens
     534        cdef int i
     535        cdef list out = []
     536        for i from 0<=i<nblocks:
     537            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])
     538            if s:
     539                out.append(s)
     540        return '*'.join(out)
     541
     542    def _reductor_(self, g, d):
     543        """
     544        Return a commutative ideal that can be used to compute the normal
     545        form of a free algebra element of a given degree.
     546
     547        INPUT:
     548
     549        ``g`` - a list of elements of this free algebra.
     550        ``d`` - an integer.
     551
     552        OUTPUT:
     553
     554        An ideal such that reduction of a letterplace polynomial by that ideal corresponds
     555        to reduction of an element of degree at most ``d`` by ``g``.
     556
     557        EXAMPLE::
     558
     559            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     560            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     561            sage: p = y*x*y + y*y*y + y*z*y - y*z*z
     562            sage: p.reduce(I)
     563            y*y*y - y*y*z + y*z*y - y*z*z
     564            sage: G = F._reductor_(I.gens(),3); G
     565            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, x_3, y_3, z_3 over Rational Field
     566
     567        We do not use the usual reduction method for polynomials in
     568        Sage, since it does the reductions in a different order
     569        compared to Singular. Therefore, we call the original Singular
     570        reduction method, and prevent a warning message by asserting
     571        that `G` is a Groebner basis.
     572
     573            sage: from sage.libs.singular.function import singular_function
     574            sage: poly_reduce = singular_function("NF")
     575            sage: q = poly_reduce(p.letterplace_polynomial(), G, ring=F.current_ring(), attributes={G:{"isSB":1}}); q
     576            y*y_1*y_2 - y*y_1*z_2 + y*z_1*y_2 - y*z_1*z_2
     577            sage: p.reduce(I).letterplace_polynomial() == q
     578            True
     579
     580        """
     581        cdef list out = []
     582        C = self.current_ring()
     583        cdef FreeAlgebraElement_letterplace x
     584        ngens = self.__ngens
     585        degbound = self._degbound
     586        cdef list G = [C(x._poly) for x in g]
     587        for y in G:
     588            out.extend([y]+[singular_system("stest",y,n+1,degbound,ngens,ring=C) for n in xrange(d-y.degree())])
     589        return C.ideal(out)
     590
     591    ###########################
     592    ## Coercion
     593    def _coerce_map_from_(self,S):
     594        """
     595        There is only a coercion from the base ring.
     596
     597        TEST::
     598
     599            sage: F.<x,y,z> = FreeAlgebra(GF(5), implementation='letterplace')
     600            sage: 5 == F.zero()    # indirect doctest
     601            True
     602
     603        """
     604        return self==S or self._current_ring.has_coerce_map_from(S)
     605
     606    def _an_element_(self):
     607        """
     608        Return an element.
     609
     610        EXAMPLE::
     611
     612            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     613            sage: F.an_element()   # indirect doctest
     614            x
     615
     616        """
     617        return FreeAlgebraElement_letterplace(self, self._current_ring.an_element(), check=False)
     618
     619#    def random_element(self, degree=2, terms=5):
     620#        """
     621#        Return a random element of a given degree and with a given number of terms.
     622#
     623#        INPUT:
     624#
     625#        - ``degree`` -- the maximal degree of the output (default 2).
     626#        - ``terms`` -- the maximal number of terms of the output (default 5).
     627#
     628#        NOTE:
     629#
     630#        This method is currently not useful at all.
     631#
     632#        Not tested.
     633#        """
     634#        self.set_degbound(degree)
     635#        while(1):
     636#            p = self._current_ring.random_element(degree=degree,terms=terms)
     637#            if p.is_homogeneous():
     638#                break
     639#        return FreeAlgebraElement_letterplace(self, p, check=False)
     640
     641    def _from_dict_(self, D, check=True):
     642        """
     643        Create an element from a dictionary.
     644
     645        INPUT:
     646
     647        - A dictionary. Keys: tuples of exponents. Values:
     648          The coefficients of the corresponding monomial
     649          in the to-be-created element.
     650        - ``check`` (optional bool, default ``True``):
     651          This is forwarded to the initialisation of
     652          :class:`~sage.algebas.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace`.
     653
     654        TEST:
     655
     656        This method applied to the dictionary of any element must
     657        return the same element. This must hold true even if the
     658        underlying letterplace ring has been extended in the meantime.
     659        ::
     660
     661            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     662            sage: p = 3*x*y+2*z^2
     663            sage: F.set_degbound(10)
     664            sage: p == F._from_dict_(dict(p))
     665            True
     666
     667        For the empty dictionary, zero is returned::
     668
     669            sage: F._from_dict_({})
     670            0
     671
     672        """
     673        if not D:
     674            return self.zero_element()
     675        cdef int l
     676        for e in D.iterkeys():
     677            l = len(e)
     678            break
     679        cdef dict out = {}
     680        self.set_degbound(l/self.__ngens)
     681        cdef int n = self._current_ring.ngens()
     682        for e,c in D.iteritems():
     683            out[tuple(e)+(0,)*(n-l)] = c
     684        return FreeAlgebraElement_letterplace(self,self._current_ring(out),
     685                                              check=check)
     686
     687    def _element_constructor_(self, x):
     688        """
     689        Return an element of this free algebra.
     690
     691        INPUT:
     692
     693        Something that can be interpreted in the polynomial ring that
     694        is used to implement the letterplace algebra out to the current
     695        degree bound, or a string that can be interpreted as an expression
     696        in the algebra (provided that the coefficients are numerical).
     697
     698        EXAMPLE::
     699
     700            sage: F.<x,y,z> = FreeAlgebra(GF(5), implementation='letterplace')
     701            sage: F(3)
     702            3
     703            sage: F('x*z+3*y^2')
     704            x*z + (3)*y*y
     705            sage: F.set_degbound(3)
     706            sage: P = F.current_ring()
     707            sage: F(P.0*P.4*P.6-2*P.2*P.5*P.8)   # indirect doctest
     708            x*y*x + (3)*z*z*z
     709
     710        """
     711        if isinstance(x, basestring):
     712            from sage.all import sage_eval
     713            return sage_eval(x,locals=self.gens_dict())
     714        try:
     715            P = x.parent()
     716        except AttributeError:
     717            P = None
     718        if P is self:
     719            (<FreeAlgebraElement_letterplace>x)._poly = self._current_ring((<FreeAlgebraElement_letterplace>x)._poly)
     720            return x
     721        return FreeAlgebraElement_letterplace(self,self._current_ring(x))
     722
     723   
  • 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 ticket #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    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 homogeneous elements of the
     60    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    """
     112    def __init__(self, ring, gens, coerce=True, side = "twosided"):
     113        """
     114        INPUT:
     115
     116        - ``ring``: A free algebra in letterplace implementation.
     117        - ``gens``: List, tuple or sequence of generators.
     118        - ``coerce`` (optional bool, default ``True``):
     119          Shall ``gens`` be coerced first?
     120        - ``side``: optional string, one of ``"twosided"`` (default),
     121          ``"left"`` or ``"right"``. Determines whether the ideal
     122          is a left, right or twosided ideal. Groebner bases or
     123          only supported in the twosided case.
     124
     125        TEST::
     126
     127            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     128            sage: from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
     129            sage: LetterplaceIdeal(F,x)
     130            Twosided Ideal (x) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     131            sage: LetterplaceIdeal(F,[x,y],side='left')
     132            Left Ideal (x, y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     133
     134        It is not correctly detected that this class inherits from an
     135        extension class. Therefore, we have to skip one item of the
     136        test suite::
     137
     138            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     139            sage: TestSuite(I).run(skip=['_test_category'],verbose=True)
     140            running ._test_eq() . . . pass
     141            running ._test_not_implemented_methods() . . . pass
     142            running ._test_pickling() . . . pass
     143
     144        """
     145        Ideal_nc.__init__(self, ring, gens, coerce=coerce, side=side)
     146        self.__GB = self
     147        self.__uptodeg = 0
     148    def groebner_basis(self, degbound=None):
     149        """
     150        Twosided Groebner basis with degree bound.
     151
     152        INPUT:
     153
     154        - ``degbound`` (optional integer, or Infinity): If it is provided,
     155          a Groebner basis at least out to that degree is returned. By
     156          default, the current degree bound of the underlying ring is used.
     157
     158        ASSUMPTIONS:
     159
     160        Currently, we can only compute Groebner bases for twosided
     161        ideals, and the ring of coefficients must be a field. A
     162        `TypeError` is raised if one of these conditions is violated.
     163
     164        NOTES:
     165
     166        - The result is cached. The same Groebner basis is returned
     167          if a smaller degree bound than the known one is requested.
     168        - If the degree bound Infinity is requested, it is attempted to
     169          compute a complete Groebner basis. But we can not guarantee
     170          that the computation will terminate, since not all twosided
     171          homogeneous ideals of a free algebra have a finite Groebner
     172          basis.
     173
     174        EXAMPLES::
     175
     176            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     177            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     178
     179        Since `F` was cached and since its degree bound can not be
     180        decreased, it may happen that, as a side effect of other tests,
     181        it already has a degree bound bigger than 3. So, we can not
     182        test against the output of ``I.groebner_basis()``::
     183
     184            sage: F.set_degbound(3)
     185            sage: I.groebner_basis()   # not tested
     186            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
     187            sage: I.groebner_basis(4)
     188            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
     189            sage: I.groebner_basis(2) is I.groebner_basis(4)
     190            True
     191            sage: G = I.groebner_basis(4)
     192            sage: G.groebner_basis(3) is G
     193            True
     194
     195        If a finite complete Groebner basis exists, we can compute
     196        it as follows::
     197
     198            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
     199            sage: I.groebner_basis(Infinity)
     200            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
     201
     202        Since the commutators of the generators are contained in the ideal,
     203        we can verify the above result by a computation in a polynomial ring
     204        in negative lexicographic order::
     205
     206            sage: P.<c,b,a> = PolynomialRing(QQ,order='neglex')
     207            sage: J = P*[a^2*b-c^3,a*b^2+c*a^2]
     208            sage: J.groebner_basis()
     209            [b*a^2 - c^3, b^2*a + c*a^2, c*a^3 + c^3*b, c^3*b^2 + c^4*a]
     210
     211        Aparently, the results are compatible, by sending `a` to `x`, `b`
     212        to `y` and `c` to `z`.
     213
     214        """
     215        cdef FreeAlgebra_letterplace A = self.ring()
     216        cdef FreeAlgebraElement_letterplace x
     217        if degbound is None:
     218            degbound = A.degbound()
     219        if self.__uptodeg >= degbound:
     220            return self.__GB
     221        if not A.base().is_field():
     222            raise TypeError, "Currently, we can only compute Groebner bases if the ring of coefficients is a field"
     223        if self.side()!='twosided':
     224            raise TypeError, "This ideal is not two-sided. We can only compute two-sided Groebner bases"
     225        if degbound == Infinity:
     226            while self.__uptodeg<Infinity:
     227                test_bound = 2*max([x._poly.degree() for x in self.__GB.gens()])
     228                self.groebner_basis(test_bound)
     229            return self.__GB
     230        # Set the options required by letterplace
     231        from sage.libs.singular.option import LibSingularOptions
     232        libsingular_options = LibSingularOptions()
     233        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     234        libsingular_options['redTail'] = True
     235        libsingular_options['redSB'] = True
     236        A.set_degbound(degbound)
     237        P = A.current_ring()
     238        out = [FreeAlgebraElement_letterplace(A,X,check=False) for X in
     239               singular_system("freegb",P.ideal([x._poly for x in self.__GB.gens()]),
     240                               degbound,A.ngens(), ring = P)]
     241        libsingular_options['redTail'] = bck[0]
     242        libsingular_options['redSB'] = bck[1]
     243        self.__GB = A.ideal(out,side='twosided',coerce=False)
     244        if degbound >= 2*max([x._poly.degree() for x in out]):
     245            degbound = Infinity
     246        self.__uptodeg = degbound
     247        self.__GB.__uptodeg = degbound
     248        return self.__GB
     249
     250    def __contains__(self,x):
     251        """
     252        The containment test is based on a normal form computation.
     253
     254        EXAMPLES::
     255
     256            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     257            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     258            sage: x*I.0-I.1*y+I.0*y in I    # indirect doctest
     259            True
     260            sage: 1 in I
     261            False
     262
     263        """
     264        R = self.ring()
     265        return (x in R) and R(x).normal_form(self).is_zero()
     266
     267    def reduce(self, G):
     268        """
     269        Reduction of this ideal by another ideal,
     270        or normal form of an algebra element with respect to this ideal.
     271
     272        INPUT:
     273
     274        - ``G``: A list or tuple of elements, an ideal,
     275          the ambient algebra, or a single element.
     276
     277        OUTPUT:
     278
     279        - The normal form of ``G`` with respect to this ideal, if
     280          ``G`` is an element of the algebra.
     281        - The reduction of this ideal by the elements resp. generators
     282          of ``G``, if ``G`` is a list, tuple or ideal.
     283        - The zero ideal, if ``G`` is the algebra containing this ideal.
     284
     285        EXAMPLES::
     286
     287            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     288            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     289            sage: I.reduce(F)
     290            Twosided Ideal (0) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     291            sage: I.reduce(x^3)
     292            -y*z*x - y*z*y - y*z*z
     293            sage: I.reduce([x*y])
     294            Twosided Ideal (y*z, x*x - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     295            sage: I.reduce(F*[x^2+x*y,y^2+y*z]*F)
     296            Twosided Ideal (x*y + y*z, -y*x + y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
     297
     298        """
     299        P = self.ring()
     300        if not isinstance(G,(list,tuple)):
     301            if G==P:
     302                return P.ideal([P.zero_element()])
     303            if G in P:
     304                return G.normal_form(self)
     305            G = G.gens()
     306        C = P.current_ring()
     307        sI = C.ideal([C(X.letterplace_polynomial()) for X in self.gens()], coerce=False)
     308        selfdeg = max([x.degree() for x in sI.gens()])
     309        gI = P._reductor_(G, selfdeg)
     310        from sage.libs.singular.option import LibSingularOptions
     311        libsingular_options = LibSingularOptions()
     312        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     313        libsingular_options['redTail'] = True
     314        libsingular_options['redSB'] = True
     315        sI = poly_reduce(sI,gI, ring=C, attributes={gI:{"isSB":1}})
     316        libsingular_options['redTail'] = bck[0]
     317        libsingular_options['redSB'] = bck[1]
     318        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  
    9999            rings also inherits from the base class of
    100100            rings. Therefore, we implemented a ``__mul__``
    101101            method for parents, that calls a ``_mul_``
    102             method implemented here. See trac ticket #11068.
     102            method implemented here. See trac ticket #7797.
    103103
    104104            INPUT:
    105105
     
    181181            The code is copied from the base class of rings.
    182182            This is since there are rings that do not inherit
    183183            from that class, such as matrix algebras.  See
    184             trac ticket #11068.
     184            trac ticket #7797.
    185185
    186186            EXAMPLE::
    187187
     
    217217            :class:`~sage.rings.ring.Ring`. This is
    218218            because there are rings that do not inherit
    219219            from that class, such as matrix algebras.
    220             See trac ticket #11068.
     220            See trac ticket #7797.
    221221
    222222            INPUT:
    223223
     
    389389            - ``names``: a list of strings to be used as names
    390390              for the variables in the quotient ring.
    391391
    392             EXAMPLES::
     392            EXAMPLES:
    393393
    394                 sage: F.<x,y,z> = FreeAlgebra(QQ, 3)
    395                 sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     394            Usually, a ring inherits a method :meth:`sage.rings.ring.Ring.quotient`.
     395            So, we need a bit of effort to make the following example work with the
     396            category framework::
     397
     398                sage: F.<x,y,z> = FreeAlgebra(QQ)
     399                sage: from sage.rings.noncommutative_ideals import Ideal_nc
     400                sage: class PowerIdeal(Ideal_nc):
     401                ...    def __init__(self, R, n):
     402                ...        self._power = n
     403                ...        self._power = n
     404                ...        Ideal_nc.__init__(self,R,[R.prod(m) for m in CartesianProduct(*[R.gens()]*n)])
     405                ...    def reduce(self,x):
     406                ...        R = self.ring()
     407                ...        return add([c*R(m) for c,m in x if len(m)<self._power],R(0))
     408                ...
     409                sage: I = PowerIdeal(F,3)
    396410                sage: Q = Rings().parent_class.quotient(F,I); Q
    397                 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)
     411                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)
    398412                sage: Q.0
    399413                xbar
    400414                sage: Q.1
    401415                ybar
    402416                sage: Q.2
    403417                zbar
     418                sage: Q.0*Q.1
     419                xbar*ybar
     420                sage: Q.0*Q.1*Q.0
     421                0
    404422
    405423            """
    406424            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 ticket #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###########################################################################
     
    208223        sage: R.quotient(I)
    209224        Ring of integers modulo 2
    210225
     226    Here is an example of the quotient of a free algebra by a
     227    twosided homogeneous ideal (see trac ticket #7797)::
     228
     229        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     230        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     231        sage: Q.<a,b,c> = F.quo(I); Q
     232        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)
     233        sage: a*b
     234        -b*c
     235        sage: a^3
     236        -b*c*a - b*c*b - b*c*c
     237        sage: J = Q*[a^3-b^3]*Q
     238        sage: R.<i,j,k> = Q.quo(J); R
     239        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)
     240        sage: i^3
     241        -j*k*i - j*k*j - j*k*k
     242        sage: j^3
     243        -j*k*i - j*k*j - j*k*k
     244
    211245    """
    212246    # 1. Not all rings inherit from the base class of rings.
    213247    # 2. We want to support quotients of free algebras by homogeneous two-sided ideals.
     
    276310        sage: is_QuotientRing(R)
    277311        False
    278312
     313    ::
     314
     315        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     316        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     317        sage: Q = F.quo(I)
     318        sage: is_QuotientRing(Q)
     319        True
     320        sage: is_QuotientRing(F)
     321        False
     322
    279323    """
    280324    return isinstance(x, QuotientRing_nc)
    281325   
     
    283327    """
    284328    The quotient ring of a ring `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::
     
    303369        sage: S(0) == a^2 + b^2
    304370        True
    305371   
    306     A quotient of a quotient is just the quotient of the original top
     372    Again, a quotient of a quotient is just the quotient of the original top
    307373    ring by the sum of two ideals.
    308374   
    309375    ::
     
    327393        -  ``I`` - a twosided ideal of `R`.
    328394        - ``names`` - a list of generator names.
    329395       
     396       
     397        EXAMPLES::
     398       
     399            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     400            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     401            sage: Q.<a,b,c> = F.quo(I); Q
     402            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)
     403            sage: a*b
     404            -b*c
     405            sage: a^3
     406            -b*c*a - b*c*b - b*c*c
     407
    330408        """
    331409        from sage.categories.rings import Rings
    332410        if R not in Rings():
     
    353431            sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
    354432            sage: R.quotient_ring(I).construction()
    355433            (QuotientFunctor, Univariate Polynomial Ring in x over Integer Ring)
     434            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     435            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     436            sage: Q = F.quo(I)
     437            sage: Q.construction()
     438            (QuotientFunctor, Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field)
    356439
    357440        TESTS::
    358441       
     
    410493
    411494        AUTHOR:
    412495
    413         - Simon King (2011-03-23): See trac ticket #11068.
     496        - Simon King (2011-03-23): See trac ticket #7797.
    414497
    415498        EXAMPLES:
    416499
     
    420503            sage: P.quo(P.random_element()).is_commutative()
    421504            True
    422505
    423         The non-commutative case is more interesting, but it
    424         will only be available once trac ticket #7797 is merged.
     506        The non-commutative case is more interesting::
     507
     508            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     509            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     510            sage: Q = F.quo(I)
     511            sage: Q.is_commutative()
     512            False
     513            sage: Q.1*Q.2==Q.2*Q.1
     514            False
     515
     516        In the next example, the generators apparently commute::
     517
     518            sage: J = F*[x*y-y*x,x*z-z*x,y*z-z*y,x^3-y^3]*F
     519            sage: R = F.quo(J)
     520            sage: R.is_commutative()
     521            True
     522           
    425523
    426524        """
    427525        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  
    521521            0
    522522            sage: a.__cmp__(b)
    523523            1
     524
     525        See trac ticket #7797:
     526
     527            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     528            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     529            sage: Q = F.quo(I)
     530            sage: Q.0^4    # indirect doctest
     531            ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar
     532
    524533        """
    525534        #if self.__rep == other.__rep or ((self.__rep - other.__rep) in self.parent().defining_ideal()):
    526535        #    return 0
  • sage/rings/ring.pyx

    diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx
    a b  
    385385            sage: Q = sage.rings.ring.Ring.quotient(F,I)
    386386            sage: Q.ideal_monoid()
    387387            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)
     388            sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
     389            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     390            sage: Q = F.quo(I)
     391            sage: Q.ideal_monoid()
     392            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)
    388393
    389394        """
    390395        if self.__ideal_monoid is not None:
     
    512517            sage: (x+y,z+y^3)*R
    513518            Ideal (x + y, y^3 + z) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 7
    514519
    515         The following was implemented in trac ticket #11068::
     520        The following was implemented in trac ticket #7797::
    516521
    517522            sage: A = SteenrodAlgebra(2)
    518523            sage: A*[A.1+A.2,A.1^2]
     
    585590            sage: RR._ideal_class_()
    586591            <class 'sage.rings.ideal.Ideal_pid'>
    587592
    588         Since #11068, non-commutative rings have ideals as well::
     593        Since #7797, non-commutative rings have ideals as well::
    589594
    590595            sage: A = SteenrodAlgebra(2)
    591596            sage: A._ideal_class_()
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    10141014        is because ``__mul__`` can not be implemented via inheritance
    10151015        from the parent methods of the category, but ``_mul_`` can
    10161016        be inherited. This is, e.g., used when creating twosided
    1017         ideals of matrix algebras. See trac ticket #11068.
     1017        ideals of matrix algebras. See trac ticket #7797.
    10181018
    10191019        EXAMPLE::
    10201020
     1021            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
    10211022            sage: MS = MatrixSpace(QQ,2,2)
    10221023
    10231024        This matrix space is in fact an algebra, and in particular
  • setup.py

    diff --git a/setup.py b/setup.py
    a b  
    921921      packages    = ['sage',
    922922                     
    923923                     'sage.algebras',
     924                     'sage.algebras.letterplace',
    924925                     'sage.algebras.quatalg',
    925926                     'sage.algebras.steenrod',
    926927