Ticket #7797: trac7797-full_letterplace_wrapper.patch

File trac7797-full_letterplace_wrapper.patch, 147.3 KB (added by SimonKing, 8 years ago)

A full wrapper for Singular's letterplace functionality, plus non-commutative ideals and ring quotients; rebased on top of 10961

  • doc/en/reference/algebras.rst

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1300979918 -3600
    # Node ID 5933de4b2acdbfb9e65cde1f3bef137ac648bc8f
    # Parent 299e7cbedfbfca1052c2bc68f3db2f7978b50135
    #7797: Wrapper for Letterplace
    Non-commutative ideals,
    truncated Groebner bases for homogeneous ideals of free algebras,
    non-commutative quotient rings.
    
    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
    1519   sage/algebras/steenrod_algebra
    1620   sage/algebras/steenrod_algebra_element
    17    sage/algebras/steenrod_algebra_bases
    18  No newline at end of file
     21   sage/algebras/steenrod_algebra_bases
  • doc/en/reference/rings.rst

    diff --git a/doc/en/reference/rings.rst b/doc/en/reference/rings.rst
    a b  
    99   sage/rings/ring
    1010   sage/rings/ideal
    1111   sage/rings/ideal_monoid
     12   sage/rings/noncommutative_ideals
    1213   sage/rings/morphism
    1314   sage/rings/homset
    1415   sage/rings/infinity
  • module_list.py

    diff --git a/module_list.py b/module_list.py
    a b  
    127127               include_dirs = [SAGE_ROOT+'/local/include/FLINT/'],
    128128               depends = flint_depends),
    129129
     130    Extension('sage.algebras.letterplace.free_algebra_letterplace',
     131              sources = ['sage/algebras/letterplace/free_algebra_letterplace.pyx'],
     132              language="c++",
     133              include_dirs = [SAGE_ROOT +'/local/include/singular'],
     134              depends = singular_depends),
     135
     136    Extension('sage.algebras.letterplace.free_algebra_element_letterplace',
     137              sources = ['sage/algebras/letterplace/free_algebra_element_letterplace.pyx'],
     138              language="c++",
     139              include_dirs = [SAGE_ROOT +'/local/include/singular'],
     140              depends = singular_depends),
     141
     142    Extension('sage.algebras.letterplace.letterplace_ideal',
     143              sources = ['sage/algebras/letterplace/letterplace_ideal.pyx'],
     144              language="c++",
     145              include_dirs = [SAGE_ROOT +'/local/include/singular'],
     146              depends = singular_depends),
     147
    130148    Extension('sage.algebras.quatalg.quaternion_algebra_cython',
    131149               sources = ['sage/algebras/quatalg/quaternion_algebra_cython.pyx'],
    132150               language='c++',
     
    12351253    Extension('sage.rings.morphism',
    12361254              sources = ['sage/rings/morphism.pyx']),
    12371255
     1256    Extension('sage.rings.noncommutative_ideals',
     1257              sources = ['sage/rings/noncommutative_ideals.pyx']),
     1258
    12381259    Extension('sage.rings.power_series_mpoly',
    12391260              sources = ['sage/rings/power_series_mpoly.pyx']),
    12401261
  • sage/algebras/free_algebra.py

    diff --git a/sage/algebras/free_algebra.py b/sage/algebras/free_algebra.py
    a b  
    88- William Stein (2006-11-01): add all doctests; implemented many
    99  things.
    1010
     11- Simon King (2011-03-21): reimplement free algebra constructor, using
     12  a :class:`~sage.structure.factory.UniqueFactory` for handling
     13  different implementations of free algebras.
     14
    1115EXAMPLES::
    1216
    1317    sage: F = FreeAlgebra(ZZ,3,'x,y,z')
     
    1822    sage: G.base_ring()
    1923    Free Algebra on 3 generators (x, y, z) over Integer Ring       
    2024
     25The above free algebra is based on a generic implementation. By trac
     26ticket #7797, there is a different implementation
     27:class:`~sage.algebras.letterplace.free_algebra_letterplace.FreeAlgebra_letterplace`
     28based on Singular's letterplace rings. It is currently restricted to
     29homogeneous elements and is therefore not the default. But the
     30arithmetic is much faster than in the generic implementation.
     31Moreover, we can compute Groebner bases with degree bound for its
     32two-sided ideals, and thus provide ideal containment tests::
     33
     34    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     35    sage: F
     36    Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     37    sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     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    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
     41    True
     42
     43NOTE:
     44
     45For efficiency, a univariate polynomial ring (resp. a multivariate polynomial ring
     46with one generator) is returned if a free algebra with one generator is requested::
     47
     48    sage: FreeAlgebra(ZZ,'a')
     49    Univariate Polynomial Ring in a over Integer Ring
     50    sage: FreeAlgebra(ZZ,'a',implementation='letterplace')
     51    Multivariate Polynomial Ring in a over Integer Ring
     52
    2153TESTS::
    2254
    2355    sage: F = FreeAlgebra(GF(5),3,'x')
    24     sage: F == loads(dumps(F))
     56    sage: F is loads(dumps(F))
     57    True
     58    sage: F = FreeAlgebra(GF(5),3,'x', implementation='letterplace')
     59    sage: F is loads(dumps(F))
    2560    True
    2661
    2762::
    2863
    2964    sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
    30     sage: F == loads(dumps(F))
     65    sage: F is loads(dumps(F))
     66    True
     67    sage: F.<x,y,z> = FreeAlgebra(GF(5),3, implementation='letterplace')
     68    sage: F is loads(dumps(F))
    3169    True
    3270
    3371::
    3472
    3573    sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'])
    36     sage: F == loads(dumps(F))
     74    sage: F is loads(dumps(F))
     75    True
     76    sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'], implementation='letterplace')
     77    sage: F is loads(dumps(F))
    3778    True
    3879
    3980::
    4081
    4182    sage: F = FreeAlgebra(GF(5),3, 'abc')
    42     sage: F == loads(dumps(F))
     83    sage: F is loads(dumps(F))
     84    True
     85    sage: F = FreeAlgebra(GF(5),3, 'abc', implementation='letterplace')
     86    sage: F is loads(dumps(F))
    4387    True
    4488
    4589::
    4690
    47     sage: F = FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x')
    48     sage: F == loads(dumps(F))
     91    sage: F = FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x')
     92    sage: F is loads(dumps(F))
    4993    True
     94
     95Note that the letterplace implementation can only be used if the corresponding
     96(multivariate) polynomial ring has an implementation in Singular::
     97
     98    sage: FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x', implementation='letterplace')
     99    Traceback (most recent call last):
     100    ...
     101    NotImplementedError: The letterplace implementation is not available for the free algebra you requested
     102
     103
    50104"""
    51105
    52106#*****************************************************************************
     
    68122
    69123import sage.structure.parent_gens
    70124
    71        
    72 def FreeAlgebra(R, n, names):
     125from sage.structure.factory import UniqueFactory
     126from sage.all import PolynomialRing
     127from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular
     128
     129class FreeAlgebraFactory(UniqueFactory):
    73130    """
    74     Return the free algebra over the ring `R` on `n`
    75     generators with given names.
    76    
    77     INPUT:
    78    
    79    
    80     -  ``R`` - ring
    81    
    82     -  ``n`` - integer
    83    
    84     -  ``names`` - string or list/tuple of n strings
    85    
    86    
    87     OUTPUT: a free algebra
    88    
     131    A constructor of free algebras.
     132
     133    See :mod:`~sage.algebras.free_algebra` for examples and corner cases.
     134
    89135    EXAMPLES::
    90    
    91         sage: FreeAlgebra(GF(5),3,'x')
    92         Free Algebra on 3 generators (x0, x1, x2) over Finite Field of size 5
    93         sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
    94         sage: (x+y+z)^2
    95         x^2 + x*y + x*z + y*x + y^2 + y*z + z*x + z*y + z^2
    96         sage: FreeAlgebra(GF(5),3, 'xx, zba, Y')
    97         Free Algebra on 3 generators (xx, zba, Y) over Finite Field of size 5
    98         sage: FreeAlgebra(GF(5),3, 'abc')
    99         Free Algebra on 3 generators (a, b, c) over Finite Field of size 5
    100         sage: FreeAlgebra(GF(5),1, 'z')
    101         Free Algebra on 1 generators (z,) over Finite Field of size 5
    102         sage: FreeAlgebra(GF(5),1, ['alpha'])
    103         Free Algebra on 1 generators (alpha,) over Finite Field of size 5
    104         sage: FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x')
    105         Free Algebra on 2 generators (x0, x1) over Free Algebra on 1 generators (a,) over Integer Ring
    106    
    107     Free algebras are globally unique::
    108    
    109         sage: F = FreeAlgebra(ZZ,3,'x,y,z')
    110         sage: G = FreeAlgebra(ZZ,3,'x,y,z')
     136
     137        sage: F.<x,y,z> = FreeAlgebra(GF(5),3)  # indirect doctest
     138        sage: F is loads(dumps(F))
     139        True
     140        sage: F is FreeAlgebra(GF(5),['x','y','z'])
     141        True
     142        sage: G = FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace')
    111143        sage: F is G
     144        False
     145        sage: G is FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace')
    112146        True
    113    
    114     Free algebras commute with their base ring.
    115    
    116     ::
    117    
    118         sage: K.<a,b> = FreeAlgebra(QQ,2)
    119         sage: K.is_commutative()
    120         False
    121         sage: L.<c> = FreeAlgebra(K,1)
    122         sage: L.is_commutative()
    123         False
    124         sage: s = a*b^2 * c^3; s
    125         a*b^2*c^3
    126         sage: parent(s)
    127         Free Algebra on 1 generators (c,) over Free Algebra on 2 generators (a, b) over Rational Field
    128         sage: c^3 * a * b^2
    129         a*b^2*c^3
     147        sage: copy(G) is G
     148        True
     149        sage: copy(F) is F
     150        True
     151
    130152    """
    131     names = sage.structure.parent_gens.normalize_names(n, names)
    132     return cache(R, n, names)
     153    def create_key(self,base_ring, arg1=None, arg2=None,
     154                                      sparse=False, order='degrevlex',
     155                                      names=None, name=None,
     156                                      implementation=None):
     157        """
     158        Create the key under which a free algebra is stored.
     159
     160        TESTS::
     161
     162            sage: FreeAlgebra.create_key(GF(5),['x','y','z'])
     163            (Finite Field of size 5, ('x', 'y', 'z'))
     164            sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3)
     165            (Finite Field of size 5, ('x', 'y', 'z'))
     166            sage: FreeAlgebra.create_key(GF(5),3,'xyz')
     167            (Finite Field of size 5, ('x', 'y', 'z'))
     168            sage: FreeAlgebra.create_key(GF(5),['x','y','z'], implementation='letterplace')
     169            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     170            sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3, implementation='letterplace')
     171            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     172            sage: FreeAlgebra.create_key(GF(5),3,'xyz', implementation='letterplace')
     173            (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,)
     174
     175        """
     176        if arg1 is None and arg2 is None and names is None:
     177            # this is used for pickling
     178            return (base_ring,)
     179        PolRing = None
     180        # test if we can use libSingular/letterplace
     181        if implementation is not None and implementation != 'generic':
     182            try:
     183                PolRing = PolynomialRing(base_ring, arg1, arg2,
     184                                   sparse=sparse, order=order,
     185                                   names=names, name=name,
     186                                   implementation=implementation if implementation!='letterplace' else None)
     187                if not isinstance(PolRing,MPolynomialRing_libsingular):
     188                    if PolRing.ngens() == 1:
     189                        PolRing = PolynomialRing(base_ring,1,PolRing.variable_names())
     190                        if not isinstance(PolRing,MPolynomialRing_libsingular):
     191                            raise TypeError
     192                    else:
     193                        raise TypeError
     194            except (TypeError, NotImplementedError),msg:
     195                raise NotImplementedError, "The letterplace implementation is not available for the free algebra you requested"
     196        if PolRing is not None:
     197            return (PolRing,)
     198        # normalise the generator names
     199        from sage.all import Integer
     200        if isinstance(arg1, (int, long, Integer)):
     201            arg1, arg2 = arg2, arg1
     202        if not names is None:
     203            arg1 = names
     204        elif not name is None:
     205            arg1 = name
     206        if arg2 is None:
     207            arg2 = len(arg1)
     208        names = sage.structure.parent_gens.normalize_names(arg2,arg1)
     209        return base_ring, names
     210    def create_object(self, version, key):
     211        """
     212        Construct the free algebra that belongs to a unique key.
     213
     214        NOTE:
     215
     216        Of course, that method should not be called directly,
     217        since it does not use the cache of free algebras.
     218
     219        TESTS::
     220
     221            sage: FreeAlgebra.create_object('4.6.2', (QQ['x','y'],))
     222            Free Associative Unital Algebra on 2 generators ('x', 'y') over Rational Field
     223            sage: FreeAlgebra.create_object('4.6.2', (QQ['x','y'],)) is FreeAlgebra(QQ,['x','y'])
     224            False
     225
     226        """
     227        if len(key)==1:
     228            if key[0].ngens()<=1:
     229                return key[0]
     230            from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     231            return FreeAlgebra_letterplace(key[0])
     232        if len(key[1])<=1:
     233            return PolynomialRing(key[0],key[1])
     234        return FreeAlgebra_generic(key[0],len(key[1]),key[1])
     235
     236FreeAlgebra = FreeAlgebraFactory('FreeAlgebra')
    133237
    134238def is_FreeAlgebra(x):
    135239    """
     
    144248        False
    145249        sage: is_FreeAlgebra(FreeAlgebra(ZZ,100,'x'))
    146250        True
     251        sage: is_FreeAlgebra(FreeAlgebra(ZZ,10,'x',implementation='letterplace'))
     252        True
     253
    147254    """
    148     return isinstance(x, FreeAlgebra_generic)
     255    from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     256    return isinstance(x, (FreeAlgebra_generic,FreeAlgebra_letterplace))
    149257
    150258
    151259class FreeAlgebra_generic(Algebra):
     
    164272        x*y*x*y*x*y*x*y*x*y*x*y + x*y*z*x*y*z*x*y*z*x*y*z
    165273        sage: (2 + x*z + x^2)^2 + (x - y)^2
    166274        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
     275
     276    TESTS:
     277
     278    Free algebras commute with their base ring.
     279    ::
     280   
     281        sage: K.<a,b> = FreeAlgebra(QQ)
     282        sage: K.is_commutative()
     283        False
     284        sage: L.<c,d> = FreeAlgebra(K)
     285        sage: L.is_commutative()
     286        False
     287        sage: s = a*b^2 * c^3; s
     288        a*b^2*c^3
     289        sage: parent(s)
     290        Free Algebra on 2 generators (c, d) over Free Algebra on 2 generators (a, b) over Rational Field
     291        sage: c^3 * a * b^2
     292        a*b^2*c^3
     293
    167294    """
    168295    def __init__(self, R, n, names):
    169296        """
     
    175302        -  ``n`` - an integer
    176303       
    177304        -  ``names`` - generator names
     305
     306        TEST:
     307
     308        Note that the following is *not* the recommended way to create
     309        a free algebra.
     310        ::
     311
     312            sage: from sage.algebras.free_algebra import FreeAlgebra_generic
     313            sage: FreeAlgebra_generic(ZZ,3,'abc')
     314            Free Algebra on 3 generators (a, b, c) over Integer Ring
     315           
    178316        """
    179317        if not isinstance(R, Ring):
    180318            raise TypeError, "Argument R must be a ring."
     
    218356    def __cmp__(self, other):
    219357        """
    220358        Two free algebras are considered the same if they have the same
    221         base ring, number of generators and variable names.
     359        base ring, number of generators and variable names, and the same
     360        implementation.
    222361       
    223362        EXAMPLES::
    224363       
     
    233372            False
    234373            sage: F == FreeAlgebra(QQ,3,'y')
    235374            False
     375
     376        Note that since trac ticket #7797 there is a different
     377        implementation of free algebras. Two corresponding free
     378        algebras in different implementations are not equal, but there
     379        is a coercion::
     380
     381           
    236382        """
    237383        if not isinstance(other, FreeAlgebra_generic):
    238384            return -1
    239385        c = cmp(self.base_ring(), other.base_ring())
    240386        if c: return c
    241         c = cmp(self.__ngens, other.__ngens)
     387        c = cmp(self.__ngens, other.ngens())
    242388        if c: return c
    243389        c = cmp(self.variable_names(), other.variable_names())
    244390        if c: return c
     
    251397        EXAMPLES::
    252398       
    253399            sage: F = FreeAlgebra(QQ,3,'x')
    254             sage: print F
     400            sage: F    # indirect doctest
    255401            Free Algebra on 3 generators (x0, x1, x2) over Rational Field
    256402            sage: F.rename('QQ<<x0,x1,x2>>')
    257             sage: print F
     403            sage: F
    258404            QQ<<x0,x1,x2>>
    259405        """
    260406        return "Free Algebra on %s generators %s over %s"%(
     
    262408
    263409    def __call__(self, x):
    264410        """
    265        Coerce x into self.
     411       Convert x into self.
    266412       
    267413       EXAMPLES::
    268414       
    269415           sage: R.<x,y> = FreeAlgebra(QQ,2)
    270416           sage: R(3)
    271417           3
     418
     419       TESTS::
     420
     421           sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     422           sage: L.<x,y,z> = FreeAlgebra(ZZ,3,implementation='letterplace')
     423           sage: F(x)     # indirect doctest
     424           x
     425           sage: F.1*L.2
     426           y*z
     427           sage: (F.1*L.2).parent() is F
     428           True
     429
     430       ::
     431
     432           sage: K.<z> = GF(25)
     433           sage: F.<a,b,c> = FreeAlgebra(K,3)
     434           sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace')
     435           sage: F.1+(z+1)*L.2
     436           b + (z+1)*c
     437         
    272438       """
    273439        if isinstance(x, FreeAlgebraElement):
    274440            P = x.parent()
     
    276442                return x
    277443            if not (P is self.base_ring()):
    278444                return FreeAlgebraElement(self, x)
     445        elif hasattr(x,'letterplace_polynomial'):
     446            P = x.parent()
     447            if self.has_coerce_map_from(P): # letterplace versus generic
     448                ngens = P.ngens()
     449                M = self.__monoid
     450                def exp_to_monomial(T):
     451                    out = []
     452                    for i in xrange(len(T)):
     453                        if T[i]:
     454                            out.append((i%ngens,T[i]))
     455                    return M(out)
     456                return FreeAlgebraElement(self, dict([(exp_to_monomial(T),c) for T,c in x.letterplace_polynomial().dict().iteritems()]))
    279457        # ok, not a free algebra element (or should not be viewed as one).
     458        if isinstance(x, basestring):
     459            from sage.all import sage_eval
     460            return sage_eval(x,locals=self.gens_dict())
    280461        F = self.__monoid
    281462        R = self.base_ring()
    282463        # coercion from free monoid
     
    297478
    298479        - this free algebra
    299480
     481        - a free algebra in letterplace implementation that has
     482          the same generator names and whose base ring coerces
     483          into self's base ring
     484
    300485        - the underlying monoid
    301486
    302487        - anything that coerces to the base ring of this free algebra
     
    361546            Traceback (most recent call last):
    362547            ...
    363548            TypeError: no natural map between bases of free algebras
     549
     550        TESTS::
     551
     552           sage: F.<x,y,z> = FreeAlgebra(GF(5),3)
     553           sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace')
     554           sage: F(x)
     555           x
     556           sage: F.1*L.2     # indirect doctest
     557           y*z
     558
    364559        """
    365560        try:
    366561            R = x.parent()
     
    384579        return self._coerce_try(x, [self.base_ring()])
    385580
    386581    def coerce_map_from_impl(self, R):
     582        """
     583        Test whether there is a coercion from R to self (old coercion framework).
     584
     585        TESTS::
     586
     587           sage: K.<z> = GF(25)
     588           sage: F.<a,b,c> = FreeAlgebra(K,3)
     589           sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace')
     590           sage: F.1+(z+1)*L.2      # indirect doctest
     591           b + (z+1)*c
     592        """
    387593        if R is self.__monoid:
    388594            return True
    389595
  • 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        v = self.__monomial_coefficients.items()
     
    101101        EXAMPLES::
    102102       
    103103            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    104             sage: latex(-x+3*y^20*z)
     104            sage: latex(-x+3*y^20*z)   # indirect doctest
    105105            \left(-1\right)x + 3y^{20}z
    106106            sage: alpha,beta,gamma=FreeAlgebra(ZZ,3,'alpha,beta,gamma').gens()
    107107            sage: latex(alpha-beta)
     
    199199        EXAMPLES::
    200200       
    201201            sage: R.<x,y> = FreeAlgebra(QQ,2)
    202             sage: x + y
     202            sage: x + y    # indirect doctest
    203203            x + y
    204204        """
    205205        A = self.parent()
     
    236236        EXAMPLES::
    237237       
    238238            sage: R.<x,y> = FreeAlgebra(QQ,2)
    239             sage: -(x+y)
     239            sage: -(x+y)    # indirect doctest
    240240            -x - y
    241241        """
    242242        y = self.parent()(0)
     
    254254        EXAMPLES::
    255255       
    256256            sage: R.<x,y> = FreeAlgebra(QQ,2)
    257             sage: x - y
     257            sage: x - y    # indirect doctest
    258258            x - y
    259259        """
    260260        A = self.parent()
     
    292292        EXAMPLES::
    293293       
    294294            sage: A.<x,y,z>=FreeAlgebra(ZZ,3)
    295             sage: (x+y+x*y)*(x+y+1)
     295            sage: (x+y+x*y)*(x+y+1)    # indirect doctest
    296296            x + y + x^2 + 2*x*y + y*x + y^2 + x*y*x + x*y^2
    297297        """
    298298        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/free_algebra_element_letterplace.pxd

    diff --git a/sage/algebras/letterplace/__init__.py b/sage/algebras/letterplace/__init__.py
    new file mode 100644
    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 __richcmp__(left, right, int op):
     287        """
     288        TEST::
     289
     290            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     291            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     292            sage: p-p.lt()<p    # indirect doctest
     293            True
     294
     295        """
     296        return (<Element>left)._richcmp(right, op)
     297    def __cmp__(left, right):
     298        """
     299        TEST::
     300
     301            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     302            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     303            sage: cmp(p,p-p.lt())    # indirect doctest
     304            1
     305
     306        """
     307        return (<Element>left)._cmp(right)
     308
     309    cdef int _cmp_c_impl(self, Element other) except -2:
     310        """
     311        Auxiliary method for comparison.
     312
     313        TEST::
     314
     315            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     316            sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z))
     317            sage: p-p.lt()<p    # indirect doctest
     318            True
     319
     320        """
     321        cdef int c = cmp(type(self),type(other))
     322        if c: return c
     323        return cmp((<FreeAlgebraElement_letterplace>self)._poly,(<FreeAlgebraElement_letterplace>other)._poly)
     324
     325    ################################
     326    ## Arithmetic
     327    cpdef ModuleElement _neg_(self):
     328        """
     329        TEST::
     330
     331            sage: K.<z> = GF(25)
     332            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     333            sage: -((z+2)*a^2*b+3*c^3)  # indirect doctest
     334            (4*z + 3)*a*a*b + (2)*c*c*c
     335            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     336            sage: -(3*x*y+2*z^2)
     337            -3*x*y - 2*z*z
     338
     339        """
     340        return FreeAlgebraElement_letterplace(self._parent,-self._poly,check=False)
     341    cpdef ModuleElement _add_(self, ModuleElement other):
     342        """
     343        Addition, under the side condition that either one summand
     344        is zero, or both summands have the same degree.
     345
     346        TEST::
     347
     348            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     349            sage: x+y    # indirect doctest
     350            x + y
     351            sage: x+1
     352            Traceback (most recent call last):
     353            ...
     354            ArithmeticError: Can only add elements of the same degree
     355            sage: x+0
     356            x
     357            sage: 0+x
     358            x
     359
     360        """
     361        if not other:
     362            return self
     363        if not self:
     364            return other
     365        cdef FreeAlgebraElement_letterplace right = other       
     366        if right._poly.degree()!=self._poly.degree():
     367            raise ArithmeticError, "Can only add elements of the same degree"
     368        # update the polynomials
     369        cdef FreeAlgebra_letterplace A = self._parent
     370        self._poly = A._current_ring(self._poly)
     371        right._poly = A._current_ring(right._poly)
     372        return FreeAlgebraElement_letterplace(self._parent,self._poly+right._poly,check=False)
     373
     374    cpdef ModuleElement _sub_(self, ModuleElement other):
     375        """
     376        Difference, under the side condition that either one summand
     377        is zero or both have the same degree.
     378
     379        TEST::
     380
     381            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     382            sage: x*y-y*x     # indirect doctest
     383            x*y - y*x
     384            sage: x-1
     385            Traceback (most recent call last):
     386            ...
     387            ArithmeticError: Can only subtract elements of the same degree
     388            sage: x-0
     389            x
     390            sage: 0-x
     391            -x
     392
     393        """
     394        if not other:
     395            return self
     396        if not self:
     397            return -other
     398        cdef FreeAlgebraElement_letterplace right = other
     399        if right._poly.degree()!=self._poly.degree():
     400            raise ArithmeticError, "Can only subtract elements of the same degree"
     401        # update the polynomials
     402        cdef FreeAlgebra_letterplace A = self._parent
     403        self._poly = A._current_ring(self._poly)
     404        right._poly = A._current_ring(right._poly)
     405        return FreeAlgebraElement_letterplace(self._parent,self._poly-right._poly,check=False)
     406
     407    cpdef ModuleElement _lmul_(self, RingElement right):
     408        """
     409        Multiplication from the right with an element of the base ring.
     410
     411        TEST::
     412
     413            sage: K.<z> = GF(25)
     414            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     415            sage: (a+b)*(z+1)    # indirect doctest
     416            (z + 1)*a + (z + 1)*b
     417
     418        """
     419        return FreeAlgebraElement_letterplace(self._parent,self._poly._lmul_(right),check=False)
     420
     421    cpdef ModuleElement _rmul_(self, RingElement left):
     422        """
     423        Multiplication from the left with an element of the base ring.
     424
     425        TEST::
     426
     427            sage: K.<z> = GF(25)
     428            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     429            sage: (z+1)*(a+b)   # indirect doctest
     430            (z + 1)*a + (z + 1)*b
     431
     432        """
     433        return FreeAlgebraElement_letterplace(self._parent,self._poly._rmul_(left),check=False)
     434
     435    cpdef RingElement _mul_(self, RingElement other):
     436        cdef FreeAlgebraElement_letterplace left = self
     437        cdef FreeAlgebraElement_letterplace right = other
     438        cdef FreeAlgebra_letterplace A = left._parent
     439        A.set_degbound(left._poly.degree()+right._poly.degree())
     440        # we must put the polynomials into the same ring
     441        left._poly = A._current_ring(left._poly)
     442        right._poly = A._current_ring(right._poly)
     443        rshift = singular_system("stest",right._poly,left._poly.degree(),A._degbound,A.__ngens, ring=A._current_ring)
     444        return FreeAlgebraElement_letterplace(A,left._poly*rshift, check=False)
     445
     446    def __pow__(FreeAlgebraElement_letterplace self, int n, k):
     447        """
     448        TEST::
     449
     450            sage: K.<z> = GF(25)
     451            sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
     452            sage: (a+z*b)^3    # indirect doctest
     453            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
     454
     455        """
     456        cdef FreeAlgebra_letterplace A = self._parent
     457        if n<0:
     458            raise ValueError, "Negative exponents are not allowed"
     459        if n==0:
     460            return FreeAlgebraElement_letterplace(A, A._current_ring(1),
     461                                                  check=False)
     462        if n==1:
     463            return self
     464        A.set_degbound(self._poly.degree()*n)
     465        cdef MPolynomial_libsingular p,q
     466        self._poly = A._current_ring(self._poly)
     467        cdef int d = self._poly.degree()
     468        q = p = self._poly
     469        cdef int i
     470        for i from 0<i<n:
     471            q = singular_system("stest",q,d,A._degbound,A.__ngens,
     472                                     ring=A._current_ring)
     473            p *= q
     474        return FreeAlgebraElement_letterplace(A, p, check=False)
     475
     476    ## Groebner related stuff
     477    def reduce(self, G):
     478        """
     479        Reduce this element by a list of elements or by a
     480        twosided homogeneous ideal.
     481
     482        INPUT:
     483
     484        Either a list or tuple of homogeneous elements of the
     485        free algebra, or an ideal of the free algebra, or an
     486        ideal in the commutative polynomial ring that is
     487        currently used to implement the multiplication in the
     488        free algebra.
     489
     490        OUTPUT:
     491
     492        The twosided reduction of this element by the argument.
     493       
     494        NOTE:
     495           
     496        This may not be the normal form of this element, unless
     497        the argument is a twosided Groebner basis up to the degree
     498        of this element.
     499
     500        EXAMPLE::
     501
     502            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     503            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     504            sage: p = y^2*z*y^2+y*z*y*z*y
     505
     506        We compute the letterplace version of the Groebneer basis
     507        of `I` with degree bound 4::
     508
     509            sage: G = F._reductor_(I.groebner_basis(4).gens(),4)
     510            sage: G.ring() is F.current_ring()
     511            True
     512
     513        Since the element `p` is of degree 5, it is no surrprise
     514        that its reductions with respect to the original generators
     515        of `I` (of degree 2), or with respect to `G` (Groebner basis
     516        with degree bound 4), or with respect to the Groebner basis
     517        with degree bound 5 (which yields its normal form) are
     518        pairwise different::
     519
     520            sage: p.reduce(I)
     521            y*y*z*y*y + y*z*y*z*y
     522            sage: p.reduce(G)
     523            y*y*z*z*y + y*z*y*z*y - y*z*z*y*y + y*z*z*z*y
     524            sage: p.normal_form(I)
     525            y*y*z*z*z + y*z*y*z*z - y*z*z*y*z + y*z*z*z*z
     526            sage: p.reduce(I) != p.reduce(G) != p.normal_form(I) != p.reduce(I)
     527            True
     528
     529        """
     530        cdef FreeAlgebra_letterplace P = self._parent
     531        if not isinstance(G,(list,tuple)):
     532            if G==P:
     533                return P.zero_element()
     534            if not (isinstance(G,MPolynomialIdeal) and G.ring()==P._current_ring):
     535                G = G.gens()
     536        C = P.current_ring()
     537        cdef int selfdeg = self._poly.degree()
     538        if isinstance(G,MPolynomialIdeal):
     539            gI = G
     540        else:
     541            gI = P._reductor_(G,selfdeg) #C.ideal(g,coerce=False)
     542        from sage.libs.singular.option import LibSingularOptions
     543        libsingular_options = LibSingularOptions()
     544        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     545        libsingular_options['redTail'] = True
     546        libsingular_options['redSB'] = True
     547        poly = poly_reduce(C(self._poly),gI, ring=C,
     548                           attributes={gI:{"isSB":1}})
     549        libsingular_options['redTail'] = bck[0]
     550        libsingular_options['redSB'] = bck[1]
     551        return FreeAlgebraElement_letterplace(P,poly,check=False)
     552
     553    def normal_form(self,I):
     554        """
     555        Return the normal form of this element with respect to
     556        a twosided homogeneous ideal.
     557
     558        INPUT:
     559
     560        A twosided homogeneous ideal `I` of the parent `F` of
     561        this element, `x`.
     562
     563        OUTPUT:
     564
     565        The normal form of `x` wrt. `I`.
     566
     567        NOTE:
     568
     569        The normal form is computed by reduction with respect
     570        to a Groebnerbasis of `I` with degree bound `deg(x)`.
     571
     572        EXAMPLE::
     573
     574            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     575            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     576            sage: (x^5).normal_form(I)
     577            -y*z*z*z*x - y*z*z*z*y - y*z*z*z*z
     578
     579        We verify two basic properties of normal forms: The
     580        difference of an element and its normal form is contained
     581        in the ideal, and if two elements of the free algebra
     582        differ by an element of the ideal then they have the same
     583        normal form::
     584
     585            sage: x^5 - (x^5).normal_form(I) in I
     586            True
     587            sage: (x^5+x*I.0*y*z-3*z^2*I.1*y).normal_form(I) == (x^5).normal_form(I)
     588            True
     589
     590        """
     591        if self._parent != I.ring():
     592            raise ValueError, "Can not compute normal form wrt an ideal that does not belong to %s"%self._parent
     593        sdeg = self._poly.degree()
     594        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        NOTE:
     376
     377        Currently, the ``FreeAlgebra`` constructor returns a polynomial ring when a free algebra
     378        with one generator is requested.
     379
     380        EXAMPLE::
     381
     382            sage: FreeAlgebra(QQ,'x')
     383            Univariate Polynomial Ring in x over Rational Field
     384            sage: FreeAlgebra(QQ,'x',implementation='letterplace')
     385            Multivariate Polynomial Ring in x over Rational Field
     386           
     387        ::
     388
     389            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     390            sage: F.is_commutative()
     391            False
     392
     393        With a bit more effort, we create a commutative free algebra that is
     394        not implemented as a polynomial ring::
     395
     396            sage: from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
     397            sage: P = FreeAlgebra_letterplace(PolynomialRing(QQ,1,['x']))
     398            sage: P
     399            Free Associative Unital Algebra on 1 generators ('x',) over Rational Field
     400            sage: P.is_commutative()
     401            True
     402
     403        """
     404        return self.__ngens <= 1
     405
     406    def is_field(self):
     407        """
     408        Tell whether this free algebra is a field.
     409
     410        NOTE:
     411
     412        This would only be the case in the degenerate case of no generators.
     413        But such an example can not be constructed in this implementation.
     414
     415        TEST::
     416
     417            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     418            sage: F.is_field()
     419            False
     420
     421        """
     422        return (not self.__ngens) and self._base.is_field()
     423
     424    def _repr_(self):
     425        """
     426        EXAMPLE::
     427
     428            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     429            sage: F     # indirect doctest
     430            Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     431
     432        """
     433        return "Free Associative Unital Algebra on %d generators %s over %s"%(self.__ngens,self._names,self._base)
     434    def degbound(self):
     435        """
     436        Return the degree bound that is currently used.
     437
     438        NOTE:
     439
     440        When multiplying two elements of this free algebra, the degree
     441        bound will be dynamically adapted. It can also be set by
     442        :meth:`set_degbound`.
     443
     444        EXAMPLE:
     445
     446        In order to avoid we get a free algebras from the cache that
     447        was created in another doctest and has a different degree
     448        bound, we choose a base ring that does not appear in other tests::
     449
     450            sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
     451            sage: F.degbound()
     452            1
     453            sage: x*y
     454            x*y
     455            sage: F.degbound()
     456            2
     457            sage: F.set_degbound(4)
     458            sage: F.degbound()
     459            4
     460
     461        """
     462        return self._degbound
     463    def set_degbound(self,d):
     464        """
     465        Increase the degree bound that is currently in place.
     466
     467        NOTE:
     468
     469        The degree bound can not be decreased.
     470
     471        EXAMPLE:
     472
     473        In order to avoid we get a free algebras from the cache that
     474        was created in another doctest and has a different degree
     475        bound, we choose a base ring that does not appear in other tests::
     476
     477            sage: F.<x,y,z> = FreeAlgebra(GF(251), implementation='letterplace')
     478            sage: F.degbound()
     479            1
     480            sage: x*y
     481            x*y
     482            sage: F.degbound()
     483            2
     484            sage: F.set_degbound(4)
     485            sage: F.degbound()
     486            4
     487            sage: F.set_degbound(2)
     488            sage: F.degbound()
     489            4         
     490
     491        """
     492        if d<=self._degbound:
     493            return
     494        self._degbound = d
     495        self._current_ring = make_letterplace_ring(self._commutative_ring,d)
     496
     497#    def base_extend(self, R):
     498#        if self._base.has_coerce_map_from(R):
     499#            return self
     500
     501    ################################################
     502    ## Ideals
     503
     504    def _ideal_class_(self, n=0):
     505        """
     506        Return the class :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`.
     507
     508        EXAMPLE::
     509
     510            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     511            sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*F
     512            sage: I
     513            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
     514            sage: type(I) is F._ideal_class_()
     515            True
     516
     517        """
     518        from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
     519        return LetterplaceIdeal
     520
     521    def ideal_monoid(self):
     522        """
     523        Return the monoid of ideals of this free algebra.
     524
     525        EXAMPLE::
     526
     527            sage: F.<x,y> = FreeAlgebra(GF(2), implementation='letterplace')
     528            sage: F.ideal_monoid()
     529            Monoid of ideals of Free Associative Unital Algebra on 2 generators ('x', 'y') over Finite Field of size 2
     530            sage: F.ideal_monoid() is F.ideal_monoid()
     531            True
     532
     533        """
     534        if self.__monoid is None:
     535            self.__monoid = IdealMonoid_nc(self)
     536        return self.__monoid
     537
     538    # Auxiliar methods
     539    cdef str exponents_to_string(self, E):
     540        """
     541        This auxiliary method is used for the string representation of elements of this free algebra.
     542
     543        EXAMPLE::
     544
     545            sage: F.<x,y> = FreeAlgebra(GF(2), implementation='letterplace')
     546            sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace
     547            sage: P = F.commutative_ring()
     548            sage: FreeAlgebraElement_letterplace(F, P.0*P.1^2+P.1^3)   # indirect doctest
     549            x.y^2 + y^3
     550
     551        """
     552        cdef int ngens = self.__ngens
     553        cdef int nblocks = len(E)/ngens
     554        cdef int i
     555        cdef list out = []
     556        for i from 0<=i<nblocks:
     557            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])
     558            if s:
     559                out.append(s)
     560        return '*'.join(out)
     561
     562    def _reductor_(self, g, d):
     563        """
     564        Return a commutative ideal that can be used to compute the normal
     565        form of a free algebra element of a given degree.
     566
     567        INPUT:
     568
     569        ``g`` - a list of elements of this free algebra.
     570        ``d`` - an integer.
     571
     572        OUTPUT:
     573
     574        An ideal such that reduction of a letterplace polynomial by that ideal corresponds
     575        to reduction of an element of degree at most ``d`` by ``g``.
     576
     577        EXAMPLE::
     578
     579            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     580            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     581            sage: p = y*x*y + y*y*y + y*z*y - y*z*z
     582            sage: p.reduce(I)
     583            y*y*y - y*y*z + y*z*y - y*z*z
     584            sage: G = F._reductor_(I.gens(),3); G
     585            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
     586
     587        We do not use the usual reduction method for polynomials in
     588        Sage, since it does the reductions in a different order
     589        compared to Singular. Therefore, we call the original Singular
     590        reduction method, and prevent a warning message by asserting
     591        that `G` is a Groebner basis.
     592
     593            sage: from sage.libs.singular.function import singular_function
     594            sage: poly_reduce = singular_function("NF")
     595            sage: q = poly_reduce(p.letterplace_polynomial(), G, ring=F.current_ring(), attributes={G:{"isSB":1}}); q
     596            y*y_1*y_2 - y*y_1*z_2 + y*z_1*y_2 - y*z_1*z_2
     597            sage: p.reduce(I).letterplace_polynomial() == q
     598            True
     599
     600        """
     601        cdef list out = []
     602        C = self.current_ring()
     603        cdef FreeAlgebraElement_letterplace x
     604        ngens = self.__ngens
     605        degbound = self._degbound
     606        cdef list G = [C(x._poly) for x in g]
     607        for y in G:
     608            out.extend([y]+[singular_system("stest",y,n+1,degbound,ngens,ring=C) for n in xrange(d-y.degree())])
     609        return C.ideal(out)
     610
     611    ###########################
     612    ## Coercion
     613    def _coerce_map_from_(self,S):
     614        """
     615        There is only a coercion from the base ring.
     616
     617        TEST::
     618
     619            sage: F.<x,y,z> = FreeAlgebra(GF(5), implementation='letterplace')
     620            sage: 5 == F.zero()    # indirect doctest
     621            True
     622
     623        """
     624        return self==S or self._current_ring.has_coerce_map_from(S)
     625
     626    def _an_element_(self):
     627        """
     628        Return an element.
     629
     630        EXAMPLE::
     631
     632            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     633            sage: F.an_element()   # indirect doctest
     634            x
     635
     636        """
     637        return FreeAlgebraElement_letterplace(self, self._current_ring.an_element(), check=False)
     638
     639#    def random_element(self, degree=2, terms=5):
     640#        """
     641#        Return a random element of a given degree and with a given number of terms.
     642#
     643#        INPUT:
     644#
     645#        - ``degree`` -- the maximal degree of the output (default 2).
     646#        - ``terms`` -- the maximal number of terms of the output (default 5).
     647#
     648#        NOTE:
     649#
     650#        This method is currently not useful at all.
     651#
     652#        Not tested.
     653#        """
     654#        self.set_degbound(degree)
     655#        while(1):
     656#            p = self._current_ring.random_element(degree=degree,terms=terms)
     657#            if p.is_homogeneous():
     658#                break
     659#        return FreeAlgebraElement_letterplace(self, p, check=False)
     660
     661    def _from_dict_(self, D, check=True):
     662        """
     663        Create an element from a dictionary.
     664
     665        INPUT:
     666
     667        - A dictionary. Keys: tuples of exponents. Values:
     668          The coefficients of the corresponding monomial
     669          in the to-be-created element.
     670        - ``check`` (optional bool, default ``True``):
     671          This is forwarded to the initialisation of
     672          :class:`~sage.algebas.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace`.
     673
     674        TEST:
     675
     676        This method applied to the dictionary of any element must
     677        return the same element. This must hold true even if the
     678        underlying letterplace ring has been extended in the meantime.
     679        ::
     680
     681            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     682            sage: p = 3*x*y+2*z^2
     683            sage: F.set_degbound(10)
     684            sage: p == F._from_dict_(dict(p))
     685            True
     686
     687        For the empty dictionary, zero is returned::
     688
     689            sage: F._from_dict_({})
     690            0
     691
     692        """
     693        if not D:
     694            return self.zero_element()
     695        cdef int l
     696        for e in D.iterkeys():
     697            l = len(e)
     698            break
     699        cdef dict out = {}
     700        self.set_degbound(l/self.__ngens)
     701        cdef int n = self._current_ring.ngens()
     702        for e,c in D.iteritems():
     703            out[tuple(e)+(0,)*(n-l)] = c
     704        return FreeAlgebraElement_letterplace(self,self._current_ring(out),
     705                                              check=check)
     706
     707    def _element_constructor_(self, x):
     708        """
     709        Return an element of this free algebra.
     710
     711        INPUT:
     712
     713        Something that can be interpreted in the polynomial ring that
     714        is used to implement the letterplace algebra out to the current
     715        degree bound, or a string that can be interpreted as an expression
     716        in the algebra (provided that the coefficients are numerical).
     717
     718        EXAMPLE::
     719
     720            sage: F.<x,y,z> = FreeAlgebra(GF(5), implementation='letterplace')
     721            sage: F(3)
     722            3
     723            sage: F('x*z+3*y^2')
     724            x*z + (3)*y*y
     725            sage: F.set_degbound(3)
     726            sage: P = F.current_ring()
     727            sage: F(P.0*P.4*P.6-2*P.2*P.5*P.8)   # indirect doctest
     728            x*y*x + (3)*z*z*z
     729
     730        """
     731        if isinstance(x, basestring):
     732            from sage.all import sage_eval
     733            return sage_eval(x,locals=self.gens_dict())
     734        try:
     735            P = x.parent()
     736        except AttributeError:
     737            P = None
     738        if P is self:
     739            (<FreeAlgebraElement_letterplace>x)._poly = self._current_ring((<FreeAlgebraElement_letterplace>x)._poly)
     740            return x
     741        return FreeAlgebraElement_letterplace(self,self._current_ring(x))
     742
     743   
  • 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
     46
     47#####################
     48# Define some singular functions
     49lib("freegb.lib")
     50singular_system=singular_function("system")
     51poly_reduce=singular_function("NF")
     52
     53class LetterplaceIdeal(Ideal_nc):
     54    """
     55    Homogeneous ideals in free algebras.
     56
     57    In the two-sided case over a field, one can compute Groebner bases
     58    up to a degree bound, normal forms of homogeneous elements of the
     59    free algebra, and ideal containment.
     60
     61    EXAMPLES::
     62
     63        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     64        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     65        sage: I
     66        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
     67        sage: I.groebner_basis(2)
     68        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
     69        sage: I.groebner_basis(4)
     70        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
     71
     72    Groebner bases are cached. If one has computed a Groebner basis
     73    out to a high degree then it will also be returned if a Groebner
     74    basis with a lower degree bound is requested::
     75
     76        sage: I.groebner_basis(2)
     77        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
     78
     79    Of course, the normal form of any element has to satisfy the following::
     80
     81        sage: x*y*z*y*x - (x*y*z*y*x).normal_form(I) in I
     82        True
     83
     84    Left and right ideals can be constructed, but only twosided ideals provide
     85    Groebner bases::
     86
     87        sage: JL = F*[x*y+y*z,x^2+x*y-y*x-y^2]; JL
     88        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
     89        sage: JR = [x*y+y*z,x^2+x*y-y*x-y^2]*F; JR
     90        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
     91        sage: JR.groebner_basis(2)
     92        Traceback (most recent call last):
     93        ...
     94        TypeError: This ideal is not two-sided. We can only compute two-sided Groebner bases
     95        sage: JL.groebner_basis(2)
     96        Traceback (most recent call last):
     97        ...
     98        TypeError: This ideal is not two-sided. We can only compute two-sided Groebner bases
     99
     100    Also, it is currently not possible to compute a Groebner basis when the base
     101    ring is not a field::
     102
     103        sage: FZ.<a,b,c> = FreeAlgebra(ZZ, implementation='letterplace')
     104        sage: J = FZ*[a^3-b^3]*FZ
     105        sage: J.groebner_basis(2)
     106        Traceback (most recent call last):
     107        ...
     108        TypeError: Currently, we can only compute Groebner bases if the ring of coefficients is a field
     109
     110    """
     111    def __init__(self, ring, gens, coerce=True, side = "twosided"):
     112        """
     113        INPUT:
     114
     115        - ``ring``: A free algebra in letterplace implementation.
     116        - ``gens``: List, tuple or sequence of generators.
     117        - ``coerce`` (optional bool, default ``True``):
     118          Shall ``gens`` be coerced first?
     119        - ``side``: optional string, one of ``"twosided"`` (default),
     120          ``"left"`` or ``"right"``. Determines whether the ideal
     121          is a left, right or twosided ideal. Groebner bases or
     122          only supported in the twosided case.
     123
     124        TEST::
     125
     126            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     127            sage: from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
     128            sage: LetterplaceIdeal(F,x)
     129            Twosided Ideal (x) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     130            sage: LetterplaceIdeal(F,[x,y],side='left')
     131            Left Ideal (x, y) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     132
     133        It is not correctly detected that this class inherits from an
     134        extension class. Therefore, we have to skip one item of the
     135        test suite::
     136
     137            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     138            sage: TestSuite(I).run(skip=['_test_category'],verbose=True)
     139            running ._test_eq() . . . pass
     140            running ._test_not_implemented_methods() . . . pass
     141            running ._test_pickling() . . . pass
     142
     143        """
     144        Ideal_nc.__init__(self, ring, gens, coerce=coerce, side=side)
     145        self.__GB = self
     146        self.__uptodeg = 0
     147    def groebner_basis(self, degbound=None):
     148        """
     149        Twosided Groebner basis with degree bound.
     150
     151        INPUT:
     152
     153        - ``degbound`` (optional integer): If it is provided, a Groebner
     154          basis at least out to that degree is returned. By default, the
     155          current degree bound of the underlying ring is used.
     156
     157        ASSUMPTIONS:
     158
     159        Currently, we can only compute Groebner bases for twosided
     160        ideals, and the ring of coefficients must be a field. A
     161        `TypeError` is raised if one of these conditions is violated.
     162
     163        NOTE:
     164
     165        The result is cached. The same Groebner basis is returned
     166        if a smaller degree bound than the known one is requested.
     167
     168        EXAMPLES::
     169
     170            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     171            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     172
     173        Since `F` was cached and since its degree bound can not be
     174        decreased, it may happen that, as a side effect of other tests,
     175        it already has a degree bound bigger than 3. So, we can not
     176        test against the output of ``I.groebner_basis()``::
     177
     178            sage: F.set_degbound(3)
     179            sage: I.groebner_basis()   # not tested
     180            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
     181            sage: I.groebner_basis(4)
     182            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
     183            sage: I.groebner_basis(2) is I.groebner_basis(4)
     184            True
     185            sage: G = I.groebner_basis(4)
     186            sage: G.groebner_basis(3) is G
     187            True
     188
     189        """
     190        A = self.ring()
     191        if degbound is None:
     192            degbound = A.degbound()
     193        if self.__uptodeg >= degbound:
     194            return self.__GB
     195        if not A.base().is_field():
     196            raise TypeError, "Currently, we can only compute Groebner bases if the ring of coefficients is a field"
     197        if self.side()!='twosided':
     198            raise TypeError, "This ideal is not two-sided. We can only compute two-sided Groebner bases"
     199        # Set the options required by letterplace
     200        from sage.libs.singular.option import LibSingularOptions
     201        libsingular_options = LibSingularOptions()
     202        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     203        libsingular_options['redTail'] = True
     204        libsingular_options['redSB'] = True
     205        A.set_degbound(degbound)
     206        P = A.current_ring()
     207        cdef FreeAlgebraElement_letterplace x
     208        out = [FreeAlgebraElement_letterplace(A,X,check=False) for X in
     209               singular_system("freegb",P.ideal([x._poly for x in self.__GB.gens()]),
     210                               degbound,A.ngens(), ring = P)]
     211        libsingular_options['redTail'] = bck[0]
     212        libsingular_options['redSB'] = bck[1]
     213        self.__GB = A.ideal(out,side='twosided',coerce=False)
     214        self.__uptodeg = degbound
     215        self.__GB.__uptodeg = degbound
     216        return self.__GB
     217
     218    def __contains__(self,x):
     219        """
     220        The containment test is based on a normal form computation.
     221
     222        EXAMPLES::
     223
     224            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     225            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     226            sage: x*I.0-I.1*y+I.0*y in I    # indirect doctest
     227            True
     228            sage: 1 in I
     229            False
     230
     231        """
     232        R = self.ring()
     233        return (x in R) and R(x).normal_form(self).is_zero()
     234
     235    def reduce(self, G):
     236        """
     237        Reduction of this ideal by another ideal,
     238        or normal form of an algebra element with respect to this ideal.
     239
     240        INPUT:
     241
     242        - ``G``: A list or tuple of elements, an ideal,
     243          the ambient algebra, or a single element.
     244
     245        OUTPUT:
     246
     247        - The normal form of ``G`` with respect to this ideal, if
     248          ``G`` is an element of the algebra.
     249        - The reduction of this ideal by the elements resp. generators
     250          of ``G``, if ``G`` is a list, tuple or ideal.
     251        - The zero ideal, if ``G`` is the algebra containing this ideal.
     252
     253        EXAMPLES::
     254
     255            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     256            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     257            sage: I.reduce(F)
     258            Twosided Ideal (0) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     259            sage: I.reduce(x^3)
     260            -y*z*x - y*z*y - y*z*z
     261            sage: I.reduce([x*y])
     262            Twosided Ideal (y*z, x*x - y*x - y*y) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     263            sage: I.reduce(F*[x^2+x*y,y^2+y*z]*F)
     264            Twosided Ideal (x*y + y*z, -y*x + y*z) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field
     265
     266        """
     267        P = self.ring()
     268        if not isinstance(G,(list,tuple)):
     269            if G==P:
     270                return P.ideal([P.zero_element()])
     271            if G in P:
     272                return G.normal_form(self)
     273            G = G.gens()
     274        C = P.current_ring()
     275        sI = C.ideal([C(X.letterplace_polynomial()) for X in self.gens()], coerce=False)
     276        selfdeg = max([x.degree() for x in sI.gens()])
     277        gI = P._reductor_(G, selfdeg)
     278        from sage.libs.singular.option import LibSingularOptions
     279        libsingular_options = LibSingularOptions()
     280        bck = (libsingular_options['redTail'],libsingular_options['redSB'])
     281        libsingular_options['redTail'] = True
     282        libsingular_options['redSB'] = True
     283        sI = poly_reduce(sI,gI, ring=C, attributes={gI:{"isSB":1}})
     284        libsingular_options['redTail'] = bck[0]
     285        libsingular_options['redSB'] = bck[1]
     286        return P.ideal([FreeAlgebraElement_letterplace(P,x,check=False) for x in sI], coerce=False)
  • sage/algebras/quatalg/quaternion_algebra.py

    diff --git a/sage/algebras/quatalg/quaternion_algebra.py b/sage/algebras/quatalg/quaternion_algebra.py
    a b  
    803803        """
    804804        return QuaternionOrder(self, basis, check=check)
    805805
    806     def ideal(self, gens, left_order=None, right_order=None, check=True):
     806    def ideal(self, gens, left_order=None, right_order=None, check=True, **kwds):
    807807        r"""
    808808        Return the quaternion ideal with given gens over `\ZZ`.
    809809        Neither a left or right order structure need be specified.
  • sage/categories/rings.py

    diff --git a/sage/categories/rings.py b/sage/categories/rings.py
    a b  
    1414from sage.categories.category import Category
    1515from category import HomCategory
    1616from sage.misc.cachefunc import cached_method
     17import sage
    1718
    1819class Rings(Category):
    1920    """
     
    8081            """
    8182            return x*y - y*x
    8283
     84        # this is already in sage.rings.ring.Ring,
     85        # but not all rings descend from that class,
     86        # e.g., matrix spaces.
     87        def _mul_(self, x, switch_sides=False):
     88            """
     89            Multiplication of rings with, e.g., lists.
     90
     91            NOTE:
     92
     93            This method is used to create ideals. It is
     94            the same as the multiplication method for
     95            :class:`~sage.rings.ring.Ring`. However, not
     96            all parents that belong to the category of
     97            rings also inherits from the base class of
     98            rings. Therefore, we implemented a ``__mul__``
     99            method for parents, that calls a ``_mul_``
     100            method implemented here. See trac ticket #7797.
     101
     102            INPUT:
     103
     104            - `x`, an object to multiply with.
     105            - `switch_sides` (optional bool): If ``False``,
     106              the product is ``self*x``; if ``True``, the
     107              product is ``x*self``.
     108
     109            EXAMPLE:
     110
     111            As we mentioned above, this method is called
     112            when a ring is involved that does not inherit
     113            from the base class of rings. This is the case,
     114            e.g., for matrix algebras::
     115
     116                sage: MS = MatrixSpace(QQ,2,2)
     117                sage: isinstance(MS,Ring)
     118                False
     119                sage: MS in Rings()
     120                True
     121                sage: MS*2     # indirect doctest
     122                Left Ideal
     123                (
     124                  [2 0]
     125                  [0 2]
     126                )
     127                 of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     128
     129            In the next example, the ring and the other factor switch sides
     130            in the product::
     131
     132                sage: [MS.2]*MS
     133                Right Ideal
     134                (
     135                  [0 0]
     136                  [1 0]
     137                )
     138                 of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     139
     140            AUTHOR:
     141
     142            - Simon King (2011-03-22)
     143
     144            """
     145            try:
     146                if self.is_commutative():
     147                    return self.ideal(x)
     148            except (AttributeError,NotImplementedError):
     149                pass
     150            try:
     151                side = x.side()
     152            except AttributeError:
     153                return self.ideal(x, side='right' if switch_sides else 'left')
     154            # presumably x is an ideal...
     155            try:
     156                x = x.gens()
     157            except (AttributeError, NotImplementedError):
     158                pass # ... not an ideal
     159            if switch_sides:
     160                if side in ['right','twosided']:
     161                    return self.ideal(x,side=side)
     162                elif side=='left':
     163                    return self.ideal(x,side='twosided')
     164            else:
     165                if side in ['left','twosided']:
     166                    return self.ideal(x,side=side)
     167                elif side=='right':
     168                    return self.ideal(x,side='twosided')
     169            # duck typing failed
     170            raise TypeError, "Don't know how to transform %s into an ideal of %s"%(x,self)
     171
     172        @cached_method
     173        def ideal_monoid(self):
     174            """
     175            The monoid of the ideals of this ring.
     176
     177            NOTE:
     178
     179            The code is copied from the base class of rings.
     180            This is since there are rings that do not inherit
     181            from that class, such as matrix algebras.  See
     182            trac ticket #7797.
     183
     184            EXAMPLE::
     185
     186                sage: MS = MatrixSpace(QQ,2,2)
     187                sage: isinstance(MS,Ring)
     188                False
     189                sage: MS in Rings()
     190                True
     191                sage: MS.ideal_monoid()
     192                Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices
     193                over Rational Field
     194
     195            Note that the monoid is cached::
     196
     197                sage: MS.ideal_monoid() is MS.ideal_monoid()
     198                True
     199
     200            """
     201            try:
     202                from sage.rings.ideal_monoid import IdealMonoid
     203                return IdealMonoid(self)
     204            except TypeError:
     205                from sage.rings.noncommutative_ideals import IdealMonoid_nc
     206                return IdealMonoid_nc(self)           
     207
     208        def ideal(self, *args, **kwds):
     209            """
     210            Create an ideal of this ring.
     211
     212            NOTE:
     213
     214            The code is copied from the base class
     215            :class:`~sage.rings.ring.Ring`. This is
     216            because there are rings that do not inherit
     217            from that class, such as matrix algebras.
     218            See trac ticket #7797.
     219
     220            INPUT:
     221
     222            - An element or a list/tuple/sequence of elements.
     223            - ``coerce`` (optional bool, default ``True``):
     224              First coerce the elements into this ring.
     225            - ``side``, optional string, one of ``"twosided"``
     226              (default), ``"left"``, ``"right"``: determines
     227              whether the resulting ideal is twosided, a left
     228              ideal or a right ideal.
     229
     230            EXAMPLE::
     231
     232                sage: MS = MatrixSpace(QQ,2,2)
     233                sage: isinstance(MS,Ring)
     234                False
     235                sage: MS in Rings()
     236                True
     237                sage: MS.ideal(2)
     238                Twosided Ideal
     239                (
     240                  [2 0]
     241                  [0 2]
     242                )
     243                 of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     244                sage: MS.ideal([MS.0,MS.1],side='right')
     245                Right Ideal
     246                (
     247                  [1 0]
     248                  [0 0],
     249                <BLANKLINE>
     250                  [0 1]
     251                  [0 0]
     252                )
     253                 of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     254
     255            """
     256            if kwds.has_key('coerce'):
     257                coerce = kwds['coerce']
     258                del kwds['coerce']
     259            else:
     260                coerce = True
     261
     262            from sage.rings.ideal import Ideal_generic
     263            from types import GeneratorType
     264            if len(args) == 0:
     265                gens = [self(0)]
     266            else:
     267                gens = args
     268                while isinstance(gens, (list, tuple, GeneratorType)) and len(gens) == 1:
     269                    first = gens[0]
     270                    if isinstance(first, Ideal_generic):
     271                        R = first.ring()
     272                        m = self.convert_map_from(R)
     273                        if m is not None:
     274                            gens = [m(g) for g in first.gens()]
     275                            coerce = False
     276                        else:
     277                            m = R.convert_map_from(self)
     278                            if m is not None:
     279                                raise NotImplementedError
     280                            else:
     281                                raise TypeError
     282                        break
     283                    elif isinstance(first, (list, tuple, GeneratorType)):
     284                        gens = first
     285                    else:
     286                        try:
     287                            if self.has_coerce_map_from(first):
     288                                gens = first.gens() # we have a ring as argument
     289                            elif hasattr(first,'parent'):
     290                                gens = [first]
     291                            else:
     292                                raise ArithmeticError, "There is no coercion from %s to %s"%(first,self)
     293                        except TypeError: # first may be a ring element
     294                            pass
     295                        break
     296            if coerce:
     297                gens = [self(g) for g in gens]
     298            from sage.categories.principal_ideal_domains import PrincipalIdealDomains
     299            if self in PrincipalIdealDomains():
     300                # Use GCD algorithm to obtain a principal ideal
     301                g = gens[0]
     302                if len(gens) == 1:
     303                    try:
     304                        g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
     305                    except (AttributeError, NotImplementedError):
     306                        pass
     307                else:
     308                    for h in gens[1:]:
     309                        g = g.gcd(h)
     310                gens = [g]
     311            if kwds.has_key('ideal_class'):
     312                C = kwds['ideal_class']
     313                del kwds['ideal_class']
     314            else:
     315                C = self._ideal_class_(len(gens))
     316            if len(gens) == 1 and isinstance(gens[0], (list, tuple)):
     317                gens = gens[0]
     318            return C(self, gens, **kwds)
     319
     320        def _ideal_class_(self,n=0):
     321            """
     322            Return the class that is used to implement ideals of this ring.
     323
     324            NOTE:
     325
     326            We copy the code from :class:`~sage.rings.ring.Ring`. This is
     327            necessary because not all rings inherit from that class, such
     328            as matrix algebras.
     329
     330            INPUT:
     331
     332            - ``n`` (optional integer, default 0): The number of generators
     333              of the ideal to be created.
     334
     335            OUTPUT:
     336
     337            The class that is used to implement ideals of this ring with
     338            ``n`` generators.
     339
     340            NOTE:
     341
     342            Often principal ideals (``n==1``) are implemented via a different
     343            class.
     344
     345            EXAMPLES::
     346
     347                sage: MS = MatrixSpace(QQ,2,2)
     348                sage: MS._ideal_class_()
     349                <class 'sage.rings.noncommutative_ideals.Ideal_nc'>
     350
     351            We don't know of a commutative ring in Sage that does not inherit
     352            from the base class of rings. So, we need to cheat in the next
     353            example::
     354
     355                sage: super(Ring,QQ)._ideal_class_.__module__
     356                'sage.categories.rings'
     357                sage: super(Ring,QQ)._ideal_class_()
     358                <class 'sage.rings.ideal.Ideal_generic'>
     359                sage: super(Ring,QQ)._ideal_class_(1)
     360                <class 'sage.rings.ideal.Ideal_principal'>
     361                sage: super(Ring,QQ)._ideal_class_(2)
     362                <class 'sage.rings.ideal.Ideal_generic'>
     363
     364            """
     365            from sage.rings.noncommutative_ideals import Ideal_nc
     366            try:
     367                if not self.is_commutative():
     368                    return Ideal_nc
     369            except (NotImplementedError,AttributeError):
     370                return Ideal_nc
     371            from sage.rings.ideal import Ideal_generic, Ideal_principal
     372            if n == 1:
     373                return Ideal_principal
     374            else:
     375                return Ideal_generic
     376
     377        ##
     378        # Quotient rings
     379        # Again, this is defined in sage.rings.ring.pyx
     380        def quotient(self, I, names=None):
     381            """
     382            Quotient of a ring by a two-sided ideal.
     383
     384            INPUT:
     385
     386            - ``I``: A twosided ideal of this ring.
     387            - ``names``: a list of strings to be used as names
     388              for the variables in the quotient ring.
     389
     390            EXAMPLES::
     391
     392                sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     393                sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     394                sage: F.quo(I)
     395                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)
     396                sage: Q = F.quotient(I)
     397                sage: Q.0
     398                xbar
     399                sage: Q.1
     400                ybar
     401                sage: Q.2
     402                zbar
     403                sage: Q.0*Q.1
     404                -ybar*zbar
     405
     406            """
     407            from sage.rings.quotient_ring import QuotientRing
     408            return QuotientRing(self, I, names=names)
     409        def quo(self, I, names=None):
     410            """
     411            Quotient of a ring by a two-sided ideal.
     412
     413            NOTE:
     414
     415            This is a synonyme for :meth:`quotient`.
     416
     417            EXAMPLE::
     418
     419                sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     420                sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     421                sage: Q = F.quo(I)
     422                sage: Q.0^4
     423                ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar
     424
     425            """
     426            return self.quotient(I,names=names)
     427        def quotient_ring(self, I, names=None):
     428            """
     429            Quotient of a ring by a two-sided ideal.
     430
     431            NOTE:
     432
     433            This is a synonyme for :meth:`quotient`.
     434
     435            EXAMPLE::
     436
     437                sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     438                sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     439                sage: Q = F.quo(I)
     440                sage: Q.0^4
     441                ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar
     442
     443            """
     444            return self.quotient(I,names=names)
     445
     446        def __div__(self, I):
     447            raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring."
     448
     449
    83450    class ElementMethods:
    84451        pass
    85452
  • sage/rings/ideal.py

    diff --git a/sage/rings/ideal.py b/sage/rings/ideal.py
    a b  
    11r"""
    2 Ideals
     2Ideals of commutative rings.
    33
    4 Sage provides functionality for computing with ideals. One can
    5 create an ideal in any commutative ring `R` by giving a
    6 list of generators, using the notation
    7 ``R.ideal([a,b,...])``.
     4Sage provides functionality for computing with ideals. One can create
     5an ideal in any commutative or non-commutative ring `R` by giving a
     6list of generators, using the notation ``R.ideal([a,b,...])``. The case
     7of non-commutative rings is implemented in :mod:`~sage.rings.noncommutative_ideals`.
     8
     9A more convenient notation may be ``R*[a,b,...]`` or ``[a,b,...]*R``.
     10If `R` is non-commutative, the former creates a left and the latter
     11a right ideal, and ``R*[a,b,...]*R`` creates a two-sided ideal.
    812"""
    913
    1014#*****************************************************************************
     
    244248        MonoidElement.__init__(self, ring.ideal_monoid())
    245249
    246250    def _repr_short(self):
    247         return '(%s)'%(', '.join([str(x) for x in self.gens()]))
    248        
     251        """
     252        Represent the list of generators.
     253
     254        EXAMPLE::
     255
     256            sage: P.<a,b,c> = QQ[]
     257            sage: P*[a^2,a*b+c,c^3]
     258            Ideal (a^2, a*b + c, c^3) of Multivariate Polynomial Ring in a, b, c over Rational Field
     259            sage: (P*[a^2,a*b+c,c^3])._repr_short()
     260            '(a^2, a*b + c, c^3)'
     261
     262        If the string representation of a generator contains a line break,
     263        the generators are not represented from left to right but from
     264        top to bottom. This is the case, e.g., for matrices::
     265
     266            sage: MS = MatrixSpace(QQ,2,2)
     267            sage: MS*[MS.1,2]
     268            Left Ideal
     269            (
     270              [0 1]
     271              [0 0],
     272            <BLANKLINE>
     273              [2 0]
     274              [0 2]
     275            )
     276             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     277
     278
     279        """
     280        L = []
     281        has_return = False
     282        for x in self.gens():
     283            s = repr(x)
     284            if '\n' in s:
     285                has_return = True
     286                s = s.replace('\n','\n  ')
     287            L.append(s)
     288        if has_return:
     289            return '\n(\n  %s\n)\n'%(',\n\n  '.join(L))
     290        return '(%s)'%(', '.join(L))
     291
    249292    def __repr__(self):
    250293        return "Ideal %s of %s"%(self._repr_short(), self.ring())
    251294
     
    827870        return self.ring().ideal(self.gens() + other.gens())
    828871
    829872    def __mul__(self, other):
     873        """
     874        EXAMPLE::
     875
     876            sage: P.<x,y,z> = QQ[]
     877            sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*P
     878            sage: I*2    # indirect doctest
     879            Ideal (2*x*y + 2*y*z, 2*x^2 - 2*y^2) of Multivariate Polynomial Ring in x, y, z over Rational Field
     880       
     881        """
    830882        if not isinstance(other, Ideal_generic):
     883            try:
     884                if self.ring().has_coerce_map_from(other):
     885                    return self
     886            except (TypeError,ArithmeticError,ValueError):
     887                pass
    831888            other = self.ring().ideal(other)
    832         return self.ring().ideal([x*y for x in self.gens() for y in other.gens()])
     889        return self.ring().ideal([z for z in [x*y for x in self.gens() for y in other.gens()] if z])
    833890
    834891    def __rmul__(self, other):
     892        """
     893        EXAMPLE::
     894
     895            sage: P.<x,y,z> = QQ[]
     896            sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*P
     897            sage: [2]*I    # indirect doctest
     898            Ideal (2*x*y + 2*y*z, 2*x^2 - 2*y^2) of Multivariate Polynomial Ring in x, y, z over Rational Field
     899
     900        """
    835901        if not isinstance(other, Ideal_generic):
     902            try:
     903                if self.ring().has_coerce_map_from(other):
     904                    return self
     905            except (TypeError,ArithmeticError,ValueError):
     906                pass
    836907            other = self.ring().ideal(other)
    837         return self.ring().ideal([y*x for x in self.gens() for y in other.gens()])
     908        return self.ring().ideal([z for z in [y*x for x in self.gens() for y in other.gens()] if z])
    838909
    839910    def norm(self):
    840911        """
  • new file sage/rings/noncommutative_ideals.pyx

    diff --git a/sage/rings/noncommutative_ideals.pyx b/sage/rings/noncommutative_ideals.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"""
     11Generic implementation of one- and twosided ideals of non-commutative rings.
     12
     13AUTHOR:
     14
     15- Simon King (2011-03-21), <simon.king@uni-jena.de>: Trac ticket #7797.
     16
     17EXAMPLES::
     18
     19    sage: MS = MatrixSpace(ZZ,2,2)
     20    sage: MS*MS([0,1,-2,3])
     21    Left Ideal
     22    (
     23      [ 0  1]
     24      [-2  3]
     25    )
     26     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     27    sage: MS([0,1,-2,3])*MS
     28    Right Ideal
     29    (
     30      [ 0  1]
     31      [-2  3]
     32    )
     33     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     34    sage: MS*MS([0,1,-2,3])*MS
     35    Twosided Ideal
     36    (
     37      [ 0  1]
     38      [-2  3]
     39    )
     40     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     41
     42See :mod:`~sage.algebras.letterplace.letterplace_ideal` for a more
     43elaborate implementation in the special case of ideals in free
     44algebras.
     45
     46TEST::
     47
     48    sage: A = SteenrodAlgebra(2)
     49    sage: IL = A*[A.1+A.2,A.1^2]; IL
     50    Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     51    sage: TestSuite(IL).run(skip=['_test_category'],verbose=True)
     52    running ._test_eq() . . . pass
     53    running ._test_not_implemented_methods() . . . pass
     54    running ._test_pickling() . . . pass
     55
     56"""
     57
     58from sage.rings.ideal_monoid import IdealMonoid_c
     59from sage.structure.parent import Parent
     60from sage.categories.monoids import Monoids
     61from sage.rings.ideal import Ideal_generic
     62import sage
     63
     64class IdealMonoid_nc(IdealMonoid_c):
     65    """
     66    Base class for the monoid of ideals over a non-commutative ring.
     67
     68    NOTE:
     69
     70    This class is essentially the same as :class:`~sage.rings.ideal_monoid.IdealMonoid_c`,
     71    but does not complain about non-commutative rings.
     72
     73    EXAMPLE::
     74
     75        sage: MS = MatrixSpace(ZZ,2,2)
     76        sage: MS.ideal_monoid()
     77        Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     78
     79    """
     80    def __init__(self, R):
     81        """
     82        INPUT:
     83
     84        A ring.
     85
     86        TEST::
     87
     88            sage: from sage.rings.noncommutative_ideals import IdealMonoid_nc
     89            sage: MS = MatrixSpace(ZZ,2,2)
     90            sage: IdealMonoid_nc(MS)
     91            Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     92
     93        """
     94        self._IdealMonoid_c__R = R
     95        Parent.__init__(self, base = sage.rings.integer_ring.ZZ,
     96                        category = Monoids())
     97        self._populate_coercion_lists_()
     98    def _element_constructor_(self, x):
     99        r"""
     100        Create an ideal in this monoid from ``x``.
     101
     102        INPUT:
     103
     104        An ideal, or a list of elements.
     105       
     106        TESTS::
     107       
     108            sage: A = SteenrodAlgebra(2)
     109            sage: IL = A*[A.1+A.2,A.1^2]; IL
     110            Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     111            sage: IR = [A.1+A.2,A.1^2]*A; IR
     112            Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     113            sage: IT = A*[A.1+A.2,A.1^2]*A; IT
     114            Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     115            sage: M = IL.parent()
     116            sage: M([A.0, 1])
     117            Twosided Ideal (Sq(1), Sq(0)) of mod 2 Steenrod algebra
     118
     119        ::
     120
     121            sage: IL == loads(dumps(IL))
     122            True
     123            sage: IR == loads(dumps(IR))
     124            True
     125            sage: IT == loads(dumps(IT))
     126            True
     127
     128        """
     129        side = "twosided"
     130        if isinstance(x, Ideal_nc):
     131            side = x.side()
     132            x = x.gens()
     133        elif isinstance(x, Ideal_generic):
     134            x = x.gens()
     135        y = self._IdealMonoid_c__R.ideal(x,side=side)
     136        y._set_parent(self)
     137        return y
     138
     139class Ideal_nc(Ideal_generic):
     140    """
     141    Generic non-commutative ideal.
     142
     143    All fancy stuff such as the computation of Groebner bases must be
     144    implemented in sub-classes. See :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`
     145    for an example.
     146
     147    EXAMPLE::
     148
     149        sage: MS = MatrixSpace(QQ,2,2)
     150        sage: I = MS*[MS.1,MS.2]; I
     151        Left Ideal
     152        (
     153          [0 1]
     154          [0 0],
     155        <BLANKLINE>
     156          [0 0]
     157          [1 0]
     158        )
     159         of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     160        sage: [MS.1,MS.2]*MS
     161        Right Ideal
     162        (
     163          [0 1]
     164          [0 0],
     165        <BLANKLINE>
     166          [0 0]
     167          [1 0]
     168        )
     169         of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     170        sage: MS*[MS.1,MS.2]*MS
     171        Twosided Ideal
     172        (
     173          [0 1]
     174          [0 0],
     175        <BLANKLINE>
     176          [0 0]
     177          [1 0]
     178        )
     179         of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     180
     181    """
     182    def __init__(self, ring, gens, coerce=True, side = "twosided"):
     183        """
     184        INPUT:
     185
     186        - A ring.
     187        - A list or tuple of elements.
     188        - ``coerce`` (optional bool, default ``True``): First coerce the given
     189          list of elements into the given ring.
     190        - ``side`` (option string, default ``"twosided"``): Must be ``"left"``,
     191          ``"right"`` or ``"twosided"``. Determines whether the ideal is a left,
     192          right or twosided ideal.
     193
     194        TEST::
     195
     196            sage: MS = MatrixSpace(ZZ,2,2)
     197            sage: from sage.rings.noncommutative_ideals import Ideal_nc
     198            sage: Ideal_nc(MS,[MS.1,MS.2], side='left')
     199            Left Ideal
     200            (
     201              [0 1]
     202              [0 0],
     203            <BLANKLINE>
     204              [0 0]
     205              [1 0]
     206            )
     207             of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     208            sage: Ideal_nc(MS,[MS.1,MS.2], side='right')
     209            Right Ideal
     210            (
     211              [0 1]
     212              [0 0],
     213            <BLANKLINE>
     214              [0 0]
     215              [1 0]
     216            )
     217             of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
     218
     219        """
     220        if side not in ['left','right','twosided']:
     221            raise ValueError, "Ideals are left, right or twosided, but not %s"%side
     222        self.__side = side
     223        Ideal_generic.__init__(self, ring, gens, coerce=coerce)
     224    def __repr__(self):
     225        """
     226        TEST::
     227
     228            sage: A = SteenrodAlgebra(2)
     229            sage: A*[A.1+A.2,A.1^2]      # indirect doctest
     230            Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     231            sage: [A.1+A.2,A.1^2]*A
     232            Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     233            sage: A*[A.1+A.2,A.1^2]*A
     234            Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     235
     236        """
     237        return "%s Ideal %s of %s"%(self.__side.capitalize(),self._repr_short(),self.ring())
     238
     239    def __cmp__(self, right):
     240        """
     241        Ideals of different sidedness do not compare equal. Apart from
     242        that, the generators are compared.
     243
     244        EXAMPLE::
     245
     246             sage: A = SteenrodAlgebra(2)
     247             sage: IR = [A.1+A.2,A.1^2]*A
     248             sage: IL = A*[A.1+A.2,A.1^2]
     249             sage: IT = A*[A.1+A.2,A.1^2]*A
     250             sage: IT==IL
     251             False
     252             sage: IR==[A.1+A.2,A.1^2]*A
     253             True
     254
     255        """
     256        if not isinstance(right,Ideal_nc):
     257            return -1
     258        c = cmp(self.side(),right.side())
     259        if c: return c
     260        S = set(self.gens())
     261        T = set(right.gens())
     262        if S == T:
     263            return 0
     264        return cmp(self.gens(), right.gens())
     265
     266    def side(self):
     267        """
     268        Return a string that describes the sidedness of this ideal.
     269
     270        EXAMPLES::
     271
     272            sage: A = SteenrodAlgebra(2)
     273            sage: IL = A*[A.1+A.2,A.1^2]
     274            sage: IR = [A.1+A.2,A.1^2]*A
     275            sage: IT = A*[A.1+A.2,A.1^2]*A
     276            sage: IL.side()
     277            'left'
     278            sage: IR.side()
     279            'right'
     280            sage: IT.side()
     281            'twosided'
     282
     283        """
     284        return self.__side
     285    def __mul__(self, other):
     286        """
     287        Multiplication of a one-sided ideal with its ring from the other side
     288        yields a two-sided ideal.
     289
     290        TESTS::
     291
     292            sage: MS = MatrixSpace(QQ,2,2)
     293            sage: IL = MS*[2*MS.0,3*MS.1]; IL
     294            Left Ideal
     295            (
     296              [2 0]
     297              [0 0],
     298            <BLANKLINE>
     299              [0 3]
     300              [0 0]
     301            )
     302             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     303            sage: IR = MS.3*MS; IR
     304            Right Ideal
     305            (
     306              [0 0]
     307              [0 1]
     308            )
     309             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     310            sage: IL*MS     # indirect doctest
     311            Twosided Ideal
     312            (
     313              [2 0]
     314              [0 0],
     315            <BLANKLINE>
     316              [0 3]
     317              [0 0]
     318            )
     319             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     320            sage: IR*IR
     321            Traceback (most recent call last):
     322            ...
     323            NotImplementedError: Can not multiply non-commutative ideals.
     324
     325        """
     326        if not isinstance(other, Ideal_nc):
     327            # Perhaps other is a ring and thus has its own
     328            # multiplication.
     329            if other == self.ring():
     330                if self.side()=='right':
     331                    return self
     332                return self.ring().ideal(self.gens(),side='twosided')
     333        if not isinstance(self, Ideal_nc):
     334            # This may happen...
     335            if self == other.ring():
     336                if other.side()=='left':
     337                    return other
     338                return other.ring().ideal(other.gens(),side='twosided')
     339        raise NotImplementedError, "Can not multiply non-commutative ideals."
  • sage/rings/polynomial/multi_polynomial_libsingular.pyx

    diff --git a/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/sage/rings/polynomial/multi_polynomial_libsingular.pyx
    a b  
    12651265            True
    12661266        """
    12671267        return sage.rings.polynomial.multi_polynomial_libsingular.unpickle_MPolynomialRing_libsingular, ( self.base_ring(),
    1268                                                                                                map(str, self.gens()),
     1268#                                                                                               map(str, self.gens()),
     1269                                                                                                         self.variable_names(),
    12691270                                                                                               self.term_order() )
    12701271
    12711272    def __temporarily_change_names(self, names, latex_names):
  • sage/rings/quotient_ring.py

    diff --git a/sage/rings/quotient_ring.py b/sage/rings/quotient_ring.py
    a b  
    33
    44AUTHORS:
    55
    6 - William Stein
     6- William Stein: Quotients of commutative rings.
     7- Simon King (2011-03-23): Quotients of non-commutative rings by
     8  twosided ideals.
    79
    810TESTS::
    911
     
    1214    sage: S = R.quotient_ring(I);
    1315    sage: S == loads(dumps(S))
    1416    True
     17
     18In trac ticket #7797, non-commutative quotient rings `R/I` were
     19implemented.  The only requirement is that the two-sided ideal `I`
     20provides a ``reduce`` method so that ``I.reduce(x)`` is the normal
     21form of an element `x` with respect to `I` (i.e., we have
     22``I.reduce(x)==I.reduce(y)`` if `x-y\\in I`, and
     23``x-I.reduce(x) in I``)::
     24
     25    sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     26    sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     27    sage: Q = F.quo(I)
     28    sage: Q.0^4
     29    ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar
     30    sage: Q.1*x
     31    ybar*xbar
     32    sage: x*Q.1
     33    -ybar*zbar
     34
    1535"""
    1636
    1737###########################################################################
     
    2747
    2848import quotient_ring_element
    2949import sage.misc.latex as latex
    30 import commutative_ring
     50import commutative_ring, ring
    3151import ideal
    3252import sage.rings.polynomial.multi_polynomial_ideal
    3353import sage.structure.parent_gens
    3454from sage.interfaces.all import singular as singular_default, is_SingularElement
     55from sage.categories.rings import Rings
    3556
    3657def QuotientRing(R, I, names=None):
    3758    r"""
    38     Creates a quotient ring of the ring `R` by the ideal `I`. Variables are
    39     labeled by ``names`` (if the quotient ring is a quotient of a
    40     polynomial ring).  If ``names`` isn't given, 'bar' will be appended to
    41     the variable names in `R`.
     59    Creates a quotient ring of the ring `R` by the twosided ideal `I`.
     60
     61    Variables are labeled by ``names`` (if the quotient ring is a quotient
     62    of a polynomial ring).  If ``names`` isn't given, 'bar' will be appended
     63    to the variable names in `R`.
    4264   
    4365    INPUTS:
    4466
    45     - ``R`` - a commutative ring
     67    - ``R`` - a ring.
    4668
    47     - ``I`` - an ideal of R
     69    - ``I`` - a twosided ideal of `R`.
    4870
    49     - ``names`` - a list of
    50       strings to be used as names for the variables in the quotient ring
    51       `R/I`
     71    - ``names`` - a list of strings to be used as names for the variables in
     72      the quotient ring `R/I`.
    5273   
    53     OUTPUTS: `R/I` - the quotient ring `R` mod the ideal `I`
    54    
     74    OUTPUTS: `R/I` - the quotient ring `R` mod the ideal `I`.
     75
     76    ASSUMPTION:
     77
     78    ``I`` has an attribute ``I.reduce(x)`` returning the normal form
     79    of elements `x\in R`. In other words, it is required that
     80    ``I.reduce(x)==I.reduce(y)`` `\iff x-y \in I`, and
     81    ``x-I.reduce(x) in I``, for all `x,y\in R`.
     82
    5583    EXAMPLES:
    5684
    5785    Some simple quotient rings with the integers::
     
    125153        d
    126154        -1
    127155        -d
     156
     157    Here is an example of the quotient of a free algebra by a
     158    twosided homogeneous ideal (see trac ticket #7797)::
     159
     160        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     161        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     162        sage: Q.<a,b,c> = F.quo(I); Q
     163        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)
     164        sage: a*b
     165        -b*c
     166        sage: a^3
     167        -b*c*a - b*c*b - b*c*c
     168        sage: J = Q*[a^3-b^3]*Q
     169        sage: R.<i,j,k> = Q.quo(J); R
     170        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)
     171        sage: i^3
     172        -j*k*i - j*k*j - j*k*k
     173        sage: j^3
     174        -j*k*i - j*k*j - j*k*k
     175
    128176    """
    129     if not isinstance(R, commutative_ring.CommutativeRing):
    130         raise TypeError, "R must be a commutative ring."
     177    # 1. Not all rings inherit from the base class of rings.
     178    # 2. We want to support quotients of free algebras by homogeneous two-sided ideals.
     179    #if not isinstance(R, commutative_ring.CommutativeRing):
     180    #    raise TypeError, "R must be a commutative ring."
     181    if not R in Rings():
     182        raise TypeError, "R must be a ring."
     183    try:
     184        is_commutative = R.is_commutative()
     185    except (AttributeError, NotImplementedError):
     186        is_commutative = False
    131187    if names is None:
    132188        names = tuple([x + 'bar' for x in R.variable_names()])
    133189    else:
     
    139195            return R.quotient_by_principal_ideal(I.gen(), names)
    140196    except (AttributeError, NotImplementedError):
    141197        pass
    142     if isinstance(R, QuotientRing_generic):
     198    if not is_commutative:
     199        try:
     200            if I.side() != 'twosided':
     201                raise AttributeError
     202        except AttributeError:
     203            raise TypeError, "A twosided ideal is required."
     204    if isinstance(R, QuotientRing_nc):
    143205        pi = R.cover()
    144206        S = pi.domain()
    145207        G = [pi.lift(x) for x in I.gens()]
    146208        I_lift = S.ideal(G)
    147209        J = R.defining_ideal()
    148         return QuotientRing_generic(S, I_lift + J, names)
     210        return R.__class__(S, I_lift + J, names)
    149211    if R.is_field():
    150212        if I.is_zero():
    151213            return R
    152214        else:
    153215            return 0
    154     return QuotientRing_generic(R, I, names)
     216    if isinstance(R, sage.rings.commutative_ring.CommutativeRing):
     217        return QuotientRing_generic(R, I, names)
     218    return QuotientRing_nc(R, I, names)
    155219
    156220def is_QuotientRing(x):
    157221    """
    158     Tests whether or not ``x`` inherits from :class:`QuotientRing_generic`.
     222    Tests whether or not ``x`` inherits from :class:`QuotientRing_nc`.
    159223   
    160224    EXAMPLES::
    161225   
     
    167231        True
    168232        sage: is_QuotientRing(R)
    169233        False
     234
     235    ::
     236
     237        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     238        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     239        sage: Q = F.quo(I)
     240        sage: is_QuotientRing(Q)
     241        True
     242        sage: is_QuotientRing(F)
     243        False
     244
    170245    """
    171     return isinstance(x, QuotientRing_generic)
     246    return isinstance(x, QuotientRing_nc)
    172247   
    173 class QuotientRing_generic(commutative_ring.CommutativeRing, sage.structure.parent_gens.ParentWithGens):
     248class QuotientRing_nc(ring.Ring, sage.structure.parent_gens.ParentWithGens):
    174249    """
    175     The quotient ring of `R` by the ideal `I`.
     250    The quotient ring of a ring `R` by a twosided ideal `I`.
    176251   
    177     EXAMPLES::
     252    EXAMPLES:
     253
     254    This class is for rings that do not inherit from :class:`~sage.rings.ring.CommutativeRing`.
     255    Here is a quotient of a free algebra by a twosided homogeneous ideal::
     256
     257        sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     258        sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     259        sage: Q.<a,b,c> = F.quo(I); Q
     260        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)
     261        sage: a*b
     262        -b*c
     263        sage: a^3
     264        -b*c*a - b*c*b - b*c*c
     265
     266    A quotient of a quotient is just the quotient of the original top
     267    ring by the sum of two ideals::
     268
     269        sage: J = Q*[a^3-b^3]*Q
     270        sage: R.<i,j,k> = Q.quo(J); R
     271        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)
     272        sage: i^3
     273        -j*k*i - j*k*j - j*k*k
     274        sage: j^3
     275        -j*k*i - j*k*j - j*k*k
     276
     277    For rings that *do* inherit from :class:`~sage.rings.ring.CommutativeRing`, we provide
     278    a subclass :class:`QuotientRing_generic`, for backwards compatibility::
    178279   
    179280        sage: R.<x> = PolynomialRing(ZZ,'x')
    180281        sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
     
    190291        sage: S(0) == a^2 + b^2
    191292        True
    192293   
    193     EXAMPLE: Quotient of quotient
    194    
    195     A quotient of a quotient is just the quotient of the original top
     294    Again, a quotient of a quotient is just the quotient of the original top
    196295    ring by the sum of two ideals.
    197296   
    198297    ::
     
    207306    """
    208307    def __init__(self, R, I, names):
    209308        """
    210         Create the quotient ring of `R` by the ideal `I`.
     309        Create the quotient ring of `R` by the twosided ideal `I`.
    211310       
    212311        INPUT:
    213312       
    214        
    215         -  ``R`` - a commutative ring
    216        
    217         -  ``I`` - an ideal
     313        -  ``R`` - a ring.
     314        -  ``I`` - a twosided ideal of `R`.
     315        - ``names`` - a list of generator names.
    218316       
    219317       
    220318        EXAMPLES::
    221319       
    222             sage: R.<x,y> = PolynomialRing(QQ)
    223             sage: R.quotient_ring(x^2 + y^2)
    224             Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2)
     320            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     321            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     322            sage: Q.<a,b,c> = F.quo(I); Q
     323            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)
     324            sage: a*b
     325            -b*c
     326            sage: a^3
     327            -b*c*a - b*c*b - b*c*c
     328
    225329        """
    226330        self.__R = R
    227331        self.__I = I
     
    236340            sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
    237341            sage: R.quotient_ring(I).construction()
    238342            (QuotientFunctor, Univariate Polynomial Ring in x over Integer Ring)
    239        
     343            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     344            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     345            sage: Q = F.quo(I)
     346            sage: Q.construction()
     347            (QuotientFunctor, Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field)
     348
    240349        TESTS::
    241350       
    242351            sage: F, R = Integers(5).construction()
     
    273382        """
    274383        return "%s/%s"%(latex.latex(self.cover_ring()), latex.latex(self.defining_ideal()))
    275384
     385    def is_commutative(self):
     386        """
     387        Tell whether this quotient ring is commutative.
     388
     389        NOTE:
     390
     391        This is certainly the case if the cover ring is commutative.
     392        Otherwise, if this ring has a finite number of generators, it
     393        is tested whether they commute. If the number of generators is
     394        infinite, a ``NotImplementedError`` is raised.
     395
     396        AUTHOR:
     397
     398        - Simon King (2011-03-23): See trac ticket #7797.
     399
     400        EXAMPLES:
     401
     402        Any quotient of a commutative ring is commutative::
     403
     404            sage: P.<a,b,c> = QQ[]
     405            sage: P.quo(P.random_element()).is_commutative()
     406            True
     407
     408        The non-commutative case is more interesting::
     409
     410            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     411            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     412            sage: Q = F.quo(I)
     413            sage: Q.is_commutative()
     414            False
     415            sage: Q.1*Q.2==Q.2*Q.1
     416            False
     417
     418        In the next example, the generators apparently commute::
     419
     420            sage: J = F*[x*y-y*x,x*z-z*x,y*z-z*y,x^3-y^3]*F
     421            sage: R = F.quo(J)
     422            sage: R.is_commutative()
     423            True
     424           
     425
     426        """
     427        try:
     428            if self.__R.is_commutative():
     429                return True
     430        except (AttributeError, NotImplementedError):
     431            pass
     432        from sage.all import Infinity
     433        if self.ngens() == Infinity:
     434            raise NotImplementedError, "This quotient ring has an infinite number of generators."
     435        for i in xrange(self.ngens()):
     436            gi = self.gen(i)
     437            for j in xrange(i+1,self.ngens()):
     438                gj = self.gen(j)
     439                if gi*gj!=gj*gi:
     440                    return False
     441        return True
     442
    276443    def cover(self):
    277444        r"""
    278445        The covering ring homomorphism `R \to R/I`, equipped with a
     
    632799            sage: R.quotient_ring(x^2 + y^2) == R.quotient_ring(-x^2 - y^2)
    633800            False
    634801        """
    635         if not isinstance(other, QuotientRing_generic):
     802        if not isinstance(other, QuotientRing_nc):
    636803            return cmp(type(self), type(other))
    637804        return cmp((self.cover_ring(), self.defining_ideal().gens()),
    638805                   (other.cover_ring(), other.defining_ideal().gens()))
     
    806973            Degree reverse lexicographic term order
    807974        """
    808975        return self.__R.term_order()
     976
     977class QuotientRing_generic(QuotientRing_nc, sage.rings.commutative_ring.CommutativeRing):
     978    r"""
     979    Creates a quotient ring of a *commutative* ring `R` by the ideal `I`.
     980
     981    EXAMPLE::
     982
     983        sage: R.<x> = PolynomialRing(ZZ)
     984        sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
     985        sage: S = R.quotient_ring(I); S
     986        Quotient of Univariate Polynomial Ring in x over Integer Ring by the ideal (x^2 + 3*x + 4, x^2 + 1)       
     987
     988    """
     989    def __init__(self, R, I, names):
     990        """
     991        INPUT:
     992       
     993        -  ``R`` - a ring that is of type <sage.rings.ring.CommutativeRing>.
     994        -  ``I`` - an ideal of `R`.
     995        - ``names`` - a list of generator names.
     996
     997        TEST::
     998
     999            sage: isinstance(ZZ.quo(2), sage.rings.ring.CommutativeRing)  # indirect doctest
     1000            True
     1001
     1002        """
     1003        if not isinstance(R, sage.rings.commutative_ring.CommutativeRing):
     1004            raise TypeError, "This class is for quotients of commutative rings only.\n    For non-commutative rings, use <sage.rings.quotient_ring.QuotientRing_nc>"
     1005        QuotientRing_nc.__init__(self, R, I, names)
  • 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        """
    525         if self.__rep == other.__rep or ((self.__rep - other.__rep) in self.parent().defining_ideal()):
     534        I = self.parent().defining_ideal()
     535        if self.__rep == other.__rep or I.reduce(self.__rep)==I.reduce(other.__rep):#((self.__rep - other.__rep) in self.parent().defining_ideal()):
    526536            return 0
    527537        return cmp(self.__rep, other.__rep)
    528538
  • sage/rings/ring.pxd

    diff --git a/sage/rings/ring.pxd b/sage/rings/ring.pxd
    a b  
    66    cdef public object _zero_ideal
    77    cdef public object _unit_ideal
    88    cdef _an_element_c_impl(self)
     9    cdef public object __ideal_monoid
    910
    1011cdef class CommutativeRing(Ring):
    1112    cdef public object __fraction_field
    12     cdef public object __ideal_monoid
    1313
    1414cdef class IntegralDomain(CommutativeRing):
    1515    pass
  • sage/rings/ring.pyx

    diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx
    a b  
    4141- David Harvey (2006-10-16): changed :class:`CommutativeAlgebra` to derive from
    4242  :class:`CommutativeRing` instead of from :class:`Algebra`
    4343- David Loeffler (2009-07-09): documentation fixes, added to reference manual
     44- Simon King (2011-03-22): Modify multiplication and _ideal_class_ to support
     45  ideals of non-commutative rings.
    4446"""
    4547
    4648#*****************************************************************************
     
    289291        from sage.categories.rings import Rings
    290292        return Rings()
    291293
     294    def ideal_monoid(self):
     295        """
     296        Return the monoid of ideals of this ring.
     297
     298        EXAMPLES::
     299
     300            sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
     301            sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
     302            sage: Q = F.quo(I)
     303            sage: Q.ideal_monoid()
     304            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)
     305
     306        """
     307        if self.__ideal_monoid is not None:
     308            return self.__ideal_monoid
     309        else:
     310            from sage.rings.noncommutative_ideals import IdealMonoid_nc
     311            M = IdealMonoid_nc(self)
     312            self.__ideal_monoid = M
     313            return M
     314
    292315    def ideal(self, *args, **kwds):
    293316        """
    294317        Return the ideal defined by x, i.e., generated by x.
     
    301324          already in the ring.
    302325        - ``ideal_class`` -- callable (default: self._ideal_class_()); this must be a keyword argument.
    303326          A constructor for ideals, taking the ring as the first argument and then the generators.
    304           Usually a subclass of sage.rings.ideal.Ideal_generic.
    305 
    306         TODO: For noncommutative rings, distinguish between ideals, right
    307         ideals, and left ideals.
     327          Usually a subclass of :class:`~sage.rings.ideal.Ideal_generic` or
     328          :class:`~sage.rings.noncommutative_ideals.Ideal_nc`.
     329        - Further named arguments (such as ``side`` in the case of non-commutative
     330          rings) are forwarded to the ideal class.
    308331
    309332        EXAMPLES::
    310333
     
    315338            Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field
    316339            sage: R.ideal( [x^3,y^3+x^3] )
    317340            Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field
     341           
     342        ::
     343
     344            sage: A = SteenrodAlgebra(2)
     345            sage: A.ideal(A.1,A.2^2)
     346            Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra
     347            sage: A.ideal(A.1,A.2^2,side='left')
     348            Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra
     349
    318350        """
    319351        if kwds.has_key('coerce'):
    320352            coerce = kwds['coerce']
     
    344376                    break
    345377                elif isinstance(first, (list, tuple, GeneratorType)):
    346378                    gens = first
     379                elif self.has_coerce_map_from(first):
     380                    gens = first.gens() # we have a ring as argument
    347381                else:
    348382                    break
    349383        if coerce:
     
    375409        Return the ideal x*R generated by x, where x is either an element
    376410        or tuple or list of elements.
    377411
    378         TODO: For noncommutative rings, distinguish between ideals, right
    379         ideals, and left ideals.
    380 
    381412        EXAMPLES::
    382413
    383414            sage: R.<x,y,z> = GF(7)[]
     
    385416            Ideal (x + y) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 7
    386417            sage: (x+y,z+y^3)*R
    387418            Ideal (x + y, y^3 + z) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 7
     419
     420        The following was implemented in trac ticket #7797::
     421
     422            sage: A = SteenrodAlgebra(2)
     423            sage: A*[A.1+A.2,A.1^2]
     424            Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     425            sage: [A.1+A.2,A.1^2]*A
     426            Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     427            sage: A*[A.1+A.2,A.1^2]*A
     428            Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra
     429
    388430        """
    389431        if isinstance(self, Ring):
    390             return self.ideal(x)
    391         else:
    392             return x.ideal(self)    # switched because this is a Cython / extension class
     432            if self.is_commutative():
     433                return self.ideal(x)
     434            try:
     435                side = x.side()
     436            except AttributeError:
     437                return self.ideal(x, side='left')
     438            # presumably x is an ideal...
     439            try:
     440                x = x.gens()
     441            except (AttributeError, NotImplementedError):
     442                pass # ... not an ideal
     443            if side in ['left','twosided']:
     444                return self.ideal(x,side=side)
     445            elif side=='right':
     446                return self.ideal(x,side='twosided')
     447            else: # duck typing failed
     448                raise TypeError, "Don't know how to transform %s into an ideal of %s"%(x,self)
     449        else: # the sides are switched because this is a Cython / extension class
     450            if x.is_commutative():
     451                return x.ideal(self)
     452            try:
     453                side = self.side()
     454            except AttributeError:
     455                return x.ideal(self, side='right')
     456            # presumably self is an ideal...
     457            try:
     458                self = self.gens()
     459            except (AttributeError, NotImplementedError):
     460                pass # ... not an ideal
     461            if side in ['right','twosided']:
     462                return x.ideal(self,side='twosided')
     463            elif side=='left':
     464                return x.ideal(self,side='twosided')
     465            else:
     466                raise TypeError, "Don't know how to transform %s into an ideal of %s"%(self,x)
    393467
    394468    def _ideal_class_(self, n=0):
    395469        r"""
     
    402476        The default input of `n=0` indicates an unspecified number of generators,
    403477        in which case a class that works for any number of generators is returned.
    404478
    405         TODO: For noncommutative rings, distinguish between ideals, right
    406         ideals, and left ideals.
    407 
    408479        EXAMPLES::
    409480
    410481            sage: R.<x,y> = GF(5)[]
     
    418489
    419490            sage: RR._ideal_class_()
    420491            <class 'sage.rings.ideal.Ideal_pid'>
     492
     493        Since #7797, non-commutative rings have ideals as well::
     494
     495            sage: A = SteenrodAlgebra(2)
     496            sage: A._ideal_class_()
     497            <class 'sage.rings.noncommutative_ideals.Ideal_nc'>
     498
    421499        """
    422500        # One might need more than just n, but I can't think of an example.
     501        from sage.rings.noncommutative_ideals import Ideal_nc
     502        try:
     503            if not self.is_commutative():
     504                return Ideal_nc
     505        except (NotImplementedError, AttributeError):
     506            return Ideal_nc
    423507        from sage.rings.ideal import Ideal_generic, Ideal_principal
    424508        if n == 1:
    425509            return Ideal_principal
     
    480564            return I
    481565        return self._zero_ideal
    482566
     567    def quotient(self, I, names=None):
     568        """
     569        Create the quotient of this ring by a twosided ideal I.
     570
     571        INPUT:
     572       
     573        - ``I`` -- a twosided ideal of this ring, `R`.
     574        - ``names`` -- (optional) names of the generators of the quotient (if
     575          there are multiple generators, you can specify a single character
     576          string and the generators are named in sequence starting with 0).
     577
     578        EXAMPLES::
     579
     580            sage: R.<x> = PolynomialRing(ZZ)
     581            sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
     582            sage: S = R.quotient(I, 'a')
     583            sage: S.gens()
     584            (a,)
     585
     586            sage: R.<x,y> = PolynomialRing(QQ,2)
     587            sage: S.<a,b> = R.quotient((x^2, y))
     588            sage: S
     589            Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y)
     590            sage: S.gens()
     591            (a, 0)
     592            sage: a == b
     593            False
     594        """
     595        import sage.rings.quotient_ring
     596        return sage.rings.quotient_ring.QuotientRing(self, I, names=names)
     597
     598    def quo(self, I, names=None):
     599        """
     600        Create the quotient of R by the ideal I.  This is a synonym for
     601        :meth:`.quotient`
     602
     603        EXAMPLES::
     604
     605            sage: R.<x,y> = PolynomialRing(QQ,2)
     606            sage: S.<a,b> = R.quo((x^2, y))
     607            sage: S
     608            Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y)
     609            sage: S.gens()
     610            (a, 0)
     611            sage: a == b
     612            False
     613        """
     614        return self.quotient(I, names=names)
     615       
     616    def __div__(self, I):
     617        """
     618        Dividing one ring by another is not supported because there is no good
     619        way to specify generator names.
     620
     621        EXAMPLES::
     622
     623            sage: QQ / ZZ
     624            Traceback (most recent call last):
     625            ...
     626            TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring.
     627        """
     628        raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring."
     629        #return self.quotient(I, names=None)
     630
     631    def quotient_ring(self, I, names=None):
     632        """
     633        Return the quotient of self by the ideal I of self.
     634        (Synonym for self.quotient(I).)
     635
     636        INPUT:
     637
     638        - ``self`` -- a ring R
     639        - ``I`` -- an ideal of R
     640        - ``names`` -- (optional) names of the generators of the quotient. (If
     641          there are multiple generators, you can specify a single character
     642          string and the generators are named in sequence starting with 0.)
     643
     644        OUTPUT:
     645
     646        - R/I -- the quotient ring of R by the ideal I
     647       
     648        EXAMPLES::
     649
     650            sage: R.<x> = PolynomialRing(ZZ)
     651            sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
     652            sage: S = R.quotient_ring(I, 'a')
     653            sage: S.gens()
     654            (a,)
     655
     656            sage: R.<x,y> = PolynomialRing(QQ,2)
     657            sage: S.<a,b> = R.quotient_ring((x^2, y))
     658            sage: S
     659            Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y)
     660            sage: S.gens()
     661            (a, 0)
     662            sage: a == b
     663            False
     664        """
     665        return self.quotient(I, names)
     666
    483667    def zero_element(self):
    484668        """
    485669        Return the zero element of this ring (cached).
     
    649833
    650834        EXAMPLES::
    651835
    652             sage: QQ.is_exact()
     836            sage: QQ.is_exact()    # indirect doctest
    653837            True
    654838            sage: ZZ.is_exact()
    655839            True
     
    11861370            self.__ideal_monoid = M
    11871371            return M
    11881372
    1189     def quotient(self, I, names=None):
    1190         """
    1191         Create the quotient of R by the ideal I.
    1192 
    1193         INPUT:
    1194        
    1195         - ``R`` -- a commutative ring
    1196         - ``I`` -- an ideal of R
    1197         - ``names`` -- (optional) names of the generators of the quotient (if
    1198           there are multiple generators, you can specify a single character
    1199           string and the generators are named in sequence starting with 0).
    1200 
    1201         EXAMPLES::
    1202 
    1203             sage: R.<x> = PolynomialRing(ZZ)
    1204             sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
    1205             sage: S = R.quotient(I, 'a')
    1206             sage: S.gens()
    1207             (a,)
    1208 
    1209             sage: R.<x,y> = PolynomialRing(QQ,2)
    1210             sage: S.<a,b> = R.quotient((x^2, y))
    1211             sage: S
    1212             Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y)
    1213             sage: S.gens()
    1214             (a, 0)
    1215             sage: a == b
    1216             False
    1217         """
    1218         import sage.rings.quotient_ring
    1219         return sage.rings.quotient_ring.QuotientRing(self, I, names=names)
    1220 
    1221     def quo(self, I, names=None):
    1222         """
    1223         Create the quotient of R by the ideal I.  This is a synonym for
    1224         :meth:`.quotient`
    1225 
    1226         EXAMPLES::
    1227 
    1228             sage: R.<x,y> = PolynomialRing(QQ,2)
    1229             sage: S.<a,b> = R.quo((x^2, y))
    1230             sage: S
    1231             Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y)
    1232             sage: S.gens()
    1233             (a, 0)
    1234             sage: a == b
    1235             False
    1236         """
    1237         return self.quotient(I, names=names)
    1238        
    12391373    def extension(self, poly, name=None, names=None, embedding=None):
    12401374        """
    12411375        Algebraically extends self by taking the quotient self[x] / (f(x)).
     
    12761410        R = self[name]
    12771411        I = R.ideal(R(poly.list()))
    12781412        return R.quotient(I, name)
    1279 
    1280     def __div__(self, I):
    1281         """
    1282         Dividing one ring by another is not supported because there is no good
    1283         way to specify generator names.
    1284 
    1285         EXAMPLES::
    1286 
    1287             sage: QQ / ZZ
    1288             Traceback (most recent call last):
    1289             ...
    1290             TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring.
    1291         """
    1292         raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring."
    1293         #return self.quotient(I, names=None)
    1294 
    1295     def quotient_ring(self, I, names=None):
    1296         """
    1297         Return the quotient of self by the ideal I of self.
    1298         (Synonym for self.quotient(I).)
    1299 
    1300         INPUT:
    1301 
    1302         - ``self`` -- a ring R
    1303         - ``I`` -- an ideal of R
    1304         - ``names`` -- (optional) names of the generators of the quotient. (If
    1305           there are multiple generators, you can specify a single character
    1306           string and the generators are named in sequence starting with 0.)
    1307 
    1308         OUTPUT:
    1309 
    1310         - R/I -- the quotient ring of R by the ideal I
    1311        
    1312         EXAMPLES::
    1313 
    1314             sage: R.<x> = PolynomialRing(ZZ)
    1315             sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2])
    1316             sage: S = R.quotient_ring(I, 'a')
    1317             sage: S.gens()
    1318             (a,)
    1319 
    1320             sage: R.<x,y> = PolynomialRing(QQ,2)
    1321             sage: S.<a,b> = R.quotient_ring((x^2, y))
    1322             sage: S
    1323             Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y)
    1324             sage: S.gens()
    1325             (a, 0)
    1326             sage: a == b
    1327             False
    1328         """
    1329         return self.quotient(I, names)
    13301413   
    13311414
    13321415cdef class IntegralDomain(CommutativeRing):
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    917917                return mor._call_with_args(x, args, kwds)
    918918           
    919919        raise TypeError, "No conversion defined from %s to %s"%(R, self)
     920
     921    def __mul__(self,x):
     922        """
     923        This is a multiplication method that more or less directly
     924        calls another attribute ``_mul_`` (single underscore). This
     925        is because ``__mul__`` can not be implemented via inheritance
     926        from the parent methods of the category, but ``_mul_`` can
     927        be inherited. This is, e.g., used when creating twosided
     928        ideals of matrix algebras. See trac ticket #7797.
     929
     930        EXAMPLE::
     931
     932            sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
     933            sage: MS = MatrixSpace(QQ,2,2)
     934
     935        This matrix space is in fact an algebra, and in particular
     936        it is a ring, from the point of view of categories::
     937
     938            sage: MS.category()
     939            Category of algebras over Rational Field
     940            sage: MS in Rings()
     941            True
     942
     943        However, its class does not inherit from the base class
     944        ``Ring``::
     945
     946            sage: isinstance(MS,Ring)
     947            False
     948
     949        Its ``_mul_`` method is inherited from the category, and
     950        can be used to create a left or right ideal::
     951
     952            sage: MS._mul_.__module__
     953            'sage.categories.rings'
     954            sage: MS*MS.1      # indirect doctest
     955            Left Ideal
     956            (
     957              [0 1]
     958              [0 0]
     959            )
     960             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     961            sage: MS*[MS.1,2]
     962            Left Ideal
     963            (
     964              [0 1]
     965              [0 0],
     966            <BLANKLINE>
     967              [2 0]
     968              [0 2]
     969            )
     970             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     971            sage: MS.1*MS
     972            Right Ideal
     973            (
     974              [0 1]
     975              [0 0]
     976            )
     977             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     978            sage: [MS.1,2]*MS
     979            Right Ideal
     980            (
     981              [0 1]
     982              [0 0],
     983            <BLANKLINE>
     984              [2 0]
     985              [0 2]
     986            )
     987             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     988
     989        """
     990        # generic multiplication method. It defers to
     991        # _mul_, which may be defined via categories.
     992        _mul_ = None
     993        switch = False
     994        try:
     995            if isinstance(self,Parent):
     996                _mul_ = self._mul_
     997        except AttributeError:
     998            pass
     999        if _mul_ is None:
     1000            try:
     1001                if isinstance(x,Parent):
     1002                    _mul_ = x._mul_
     1003                    switch = True
     1004            except AttributeError:
     1005                pass
     1006        if _mul_ is None:
     1007            raise TypeError,"For implementing multiplication, provide the method '_mul_' for %s resp. %s"%(self,x)
     1008        if switch:
     1009            return _mul_(self,switch_sides=True)
     1010        return _mul_(x)
    9201011       
    9211012    #############################################################################
    9221013    # Containment testing
  • setup.py

    diff --git a/setup.py b/setup.py
    a b  
    885885      packages    = ['sage',
    886886                     
    887887                     'sage.algebras',
     888                     'sage.algebras.letterplace',
    888889                     'sage.algebras.quatalg',
    889890                     
    890891                     'sage.calculus',