Tensors on free modules¶
The class FreeModuleTensor implements tensors on a free module \(M\)
of finite rank over a commutative ring. A tensor of type \((k,l)\) on \(M\)
is a multilinear map:
where \(R\) is the commutative ring over which the free module \(M\) is defined
and \(M^* = \mathrm{Hom}_R(M,R)\) is the dual of \(M\). The integer \(k + l\) is
called the tensor rank. The set \(T^{(k,l)}(M)\) of tensors of type \((k,l)\)
on \(M\) is a free module of finite rank over \(R\), described by the
class TensorFreeModule.
Various derived classes of FreeModuleTensor are devoted to specific
tensors:
- AlternatingContrTensorfor fully antisymmetric type-\((k, 0)\) tensors (alternating contravariant tensors);- FiniteRankFreeModuleElementfor elements of \(M\), considered as type-\((1,0)\) tensors thanks to the canonical identification \(M^{**}=M\) (which holds since \(M\) is a free module of finite rank);
 
- FreeModuleAltFormfor fully antisymmetric type-\((0, l)\) tensors (alternating forms);
- FreeModuleAutomorphismfor type-\((1,1)\) tensors representing invertible endomorphisms.
Each of these classes is a Sage element class, the corresponding parent class being:
AUTHORS:
- Eric Gourgoulhon, Michal Bejger (2014-2015): initial version 
- Michael Jung (2019): improve treatment of the zero element; add method - copy_from
REFERENCES:
- Chap. 21 of R. Godement : Algebra [God1968] 
- Chap. 12 of J. M. Lee: Introduction to Smooth Manifolds [Lee2013] (only when the free module is a vector space) 
- Chap. 2 of B. O’Neill: Semi-Riemannian Geometry [ONe1983] 
EXAMPLES:
A tensor of type \((1, 1)\) on a rank-3 free module over \(\ZZ\):
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: t = M.tensor((1,1), name='t') ; t
Type-(1,1) tensor t on the Rank-3 free module M over the Integer Ring
sage: t.parent()
Free module of type-(1,1) tensors on the Rank-3 free module M
 over the Integer Ring
sage: t.parent() is M.tensor_module(1,1)
True
sage: t in M.tensor_module(1,1)
True
>>> from sage.all import *
>>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M')
>>> t = M.tensor((Integer(1),Integer(1)), name='t') ; t
Type-(1,1) tensor t on the Rank-3 free module M over the Integer Ring
>>> t.parent()
Free module of type-(1,1) tensors on the Rank-3 free module M
 over the Integer Ring
>>> t.parent() is M.tensor_module(Integer(1),Integer(1))
True
>>> t in M.tensor_module(Integer(1),Integer(1))
True
Setting some component of the tensor in a given basis:
sage: e = M.basis('e') ; e
Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
sage: t.set_comp(e)[0,0] = -3  # the component [0,0] w.r.t. basis e is set to -3
>>> from sage.all import *
>>> e = M.basis('e') ; e
Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
>>> t.set_comp(e)[Integer(0),Integer(0)] = -Integer(3)  # the component [0,0] w.r.t. basis e is set to -3
The unset components are assumed to be zero:
sage: t.comp(e)[:]  # list of all components w.r.t. basis e
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
sage: t.display(e)  # displays the expansion of t on the basis e_i⊗e^j of T^(1,1)(M)
t = -3 e_0⊗e^0
>>> from sage.all import *
>>> t.comp(e)[:]  # list of all components w.r.t. basis e
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
>>> t.display(e)  # displays the expansion of t on the basis e_i⊗e^j of T^(1,1)(M)
t = -3 e_0⊗e^0
The commands t.set_comp(e) and t.comp(e) can be abridged by providing
the basis as the first argument in the square brackets:
sage: t[e,0,0] = -3
sage: t[e,:]
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
>>> from sage.all import *
>>> t[e,Integer(0),Integer(0)] = -Integer(3)
>>> t[e,:]
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
Actually, since e is M’s default basis, the mention of e
can be omitted:
sage: t[0,0] = -3
sage: t[:]
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
>>> from sage.all import *
>>> t[Integer(0),Integer(0)] = -Integer(3)
>>> t[:]
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
For tensors of rank 2, the matrix of components w.r.t. a given basis is
obtained via the function matrix:
sage: matrix(t.comp(e))
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
sage: matrix(t.comp(e)).parent()
Full MatrixSpace of 3 by 3 dense matrices over Integer Ring
>>> from sage.all import *
>>> matrix(t.comp(e))
[-3  0  0]
[ 0  0  0]
[ 0  0  0]
>>> matrix(t.comp(e)).parent()
Full MatrixSpace of 3 by 3 dense matrices over Integer Ring
Tensor components can be modified (reset) at any time:
sage: t[0,0] = 0
sage: t[:]
[0 0 0]
[0 0 0]
[0 0 0]
>>> from sage.all import *
>>> t[Integer(0),Integer(0)] = Integer(0)
>>> t[:]
[0 0 0]
[0 0 0]
[0 0 0]
Checking that t is zero:
sage: t.is_zero()
True
sage: t == 0
True
sage: t == M.tensor_module(1,1).zero()  # the zero element of the module of all type-(1,1) tensors on M
True
>>> from sage.all import *
>>> t.is_zero()
True
>>> t == Integer(0)
True
>>> t == M.tensor_module(Integer(1),Integer(1)).zero()  # the zero element of the module of all type-(1,1) tensors on M
True
The components are managed by the class
Components:
sage: type(t.comp(e))
<class 'sage.tensor.modules.comp.Components'>
>>> from sage.all import *
>>> type(t.comp(e))
<class 'sage.tensor.modules.comp.Components'>
Only nonzero components are actually stored, in the dictionary _comp
of class Components, whose keys are
the indices:
sage: t.comp(e)._comp
{}
sage: t.set_comp(e)[0,0] = -3 ; t.set_comp(e)[1,2] = 2
sage: t.comp(e)._comp  # random output order (dictionary)
{(0, 0): -3, (1, 2): 2}
sage: t.display(e)
t = -3 e_0⊗e^0 + 2 e_1⊗e^2
>>> from sage.all import *
>>> t.comp(e)._comp
{}
>>> t.set_comp(e)[Integer(0),Integer(0)] = -Integer(3) ; t.set_comp(e)[Integer(1),Integer(2)] = Integer(2)
>>> t.comp(e)._comp  # random output order (dictionary)
{(0, 0): -3, (1, 2): 2}
>>> t.display(e)
t = -3 e_0⊗e^0 + 2 e_1⊗e^2
Further tests of the comparison operator:
sage: t.is_zero()
False
sage: t == 0
False
sage: t == M.tensor_module(1,1).zero()
False
sage: t1 = t.copy()
sage: t1 == t
True
sage: t1[2,0] = 4
sage: t1 == t
False
>>> from sage.all import *
>>> t.is_zero()
False
>>> t == Integer(0)
False
>>> t == M.tensor_module(Integer(1),Integer(1)).zero()
False
>>> t1 = t.copy()
>>> t1 == t
True
>>> t1[Integer(2),Integer(0)] = Integer(4)
>>> t1 == t
False
As a multilinear map \(M^* \times M \rightarrow \ZZ\), the type-\((1,1)\)
tensor t acts on pairs formed by a linear form and a module element:
sage: a = M.linear_form(name='a') ; a[:] = (2, 1, -3) ; a
Linear form a on the Rank-3 free module M over the Integer Ring
sage: b = M([1,-6,2], name='b') ; b
Element b of the Rank-3 free module M over the Integer Ring
sage: t(a,b)
-2
>>> from sage.all import *
>>> a = M.linear_form(name='a') ; a[:] = (Integer(2), Integer(1), -Integer(3)) ; a
Linear form a on the Rank-3 free module M over the Integer Ring
>>> b = M([Integer(1),-Integer(6),Integer(2)], name='b') ; b
Element b of the Rank-3 free module M over the Integer Ring
>>> t(a,b)
-2
- class sage.tensor.modules.free_module_tensor.FreeModuleTensor(fmodule: FiniteRankFreeModule, tensor_type, name: str | None = None, latex_name: str | None = None, sym=None, antisym=None, parent=None)[source]¶
- Bases: - ModuleElementWithMutability- Tensor over a free module of finite rank over a commutative ring. - This is a Sage element class, the corresponding parent class being - TensorFreeModule.- INPUT: - fmodule– free module \(M\) of finite rank over a commutative ring \(R\), as an instance of- FiniteRankFreeModule
- tensor_type– pair- (k, l)with- kbeing the contravariant rank and- lthe covariant rank
- name– (default:- None) name given to the tensor
- latex_name– (default:- None) LaTeX symbol to denote the tensor; if none is provided, the LaTeX symbol is set to- name
- sym– (default:- None) a symmetry or a list of symmetries among the tensor arguments: each symmetry is described by a tuple containing the positions of the involved arguments, with the convention- position=0for the first argument. For instance:- sym = (0,1)for a symmetry between the 1st and 2nd arguments;
- sym = [(0,2), (1,3,4)]for a symmetry between the 1st and 3rd arguments and a symmetry between the 2nd, 4th and 5th arguments.
 
- antisym– (default:- None) antisymmetry or list of antisymmetries among the arguments, with the same convention as for- sym
- parent– (default:- None) some specific parent (e.g. exterior power for alternating forms); if- None,- fmodule.tensor_module(k,l)is used
 - EXAMPLES: - A tensor of type \((1,1)\) on a rank-3 free module over \(\ZZ\): - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: t = M.tensor((1,1), name='t') ; t Type-(1,1) tensor t on the Rank-3 free module M over the Integer Ring - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> t = M.tensor((Integer(1),Integer(1)), name='t') ; t Type-(1,1) tensor t on the Rank-3 free module M over the Integer Ring - Tensors are Element objects whose parents are tensor free modules: - sage: t.parent() Free module of type-(1,1) tensors on the Rank-3 free module M over the Integer Ring sage: t.parent() is M.tensor_module(1,1) True - >>> from sage.all import * >>> t.parent() Free module of type-(1,1) tensors on the Rank-3 free module M over the Integer Ring >>> t.parent() is M.tensor_module(Integer(1),Integer(1)) True - add_comp(basis=None)[source]¶
- Return the components of - selfw.r.t. a given module basis for assignment, keeping the components w.r.t. other bases.- To delete the components w.r.t. other bases, use the method - set_comp()instead.- INPUT: - basis– (default:- None) basis in which the components are defined; if none is provided, the components are assumed to refer to the module’s default basis
 - Warning - If the tensor has already components in other bases, it is the user’s responsibility to make sure that the components to be added are consistent with them. - OUTPUT: - components in the given basis, as an instance of the class - Components; if such components did not exist previously, they are created
 - EXAMPLES: - Setting components of a type-\((1,1)\) tensor: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: t = M.tensor((1,1), name='t') sage: t.add_comp()[0,1] = -3 sage: t.display() t = -3 e_0⊗e^1 sage: t.add_comp()[1,2] = 2 sage: t.display() t = -3 e_0⊗e^1 + 2 e_1⊗e^2 sage: t.add_comp(e) 2-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> e = M.basis('e') >>> t = M.tensor((Integer(1),Integer(1)), name='t') >>> t.add_comp()[Integer(0),Integer(1)] = -Integer(3) >>> t.display() t = -3 e_0⊗e^1 >>> t.add_comp()[Integer(1),Integer(2)] = Integer(2) >>> t.display() t = -3 e_0⊗e^1 + 2 e_1⊗e^2 >>> t.add_comp(e) 2-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring - Adding components in a new basis: - sage: f = M.basis('f') sage: t.add_comp(f)[0,1] = 4 - >>> from sage.all import * >>> f = M.basis('f') >>> t.add_comp(f)[Integer(0),Integer(1)] = Integer(4) - The components w.r.t. basis e have been kept: - sage: sorted(t._components, key=repr) [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring] sage: t.display(f) t = 4 f_0⊗f^1 sage: t.display(e) t = -3 e_0⊗e^1 + 2 e_1⊗e^2 - >>> from sage.all import * >>> sorted(t._components, key=repr) [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring] >>> t.display(f) t = 4 f_0⊗f^1 >>> t.display(e) t = -3 e_0⊗e^1 + 2 e_1⊗e^2 - Since zero is an immutable element, its components cannot be changed: - sage: z = M.tensor_module(1, 1).zero() sage: z.add_comp(e)[0,1] = 1 Traceback (most recent call last): ... ValueError: the components of an immutable element cannot be changed - >>> from sage.all import * >>> z = M.tensor_module(Integer(1), Integer(1)).zero() >>> z.add_comp(e)[Integer(0),Integer(1)] = Integer(1) Traceback (most recent call last): ... ValueError: the components of an immutable element cannot be changed 
 - antisymmetrize(*pos, **kwargs)[source]¶
- Antisymmetrization over some arguments. - INPUT: - pos– list of argument positions involved in the antisymmetrization (with the convention- position=0for the first argument); if none, the antisymmetrization is performed over all the arguments
- basis– (default:- None) module basis with respect to which the component computation is to be performed; if none, the module’s default basis is used if the tensor field has already components in it; otherwise another basis w.r.t. which the tensor has components will be picked
 - OUTPUT: - the antisymmetrized tensor (instance of - FreeModuleTensor)
 - EXAMPLES: - Antisymmetrization of a tensor of type \((2,0)\): - sage: M = FiniteRankFreeModule(QQ, 3, name='M') sage: e = M.basis('e') sage: t = M.tensor((2,0)) sage: t[:] = [[1,-2,3], [4,5,6], [7,8,-9]] sage: s = t.antisymmetrize() ; s Alternating contravariant tensor of degree 2 on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() no symmetry; antisymmetry: (0, 1) sage: t[:], s[:] ( [ 1 -2 3] [ 0 -3 -2] [ 4 5 6] [ 3 0 -1] [ 7 8 -9], [ 2 1 0] ) sage: all(s[i,j] == 1/2*(t[i,j]-t[j,i]) # Check: ....: for i in range(3) for j in range(3)) True sage: s.antisymmetrize() == s # another test True sage: t.antisymmetrize() == t.antisymmetrize(0,1) True - >>> from sage.all import * >>> M = FiniteRankFreeModule(QQ, Integer(3), name='M') >>> e = M.basis('e') >>> t = M.tensor((Integer(2),Integer(0))) >>> t[:] = [[Integer(1),-Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),-Integer(9)]] >>> s = t.antisymmetrize() ; s Alternating contravariant tensor of degree 2 on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() no symmetry; antisymmetry: (0, 1) >>> t[:], s[:] ( [ 1 -2 3] [ 0 -3 -2] [ 4 5 6] [ 3 0 -1] [ 7 8 -9], [ 2 1 0] ) >>> all(s[i,j] == Integer(1)/Integer(2)*(t[i,j]-t[j,i]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3))) True >>> s.antisymmetrize() == s # another test True >>> t.antisymmetrize() == t.antisymmetrize(Integer(0),Integer(1)) True - Antisymmetrization of a tensor of type \((0, 3)\) over the first two arguments: - sage: t = M.tensor((0,3)) sage: t[:] = [[[1,2,3], [-4,5,6], [7,8,-9]], ....: [[10,-11,12], [13,14,-15], [16,17,18]], ....: [[19,-20,-21], [-22,23,24], [25,26,-27]]] sage: s = t.antisymmetrize(0,1) ; s # (0,1) = the first two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() no symmetry; antisymmetry: (0, 1) sage: s[:] [[[0, 0, 0], [-7, 8, -3], [-6, 14, 6]], [[7, -8, 3], [0, 0, 0], [19, -3, -3]], [[6, -14, -6], [-19, 3, 3], [0, 0, 0]]] sage: all(s[i,j,k] == 1/2*(t[i,j,k]-t[j,i,k]) # Check: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.antisymmetrize(0,1) == s # another test True sage: s.symmetrize(0,1) == 0 # of course True - >>> from sage.all import * >>> t = M.tensor((Integer(0),Integer(3))) >>> t[:] = [[[Integer(1),Integer(2),Integer(3)], [-Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),-Integer(9)]], ... [[Integer(10),-Integer(11),Integer(12)], [Integer(13),Integer(14),-Integer(15)], [Integer(16),Integer(17),Integer(18)]], ... [[Integer(19),-Integer(20),-Integer(21)], [-Integer(22),Integer(23),Integer(24)], [Integer(25),Integer(26),-Integer(27)]]] >>> s = t.antisymmetrize(Integer(0),Integer(1)) ; s # (0,1) = the first two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() no symmetry; antisymmetry: (0, 1) >>> s[:] [[[0, 0, 0], [-7, 8, -3], [-6, 14, 6]], [[7, -8, 3], [0, 0, 0], [19, -3, -3]], [[6, -14, -6], [-19, 3, 3], [0, 0, 0]]] >>> all(s[i,j,k] == Integer(1)/Integer(2)*(t[i,j,k]-t[j,i,k]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.antisymmetrize(Integer(0),Integer(1)) == s # another test True >>> s.symmetrize(Integer(0),Integer(1)) == Integer(0) # of course True - Instead of invoking the method - antisymmetrize(), one can use the index notation with square brackets denoting the antisymmetrization; it suffices to pass the indices as a string inside square brackets:- sage: s1 = t['_[ij]k'] ; s1 Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s1.symmetries() no symmetry; antisymmetry: (0, 1) sage: s1 == s True - >>> from sage.all import * >>> s1 = t['_[ij]k'] ; s1 Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s1.symmetries() no symmetry; antisymmetry: (0, 1) >>> s1 == s True - The LaTeX notation is recognized: - sage: t['_{[ij]k}'] == s True - >>> from sage.all import * >>> t['_{[ij]k}'] == s True - Note that in the index notation, the name of the indices is irrelevant; they can even be replaced by dots: - sage: t['_[..].'] == s True - >>> from sage.all import * >>> t['_[..].'] == s True - Antisymmetrization of a tensor of type \((0,3)\) over the first and last arguments: - sage: s = t.antisymmetrize(0,2) ; s # (0,2) = first and last arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() no symmetry; antisymmetry: (0, 2) sage: s[:] [[[0, -4, -8], [0, -4, 14], [0, -4, -17]], [[4, 0, 16], [4, 0, -19], [4, 0, -4]], [[8, -16, 0], [-14, 19, 0], [17, 4, 0]]] sage: all(s[i,j,k] == 1/2*(t[i,j,k]-t[k,j,i]) # Check: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.antisymmetrize(0,2) == s # another test True sage: s.symmetrize(0,2) == 0 # of course True sage: s.symmetrize(0,1) == 0 # no reason for this to hold False - >>> from sage.all import * >>> s = t.antisymmetrize(Integer(0),Integer(2)) ; s # (0,2) = first and last arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() no symmetry; antisymmetry: (0, 2) >>> s[:] [[[0, -4, -8], [0, -4, 14], [0, -4, -17]], [[4, 0, 16], [4, 0, -19], [4, 0, -4]], [[8, -16, 0], [-14, 19, 0], [17, 4, 0]]] >>> all(s[i,j,k] == Integer(1)/Integer(2)*(t[i,j,k]-t[k,j,i]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.antisymmetrize(Integer(0),Integer(2)) == s # another test True >>> s.symmetrize(Integer(0),Integer(2)) == Integer(0) # of course True >>> s.symmetrize(Integer(0),Integer(1)) == Integer(0) # no reason for this to hold False - Antisymmetrization of a tensor of type \((0,3)\) over the last two arguments: - sage: s = t.antisymmetrize(1,2) ; s # (1,2) = the last two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() no symmetry; antisymmetry: (1, 2) sage: s[:] [[[0, 3, -2], [-3, 0, -1], [2, 1, 0]], [[0, -12, -2], [12, 0, -16], [2, 16, 0]], [[0, 1, -23], [-1, 0, -1], [23, 1, 0]]] sage: all(s[i,j,k] == 1/2*(t[i,j,k]-t[i,k,j]) # Check: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.antisymmetrize(1,2) == s # another test True sage: s.symmetrize(1,2) == 0 # of course True - >>> from sage.all import * >>> s = t.antisymmetrize(Integer(1),Integer(2)) ; s # (1,2) = the last two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() no symmetry; antisymmetry: (1, 2) >>> s[:] [[[0, 3, -2], [-3, 0, -1], [2, 1, 0]], [[0, -12, -2], [12, 0, -16], [2, 16, 0]], [[0, 1, -23], [-1, 0, -1], [23, 1, 0]]] >>> all(s[i,j,k] == Integer(1)/Integer(2)*(t[i,j,k]-t[i,k,j]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.antisymmetrize(Integer(1),Integer(2)) == s # another test True >>> s.symmetrize(Integer(1),Integer(2)) == Integer(0) # of course True - The index notation can be used instead of the explicit call to - antisymmetrize():- sage: t['_i[jk]'] == t.antisymmetrize(1,2) True - >>> from sage.all import * >>> t['_i[jk]'] == t.antisymmetrize(Integer(1),Integer(2)) True - Full antisymmetrization of a tensor of type \((0,3)\): - sage: s = t.antisymmetrize() ; s Alternating form of degree 3 on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() no symmetry; antisymmetry: (0, 1, 2) sage: s[:] [[[0, 0, 0], [0, 0, 2/3], [0, -2/3, 0]], [[0, 0, -2/3], [0, 0, 0], [2/3, 0, 0]], [[0, 2/3, 0], [-2/3, 0, 0], [0, 0, 0]]] sage: all(s[i,j,k] == 1/6*(t[i,j,k]-t[i,k,j]+t[j,k,i]-t[j,i,k] ....: +t[k,i,j]-t[k,j,i]) ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.antisymmetrize() == s # another test True sage: s.symmetrize(0,1) == 0 # of course True sage: s.symmetrize(0,2) == 0 # of course True sage: s.symmetrize(1,2) == 0 # of course True sage: t.antisymmetrize() == t.antisymmetrize(0,1,2) True - >>> from sage.all import * >>> s = t.antisymmetrize() ; s Alternating form of degree 3 on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() no symmetry; antisymmetry: (0, 1, 2) >>> s[:] [[[0, 0, 0], [0, 0, 2/3], [0, -2/3, 0]], [[0, 0, -2/3], [0, 0, 0], [2/3, 0, 0]], [[0, 2/3, 0], [-2/3, 0, 0], [0, 0, 0]]] >>> all(s[i,j,k] == Integer(1)/Integer(6)*(t[i,j,k]-t[i,k,j]+t[j,k,i]-t[j,i,k] ... +t[k,i,j]-t[k,j,i]) ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.antisymmetrize() == s # another test True >>> s.symmetrize(Integer(0),Integer(1)) == Integer(0) # of course True >>> s.symmetrize(Integer(0),Integer(2)) == Integer(0) # of course True >>> s.symmetrize(Integer(1),Integer(2)) == Integer(0) # of course True >>> t.antisymmetrize() == t.antisymmetrize(Integer(0),Integer(1),Integer(2)) True - The index notation can be used instead of the explicit call to - antisymmetrize():- sage: t['_[ijk]'] == t.antisymmetrize() True sage: t['_[abc]'] == t.antisymmetrize() True sage: t['_[...]'] == t.antisymmetrize() True sage: t['_{[ijk]}'] == t.antisymmetrize() # LaTeX notation True - >>> from sage.all import * >>> t['_[ijk]'] == t.antisymmetrize() True >>> t['_[abc]'] == t.antisymmetrize() True >>> t['_[...]'] == t.antisymmetrize() True >>> t['_{[ijk]}'] == t.antisymmetrize() # LaTeX notation True - Antisymmetrization can be performed only on arguments on the same type: - sage: t = M.tensor((1,2)) sage: t[:] = [[[1,2,3], [-4,5,6], [7,8,-9]], ....: [[10,-11,12], [13,14,-15], [16,17,18]], ....: [[19,-20,-21], [-22,23,24], [25,26,-27]]] sage: s = t.antisymmetrize(0,1) Traceback (most recent call last): ... TypeError: 0 is a contravariant position, while 1 is a covariant position; antisymmetrization is meaningful only on tensor arguments of the same type sage: s = t.antisymmetrize(1,2) # OK: both 1 and 2 are covariant positions - >>> from sage.all import * >>> t = M.tensor((Integer(1),Integer(2))) >>> t[:] = [[[Integer(1),Integer(2),Integer(3)], [-Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),-Integer(9)]], ... [[Integer(10),-Integer(11),Integer(12)], [Integer(13),Integer(14),-Integer(15)], [Integer(16),Integer(17),Integer(18)]], ... [[Integer(19),-Integer(20),-Integer(21)], [-Integer(22),Integer(23),Integer(24)], [Integer(25),Integer(26),-Integer(27)]]] >>> s = t.antisymmetrize(Integer(0),Integer(1)) Traceback (most recent call last): ... TypeError: 0 is a contravariant position, while 1 is a covariant position; antisymmetrization is meaningful only on tensor arguments of the same type >>> s = t.antisymmetrize(Integer(1),Integer(2)) # OK: both 1 and 2 are covariant positions - The order of positions does not matter: - sage: t.antisymmetrize(2,1) == t.antisymmetrize(1,2) True - >>> from sage.all import * >>> t.antisymmetrize(Integer(2),Integer(1)) == t.antisymmetrize(Integer(1),Integer(2)) True - Again, the index notation can be used: - sage: t['^i_[jk]'] == t.antisymmetrize(1,2) True sage: t['^i_{[jk]}'] == t.antisymmetrize(1,2) # LaTeX notation True - >>> from sage.all import * >>> t['^i_[jk]'] == t.antisymmetrize(Integer(1),Integer(2)) True >>> t['^i_{[jk]}'] == t.antisymmetrize(Integer(1),Integer(2)) # LaTeX notation True - The character ‘^’ can be skipped: - sage: t['i_[jk]'] == t.antisymmetrize(1,2) True - >>> from sage.all import * >>> t['i_[jk]'] == t.antisymmetrize(Integer(1),Integer(2)) True 
 - base_module()[source]¶
- Return the module on which - selfis defined.- OUTPUT: - instance of - FiniteRankFreeModulerepresenting the free module on which the tensor is defined.
 - EXAMPLES: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: M.an_element().base_module() Rank-3 free module M over the Integer Ring sage: t = M.tensor((2,1)) sage: t.base_module() Rank-3 free module M over the Integer Ring sage: t.base_module() is M True - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> M.an_element().base_module() Rank-3 free module M over the Integer Ring >>> t = M.tensor((Integer(2),Integer(1))) >>> t.base_module() Rank-3 free module M over the Integer Ring >>> t.base_module() is M True 
 - common_basis(other)[source]¶
- Find a common basis for the components of - selfand- other.- In case of multiple common bases, the free module’s default basis is privileged. If the current components of - selfand- otherare all relative to different bases, a common basis is searched by performing a component transformation, via the transformations listed in- self._fmodule._basis_changes, still privileging transformations to the free module’s default basis.- INPUT: - other– a tensor (instance of- FreeModuleTensor)
 - OUTPUT: - instance of - FreeModuleBasisrepresenting the common basis; if no common basis is found,- Noneis returned
 - EXAMPLES: - Common basis for the components of two module elements: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: u = M([2,1,-5]) sage: f = M.basis('f') sage: M._basis_changes.clear() # to ensure that bases e and f are unrelated at this stage sage: v = M([0,4,2], basis=f) sage: u.common_basis(v) - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> u = M([Integer(2),Integer(1),-Integer(5)]) >>> f = M.basis('f') >>> M._basis_changes.clear() # to ensure that bases e and f are unrelated at this stage >>> v = M([Integer(0),Integer(4),Integer(2)], basis=f) >>> u.common_basis(v) - The above result is - Nonesince- uand- vhave been defined on different bases and no connection between these bases have been set:- sage: list(u._components) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring] sage: list(v._components) [Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] - >>> from sage.all import * >>> list(u._components) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring] >>> list(v._components) [Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] - Linking bases - eand- fchanges the result:- sage: a = M.automorphism() sage: a[:] = [[0,0,1], [1,0,0], [0,-1,0]] sage: M.set_change_of_basis(e, f, a) sage: u.common_basis(v) Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring - >>> from sage.all import * >>> a = M.automorphism() >>> a[:] = [[Integer(0),Integer(0),Integer(1)], [Integer(1),Integer(0),Integer(0)], [Integer(0),-Integer(1),Integer(0)]] >>> M.set_change_of_basis(e, f, a) >>> u.common_basis(v) Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring - Indeed, v is now known in basis e: - sage: sorted(v._components, key=repr) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring, Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] - >>> from sage.all import * >>> sorted(v._components, key=repr) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring, Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] 
 - comp(basis=None, from_basis=None)[source]¶
- Return the components of - selfw.r.t to a given module basis.- If the components are not known already, they are computed by the tensor change-of-basis formula from components in another basis. - INPUT: - basis– (default:- None) basis in which the components are required; if none is provided, the components are assumed to refer to the module’s default basis
- from_basis– (default:- None) basis from which the required components are computed, via the tensor change-of-basis formula, if they are not known already in the basis- basis; if none, a basis from which both the components and a change-of-basis to- basisare known is selected.
 - OUTPUT: - components in the basis - basis, as an instance of the class- Components
 - EXAMPLES: - Components of a tensor of type-\((1,1)\): - sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: t = M.tensor((1,1), name='t') sage: t[1,2] = -3 ; t[3,3] = 2 sage: t.components() 2-indices components w.r.t. Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring sage: t.components() is t.components(e) # since e is M's default basis True sage: t.components()[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> t = M.tensor((Integer(1),Integer(1)), name='t') >>> t[Integer(1),Integer(2)] = -Integer(3) ; t[Integer(3),Integer(3)] = Integer(2) >>> t.components() 2-indices components w.r.t. Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring >>> t.components() is t.components(e) # since e is M's default basis True >>> t.components()[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - A shortcut is - t.comp():- sage: t.comp() is t.components() True - >>> from sage.all import * >>> t.comp() is t.components() True - A direct access to the components w.r.t. the module’s default basis is provided by the square brackets applied to the tensor itself: - sage: t[1,2] is t.comp(e)[1,2] True sage: t[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - >>> from sage.all import * >>> t[Integer(1),Integer(2)] is t.comp(e)[Integer(1),Integer(2)] True >>> t[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - Components computed via a change-of-basis formula: - sage: a = M.automorphism() sage: a[:] = [[0,0,1], [1,0,0], [0,-1,0]] sage: f = e.new_basis(a, 'f') sage: t.comp(f) 2-indices components w.r.t. Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring sage: t.comp(f)[:] [ 0 0 0] [ 0 2 0] [-3 0 0] - >>> from sage.all import * >>> a = M.automorphism() >>> a[:] = [[Integer(0),Integer(0),Integer(1)], [Integer(1),Integer(0),Integer(0)], [Integer(0),-Integer(1),Integer(0)]] >>> f = e.new_basis(a, 'f') >>> t.comp(f) 2-indices components w.r.t. Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring >>> t.comp(f)[:] [ 0 0 0] [ 0 2 0] [-3 0 0] 
 - components(basis=None, from_basis=None)[source]¶
- Return the components of - selfw.r.t to a given module basis.- If the components are not known already, they are computed by the tensor change-of-basis formula from components in another basis. - INPUT: - basis– (default:- None) basis in which the components are required; if none is provided, the components are assumed to refer to the module’s default basis
- from_basis– (default:- None) basis from which the required components are computed, via the tensor change-of-basis formula, if they are not known already in the basis- basis; if none, a basis from which both the components and a change-of-basis to- basisare known is selected.
 - OUTPUT: - components in the basis - basis, as an instance of the class- Components
 - EXAMPLES: - Components of a tensor of type-\((1,1)\): - sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: t = M.tensor((1,1), name='t') sage: t[1,2] = -3 ; t[3,3] = 2 sage: t.components() 2-indices components w.r.t. Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring sage: t.components() is t.components(e) # since e is M's default basis True sage: t.components()[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> t = M.tensor((Integer(1),Integer(1)), name='t') >>> t[Integer(1),Integer(2)] = -Integer(3) ; t[Integer(3),Integer(3)] = Integer(2) >>> t.components() 2-indices components w.r.t. Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring >>> t.components() is t.components(e) # since e is M's default basis True >>> t.components()[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - A shortcut is - t.comp():- sage: t.comp() is t.components() True - >>> from sage.all import * >>> t.comp() is t.components() True - A direct access to the components w.r.t. the module’s default basis is provided by the square brackets applied to the tensor itself: - sage: t[1,2] is t.comp(e)[1,2] True sage: t[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - >>> from sage.all import * >>> t[Integer(1),Integer(2)] is t.comp(e)[Integer(1),Integer(2)] True >>> t[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] - Components computed via a change-of-basis formula: - sage: a = M.automorphism() sage: a[:] = [[0,0,1], [1,0,0], [0,-1,0]] sage: f = e.new_basis(a, 'f') sage: t.comp(f) 2-indices components w.r.t. Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring sage: t.comp(f)[:] [ 0 0 0] [ 0 2 0] [-3 0 0] - >>> from sage.all import * >>> a = M.automorphism() >>> a[:] = [[Integer(0),Integer(0),Integer(1)], [Integer(1),Integer(0),Integer(0)], [Integer(0),-Integer(1),Integer(0)]] >>> f = e.new_basis(a, 'f') >>> t.comp(f) 2-indices components w.r.t. Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring >>> t.comp(f)[:] [ 0 0 0] [ 0 2 0] [-3 0 0] 
 - contract(*args)[source]¶
- Contraction on one or more indices with another tensor. - INPUT: - pos1– positions of the indices in- selfinvolved in the contraction;- pos1must be a sequence of integers, with 0 standing for the first index position, 1 for the second one, etc; if- pos1is not provided, a single contraction on the last index position of- selfis assumed
- other– the tensor to contract with
- pos2– positions of the indices in- otherinvolved in the contraction, with the same conventions as for- pos1; if- pos2is not provided, a single contraction on the first index position of- otheris assumed
 - OUTPUT: - tensor resulting from the contraction at the positions - pos1and- pos2of- selfwith- other
 - EXAMPLES: - Contraction of a tensor of type \((0,1)\) with a tensor of type \((1,0)\): - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: a = M.linear_form() # tensor of type (0,1) is a linear form sage: a[:] = [-3,2,1] sage: b = M([2,5,-2]) # tensor of type (1,0) is a module element sage: s = a.contract(b) ; s 2 sage: s in M.base_ring() True sage: s == a[0]*b[0] + a[1]*b[1] + a[2]*b[2] # check of the computation True - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> e = M.basis('e') >>> a = M.linear_form() # tensor of type (0,1) is a linear form >>> a[:] = [-Integer(3),Integer(2),Integer(1)] >>> b = M([Integer(2),Integer(5),-Integer(2)]) # tensor of type (1,0) is a module element >>> s = a.contract(b) ; s 2 >>> s in M.base_ring() True >>> s == a[Integer(0)]*b[Integer(0)] + a[Integer(1)]*b[Integer(1)] + a[Integer(2)]*b[Integer(2)] # check of the computation True - The positions of the contraction indices can be set explicitly: - sage: s == a.contract(0, b, 0) True sage: s == a.contract(0, b) True sage: s == a.contract(b, 0) True - >>> from sage.all import * >>> s == a.contract(Integer(0), b, Integer(0)) True >>> s == a.contract(Integer(0), b) True >>> s == a.contract(b, Integer(0)) True - Instead of the explicit call to the method - contract(), the index notation can be used to specify the contraction, via Einstein convention (summation on repeated indices); it suffices to pass the indices as a string inside square brackets:- sage: s1 = a['_i']*b['^i'] ; s1 2 sage: s1 == s True - >>> from sage.all import * >>> s1 = a['_i']*b['^i'] ; s1 2 >>> s1 == s True - In the present case, performing the contraction is identical to applying the linear form to the module element: - sage: a.contract(b) == a(b) True - >>> from sage.all import * >>> a.contract(b) == a(b) True - or to applying the module element, considered as a tensor of type \((1,0)\), to the linear form: - sage: a.contract(b) == b(a) True - >>> from sage.all import * >>> a.contract(b) == b(a) True - We have also: - sage: a.contract(b) == b.contract(a) True - >>> from sage.all import * >>> a.contract(b) == b.contract(a) True - Contraction of a tensor of type \((1,1)\) with a tensor of type \((1,0)\): - sage: a = M.tensor((1,1)) sage: a[:] = [[-1,2,3],[4,-5,6],[7,8,9]] sage: s = a.contract(b) ; s Element of the Rank-3 free module M over the Integer Ring sage: s.display() 2 e_0 - 29 e_1 + 36 e_2 - >>> from sage.all import * >>> a = M.tensor((Integer(1),Integer(1))) >>> a[:] = [[-Integer(1),Integer(2),Integer(3)],[Integer(4),-Integer(5),Integer(6)],[Integer(7),Integer(8),Integer(9)]] >>> s = a.contract(b) ; s Element of the Rank-3 free module M over the Integer Ring >>> s.display() 2 e_0 - 29 e_1 + 36 e_2 - Since the index positions have not been specified, the contraction takes place on the last position of a (i.e. no. 1) and the first position of - b(i.e. no. 0):- sage: a.contract(b) == a.contract(1, b, 0) True sage: a.contract(b) == b.contract(0, a, 1) True sage: a.contract(b) == b.contract(a, 1) True - >>> from sage.all import * >>> a.contract(b) == a.contract(Integer(1), b, Integer(0)) True >>> a.contract(b) == b.contract(Integer(0), a, Integer(1)) True >>> a.contract(b) == b.contract(a, Integer(1)) True - Using the index notation with Einstein convention: - sage: a['^i_j']*b['^j'] == a.contract(b) True - >>> from sage.all import * >>> a['^i_j']*b['^j'] == a.contract(b) True - The index - ican be replaced by a dot:- sage: a['^._j']*b['^j'] == a.contract(b) True - >>> from sage.all import * >>> a['^._j']*b['^j'] == a.contract(b) True - and the symbol - ^may be omitted, the distinction between contravariant and covariant indices being the position with respect to the symbol- _:- sage: a['._j']*b['j'] == a.contract(b) True - >>> from sage.all import * >>> a['._j']*b['j'] == a.contract(b) True - Contraction is possible only between a contravariant index and a covariant one: - sage: a.contract(0, b) Traceback (most recent call last): ... TypeError: contraction on two contravariant indices not permitted - >>> from sage.all import * >>> a.contract(Integer(0), b) Traceback (most recent call last): ... TypeError: contraction on two contravariant indices not permitted - Contraction of a tensor of type \((2,1)\) with a tensor of type \((0,2)\): - sage: a = a*b ; a Type-(2,1) tensor on the Rank-3 free module M over the Integer Ring sage: b = M.tensor((0,2)) sage: b[:] = [[-2,3,1], [0,-2,3], [4,-7,6]] sage: s = a.contract(1, b, 1) ; s Type-(1,2) tensor on the Rank-3 free module M over the Integer Ring sage: s[:] [[[-9, 16, 39], [18, -32, -78], [27, -48, -117]], [[36, -64, -156], [-45, 80, 195], [54, -96, -234]], [[63, -112, -273], [72, -128, -312], [81, -144, -351]]] - >>> from sage.all import * >>> a = a*b ; a Type-(2,1) tensor on the Rank-3 free module M over the Integer Ring >>> b = M.tensor((Integer(0),Integer(2))) >>> b[:] = [[-Integer(2),Integer(3),Integer(1)], [Integer(0),-Integer(2),Integer(3)], [Integer(4),-Integer(7),Integer(6)]] >>> s = a.contract(Integer(1), b, Integer(1)) ; s Type-(1,2) tensor on the Rank-3 free module M over the Integer Ring >>> s[:] [[[-9, 16, 39], [18, -32, -78], [27, -48, -117]], [[36, -64, -156], [-45, 80, 195], [54, -96, -234]], [[63, -112, -273], [72, -128, -312], [81, -144, -351]]] - Check of the computation: - sage: all(s[i,j,k] == a[i,0,j]*b[k,0]+a[i,1,j]*b[k,1]+a[i,2,j]*b[k,2] ....: for i in range(3) for j in range(3) for k in range(3)) True - >>> from sage.all import * >>> all(s[i,j,k] == a[i,Integer(0),j]*b[k,Integer(0)]+a[i,Integer(1),j]*b[k,Integer(1)]+a[i,Integer(2),j]*b[k,Integer(2)] ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True - Using index notation: - sage: a['il_j']*b['_kl'] == a.contract(1, b, 1) True - >>> from sage.all import * >>> a['il_j']*b['_kl'] == a.contract(Integer(1), b, Integer(1)) True - LaTeX notation are allowed: - sage: a['^{il}_j']*b['_{kl}'] == a.contract(1, b, 1) True - >>> from sage.all import * >>> a['^{il}_j']*b['_{kl}'] == a.contract(Integer(1), b, Integer(1)) True - Indices not involved in the contraction may be replaced by dots: - sage: a['.l_.']*b['_.l'] == a.contract(1, b, 1) True - >>> from sage.all import * >>> a['.l_.']*b['_.l'] == a.contract(Integer(1), b, Integer(1)) True - The two tensors do not have to be defined on the same basis for the contraction to take place, reflecting the fact that the contraction is basis-independent: - sage: A = M.automorphism() sage: A[:] = [[0,0,1], [1,0,0], [0,-1,0]] sage: h = e.new_basis(A, 'h') sage: b.comp(h)[:] # forces the computation of b's components w.r.t. basis h [-2 -3 0] [ 7 6 -4] [ 3 -1 -2] sage: b.del_other_comp(h) # deletes components w.r.t. basis e sage: list(b._components) # indeed: [Basis (h_0,h_1,h_2) on the Rank-3 free module M over the Integer Ring] sage: list(a._components) # while a is known only in basis e: [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring] sage: s1 = a.contract(1, b, 1) ; s1 # yet the computation is possible Type-(1,2) tensor on the Rank-3 free module M over the Integer Ring sage: s1 == s # ... and yields the same result as previously: True - >>> from sage.all import * >>> A = M.automorphism() >>> A[:] = [[Integer(0),Integer(0),Integer(1)], [Integer(1),Integer(0),Integer(0)], [Integer(0),-Integer(1),Integer(0)]] >>> h = e.new_basis(A, 'h') >>> b.comp(h)[:] # forces the computation of b's components w.r.t. basis h [-2 -3 0] [ 7 6 -4] [ 3 -1 -2] >>> b.del_other_comp(h) # deletes components w.r.t. basis e >>> list(b._components) # indeed: [Basis (h_0,h_1,h_2) on the Rank-3 free module M over the Integer Ring] >>> list(a._components) # while a is known only in basis e: [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring] >>> s1 = a.contract(Integer(1), b, Integer(1)) ; s1 # yet the computation is possible Type-(1,2) tensor on the Rank-3 free module M over the Integer Ring >>> s1 == s # ... and yields the same result as previously: True - The contraction can be performed on more than a single index; for instance a \(2\)-indices contraction of a type-\((2,1)\) tensor with a type-\((1,2)\) one is: - sage: a # a is a tensor of type-(2,1) Type-(2,1) tensor on the Rank-3 free module M over the Integer Ring sage: b = M([1,-1,2])*b ; b # a tensor of type (1,2) Type-(1,2) tensor on the Rank-3 free module M over the Integer Ring sage: s = a.contract(1,2,b,1,0) ; s # the double contraction Type-(1,1) tensor on the Rank-3 free module M over the Integer Ring sage: s[:] [ -36 30 15] [-252 210 105] [-204 170 85] sage: s == a['^.k_l']*b['^l_k.'] # the same thing in index notation True - >>> from sage.all import * >>> a # a is a tensor of type-(2,1) Type-(2,1) tensor on the Rank-3 free module M over the Integer Ring >>> b = M([Integer(1),-Integer(1),Integer(2)])*b ; b # a tensor of type (1,2) Type-(1,2) tensor on the Rank-3 free module M over the Integer Ring >>> s = a.contract(Integer(1),Integer(2),b,Integer(1),Integer(0)) ; s # the double contraction Type-(1,1) tensor on the Rank-3 free module M over the Integer Ring >>> s[:] [ -36 30 15] [-252 210 105] [-204 170 85] >>> s == a['^.k_l']*b['^l_k.'] # the same thing in index notation True 
 - copy(name=None, latex_name=None)[source]¶
- Return an exact copy of - self.- The name and the derived quantities are not copied. - INPUT: - name– (default:- None) name given to the copy
- latex_name– (default:- None) LaTeX symbol to denote the copy; if none is provided, the LaTeX symbol is set to- name
 - EXAMPLES: - Copy of a tensor of type \((1,1)\): - sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: t = M.tensor((1,1), name='t') sage: t[1,2] = -3 ; t[3,3] = 2 sage: t1 = t.copy() sage: t1[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] sage: t1 == t True - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> t = M.tensor((Integer(1),Integer(1)), name='t') >>> t[Integer(1),Integer(2)] = -Integer(3) ; t[Integer(3),Integer(3)] = Integer(2) >>> t1 = t.copy() >>> t1[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] >>> t1 == t True - If the original tensor is modified, the copy is not: - sage: t[2,2] = 4 sage: t1[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] sage: t1 == t False - >>> from sage.all import * >>> t[Integer(2),Integer(2)] = Integer(4) >>> t1[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] >>> t1 == t False 
 - copy_from(other)[source]¶
- Make - selfto a copy from- other.- INPUT: - other– other tensor in the very same module from which- selfshould be a copy of
 - Warning - All previous defined components will be deleted! - EXAMPLES: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: t = M.tensor((1,1), name='t') sage: t[1,2] = -3 ; t[3,3] = 2 sage: s = M.tensor((1,1), name='s') sage: s.copy_from(t) sage: s[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] sage: s == t True - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> t = M.tensor((Integer(1),Integer(1)), name='t') >>> t[Integer(1),Integer(2)] = -Integer(3) ; t[Integer(3),Integer(3)] = Integer(2) >>> s = M.tensor((Integer(1),Integer(1)), name='s') >>> s.copy_from(t) >>> s[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] >>> s == t True - If the original tensor is modified, the copy is not: - sage: t[2,2] = 4 sage: s[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] sage: s == t False - >>> from sage.all import * >>> t[Integer(2),Integer(2)] = Integer(4) >>> s[:] [ 0 -3 0] [ 0 0 0] [ 0 0 2] >>> s == t False 
 - del_other_comp(basis=None)[source]¶
- Delete all the components but those corresponding to - basis.- INPUT: - basis– (default:- None) basis in which the components are kept; if none the module’s default basis is assumed
 - EXAMPLES: - Deleting components of a module element: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: u = M([2,1,-5]) sage: f = M.basis('f') sage: u.add_comp(f)[:] = [0,4,2] sage: sorted(u._components, key=repr) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring, Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] sage: u.del_other_comp(f) sage: list(u._components) [Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> u = M([Integer(2),Integer(1),-Integer(5)]) >>> f = M.basis('f') >>> u.add_comp(f)[:] = [Integer(0),Integer(4),Integer(2)] >>> sorted(u._components, key=repr) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring, Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] >>> u.del_other_comp(f) >>> list(u._components) [Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] - Let us restore the components w.r.t. e and delete those w.r.t. f: - sage: u.add_comp(e)[:] = [2,1,-5] sage: sorted(u._components, key=repr) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring, Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] sage: u.del_other_comp() # default argument: basis = e sage: list(u._components) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring] - >>> from sage.all import * >>> u.add_comp(e)[:] = [Integer(2),Integer(1),-Integer(5)] >>> sorted(u._components, key=repr) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring, Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring] >>> u.del_other_comp() # default argument: basis = e >>> list(u._components) [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring] 
 - disp(basis=None, format_spec=None)[source]¶
- Display - selfin terms of its expansion w.r.t. a given module basis.- The expansion is actually performed onto tensor products of elements of the given basis and of elements of its dual basis (see examples below). The output is either text-formatted (console mode) or LaTeX-formatted (notebook mode). - INPUT: - basis– (default:- None) basis of the free module with respect to which the tensor is expanded; if none is provided, the module’s default basis is assumed
- format_spec– (default:- None) format specification passed to- self._fmodule._output_formatterto format the output
 - EXAMPLES: - Display of a module element (type-\((1,0)\) tensor): - sage: M = FiniteRankFreeModule(QQ, 2, name='M', start_index=1) sage: e = M.basis('e') ; e Basis (e_1,e_2) on the 2-dimensional vector space M over the Rational Field sage: v = M([1/3,-2], name='v') sage: v.display(e) v = 1/3 e_1 - 2 e_2 sage: v.display() # a shortcut since e is M's default basis v = 1/3 e_1 - 2 e_2 sage: latex(v.display()) # display in the notebook v = \frac{1}{3} e_{1} -2 e_{2} - >>> from sage.all import * >>> M = FiniteRankFreeModule(QQ, Integer(2), name='M', start_index=Integer(1)) >>> e = M.basis('e') ; e Basis (e_1,e_2) on the 2-dimensional vector space M over the Rational Field >>> v = M([Integer(1)/Integer(3),-Integer(2)], name='v') >>> v.display(e) v = 1/3 e_1 - 2 e_2 >>> v.display() # a shortcut since e is M's default basis v = 1/3 e_1 - 2 e_2 >>> latex(v.display()) # display in the notebook v = \frac{1}{3} e_{1} -2 e_{2} - A shortcut is - disp():- sage: v.disp() v = 1/3 e_1 - 2 e_2 - >>> from sage.all import * >>> v.disp() v = 1/3 e_1 - 2 e_2 - Display of a linear form (type-\((0,1)\) tensor): - sage: de = e.dual_basis() ; de Dual basis (e^1,e^2) on the 2-dimensional vector space M over the Rational Field sage: w = - 3/4 * de[1] + de[2] ; w Linear form on the 2-dimensional vector space M over the Rational Field sage: w.set_name('w', latex_name='\\omega') sage: w.display() w = -3/4 e^1 + e^2 sage: latex(w.display()) # display in the notebook \omega = -\frac{3}{4} e^{1} +e^{2} - >>> from sage.all import * >>> de = e.dual_basis() ; de Dual basis (e^1,e^2) on the 2-dimensional vector space M over the Rational Field >>> w = - Integer(3)/Integer(4) * de[Integer(1)] + de[Integer(2)] ; w Linear form on the 2-dimensional vector space M over the Rational Field >>> w.set_name('w', latex_name='\\omega') >>> w.display() w = -3/4 e^1 + e^2 >>> latex(w.display()) # display in the notebook \omega = -\frac{3}{4} e^{1} +e^{2} - Display of a type-\((1,1)\) tensor: - sage: t = v*w ; t # the type-(1,1) is formed as the tensor product of v by w Type-(1,1) tensor v⊗w on the 2-dimensional vector space M over the Rational Field sage: t.display() v⊗w = -1/4 e_1⊗e^1 + 1/3 e_1⊗e^2 + 3/2 e_2⊗e^1 - 2 e_2⊗e^2 sage: latex(t.display()) # display in the notebook v\otimes \omega = -\frac{1}{4} e_{1}\otimes e^{1} + \frac{1}{3} e_{1}\otimes e^{2} + \frac{3}{2} e_{2}\otimes e^{1} -2 e_{2}\otimes e^{2} - >>> from sage.all import * >>> t = v*w ; t # the type-(1,1) is formed as the tensor product of v by w Type-(1,1) tensor v⊗w on the 2-dimensional vector space M over the Rational Field >>> t.display() v⊗w = -1/4 e_1⊗e^1 + 1/3 e_1⊗e^2 + 3/2 e_2⊗e^1 - 2 e_2⊗e^2 >>> latex(t.display()) # display in the notebook v\otimes \omega = -\frac{1}{4} e_{1}\otimes e^{1} + \frac{1}{3} e_{1}\otimes e^{2} + \frac{3}{2} e_{2}\otimes e^{1} -2 e_{2}\otimes e^{2} - Display in a basis which is not the default one: - sage: a = M.automorphism(matrix=[[1,2],[3,4]], basis=e) sage: f = e.new_basis(a, 'f') sage: v.display(f) # the components w.r.t basis f are first computed via the change-of-basis formula defined by a v = -8/3 f_1 + 3/2 f_2 sage: w.display(f) w = 9/4 f^1 + 5/2 f^2 sage: t.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 - >>> from sage.all import * >>> a = M.automorphism(matrix=[[Integer(1),Integer(2)],[Integer(3),Integer(4)]], basis=e) >>> f = e.new_basis(a, 'f') >>> v.display(f) # the components w.r.t basis f are first computed via the change-of-basis formula defined by a v = -8/3 f_1 + 3/2 f_2 >>> w.display(f) w = 9/4 f^1 + 5/2 f^2 >>> t.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 - Parallel computation: - sage: Parallelism().set('tensor', nproc=2) sage: t2 = v*w sage: t2.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 sage: t2[f,:] == t[f,:] # check of the parallel computation True sage: Parallelism().set('tensor', nproc=1) # switch off parallelization - >>> from sage.all import * >>> Parallelism().set('tensor', nproc=Integer(2)) >>> t2 = v*w >>> t2.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 >>> t2[f,:] == t[f,:] # check of the parallel computation True >>> Parallelism().set('tensor', nproc=Integer(1)) # switch off parallelization - The output format can be set via the argument - output_formatterpassed at the module construction:- sage: N = FiniteRankFreeModule(QQ, 2, name='N', start_index=1, ....: output_formatter=Rational.numerical_approx) sage: e = N.basis('e') sage: v = N([1/3,-2], name='v') sage: v.display() # default format (53 bits of precision) v = 0.333333333333333 e_1 - 2.00000000000000 e_2 sage: latex(v.display()) v = 0.333333333333333 e_{1} -2.00000000000000 e_{2} - >>> from sage.all import * >>> N = FiniteRankFreeModule(QQ, Integer(2), name='N', start_index=Integer(1), ... output_formatter=Rational.numerical_approx) >>> e = N.basis('e') >>> v = N([Integer(1)/Integer(3),-Integer(2)], name='v') >>> v.display() # default format (53 bits of precision) v = 0.333333333333333 e_1 - 2.00000000000000 e_2 >>> latex(v.display()) v = 0.333333333333333 e_{1} -2.00000000000000 e_{2} - The output format is then controlled by the argument - format_specof the method- display():- sage: v.display(format_spec=10) # 10 bits of precision v = 0.33 e_1 - 2.0 e_2 - >>> from sage.all import * >>> v.display(format_spec=Integer(10)) # 10 bits of precision v = 0.33 e_1 - 2.0 e_2 - Check that the bug reported in Issue #22520 is fixed: - sage: # needs sage.symbolic sage: M = FiniteRankFreeModule(SR, 3, name='M') sage: e = M.basis('e') sage: t = SR.var('t', domain='real') sage: (t*e[0]).display() t e_0 - >>> from sage.all import * >>> # needs sage.symbolic >>> M = FiniteRankFreeModule(SR, Integer(3), name='M') >>> e = M.basis('e') >>> t = SR.var('t', domain='real') >>> (t*e[Integer(0)]).display() t e_0 
 - display(basis=None, format_spec=None)[source]¶
- Display - selfin terms of its expansion w.r.t. a given module basis.- The expansion is actually performed onto tensor products of elements of the given basis and of elements of its dual basis (see examples below). The output is either text-formatted (console mode) or LaTeX-formatted (notebook mode). - INPUT: - basis– (default:- None) basis of the free module with respect to which the tensor is expanded; if none is provided, the module’s default basis is assumed
- format_spec– (default:- None) format specification passed to- self._fmodule._output_formatterto format the output
 - EXAMPLES: - Display of a module element (type-\((1,0)\) tensor): - sage: M = FiniteRankFreeModule(QQ, 2, name='M', start_index=1) sage: e = M.basis('e') ; e Basis (e_1,e_2) on the 2-dimensional vector space M over the Rational Field sage: v = M([1/3,-2], name='v') sage: v.display(e) v = 1/3 e_1 - 2 e_2 sage: v.display() # a shortcut since e is M's default basis v = 1/3 e_1 - 2 e_2 sage: latex(v.display()) # display in the notebook v = \frac{1}{3} e_{1} -2 e_{2} - >>> from sage.all import * >>> M = FiniteRankFreeModule(QQ, Integer(2), name='M', start_index=Integer(1)) >>> e = M.basis('e') ; e Basis (e_1,e_2) on the 2-dimensional vector space M over the Rational Field >>> v = M([Integer(1)/Integer(3),-Integer(2)], name='v') >>> v.display(e) v = 1/3 e_1 - 2 e_2 >>> v.display() # a shortcut since e is M's default basis v = 1/3 e_1 - 2 e_2 >>> latex(v.display()) # display in the notebook v = \frac{1}{3} e_{1} -2 e_{2} - A shortcut is - disp():- sage: v.disp() v = 1/3 e_1 - 2 e_2 - >>> from sage.all import * >>> v.disp() v = 1/3 e_1 - 2 e_2 - Display of a linear form (type-\((0,1)\) tensor): - sage: de = e.dual_basis() ; de Dual basis (e^1,e^2) on the 2-dimensional vector space M over the Rational Field sage: w = - 3/4 * de[1] + de[2] ; w Linear form on the 2-dimensional vector space M over the Rational Field sage: w.set_name('w', latex_name='\\omega') sage: w.display() w = -3/4 e^1 + e^2 sage: latex(w.display()) # display in the notebook \omega = -\frac{3}{4} e^{1} +e^{2} - >>> from sage.all import * >>> de = e.dual_basis() ; de Dual basis (e^1,e^2) on the 2-dimensional vector space M over the Rational Field >>> w = - Integer(3)/Integer(4) * de[Integer(1)] + de[Integer(2)] ; w Linear form on the 2-dimensional vector space M over the Rational Field >>> w.set_name('w', latex_name='\\omega') >>> w.display() w = -3/4 e^1 + e^2 >>> latex(w.display()) # display in the notebook \omega = -\frac{3}{4} e^{1} +e^{2} - Display of a type-\((1,1)\) tensor: - sage: t = v*w ; t # the type-(1,1) is formed as the tensor product of v by w Type-(1,1) tensor v⊗w on the 2-dimensional vector space M over the Rational Field sage: t.display() v⊗w = -1/4 e_1⊗e^1 + 1/3 e_1⊗e^2 + 3/2 e_2⊗e^1 - 2 e_2⊗e^2 sage: latex(t.display()) # display in the notebook v\otimes \omega = -\frac{1}{4} e_{1}\otimes e^{1} + \frac{1}{3} e_{1}\otimes e^{2} + \frac{3}{2} e_{2}\otimes e^{1} -2 e_{2}\otimes e^{2} - >>> from sage.all import * >>> t = v*w ; t # the type-(1,1) is formed as the tensor product of v by w Type-(1,1) tensor v⊗w on the 2-dimensional vector space M over the Rational Field >>> t.display() v⊗w = -1/4 e_1⊗e^1 + 1/3 e_1⊗e^2 + 3/2 e_2⊗e^1 - 2 e_2⊗e^2 >>> latex(t.display()) # display in the notebook v\otimes \omega = -\frac{1}{4} e_{1}\otimes e^{1} + \frac{1}{3} e_{1}\otimes e^{2} + \frac{3}{2} e_{2}\otimes e^{1} -2 e_{2}\otimes e^{2} - Display in a basis which is not the default one: - sage: a = M.automorphism(matrix=[[1,2],[3,4]], basis=e) sage: f = e.new_basis(a, 'f') sage: v.display(f) # the components w.r.t basis f are first computed via the change-of-basis formula defined by a v = -8/3 f_1 + 3/2 f_2 sage: w.display(f) w = 9/4 f^1 + 5/2 f^2 sage: t.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 - >>> from sage.all import * >>> a = M.automorphism(matrix=[[Integer(1),Integer(2)],[Integer(3),Integer(4)]], basis=e) >>> f = e.new_basis(a, 'f') >>> v.display(f) # the components w.r.t basis f are first computed via the change-of-basis formula defined by a v = -8/3 f_1 + 3/2 f_2 >>> w.display(f) w = 9/4 f^1 + 5/2 f^2 >>> t.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 - Parallel computation: - sage: Parallelism().set('tensor', nproc=2) sage: t2 = v*w sage: t2.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 sage: t2[f,:] == t[f,:] # check of the parallel computation True sage: Parallelism().set('tensor', nproc=1) # switch off parallelization - >>> from sage.all import * >>> Parallelism().set('tensor', nproc=Integer(2)) >>> t2 = v*w >>> t2.display(f) v⊗w = -6 f_1⊗f^1 - 20/3 f_1⊗f^2 + 27/8 f_2⊗f^1 + 15/4 f_2⊗f^2 >>> t2[f,:] == t[f,:] # check of the parallel computation True >>> Parallelism().set('tensor', nproc=Integer(1)) # switch off parallelization - The output format can be set via the argument - output_formatterpassed at the module construction:- sage: N = FiniteRankFreeModule(QQ, 2, name='N', start_index=1, ....: output_formatter=Rational.numerical_approx) sage: e = N.basis('e') sage: v = N([1/3,-2], name='v') sage: v.display() # default format (53 bits of precision) v = 0.333333333333333 e_1 - 2.00000000000000 e_2 sage: latex(v.display()) v = 0.333333333333333 e_{1} -2.00000000000000 e_{2} - >>> from sage.all import * >>> N = FiniteRankFreeModule(QQ, Integer(2), name='N', start_index=Integer(1), ... output_formatter=Rational.numerical_approx) >>> e = N.basis('e') >>> v = N([Integer(1)/Integer(3),-Integer(2)], name='v') >>> v.display() # default format (53 bits of precision) v = 0.333333333333333 e_1 - 2.00000000000000 e_2 >>> latex(v.display()) v = 0.333333333333333 e_{1} -2.00000000000000 e_{2} - The output format is then controlled by the argument - format_specof the method- display():- sage: v.display(format_spec=10) # 10 bits of precision v = 0.33 e_1 - 2.0 e_2 - >>> from sage.all import * >>> v.display(format_spec=Integer(10)) # 10 bits of precision v = 0.33 e_1 - 2.0 e_2 - Check that the bug reported in Issue #22520 is fixed: - sage: # needs sage.symbolic sage: M = FiniteRankFreeModule(SR, 3, name='M') sage: e = M.basis('e') sage: t = SR.var('t', domain='real') sage: (t*e[0]).display() t e_0 - >>> from sage.all import * >>> # needs sage.symbolic >>> M = FiniteRankFreeModule(SR, Integer(3), name='M') >>> e = M.basis('e') >>> t = SR.var('t', domain='real') >>> (t*e[Integer(0)]).display() t e_0 
 - display_comp(basis=None, format_spec=None, symbol=None, latex_symbol=None, index_labels=None, index_latex_labels=None, only_nonzero=True, only_nonredundant=False)[source]¶
- Display the tensor components with respect to a given module basis, one per line. - The output is either text-formatted (console mode) or LaTeX-formatted (notebook mode). - INPUT: - basis– (default:- None) basis of the free module with respect to which the tensor components are defined; if- None, the module’s default basis is assumed
- format_spec– (default:- None) format specification passed to- self._fmodule._output_formatterto format the output
- symbol– (default:- None) string (typically a single letter) specifying the symbol for the components; if- None, the tensor name is used if it has been set, otherwise- 'X'is used
- latex_symbol– (default:- None) string specifying the LaTeX symbol for the components; if- None, the tensor LaTeX name is used if it has been set, otherwise- 'X'is used
- index_labels– (default:- None) list of strings representing the labels of each of the individual indices; if- None, integer labels are used
- index_latex_labels– (default:- None) list of strings representing the LaTeX labels of each of the individual indices; if- None, integers labels are used
- only_nonzero– boolean (default:- True); if- True, only nonzero components are displayed
- only_nonredundant– boolean (default:- False); if- True, only nonredundant components are displayed in case of symmetries
 - EXAMPLES: - Display of the components of a type-\((2,1)\) tensor on a rank 2 vector space over \(\QQ\): - sage: FiniteRankFreeModule._clear_cache_() # for doctests only sage: M = FiniteRankFreeModule(QQ, 2, name='M', start_index=1) sage: e = M.basis('e') sage: t = M.tensor((2,1), name='T', sym=(0,1)) sage: t[1,2,1], t[1,2,2], t[2,2,2] = 2/3, -1/4, 3 sage: t.display() T = 2/3 e_1⊗e_2⊗e^1 - 1/4 e_1⊗e_2⊗e^2 + 2/3 e_2⊗e_1⊗e^1 - 1/4 e_2⊗e_1⊗e^2 + 3 e_2⊗e_2⊗e^2 sage: t.display_comp() T^12_1 = 2/3 T^12_2 = -1/4 T^21_1 = 2/3 T^21_2 = -1/4 T^22_2 = 3 - >>> from sage.all import * >>> FiniteRankFreeModule._clear_cache_() # for doctests only >>> M = FiniteRankFreeModule(QQ, Integer(2), name='M', start_index=Integer(1)) >>> e = M.basis('e') >>> t = M.tensor((Integer(2),Integer(1)), name='T', sym=(Integer(0),Integer(1))) >>> t[Integer(1),Integer(2),Integer(1)], t[Integer(1),Integer(2),Integer(2)], t[Integer(2),Integer(2),Integer(2)] = Integer(2)/Integer(3), -Integer(1)/Integer(4), Integer(3) >>> t.display() T = 2/3 e_1⊗e_2⊗e^1 - 1/4 e_1⊗e_2⊗e^2 + 2/3 e_2⊗e_1⊗e^1 - 1/4 e_2⊗e_1⊗e^2 + 3 e_2⊗e_2⊗e^2 >>> t.display_comp() T^12_1 = 2/3 T^12_2 = -1/4 T^21_1 = 2/3 T^21_2 = -1/4 T^22_2 = 3 - The LaTeX output for the notebook: - sage: latex(t.display_comp()) \begin{array}{lcl} {T}_{\phantom{\, 1}\phantom{\, 2}\,1}^{\,1\,2\phantom{\, 1}} & = & \frac{2}{3} \\ {T}_{\phantom{\, 1}\phantom{\, 2}\,2}^{\,1\,2\phantom{\, 2}} & = & -\frac{1}{4} \\ {T}_{\phantom{\, 2}\phantom{\, 1}\,1}^{\,2\,1\phantom{\, 1}} & = & \frac{2}{3} \\ {T}_{\phantom{\, 2}\phantom{\, 1}\,2}^{\,2\,1\phantom{\, 2}} & = & -\frac{1}{4} \\ {T}_{\phantom{\, 2}\phantom{\, 2}\,2}^{\,2\,2\phantom{\, 2}} & = & 3 \end{array} - >>> from sage.all import * >>> latex(t.display_comp()) \begin{array}{lcl} {T}_{\phantom{\, 1}\phantom{\, 2}\,1}^{\,1\,2\phantom{\, 1}} & = & \frac{2}{3} \\ {T}_{\phantom{\, 1}\phantom{\, 2}\,2}^{\,1\,2\phantom{\, 2}} & = & -\frac{1}{4} \\ {T}_{\phantom{\, 2}\phantom{\, 1}\,1}^{\,2\,1\phantom{\, 1}} & = & \frac{2}{3} \\ {T}_{\phantom{\, 2}\phantom{\, 1}\,2}^{\,2\,1\phantom{\, 2}} & = & -\frac{1}{4} \\ {T}_{\phantom{\, 2}\phantom{\, 2}\,2}^{\,2\,2\phantom{\, 2}} & = & 3 \end{array} - By default, only the non-vanishing components are displayed; to see all the components, the argument - only_nonzeromust be set to- False:- sage: t.display_comp(only_nonzero=False) T^11_1 = 0 T^11_2 = 0 T^12_1 = 2/3 T^12_2 = -1/4 T^21_1 = 2/3 T^21_2 = -1/4 T^22_1 = 0 T^22_2 = 3 - >>> from sage.all import * >>> t.display_comp(only_nonzero=False) T^11_1 = 0 T^11_2 = 0 T^12_1 = 2/3 T^12_2 = -1/4 T^21_1 = 2/3 T^21_2 = -1/4 T^22_1 = 0 T^22_2 = 3 - tbeing symmetric w.r.t. to its first two indices, one may ask to skip the components that can be deduced by symmetry:- sage: t.display_comp(only_nonredundant=True) T^12_1 = 2/3 T^12_2 = -1/4 T^22_2 = 3 - >>> from sage.all import * >>> t.display_comp(only_nonredundant=True) T^12_1 = 2/3 T^12_2 = -1/4 T^22_2 = 3 - The index symbols can be customized: - sage: t.display_comp(index_labels=['x', 'y']) T^xy_x = 2/3 T^xy_y = -1/4 T^yx_x = 2/3 T^yx_y = -1/4 T^yy_y = 3 - >>> from sage.all import * >>> t.display_comp(index_labels=['x', 'y']) T^xy_x = 2/3 T^xy_y = -1/4 T^yx_x = 2/3 T^yx_y = -1/4 T^yy_y = 3 - Display of the components w.r.t. a basis different from the default one: - sage: f = M.basis('f', from_family=(-e[1]+e[2], e[1]+e[2])) sage: t.display_comp(basis=f) T^11_1 = 29/24 T^11_2 = 13/24 T^12_1 = 3/4 T^12_2 = 3/4 T^21_1 = 3/4 T^21_2 = 3/4 T^22_1 = 7/24 T^22_2 = 23/24 - >>> from sage.all import * >>> f = M.basis('f', from_family=(-e[Integer(1)]+e[Integer(2)], e[Integer(1)]+e[Integer(2)])) >>> t.display_comp(basis=f) T^11_1 = 29/24 T^11_2 = 13/24 T^12_1 = 3/4 T^12_2 = 3/4 T^21_1 = 3/4 T^21_2 = 3/4 T^22_1 = 7/24 T^22_2 = 23/24 
 - pick_a_basis()[source]¶
- Return a basis in which the tensor components are defined. - The free module’s default basis is privileged. - OUTPUT: - instance of - FreeModuleBasisrepresenting the basis
 - EXAMPLES: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: t = M.tensor((2,0), name='t') sage: e = M.basis('e') sage: t[0,1] = 4 # component set in the default basis (e) sage: t.pick_a_basis() Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: f = M.basis('f') sage: t.add_comp(f)[2,1] = -4 # the components in basis e are not erased sage: t.pick_a_basis() Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: t.set_comp(f)[2,1] = -4 # the components in basis e not erased sage: t.pick_a_basis() Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> t = M.tensor((Integer(2),Integer(0)), name='t') >>> e = M.basis('e') >>> t[Integer(0),Integer(1)] = Integer(4) # component set in the default basis (e) >>> t.pick_a_basis() Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring >>> f = M.basis('f') >>> t.add_comp(f)[Integer(2),Integer(1)] = -Integer(4) # the components in basis e are not erased >>> t.pick_a_basis() Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring >>> t.set_comp(f)[Integer(2),Integer(1)] = -Integer(4) # the components in basis e not erased >>> t.pick_a_basis() Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring 
 - set_comp(basis=None)[source]¶
- Return the components of - selfw.r.t. a given module basis for assignment.- The components with respect to other bases are deleted, in order to avoid any inconsistency. To keep them, use the method - add_comp()instead.- INPUT: - basis– (default:- None) basis in which the components are defined; if none is provided, the components are assumed to refer to the module’s default basis
 - OUTPUT: - components in the given basis, as an instance of the class - Components; if such components did not exist previously, they are created.
 - EXAMPLES: - Setting components of a type-\((1,1)\) tensor: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: t = M.tensor((1,1), name='t') sage: t.set_comp()[0,1] = -3 sage: t.display() t = -3 e_0⊗e^1 sage: t.set_comp()[1,2] = 2 sage: t.display() t = -3 e_0⊗e^1 + 2 e_1⊗e^2 sage: t.set_comp(e) 2-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> e = M.basis('e') >>> t = M.tensor((Integer(1),Integer(1)), name='t') >>> t.set_comp()[Integer(0),Integer(1)] = -Integer(3) >>> t.display() t = -3 e_0⊗e^1 >>> t.set_comp()[Integer(1),Integer(2)] = Integer(2) >>> t.display() t = -3 e_0⊗e^1 + 2 e_1⊗e^2 >>> t.set_comp(e) 2-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring - Setting components in a new basis: - sage: f = M.basis('f') sage: t.set_comp(f)[0,1] = 4 sage: list(t._components) # the components w.r.t. basis e have been deleted [Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring] sage: t.display(f) t = 4 f_0⊗f^1 - >>> from sage.all import * >>> f = M.basis('f') >>> t.set_comp(f)[Integer(0),Integer(1)] = Integer(4) >>> list(t._components) # the components w.r.t. basis e have been deleted [Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring] >>> t.display(f) t = 4 f_0⊗f^1 - The components w.r.t. basis e can be deduced from those w.r.t. basis f, once a relation between the two bases has been set: - sage: a = M.automorphism() sage: a[:] = [[0,0,1], [1,0,0], [0,-1,0]] sage: M.set_change_of_basis(e, f, a) sage: t.display(e) t = -4 e_1⊗e^2 sage: sorted(t._components, key=repr) [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring] - >>> from sage.all import * >>> a = M.automorphism() >>> a[:] = [[Integer(0),Integer(0),Integer(1)], [Integer(1),Integer(0),Integer(0)], [Integer(0),-Integer(1),Integer(0)]] >>> M.set_change_of_basis(e, f, a) >>> t.display(e) t = -4 e_1⊗e^2 >>> sorted(t._components, key=repr) [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring] - Since zero is an immutable element, its components cannot be changed: - sage: z = M.tensor_module(1, 1).zero() sage: z.set_comp(e)[0,1] = 1 Traceback (most recent call last): ... ValueError: the components of an immutable element cannot be changed - >>> from sage.all import * >>> z = M.tensor_module(Integer(1), Integer(1)).zero() >>> z.set_comp(e)[Integer(0),Integer(1)] = Integer(1) Traceback (most recent call last): ... ValueError: the components of an immutable element cannot be changed 
 - set_name(name=None, latex_name=None)[source]¶
- Set (or change) the text name and LaTeX name of - self.- INPUT: - name– (default:- None) string; name given to the tensor
- latex_name– (default:- None) string; LaTeX symbol to denote the tensor; if None while- nameis provided, the LaTeX symbol is set to- name
 - EXAMPLES: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: t = M.tensor((2,1)) ; t Type-(2,1) tensor on the Rank-3 free module M over the Integer Ring sage: t.set_name('t') ; t Type-(2,1) tensor t on the Rank-3 free module M over the Integer Ring sage: latex(t) t sage: t.set_name(latex_name=r'\tau') ; t Type-(2,1) tensor t on the Rank-3 free module M over the Integer Ring sage: latex(t) \tau - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> t = M.tensor((Integer(2),Integer(1))) ; t Type-(2,1) tensor on the Rank-3 free module M over the Integer Ring >>> t.set_name('t') ; t Type-(2,1) tensor t on the Rank-3 free module M over the Integer Ring >>> latex(t) t >>> t.set_name(latex_name=r'\tau') ; t Type-(2,1) tensor t on the Rank-3 free module M over the Integer Ring >>> latex(t) \tau 
 - symmetries()[source]¶
- Print the list of symmetries and antisymmetries of - self.- EXAMPLES: - Various symmetries / antisymmetries for a rank-4 tensor: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: t = M.tensor((4,0), name='T') # no symmetry declared sage: t.symmetries() no symmetry; no antisymmetry sage: t = M.tensor((4,0), name='T', sym=(0,1)) sage: t.symmetries() symmetry: (0, 1); no antisymmetry sage: t = M.tensor((4,0), name='T', sym=[(0,1), (2,3)]) sage: t.symmetries() symmetries: [(0, 1), (2, 3)]; no antisymmetry sage: t = M.tensor((4,0), name='T', sym=(0,1), antisym=(2,3)) sage: t.symmetries() symmetry: (0, 1); antisymmetry: (2, 3) - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> t = M.tensor((Integer(4),Integer(0)), name='T') # no symmetry declared >>> t.symmetries() no symmetry; no antisymmetry >>> t = M.tensor((Integer(4),Integer(0)), name='T', sym=(Integer(0),Integer(1))) >>> t.symmetries() symmetry: (0, 1); no antisymmetry >>> t = M.tensor((Integer(4),Integer(0)), name='T', sym=[(Integer(0),Integer(1)), (Integer(2),Integer(3))]) >>> t.symmetries() symmetries: [(0, 1), (2, 3)]; no antisymmetry >>> t = M.tensor((Integer(4),Integer(0)), name='T', sym=(Integer(0),Integer(1)), antisym=(Integer(2),Integer(3))) >>> t.symmetries() symmetry: (0, 1); antisymmetry: (2, 3) 
 - symmetrize(*pos, **kwargs)[source]¶
- Symmetrization over some arguments. - INPUT: - pos– list of argument positions involved in the symmetrization (with the convention- position=0for the first argument); if none, the symmetrization is performed over all the arguments
- basis– (default:- None) module basis with respect to which the component computation is to be performed; if none, the module’s default basis is used if the tensor field has already components in it; otherwise another basis w.r.t. which the tensor has components will be picked
 - OUTPUT: - the symmetrized tensor (instance of - FreeModuleTensor)
 - EXAMPLES: - Symmetrization of a tensor of type \((2,0)\): - sage: M = FiniteRankFreeModule(QQ, 3, name='M') sage: e = M.basis('e') sage: t = M.tensor((2,0)) sage: t[:] = [[2,1,-3],[0,-4,5],[-1,4,2]] sage: s = t.symmetrize() ; s Type-(2,0) tensor on the 3-dimensional vector space M over the Rational Field sage: t[:], s[:] ( [ 2 1 -3] [ 2 1/2 -2] [ 0 -4 5] [1/2 -4 9/2] [-1 4 2], [ -2 9/2 2] ) sage: s.symmetries() symmetry: (0, 1); no antisymmetry sage: all(s[i,j] == 1/2*(t[i,j]+t[j,i]) # check: ....: for i in range(3) for j in range(3)) True - >>> from sage.all import * >>> M = FiniteRankFreeModule(QQ, Integer(3), name='M') >>> e = M.basis('e') >>> t = M.tensor((Integer(2),Integer(0))) >>> t[:] = [[Integer(2),Integer(1),-Integer(3)],[Integer(0),-Integer(4),Integer(5)],[-Integer(1),Integer(4),Integer(2)]] >>> s = t.symmetrize() ; s Type-(2,0) tensor on the 3-dimensional vector space M over the Rational Field >>> t[:], s[:] ( [ 2 1 -3] [ 2 1/2 -2] [ 0 -4 5] [1/2 -4 9/2] [-1 4 2], [ -2 9/2 2] ) >>> s.symmetries() symmetry: (0, 1); no antisymmetry >>> all(s[i,j] == Integer(1)/Integer(2)*(t[i,j]+t[j,i]) # check: ... for i in range(Integer(3)) for j in range(Integer(3))) True - Instead of invoking the method - symmetrize(), one may use the index notation with parentheses to denote the symmetrization; it suffices to pass the indices as a string inside square brackets:- sage: t['(ij)'] Type-(2,0) tensor on the 3-dimensional vector space M over the Rational Field sage: t['(ij)'].symmetries() symmetry: (0, 1); no antisymmetry sage: t['(ij)'] == t.symmetrize() True - >>> from sage.all import * >>> t['(ij)'] Type-(2,0) tensor on the 3-dimensional vector space M over the Rational Field >>> t['(ij)'].symmetries() symmetry: (0, 1); no antisymmetry >>> t['(ij)'] == t.symmetrize() True - The indices names are not significant; they can even be replaced by dots: - sage: t['(..)'] == t.symmetrize() True - >>> from sage.all import * >>> t['(..)'] == t.symmetrize() True - The LaTeX notation can be used as well: - sage: t['^{(ij)}'] == t.symmetrize() True - >>> from sage.all import * >>> t['^{(ij)}'] == t.symmetrize() True - Symmetrization of a tensor of type \((0,3)\) on the first two arguments: - sage: t = M.tensor((0,3)) sage: t[:] = [[[1,2,3], [-4,5,6], [7,8,-9]], ....: [[10,-11,12], [13,14,-15], [16,17,18]], ....: [[19,-20,-21], [-22,23,24], [25,26,-27]]] sage: s = t.symmetrize(0,1) ; s # (0,1) = the first two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() symmetry: (0, 1); no antisymmetry sage: s[:] [[[1, 2, 3], [3, -3, 9], [13, -6, -15]], [[3, -3, 9], [13, 14, -15], [-3, 20, 21]], [[13, -6, -15], [-3, 20, 21], [25, 26, -27]]] sage: all(s[i,j,k] == 1/2*(t[i,j,k]+t[j,i,k]) # Check: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.symmetrize(0,1) == s # another test True - >>> from sage.all import * >>> t = M.tensor((Integer(0),Integer(3))) >>> t[:] = [[[Integer(1),Integer(2),Integer(3)], [-Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),-Integer(9)]], ... [[Integer(10),-Integer(11),Integer(12)], [Integer(13),Integer(14),-Integer(15)], [Integer(16),Integer(17),Integer(18)]], ... [[Integer(19),-Integer(20),-Integer(21)], [-Integer(22),Integer(23),Integer(24)], [Integer(25),Integer(26),-Integer(27)]]] >>> s = t.symmetrize(Integer(0),Integer(1)) ; s # (0,1) = the first two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() symmetry: (0, 1); no antisymmetry >>> s[:] [[[1, 2, 3], [3, -3, 9], [13, -6, -15]], [[3, -3, 9], [13, 14, -15], [-3, 20, 21]], [[13, -6, -15], [-3, 20, 21], [25, 26, -27]]] >>> all(s[i,j,k] == Integer(1)/Integer(2)*(t[i,j,k]+t[j,i,k]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.symmetrize(Integer(0),Integer(1)) == s # another test True - Again the index notation can be used: - sage: t['_(ij)k'] == t.symmetrize(0,1) True sage: t['_(..).'] == t.symmetrize(0,1) # no index name True sage: t['_{(ij)k}'] == t.symmetrize(0,1) # LaTeX notation True sage: t['_{(..).}'] == t.symmetrize(0,1) # this also allowed True - >>> from sage.all import * >>> t['_(ij)k'] == t.symmetrize(Integer(0),Integer(1)) True >>> t['_(..).'] == t.symmetrize(Integer(0),Integer(1)) # no index name True >>> t['_{(ij)k}'] == t.symmetrize(Integer(0),Integer(1)) # LaTeX notation True >>> t['_{(..).}'] == t.symmetrize(Integer(0),Integer(1)) # this also allowed True - Symmetrization of a tensor of type \((0,3)\) on the first and last arguments: - sage: s = t.symmetrize(0,2) ; s # (0,2) = first and last arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() symmetry: (0, 2); no antisymmetry sage: s[:] [[[1, 6, 11], [-4, 9, -8], [7, 12, 8]], [[6, -11, -4], [9, 14, 4], [12, 17, 22]], [[11, -4, -21], [-8, 4, 24], [8, 22, -27]]] sage: all(s[i,j,k] == 1/2*(t[i,j,k]+t[k,j,i]) ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.symmetrize(0,2) == s # another test True - >>> from sage.all import * >>> s = t.symmetrize(Integer(0),Integer(2)) ; s # (0,2) = first and last arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() symmetry: (0, 2); no antisymmetry >>> s[:] [[[1, 6, 11], [-4, 9, -8], [7, 12, 8]], [[6, -11, -4], [9, 14, 4], [12, 17, 22]], [[11, -4, -21], [-8, 4, 24], [8, 22, -27]]] >>> all(s[i,j,k] == Integer(1)/Integer(2)*(t[i,j,k]+t[k,j,i]) ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.symmetrize(Integer(0),Integer(2)) == s # another test True - Symmetrization of a tensor of type \((0,3)\) on the last two arguments: - sage: s = t.symmetrize(1,2) ; s # (1,2) = the last two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() symmetry: (1, 2); no antisymmetry sage: s[:] [[[1, -1, 5], [-1, 5, 7], [5, 7, -9]], [[10, 1, 14], [1, 14, 1], [14, 1, 18]], [[19, -21, 2], [-21, 23, 25], [2, 25, -27]]] sage: all(s[i,j,k] == 1/2*(t[i,j,k]+t[i,k,j]) # Check: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.symmetrize(1,2) == s # another test True - >>> from sage.all import * >>> s = t.symmetrize(Integer(1),Integer(2)) ; s # (1,2) = the last two arguments Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() symmetry: (1, 2); no antisymmetry >>> s[:] [[[1, -1, 5], [-1, 5, 7], [5, 7, -9]], [[10, 1, 14], [1, 14, 1], [14, 1, 18]], [[19, -21, 2], [-21, 23, 25], [2, 25, -27]]] >>> all(s[i,j,k] == Integer(1)/Integer(2)*(t[i,j,k]+t[i,k,j]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.symmetrize(Integer(1),Integer(2)) == s # another test True - Use of the index notation: - sage: t['_i(jk)'] == t.symmetrize(1,2) True sage: t['_.(..)'] == t.symmetrize(1,2) True sage: t['_{i(jk)}'] == t.symmetrize(1,2) # LaTeX notation True - >>> from sage.all import * >>> t['_i(jk)'] == t.symmetrize(Integer(1),Integer(2)) True >>> t['_.(..)'] == t.symmetrize(Integer(1),Integer(2)) True >>> t['_{i(jk)}'] == t.symmetrize(Integer(1),Integer(2)) # LaTeX notation True - Full symmetrization of a tensor of type \((0,3)\): - sage: s = t.symmetrize() ; s Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field sage: s.symmetries() symmetry: (0, 1, 2); no antisymmetry sage: s[:] [[[1, 8/3, 29/3], [8/3, 7/3, 0], [29/3, 0, -5/3]], [[8/3, 7/3, 0], [7/3, 14, 25/3], [0, 25/3, 68/3]], [[29/3, 0, -5/3], [0, 25/3, 68/3], [-5/3, 68/3, -27]]] sage: all(s[i,j,k] == 1/6*(t[i,j,k]+t[i,k,j]+t[j,k,i]+t[j,i,k]+t[k,i,j]+t[k,j,i]) # Check: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s.symmetrize() == s # another test True - >>> from sage.all import * >>> s = t.symmetrize() ; s Type-(0,3) tensor on the 3-dimensional vector space M over the Rational Field >>> s.symmetries() symmetry: (0, 1, 2); no antisymmetry >>> s[:] [[[1, 8/3, 29/3], [8/3, 7/3, 0], [29/3, 0, -5/3]], [[8/3, 7/3, 0], [7/3, 14, 25/3], [0, 25/3, 68/3]], [[29/3, 0, -5/3], [0, 25/3, 68/3], [-5/3, 68/3, -27]]] >>> all(s[i,j,k] == Integer(1)/Integer(6)*(t[i,j,k]+t[i,k,j]+t[j,k,i]+t[j,i,k]+t[k,i,j]+t[k,j,i]) # Check: ... for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3))) True >>> s.symmetrize() == s # another test True - Index notation for the full symmetrization: - sage: t['_(ijk)'] == t.symmetrize() True sage: t['_{(ijk)}'] == t.symmetrize() # LaTeX notation True - >>> from sage.all import * >>> t['_(ijk)'] == t.symmetrize() True >>> t['_{(ijk)}'] == t.symmetrize() # LaTeX notation True - Symmetrization can be performed only on arguments on the same type: - sage: t = M.tensor((1,2)) sage: t[:] = [[[1,2,3], [-4,5,6], [7,8,-9]], ....: [[10,-11,12], [13,14,-15], [16,17,18]], ....: [[19,-20,-21], [-22,23,24], [25,26,-27]]] sage: s = t.symmetrize(0,1) Traceback (most recent call last): ... TypeError: 0 is a contravariant position, while 1 is a covariant position; symmetrization is meaningful only on tensor arguments of the same type sage: s = t.symmetrize(1,2) # OK: both 1 and 2 are covariant positions - >>> from sage.all import * >>> t = M.tensor((Integer(1),Integer(2))) >>> t[:] = [[[Integer(1),Integer(2),Integer(3)], [-Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),-Integer(9)]], ... [[Integer(10),-Integer(11),Integer(12)], [Integer(13),Integer(14),-Integer(15)], [Integer(16),Integer(17),Integer(18)]], ... [[Integer(19),-Integer(20),-Integer(21)], [-Integer(22),Integer(23),Integer(24)], [Integer(25),Integer(26),-Integer(27)]]] >>> s = t.symmetrize(Integer(0),Integer(1)) Traceback (most recent call last): ... TypeError: 0 is a contravariant position, while 1 is a covariant position; symmetrization is meaningful only on tensor arguments of the same type >>> s = t.symmetrize(Integer(1),Integer(2)) # OK: both 1 and 2 are covariant positions - The order of positions does not matter: - sage: t.symmetrize(2,1) == t.symmetrize(1,2) True - >>> from sage.all import * >>> t.symmetrize(Integer(2),Integer(1)) == t.symmetrize(Integer(1),Integer(2)) True - Use of the index notation: - sage: t['^i_(jk)'] == t.symmetrize(1,2) True sage: t['^._(..)'] == t.symmetrize(1,2) True - >>> from sage.all import * >>> t['^i_(jk)'] == t.symmetrize(Integer(1),Integer(2)) True >>> t['^._(..)'] == t.symmetrize(Integer(1),Integer(2)) True - The character - ^can be skipped, the character- _being sufficient to separate contravariant indices from covariant ones:- sage: t['i_(jk)'] == t.symmetrize(1,2) True - >>> from sage.all import * >>> t['i_(jk)'] == t.symmetrize(Integer(1),Integer(2)) True - The LaTeX notation can be employed: - sage: t['^{i}_{(jk)}'] == t.symmetrize(1,2) True - >>> from sage.all import * >>> t['^{i}_{(jk)}'] == t.symmetrize(Integer(1),Integer(2)) True 
 - tensor_rank()[source]¶
- Return the tensor rank of - self.- OUTPUT: - integer - k+l, where- kis the contravariant rank and- lis the covariant rank
 - EXAMPLES: - sage: M = FiniteRankFreeModule(ZZ, 3) sage: M.an_element().tensor_rank() 1 sage: t = M.tensor((2,1)) sage: t.tensor_rank() 3 - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3)) >>> M.an_element().tensor_rank() 1 >>> t = M.tensor((Integer(2),Integer(1))) >>> t.tensor_rank() 3 
 - tensor_type()[source]¶
- Return the tensor type of - self.- OUTPUT: - pair - (k, l), where- kis the contravariant rank and- lis the covariant rank
 - EXAMPLES: - sage: M = FiniteRankFreeModule(ZZ, 3) sage: M.an_element().tensor_type() (1, 0) sage: t = M.tensor((2,1)) sage: t.tensor_type() (2, 1) - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3)) >>> M.an_element().tensor_type() (1, 0) >>> t = M.tensor((Integer(2),Integer(1))) >>> t.tensor_type() (2, 1) 
 - trace(pos1=0, pos2=1, using=None)[source]¶
- Trace (contraction) on two slots of the tensor. - If a non-degenerate form is provided, the trace of a type-\((0,2)\) tensor is computed by first raising the last index. - INPUT: - pos1– (default: 0) position of the first index for the contraction, with the convention- pos1=0for the first slot
- pos2– (default: 1) position of the second index for the contraction, with the same convention as for- pos1; the variance type of- pos2must be opposite to that of- pos1
- using– (default:- None) a non-degenerate form
 - OUTPUT: - tensor or scalar resulting from the - (pos1, pos2)contraction
 - EXAMPLES: - Trace of a type-\((1,1)\) tensor: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') ; e Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: a = M.tensor((1,1), name='a') ; a Type-(1,1) tensor a on the Rank-3 free module M over the Integer Ring sage: a[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: a.trace() 15 sage: a.trace(0,1) # equivalent to above (contraction of slot 0 with slot 1) 15 sage: a.trace(1,0) # the order of the slots does not matter 15 - >>> from sage.all import * >>> M = FiniteRankFreeModule(ZZ, Integer(3), name='M') >>> e = M.basis('e') ; e Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring >>> a = M.tensor((Integer(1),Integer(1)), name='a') ; a Type-(1,1) tensor a on the Rank-3 free module M over the Integer Ring >>> a[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]] >>> a.trace() 15 >>> a.trace(Integer(0),Integer(1)) # equivalent to above (contraction of slot 0 with slot 1) 15 >>> a.trace(Integer(1),Integer(0)) # the order of the slots does not matter 15 - Instead of the explicit call to the method - trace(), one may use the index notation with Einstein convention (summation over repeated indices); it suffices to pass the indices as a string inside square brackets:- sage: a['^i_i'] 15 - >>> from sage.all import * >>> a['^i_i'] 15 - The letter ‘i’ to denote the repeated index can be replaced by any other letter: - sage: a['^s_s'] 15 - >>> from sage.all import * >>> a['^s_s'] 15 - Moreover, the symbol - ^can be omitted:- sage: a['i_i'] 15 - >>> from sage.all import * >>> a['i_i'] 15 - The contraction on two slots having the same tensor type cannot occur: - sage: b = M.tensor((2,0), name='b') ; b Type-(2,0) tensor b on the Rank-3 free module M over the Integer Ring sage: b[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: b.trace(0,1) Traceback (most recent call last): ... IndexError: contraction on two contravariant indices is not allowed - >>> from sage.all import * >>> b = M.tensor((Integer(2),Integer(0)), name='b') ; b Type-(2,0) tensor b on the Rank-3 free module M over the Integer Ring >>> b[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]] >>> b.trace(Integer(0),Integer(1)) Traceback (most recent call last): ... IndexError: contraction on two contravariant indices is not allowed - The contraction either preserves or destroys the symmetries: - sage: b = M.alternating_form(2, 'b') ; b Alternating form b of degree 2 on the Rank-3 free module M over the Integer Ring sage: b[0,1], b[0,2], b[1,2] = 3, 2, 1 sage: t = a*b ; t Type-(1,3) tensor a⊗b on the Rank-3 free module M over the Integer Ring - >>> from sage.all import * >>> b = M.alternating_form(Integer(2), 'b') ; b Alternating form b of degree 2 on the Rank-3 free module M over the Integer Ring >>> b[Integer(0),Integer(1)], b[Integer(0),Integer(2)], b[Integer(1),Integer(2)] = Integer(3), Integer(2), Integer(1) >>> t = a*b ; t Type-(1,3) tensor a⊗b on the Rank-3 free module M over the Integer Ring - By construction, - tis a tensor field antisymmetric w.r.t. its last two slots:- sage: t.symmetries() no symmetry; antisymmetry: (2, 3) sage: s = t.trace(0,1) ; s # contraction on the first two slots Alternating form of degree 2 on the Rank-3 free module M over the Integer Ring sage: s.symmetries() # the antisymmetry is preserved no symmetry; antisymmetry: (0, 1) sage: s[:] [ 0 45 30] [-45 0 15] [-30 -15 0] sage: s == 15*b # check True sage: s = t.trace(0,2) ; s # contraction on the first and third slots Type-(0,2) tensor on the Rank-3 free module M over the Integer Ring sage: s.symmetries() # the antisymmetry has been destroyed by the above contraction: no symmetry; no antisymmetry sage: s[:] # indeed: [-26 -4 6] [-31 -2 9] [-36 0 12] sage: s[:] == matrix( [[sum(t[k,i,k,j] for k in M.irange()) ....: for j in M.irange()] for i in M.irange()] ) # check True - >>> from sage.all import * >>> t.symmetries() no symmetry; antisymmetry: (2, 3) >>> s = t.trace(Integer(0),Integer(1)) ; s # contraction on the first two slots Alternating form of degree 2 on the Rank-3 free module M over the Integer Ring >>> s.symmetries() # the antisymmetry is preserved no symmetry; antisymmetry: (0, 1) >>> s[:] [ 0 45 30] [-45 0 15] [-30 -15 0] >>> s == Integer(15)*b # check True >>> s = t.trace(Integer(0),Integer(2)) ; s # contraction on the first and third slots Type-(0,2) tensor on the Rank-3 free module M over the Integer Ring >>> s.symmetries() # the antisymmetry has been destroyed by the above contraction: no symmetry; no antisymmetry >>> s[:] # indeed: [-26 -4 6] [-31 -2 9] [-36 0 12] >>> s[:] == matrix( [[sum(t[k,i,k,j] for k in M.irange()) ... for j in M.irange()] for i in M.irange()] ) # check True - Use of index notation instead of - trace():- sage: t['^k_kij'] == t.trace(0,1) True sage: t['^k_{kij}'] == t.trace(0,1) # LaTeX notation True sage: t['^k_ikj'] == t.trace(0,2) True sage: t['^k_ijk'] == t.trace(0,3) True - >>> from sage.all import * >>> t['^k_kij'] == t.trace(Integer(0),Integer(1)) True >>> t['^k_{kij}'] == t.trace(Integer(0),Integer(1)) # LaTeX notation True >>> t['^k_ikj'] == t.trace(Integer(0),Integer(2)) True >>> t['^k_ijk'] == t.trace(Integer(0),Integer(3)) True - Index symbols not involved in the contraction may be replaced by dots: - sage: t['^k_k..'] == t.trace(0,1) True sage: t['^k_.k.'] == t.trace(0,2) True sage: t['^k_..k'] == t.trace(0,3) True - >>> from sage.all import * >>> t['^k_k..'] == t.trace(Integer(0),Integer(1)) True >>> t['^k_.k.'] == t.trace(Integer(0),Integer(2)) True >>> t['^k_..k'] == t.trace(Integer(0),Integer(3)) True