# Revision history [back]

In this case, the tensor algebra of $M$ could be defined as free algebra in Sage:

sage: Q = QuadraticForm(QQ, -2 * matrix.identity(4))
sage: V.<e1,e2,e3,e4> = CliffordAlgebra(Q)
sage: T.<m1,m2,m3,m4> = FreeAlgebra(QQ); T
Free Algebra on 4 generators (m1, m2, m3, m4) over Rational Field


To define the map $q$, usually it should be enough to specify the images of the four algebra generators of T, but unfortunately, in this case, this is not implemented yet:

sage: q = T.hom(V.gens(), V, check=False)
sage: q(m1)
...
NotImplementedError:


Instead, we define the map as a map of vector spaces, by specifying the images of a vector space basis of T. The basis elements are indexed by the free monoid on four generators. The monoid elements are the ones that on_basis receives.

sage: T.indices()
Free monoid on 4 generators (m1, m2, m3, m4)
sage: # map the free monoid generators to generators of V
sage: convert_index = dict(zip(T.indices().gens(), V.gens()))
sage: # map the free monoid elements to elements of V
sage: q = T.module_morphism(on_basis=lambda a: V.prod(convert_index[ai]^ni for ai, ni in a),
....:                       codomain=V)


Now we can check that q indeed has the correct properties. Here, B is a list of some small elements of the vector space basis of T.

sage: q(T(1)), q(m1), q(m2), q(m3), q(m4)
(1, e1, e2, e3, e4)
sage: B = list(T.basis().iterator_range(0, 50))
sage: all(q(a * b) == q(a) * q(b) for a in B for b in B)
True


In this case, the tensor algebra of $M$ could be defined as free algebra in Sage:

sage: Q = QuadraticForm(QQ, -2 * matrix.identity(4))
sage: V.<e1,e2,e3,e4> = CliffordAlgebra(Q)
sage: T.<m1,m2,m3,m4> = FreeAlgebra(QQ); T
Free Algebra on 4 generators (m1, m2, m3, m4) over Rational Field


To define the map $q$, usually it should be enough to specify the images of the four algebra generators of T, but unfortunately, in this case, this is not implemented yet:

sage: q = T.hom(V.gens(), V, check=False)
sage: q(m1)
...
NotImplementedError:


Instead, we define the map as a map of vector spaces, by specifying the images of a vector space basis of T. The basis elements are indexed by the free monoid on four generators. The monoid elements are the ones that on_basis receives.

sage: T.indices()
Free monoid on 4 generators (m1, m2, m3, m4)
sage: # map the free monoid generators to generators of V
sage: convert_index = dict(zip(T.indices().gens(), V.gens()))
sage: # map the free monoid elements to elements of V
sage: q = T.module_morphism(on_basis=lambda a: V.prod(convert_index[ai]^ni for ai, ni in a),
....:                       codomain=V)


Now we can check that q indeed has the correct properties. Here, B is a list of some small elements of the vector space basis of T.

sage: q(T(1)), q(m1), q(m2), q(m3), q(m4)
(1, e1, e2, e3, e4)
sage: B = list(T.basis().iterator_range(0, 50))
sage: all(q(a * b) == q(a) * q(b) for a in B for b in B)
True


Edit:

If you want to work to work with FiniteRankFreeModule, this is more difficult. It is not possible to construct the tensor algebra of FiniteRankFreeModule in Sage (as of 9.1). If you only have homogeneous elements, here is what you could do:

M = FiniteRankFreeModule(QQ, 4, name='M', start_index=1)
m = M.basis('m')
Q = QuadraticForm(QQ, -2 * matrix.identity(4))
V.<e1,e2,e3,e4> = CliffordAlgebra(Q)

def q(t):
comp = t.components()  # uses the default basis of M
def components_iter():
for idx in comp.index_generator():
c = comp[idx]
if not c.is_zero():
yield V.prod(V.gen(j - comp._sindex) for j in idx), c
return V.linear_combination(components_iter())


Example:

sage: q(m[1]*m[2]*m[3] + m[2]*m[3]*m[4] + m[1]*m[2]*m[2])
e1*e2*e3 + e2*e3*e4 - e1


Here, q is just a Python function, not a morphism, as the domain of this morphism cannot be constructed in Sage.

Another problem with this is that this ignores any sparsity that might be present, so this becomes very slow for higher order tensors, even if they are very sparse. If your tensors have been constructed without any kind of symmetry, you could replace comp.index_generator() by comp._comp to iterate only over the non-zero entries.