def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctest Free Algebra on 3 generators (x, y, z) over Rational Field TESTS: 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 R not in Rings(): raise TypeError("Argument R must be a ring.") self.__ngens = n indices = FreeMonoid(n, names=names) cat = AlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, indices, prefix='F', category=cat) self._assign_names(indices.variable_names())
def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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.") self.__monoid = FreeMonoid(n, names=names) self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) Algebra.__init__(self, R, names=names)
def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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 R not in Rings(): raise TypeError("Argument R must be a ring.") self.__ngens = n indices = FreeMonoid(n, names=names) cat = AlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, indices, prefix='F', category=cat) self._assign_names(indices.variable_names())
def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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.") self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) self._basis_keys = FreeMonoid(n, names=names) Algebra.__init__(self, R, names, category=AlgebrasWithBasis(R))
def __classcall_private__(cls, M=None, I=frozenset(), names=None): """ Normalize input to ensure a unique representation. TESTS:: sage: from sage.monoids.trace_monoid import TraceMonoid sage: M1.<a,b,c> = TraceMonoid(I=(('a','c'), ('c','a'))) sage: M2.<a,b,c> = TraceMonoid(I=[('a','c')]) sage: M3 = TraceMonoid(I=[{'a','c'}], names=('a', 'b', 'c')) sage: M1 is M2 and M2 is M3 True """ if not M: if names: M = FreeMonoid(names=names) else: raise ValueError("names must be provided") elif not names: names = [str(g) for g in M.gens()] names = tuple(names) rels = set() gen_from_str = {names[i]: gen for i, gen in enumerate(M.gens())} for (x, y) in I: try: if isinstance(x, str): x = gen_from_str[x] x = M(x) if isinstance(y, str): y = gen_from_str[y] y = M(y) if x == y: raise ValueError except (TypeError, ValueError): raise ValueError("invalid relation defined") rels.add((x, y)) rels.add((y, x)) I = frozenset(rels) return super(TraceMonoid, cls).__classcall__(cls, M, I, names)
def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field """ if not isinstance(R, Ring): raise TypeError("Argument R must be a ring.") self.__monoid = FreeMonoid(n, names=names) self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) Algebra.__init__(self, R, names=names)
def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field """ if not isinstance(R, Ring): raise TypeError, "Argument R must be a ring." self.__monoid = FreeMonoid(n, names=names) self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) Algebra.__init__(self, R,names=names)
class FreeAlgebra_generic(Algebra): """ The free algebra on `n` generators over a base ring. EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F Free Algebra on 3 generators (x, y, z) over Rational Field sage: mul(F.gens()) x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) x*y*z*x*y*z*x*y*z*x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) + mul([ F.gen(i%2) for i in range(12) ]) 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.<a,b> = FreeAlgebra(QQ) sage: K.is_commutative() False sage: L.<c,d> = 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 """ Element = FreeAlgebraElement def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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.") self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) self._basis_keys = FreeMonoid(n, names=names) Algebra.__init__(self, R, names, category=AlgebrasWithBasis(R)) def one_basis(self): """ Return the index of the basis element `1`. EXAMPLES:: sage: F = FreeAlgebra(QQ, 2, 'x,y') sage: F.one_basis() 1 sage: F.one_basis().parent() Free monoid on 2 generators (x, y) """ return self._basis_keys.one() # Needed for the category AlgebrasWithBasis (but not for Algebras) def term(self, index, coeff=None): """ Construct a term of ``self``. INPUT: - ``index`` -- the index of the basis element - ``coeff`` -- (default: 1) an element of the coefficient ring EXAMPLES:: sage: M.<x,y> = FreeMonoid(2) sage: F = FreeAlgebra(QQ, 2, 'x,y') sage: F.term(x*x*y) x^2*y sage: F.term(y^3*x*y, 4) 4*y^3*x*y sage: F.term(M.one(), 2) 2 TESTS: Check to make sure that a coefficient of 0 is properly handled:: sage: list(F.term(M.one(), 0)) [] """ if coeff is None: coeff = self.base_ring().one() if coeff == 0: return self.element_class(self, {}) return self.element_class(self, {index: coeff}) def is_field(self, proof = True): """ Return True if this Free Algebra is a field, which is only if the base ring is a field and there are no generators EXAMPLES:: sage: A=FreeAlgebra(QQ,0,'') sage: A.is_field() True sage: A=FreeAlgebra(QQ,1,'x') sage: A.is_field() False """ if self.__ngens == 0: return self.base_ring().is_field(proof) return False def is_commutative(self): """ Return True if this free algebra is commutative. EXAMPLES:: sage: R.<x> = FreeAlgebra(QQ,1) sage: R.is_commutative() True sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R.is_commutative() False """ return self.__ngens <= 1 and self.base_ring().is_commutative() def __cmp__(self, other): """ Two free algebras are considered the same if they have the same base ring, number of generators and variable names, and the same implementation. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F == FreeAlgebra(QQ,3,'x') True sage: F is FreeAlgebra(QQ,3,'x') True sage: F == FreeAlgebra(ZZ,3,'x') False sage: F == FreeAlgebra(QQ,4,'x') False sage: F == FreeAlgebra(QQ,3,'y') False Note that since :trac:`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()) if c: return c c = cmp(self.variable_names(), other.variable_names()) if c: return c return 0 def _repr_(self): """ Text representation of this free algebra. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<<x0,x1,x2>>') sage: F #indirect doctest QQ<<x0,x1,x2>> sage: FreeAlgebra(ZZ,1,['a']) Free Algebra on 1 generators (a,) over Integer Ring """ return "Free Algebra on %s generators %s over %s"%( self.__ngens, self.gens(), self.base_ring()) def _element_constructor_(self, x): """ Convert ``x`` into ``self``. EXAMPLES:: sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R(3) # indirect doctest 3 TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = 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.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 b + (z+1)*c Check that :trac:`15169` is fixed:: sage: A.<x> = FreeAlgebra(CC) sage: A(2) 2.00000000000000 """ if isinstance(x, FreeAlgebraElement): P = x.parent() if P is self: return x if not (P is self.base_ring()): return self.element_class(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._basis_keys 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 self.element_class(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()) R = self.base_ring() # coercion from free monoid if isinstance(x, FreeMonoidElement) and x.parent() is self._basis_keys: return self.element_class(self,{x:R(1)}) # coercion from the PBW basis if isinstance(x, PBWBasisOfFreeAlgebra.Element) \ and self.has_coerce_map_from(x.parent()._alg): return self(x.parent().expansion(x)) # coercion via base ring x = R(x) if x == 0: return self.element_class(self,{}) return self.element_class(self,{self._basis_keys.one():x}) def _coerce_impl(self, x): """ Canonical coercion of ``x`` into ``self``. Here's what canonically coerces to ``self``: - 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 - the PBW basis of ``self`` - anything that coerces to the base ring of this free algebra - any free algebra whose base ring coerces to the base ring of this free algebra EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(GF(7),3); F Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the free algebra canonically coerce in. :: sage: F._coerce_(x*y) # indirect doctest x*y Elements of the integers coerce in, since there is a coerce map from ZZ to GF(7). :: sage: F._coerce_(1) # indirect doctest 1 There is no coerce map from QQ to GF(7). :: sage: F._coerce_(2/3) Traceback (most recent call last): ... TypeError: no canonical coercion from Rational Field to Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the base ring coerce in. :: sage: F._coerce_(GF(7)(5)) 5 Elements of the corresponding monoid (of monomials) coerce in:: sage: M = F.monoid(); m = M.0*M.1^2; m x*y^2 sage: F._coerce_(m) x*y^2 Elements of the PBW basis:: sage: PBW = F.pbw_basis() sage: px,py,pz = PBW.gens() sage: F(pz*px*py) z*x*y The free algebra over ZZ on x,y,z coerces in, since ZZ coerces to GF(7):: sage: G = FreeAlgebra(ZZ,3,'x,y,z') sage: F._coerce_(G.0^3 * G.1) x^3*y However, GF(7) doesn't coerce to ZZ, so the free algebra over GF(7) doesn't coerce to the one over ZZ:: sage: G._coerce_(x^3*y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 to Free Algebra on 3 generators (x, y, z) over Integer Ring TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace') sage: F(x) x sage: F.1*L.2 # indirect doctest y*z """ try: R = x.parent() # monoid if R is self._basis_keys: return self(x) # polynomial rings in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.has_coerce_map_from(R.base_ring()): return self(x) else: raise TypeError("no natural map between bases of free algebras") if isinstance(R, PBWBasisOfFreeAlgebra) and self.has_coerce_map_from(R._alg): return self(R.expansion(x)) except AttributeError: pass # any ring that coerces to the base ring of this free algebra. return self._coerce_try(x, [self.base_ring()]) def _coerce_map_from_(self, R): """ Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are: - Anything with a coercion into ``self.monoid()``. - Free Algebras in the same variables over a base with a coercion map into ``self.base_ring()``. - The PBW basis of ``self``. - Anything with a coercion into ``self.base_ring()``. TESTS:: sage: F = FreeAlgebra(ZZ, 3, 'x,y,z') sage: G = FreeAlgebra(QQ, 3, 'x,y,z') sage: H = FreeAlgebra(ZZ, 1, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) False sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) True sage: F._coerce_map_from_(G.monoid()) True sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False sage: K.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 # indirect doctest b + (z+1)*c """ if self._basis_keys.has_coerce_map_from(R): return True # free algebras in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.base_ring().has_coerce_map_from(R.base_ring()): return True else: return False if isinstance(R, PBWBasisOfFreeAlgebra): return self.has_coerce_map_from(R._alg) return self.base_ring().has_coerce_map_from(R) def gen(self,i): """ The i-th generator of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.gen(0) x """ n = self.__ngens if i < 0 or not i < n: raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1)) R = self.base_ring() F = self._basis_keys return self.element_class(self,{F.gen(i):R(1)}) def quotient(self, mons, mats, names): """ Returns a quotient algebra. The quotient algebra is defined via the action of a free algebra A on a (finitely generated) free module. The input for the quotient algebra is a list of monomials (in the underlying monoid for A) which form a free basis for the module of A, and a list of matrices, which give the action of the free generators of A on this monomial basis. EXAMPLE: Here is the quaternion algebra defined in terms of three generators:: sage: n = 3 sage: A = FreeAlgebra(QQ,n,'i') sage: F = A.monoid() sage: i, j, k = F.gens() sage: mons = [ F(1), i, j, k ] sage: M = MatrixSpace(QQ,4) sage: mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] sage: H.<i,j,k> = A.quotient(mons, mats); H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field """ import free_algebra_quotient return free_algebra_quotient.FreeAlgebraQuotient(self, mons, mats, names) quo = quotient def ngens(self): """ The number of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.ngens() 3 """ return self.__ngens def monoid(self): """ The free monoid of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.monoid() Free monoid on 3 generators (x, y, z) """ return self._basis_keys def g_algebra(self, relations, names=None, order='degrevlex', check = True): """ The G-Algebra derived from this algebra by relations. By default is assumed, that two variables commute. TODO: - Coercion doesn't work yet, there is some cheating about assumptions - The optional argument ``check`` controls checking the degeneracy conditions. Furthermore, the default values interfere with non-degeneracy conditions. EXAMPLES:: sage: A.<x,y,z>=FreeAlgebra(QQ,3) sage: G=A.g_algebra({y*x:-x*y}) sage: (x,y,z)=G.gens() sage: x*y x*y sage: y*x -x*y sage: z*x x*z sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+1}) sage: (x,y,z)=G.gens() sage: y*x -x*y + 1 sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+z}) sage: (x,y,z)=G.gens() sage: y*x -x*y + z """ from sage.matrix.constructor import Matrix base_ring=self.base_ring() n=self.ngens() cmat=Matrix(base_ring,n) dmat=Matrix(self,n) for i in xrange(n): for j in xrange(i+1,n): cmat[i,j]=1 for (to_commute,commuted) in relations.iteritems(): #This is dirty, coercion is broken assert isinstance(to_commute,FreeAlgebraElement), to_commute.__class__ assert isinstance(commuted,FreeAlgebraElement), commuted ((v1,e1),(v2,e2))=list(list(to_commute)[0][1]) assert e1==1 assert e2==1 assert v1>v2 c_coef=None d_poly=None for (c,m) in commuted: if list(m)==[(v2,1),(v1,1)]: c_coef=c #buggy coercion workaround d_poly=commuted-self(c)*self(m) break assert not c_coef is None,list(m) v2_ind = self.gens().index(v2) v1_ind = self.gens().index(v1) cmat[v2_ind,v1_ind]=c_coef if d_poly: dmat[v2_ind,v1_ind]=d_poly from sage.rings.polynomial.plural import g_Algebra return g_Algebra(base_ring, cmat, dmat, names = names or self.variable_names(), order=order, check=check) def poincare_birkhoff_witt_basis(self): """ Return the Poincare-Birkhoff-Witt (PBW) basis of ``self``. EXAMPLES:: sage: F.<x,y> = FreeAlgebra(QQ, 2) sage: F.poincare_birkhoff_witt_basis() The Poincare-Birkhoff-Witt basis of Free Algebra on 2 generators (x, y) over Rational Field """ return PBWBasisOfFreeAlgebra(self) pbw_basis = poincare_birkhoff_witt_basis def pbw_element(self, elt): """ Return the element ``elt`` in the Poincare-Birkhoff-Witt basis. EXAMPLES:: sage: F.<x,y> = FreeAlgebra(QQ, 2) sage: F.pbw_element(x*y - y*x + 2) 2*PBW[1] + PBW[x*y] sage: F.pbw_element(F.one()) PBW[1] sage: F.pbw_element(x*y*x + x^3*y) PBW[x*y]*PBW[x] + PBW[y]*PBW[x]^2 + PBW[x^3*y] + PBW[x^2*y]*PBW[x] + PBW[x*y]*PBW[x]^2 + PBW[y]*PBW[x]^3 """ PBW = self.pbw_basis() if elt == self.zero(): return PBW.zero() l = {} while elt: # != 0 lst = list(elt) support = [i[1].to_word() for i in lst] min_elt = support[0] for word in support[1:len(support)-1]: if min_elt.lex_less(word): min_elt = word coeff = lst[support.index(min_elt)][0] min_elt = min_elt.to_monoid_element() l[min_elt] = l.get(min_elt, 0) + coeff elt = elt - coeff * self.lie_polynomial(min_elt) return PBW.sum_of_terms([(k, v) for k,v in l.items() if v != 0], distinct=True) def lie_polynomial(self, w): """ Return the Lie polynomial associated to the Lyndon word ``w``. If ``w`` is not Lyndon, then return the product of Lie polynomials of the Lyndon factorization of ``w``. INPUT: - ``w``-- a word or an element of the free monoid EXAMPLES:: sage: F = FreeAlgebra(QQ, 3, 'x,y,z') sage: M.<x,y,z> = FreeMonoid(3) sage: F.lie_polynomial(x*y) x*y - y*x sage: F.lie_polynomial(y*x) y*x sage: F.lie_polynomial(x^2*y*x) x^2*y*x - x*y*x^2 sage: F.lie_polynomial(y*z*x*z*x*z) y*z*x*z*x*z - y*z*x*z^2*x - y*z^2*x^2*z + y*z^2*x*z*x - z*y*x*z*x*z + z*y*x*z^2*x + z*y*z*x^2*z - z*y*z*x*z*x TESTS: We test some corner cases and alternative inputs:: sage: F.lie_polynomial(Word('xy')) x*y - y*x sage: F.lie_polynomial('xy') x*y - y*x sage: F.lie_polynomial(M.one()) 1 sage: F.lie_polynomial(Word([])) 1 sage: F.lie_polynomial('') 1 """ if not w: return self.one() M = self._basis_keys if len(w) == 1: return self(M(w)) ret = self.one() # We have to be careful about order here. # Since the Lyndon factors appear from left to right # we must multiply from left to right as well. for factor in Word(w).lyndon_factorization(): if len(factor) == 1: ret = ret * self(M(factor)) continue x,y = factor.standard_factorization() x = M(x) y = M(y) ret = ret * (self(x * y) - self(y * x)) return ret
class FreeAlgebra_generic(Algebra): """ The free algebra on `n` generators over a base ring. EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F Free Algebra on 3 generators (x, y, z) over Rational Field sage: mul(F.gens()) x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) x*y*z*x*y*z*x*y*z*x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) + mul([ F.gen(i%2) for i in range(12) ]) 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.<a,b> = FreeAlgebra(QQ) sage: K.is_commutative() False sage: L.<c,d> = 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 """ Element = FreeAlgebraElement def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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.") self.__monoid = FreeMonoid(n, names=names) self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) Algebra.__init__(self, R, names=names) def is_field(self, proof=True): """ Return True if this Free Algebra is a field, which is only if the base ring is a field and there are no generators EXAMPLES:: sage: A=FreeAlgebra(QQ,0,'') sage: A.is_field() True sage: A=FreeAlgebra(QQ,1,'x') sage: A.is_field() False """ if self.__ngens == 0: return self.base_ring().is_field(proof) return False def is_commutative(self): """ Return True if this free algebra is commutative. EXAMPLES:: sage: R.<x> = FreeAlgebra(QQ,1) sage: R.is_commutative() True sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R.is_commutative() False """ return self.__ngens <= 1 and self.base_ring().is_commutative() def __cmp__(self, other): """ Two free algebras are considered the same if they have the same base ring, number of generators and variable names, and the same implementation. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F == FreeAlgebra(QQ,3,'x') True sage: F is FreeAlgebra(QQ,3,'x') True sage: F == FreeAlgebra(ZZ,3,'x') False sage: F == FreeAlgebra(QQ,4,'x') False sage: F == FreeAlgebra(QQ,3,'y') False Note that since :trac:`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()) if c: return c c = cmp(self.variable_names(), other.variable_names()) if c: return c return 0 def _repr_(self): """ Text representation of this free algebra. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<<x0,x1,x2>>') sage: F #indirect doctest QQ<<x0,x1,x2>> sage: FreeAlgebra(ZZ,1,['a']) Free Algebra on 1 generators (a,) over Integer Ring """ return "Free Algebra on %s generators %s over %s" % ( self.__ngens, self.gens(), self.base_ring()) def _element_constructor_(self, x): """ Convert x into self. EXAMPLES:: sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R(3) # indirect doctest 3 TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = 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.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 b + (z+1)*c """ if isinstance(x, FreeAlgebraElement): P = x.parent() if P is self: return x if not (P is self.base_ring()): return self.element_class(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 self.element_class( 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 if isinstance(x, FreeMonoidElement) and x.parent() is F: return self.element_class(self, {x: R(1)}) # coercion via base ring x = R(x) if x == 0: return self.element_class(self, {}) else: return self.element_class(self, {F(1): x}) def _coerce_impl(self, x): """ Canonical coercion of x into self. Here's what canonically coerces to self: - 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 - any free algebra whose base ring coerces to the base ring of this free algebra EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(GF(7),3); F Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the free algebra canonically coerce in. :: sage: F._coerce_(x*y) # indirect doctest x*y Elements of the integers coerce in, since there is a coerce map from ZZ to GF(7). :: sage: F._coerce_(1) # indirect doctest 1 There is no coerce map from QQ to GF(7). :: sage: F._coerce_(2/3) Traceback (most recent call last): ... TypeError: no canonical coercion from Rational Field to Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the base ring coerce in. :: sage: F._coerce_(GF(7)(5)) 5 Elements of the corresponding monoid (of monomials) coerce in:: sage: M = F.monoid(); m = M.0*M.1^2; m x*y^2 sage: F._coerce_(m) x*y^2 The free algebra over ZZ on x,y,z coerces in, since ZZ coerces to GF(7):: sage: G = FreeAlgebra(ZZ,3,'x,y,z') sage: F._coerce_(G.0^3 * G.1) x^3*y However, GF(7) doesn't coerce to ZZ, so the free algebra over GF(7) doesn't coerce to the one over ZZ:: sage: G._coerce_(x^3*y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 to Free Algebra on 3 generators (x, y, z) over Integer Ring TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace') sage: F(x) x sage: F.1*L.2 # indirect doctest y*z """ try: R = x.parent() # monoid if R is self.__monoid: return self(x) # polynomial rings in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.has_coerce_map_from(R.base_ring()): return self(x) else: raise TypeError( "no natural map between bases of free algebras") except AttributeError: pass # any ring that coerces to the base ring of this free algebra. return self._coerce_try(x, [self.base_ring()]) def _coerce_map_from_(self, R): """ Returns True if there is a coercion from R into self and false otherwise. The things that coerce into self are: - Anything with a coercion into self.monoid() - Free Algebras in the same variables over a base with a coercion map into self.base_ring() - Anything with a coercion into self.base_ring() TESTS:: sage: F = FreeAlgebra(ZZ, 3, 'x,y,z') sage: G = FreeAlgebra(QQ, 3, 'x,y,z') sage: H = FreeAlgebra(ZZ, 1, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) False sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) True sage: F._coerce_map_from_(G.monoid()) True sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False sage: K.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 # indirect doctest b + (z+1)*c """ if self.__monoid.has_coerce_map_from(R): return True # free algebras in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.base_ring().has_coerce_map_from(R.base_ring()): return True else: return False return self.base_ring().has_coerce_map_from(R) def gen(self, i): """ The i-th generator of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.gen(0) x """ n = self.__ngens if i < 0 or not i < n: raise IndexError("Argument i (= %s) must be between 0 and %s." % (i, n - 1)) R = self.base_ring() F = self.__monoid return self.element_class(self, {F.gen(i): R(1)}) def quotient(self, mons, mats, names): """ Returns a quotient algebra. The quotient algebra is defined via the action of a free algebra A on a (finitely generated) free module. The input for the quotient algebra is a list of monomials (in the underlying monoid for A) which form a free basis for the module of A, and a list of matrices, which give the action of the free generators of A on this monomial basis. EXAMPLE: Here is the quaternion algebra defined in terms of three generators:: sage: n = 3 sage: A = FreeAlgebra(QQ,n,'i') sage: F = A.monoid() sage: i, j, k = F.gens() sage: mons = [ F(1), i, j, k ] sage: M = MatrixSpace(QQ,4) sage: mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] sage: H.<i,j,k> = A.quotient(mons, mats); H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field """ import free_algebra_quotient return free_algebra_quotient.FreeAlgebraQuotient( self, mons, mats, names) quo = quotient def ngens(self): """ The number of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.ngens() 3 """ return self.__ngens def monoid(self): """ The free monoid of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.monoid() Free monoid on 3 generators (x, y, z) """ return self.__monoid def g_algebra(self, relations, names=None, order='degrevlex', check=True): """ The G-Algebra derived from this algebra by relations. By default is assumed, that two variables commute. TODO: - Coercion doesn't work yet, there is some cheating about assumptions - The optional argument ``check`` controls checking the degeneracy conditions. Furthermore, the default values interfere with non-degeneracy conditions. EXAMPLES:: sage: A.<x,y,z>=FreeAlgebra(QQ,3) sage: G=A.g_algebra({y*x:-x*y}) sage: (x,y,z)=G.gens() sage: x*y x*y sage: y*x -x*y sage: z*x x*z sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+1}) sage: (x,y,z)=G.gens() sage: y*x -x*y + 1 sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+z}) sage: (x,y,z)=G.gens() sage: y*x -x*y + z """ from sage.matrix.constructor import Matrix base_ring = self.base_ring() n = self.ngens() cmat = Matrix(base_ring, n) dmat = Matrix(self, n) for i in xrange(n): for j in xrange(i + 1, n): cmat[i, j] = 1 for (to_commute, commuted) in relations.iteritems(): #This is dirty, coercion is broken assert isinstance(to_commute, FreeAlgebraElement), to_commute.__class__ assert isinstance(commuted, FreeAlgebraElement), commuted ((v1, e1), (v2, e2)) = list(list(to_commute)[0][1]) assert e1 == 1 assert e2 == 1 assert v1 > v2 c_coef = None d_poly = None for (c, m) in commuted: if list(m) == [(v2, 1), (v1, 1)]: c_coef = c #buggy coercion workaround d_poly = commuted - self(c) * self(m) break assert not c_coef is None, list(m) v2_ind = self.gens().index(v2) v1_ind = self.gens().index(v1) cmat[v2_ind, v1_ind] = c_coef if d_poly: dmat[v2_ind, v1_ind] = d_poly from sage.rings.polynomial.plural import g_Algebra return g_Algebra(base_ring, cmat, dmat, names=names or self.variable_names(), order=order, check=check)
class FreeAlgebra_generic(Algebra): """ The free algebra on `n` generators over a base ring. EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F Free Algebra on 3 generators (x, y, z) over Rational Field sage: mul(F.gens()) x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) x*y*z*x*y*z*x*y*z*x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) + mul([ F.gen(i%2) for i in range(12) ]) 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.<a,b> = FreeAlgebra(QQ) sage: K.is_commutative() False sage: L.<c,d> = 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 """ Element = FreeAlgebraElement def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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.") self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) self._basis_keys = FreeMonoid(n, names=names) Algebra.__init__(self, R, names, category=AlgebrasWithBasis(R)) def one_basis(self): """ Return the index of the basis element `1`. EXAMPLES:: sage: F = FreeAlgebra(QQ, 2, 'x,y') sage: F.one_basis() 1 sage: F.one_basis().parent() Free monoid on 2 generators (x, y) """ return self._basis_keys.one() # Needed for the category AlgebrasWithBasis (but not for Algebras) def term(self, index, coeff=None): """ Construct a term of ``self``. INPUT: - ``index`` -- the index of the basis element - ``coeff`` -- (default: 1) an element of the coefficient ring EXAMPLES:: sage: M.<x,y> = FreeMonoid(2) sage: F = FreeAlgebra(QQ, 2, 'x,y') sage: F.term(x*x*y) x^2*y sage: F.term(y^3*x*y, 4) 4*y^3*x*y sage: F.term(M.one(), 2) 2 TESTS: Check to make sure that a coefficient of 0 is properly handled:: sage: list(F.term(M.one(), 0)) [] """ if coeff is None: coeff = self.base_ring().one() if coeff == 0: return self.element_class(self, {}) return self.element_class(self, {index: coeff}) def is_field(self, proof=True): """ Return True if this Free Algebra is a field, which is only if the base ring is a field and there are no generators EXAMPLES:: sage: A=FreeAlgebra(QQ,0,'') sage: A.is_field() True sage: A=FreeAlgebra(QQ,1,'x') sage: A.is_field() False """ if self.__ngens == 0: return self.base_ring().is_field(proof) return False def is_commutative(self): """ Return True if this free algebra is commutative. EXAMPLES:: sage: R.<x> = FreeAlgebra(QQ,1) sage: R.is_commutative() True sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R.is_commutative() False """ return self.__ngens <= 1 and self.base_ring().is_commutative() def __cmp__(self, other): """ Two free algebras are considered the same if they have the same base ring, number of generators and variable names, and the same implementation. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F == FreeAlgebra(QQ,3,'x') True sage: F is FreeAlgebra(QQ,3,'x') True sage: F == FreeAlgebra(ZZ,3,'x') False sage: F == FreeAlgebra(QQ,4,'x') False sage: F == FreeAlgebra(QQ,3,'y') False Note that since :trac:`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()) if c: return c c = cmp(self.variable_names(), other.variable_names()) if c: return c return 0 def _repr_(self): """ Text representation of this free algebra. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<<x0,x1,x2>>') sage: F #indirect doctest QQ<<x0,x1,x2>> sage: FreeAlgebra(ZZ,1,['a']) Free Algebra on 1 generators (a,) over Integer Ring """ return "Free Algebra on %s generators %s over %s" % ( self.__ngens, self.gens(), self.base_ring()) def _element_constructor_(self, x): """ Convert ``x`` into ``self``. EXAMPLES:: sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R(3) # indirect doctest 3 TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = 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.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 b + (z+1)*c Check that :trac:`15169` is fixed:: sage: A.<x> = FreeAlgebra(CC) sage: A(2) 2.00000000000000 """ if isinstance(x, FreeAlgebraElement): P = x.parent() if P is self: return x if not (P is self.base_ring()): return self.element_class(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._basis_keys 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 self.element_class( 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()) R = self.base_ring() # coercion from free monoid if isinstance(x, FreeMonoidElement) and x.parent() is self._basis_keys: return self.element_class(self, {x: R(1)}) # coercion from the PBW basis if isinstance(x, PBWBasisOfFreeAlgebra.Element) \ and self.has_coerce_map_from(x.parent()._alg): return self(x.parent().expansion(x)) # coercion via base ring x = R(x) if x == 0: return self.element_class(self, {}) return self.element_class(self, {self._basis_keys.one(): x}) def _coerce_impl(self, x): """ Canonical coercion of ``x`` into ``self``. Here's what canonically coerces to ``self``: - 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 - the PBW basis of ``self`` - anything that coerces to the base ring of this free algebra - any free algebra whose base ring coerces to the base ring of this free algebra EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(GF(7),3); F Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the free algebra canonically coerce in. :: sage: F._coerce_(x*y) # indirect doctest x*y Elements of the integers coerce in, since there is a coerce map from ZZ to GF(7). :: sage: F._coerce_(1) # indirect doctest 1 There is no coerce map from QQ to GF(7). :: sage: F._coerce_(2/3) Traceback (most recent call last): ... TypeError: no canonical coercion from Rational Field to Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the base ring coerce in. :: sage: F._coerce_(GF(7)(5)) 5 Elements of the corresponding monoid (of monomials) coerce in:: sage: M = F.monoid(); m = M.0*M.1^2; m x*y^2 sage: F._coerce_(m) x*y^2 Elements of the PBW basis:: sage: PBW = F.pbw_basis() sage: px,py,pz = PBW.gens() sage: F(pz*px*py) z*x*y The free algebra over ZZ on x,y,z coerces in, since ZZ coerces to GF(7):: sage: G = FreeAlgebra(ZZ,3,'x,y,z') sage: F._coerce_(G.0^3 * G.1) x^3*y However, GF(7) doesn't coerce to ZZ, so the free algebra over GF(7) doesn't coerce to the one over ZZ:: sage: G._coerce_(x^3*y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 to Free Algebra on 3 generators (x, y, z) over Integer Ring TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace') sage: F(x) x sage: F.1*L.2 # indirect doctest y*z """ try: R = x.parent() # monoid if R is self._basis_keys: return self(x) # polynomial rings in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.has_coerce_map_from(R.base_ring()): return self(x) else: raise TypeError( "no natural map between bases of free algebras") if isinstance(R, PBWBasisOfFreeAlgebra) and self.has_coerce_map_from( R._alg): return self(R.expansion(x)) except AttributeError: pass # any ring that coerces to the base ring of this free algebra. return self._coerce_try(x, [self.base_ring()]) def _coerce_map_from_(self, R): """ Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are: - Anything with a coercion into ``self.monoid()``. - Free Algebras in the same variables over a base with a coercion map into ``self.base_ring()``. - The PBW basis of ``self``. - Anything with a coercion into ``self.base_ring()``. TESTS:: sage: F = FreeAlgebra(ZZ, 3, 'x,y,z') sage: G = FreeAlgebra(QQ, 3, 'x,y,z') sage: H = FreeAlgebra(ZZ, 1, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) False sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) True sage: F._coerce_map_from_(G.monoid()) True sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False sage: K.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 # indirect doctest b + (z+1)*c """ if self._basis_keys.has_coerce_map_from(R): return True # free algebras in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.base_ring().has_coerce_map_from(R.base_ring()): return True else: return False if isinstance(R, PBWBasisOfFreeAlgebra): return self.has_coerce_map_from(R._alg) return self.base_ring().has_coerce_map_from(R) def gen(self, i): """ The i-th generator of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.gen(0) x """ n = self.__ngens if i < 0 or not i < n: raise IndexError("Argument i (= %s) must be between 0 and %s." % (i, n - 1)) R = self.base_ring() F = self._basis_keys return self.element_class(self, {F.gen(i): R(1)}) def quotient(self, mons, mats, names): """ Returns a quotient algebra. The quotient algebra is defined via the action of a free algebra A on a (finitely generated) free module. The input for the quotient algebra is a list of monomials (in the underlying monoid for A) which form a free basis for the module of A, and a list of matrices, which give the action of the free generators of A on this monomial basis. EXAMPLE: Here is the quaternion algebra defined in terms of three generators:: sage: n = 3 sage: A = FreeAlgebra(QQ,n,'i') sage: F = A.monoid() sage: i, j, k = F.gens() sage: mons = [ F(1), i, j, k ] sage: M = MatrixSpace(QQ,4) sage: mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] sage: H.<i,j,k> = A.quotient(mons, mats); H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field """ import free_algebra_quotient return free_algebra_quotient.FreeAlgebraQuotient( self, mons, mats, names) quo = quotient def ngens(self): """ The number of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.ngens() 3 """ return self.__ngens def monoid(self): """ The free monoid of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.monoid() Free monoid on 3 generators (x, y, z) """ return self._basis_keys def g_algebra(self, relations, names=None, order='degrevlex', check=True): """ The G-Algebra derived from this algebra by relations. By default is assumed, that two variables commute. TODO: - Coercion doesn't work yet, there is some cheating about assumptions - The optional argument ``check`` controls checking the degeneracy conditions. Furthermore, the default values interfere with non-degeneracy conditions. EXAMPLES:: sage: A.<x,y,z>=FreeAlgebra(QQ,3) sage: G=A.g_algebra({y*x:-x*y}) sage: (x,y,z)=G.gens() sage: x*y x*y sage: y*x -x*y sage: z*x x*z sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+1}) sage: (x,y,z)=G.gens() sage: y*x -x*y + 1 sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+z}) sage: (x,y,z)=G.gens() sage: y*x -x*y + z """ from sage.matrix.constructor import Matrix base_ring = self.base_ring() n = self.ngens() cmat = Matrix(base_ring, n) dmat = Matrix(self, n) for i in xrange(n): for j in xrange(i + 1, n): cmat[i, j] = 1 for (to_commute, commuted) in relations.iteritems(): #This is dirty, coercion is broken assert isinstance(to_commute, FreeAlgebraElement), to_commute.__class__ assert isinstance(commuted, FreeAlgebraElement), commuted ((v1, e1), (v2, e2)) = list(list(to_commute)[0][1]) assert e1 == 1 assert e2 == 1 assert v1 > v2 c_coef = None d_poly = None for (c, m) in commuted: if list(m) == [(v2, 1), (v1, 1)]: c_coef = c #buggy coercion workaround d_poly = commuted - self(c) * self(m) break assert not c_coef is None, list(m) v2_ind = self.gens().index(v2) v1_ind = self.gens().index(v1) cmat[v2_ind, v1_ind] = c_coef if d_poly: dmat[v2_ind, v1_ind] = d_poly from sage.rings.polynomial.plural import g_Algebra return g_Algebra(base_ring, cmat, dmat, names=names or self.variable_names(), order=order, check=check) def poincare_birkhoff_witt_basis(self): """ Return the Poincare-Birkhoff-Witt (PBW) basis of ``self``. EXAMPLES:: sage: F.<x,y> = FreeAlgebra(QQ, 2) sage: F.poincare_birkhoff_witt_basis() The Poincare-Birkhoff-Witt basis of Free Algebra on 2 generators (x, y) over Rational Field """ return PBWBasisOfFreeAlgebra(self) pbw_basis = poincare_birkhoff_witt_basis def pbw_element(self, elt): """ Return the element ``elt`` in the Poincare-Birkhoff-Witt basis. EXAMPLES:: sage: F.<x,y> = FreeAlgebra(QQ, 2) sage: F.pbw_element(x*y - y*x + 2) 2*PBW[1] + PBW[x*y] sage: F.pbw_element(F.one()) PBW[1] sage: F.pbw_element(x*y*x + x^3*y) PBW[x*y]*PBW[x] + PBW[y]*PBW[x]^2 + PBW[x^3*y] + PBW[x^2*y]*PBW[x] + PBW[x*y]*PBW[x]^2 + PBW[y]*PBW[x]^3 """ PBW = self.pbw_basis() if elt == self.zero(): return PBW.zero() l = {} while elt: # != 0 lst = list(elt) support = [i[1].to_word() for i in lst] min_elt = support[0] for word in support[1:len(support) - 1]: if min_elt.lex_less(word): min_elt = word coeff = lst[support.index(min_elt)][0] min_elt = min_elt.to_monoid_element() l[min_elt] = l.get(min_elt, 0) + coeff elt = elt - coeff * self.lie_polynomial(min_elt) return PBW.sum_of_terms([(k, v) for k, v in l.items() if v != 0], distinct=True) def lie_polynomial(self, w): """ Return the Lie polynomial associated to the Lyndon word ``w``. If ``w`` is not Lyndon, then return the product of Lie polynomials of the Lyndon factorization of ``w``. INPUT: - ``w``-- a word or an element of the free monoid EXAMPLES:: sage: F = FreeAlgebra(QQ, 3, 'x,y,z') sage: M.<x,y,z> = FreeMonoid(3) sage: F.lie_polynomial(x*y) x*y - y*x sage: F.lie_polynomial(y*x) y*x sage: F.lie_polynomial(x^2*y*x) x^2*y*x - x*y*x^2 sage: F.lie_polynomial(y*z*x*z*x*z) y*z*x*z*x*z - y*z*x*z^2*x - y*z^2*x^2*z + y*z^2*x*z*x - z*y*x*z*x*z + z*y*x*z^2*x + z*y*z*x^2*z - z*y*z*x*z*x TESTS: We test some corner cases and alternative inputs:: sage: F.lie_polynomial(Word('xy')) x*y - y*x sage: F.lie_polynomial('xy') x*y - y*x sage: F.lie_polynomial(M.one()) 1 sage: F.lie_polynomial(Word([])) 1 sage: F.lie_polynomial('') 1 """ if not w: return self.one() M = self._basis_keys if len(w) == 1: return self(M(w)) ret = self.one() # We have to be careful about order here. # Since the Lyndon factors appear from left to right # we must multiply from left to right as well. for factor in Word(w).lyndon_factorization(): if len(factor) == 1: ret = ret * self(M(factor)) continue x, y = factor.standard_factorization() x = M(x) y = M(y) ret = ret * (self(x * y) - self(y * x)) return ret
class FreeAlgebra_generic(Algebra): """ The free algebra on `n` generators over a base ring. EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F Free Algebra on 3 generators (x, y, z) over Rational Field sage: mul(F.gens()) x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) x*y*z*x*y*z*x*y*z*x*y*z sage: mul([ F.gen(i%3) for i in range(12) ]) + mul([ F.gen(i%2) for i in range(12) ]) 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.<a,b> = FreeAlgebra(QQ) sage: K.is_commutative() False sage: L.<c,d> = 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 """ Element = FreeAlgebraElement def __init__(self, R, n, names): """ The free algebra on `n` generators over a base ring. INPUT: - ``R`` - ring - ``n`` - an integer - ``names`` - generator names EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field 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.") self.__monoid = FreeMonoid(n, names=names) self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) Algebra.__init__(self, R,names=names) def is_field(self, proof = True): """ Return True if this Free Algebra is a field, which is only if the base ring is a field and there are no generators EXAMPLES:: sage: A=FreeAlgebra(QQ,0,'') sage: A.is_field() True sage: A=FreeAlgebra(QQ,1,'x') sage: A.is_field() False """ if self.__ngens == 0: return self.base_ring().is_field(proof) return False def is_commutative(self): """ Return True if this free algebra is commutative. EXAMPLES:: sage: R.<x> = FreeAlgebra(QQ,1) sage: R.is_commutative() True sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R.is_commutative() False """ return self.__ngens <= 1 and self.base_ring().is_commutative() def __cmp__(self, other): """ Two free algebras are considered the same if they have the same base ring, number of generators and variable names, and the same implementation. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F == FreeAlgebra(QQ,3,'x') True sage: F is FreeAlgebra(QQ,3,'x') True sage: F == FreeAlgebra(ZZ,3,'x') False sage: F == FreeAlgebra(QQ,4,'x') False sage: F == FreeAlgebra(QQ,3,'y') False Note that since :trac:`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()) if c: return c c = cmp(self.variable_names(), other.variable_names()) if c: return c return 0 def _repr_(self): """ Text representation of this free algebra. EXAMPLES:: sage: F = FreeAlgebra(QQ,3,'x') sage: F # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<<x0,x1,x2>>') sage: F #indirect doctest QQ<<x0,x1,x2>> sage: FreeAlgebra(ZZ,1,['a']) Free Algebra on 1 generators (a,) over Integer Ring """ return "Free Algebra on %s generators %s over %s"%( self.__ngens, self.gens(), self.base_ring()) def _element_constructor_(self, x): """ Convert x into self. EXAMPLES:: sage: R.<x,y> = FreeAlgebra(QQ,2) sage: R(3) # indirect doctest 3 TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = 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.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 b + (z+1)*c """ if isinstance(x, FreeAlgebraElement): P = x.parent() if P is self: return x if not (P is self.base_ring()): return self.element_class(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 self.element_class(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 if isinstance(x, FreeMonoidElement) and x.parent() is F: return self.element_class(self,{x:R(1)}) # coercion via base ring x = R(x) if x == 0: return self.element_class(self,{}) else: return self.element_class(self,{F(1):x}) def _coerce_impl(self, x): """ Canonical coercion of x into self. Here's what canonically coerces to self: - 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 - any free algebra whose base ring coerces to the base ring of this free algebra EXAMPLES:: sage: F.<x,y,z> = FreeAlgebra(GF(7),3); F Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the free algebra canonically coerce in. :: sage: F._coerce_(x*y) # indirect doctest x*y Elements of the integers coerce in, since there is a coerce map from ZZ to GF(7). :: sage: F._coerce_(1) # indirect doctest 1 There is no coerce map from QQ to GF(7). :: sage: F._coerce_(2/3) Traceback (most recent call last): ... TypeError: no canonical coercion from Rational Field to Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 Elements of the base ring coerce in. :: sage: F._coerce_(GF(7)(5)) 5 Elements of the corresponding monoid (of monomials) coerce in:: sage: M = F.monoid(); m = M.0*M.1^2; m x*y^2 sage: F._coerce_(m) x*y^2 The free algebra over ZZ on x,y,z coerces in, since ZZ coerces to GF(7):: sage: G = FreeAlgebra(ZZ,3,'x,y,z') sage: F._coerce_(G.0^3 * G.1) x^3*y However, GF(7) doesn't coerce to ZZ, so the free algebra over GF(7) doesn't coerce to the one over ZZ:: sage: G._coerce_(x^3*y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free Algebra on 3 generators (x, y, z) over Finite Field of size 7 to Free Algebra on 3 generators (x, y, z) over Integer Ring TESTS:: sage: F.<x,y,z> = FreeAlgebra(GF(5),3) sage: L.<x,y,z> = FreeAlgebra(GF(5),3,implementation='letterplace') sage: F(x) x sage: F.1*L.2 # indirect doctest y*z """ try: R = x.parent() # monoid if R is self.__monoid: return self(x) # polynomial rings in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.has_coerce_map_from(R.base_ring()): return self(x) else: raise TypeError("no natural map between bases of free algebras") except AttributeError: pass # any ring that coerces to the base ring of this free algebra. return self._coerce_try(x, [self.base_ring()]) def _coerce_map_from_(self, R): """ Returns True if there is a coercion from R into self and false otherwise. The things that coerce into self are: - Anything with a coercion into self.monoid() - Free Algebras in the same variables over a base with a coercion map into self.base_ring() - Anything with a coercion into self.base_ring() TESTS:: sage: F = FreeAlgebra(ZZ, 3, 'x,y,z') sage: G = FreeAlgebra(QQ, 3, 'x,y,z') sage: H = FreeAlgebra(ZZ, 1, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) False sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) True sage: F._coerce_map_from_(G.monoid()) True sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False sage: K.<z> = GF(25) sage: F.<a,b,c> = FreeAlgebra(K,3) sage: L.<a,b,c> = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 # indirect doctest b + (z+1)*c """ if self.__monoid.has_coerce_map_from(R): return True # free algebras in the same variable over any base that coerces in: if is_FreeAlgebra(R): if R.variable_names() == self.variable_names(): if self.base_ring().has_coerce_map_from(R.base_ring()): return True else: return False return self.base_ring().has_coerce_map_from(R) def gen(self,i): """ The i-th generator of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.gen(0) x """ n = self.__ngens if i < 0 or not i < n: raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1)) R = self.base_ring() F = self.__monoid return self.element_class(self,{F.gen(i):R(1)}) def quotient(self, mons, mats, names): """ Returns a quotient algebra. The quotient algebra is defined via the action of a free algebra A on a (finitely generated) free module. The input for the quotient algebra is a list of monomials (in the underlying monoid for A) which form a free basis for the module of A, and a list of matrices, which give the action of the free generators of A on this monomial basis. EXAMPLE: Here is the quaternion algebra defined in terms of three generators:: sage: n = 3 sage: A = FreeAlgebra(QQ,n,'i') sage: F = A.monoid() sage: i, j, k = F.gens() sage: mons = [ F(1), i, j, k ] sage: M = MatrixSpace(QQ,4) sage: mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] sage: H.<i,j,k> = A.quotient(mons, mats); H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field """ import free_algebra_quotient return free_algebra_quotient.FreeAlgebraQuotient(self, mons, mats, names) quo = quotient def ngens(self): """ The number of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.ngens() 3 """ return self.__ngens def monoid(self): """ The free monoid of generators of the algebra. EXAMPLES:: sage: F = FreeAlgebra(ZZ,3,'x,y,z') sage: F.monoid() Free monoid on 3 generators (x, y, z) """ return self.__monoid def g_algebra(self, relations, names=None, order='degrevlex', check = True): """ The G-Algebra derived from this algebra by relations. By default is assumed, that two variables commute. TODO: - Coercion doesn't work yet, there is some cheating about assumptions - The optional argument ``check`` controls checking the degeneracy conditions. Furthermore, the default values interfere with non-degeneracy conditions. EXAMPLES:: sage: A.<x,y,z>=FreeAlgebra(QQ,3) sage: G=A.g_algebra({y*x:-x*y}) sage: (x,y,z)=G.gens() sage: x*y x*y sage: y*x -x*y sage: z*x x*z sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+1}) sage: (x,y,z)=G.gens() sage: y*x -x*y + 1 sage: (x,y,z)=A.gens() sage: G=A.g_algebra({y*x:-x*y+z}) sage: (x,y,z)=G.gens() sage: y*x -x*y + z """ from sage.matrix.constructor import Matrix base_ring=self.base_ring() n=self.ngens() cmat=Matrix(base_ring,n) dmat=Matrix(self,n) for i in xrange(n): for j in xrange(i+1,n): cmat[i,j]=1 for (to_commute,commuted) in relations.iteritems(): #This is dirty, coercion is broken assert isinstance(to_commute,FreeAlgebraElement), to_commute.__class__ assert isinstance(commuted,FreeAlgebraElement), commuted ((v1,e1),(v2,e2))=list(list(to_commute)[0][1]) assert e1==1 assert e2==1 assert v1>v2 c_coef=None d_poly=None for (c,m) in commuted: if list(m)==[(v2,1),(v1,1)]: c_coef=c #buggy coercion workaround d_poly=commuted-self(c)*self(m) break assert not c_coef is None,list(m) v2_ind = self.gens().index(v2) v1_ind = self.gens().index(v1) cmat[v2_ind,v1_ind]=c_coef if d_poly: dmat[v2_ind,v1_ind]=d_poly from sage.rings.polynomial.plural import g_Algebra return g_Algebra(base_ring, cmat, dmat, names = names or self.variable_names(), order=order, check=check)