# 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 sage/algebras/free_algebra sage/algebras/free_algebra_element sage/algebras/letterplace/free_algebra_letterplace sage/algebras/letterplace/free_algebra_element_letterplace sage/algebras/letterplace/letterplace_ideal sage/algebras/free_algebra_quotient sage/algebras/free_algebra_quotient_element sage/algebras/steenrod_algebra sage/algebras/steenrod_algebra_element sage/algebras/steenrod_algebra_bases No newline at end of file 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 sage/rings/ring sage/rings/ideal sage/rings/ideal_monoid sage/rings/noncommutative_ideals sage/rings/morphism sage/rings/homset sage/rings/infinity
• ## module_list.py

diff --git a/module_list.py b/module_list.py
 a include_dirs = [SAGE_ROOT+'/local/include/FLINT/'], depends = flint_depends), Extension('sage.algebras.letterplace.free_algebra_letterplace', sources = ['sage/algebras/letterplace/free_algebra_letterplace.pyx'], language="c++", include_dirs = [SAGE_ROOT +'/local/include/singular'], depends = singular_depends), Extension('sage.algebras.letterplace.free_algebra_element_letterplace', sources = ['sage/algebras/letterplace/free_algebra_element_letterplace.pyx'], language="c++", include_dirs = [SAGE_ROOT +'/local/include/singular'], depends = singular_depends), Extension('sage.algebras.letterplace.letterplace_ideal', sources = ['sage/algebras/letterplace/letterplace_ideal.pyx'], language="c++", include_dirs = [SAGE_ROOT +'/local/include/singular'], depends = singular_depends), Extension('sage.algebras.quatalg.quaternion_algebra_cython', sources = ['sage/algebras/quatalg/quaternion_algebra_cython.pyx'], language='c++', Extension('sage.rings.morphism', sources = ['sage/rings/morphism.pyx']), Extension('sage.rings.noncommutative_ideals', sources = ['sage/rings/noncommutative_ideals.pyx']), Extension('sage.rings.power_series_mpoly', sources = ['sage/rings/power_series_mpoly.pyx']),
• ## sage/algebras/free_algebra.py

diff --git a/sage/algebras/free_algebra.py b/sage/algebras/free_algebra.py
 a - William Stein (2006-11-01): add all doctests; implemented many things. - Simon King (2011-03-21): reimplement free algebra constructor, using a :class:~sage.structure.factory.UniqueFactory for handling different implementations of free algebras. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: G.base_ring() Free Algebra on 3 generators (x, y, z) over Integer Ring The above free algebra is based on a generic implementation. By trac ticket #7797, there is a different implementation :class:~sage.algebras.letterplace.free_algebra_letterplace.FreeAlgebra_letterplace based on Singular's letterplace rings. It is currently restricted to homogeneous elements and is therefore not the default. But the arithmetic is much faster than in the generic implementation. Moreover, we can compute Groebner bases with degree bound for its two-sided ideals, and thus provide ideal containment tests:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: I.groebner_basis(degbound=4) 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 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 True NOTE: For efficiency, a univariate polynomial ring (resp. a multivariate polynomial ring with one generator) is returned if a free algebra with one generator is requested:: sage: FreeAlgebra(ZZ,'a') Univariate Polynomial Ring in a over Integer Ring sage: FreeAlgebra(ZZ,'a',implementation='letterplace') Multivariate Polynomial Ring in a over Integer Ring TESTS:: sage: F = FreeAlgebra(GF(5),3,'x') sage: F == loads(dumps(F)) sage: F is loads(dumps(F)) True sage: F = FreeAlgebra(GF(5),3,'x', implementation='letterplace') sage: F is loads(dumps(F)) True :: sage: F. = FreeAlgebra(GF(5),3) sage: F == loads(dumps(F)) sage: F is loads(dumps(F)) True sage: F. = FreeAlgebra(GF(5),3, implementation='letterplace') sage: F is loads(dumps(F)) True :: sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y']) sage: F == loads(dumps(F)) sage: F is loads(dumps(F)) True sage: F = FreeAlgebra(GF(5),3, ['xx', 'zba', 'Y'], implementation='letterplace') sage: F is loads(dumps(F)) True :: sage: F = FreeAlgebra(GF(5),3, 'abc') sage: F == loads(dumps(F)) sage: F is loads(dumps(F)) True sage: F = FreeAlgebra(GF(5),3, 'abc', implementation='letterplace') sage: F is loads(dumps(F)) True :: sage: F = FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x') sage: F == loads(dumps(F)) sage: F = FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x') sage: F is loads(dumps(F)) True Note that the letterplace implementation can only be used if the corresponding (multivariate) polynomial ring has an implementation in Singular:: sage: FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x', implementation='letterplace') Traceback (most recent call last): ... NotImplementedError: The letterplace implementation is not available for the free algebra you requested """ #***************************************************************************** import sage.structure.parent_gens def FreeAlgebra(R, n, names): from sage.structure.factory import UniqueFactory from sage.all import PolynomialRing from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular class FreeAlgebraFactory(UniqueFactory): """ Return the free algebra over the ring R on n generators with given names. INPUT: -  R - ring -  n - integer -  names - string or list/tuple of n strings OUTPUT: a free algebra A constructor of free algebras. See :mod:~sage.algebras.free_algebra for examples and corner cases. EXAMPLES:: sage: FreeAlgebra(GF(5),3,'x') Free Algebra on 3 generators (x0, x1, x2) over Finite Field of size 5 sage: F. = FreeAlgebra(GF(5),3) sage: (x+y+z)^2 x^2 + x*y + x*z + y*x + y^2 + y*z + z*x + z*y + z^2 sage: FreeAlgebra(GF(5),3, 'xx, zba, Y') Free Algebra on 3 generators (xx, zba, Y) over Finite Field of size 5 sage: FreeAlgebra(GF(5),3, 'abc') Free Algebra on 3 generators (a, b, c) over Finite Field of size 5 sage: FreeAlgebra(GF(5),1, 'z') Free Algebra on 1 generators (z,) over Finite Field of size 5 sage: FreeAlgebra(GF(5),1, ['alpha']) Free Algebra on 1 generators (alpha,) over Finite Field of size 5 sage: FreeAlgebra(FreeAlgebra(ZZ,1,'a'), 2, 'x') Free Algebra on 2 generators (x0, x1) over Free Algebra on 1 generators (a,) over Integer Ring Free algebras are globally unique:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: G = FreeAlgebra(ZZ,3,'x,y,z') sage: F. = FreeAlgebra(GF(5),3)  # indirect doctest sage: F is loads(dumps(F)) True sage: F is FreeAlgebra(GF(5),['x','y','z']) True sage: G = FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace') sage: F is G False sage: G is FreeAlgebra(GF(5),['x','y','z'], implementation='letterplace') True Free algebras commute with their base ring. :: sage: K. = FreeAlgebra(QQ,2) sage: K.is_commutative() False sage: L. = FreeAlgebra(K,1) sage: L.is_commutative() False sage: s = a*b^2 * c^3; s a*b^2*c^3 sage: parent(s) Free Algebra on 1 generators (c,) over Free Algebra on 2 generators (a, b) over Rational Field sage: c^3 * a * b^2 a*b^2*c^3 sage: copy(G) is G True sage: copy(F) is F True """ names = sage.structure.parent_gens.normalize_names(n, names) return cache(R, n, names) def create_key(self,base_ring, arg1=None, arg2=None, sparse=False, order='degrevlex', names=None, name=None, implementation=None): """ Create the key under which a free algebra is stored. TESTS:: sage: FreeAlgebra.create_key(GF(5),['x','y','z']) (Finite Field of size 5, ('x', 'y', 'z')) sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3) (Finite Field of size 5, ('x', 'y', 'z')) sage: FreeAlgebra.create_key(GF(5),3,'xyz') (Finite Field of size 5, ('x', 'y', 'z')) sage: FreeAlgebra.create_key(GF(5),['x','y','z'], implementation='letterplace') (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,) sage: FreeAlgebra.create_key(GF(5),['x','y','z'],3, implementation='letterplace') (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,) sage: FreeAlgebra.create_key(GF(5),3,'xyz', implementation='letterplace') (Multivariate Polynomial Ring in x, y, z over Finite Field of size 5,) """ if arg1 is None and arg2 is None and names is None: # this is used for pickling return (base_ring,) PolRing = None # test if we can use libSingular/letterplace if implementation is not None and implementation != 'generic': try: PolRing = PolynomialRing(base_ring, arg1, arg2, sparse=sparse, order=order, names=names, name=name, implementation=implementation if implementation!='letterplace' else None) if not isinstance(PolRing,MPolynomialRing_libsingular): if PolRing.ngens() == 1: PolRing = PolynomialRing(base_ring,1,PolRing.variable_names()) if not isinstance(PolRing,MPolynomialRing_libsingular): raise TypeError else: raise TypeError except (TypeError, NotImplementedError),msg: raise NotImplementedError, "The letterplace implementation is not available for the free algebra you requested" if PolRing is not None: return (PolRing,) # normalise the generator names from sage.all import Integer if isinstance(arg1, (int, long, Integer)): arg1, arg2 = arg2, arg1 if not names is None: arg1 = names elif not name is None: arg1 = name if arg2 is None: arg2 = len(arg1) names = sage.structure.parent_gens.normalize_names(arg2,arg1) return base_ring, names def create_object(self, version, key): """ Construct the free algebra that belongs to a unique key. NOTE: Of course, that method should not be called directly, since it does not use the cache of free algebras. TESTS:: sage: FreeAlgebra.create_object('4.6.2', (QQ['x','y'],)) Free Associative Unital Algebra on 2 generators ('x', 'y') over Rational Field sage: FreeAlgebra.create_object('4.6.2', (QQ['x','y'],)) is FreeAlgebra(QQ,['x','y']) False """ if len(key)==1: if key[0].ngens()<=1: return key[0] from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace return FreeAlgebra_letterplace(key[0]) if len(key[1])<=1: return PolynomialRing(key[0],key[1]) return FreeAlgebra_generic(key[0],len(key[1]),key[1]) FreeAlgebra = FreeAlgebraFactory('FreeAlgebra') def is_FreeAlgebra(x): """ False sage: is_FreeAlgebra(FreeAlgebra(ZZ,100,'x')) True sage: is_FreeAlgebra(FreeAlgebra(ZZ,10,'x',implementation='letterplace')) True """ return isinstance(x, FreeAlgebra_generic) from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace return isinstance(x, (FreeAlgebra_generic,FreeAlgebra_letterplace)) class FreeAlgebra_generic(Algebra): x*y*x*y*x*y*x*y*x*y*x*y + x*y*z*x*y*z*x*y*z*x*y*z sage: (2 + x*z + x^2)^2 + (x - y)^2 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 TESTS: Free algebras commute with their base ring. :: sage: K. = FreeAlgebra(QQ) sage: K.is_commutative() False sage: L. = FreeAlgebra(K) sage: L.is_commutative() False sage: s = a*b^2 * c^3; s a*b^2*c^3 sage: parent(s) Free Algebra on 2 generators (c, d) over Free Algebra on 2 generators (a, b) over Rational Field sage: c^3 * a * b^2 a*b^2*c^3 """ def __init__(self, R, n, names): """ -  n - an integer -  names - generator names TEST: Note that the following is *not* the recommended way to create a free algebra. :: sage: from sage.algebras.free_algebra import FreeAlgebra_generic sage: FreeAlgebra_generic(ZZ,3,'abc') Free Algebra on 3 generators (a, b, c) over Integer Ring """ if not isinstance(R, Ring): raise TypeError, "Argument R must be a ring." def __cmp__(self, other): """ Two free algebras are considered the same if they have the same base ring, number of generators and variable names. base ring, number of generators and variable names, and the same implementation. EXAMPLES:: False sage: F == FreeAlgebra(QQ,3,'y') False Note that since trac ticket #7797 there is a different implementation of free algebras. Two corresponding free algebras in different implementations are not equal, but there is a coercion:: """ if not isinstance(other, FreeAlgebra_generic): return -1 c = cmp(self.base_ring(), other.base_ring()) if c: return c c = cmp(self.__ngens, other.__ngens) c = cmp(self.__ngens, other.ngens()) if c: return c c = cmp(self.variable_names(), other.variable_names()) if c: return c EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: print F sage: F    # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<>') sage: print F sage: F QQ<> """ return "Free Algebra on %s generators %s over %s"%( def __call__(self, x): """ Coerce x into self. Convert x into self. EXAMPLES:: sage: R. = FreeAlgebra(QQ,2) sage: R(3) 3 TESTS:: sage: F. = FreeAlgebra(GF(5),3) sage: L. = FreeAlgebra(ZZ,3,implementation='letterplace') sage: F(x)     # indirect doctest x sage: F.1*L.2 y*z sage: (F.1*L.2).parent() is F True :: sage: K. = GF(25) sage: F. = FreeAlgebra(K,3) sage: L. = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 b + (z+1)*c """ if isinstance(x, FreeAlgebraElement): P = x.parent() return x if not (P is self.base_ring()): return FreeAlgebraElement(self, x) elif hasattr(x,'letterplace_polynomial'): P = x.parent() if self.has_coerce_map_from(P): # letterplace versus generic ngens = P.ngens() M = self.__monoid def exp_to_monomial(T): out = [] for i in xrange(len(T)): if T[i]: out.append((i%ngens,T[i])) return M(out) return FreeAlgebraElement(self, dict([(exp_to_monomial(T),c) for T,c in x.letterplace_polynomial().dict().iteritems()])) # ok, not a free algebra element (or should not be viewed as one). if isinstance(x, basestring): from sage.all import sage_eval return sage_eval(x,locals=self.gens_dict()) F = self.__monoid R = self.base_ring() # coercion from free monoid - this free algebra - a free algebra in letterplace implementation that has the same generator names and whose base ring coerces into self's base ring - the underlying monoid - anything that coerces to the base ring of this free algebra Traceback (most recent call last): ... TypeError: no natural map between bases of free algebras TESTS:: sage: F. = FreeAlgebra(GF(5),3) sage: L. = FreeAlgebra(GF(5),3,implementation='letterplace') sage: F(x) x sage: F.1*L.2     # indirect doctest y*z """ try: R = x.parent() return self._coerce_try(x, [self.base_ring()]) def coerce_map_from_impl(self, R): """ Test whether there is a coercion from R to self (old coercion framework). TESTS:: sage: K. = GF(25) sage: F. = FreeAlgebra(K,3) sage: L. = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2      # indirect doctest b + (z+1)*c """ if R is self.__monoid: return True
• ## sage/algebras/free_algebra_element.py

diff --git a/sage/algebras/free_algebra_element.py b/sage/algebras/free_algebra_element.py
 a EXAMPLES:: sage: A.=FreeAlgebra(ZZ,3) sage: repr(-x+3*y*z) sage: repr(-x+3*y*z)    # indirect doctest '-x + 3*y*z' """ v = self.__monomial_coefficients.items() EXAMPLES:: sage: A.=FreeAlgebra(ZZ,3) sage: latex(-x+3*y^20*z) sage: latex(-x+3*y^20*z)   # indirect doctest \left(-1\right)x + 3y^{20}z sage: alpha,beta,gamma=FreeAlgebra(ZZ,3,'alpha,beta,gamma').gens() sage: latex(alpha-beta) EXAMPLES:: sage: R. = FreeAlgebra(QQ,2) sage: x + y sage: x + y    # indirect doctest x + y """ A = self.parent() EXAMPLES:: sage: R. = FreeAlgebra(QQ,2) sage: -(x+y) sage: -(x+y)    # indirect doctest -x - y """ y = self.parent()(0) EXAMPLES:: sage: R. = FreeAlgebra(QQ,2) sage: x - y sage: x - y    # indirect doctest x - y """ A = self.parent() EXAMPLES:: sage: A.=FreeAlgebra(ZZ,3) sage: (x+y+x*y)*(x+y+1) sage: (x+y+x*y)*(x+y+1)    # indirect doctest x + y + x^2 + 2*x*y + y*x + y^2 + x*y*x + x*y^2 """ 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 """ Free algebra quotients Finite dimensional free algebra quotients REMARK: This implementation only works for finite dimensional quotients, since a list of basis monomials and the multiplication matrices need to be explicitly provided. The homogeneous part of a quotient of a free algebra over a field by a finitely generated homogeneous twosided ideal is available in a different implementation. See :mod:~sage.algebras.letterplace.free_algebra_letterplace and :mod:~sage.rings.quotient_ring. TESTS::
• ## 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
 - ############################################################################### # #       Copyright (C) 2011 Simon King #  Distributed under the terms of the GNU General Public License (GPL), #  version 2 or any later version.  The full text of the GPL is available at: #                  http://www.gnu.org/licenses/ # ############################################################################### cdef class FreeAlgebraElement_letterplace from sage.structure.element cimport AlgebraElement, ModuleElement, RingElement, Element from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_libsingular, MPolynomial_libsingular from sage.algebras.letterplace.free_algebra_letterplace cimport FreeAlgebra_letterplace include "../../ext/stdsage.pxi" cdef class FreeAlgebraElement_letterplace(AlgebraElement): cdef MPolynomial_libsingular _poly
• ## 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
 - ############################################################################### # #       Copyright (C) 2011 Simon King #  Distributed under the terms of the GNU General Public License (GPL), #  version 2 or any later version.  The full text of the GPL is available at: #                  http://www.gnu.org/licenses/ # ############################################################################### """ Homogeneous elements of free algebras, in letterplace implementation AUTHOR: - Simon King (2011-03-23): Trac ticket #7797. """ from sage.libs.singular.function import lib, singular_function from sage.misc.misc import repr_lincomb from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal # Define some singular functions lib("freegb.lib") poly_reduce = singular_function("NF") singular_system=singular_function("system") ##################### # Free algebra elements cdef class FreeAlgebraElement_letterplace(AlgebraElement): """ Homogeneous elements of a free associative unital algebra (letterplace implementation) EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: x+y x + y sage: x*y !=y*x True sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: (y^3).reduce(I) y*y*y sage: (y^3).normal_form(I) y*y*z - y*z*y + y*z*z """ def __init__(self, A, x, check=True): """ INPUT: - A free associative unital algebra in letterplace implementation, A. - A homogeneous polynomial that can be coerced into the currently used polynomial ring of A. - check (optional bool, default True): Do not attempt the above coercion (for internal use only). TEST:: sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace sage: F. = FreeAlgebra(GF(3), implementation='letterplace') sage: F.set_degbound(2) sage: P = F.current_ring() sage: F.set_degbound(4) sage: P == F.current_ring() False sage: p = FreeAlgebraElement_letterplace(F,P.1*P.3+2*P.0*P.4); p -x*y + y*x sage: loads(dumps(p)) == p True """ cdef FreeAlgebra_letterplace P = A if check: if not x.is_homogeneous(): raise ValueError, "Free algebras based on Letterplace can currently only work with homogeneous elements" P.set_degbound(x.degree()) x = P._current_ring(x) AlgebraElement.__init__(self,P) self._poly = x def __reduce__(self): """ Pickling. TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: loads(dumps(x*y*x)) == x*y*x   # indirect doctest True """ return self.__class__, (self._parent,self._poly) def __copy__(self): """ TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: copy(x*y*z+z*y*x) == x*y*z+z*y*x   # indirect doctest True """ self._poly = (self._parent)._current_ring(self._poly) return self.__class__(self._parent,self._poly,check=False) def __hash__(self): """ TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: set([x*y*z, z*y+x*z,x*y*z])  # indirect doctest set([x*z + z*y, x*y*z]) """ return hash(self._poly) def __iter__(self): """ Iterates over the pairs "tuple of exponents, coefficient". EXAMPLE:: sage: F. = FreeAlgebra(GF(3), implementation='letterplace') sage: p = x*y-z^2 sage: list(p)   # indirect doctest [((0, 0, 0, 1, 0, 0, 0, 1), 2), ((0, 1, 0, 0, 0, 0, 1, 0), 1)] """ return self._poly.dict().iteritems() def _repr_(self): """ TEST:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: -(a+b*(z+1)-c)^2     # indirect doctest -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 """ cdef list L = [] cdef FreeAlgebra_letterplace P = self._parent cdef int ngens = P.__ngens if P._base.is_atomic_repr(): for E,c in zip(self._poly.exponents(),self._poly.coefficients()): monstr = P.exponents_to_string(E) if monstr: if c==1: if L: L.extend(['+',monstr]) else: L.append(monstr) elif c==-1: if L: L.extend(['-',monstr]) else: L.append('-'+monstr) else: if L: if c>=0: L.extend(['+',repr(c)+'*'+monstr]) else: L.extend(['-',repr(-c)+'*'+monstr]) else: L.append(repr(c)+'*'+monstr) else: if c>=0: if L: L.extend(['+',repr(c)]) else: L.append(repr(c)) else: if L: L.extend(['-',repr(-c)]) else: L.append(repr(c)) else: for E,c in zip(self._poly.exponents(),self._poly.coefficients()): monstr = P.exponents_to_string(E) if monstr: if c==1: if L: L.extend(['+',monstr]) else: L.append(monstr) elif c==-1: if L: L.extend(['-',monstr]) else: L.append('-'+monstr) else: if L: L.extend(['+','('+repr(c)+')*'+monstr]) else: L.append('('+repr(c)+')*'+monstr) else: if L: L.extend(['+',repr(c)]) else: L.append(repr(c)) if L: return ' '.join(L) return '0' def degree(self): """ Return the degree of this element. NOTE: Currently, generators can only have the degree one. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: ((x+y+z)^3).degree() 3 """ return self._poly.degree() def letterplace_polynomial(self): """ Return the commutative polynomial that is used internally to represent this free algebra element. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: ((x+y-z)^2).letterplace_polynomial() 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 """ return self._poly def lm(self): """ The leading monomial of this free algebra element. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lm() x*x*y """ return FreeAlgebraElement_letterplace(self._parent, self._poly.lm()) def lt(self): """ The leading term (monomial times coefficient) of this free algebra element. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lt() 20*x*x*y """ return FreeAlgebraElement_letterplace(self._parent, self._poly.lt()) def lc(self): """ The leading coefficient of this free algebra element, as element of the base ring. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lc() 20 sage: ((2*x+3*y-4*z)^2*(5*y+6*z)).lc().parent() is F.base() True """ return self._poly.lc() def __nonzero__(self): """ TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: bool(x)      # indirect doctest True sage: bool(F.zero()) False """ return bool(self._poly) def __richcmp__(left, right, int op): """ TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z)) sage: p-p.lt()left)._richcmp(right, op) def __cmp__(left, right): """ TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z)) sage: cmp(p,p-p.lt())    # indirect doctest 1 """ return (left)._cmp(right) cdef int _cmp_c_impl(self, Element other) except -2: """ Auxiliary method for comparison. TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: p = ((2*x+3*y-4*z)^2*(5*y+6*z)) sage: p-p.lt()self)._poly,(other)._poly) ################################ ## Arithmetic cpdef ModuleElement _neg_(self): """ TEST:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: -((z+2)*a^2*b+3*c^3)  # indirect doctest (4*z + 3)*a*a*b + (2)*c*c*c sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: -(3*x*y+2*z^2) -3*x*y - 2*z*z """ return FreeAlgebraElement_letterplace(self._parent,-self._poly,check=False) cpdef ModuleElement _add_(self, ModuleElement other): """ Addition, under the side condition that either one summand is zero, or both summands have the same degree. TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: x+y    # indirect doctest x + y sage: x+1 Traceback (most recent call last): ... ArithmeticError: Can only add elements of the same degree sage: x+0 x sage: 0+x x """ if not other: return self if not self: return other cdef FreeAlgebraElement_letterplace right = other if right._poly.degree()!=self._poly.degree(): raise ArithmeticError, "Can only add elements of the same degree" # update the polynomials cdef FreeAlgebra_letterplace A = self._parent self._poly = A._current_ring(self._poly) right._poly = A._current_ring(right._poly) return FreeAlgebraElement_letterplace(self._parent,self._poly+right._poly,check=False) cpdef ModuleElement _sub_(self, ModuleElement other): """ Difference, under the side condition that either one summand is zero or both have the same degree. TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: x*y-y*x     # indirect doctest x*y - y*x sage: x-1 Traceback (most recent call last): ... ArithmeticError: Can only subtract elements of the same degree sage: x-0 x sage: 0-x -x """ if not other: return self if not self: return -other cdef FreeAlgebraElement_letterplace right = other if right._poly.degree()!=self._poly.degree(): raise ArithmeticError, "Can only subtract elements of the same degree" # update the polynomials cdef FreeAlgebra_letterplace A = self._parent self._poly = A._current_ring(self._poly) right._poly = A._current_ring(right._poly) return FreeAlgebraElement_letterplace(self._parent,self._poly-right._poly,check=False) cpdef ModuleElement _lmul_(self, RingElement right): """ Multiplication from the right with an element of the base ring. TEST:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: (a+b)*(z+1)    # indirect doctest (z + 1)*a + (z + 1)*b """ return FreeAlgebraElement_letterplace(self._parent,self._poly._lmul_(right),check=False) cpdef ModuleElement _rmul_(self, RingElement left): """ Multiplication from the left with an element of the base ring. TEST:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: (z+1)*(a+b)   # indirect doctest (z + 1)*a + (z + 1)*b """ return FreeAlgebraElement_letterplace(self._parent,self._poly._rmul_(left),check=False) cpdef RingElement _mul_(self, RingElement other): cdef FreeAlgebraElement_letterplace left = self cdef FreeAlgebraElement_letterplace right = other cdef FreeAlgebra_letterplace A = left._parent A.set_degbound(left._poly.degree()+right._poly.degree()) # we must put the polynomials into the same ring left._poly = A._current_ring(left._poly) right._poly = A._current_ring(right._poly) rshift = singular_system("stest",right._poly,left._poly.degree(),A._degbound,A.__ngens, ring=A._current_ring) return FreeAlgebraElement_letterplace(A,left._poly*rshift, check=False) def __pow__(FreeAlgebraElement_letterplace self, int n, k): """ TEST:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: (a+z*b)^3    # indirect doctest 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 """ cdef FreeAlgebra_letterplace A = self._parent if n<0: raise ValueError, "Negative exponents are not allowed" if n==0: return FreeAlgebraElement_letterplace(A, A._current_ring(1), check=False) if n==1: return self A.set_degbound(self._poly.degree()*n) cdef MPolynomial_libsingular p,q self._poly = A._current_ring(self._poly) cdef int d = self._poly.degree() q = p = self._poly cdef int i for i from 0 = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: p = y^2*z*y^2+y*z*y*z*y We compute the letterplace version of the Groebneer basis of I with degree bound 4:: sage: G = F._reductor_(I.groebner_basis(4).gens(),4) sage: G.ring() is F.current_ring() True Since the element p is of degree 5, it is no surrprise that its reductions with respect to the original generators of I (of degree 2), or with respect to G (Groebner basis with degree bound 4), or with respect to the Groebner basis with degree bound 5 (which yields its normal form) are pairwise different:: sage: p.reduce(I) y*y*z*y*y + y*z*y*z*y sage: p.reduce(G) y*y*z*z*y + y*z*y*z*y - y*z*z*y*y + y*z*z*z*y sage: p.normal_form(I) y*y*z*z*z + y*z*y*z*z - y*z*z*y*z + y*z*z*z*z sage: p.reduce(I) != p.reduce(G) != p.normal_form(I) != p.reduce(I) True """ cdef FreeAlgebra_letterplace P = self._parent if not isinstance(G,(list,tuple)): if G==P: return P.zero_element() if not (isinstance(G,MPolynomialIdeal) and G.ring()==P._current_ring): G = G.gens() C = P.current_ring() cdef int selfdeg = self._poly.degree() if isinstance(G,MPolynomialIdeal): gI = G else: gI = P._reductor_(G,selfdeg) #C.ideal(g,coerce=False) from sage.libs.singular.option import LibSingularOptions libsingular_options = LibSingularOptions() bck = (libsingular_options['redTail'],libsingular_options['redSB']) libsingular_options['redTail'] = True libsingular_options['redSB'] = True poly = poly_reduce(C(self._poly),gI, ring=C, attributes={gI:{"isSB":1}}) libsingular_options['redTail'] = bck[0] libsingular_options['redSB'] = bck[1] return FreeAlgebraElement_letterplace(P,poly,check=False) def normal_form(self,I): """ Return the normal form of this element with respect to a twosided homogeneous ideal. INPUT: A twosided homogeneous ideal I of the parent F of this element, x. OUTPUT: The normal form of x wrt. I. NOTE: The normal form is computed by reduction with respect to a Groebnerbasis of I with degree bound deg(x). EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: (x^5).normal_form(I) -y*z*z*z*x - y*z*z*z*y - y*z*z*z*z We verify two basic properties of normal forms: The difference of an element and its normal form is contained in the ideal, and if two elements of the free algebra differ by an element of the ideal then they have the same normal form:: sage: x^5 - (x^5).normal_form(I) in I True sage: (x^5+x*I.0*y*z-3*z^2*I.1*y).normal_form(I) == (x^5).normal_form(I) True """ if self._parent != I.ring(): raise ValueError, "Can not compute normal form wrt an ideal that does not belong to %s"%self._parent sdeg = self._poly.degree() 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
 - ############################################################################### # #       Copyright (C) 2011 Simon King #  Distributed under the terms of the GNU General Public License (GPL), #  version 2 or any later version.  The full text of the GPL is available at: #                  http://www.gnu.org/licenses/ # ############################################################################### cdef class FreeAlgebra_letterplace from sage.rings.ring cimport Algebra from sage.structure.element cimport AlgebraElement, ModuleElement, RingElement, Element from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_libsingular, MPolynomial_libsingular from sage.algebras.letterplace.free_algebra_element_letterplace cimport FreeAlgebraElement_letterplace include "../../ext/stdsage.pxi" cdef class FreeAlgebra_letterplace(Algebra): cdef MPolynomialRing_libsingular _commutative_ring cdef MPolynomialRing_libsingular _current_ring cdef int _degbound cdef int __ngens cdef object __monoid cdef public object __custom_name 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
 - ############################################################################### # #       Copyright (C) 2011 Simon King #  Distributed under the terms of the GNU General Public License (GPL), #  version 2 or any later version.  The full text of the GPL is available at: #                  http://www.gnu.org/licenses/ # ############################################################################### """ Free associative unital algebras, implemented via Singular's letterplace rings AUTHOR: - Simon King (2011-03-21): Trac ticket #7797 With this implementation, Groebner bases out to a degree bound and normal forms can be computed for twosided homogeneous ideals of free algebras. For now, all computations are restricted to the homogeneous case. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: I 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 sage: x*(x*I.0-I.1*y+I.0*y)-I.1*y*z x*y*x*y + x*y*y*y - x*y*y*z + x*y*z*y + y*x*y*z + y*y*y*z sage: x^2*I.0-x*I.1*y+x*I.0*y-I.1*y*z in I True The preceding containment test is based on the computation of Groebner bases with degree bound:: sage: I.groebner_basis(degbound=4) 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 When reducing an element by I, the original generators are chosen:: sage: (y*z*y*y).reduce(I) y*z*y*y However, there is a method for computing the normal form of an element, which is the same as reduction by the Groebner basis out to the degree of that element:: sage: (y*z*y*y).normal_form(I) y*z*y*z - y*z*z*y + y*z*z*z sage: (y*z*y*y).reduce(I.groebner_basis(4)) y*z*y*z - y*z*z*y + y*z*z*z The default term order derives from the degree reverse lexicographic order on the commutative version of the free algebra:: sage: F.commutative_ring().term_order() Degree reverse lexicographic term order A different term order can be chosen, and of course may yield a different normal form:: sage: L. = FreeAlgebra(QQ, implementation='letterplace', order='lex') sage: L.commutative_ring().term_order() Lexicographic term order sage: J = L*[a*b+b*c,a^2+a*b-b*c-c^2]*L sage: J.groebner_basis(4) 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 sage: (b*c*b*b).normal_form(J) 1/2*b*c*c*b - 1/2*c*c*b*b TEST:: sage: TestSuite(F).run() sage: TestSuite(L).run() sage: loads(dumps(F)) is F True TODO: In this first version of a wrapper for the letterplace algebra, we can only support computations with homogeneous elements. Moreover, the computation of Groebner bases only works for global term orderings, and all generators have degree 1. These restrictions are inherited from the letterplace implementation in Singular, but it is ongoing work to lift these restrictions. We support coercion from the letterplace wrapper to the corresponding generic implementation of a free algebra (:class:~sage.algebras.free_algebra.FreeAlgebra_generic), but there is no coercion the opposite direction, since the generic implementation also comprises non-homogeneous elements. We also do not support coercion from a subalgebra, or between free algebras with different term orderings, yet. """ from sage.all import PolynomialRing, prod from sage.libs.singular.function import lib, singular_function from sage.rings.polynomial.term_order import TermOrder from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic from sage.categories.algebras import Algebras from sage.rings.noncommutative_ideals import IdealMonoid_nc ##################### # Define some singular functions lib("freegb.lib") poly_reduce = singular_function("NF") singular_system=singular_function("system") # unfortunately we can not set Singular attributes for MPolynomialRing_libsingular # Hence, we must constantly work around Letterplace's sanity checks, # and can not use the following library functions: #set_letterplace_attributes = singular_function("setLetterplaceAttributes") #lpMult = singular_function("lpMult") ##################### # Auxiliar functions cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks): """ Create a polynomial ring in block order. INPUT: - base_ring: A multivariate polynomial ring. - blocks: The number of blocks to be formed. OUTPUT: A multivariate polynomial ring in block order, all blocks isomorphic (as ordered rings) with the given ring, and the variable names of the n-th block (n>0) ending with "_%d"%n. TEST: Note that, since the algebras are cached, we need to choose a different base ring, since other doctests could have a side effect on the atteined degree bound:: sage: F. = FreeAlgebra(GF(17), implementation='letterplace') sage: L. = FreeAlgebra(GF(17), implementation='letterplace', order='lex') sage: F.set_degbound(4) sage: F.current_ring()  # indirect doctest 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 sage: F.current_ring().term_order() degrevlex(3),degrevlex(3),degrevlex(3),degrevlex(3) term order sage: L.set_degbound(2) sage: L.current_ring().term_order() lex(3),lex(3) term order """ n = base_ring.ngens() T0 = base_ring.term_order() T = T0 cdef i cdef tuple names0 = base_ring.variable_names() cdef list names = list(names0) for i from 1<=i = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: F Free Associative Unital Algebra on 3 generators ('a', 'b', 'c') over Finite Field in z of size 5^2 sage: P = F.commutative_ring() sage: P Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2 We can do arithmetic as usual, as long as we stay homogeneous:: sage: (z*a+(z+1)*b+2*c)^2 (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 sage: a+1 Traceback (most recent call last): ... ArithmeticError: Can only add elements of the same degree """ # It is not really a free algebra over the given generators. Rather, # it is a free algebra over the commutative monoid generated by the given generators. def __init__(self, R): """ INPUT: A multivariate polynomial ring of type :class:~sage.rings.polynomial.multipolynomial_libsingular.MPolynomialRing_libsingular. OUTPUT: The free associative version of the given commutative ring. NOTE: One is supposed to use the FreeAlgebra constructor, in order to use the cache. TEST:: sage: from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace sage: FreeAlgebra_letterplace(QQ['x','y']) Free Associative Unital Algebra on 2 generators ('x', 'y') over Rational Field sage: FreeAlgebra_letterplace(QQ['x']) Traceback (most recent call last): ... TypeError: A letterplace algebra must be provided by a polynomial ring of type :: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: TestSuite(F).run(verbose=True) running ._test_additive_associativity() . . . pass running ._test_an_element() . . . pass running ._test_associativity() . . . pass running ._test_category() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass pass running ._test_elements_eq() . . . pass running ._test_eq() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_one() . . . pass running ._test_pickling() . . . pass running ._test_prod() . . . pass running ._test_some_elements() . . . pass running ._test_zero() . . . pass """ if not isinstance(R,MPolynomialRing_libsingular): raise TypeError, "A letterplace algebra must be provided by a polynomial ring of type %s"%MPolynomialRing_libsingular self.__ngens = R.ngens() base_ring = R.base_ring() Algebra.__init__(self, base_ring, R.variable_names(), normalize=False, category=Algebras(base_ring)) self._commutative_ring = R self._current_ring = make_letterplace_ring(R,1) self._degbound = 1 self._populate_coercion_lists_(coerce_list=[base_ring]) def __reduce__(self): """ TEST:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: loads(dumps(F)) is F    # indirect doctest True """ from sage.algebras.free_algebra import FreeAlgebra return FreeAlgebra,(self._commutative_ring,) # Small methods def ngens(self): """ Return the number of generators. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.ngens() 3 """ return self.__ngens def gen(self,i): """ Return the i-th generator. INPUT: i -- an integer. OUTPUT: Generator number i. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.1 is F.1  # indirect doctest True sage: F.gen(2) c """ if i>=self.__ngens: raise ValueError, "This free algebra only has %d generators"%self.__ngens if self._gens is not None: return self._gens[i] return FreeAlgebraElement_letterplace(self, self._current_ring.gen(i)) def current_ring(self): """ Return the commutative ring that is used to emulate the non-commutative multiplication out to the current degree. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.current_ring() Multivariate Polynomial Ring in a, b, c over Rational Field sage: a*b a*b sage: F.current_ring() Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1 over Rational Field sage: F.set_degbound(3) sage: F.current_ring() Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1, a_2, b_2, c_2 over Rational Field """ return self._current_ring def commutative_ring(self): """ Return the commutative version of this free algebra. NOTE: This commutative ring is used as a unique key of the free algebra. EXAMPLE:: sage: K. = GF(25) sage: F. = FreeAlgebra(K, implementation='letterplace') sage: F Free Associative Unital Algebra on 3 generators ('a', 'b', 'c') over Finite Field in z of size 5^2 sage: F.commutative_ring() Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2 sage: FreeAlgebra(F.commutative_ring()) is F True """ return self._commutative_ring def term_order_of_block(self): """ Return the term order that is used for the commutative version of this free algebra. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.term_order_of_block() Degree reverse lexicographic term order sage: L. = FreeAlgebra(QQ, implementation='letterplace',order='lex') sage: L.term_order_of_block() Lexicographic term order """ return self._commutative_ring.term_order() # Some basic properties of this ring def is_commutative(self): """ Tell whether this algebra is commutative, i.e., whether the generator number is one. NOTE: Currently, the FreeAlgebra constructor returns a polynomial ring when a free algebra with one generator is requested. EXAMPLE:: sage: FreeAlgebra(QQ,'x') Univariate Polynomial Ring in x over Rational Field sage: FreeAlgebra(QQ,'x',implementation='letterplace') Multivariate Polynomial Ring in x over Rational Field :: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.is_commutative() False With a bit more effort, we create a commutative free algebra that is not implemented as a polynomial ring:: sage: from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace sage: P = FreeAlgebra_letterplace(PolynomialRing(QQ,1,['x'])) sage: P Free Associative Unital Algebra on 1 generators ('x',) over Rational Field sage: P.is_commutative() True """ return self.__ngens <= 1 def is_field(self): """ Tell whether this free algebra is a field. NOTE: This would only be the case in the degenerate case of no generators. But such an example can not be constructed in this implementation. TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.is_field() False """ return (not self.__ngens) and self._base.is_field() def _repr_(self): """ EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F     # indirect doctest Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field """ return "Free Associative Unital Algebra on %d generators %s over %s"%(self.__ngens,self._names,self._base) def degbound(self): """ Return the degree bound that is currently used. NOTE: When multiplying two elements of this free algebra, the degree bound will be dynamically adapted. It can also be set by :meth:set_degbound. EXAMPLE: In order to avoid we get a free algebras from the cache that was created in another doctest and has a different degree bound, we choose a base ring that does not appear in other tests:: sage: F. = FreeAlgebra(ZZ, implementation='letterplace') sage: F.degbound() 1 sage: x*y x*y sage: F.degbound() 2 sage: F.set_degbound(4) sage: F.degbound() 4 """ return self._degbound def set_degbound(self,d): """ Increase the degree bound that is currently in place. NOTE: The degree bound can not be decreased. EXAMPLE: In order to avoid we get a free algebras from the cache that was created in another doctest and has a different degree bound, we choose a base ring that does not appear in other tests:: sage: F. = FreeAlgebra(GF(251), implementation='letterplace') sage: F.degbound() 1 sage: x*y x*y sage: F.degbound() 2 sage: F.set_degbound(4) sage: F.degbound() 4 sage: F.set_degbound(2) sage: F.degbound() 4 """ if d<=self._degbound: return self._degbound = d self._current_ring = make_letterplace_ring(self._commutative_ring,d) #    def base_extend(self, R): #        if self._base.has_coerce_map_from(R): #            return self ################################################ ## Ideals def _ideal_class_(self, n=0): """ Return the class :class:~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*F sage: I 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 sage: type(I) is F._ideal_class_() True """ from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal return LetterplaceIdeal def ideal_monoid(self): """ Return the monoid of ideals of this free algebra. EXAMPLE:: sage: F. = FreeAlgebra(GF(2), implementation='letterplace') sage: F.ideal_monoid() Monoid of ideals of Free Associative Unital Algebra on 2 generators ('x', 'y') over Finite Field of size 2 sage: F.ideal_monoid() is F.ideal_monoid() True """ if self.__monoid is None: self.__monoid = IdealMonoid_nc(self) return self.__monoid # Auxiliar methods cdef str exponents_to_string(self, E): """ This auxiliary method is used for the string representation of elements of this free algebra. EXAMPLE:: sage: F. = FreeAlgebra(GF(2), implementation='letterplace') sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace sage: P = F.commutative_ring() sage: FreeAlgebraElement_letterplace(F, P.0*P.1^2+P.1^3)   # indirect doctest x.y^2 + y^3 """ cdef int ngens = self.__ngens cdef int nblocks = len(E)/ngens cdef int i cdef list out = [] for i from 0<=i1 else x) for x,e in zip(self._names,E[i*ngens:(i+1)*ngens]) if e]) if s: out.append(s) return '*'.join(out) def _reductor_(self, g, d): """ Return a commutative ideal that can be used to compute the normal form of a free algebra element of a given degree. INPUT: g - a list of elements of this free algebra. d - an integer. OUTPUT: An ideal such that reduction of a letterplace polynomial by that ideal corresponds to reduction of an element of degree at most d by g. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: p = y*x*y + y*y*y + y*z*y - y*z*z sage: p.reduce(I) y*y*y - y*y*z + y*z*y - y*z*z sage: G = F._reductor_(I.gens(),3); G 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 We do not use the usual reduction method for polynomials in Sage, since it does the reductions in a different order compared to Singular. Therefore, we call the original Singular reduction method, and prevent a warning message by asserting that G is a Groebner basis. sage: from sage.libs.singular.function import singular_function sage: poly_reduce = singular_function("NF") sage: q = poly_reduce(p.letterplace_polynomial(), G, ring=F.current_ring(), attributes={G:{"isSB":1}}); q y*y_1*y_2 - y*y_1*z_2 + y*z_1*y_2 - y*z_1*z_2 sage: p.reduce(I).letterplace_polynomial() == q True """ cdef list out = [] C = self.current_ring() cdef FreeAlgebraElement_letterplace x ngens = self.__ngens degbound = self._degbound cdef list G = [C(x._poly) for x in g] for y in G: out.extend([y]+[singular_system("stest",y,n+1,degbound,ngens,ring=C) for n in xrange(d-y.degree())]) return C.ideal(out) ########################### ## Coercion def _coerce_map_from_(self,S): """ There is only a coercion from the base ring. TEST:: sage: F. = FreeAlgebra(GF(5), implementation='letterplace') sage: 5 == F.zero()    # indirect doctest True """ return self==S or self._current_ring.has_coerce_map_from(S) def _an_element_(self): """ Return an element. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F.an_element()   # indirect doctest x """ return FreeAlgebraElement_letterplace(self, self._current_ring.an_element(), check=False) #    def random_element(self, degree=2, terms=5): #        """ #        Return a random element of a given degree and with a given number of terms. # #        INPUT: # #        - degree -- the maximal degree of the output (default 2). #        - terms -- the maximal number of terms of the output (default 5). # #        NOTE: # #        This method is currently not useful at all. # #        Not tested. #        """ #        self.set_degbound(degree) #        while(1): #            p = self._current_ring.random_element(degree=degree,terms=terms) #            if p.is_homogeneous(): #                break #        return FreeAlgebraElement_letterplace(self, p, check=False) def _from_dict_(self, D, check=True): """ Create an element from a dictionary. INPUT: - A dictionary. Keys: tuples of exponents. Values: The coefficients of the corresponding monomial in the to-be-created element. - check (optional bool, default True): This is forwarded to the initialisation of :class:~sage.algebas.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace. TEST: This method applied to the dictionary of any element must return the same element. This must hold true even if the underlying letterplace ring has been extended in the meantime. :: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: p = 3*x*y+2*z^2 sage: F.set_degbound(10) sage: p == F._from_dict_(dict(p)) True For the empty dictionary, zero is returned:: sage: F._from_dict_({}) 0 """ if not D: return self.zero_element() cdef int l for e in D.iterkeys(): l = len(e) break cdef dict out = {} self.set_degbound(l/self.__ngens) cdef int n = self._current_ring.ngens() for e,c in D.iteritems(): out[tuple(e)+(0,)*(n-l)] = c return FreeAlgebraElement_letterplace(self,self._current_ring(out), check=check) def _element_constructor_(self, x): """ Return an element of this free algebra. INPUT: Something that can be interpreted in the polynomial ring that is used to implement the letterplace algebra out to the current degree bound, or a string that can be interpreted as an expression in the algebra (provided that the coefficients are numerical). EXAMPLE:: sage: F. = FreeAlgebra(GF(5), implementation='letterplace') sage: F(3) 3 sage: F('x*z+3*y^2') x*z + (3)*y*y sage: F.set_degbound(3) sage: P = F.current_ring() sage: F(P.0*P.4*P.6-2*P.2*P.5*P.8)   # indirect doctest x*y*x + (3)*z*z*z """ if isinstance(x, basestring): from sage.all import sage_eval return sage_eval(x,locals=self.gens_dict()) try: P = x.parent() except AttributeError: P = None if P is self: (x)._poly = self._current_ring((x)._poly) return x return FreeAlgebraElement_letterplace(self,self._current_ring(x))
• ## new file sage/algebras/letterplace/letterplace_ideal.pyx

diff --git a/sage/algebras/letterplace/letterplace_ideal.pyx b/sage/algebras/letterplace/letterplace_ideal.pyx
new file mode 100644
 - ############################################################################### # #       Copyright (C) 2011 Simon King #  Distributed under the terms of the GNU General Public License (GPL), #  version 2 or any later version.  The full text of the GPL is available at: #                  http://www.gnu.org/licenses/ # ############################################################################### """ Homogeneous ideals of free algebras. For twosided ideals and when the base ring is a field, this implementation also provides Groebner bases and ideal containment tests. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: F Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: I 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 One can compute Groebner bases out to a finite degree, can compute normal forms and can test containment in the ideal:: sage: I.groebner_basis(degbound=3) 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 sage: (x*y*z*y*x).normal_form(I) y*z*z*y*z + y*z*z*z*x + y*z*z*z*z sage: x*y*z*y*x - (x*y*z*y*x).normal_form(I) in I True AUTHOR: - Simon King (2011-03-22):  See trac ticket #7797. """ from sage.rings.noncommutative_ideals import Ideal_nc from sage.libs.singular.function import lib, singular_function from sage.algebras.letterplace.free_algebra_letterplace cimport FreeAlgebra_letterplace from sage.algebras.letterplace.free_algebra_element_letterplace cimport FreeAlgebraElement_letterplace ##################### # Define some singular functions lib("freegb.lib") singular_system=singular_function("system") poly_reduce=singular_function("NF") class LetterplaceIdeal(Ideal_nc): """ Homogeneous ideals in free algebras. In the two-sided case over a field, one can compute Groebner bases up to a degree bound, normal forms of homogeneous elements of the free algebra, and ideal containment. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: I 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 sage: I.groebner_basis(2) 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 sage: I.groebner_basis(4) 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 Groebner bases are cached. If one has computed a Groebner basis out to a high degree then it will also be returned if a Groebner basis with a lower degree bound is requested:: sage: I.groebner_basis(2) 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 Of course, the normal form of any element has to satisfy the following:: sage: x*y*z*y*x - (x*y*z*y*x).normal_form(I) in I True Left and right ideals can be constructed, but only twosided ideals provide Groebner bases:: sage: JL = F*[x*y+y*z,x^2+x*y-y*x-y^2]; JL 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 sage: JR = [x*y+y*z,x^2+x*y-y*x-y^2]*F; JR 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 sage: JR.groebner_basis(2) Traceback (most recent call last): ... TypeError: This ideal is not two-sided. We can only compute two-sided Groebner bases sage: JL.groebner_basis(2) Traceback (most recent call last): ... TypeError: This ideal is not two-sided. We can only compute two-sided Groebner bases Also, it is currently not possible to compute a Groebner basis when the base ring is not a field:: sage: FZ. = FreeAlgebra(ZZ, implementation='letterplace') sage: J = FZ*[a^3-b^3]*FZ sage: J.groebner_basis(2) Traceback (most recent call last): ... TypeError: Currently, we can only compute Groebner bases if the ring of coefficients is a field """ def __init__(self, ring, gens, coerce=True, side = "twosided"): """ INPUT: - ring: A free algebra in letterplace implementation. - gens: List, tuple or sequence of generators. - coerce (optional bool, default True): Shall gens be coerced first? - side: optional string, one of "twosided" (default), "left" or "right". Determines whether the ideal is a left, right or twosided ideal. Groebner bases or only supported in the twosided case. TEST:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal sage: LetterplaceIdeal(F,x) Twosided Ideal (x) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field sage: LetterplaceIdeal(F,[x,y],side='left') Left Ideal (x, y) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field It is not correctly detected that this class inherits from an extension class. Therefore, we have to skip one item of the test suite:: sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: TestSuite(I).run(skip=['_test_category'],verbose=True) running ._test_eq() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass """ Ideal_nc.__init__(self, ring, gens, coerce=coerce, side=side) self.__GB = self self.__uptodeg = 0 def groebner_basis(self, degbound=None): """ Twosided Groebner basis with degree bound. INPUT: - degbound (optional integer): If it is provided, a Groebner basis at least out to that degree is returned. By default, the current degree bound of the underlying ring is used. ASSUMPTIONS: Currently, we can only compute Groebner bases for twosided ideals, and the ring of coefficients must be a field. A TypeError is raised if one of these conditions is violated. NOTE: The result is cached. The same Groebner basis is returned if a smaller degree bound than the known one is requested. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F Since F was cached and since its degree bound can not be decreased, it may happen that, as a side effect of other tests, it already has a degree bound bigger than 3. So, we can not test against the output of I.groebner_basis():: sage: F.set_degbound(3) sage: I.groebner_basis()   # not tested 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 sage: I.groebner_basis(4) 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 sage: I.groebner_basis(2) is I.groebner_basis(4) True sage: G = I.groebner_basis(4) sage: G.groebner_basis(3) is G True """ A = self.ring() if degbound is None: degbound = A.degbound() if self.__uptodeg >= degbound: return self.__GB if not A.base().is_field(): raise TypeError, "Currently, we can only compute Groebner bases if the ring of coefficients is a field" if self.side()!='twosided': raise TypeError, "This ideal is not two-sided. We can only compute two-sided Groebner bases" # Set the options required by letterplace from sage.libs.singular.option import LibSingularOptions libsingular_options = LibSingularOptions() bck = (libsingular_options['redTail'],libsingular_options['redSB']) libsingular_options['redTail'] = True libsingular_options['redSB'] = True A.set_degbound(degbound) P = A.current_ring() cdef FreeAlgebraElement_letterplace x out = [FreeAlgebraElement_letterplace(A,X,check=False) for X in singular_system("freegb",P.ideal([x._poly for x in self.__GB.gens()]), degbound,A.ngens(), ring = P)] libsingular_options['redTail'] = bck[0] libsingular_options['redSB'] = bck[1] self.__GB = A.ideal(out,side='twosided',coerce=False) self.__uptodeg = degbound self.__GB.__uptodeg = degbound return self.__GB def __contains__(self,x): """ The containment test is based on a normal form computation. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: x*I.0-I.1*y+I.0*y in I    # indirect doctest True sage: 1 in I False """ R = self.ring() return (x in R) and R(x).normal_form(self).is_zero() def reduce(self, G): """ Reduction of this ideal by another ideal, or normal form of an algebra element with respect to this ideal. INPUT: - G: A list or tuple of elements, an ideal, the ambient algebra, or a single element. OUTPUT: - The normal form of G with respect to this ideal, if G is an element of the algebra. - The reduction of this ideal by the elements resp. generators of G, if G is a list, tuple or ideal. - The zero ideal, if G is the algebra containing this ideal. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: I.reduce(F) Twosided Ideal (0) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field sage: I.reduce(x^3) -y*z*x - y*z*y - y*z*z sage: I.reduce([x*y]) Twosided Ideal (y*z, x*x - y*x - y*y) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field sage: I.reduce(F*[x^2+x*y,y^2+y*z]*F) Twosided Ideal (x*y + y*z, -y*x + y*z) of Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field """ P = self.ring() if not isinstance(G,(list,tuple)): if G==P: return P.ideal([P.zero_element()]) if G in P: return G.normal_form(self) G = G.gens() C = P.current_ring() sI = C.ideal([C(X.letterplace_polynomial()) for X in self.gens()], coerce=False) selfdeg = max([x.degree() for x in sI.gens()]) gI = P._reductor_(G, selfdeg) from sage.libs.singular.option import LibSingularOptions libsingular_options = LibSingularOptions() bck = (libsingular_options['redTail'],libsingular_options['redSB']) libsingular_options['redTail'] = True libsingular_options['redSB'] = True sI = poly_reduce(sI,gI, ring=C, attributes={gI:{"isSB":1}}) libsingular_options['redTail'] = bck[0] libsingular_options['redSB'] = bck[1] 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 """ return QuaternionOrder(self, basis, check=check) def ideal(self, gens, left_order=None, right_order=None, check=True): def ideal(self, gens, left_order=None, right_order=None, check=True, **kwds): r""" Return the quaternion ideal with given gens over \ZZ. 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 from sage.categories.category import Category from category import HomCategory from sage.misc.cachefunc import cached_method import sage class Rings(Category): """ """ return x*y - y*x # this is already in sage.rings.ring.Ring, # but not all rings descend from that class, # e.g., matrix spaces. def _mul_(self, x, switch_sides=False): """ Multiplication of rings with, e.g., lists. NOTE: This method is used to create ideals. It is the same as the multiplication method for :class:~sage.rings.ring.Ring. However, not all parents that belong to the category of rings also inherits from the base class of rings. Therefore, we implemented a __mul__ method for parents, that calls a _mul_ method implemented here. See trac ticket #7797. INPUT: - x, an object to multiply with. - switch_sides (optional bool): If False, the product is self*x; if True, the product is x*self. EXAMPLE: As we mentioned above, this method is called when a ring is involved that does not inherit from the base class of rings. This is the case, e.g., for matrix algebras:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS*2     # indirect doctest Left Ideal ( [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field In the next example, the ring and the other factor switch sides in the product:: sage: [MS.2]*MS Right Ideal ( [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field AUTHOR: - Simon King (2011-03-22) """ try: if self.is_commutative(): return self.ideal(x) except (AttributeError,NotImplementedError): pass try: side = x.side() except AttributeError: return self.ideal(x, side='right' if switch_sides else 'left') # presumably x is an ideal... try: x = x.gens() except (AttributeError, NotImplementedError): pass # ... not an ideal if switch_sides: if side in ['right','twosided']: return self.ideal(x,side=side) elif side=='left': return self.ideal(x,side='twosided') else: if side in ['left','twosided']: return self.ideal(x,side=side) elif side=='right': return self.ideal(x,side='twosided') # duck typing failed raise TypeError, "Don't know how to transform %s into an ideal of %s"%(x,self) @cached_method def ideal_monoid(self): """ The monoid of the ideals of this ring. NOTE: The code is copied from the base class of rings. This is since there are rings that do not inherit from that class, such as matrix algebras.  See trac ticket #7797. EXAMPLE:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS.ideal_monoid() Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Rational Field Note that the monoid is cached:: sage: MS.ideal_monoid() is MS.ideal_monoid() True """ try: from sage.rings.ideal_monoid import IdealMonoid return IdealMonoid(self) except TypeError: from sage.rings.noncommutative_ideals import IdealMonoid_nc return IdealMonoid_nc(self) def ideal(self, *args, **kwds): """ Create an ideal of this ring. NOTE: The code is copied from the base class :class:~sage.rings.ring.Ring. This is because there are rings that do not inherit from that class, such as matrix algebras. See trac ticket #7797. INPUT: - An element or a list/tuple/sequence of elements. - coerce (optional bool, default True): First coerce the elements into this ring. - side, optional string, one of "twosided" (default), "left", "right": determines whether the resulting ideal is twosided, a left ideal or a right ideal. EXAMPLE:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS.ideal(2) Twosided Ideal ( [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS.ideal([MS.0,MS.1],side='right') Right Ideal ( [1 0] [0 0], [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ if kwds.has_key('coerce'): coerce = kwds['coerce'] del kwds['coerce'] else: coerce = True from sage.rings.ideal import Ideal_generic from types import GeneratorType if len(args) == 0: gens = [self(0)] else: gens = args while isinstance(gens, (list, tuple, GeneratorType)) and len(gens) == 1: first = gens[0] if isinstance(first, Ideal_generic): R = first.ring() m = self.convert_map_from(R) if m is not None: gens = [m(g) for g in first.gens()] coerce = False else: m = R.convert_map_from(self) if m is not None: raise NotImplementedError else: raise TypeError break elif isinstance(first, (list, tuple, GeneratorType)): gens = first else: try: if self.has_coerce_map_from(first): gens = first.gens() # we have a ring as argument elif hasattr(first,'parent'): gens = [first] else: raise ArithmeticError, "There is no coercion from %s to %s"%(first,self) except TypeError: # first may be a ring element pass break if coerce: gens = [self(g) for g in gens] from sage.categories.principal_ideal_domains import PrincipalIdealDomains if self in PrincipalIdealDomains(): # Use GCD algorithm to obtain a principal ideal g = gens[0] if len(gens) == 1: try: g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. except (AttributeError, NotImplementedError): pass else: for h in gens[1:]: g = g.gcd(h) gens = [g] if kwds.has_key('ideal_class'): C = kwds['ideal_class'] del kwds['ideal_class'] else: C = self._ideal_class_(len(gens)) if len(gens) == 1 and isinstance(gens[0], (list, tuple)): gens = gens[0] return C(self, gens, **kwds) def _ideal_class_(self,n=0): """ Return the class that is used to implement ideals of this ring. NOTE: We copy the code from :class:~sage.rings.ring.Ring. This is necessary because not all rings inherit from that class, such as matrix algebras. INPUT: - n (optional integer, default 0): The number of generators of the ideal to be created. OUTPUT: The class that is used to implement ideals of this ring with n generators. NOTE: Often principal ideals (n==1) are implemented via a different class. EXAMPLES:: sage: MS = MatrixSpace(QQ,2,2) sage: MS._ideal_class_() We don't know of a commutative ring in Sage that does not inherit from the base class of rings. So, we need to cheat in the next example:: sage: super(Ring,QQ)._ideal_class_.__module__ 'sage.categories.rings' sage: super(Ring,QQ)._ideal_class_() sage: super(Ring,QQ)._ideal_class_(1) sage: super(Ring,QQ)._ideal_class_(2) """ from sage.rings.noncommutative_ideals import Ideal_nc try: if not self.is_commutative(): return Ideal_nc except (NotImplementedError,AttributeError): return Ideal_nc from sage.rings.ideal import Ideal_generic, Ideal_principal if n == 1: return Ideal_principal else: return Ideal_generic ## # Quotient rings # Again, this is defined in sage.rings.ring.pyx def quotient(self, I, names=None): """ Quotient of a ring by a two-sided ideal. INPUT: - I: A twosided ideal of this ring. - names: a list of strings to be used as names for the variables in the quotient ring. EXAMPLES:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: F.quo(I) 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) sage: Q = F.quotient(I) sage: Q.0 xbar sage: Q.1 ybar sage: Q.2 zbar sage: Q.0*Q.1 -ybar*zbar """ from sage.rings.quotient_ring import QuotientRing return QuotientRing(self, I, names=names) def quo(self, I, names=None): """ Quotient of a ring by a two-sided ideal. NOTE: This is a synonyme for :meth:quotient. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.0^4 ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar """ return self.quotient(I,names=names) def quotient_ring(self, I, names=None): """ Quotient of a ring by a two-sided ideal. NOTE: This is a synonyme for :meth:quotient. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.0^4 ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar """ return self.quotient(I,names=names) def __div__(self, I): raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring." class ElementMethods: pass
• ## sage/rings/ideal.py

diff --git a/sage/rings/ideal.py b/sage/rings/ideal.py
 a r""" Ideals Ideals of commutative rings. Sage provides functionality for computing with ideals. One can create an ideal in any commutative ring R by giving a list of generators, using the notation R.ideal([a,b,...]). Sage provides functionality for computing with ideals. One can create an ideal in any commutative or non-commutative ring R by giving a list of generators, using the notation R.ideal([a,b,...]). The case of non-commutative rings is implemented in :mod:~sage.rings.noncommutative_ideals. A more convenient notation may be R*[a,b,...] or [a,b,...]*R. If R is non-commutative, the former creates a left and the latter a right ideal, and R*[a,b,...]*R creates a two-sided ideal. """ #***************************************************************************** MonoidElement.__init__(self, ring.ideal_monoid()) def _repr_short(self): return '(%s)'%(', '.join([str(x) for x in self.gens()])) """ Represent the list of generators. EXAMPLE:: sage: P. = QQ[] sage: P*[a^2,a*b+c,c^3] Ideal (a^2, a*b + c, c^3) of Multivariate Polynomial Ring in a, b, c over Rational Field sage: (P*[a^2,a*b+c,c^3])._repr_short() '(a^2, a*b + c, c^3)' If the string representation of a generator contains a line break, the generators are not represented from left to right but from top to bottom. This is the case, e.g., for matrices:: sage: MS = MatrixSpace(QQ,2,2) sage: MS*[MS.1,2] Left Ideal ( [0 1] [0 0], [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ L = [] has_return = False for x in self.gens(): s = repr(x) if '\n' in s: has_return = True s = s.replace('\n','\n  ') L.append(s) if has_return: return '\n(\n  %s\n)\n'%(',\n\n  '.join(L)) return '(%s)'%(', '.join(L)) def __repr__(self): return "Ideal %s of %s"%(self._repr_short(), self.ring()) return self.ring().ideal(self.gens() + other.gens()) def __mul__(self, other): """ EXAMPLE:: sage: P. = QQ[] sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*P sage: I*2    # indirect doctest Ideal (2*x*y + 2*y*z, 2*x^2 - 2*y^2) of Multivariate Polynomial Ring in x, y, z over Rational Field """ if not isinstance(other, Ideal_generic): try: if self.ring().has_coerce_map_from(other): return self except (TypeError,ArithmeticError,ValueError): pass other = self.ring().ideal(other) return self.ring().ideal([x*y for x in self.gens() for y in other.gens()]) return self.ring().ideal([z for z in [x*y for x in self.gens() for y in other.gens()] if z]) def __rmul__(self, other): """ EXAMPLE:: sage: P. = QQ[] sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*P sage: [2]*I    # indirect doctest Ideal (2*x*y + 2*y*z, 2*x^2 - 2*y^2) of Multivariate Polynomial Ring in x, y, z over Rational Field """ if not isinstance(other, Ideal_generic): try: if self.ring().has_coerce_map_from(other): return self except (TypeError,ArithmeticError,ValueError): pass other = self.ring().ideal(other) return self.ring().ideal([y*x for x in self.gens() for y in other.gens()]) return self.ring().ideal([z for z in [y*x for x in self.gens() for y in other.gens()] if z]) def norm(self): """
• ## 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
 - ############################################################################### # #       Copyright (C) 2011 Simon King #  Distributed under the terms of the GNU General Public License (GPL), #  version 2 or any later version.  The full text of the GPL is available at: #                  http://www.gnu.org/licenses/ # ############################################################################### """ Generic implementation of one- and twosided ideals of non-commutative rings. AUTHOR: - Simon King (2011-03-21), : Trac ticket #7797. EXAMPLES:: sage: MS = MatrixSpace(ZZ,2,2) sage: MS*MS([0,1,-2,3]) Left Ideal ( [ 0  1] [-2  3] ) of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring sage: MS([0,1,-2,3])*MS Right Ideal ( [ 0  1] [-2  3] ) of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring sage: MS*MS([0,1,-2,3])*MS Twosided Ideal ( [ 0  1] [-2  3] ) of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring See :mod:~sage.algebras.letterplace.letterplace_ideal for a more elaborate implementation in the special case of ideals in free algebras. TEST:: sage: A = SteenrodAlgebra(2) sage: IL = A*[A.1+A.2,A.1^2]; IL Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: TestSuite(IL).run(skip=['_test_category'],verbose=True) running ._test_eq() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass """ from sage.rings.ideal_monoid import IdealMonoid_c from sage.structure.parent import Parent from sage.categories.monoids import Monoids from sage.rings.ideal import Ideal_generic import sage class IdealMonoid_nc(IdealMonoid_c): """ Base class for the monoid of ideals over a non-commutative ring. NOTE: This class is essentially the same as :class:~sage.rings.ideal_monoid.IdealMonoid_c, but does not complain about non-commutative rings. EXAMPLE:: sage: MS = MatrixSpace(ZZ,2,2) sage: MS.ideal_monoid() Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring """ def __init__(self, R): """ INPUT: A ring. TEST:: sage: from sage.rings.noncommutative_ideals import IdealMonoid_nc sage: MS = MatrixSpace(ZZ,2,2) sage: IdealMonoid_nc(MS) Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring """ self._IdealMonoid_c__R = R Parent.__init__(self, base = sage.rings.integer_ring.ZZ, category = Monoids()) self._populate_coercion_lists_() def _element_constructor_(self, x): r""" Create an ideal in this monoid from x. INPUT: An ideal, or a list of elements. TESTS:: sage: A = SteenrodAlgebra(2) sage: IL = A*[A.1+A.2,A.1^2]; IL Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: IR = [A.1+A.2,A.1^2]*A; IR Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: IT = A*[A.1+A.2,A.1^2]*A; IT Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: M = IL.parent() sage: M([A.0, 1]) Twosided Ideal (Sq(1), Sq(0)) of mod 2 Steenrod algebra :: sage: IL == loads(dumps(IL)) True sage: IR == loads(dumps(IR)) True sage: IT == loads(dumps(IT)) True """ side = "twosided" if isinstance(x, Ideal_nc): side = x.side() x = x.gens() elif isinstance(x, Ideal_generic): x = x.gens() y = self._IdealMonoid_c__R.ideal(x,side=side) y._set_parent(self) return y class Ideal_nc(Ideal_generic): """ Generic non-commutative ideal. All fancy stuff such as the computation of Groebner bases must be implemented in sub-classes. See :class:~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal for an example. EXAMPLE:: sage: MS = MatrixSpace(QQ,2,2) sage: I = MS*[MS.1,MS.2]; I Left Ideal ( [0 1] [0 0], [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: [MS.1,MS.2]*MS Right Ideal ( [0 1] [0 0], [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS*[MS.1,MS.2]*MS Twosided Ideal ( [0 1] [0 0], [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ def __init__(self, ring, gens, coerce=True, side = "twosided"): """ INPUT: - A ring. - A list or tuple of elements. - coerce (optional bool, default True): First coerce the given list of elements into the given ring. - side (option string, default "twosided"): Must be "left", "right" or "twosided". Determines whether the ideal is a left, right or twosided ideal. TEST:: sage: MS = MatrixSpace(ZZ,2,2) sage: from sage.rings.noncommutative_ideals import Ideal_nc sage: Ideal_nc(MS,[MS.1,MS.2], side='left') Left Ideal ( [0 1] [0 0], [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring sage: Ideal_nc(MS,[MS.1,MS.2], side='right') Right Ideal ( [0 1] [0 0], [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring """ if side not in ['left','right','twosided']: raise ValueError, "Ideals are left, right or twosided, but not %s"%side self.__side = side Ideal_generic.__init__(self, ring, gens, coerce=coerce) def __repr__(self): """ TEST:: sage: A = SteenrodAlgebra(2) sage: A*[A.1+A.2,A.1^2]      # indirect doctest Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: [A.1+A.2,A.1^2]*A Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: A*[A.1+A.2,A.1^2]*A Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra """ return "%s Ideal %s of %s"%(self.__side.capitalize(),self._repr_short(),self.ring()) def __cmp__(self, right): """ Ideals of different sidedness do not compare equal. Apart from that, the generators are compared. EXAMPLE:: sage: A = SteenrodAlgebra(2) sage: IR = [A.1+A.2,A.1^2]*A sage: IL = A*[A.1+A.2,A.1^2] sage: IT = A*[A.1+A.2,A.1^2]*A sage: IT==IL False sage: IR==[A.1+A.2,A.1^2]*A True """ if not isinstance(right,Ideal_nc): return -1 c = cmp(self.side(),right.side()) if c: return c S = set(self.gens()) T = set(right.gens()) if S == T: return 0 return cmp(self.gens(), right.gens()) def side(self): """ Return a string that describes the sidedness of this ideal. EXAMPLES:: sage: A = SteenrodAlgebra(2) sage: IL = A*[A.1+A.2,A.1^2] sage: IR = [A.1+A.2,A.1^2]*A sage: IT = A*[A.1+A.2,A.1^2]*A sage: IL.side() 'left' sage: IR.side() 'right' sage: IT.side() 'twosided' """ return self.__side def __mul__(self, other): """ Multiplication of a one-sided ideal with its ring from the other side yields a two-sided ideal. TESTS:: sage: MS = MatrixSpace(QQ,2,2) sage: IL = MS*[2*MS.0,3*MS.1]; IL Left Ideal ( [2 0] [0 0], [0 3] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: IR = MS.3*MS; IR Right Ideal ( [0 0] [0 1] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: IL*MS     # indirect doctest Twosided Ideal ( [2 0] [0 0], [0 3] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: IR*IR Traceback (most recent call last): ... NotImplementedError: Can not multiply non-commutative ideals. """ if not isinstance(other, Ideal_nc): # Perhaps other is a ring and thus has its own # multiplication. if other == self.ring(): if self.side()=='right': return self return self.ring().ideal(self.gens(),side='twosided') if not isinstance(self, Ideal_nc): # This may happen... if self == other.ring(): if other.side()=='left': return other return other.ring().ideal(other.gens(),side='twosided') 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 True """ return sage.rings.polynomial.multi_polynomial_libsingular.unpickle_MPolynomialRing_libsingular, ( self.base_ring(), map(str, self.gens()), #                                                                                               map(str, self.gens()), self.variable_names(), self.term_order() ) 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 AUTHORS: - William Stein - William Stein: Quotients of commutative rings. - Simon King (2011-03-23): Quotients of non-commutative rings by twosided ideals. TESTS:: sage: S = R.quotient_ring(I); sage: S == loads(dumps(S)) True In trac ticket #7797, non-commutative quotient rings R/I were implemented.  The only requirement is that the two-sided ideal I provides a reduce method so that I.reduce(x) is the normal form of an element x with respect to I (i.e., we have I.reduce(x)==I.reduce(y) if x-y\\in I, and x-I.reduce(x) in I):: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.0^4 ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar sage: Q.1*x ybar*xbar sage: x*Q.1 -ybar*zbar """ ########################################################################### import quotient_ring_element import sage.misc.latex as latex import commutative_ring import commutative_ring, ring import ideal import sage.rings.polynomial.multi_polynomial_ideal import sage.structure.parent_gens from sage.interfaces.all import singular as singular_default, is_SingularElement from sage.categories.rings import Rings def QuotientRing(R, I, names=None): r""" Creates a quotient ring of the ring R by the ideal I. Variables are labeled by names (if the quotient ring is a quotient of a polynomial ring).  If names isn't given, 'bar' will be appended to the variable names in R. Creates a quotient ring of the ring R by the twosided ideal I. Variables are labeled by names (if the quotient ring is a quotient of a polynomial ring).  If names isn't given, 'bar' will be appended to the variable names in R. INPUTS: - R - a commutative ring - R - a ring. - I - an ideal of R - I - a twosided ideal of R. - names - a list of strings to be used as names for the variables in the quotient ring R/I - names - a list of strings to be used as names for the variables in the quotient ring R/I. OUTPUTS: R/I - the quotient ring R mod the ideal I OUTPUTS: R/I - the quotient ring R mod the ideal I. ASSUMPTION: I has an attribute I.reduce(x) returning the normal form of elements x\in R. In other words, it is required that I.reduce(x)==I.reduce(y) \iff x-y \in I, and x-I.reduce(x) in I, for all x,y\in R. EXAMPLES: Some simple quotient rings with the integers:: d -1 -d Here is an example of the quotient of a free algebra by a twosided homogeneous ideal (see trac ticket #7797):: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q. = F.quo(I); Q 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) sage: a*b -b*c sage: a^3 -b*c*a - b*c*b - b*c*c sage: J = Q*[a^3-b^3]*Q sage: R. = Q.quo(J); R 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) sage: i^3 -j*k*i - j*k*j - j*k*k sage: j^3 -j*k*i - j*k*j - j*k*k """ if not isinstance(R, commutative_ring.CommutativeRing): raise TypeError, "R must be a commutative ring." # 1. Not all rings inherit from the base class of rings. # 2. We want to support quotients of free algebras by homogeneous two-sided ideals. #if not isinstance(R, commutative_ring.CommutativeRing): #    raise TypeError, "R must be a commutative ring." if not R in Rings(): raise TypeError, "R must be a ring." try: is_commutative = R.is_commutative() except (AttributeError, NotImplementedError): is_commutative = False if names is None: names = tuple([x + 'bar' for x in R.variable_names()]) else: return R.quotient_by_principal_ideal(I.gen(), names) except (AttributeError, NotImplementedError): pass if isinstance(R, QuotientRing_generic): if not is_commutative: try: if I.side() != 'twosided': raise AttributeError except AttributeError: raise TypeError, "A twosided ideal is required." if isinstance(R, QuotientRing_nc): pi = R.cover() S = pi.domain() G = [pi.lift(x) for x in I.gens()] I_lift = S.ideal(G) J = R.defining_ideal() return QuotientRing_generic(S, I_lift + J, names) return R.__class__(S, I_lift + J, names) if R.is_field(): if I.is_zero(): return R else: return 0 return QuotientRing_generic(R, I, names) if isinstance(R, sage.rings.commutative_ring.CommutativeRing): return QuotientRing_generic(R, I, names) return QuotientRing_nc(R, I, names) def is_QuotientRing(x): """ Tests whether or not x inherits from :class:QuotientRing_generic. Tests whether or not x inherits from :class:QuotientRing_nc. EXAMPLES:: True sage: is_QuotientRing(R) False :: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: is_QuotientRing(Q) True sage: is_QuotientRing(F) False """ return isinstance(x, QuotientRing_generic) return isinstance(x, QuotientRing_nc) class QuotientRing_generic(commutative_ring.CommutativeRing, sage.structure.parent_gens.ParentWithGens): class QuotientRing_nc(ring.Ring, sage.structure.parent_gens.ParentWithGens): """ The quotient ring of R by the ideal I. The quotient ring of a ring R by a twosided ideal I. EXAMPLES:: EXAMPLES: This class is for rings that do not inherit from :class:~sage.rings.ring.CommutativeRing. Here is a quotient of a free algebra by a twosided homogeneous ideal:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q. = F.quo(I); Q 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) sage: a*b -b*c sage: a^3 -b*c*a - b*c*b - b*c*c A quotient of a quotient is just the quotient of the original top ring by the sum of two ideals:: sage: J = Q*[a^3-b^3]*Q sage: R. = Q.quo(J); R 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) sage: i^3 -j*k*i - j*k*j - j*k*k sage: j^3 -j*k*i - j*k*j - j*k*k For rings that *do* inherit from :class:~sage.rings.ring.CommutativeRing, we provide a subclass :class:QuotientRing_generic, for backwards compatibility:: sage: R. = PolynomialRing(ZZ,'x') sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: S(0) == a^2 + b^2 True EXAMPLE: Quotient of quotient A quotient of a quotient is just the quotient of the original top Again, a quotient of a quotient is just the quotient of the original top ring by the sum of two ideals. :: """ def __init__(self, R, I, names): """ Create the quotient ring of R by the ideal I. Create the quotient ring of R by the twosided ideal I. INPUT: -  R - a commutative ring -  I - an ideal -  R - a ring. -  I - a twosided ideal of R. - names - a list of generator names. EXAMPLES:: sage: R. = PolynomialRing(QQ) sage: R.quotient_ring(x^2 + y^2) Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q. = F.quo(I); Q 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) sage: a*b -b*c sage: a^3 -b*c*a - b*c*b - b*c*c """ self.__R = R self.__I = I sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: R.quotient_ring(I).construction() (QuotientFunctor, Univariate Polynomial Ring in x over Integer Ring) sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.construction() (QuotientFunctor, Free Associative Unital Algebra on 3 generators ('x', 'y', 'z') over Rational Field) TESTS:: sage: F, R = Integers(5).construction() """ return "%s/%s"%(latex.latex(self.cover_ring()), latex.latex(self.defining_ideal())) def is_commutative(self): """ Tell whether this quotient ring is commutative. NOTE: This is certainly the case if the cover ring is commutative. Otherwise, if this ring has a finite number of generators, it is tested whether they commute. If the number of generators is infinite, a NotImplementedError is raised. AUTHOR: - Simon King (2011-03-23): See trac ticket #7797. EXAMPLES: Any quotient of a commutative ring is commutative:: sage: P. = QQ[] sage: P.quo(P.random_element()).is_commutative() True The non-commutative case is more interesting:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.is_commutative() False sage: Q.1*Q.2==Q.2*Q.1 False In the next example, the generators apparently commute:: sage: J = F*[x*y-y*x,x*z-z*x,y*z-z*y,x^3-y^3]*F sage: R = F.quo(J) sage: R.is_commutative() True """ try: if self.__R.is_commutative(): return True except (AttributeError, NotImplementedError): pass from sage.all import Infinity if self.ngens() == Infinity: raise NotImplementedError, "This quotient ring has an infinite number of generators." for i in xrange(self.ngens()): gi = self.gen(i) for j in xrange(i+1,self.ngens()): gj = self.gen(j) if gi*gj!=gj*gi: return False return True def cover(self): r""" The covering ring homomorphism R \to R/I, equipped with a sage: R.quotient_ring(x^2 + y^2) == R.quotient_ring(-x^2 - y^2) False """ if not isinstance(other, QuotientRing_generic): if not isinstance(other, QuotientRing_nc): return cmp(type(self), type(other)) return cmp((self.cover_ring(), self.defining_ideal().gens()), (other.cover_ring(), other.defining_ideal().gens())) Degree reverse lexicographic term order """ return self.__R.term_order() class QuotientRing_generic(QuotientRing_nc, sage.rings.commutative_ring.CommutativeRing): r""" Creates a quotient ring of a *commutative* ring R by the ideal I. EXAMPLE:: sage: R. = PolynomialRing(ZZ) sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: S = R.quotient_ring(I); S Quotient of Univariate Polynomial Ring in x over Integer Ring by the ideal (x^2 + 3*x + 4, x^2 + 1) """ def __init__(self, R, I, names): """ INPUT: -  R - a ring that is of type . -  I - an ideal of R. - names - a list of generator names. TEST:: sage: isinstance(ZZ.quo(2), sage.rings.ring.CommutativeRing)  # indirect doctest True """ if not isinstance(R, sage.rings.commutative_ring.CommutativeRing): raise TypeError, "This class is for quotients of commutative rings only.\n    For non-commutative rings, use " 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 0 sage: a.__cmp__(b) 1 See trac ticket #7797: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.0^4    # indirect doctest ybar*zbar*zbar*xbar + ybar*zbar*zbar*ybar + ybar*zbar*zbar*zbar """ if self.__rep == other.__rep or ((self.__rep - other.__rep) in self.parent().defining_ideal()): I = self.parent().defining_ideal() if self.__rep == other.__rep or I.reduce(self.__rep)==I.reduce(other.__rep):#((self.__rep - other.__rep) in self.parent().defining_ideal()): return 0 return cmp(self.__rep, other.__rep)
• ## sage/rings/ring.pxd

diff --git a/sage/rings/ring.pxd b/sage/rings/ring.pxd
 a cdef public object _zero_ideal cdef public object _unit_ideal cdef _an_element_c_impl(self) cdef public object __ideal_monoid cdef class CommutativeRing(Ring): cdef public object __fraction_field cdef public object __ideal_monoid cdef class IntegralDomain(CommutativeRing): pass
• ## sage/rings/ring.pyx

diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx
 a - David Harvey (2006-10-16): changed :class:CommutativeAlgebra to derive from :class:CommutativeRing instead of from :class:Algebra - David Loeffler (2009-07-09): documentation fixes, added to reference manual - Simon King (2011-03-22): Modify multiplication and _ideal_class_ to support ideals of non-commutative rings. """ #***************************************************************************** from sage.categories.rings import Rings return Rings() def ideal_monoid(self): """ Return the monoid of ideals of this ring. EXAMPLES:: sage: F. = FreeAlgebra(ZZ, implementation='letterplace') sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F sage: Q = F.quo(I) sage: Q.ideal_monoid() 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) """ if self.__ideal_monoid is not None: return self.__ideal_monoid else: from sage.rings.noncommutative_ideals import IdealMonoid_nc M = IdealMonoid_nc(self) self.__ideal_monoid = M return M def ideal(self, *args, **kwds): """ Return the ideal defined by x, i.e., generated by x. already in the ring. - ideal_class -- callable (default: self._ideal_class_()); this must be a keyword argument. A constructor for ideals, taking the ring as the first argument and then the generators. Usually a subclass of sage.rings.ideal.Ideal_generic. TODO: For noncommutative rings, distinguish between ideals, right ideals, and left ideals. Usually a subclass of :class:~sage.rings.ideal.Ideal_generic or :class:~sage.rings.noncommutative_ideals.Ideal_nc. - Further named arguments (such as side in the case of non-commutative rings) are forwarded to the ideal class. EXAMPLES:: Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field sage: R.ideal( [x^3,y^3+x^3] ) Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field :: sage: A = SteenrodAlgebra(2) sage: A.ideal(A.1,A.2^2) Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra sage: A.ideal(A.1,A.2^2,side='left') Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra """ if kwds.has_key('coerce'): coerce = kwds['coerce'] break elif isinstance(first, (list, tuple, GeneratorType)): gens = first elif self.has_coerce_map_from(first): gens = first.gens() # we have a ring as argument else: break if coerce: Return the ideal x*R generated by x, where x is either an element or tuple or list of elements. TODO: For noncommutative rings, distinguish between ideals, right ideals, and left ideals. EXAMPLES:: sage: R. = GF(7)[] Ideal (x + y) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 7 sage: (x+y,z+y^3)*R Ideal (x + y, y^3 + z) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 7 The following was implemented in trac ticket #7797:: sage: A = SteenrodAlgebra(2) sage: A*[A.1+A.2,A.1^2] Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: [A.1+A.2,A.1^2]*A Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra sage: A*[A.1+A.2,A.1^2]*A Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra """ if isinstance(self, Ring): return self.ideal(x) else: return x.ideal(self)    # switched because this is a Cython / extension class if self.is_commutative(): return self.ideal(x) try: side = x.side() except AttributeError: return self.ideal(x, side='left') # presumably x is an ideal... try: x = x.gens() except (AttributeError, NotImplementedError): pass # ... not an ideal if side in ['left','twosided']: return self.ideal(x,side=side) elif side=='right': return self.ideal(x,side='twosided') else: # duck typing failed raise TypeError, "Don't know how to transform %s into an ideal of %s"%(x,self) else: # the sides are switched because this is a Cython / extension class if x.is_commutative(): return x.ideal(self) try: side = self.side() except AttributeError: return x.ideal(self, side='right') # presumably self is an ideal... try: self = self.gens() except (AttributeError, NotImplementedError): pass # ... not an ideal if side in ['right','twosided']: return x.ideal(self,side='twosided') elif side=='left': return x.ideal(self,side='twosided') else: raise TypeError, "Don't know how to transform %s into an ideal of %s"%(self,x) def _ideal_class_(self, n=0): r""" The default input of n=0 indicates an unspecified number of generators, in which case a class that works for any number of generators is returned. TODO: For noncommutative rings, distinguish between ideals, right ideals, and left ideals. EXAMPLES:: sage: R. = GF(5)[] sage: RR._ideal_class_() Since #7797, non-commutative rings have ideals as well:: sage: A = SteenrodAlgebra(2) sage: A._ideal_class_() """ # One might need more than just n, but I can't think of an example. from sage.rings.noncommutative_ideals import Ideal_nc try: if not self.is_commutative(): return Ideal_nc except (NotImplementedError, AttributeError): return Ideal_nc from sage.rings.ideal import Ideal_generic, Ideal_principal if n == 1: return Ideal_principal return I return self._zero_ideal def quotient(self, I, names=None): """ Create the quotient of this ring by a twosided ideal I. INPUT: - I -- a twosided ideal of this ring, R. - names -- (optional) names of the generators of the quotient (if there are multiple generators, you can specify a single character string and the generators are named in sequence starting with 0). EXAMPLES:: sage: R. = PolynomialRing(ZZ) sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: S = R.quotient(I, 'a') sage: S.gens() (a,) sage: R. = PolynomialRing(QQ,2) sage: S. = R.quotient((x^2, y)) sage: S Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) sage: S.gens() (a, 0) sage: a == b False """ import sage.rings.quotient_ring return sage.rings.quotient_ring.QuotientRing(self, I, names=names) def quo(self, I, names=None): """ Create the quotient of R by the ideal I.  This is a synonym for :meth:.quotient EXAMPLES:: sage: R. = PolynomialRing(QQ,2) sage: S. = R.quo((x^2, y)) sage: S Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) sage: S.gens() (a, 0) sage: a == b False """ return self.quotient(I, names=names) def __div__(self, I): """ Dividing one ring by another is not supported because there is no good way to specify generator names. EXAMPLES:: sage: QQ / ZZ Traceback (most recent call last): ... TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. """ raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring." #return self.quotient(I, names=None) def quotient_ring(self, I, names=None): """ Return the quotient of self by the ideal I of self. (Synonym for self.quotient(I).) INPUT: - self -- a ring R - I -- an ideal of R - names -- (optional) names of the generators of the quotient. (If there are multiple generators, you can specify a single character string and the generators are named in sequence starting with 0.) OUTPUT: - R/I -- the quotient ring of R by the ideal I EXAMPLES:: sage: R. = PolynomialRing(ZZ) sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: S = R.quotient_ring(I, 'a') sage: S.gens() (a,) sage: R. = PolynomialRing(QQ,2) sage: S. = R.quotient_ring((x^2, y)) sage: S Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) sage: S.gens() (a, 0) sage: a == b False """ return self.quotient(I, names) def zero_element(self): """ Return the zero element of this ring (cached). EXAMPLES:: sage: QQ.is_exact() sage: QQ.is_exact()    # indirect doctest True sage: ZZ.is_exact() True self.__ideal_monoid = M return M def quotient(self, I, names=None): """ Create the quotient of R by the ideal I. INPUT: - R -- a commutative ring - I -- an ideal of R - names -- (optional) names of the generators of the quotient (if there are multiple generators, you can specify a single character string and the generators are named in sequence starting with 0). EXAMPLES:: sage: R. = PolynomialRing(ZZ) sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: S = R.quotient(I, 'a') sage: S.gens() (a,) sage: R. = PolynomialRing(QQ,2) sage: S. = R.quotient((x^2, y)) sage: S Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) sage: S.gens() (a, 0) sage: a == b False """ import sage.rings.quotient_ring return sage.rings.quotient_ring.QuotientRing(self, I, names=names) def quo(self, I, names=None): """ Create the quotient of R by the ideal I.  This is a synonym for :meth:.quotient EXAMPLES:: sage: R. = PolynomialRing(QQ,2) sage: S. = R.quo((x^2, y)) sage: S Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) sage: S.gens() (a, 0) sage: a == b False """ return self.quotient(I, names=names) def extension(self, poly, name=None, names=None, embedding=None): """ Algebraically extends self by taking the quotient self[x] / (f(x)). R = self[name] I = R.ideal(R(poly.list())) return R.quotient(I, name) def __div__(self, I): """ Dividing one ring by another is not supported because there is no good way to specify generator names. EXAMPLES:: sage: QQ / ZZ Traceback (most recent call last): ... TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. """ raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring." #return self.quotient(I, names=None) def quotient_ring(self, I, names=None): """ Return the quotient of self by the ideal I of self. (Synonym for self.quotient(I).) INPUT: - self -- a ring R - I -- an ideal of R - names -- (optional) names of the generators of the quotient. (If there are multiple generators, you can specify a single character string and the generators are named in sequence starting with 0.) OUTPUT: - R/I -- the quotient ring of R by the ideal I EXAMPLES:: sage: R. = PolynomialRing(ZZ) sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) sage: S = R.quotient_ring(I, 'a') sage: S.gens() (a,) sage: R. = PolynomialRing(QQ,2) sage: S. = R.quotient_ring((x^2, y)) sage: S Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) sage: S.gens() (a, 0) sage: a == b False """ return self.quotient(I, names) cdef class IntegralDomain(CommutativeRing):
• ## sage/structure/parent.pyx

diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
 a return mor._call_with_args(x, args, kwds) raise TypeError, "No conversion defined from %s to %s"%(R, self) def __mul__(self,x): """ This is a multiplication method that more or less directly calls another attribute _mul_ (single underscore). This is because __mul__ can not be implemented via inheritance from the parent methods of the category, but _mul_ can be inherited. This is, e.g., used when creating twosided ideals of matrix algebras. See trac ticket #7797. EXAMPLE:: sage: F. = FreeAlgebra(QQ, implementation='letterplace') sage: MS = MatrixSpace(QQ,2,2) This matrix space is in fact an algebra, and in particular it is a ring, from the point of view of categories:: sage: MS.category() Category of algebras over Rational Field sage: MS in Rings() True However, its class does not inherit from the base class Ring:: sage: isinstance(MS,Ring) False Its _mul_ method is inherited from the category, and can be used to create a left or right ideal:: sage: MS._mul_.__module__ 'sage.categories.rings' sage: MS*MS.1      # indirect doctest Left Ideal ( [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS*[MS.1,2] Left Ideal ( [0 1] [0 0], [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS.1*MS Right Ideal ( [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: [MS.1,2]*MS Right Ideal ( [0 1] [0 0], [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ # generic multiplication method. It defers to # _mul_, which may be defined via categories. _mul_ = None switch = False try: if isinstance(self,Parent): _mul_ = self._mul_ except AttributeError: pass if _mul_ is None: try: if isinstance(x,Parent): _mul_ = x._mul_ switch = True except AttributeError: pass if _mul_ is None: raise TypeError,"For implementing multiplication, provide the method '_mul_' for %s resp. %s"%(self,x) if switch: return _mul_(self,switch_sides=True) return _mul_(x) ############################################################################# # Containment testing
• ## setup.py

diff --git a/setup.py b/setup.py
 a packages    = ['sage', 'sage.algebras', 'sage.algebras.letterplace', 'sage.algebras.quatalg', 'sage.calculus',