Components as indexed sets of ring elements¶
The class Components
is a technical class to take in charge the
storage and manipulation of indexed elements of a commutative ring that
represent the components of some “mathematical entity” with respect to some
“frame”. Examples of entity/frame are vector/vectorspace basis or
vector field/vector frame on some manifold. More generally, the components
can be those of a tensor on a free module or those of a tensor field on a
manifold. They can also be nontensorial quantities, like connection
coefficients or structure coefficients of a vector frame.
The individual components are assumed to belong to a given commutative ring and are labelled by indices, which are tuples of integers. The following operations are implemented on components with respect to a given frame:
 arithmetics (addition, subtraction, multiplication by a ring element)
 handling of symmetries or antisymmetries on the indices
 symmetrization and antisymmetrization
 tensor product
 contraction
Various subclasses of class Components
are
CompWithSym
for components with symmetries or antisymmetries w.r.t. index permutationsCompFullySym
for fully symmetric components w.r.t. index permutationsKroneckerDelta
for the Kronecker delta symbol
CompFullyAntiSym
for fully antisymmetric components w.r.t. index permutations
AUTHORS:
 Eric Gourgoulhon, Michal Bejger (20142015): initial version
 Joris Vankerschaver (2010): for the idea of storing only the nonzero
components as dictionaries, whose keys are the component indices (see
class
DifferentialForm
)  Marco Mancini (2015) : parallelization of some computations
EXAMPLES:
Set of components with 2 indices on a 3dimensional vector space, the frame being some basis of the vector space:
sage: from sage.tensor.modules.comp import Components
sage: V = VectorSpace(QQ,3)
sage: basis = V.basis() ; basis
[
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
sage: c = Components(QQ, basis, 2) ; c
2indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
Actually, the frame can be any object that has some length, i.e. on which
the function len()
can be called:
sage: basis1 = V.gens() ; basis1
((1, 0, 0), (0, 1, 0), (0, 0, 1))
sage: c1 = Components(QQ, basis1, 2) ; c1
2indices components w.r.t. ((1, 0, 0), (0, 1, 0), (0, 0, 1))
sage: basis2 = ['a', 'b' , 'c']
sage: c2 = Components(QQ, basis2, 2) ; c2
2indices components w.r.t. ['a', 'b', 'c']
A just created set of components is initialized to zero:
sage: c.is_zero()
True
sage: c == 0
True
This can also be checked on the list of components, which is returned by
the operator [:]
:
sage: c[:]
[0 0 0]
[0 0 0]
[0 0 0]
Individual components are accessed by providing their indices inside square brackets:
sage: c[1,2] = 3
sage: c[:]
[ 0 0 0]
[ 0 0 3]
[ 0 0 0]
sage: v = Components(QQ, basis, 1)
sage: v[:]
[0, 0, 0]
sage: v[0]
0
sage: v[:] = (1,3,2)
sage: v[:]
[1, 3, 2]
sage: v[0]
1
Sets of components with 2 indices can be converted into a matrix:
sage: matrix(c)
[ 0 0 0]
[ 0 0 3]
[ 0 0 0]
sage: matrix(c).parent()
Full MatrixSpace of 3 by 3 dense matrices over Rational Field
By default, the indices range from \(0\) to \(n1\), where \(n\) is the length
of the frame. This can be changed via the argument start_index
in
the Components
constructor:
sage: v1 = Components(QQ, basis, 1, start_index=1)
sage: v1[:]
[0, 0, 0]
sage: v1[0]
Traceback (most recent call last):
...
IndexError: index out of range: 0 not in [1, 3]
sage: v1[1]
0
sage: v1[:] = v[:] # list copy of all components
sage: v1[:]
[1, 3, 2]
sage: v1[1], v1[2], v1[3]
(1, 3, 2)
sage: v[0], v[1], v[2]
(1, 3, 2)
If some formatter function or unbound method is provided via the argument
output_formatter
in the Components
constructor, it is used to
change the ouput of the access operator [...]
:
sage: a = Components(QQ, basis, 2, output_formatter=Rational.numerical_approx)
sage: a[1,2] = 1/3
sage: a[1,2]
0.333333333333333
The format can be passed to the formatter as the last argument of the
access operator [...]
:
sage: a[1,2,10] # here the format is 10, for 10 bits of precision
0.33
sage: a[1,2,100]
0.33333333333333333333333333333
The raw (unformatted) components are then accessed by the double bracket operator:
sage: a[[1,2]]
1/3
For sets of components declared without any output formatter, there is no
difference between [...]
and [[...]]
:
sage: c[1,2] = 1/3
sage: c[1,2], c[[1,2]]
(1/3, 1/3)
The formatter is also used for the complete list of components:
sage: a[:]
[0.000000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000 0.000000000000000 0.333333333333333]
[0.000000000000000 0.000000000000000 0.000000000000000]
sage: a[:,10] # with a format different from the default one (53 bits)
[0.00 0.00 0.00]
[0.00 0.00 0.33]
[0.00 0.00 0.00]
The complete list of components in raw form can be recovered by the double
bracket operator, replacing :
by slice(None)
(since a[[:]]
generates a Python syntax error):
sage: a[[slice(None)]]
[ 0 0 0]
[ 0 0 1/3]
[ 0 0 0]
Another example of formatter: the Python builtin function str()
to generate string outputs:
sage: b = Components(QQ, V.basis(), 1, output_formatter=str)
sage: b[:] = (1, 0, 4)
sage: b[:]
['1', '0', '4']
For such a formatter, 2indices components are no longer displayed as a matrix:
sage: b = Components(QQ, basis, 2, output_formatter=str)
sage: b[0,1] = 1/3
sage: b[:]
[['0', '1/3', '0'], ['0', '0', '0'], ['0', '0', '0']]
But unformatted outputs still are:
sage: b[[slice(None)]]
[ 0 1/3 0]
[ 0 0 0]
[ 0 0 0]
Internally, the components are stored as a dictionary (_comp
) whose
keys are the indices; only the nonzero components are stored:
sage: a[:]
[0.000000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000 0.000000000000000 0.333333333333333]
[0.000000000000000 0.000000000000000 0.000000000000000]
sage: a._comp
{(1, 2): 1/3}
sage: v[:] = (1, 0, 3)
sage: v._comp # random output order of the component dictionary
{(0,): 1, (2,): 3}
In case of symmetries, only nonredundant components are stored:
sage: from sage.tensor.modules.comp import CompFullyAntiSym
sage: c = CompFullyAntiSym(QQ, basis, 2)
sage: c[0,1] = 3
sage: c[:]
[ 0 3 0]
[3 0 0]
[ 0 0 0]
sage: c._comp
{(0, 1): 3}

class
sage.tensor.modules.comp.
CompFullyAntiSym
(ring, frame, nb_indices, start_index=0, output_formatter=None)¶ Bases:
sage.tensor.modules.comp.CompWithSym
Indexed set of ring elements forming some components with respect to a given “frame” that are fully antisymmetric with respect to any permutation of the indices.
The “frame” can be a basis of some vector space or a vector frame on some manifold (i.e. a field of bases). The stored quantities can be tensor components or nontensorial quantities.
INPUT:
ring
– commutative ring in which each component takes its valueframe
– frame with respect to which the components are defined; whatever typeframe
is, it should have some method__len__()
implemented, so thatlen(frame)
returns the dimension, i.e. the size of a single index rangenb_indices
– number of indices labeling the componentsstart_index
– (default: 0) first value of a single index; accordingly a component index i must obeystart_index <= i <= start_index + dim  1
, wheredim = len(frame)
.output_formatter
– (default:None
) function or unbound method called to format the output of the component access operator[...]
(method __getitem__);output_formatter
must take 1 or 2 arguments: the 1st argument must be an instance ofring
and the second one, if any, some format specification.
EXAMPLES:
Antisymmetric components with 2 indices on a 3dimensional space:
sage: from sage.tensor.modules.comp import CompWithSym, CompFullyAntiSym sage: V = VectorSpace(QQ, 3) sage: c = CompFullyAntiSym(QQ, V.basis(), 2) sage: c[0,1], c[0,2], c[1,2] = 3, 1/2, 1 sage: c[:] # note that all components have been set according to the antisymmetry [ 0 3 1/2] [ 3 0 1] [1/2 1 0]
Internally, only nonredundant and nonzero components are stored:
sage: c._comp # random output order of the component dictionary {(0, 1): 3, (0, 2): 1/2, (1, 2): 1}
Same thing, but with the starting index set to 1:
sage: c1 = CompFullyAntiSym(QQ, V.basis(), 2, start_index=1) sage: c1[1,2], c1[1,3], c1[2,3] = 3, 1/2, 1 sage: c1[:] [ 0 3 1/2] [ 3 0 1] [1/2 1 0]
The values stored in
c
andc1
are equal:sage: c1[:] == c[:] True
but not
c
andc1
, since their starting indices differ:sage: c1 == c False
Fully antisymmetric components with 3 indices on a 3dimensional space:
sage: a = CompFullyAntiSym(QQ, V.basis(), 3) sage: a[0,1,2] = 3 # the only independent component in dimension 3 sage: a[:] [[[0, 0, 0], [0, 0, 3], [0, 3, 0]], [[0, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]]
Setting a nonzero value incompatible with the antisymmetry results in an error:
sage: a[0,1,0] = 4 Traceback (most recent call last): ... ValueError: by antisymmetry, the component cannot have a nonzero value for the indices (0, 1, 0) sage: a[0,1,0] = 0 # OK sage: a[2,0,1] = 3 # OK
The full antisymmetry is preserved by the arithmetics:
sage: b = CompFullyAntiSym(QQ, V.basis(), 3) sage: b[0,1,2] = 4 sage: s = a + 2*b ; s Fully antisymmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: a[:], b[:], s[:] ([[[0, 0, 0], [0, 0, 3], [0, 3, 0]], [[0, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 4], [0, 4, 0]], [[0, 0, 4], [0, 0, 0], [4, 0, 0]], [[0, 4, 0], [4, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 5], [0, 5, 0]], [[0, 0, 5], [0, 0, 0], [5, 0, 0]], [[0, 5, 0], [5, 0, 0], [0, 0, 0]]])
It is lost if the added object is not fully antisymmetric:
sage: b1 = CompWithSym(QQ, V.basis(), 3, antisym=(0,1)) # b1 has only antisymmetry on index positions (0,1) sage: b1[0,1,2] = 4 sage: s = a + 2*b1 ; s # the result has the same symmetry as b1: 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: a[:], b1[:], s[:] ([[[0, 0, 0], [0, 0, 3], [0, 3, 0]], [[0, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 4], [0, 0, 0]], [[0, 0, 4], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 5], [0, 3, 0]], [[0, 0, 5], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]]) sage: s = 2*b1 + a ; s 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: 2*b1 + a == a + 2*b1 True

interior_product
(other)¶ Interior product with another set of fully antisymmetric components.
The interior product amounts to a contraction over all the \(p\) indices of
self
with the first \(p\) indices ofother
, assuming that the number \(q\) of indices ofother
obeys \(q\geq p\).Note
self.interior_product(other)
yields the same result asself.contract(0,..., p1, other, 0,..., p1)
(cf.contract()
), butinterior_product
is more efficient, the antisymmetry ofself
being not used to reduce the computation incontract()
.INPUT:
other
– fully antisymmetric components defined on the same frame asself
and with a number of indices at least equal to that ofself
OUTPUT:
 base ring element (case \(p=q\)) or set of components (case \(p<q\))
resulting from the contraction over all the \(p\) indices of
self
with the first \(p\) indices ofother
EXAMPLES:
Interior product of a set of components
a
withp
indices with a set of componentsb
withq
indices on a 4dimensional vector space.Case
p=2
andq=2
:sage: from sage.tensor.modules.comp import CompFullyAntiSym sage: V = VectorSpace(QQ, 4) sage: a = CompFullyAntiSym(QQ, V.basis(), 2) sage: a[0,1], a[0,2], a[0,3] = 2, 4, 3 sage: a[1,2], a[1,3], a[2,3] = 5, 3, 1 sage: b = CompFullyAntiSym(QQ, V.basis(), 2) sage: b[0,1], b[0,2], b[0,3] = 3, 4, 2 sage: b[1,2], b[1,3], b[2,3] = 2, 5, 1 sage: c = a.interior_product(b) sage: c 40 sage: c == a.contract(0, 1, b, 0, 1) True
Case
p=2
andq=3
:sage: b = CompFullyAntiSym(QQ, V.basis(), 3) sage: b[0,1,2], b[0,1,3], b[0,2,3], b[1,2,3] = 3, 4, 2, 5 sage: c = a.interior_product(b) sage: c[:] [58, 10, 6, 82] sage: c == a.contract(0, 1, b, 0, 1) True
Case
p=2
andq=4
:sage: b = CompFullyAntiSym(QQ, V.basis(), 4) sage: b[0,1,2,3] = 5 sage: c = a.interior_product(b) sage: c[:] [ 0 10 30 50] [10 0 30 40] [30 30 0 20] [50 40 20 0] sage: c == a.contract(0, 1, b, 0, 1) True
Case
p=3
andq=3
:sage: a = CompFullyAntiSym(QQ, V.basis(), 3) sage: a[0,1,2], a[0,1,3], a[0,2,3], a[1,2,3] = 2, 1, 3, 5 sage: b = CompFullyAntiSym(QQ, V.basis(), 3) sage: b[0,1,2], b[0,1,3], b[0,2,3], b[1,2,3] = 2, 1, 4, 2 sage: c = a.interior_product(b) sage: c 102 sage: c == a.contract(0, 1, 2, b, 0, 1, 2) True
Case
p=3
andq=4
:sage: b = CompFullyAntiSym(QQ, V.basis(), 4) sage: b[0,1,2,3] = 5 sage: c = a.interior_product(b) sage: c[:] [150, 90, 30, 60] sage: c == a.contract(0, 1, 2, b, 0, 1, 2) True
Case
p=4
andq=4
:sage: a = CompFullyAntiSym(QQ, V.basis(), 4) sage: a[0,1,2,3] = 3 sage: c = a.interior_product(b) sage: c 360 sage: c == a.contract(0, 1, 2, 3, b, 0, 1, 2, 3) True

class
sage.tensor.modules.comp.
CompFullySym
(ring, frame, nb_indices, start_index=0, output_formatter=None)¶ Bases:
sage.tensor.modules.comp.CompWithSym
Indexed set of ring elements forming some components with respect to a given “frame” that are fully symmetric with respect to any permutation of the indices.
The “frame” can be a basis of some vector space or a vector frame on some manifold (i.e. a field of bases). The stored quantities can be tensor components or nontensorial quantities.
INPUT:
ring
– commutative ring in which each component takes its valueframe
– frame with respect to which the components are defined; whatever typeframe
is, it should have some method__len__()
implemented, so thatlen(frame)
returns the dimension, i.e. the size of a single index rangenb_indices
– number of indices labeling the componentsstart_index
– (default: 0) first value of a single index; accordingly a component index i must obeystart_index <= i <= start_index + dim  1
, wheredim = len(frame)
.output_formatter
– (default:None
) function or unbound method called to format the output of the component access operator[...]
(method __getitem__);output_formatter
must take 1 or 2 arguments: the 1st argument must be an instance ofring
and the second one, if any, some format specification.
EXAMPLES:
Symmetric components with 2 indices on a 3dimensional space:
sage: from sage.tensor.modules.comp import CompFullySym, CompWithSym sage: V = VectorSpace(QQ, 3) sage: c = CompFullySym(QQ, V.basis(), 2) sage: c[0,0], c[0,1], c[1,2] = 1, 2, 3 sage: c[:] # note that c[1,0] and c[2,1] have been updated automatically (by symmetry) [ 1 2 0] [2 0 3] [ 0 3 0]
Internally, only nonredundant and nonzero components are stored:
sage: c._comp # random output order of the component dictionary {(0, 0): 1, (0, 1): 2, (1, 2): 3}
Same thing, but with the starting index set to 1:
sage: c1 = CompFullySym(QQ, V.basis(), 2, start_index=1) sage: c1[1,1], c1[1,2], c1[2,3] = 1, 2, 3 sage: c1[:] [ 1 2 0] [2 0 3] [ 0 3 0]
The values stored in
c
andc1
are equal:sage: c1[:] == c[:] True
but not
c
andc1
, since their starting indices differ:sage: c1 == c False
Fully symmetric components with 3 indices on a 3dimensional space:
sage: a = CompFullySym(QQ, V.basis(), 3) sage: a[0,1,2] = 3 sage: a[:] [[[0, 0, 0], [0, 0, 3], [0, 3, 0]], [[0, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]] sage: a[0,1,0] = 4 sage: a[:] [[[0, 4, 0], [4, 0, 3], [0, 3, 0]], [[4, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]]
The full symmetry is preserved by the arithmetics:
sage: b = CompFullySym(QQ, V.basis(), 3) sage: b[0,0,0], b[0,1,0], b[1,0,2], b[1,2,2] = 2, 3, 1, 5 sage: s = a + 2*b ; s Fully symmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: a[:], b[:], s[:] ([[[0, 4, 0], [4, 0, 3], [0, 3, 0]], [[4, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]], [[[2, 3, 0], [3, 0, 1], [0, 1, 0]], [[3, 0, 1], [0, 0, 0], [1, 0, 5]], [[0, 1, 0], [1, 0, 5], [0, 5, 0]]], [[[4, 10, 0], [10, 0, 5], [0, 5, 0]], [[10, 0, 5], [0, 0, 0], [5, 0, 10]], [[0, 5, 0], [5, 0, 10], [0, 10, 0]]])
It is lost if the added object is not fully symmetric:
sage: b1 = CompWithSym(QQ, V.basis(), 3, sym=(0,1)) # b1 has only symmetry on index positions (0,1) sage: b1[0,0,0], b1[0,1,0], b1[1,0,2], b1[1,2,2] = 2, 3, 1, 5 sage: s = a + 2*b1 ; s # the result has the same symmetry as b1: 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: a[:], b1[:], s[:] ([[[0, 4, 0], [4, 0, 3], [0, 3, 0]], [[4, 0, 3], [0, 0, 0], [3, 0, 0]], [[0, 3, 0], [3, 0, 0], [0, 0, 0]]], [[[2, 0, 0], [3, 0, 1], [0, 0, 0]], [[3, 0, 1], [0, 0, 0], [0, 0, 5]], [[0, 0, 0], [0, 0, 5], [0, 0, 0]]], [[[4, 4, 0], [10, 0, 5], [0, 3, 0]], [[10, 0, 5], [0, 0, 0], [3, 0, 10]], [[0, 3, 0], [3, 0, 10], [0, 0, 0]]]) sage: s = 2*b1 + a ; s 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: 2*b1 + a == a + 2*b1 True

class
sage.tensor.modules.comp.
CompWithSym
(ring, frame, nb_indices, start_index=0, output_formatter=None, sym=None, antisym=None)¶ Bases:
sage.tensor.modules.comp.Components
Indexed set of ring elements forming some components with respect to a given “frame”, with symmetries or antisymmetries regarding permutations of the indices.
The “frame” can be a basis of some vector space or a vector frame on some manifold (i.e. a field of bases). The stored quantities can be tensor components or nontensorial quantities, such as connection coefficients or structure coefficients.
Subclasses of
CompWithSym
areCompFullySym
for fully symmetric components.CompFullyAntiSym
for fully antisymmetric components.
INPUT:
ring
– commutative ring in which each component takes its valueframe
– frame with respect to which the components are defined; whatever typeframe
is, it should have some method__len__()
implemented, so thatlen(frame)
returns the dimension, i.e. the size of a single index rangenb_indices
– number of indices labeling the componentsstart_index
– (default: 0) first value of a single index; accordingly a component index i must obeystart_index <= i <= start_index + dim  1
, wheredim = len(frame)
.output_formatter
– (default:None
) function or unbound method called to format the output of the component access operator[...]
(method __getitem__);output_formatter
must take 1 or 2 arguments: the 1st argument must be an instance ofring
and the second one, if any, some format specification.sym
– (default:None
) a symmetry or a list of symmetries among the indices: each symmetry is described by a tuple containing the positions of the involved indices, with the conventionposition=0
for the first slot; for instance:sym = (0, 1)
for a symmetry between the 1st and 2nd indicessym = [(0,2), (1,3,4)]
for a symmetry between the 1st and 3rd indices and a symmetry between the 2nd, 4th and 5th indices.
antisym
– (default:None
) antisymmetry or list of antisymmetries among the indices, with the same convention as forsym
EXAMPLES:
Symmetric components with 2 indices:
sage: from sage.tensor.modules.comp import Components, CompWithSym sage: V = VectorSpace(QQ,3) sage: c = CompWithSym(QQ, V.basis(), 2, sym=(0,1)) # for demonstration only: it is preferable to use CompFullySym in this case sage: c[0,1] = 3 sage: c[:] # note that c[1,0] has been set automatically [0 3 0] [3 0 0] [0 0 0]
Antisymmetric components with 2 indices:
sage: c = CompWithSym(QQ, V.basis(), 2, antisym=(0,1)) # for demonstration only: it is preferable to use CompFullyAntiSym in this case sage: c[0,1] = 3 sage: c[:] # note that c[1,0] has been set automatically [ 0 3 0] [3 0 0] [ 0 0 0]
Internally, only nonredundant components are stored:
sage: c._comp {(0, 1): 3}
Components with 6 indices, symmetric among 3 indices (at position \((0, 1, 5)\)) and antisymmetric among 2 indices (at position \((2, 4)\)):
sage: c = CompWithSym(QQ, V.basis(), 6, sym=(0,1,5), antisym=(2,4)) sage: c[0,1,2,0,1,2] = 3 sage: c[1,0,2,0,1,2] # symmetry between indices in position 0 and 1 3 sage: c[2,1,2,0,1,0] # symmetry between indices in position 0 and 5 3 sage: c[0,2,2,0,1,1] # symmetry between indices in position 1 and 5 3 sage: c[0,1,1,0,2,2] # antisymmetry between indices in position 2 and 4 3
Components with 4 indices, antisymmetric with respect to the first pair of indices as well as with the second pair of indices:
sage: c = CompWithSym(QQ, V.basis(), 4, antisym=[(0,1),(2,3)]) sage: c[0,1,0,1] = 3 sage: c[1,0,0,1] # antisymmetry on the first pair of indices 3 sage: c[0,1,1,0] # antisymmetry on the second pair of indices 3 sage: c[1,0,1,0] # consequence of the above 3
ARITHMETIC EXAMPLES
Addition of a symmetric set of components with a nonsymmetric one: the symmetry is lost:
sage: V = VectorSpace(QQ, 3) sage: a = Components(QQ, V.basis(), 2) sage: a[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: b = CompWithSym(QQ, V.basis(), 2, sym=(0,1)) # for demonstration only: it is preferable to declare b = CompFullySym(QQ, V.basis(), 2) sage: b[0,0], b[0,1], b[0,2] = 1, 2, 3 sage: b[1,1], b[1,2] = 5, 7 sage: b[2,2] = 11 sage: s = a + b ; s 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: a[:], b[:], s[:] ( [ 1 2 3] [ 1 2 3] [ 2 0 6] [ 4 5 6] [ 2 5 7] [ 6 10 1] [7 8 9], [ 3 7 11], [4 15 20] ) sage: a + b == b + a True
Addition of two symmetric set of components: the symmetry is preserved:
sage: c = CompWithSym(QQ, V.basis(), 2, sym=(0,1)) # for demonstration only: it is preferable to declare c = CompFullySym(QQ, V.basis(), 2) sage: c[0,0], c[0,1], c[0,2] = 4, 7, 8 sage: c[1,1], c[1,2] = 2, 4 sage: c[2,2] = 2 sage: s = b + c ; s 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: b[:], c[:], s[:] ( [ 1 2 3] [4 7 8] [3 9 5] [ 2 5 7] [ 7 2 4] [ 9 7 3] [ 3 7 11], [8 4 2], [5 3 13] ) sage: b + c == c + b True
Check of the addition with counterparts not declared symmetric:
sage: bn = Components(QQ, V.basis(), 2) sage: bn[:] = b[:] sage: bn == b True sage: cn = Components(QQ, V.basis(), 2) sage: cn[:] = c[:] sage: cn == c True sage: bn + cn == b + c True
Addition of an antisymmetric set of components with a nonsymmetric one: the antisymmetry is lost:
sage: d = CompWithSym(QQ, V.basis(), 2, antisym=(0,1)) # for demonstration only: it is preferable to declare d = CompFullyAntiSym(QQ, V.basis(), 2) sage: d[0,1], d[0,2], d[1,2] = 4, 1, 3 sage: s = a + d ; s 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: a[:], d[:], s[:] ( [ 1 2 3] [ 0 4 1] [ 1 2 2] [ 4 5 6] [4 0 3] [ 0 5 3] [7 8 9], [ 1 3 0], [6 5 9] ) sage: d + a == a + d True
Addition of two antisymmetric set of components: the antisymmetry is preserved:
sage: e = CompWithSym(QQ, V.basis(), 2, antisym=(0,1)) # for demonstration only: it is preferable to declare e = CompFullyAntiSym(QQ, V.basis(), 2) sage: e[0,1], e[0,2], e[1,2] = 2, 3, 1 sage: s = d + e ; s 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: d[:], e[:], s[:] ( [ 0 4 1] [ 0 2 3] [ 0 6 2] [4 0 3] [2 0 1] [6 0 2] [ 1 3 0], [3 1 0], [2 2 0] ) sage: e + d == d + e True

antisymmetrize
(*pos)¶ Antisymmetrization over the given index positions.
INPUT:
pos
– list of index positions involved in the antisymmetrization (with the conventionposition=0
for the first slot); if none, the antisymmetrization is performed over all the indices
OUTPUT:
 an instance of
CompWithSym
describing the antisymmetrized components
EXAMPLES:
Antisymmetrization of 3indices components on a 3dimensional space:
sage: from sage.tensor.modules.comp import Components, CompWithSym, \ ....: CompFullySym, CompFullyAntiSym sage: V = VectorSpace(QQ, 3) sage: a = Components(QQ, V.basis(), 1) sage: a[:] = (2,1,3) sage: b = CompFullyAntiSym(QQ, V.basis(), 2) sage: b[0,1], b[0,2], b[1,2] = (4,1,2) sage: c = a*b ; c # tensor product of a by b 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (1, 2) sage: s = c.antisymmetrize() ; s Fully antisymmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c[:], s[:] ([[[0, 8, 2], [8, 0, 4], [2, 4, 0]], [[0, 4, 1], [4, 0, 2], [1, 2, 0]], [[0, 12, 3], [12, 0, 6], [3, 6, 0]]], [[[0, 0, 0], [0, 0, 7/3], [0, 7/3, 0]], [[0, 0, 7/3], [0, 0, 0], [7/3, 0, 0]], [[0, 7/3, 0], [7/3, 0, 0], [0, 0, 0]]])
Check of the antisymmetrization:
sage: all(s[i,j,k] == (c[i,j,k]c[i,k,j]+c[j,k,i]c[j,i,k]+c[k,i,j]c[k,j,i])/6 ....: for i in range(3) for j in range(3) for k in range(3)) True
Antisymmetrization over already antisymmetric indices does not change anything:
sage: s1 = s.antisymmetrize(1,2) ; s1 Fully antisymmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s1 == s True sage: c1 = c.antisymmetrize(1,2) ; c1 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (1, 2) sage: c1 == c True
But in general, antisymmetrization may alter previous antisymmetries:
sage: c2 = c.antisymmetrize(0,1) ; c2 # the antisymmetry (2,3) is lost: 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: c2 == c False sage: c = s*a ; c 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1, 2) sage: s = c.antisymmetrize(1,3) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (1, 3), with antisymmetry on the index positions (0, 2) sage: s._antisym # the antisymmetry (0,1,2) has been reduced to (0,2), since 1 is involved in the new antisymmetry (1,3): [(1, 3), (0, 2)]
Partial antisymmetrization of 4indices components with a symmetry on the first two indices:
sage: a = CompFullySym(QQ, V.basis(), 2) sage: a[:] = [[2,1,3], [1,0,5], [3,5,4]] sage: b = Components(QQ, V.basis(), 2) sage: b[:] = [[1,2,3], [5,7,11], [13,17,19]] sage: c = a*b ; c 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: s = c.antisymmetrize(2,3) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1), with antisymmetry on the index positions (2, 3)
Some check of the antisymmetrization:
sage: all(s[2,2,i,j] == (c[2,2,i,j]  c[2,2,j,i])/2 ....: for i in range(3) for j in range(i,3)) True
The full antisymmetrization results in zero because of the symmetry on the first two indices:
sage: s = c.antisymmetrize() ; s Fully antisymmetric 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s == 0 True
Similarly, the partial antisymmetrization on the first two indices results in zero:
sage: s = c.antisymmetrize(0,1) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: s == 0 True
The partial antisymmetrization on the positions \((0, 2)\) destroys the symmetry on \((0, 1)\):
sage: s = c.antisymmetrize(0,2) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 2) sage: s != 0 True sage: s[0,1,2,1] 27/2 sage: s[1,0,2,1] # the symmetry (0,1) is lost 2 sage: s[2,1,0,1] # the antisymmetry (0,2) holds 27/2

non_redundant_index_generator
()¶ Generator of indices, with only ordered indices in case of symmetries, so that only nonredundant indices are generated.
OUTPUT:
 an iterable index
EXAMPLES:
Indices on a 2dimensional space:
sage: from sage.tensor.modules.comp import Components, CompWithSym, \ ....: CompFullySym, CompFullyAntiSym sage: V = VectorSpace(QQ, 2) sage: c = CompFullySym(QQ, V.basis(), 2) sage: list(c.non_redundant_index_generator()) [(0, 0), (0, 1), (1, 1)] sage: c = CompFullySym(QQ, V.basis(), 2, start_index=1) sage: list(c.non_redundant_index_generator()) [(1, 1), (1, 2), (2, 2)] sage: c = CompFullyAntiSym(QQ, V.basis(), 2) sage: list(c.non_redundant_index_generator()) [(0, 1)]
Indices on a 3dimensional space:
sage: V = VectorSpace(QQ, 3) sage: c = CompFullySym(QQ, V.basis(), 2) sage: list(c.non_redundant_index_generator()) [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)] sage: c = CompFullySym(QQ, V.basis(), 2, start_index=1) sage: list(c.non_redundant_index_generator()) [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)] sage: c = CompFullyAntiSym(QQ, V.basis(), 2) sage: list(c.non_redundant_index_generator()) [(0, 1), (0, 2), (1, 2)] sage: c = CompWithSym(QQ, V.basis(), 3, sym=(1,2)) # symmetry on the last two indices sage: list(c.non_redundant_index_generator()) [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 1), (0, 1, 2), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 1), (1, 1, 2), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 1), (2, 1, 2), (2, 2, 2)] sage: c = CompWithSym(QQ, V.basis(), 3, antisym=(1,2)) # antisymmetry on the last two indices sage: list(c.non_redundant_index_generator()) [(0, 0, 1), (0, 0, 2), (0, 1, 2), (1, 0, 1), (1, 0, 2), (1, 1, 2), (2, 0, 1), (2, 0, 2), (2, 1, 2)] sage: c = CompFullySym(QQ, V.basis(), 3) sage: list(c.non_redundant_index_generator()) [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 1), (0, 1, 2), (0, 2, 2), (1, 1, 1), (1, 1, 2), (1, 2, 2), (2, 2, 2)] sage: c = CompFullyAntiSym(QQ, V.basis(), 3) sage: list(c.non_redundant_index_generator()) [(0, 1, 2)]
Indices on a 4dimensional space:
sage: V = VectorSpace(QQ, 4) sage: c = Components(QQ, V.basis(), 1) sage: list(c.non_redundant_index_generator()) [(0,), (1,), (2,), (3,)] sage: c = CompFullyAntiSym(QQ, V.basis(), 2) sage: list(c.non_redundant_index_generator()) [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] sage: c = CompFullyAntiSym(QQ, V.basis(), 3) sage: list(c.non_redundant_index_generator()) [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)] sage: c = CompFullyAntiSym(QQ, V.basis(), 4) sage: list(c.non_redundant_index_generator()) [(0, 1, 2, 3)] sage: c = CompFullyAntiSym(QQ, V.basis(), 5) sage: list(c.non_redundant_index_generator()) # nothing since c is identically zero in this case (for 5 > 4) []

swap_adjacent_indices
(pos1, pos2, pos3)¶ Swap two adjacent sets of indices.
This method is essentially required to reorder the covariant and contravariant indices in the computation of a tensor product.
The symmetries are preserved and the corresponding indices are adjusted consequently.
INPUT:
pos1
– position of the first index of set 1 (with the convention position=0 for the first slot)pos2
– position of the first index of set 2 = 1 + position of the last index of set 1 (since the two sets are adjacent)pos3
– 1 + position of the last index of set 2
OUTPUT:
 Components with index set 1 permuted with index set 2.
EXAMPLES:
Swap of the index in position 0 with the pair of indices in position (1,2) in a set of components antisymmetric with respect to the indices in position (1,2):
sage: from sage.tensor.modules.comp import CompWithSym sage: V = VectorSpace(QQ, 3) sage: c = CompWithSym(QQ, V.basis(), 3, antisym=(1,2)) sage: c[0,0,1], c[0,0,2], c[0,1,2] = (1,2,3) sage: c[1,0,1], c[1,0,2], c[1,1,2] = (4,5,6) sage: c[2,0,1], c[2,0,2], c[2,1,2] = (7,8,9) sage: c[:] [[[0, 1, 2], [1, 0, 3], [2, 3, 0]], [[0, 4, 5], [4, 0, 6], [5, 6, 0]], [[0, 7, 8], [7, 0, 9], [8, 9, 0]]] sage: c1 = c.swap_adjacent_indices(0,1,3) sage: c._antisym # c is antisymmetric with respect to the last pair of indices... [(1, 2)] sage: c1._antisym #...while c1 is antisymmetric with respect to the first pair of indices [(0, 1)] sage: c[0,1,2] 3 sage: c1[1,2,0] 3 sage: c1[2,1,0] 3

symmetrize
(*pos)¶ Symmetrization over the given index positions.
INPUT:
pos
– list of index positions involved in the symmetrization (with the conventionposition=0
for the first slot); if none, the symmetrization is performed over all the indices
OUTPUT:
 an instance of
CompWithSym
describing the symmetrized components
EXAMPLES:
Symmetrization of 3indices components on a 3dimensional space:
sage: from sage.tensor.modules.comp import Components, CompWithSym, \ ....: CompFullySym, CompFullyAntiSym sage: V = VectorSpace(QQ, 3) sage: c = Components(QQ, V.basis(), 3) sage: c[:] = [[[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: cs = c.symmetrize(0,1) ; cs 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: s = cs.symmetrize() ; s Fully symmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: cs[:], s[:] ([[[1, 2, 3], [7, 8, 9], [13, 14, 15]], [[7, 8, 9], [13, 14, 15], [19, 20, 21]], [[13, 14, 15], [19, 20, 21], [25, 26, 27]]], [[[1, 16/3, 29/3], [16/3, 29/3, 14], [29/3, 14, 55/3]], [[16/3, 29/3, 14], [29/3, 14, 55/3], [14, 55/3, 68/3]], [[29/3, 14, 55/3], [14, 55/3, 68/3], [55/3, 68/3, 27]]]) sage: s == c.symmetrize() # should be true True sage: s1 = cs.symmetrize(0,1) ; s1 # should return a copy of cs 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: s1 == cs # check that s1 is a copy of cs True
Let us now start with a symmetry on the last two indices:
sage: cs1 = c.symmetrize(1,2) ; cs1 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (1, 2) sage: s2 = cs1.symmetrize() ; s2 Fully symmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s2 == c.symmetrize() True
Symmetrization alters preexisting symmetries: let us symmetrize w.r.t. the index positions \((1, 2)\) a set of components that is symmetric w.r.t. the index positions \((0, 1)\):
sage: cs = c.symmetrize(0,1) ; cs 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: css = cs.symmetrize(1,2) sage: css # the symmetry (0,1) has been lost: 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (1, 2) sage: css[:] [[[1, 9/2, 8], [9/2, 8, 23/2], [8, 23/2, 15]], [[7, 21/2, 14], [21/2, 14, 35/2], [14, 35/2, 21]], [[13, 33/2, 20], [33/2, 20, 47/2], [20, 47/2, 27]]] sage: cs[:] [[[1, 2, 3], [7, 8, 9], [13, 14, 15]], [[7, 8, 9], [13, 14, 15], [19, 20, 21]], [[13, 14, 15], [19, 20, 21], [25, 26, 27]]] sage: css == c.symmetrize() # css differs from the full symmetrized version False sage: css.symmetrize() == c.symmetrize() # one has to symmetrize css over all indices to recover it True
Another example of symmetry alteration: symmetrization over \((0, 1)\) of a 4indices set of components that is symmetric w.r.t. \((1, 2, 3)\):
sage: v = Components(QQ, V.basis(), 1) sage: v[:] = (2,1,4) sage: a = v*s ; a 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (1, 2, 3) sage: a1 = a.symmetrize(0,1) ; a1 # the symmetry (1,2,3) has been reduced to (2,3): 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) sage: a1._sym # a1 has two distinct symmetries: [(0, 1), (2, 3)] sage: a[0,1,2,0] == a[0,0,2,1] # a is symmetric w.r.t. positions 1 and 3 True sage: a1[0,1,2,0] == a1[0,0,2,1] # a1 is not False sage: a1[0,1,2,0] == a1[1,0,2,0] # but it is symmetric w.r.t. position 0 and 1 True sage: a[0,1,2,0] == a[1,0,2,0] # while a is not False
Partial symmetrization of 4indices components with an antisymmetry on the last two indices:
sage: a = Components(QQ, V.basis(), 2) sage: a[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: b = CompFullyAntiSym(QQ, V.basis(), 2) sage: b[0,1], b[0,2], b[1,2] = (2, 4, 8) sage: c = a*b ; c 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (2, 3) sage: s = c.symmetrize(0,1) ; s # symmetrization on the first two indices 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1), with antisymmetry on the index positions (2, 3) sage: s[0,1,2,1] == (c[0,1,2,1] + c[1,0,2,1]) / 2 # check of the symmetrization True sage: s = c.symmetrize() ; s # symmetrization over all the indices Fully symmetric 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s == 0 # the full symmetrization results in zero due to the antisymmetry on the last two indices True sage: s = c.symmetrize(2,3) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (2, 3) sage: s == 0 # must be zero since the symmetrization has been performed on the antisymmetric indices True sage: s = c.symmetrize(0,2) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 2) sage: s != 0 # s is not zero, but the antisymmetry on (2,3) is lost because the position 2 is involved in the new symmetry True
Partial symmetrization of 4indices components with an antisymmetry on the last three indices:
sage: a = Components(QQ, V.basis(), 1) sage: a[:] = (1, 2, 3) sage: b = CompFullyAntiSym(QQ, V.basis(), 3) sage: b[0,1,2] = 4 sage: c = a*b ; c 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (1, 2, 3) sage: s = c.symmetrize(0,1) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1), with antisymmetry on the index positions (2, 3)
Note that the antisymmetry on \((1, 2, 3)\) has been reduced to \((2, 3)\) only:
sage: s = c.symmetrize(1,2) ; s 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (1, 2) sage: s == 0 # because (1,2) are involved in the original antisymmetry True

trace
(pos1, pos2)¶ Index contraction, taking care of the symmetries.
INPUT:
pos1
– position of the first index for the contraction (with the convention position=0 for the first slot)pos2
– position of the second index for the contraction
OUTPUT:
 set of components resulting from the (pos1, pos2) contraction
EXAMPLES:
Selfcontraction of symmetric 2indices components:
sage: from sage.tensor.modules.comp import Components, CompWithSym, \ ....: CompFullySym, CompFullyAntiSym sage: V = VectorSpace(QQ, 3) sage: a = CompFullySym(QQ, V.basis(), 2) sage: a[:] = [[1,2,3],[2,4,5],[3,5,6]] sage: a.trace(0,1) 11 sage: a[0,0] + a[1,1] + a[2,2] 11
Selfcontraction of antisymmetric 2indices components:
sage: b = CompFullyAntiSym(QQ, V.basis(), 2) sage: b[0,1], b[0,2], b[1,2] = (3, 2, 1) sage: b.trace(0,1) # must be zero by antisymmetry 0
Selfcontraction of 3indices components with one symmetry:
sage: v = Components(QQ, V.basis(), 1) sage: v[:] = (2, 4, 8) sage: c = v*b ; c 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (1, 2) sage: s = c.trace(0,1) ; s 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s[:] [28, 2, 8] sage: [sum(v[k]*b[k,i] for k in range(3)) for i in range(3)] # check [28, 2, 8] sage: s = c.trace(1,2) ; s 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s[:] # is zero by antisymmetry [0, 0, 0] sage: c = b*v ; c 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: s = c.trace(0,1) sage: s[:] # is zero by antisymmetry [0, 0, 0] sage: s = c.trace(1,2) ; s[:] [28, 2, 8] sage: [sum(b[i,k]*v[k] for k in range(3)) for i in range(3)] # check [28, 2, 8]
Selfcontraction of 4indices components with two symmetries:
sage: c = a*b ; c 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1), with antisymmetry on the index positions (2, 3) sage: s = c.trace(0,1) ; s # the symmetry on (0,1) is lost: Fully antisymmetric 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s[:] [ 0 33 22] [33 0 11] [ 22 11 0] sage: [[sum(c[k,k,i,j] for k in range(3)) for j in range(3)] for i in range(3)] # check [[0, 33, 22], [33, 0, 11], [22, 11, 0]] sage: s = c.trace(1,2) ; s # both symmetries are lost by this contraction 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s[:] [ 0 0 0] [2 1 0] [3 3 1] sage: [[sum(c[i,k,k,j] for k in range(3)) for j in range(3)] for i in range(3)] # check [[0, 0, 0], [2, 1, 0], [3, 3, 1]]

class
sage.tensor.modules.comp.
Components
(ring, frame, nb_indices, start_index=0, output_formatter=None)¶ Bases:
sage.structure.sage_object.SageObject
Indexed set of ring elements forming some components with respect to a given “frame”.
The “frame” can be a basis of some vector space or a vector frame on some manifold (i.e. a field of bases). The stored quantities can be tensor components or nontensorial quantities, such as connection coefficients or structure coefficients. The symmetries over some indices are dealt by subclasses of the class
Components
.INPUT:
ring
– commutative ring in which each component takes its valueframe
– frame with respect to which the components are defined; whatever typeframe
is, it should have a method__len__()
implemented, so thatlen(frame)
returns the dimension, i.e. the size of a single index rangenb_indices
– number of integer indices labeling the componentsstart_index
– (default: 0) first value of a single index; accordingly a component index i must obeystart_index <= i <= start_index + dim  1
, wheredim = len(frame)
.output_formatter
– (default:None
) function or unbound method called to format the output of the component access operator[...]
(method __getitem__);output_formatter
must take 1 or 2 arguments: the 1st argument must be an element ofring
and the second one, if any, some format specification.
EXAMPLES:
Set of components with 2 indices on a 3dimensional vector space, the frame being some basis of the vector space:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ,3) sage: basis = V.basis() ; basis [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c = Components(QQ, basis, 2) ; c 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ]
Actually, the frame can be any object that has some length, i.e. on which the function
len()
can be called:sage: basis1 = V.gens() ; basis1 ((1, 0, 0), (0, 1, 0), (0, 0, 1)) sage: c1 = Components(QQ, basis1, 2) ; c1 2indices components w.r.t. ((1, 0, 0), (0, 1, 0), (0, 0, 1)) sage: basis2 = ['a', 'b' , 'c'] sage: c2 = Components(QQ, basis2, 2) ; c2 2indices components w.r.t. ['a', 'b', 'c']
By default, the indices range from \(0\) to \(n1\), where \(n\) is the length of the frame. This can be changed via the argument
start_index
:sage: c1 = Components(QQ, basis, 2, start_index=1) sage: c1[0,1] Traceback (most recent call last): ... IndexError: index out of range: 0 not in [1, 3] sage: c[0,1] # for c, the index 0 is OK 0 sage: c[0,1] = 3 sage: c1[:] = c[:] # list copy of all components sage: c1[1,2] # (1,2) = (0,1) shifted by 1 3
If some formatter function or unbound method is provided via the argument
output_formatter
, it is used to change the ouput of the access operator[...]
:sage: a = Components(QQ, basis, 2, output_formatter=Rational.numerical_approx) sage: a[1,2] = 1/3 sage: a[1,2] 0.333333333333333
The format can be passed to the formatter as the last argument of the access operator
[...]
:sage: a[1,2,10] # here the format is 10, for 10 bits of precision 0.33 sage: a[1,2,100] 0.33333333333333333333333333333
The raw (unformatted) components are then accessed by the double bracket operator:
sage: a[[1,2]] 1/3
For sets of components declared without any output formatter, there is no difference between
[...]
and[[...]]
:sage: c[1,2] = 1/3 sage: c[1,2], c[[1,2]] (1/3, 1/3)
The formatter is also used for the complete list of components:
sage: a[:] [0.000000000000000 0.000000000000000 0.000000000000000] [0.000000000000000 0.000000000000000 0.333333333333333] [0.000000000000000 0.000000000000000 0.000000000000000] sage: a[:,10] # with a format different from the default one (53 bits) [0.00 0.00 0.00] [0.00 0.00 0.33] [0.00 0.00 0.00]
The complete list of components in raw form can be recovered by the double bracket operator, replacing
:
byslice(None)
(sincea[[:]]
generates a Python syntax error):sage: a[[slice(None)]] [ 0 0 0] [ 0 0 1/3] [ 0 0 0]
Another example of formatter: the Python builtin function
str()
to generate string outputs:sage: b = Components(QQ, V.basis(), 1, output_formatter=str) sage: b[:] = (1, 0, 4) sage: b[:] ['1', '0', '4']
For such a formatter, 2indices components are no longer displayed as a matrix:
sage: b = Components(QQ, basis, 2, output_formatter=str) sage: b[0,1] = 1/3 sage: b[:] [['0', '1/3', '0'], ['0', '0', '0'], ['0', '0', '0']]
But unformatted outputs still are:
sage: b[[slice(None)]] [ 0 1/3 0] [ 0 0 0] [ 0 0 0]
Internally, the components are stored as a dictionary (
_comp
) whose keys are the indices; only the nonzero components are stored:sage: a[:] [0.000000000000000 0.000000000000000 0.000000000000000] [0.000000000000000 0.000000000000000 0.333333333333333] [0.000000000000000 0.000000000000000 0.000000000000000] sage: a._comp {(1, 2): 1/3} sage: v = Components(QQ, basis, 1) sage: v[:] = (1, 0, 3) sage: v._comp # random output order of the component dictionary {(0,): 1, (2,): 3}
ARITHMETIC EXAMPLES:
Unary plus operator:
sage: a = Components(QQ, basis, 1) sage: a[:] = (1, 0, 3) sage: s = +a ; s[:] [1, 0, 3] sage: +a == a True
Unary minus operator:
sage: s = a ; s[:] [1, 0, 3]
Addition:
sage: b = Components(QQ, basis, 1) sage: b[:] = (2, 1, 4) sage: s = a + b ; s[:] [1, 1, 7] sage: a + b == b + a True sage: a + (a) == 0 True
Subtraction:
sage: s = a  b ; s[:] [3, 1, 1] sage: s + b == a True sage: a  b ==  (b  a) True
Multiplication by a scalar:
sage: s = 2*a ; s[:] [2, 0, 6]
Division by a scalar:
sage: s = a/2 ; s[:] [1/2, 0, 3/2] sage: 2*(a/2) == a True
Tensor product (by means of the operator
*
):sage: c = a*b ; c 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: a[:], b[:] ([1, 0, 3], [2, 1, 4]) sage: c[:] [2 1 4] [ 0 0 0] [ 6 3 12] sage: d = c*a ; d 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: d[:] [[[2, 0, 6], [1, 0, 3], [4, 0, 12]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[6, 0, 18], [3, 0, 9], [12, 0, 36]]] sage: d[0,1,2] == a[0]*b[1]*a[2] True

antisymmetrize
(*pos)¶ Antisymmetrization over the given index positions
INPUT:
pos
– list of index positions involved in the antisymmetrization (with the convention position=0 for the first slot); if none, the antisymmetrization is performed over all the indices
OUTPUT:
 an instance of
CompWithSym
describing the antisymmetrized components.
EXAMPLES:
Antisymmetrization of 2indices components:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ, 3) sage: c = Components(QQ, V.basis(), 2) sage: c[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: s = c.antisymmetrize() ; s Fully antisymmetric 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c[:], s[:] ( [1 2 3] [ 0 1 2] [4 5 6] [ 1 0 1] [7 8 9], [ 2 1 0] ) sage: c.antisymmetrize() == c.antisymmetrize(0,1) True
Full antisymmetrization of 3indices components:
sage: c = Components(QQ, V.basis(), 3) sage: c[:] = [[[1,2,3], [4,5,4], [7,8,9]], [[10,10,12], [13,14,15], [16,17,19]], [[19,20,21], [1,2,3], [25,26,27]]] sage: s = c.antisymmetrize() ; s Fully antisymmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c[:], s[:] ([[[1, 2, 3], [4, 5, 4], [7, 8, 9]], [[10, 10, 12], [13, 14, 15], [16, 17, 19]], [[19, 20, 21], [1, 2, 3], [25, 26, 27]]], [[[0, 0, 0], [0, 0, 13/6], [0, 13/6, 0]], [[0, 0, 13/6], [0, 0, 0], [13/6, 0, 0]], [[0, 13/6, 0], [13/6, 0, 0], [0, 0, 0]]]) sage: all(s[i,j,k] == (c[i,j,k]c[i,k,j]+c[j,k,i]c[j,i,k]+c[k,i,j]c[k,j,i])/6 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: c.symmetrize() == c.symmetrize(0,1,2) True
Partial antisymmetrization of 3indices components:
sage: s = c.antisymmetrize(0,1) ; s # antisymmetrization on the first two indices 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 1) sage: c[:], s[:] ([[[1, 2, 3], [4, 5, 4], [7, 8, 9]], [[10, 10, 12], [13, 14, 15], [16, 17, 19]], [[19, 20, 21], [1, 2, 3], [25, 26, 27]]], [[[0, 0, 0], [3, 15/2, 4], [6, 6, 6]], [[3, 15/2, 4], [0, 0, 0], [17/2, 15/2, 8]], [[6, 6, 6], [17/2, 15/2, 8], [0, 0, 0]]]) sage: all(s[i,j,k] == (c[i,j,k]c[j,i,k])/2 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s = c.antisymmetrize(1,2) ; s # antisymmetrization on the last two indices 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (1, 2) sage: c[:], s[:] ([[[1, 2, 3], [4, 5, 4], [7, 8, 9]], [[10, 10, 12], [13, 14, 15], [16, 17, 19]], [[19, 20, 21], [1, 2, 3], [25, 26, 27]]], [[[0, 3, 5], [3, 0, 2], [5, 2, 0]], [[0, 3/2, 14], [3/2, 0, 1], [14, 1, 0]], [[0, 19/2, 23], [19/2, 0, 23/2], [23, 23/2, 0]]]) sage: all(s[i,j,k] == (c[i,j,k]c[i,k,j])/2 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s = c.antisymmetrize(0,2) ; s # antisymmetrization on the first and last indices 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 2) sage: c[:], s[:] ([[[1, 2, 3], [4, 5, 4], [7, 8, 9]], [[10, 10, 12], [13, 14, 15], [16, 17, 19]], [[19, 20, 21], [1, 2, 3], [25, 26, 27]]], [[[0, 6, 11], [0, 9, 3/2], [0, 12, 17]], [[6, 0, 4], [9, 0, 13/2], [12, 0, 7/2]], [[11, 4, 0], [3/2, 13/2, 0], [17, 7/2, 0]]]) sage: all(s[i,j,k] == (c[i,j,k]c[k,j,i])/2 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True
The order of index positions in the argument does not matter:
sage: c.antisymmetrize(1,0) == c.antisymmetrize(0,1) True sage: c.antisymmetrize(2,1) == c.antisymmetrize(1,2) True sage: c.antisymmetrize(2,0) == c.antisymmetrize(0,2) True

contract
(*args)¶ Contraction on one or many indices with another instance of
Components
.INPUT:
pos1
– positions of the indices inself
involved in the contraction;pos1
must be a sequence of integers, with 0 standing for the first index position, 1 for the second one, etc. Ifpos1
is not provided, a single contraction on the last index position ofself
is assumedother
– the set of components to contract withpos2
– positions of the indices inother
involved in the contraction, with the same conventions as forpos1
. Ifpos2
is not provided, a single contraction on the first index position ofother
is assumed
OUTPUT:
 set of components resulting from the contraction
EXAMPLES:
Contraction of a 1index set of components with a 2index one:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ, 3) sage: a = Components(QQ, V.basis(), 1) sage: a[:] = (1, 2, 3) sage: b = Components(QQ, V.basis(), 2) sage: b[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: s0 = a.contract(0, b, 0) ; s0 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s0[:] [28, 32, 36] sage: s0[:] == [sum(a[j]*b[j,i] for j in range(3)) for i in range(3)] # check True sage: s1 = a.contract(0, b, 1) ; s1[:] [12, 24, 36] sage: s1[:] == [sum(a[j]*b[i,j] for j in range(3)) for i in range(3)] # check True
Parallel computations (see
Parallelism
):sage: Parallelism().set('tensor', nproc=2) sage: Parallelism().get('tensor') 2 sage: s0_par = a.contract(0, b, 0) ; s0_par 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s0_par[:] [28, 32, 36] sage: s0_par == s0 True sage: s1_par = a.contract(0, b, 1) ; s1_par[:] [12, 24, 36] sage: s1_par == s1 True sage: Parallelism().set('tensor', nproc = 1) # switch off parallelization
Contraction on 2 indices:
sage: c = a*b ; c 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s = c.contract(1,2, b, 0,1) ; s 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s[:] [285, 570, 855] sage: [sum(sum(c[i,j,k]*b[j,k] for k in range(3)) # check ....: for j in range(3)) for i in range(3)] [285, 570, 855]
Parallel computation:
sage: Parallelism().set('tensor', nproc=2) sage: c_par = a*b ; c_par 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c_par == c True sage: s_par = c_par.contract(1,2, b, 0,1) ; s_par 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s_par[:] [285, 570, 855] sage: s_par == s True sage: Parallelism().set('tensor', nproc=1) # switch off parallelization
Consistency check with
trace()
:sage: b = a*a ; b # the tensor product of a with itself Fully symmetric 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: b[:] [ 1 2 3] [2 4 6] [3 6 9] sage: b.trace(0,1) 14 sage: a.contract(0, a, 0) == b.trace(0,1) True

copy
()¶ Return an exact copy of
self
.EXAMPLES:
Copy of a set of components with a single index:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ,3) sage: a = Components(QQ, V.basis(), 1) sage: a[:] = 2, 1, 5 sage: b = a.copy() ; b 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: b[:] [2, 1, 5] sage: b == a True sage: b is a # b is a distinct object False

display
(symbol, latex_symbol=None, index_positions=None, index_labels=None, index_latex_labels=None, format_spec=None, only_nonzero=True, only_nonredundant=False)¶ Display all the components, one per line.
The output is either textformatted (console mode) or LaTeXformatted (notebook mode).
INPUT:
symbol
– string (typically a single letter) specifying the symbol for the componentslatex_symbol
– (default:None
) string specifying the LaTeX symbol for the components; ifNone
,symbol
is usedindex_positions
– (default:None
) string of length the number of indices of the components and composed of characters ‘d’ (for “down”) or ‘u’ (for “up”) to specify the position of each index: ‘d’ corresponds to a subscript and ‘u’ to a superscript. Ifindex_positions
isNone
, all indices are printed as subscriptsindex_labels
– (default:None
) list of strings representing the labels of each of the individual indices within the index range defined at the construction of the object; ifNone
, integer labels are usedindex_latex_labels
– (default:None
) list of strings representing the LaTeX labels of each of the individual indices within the index range defined at the construction of the object; ifNone
, integers labels are usedformat_spec
– (default:None
) format specification passed to the output formatter declared at the construction of the objectonly_nonzero
– (default:True
) boolean; ifTrue
, only nonzero components are displayedonly_nonredundant
– (default:False
) boolean; ifTrue
, only nonredundant components are displayed in case of symmetries
EXAMPLES:
Display of 3indices components w.r.t. to the canonical basis of the free module \(\ZZ^2\) over the integer ring:
sage: from sage.tensor.modules.comp import Components sage: c = Components(ZZ, (ZZ^2).basis(), 3) sage: c[0,1,0], c[1,0,1], c[1,1,1] = 2, 5, 3 sage: c.display('c') c_010 = 2 c_101 = 5 c_111 = 3
By default, only nonzero components are shown; to display all the components, it suffices to set the parameter
only_nonzero
toFalse
:sage: c.display('c', only_nonzero=False) c_000 = 0 c_001 = 0 c_010 = 2 c_011 = 0 c_100 = 0 c_101 = 5 c_110 = 0 c_111 = 3
By default, all indices are printed as subscripts, but any index position can be specifed:
sage: c.display('c', index_positions='udd') c^0_10 = 2 c^1_01 = 5 c^1_11 = 3 sage: c.display('c', index_positions='udu') c^0_1^0 = 2 c^1_0^1 = 5 c^1_1^1 = 3 sage: c.display('c', index_positions='ddu') c_01^0 = 2 c_10^1 = 5 c_11^1 = 3
The LaTeX output is performed as an array, with the symbol adjustable if it differs from the text symbol:
sage: latex(c.display('c', latex_symbol=r'\Gamma', index_positions='udd')) \begin{array}{lcl} \Gamma_{\phantom{\, 0}\,1\,0}^{\,0\phantom{\, 1}\phantom{\, 0}} & = & 2 \\ \Gamma_{\phantom{\, 1}\,0\,1}^{\,1\phantom{\, 0}\phantom{\, 1}} & = & 5 \\ \Gamma_{\phantom{\, 1}\,1\,1}^{\,1\phantom{\, 1}\phantom{\, 1}} & = & 3 \end{array}
The index labels can differ from integers:
sage: c.display('c', index_labels=['x','y']) c_xyx = 2 c_yxy = 5 c_yyy = 3
If the index labels are longer than a single character, they are separated by a comma:
sage: c.display('c', index_labels=['r', 'th']) c_r,th,r = 2 c_th,r,th = 5 c_th,th,th = 3
The LaTeX labels for the indices can be specified if they differ from the text ones:
sage: c.display('c', index_labels=['r', 'th'], ....: index_latex_labels=['r', r'\theta']) c_r,th,r = 2 c_th,r,th = 5 c_th,th,th = 3
The display of components with symmetries is governed by the parameter
only_nonredundant
:sage: from sage.tensor.modules.comp import CompWithSym sage: c = CompWithSym(ZZ, (ZZ^2).basis(), 3, sym=(1,2)) ; c 3indices components w.r.t. [ (1, 0), (0, 1) ], with symmetry on the index positions (1, 2) sage: c[0,0,1] = 2 sage: c.display('c') c_001 = 2 c_010 = 2 sage: c.display('c', only_nonredundant=True) c_001 = 2
If some nontrivial output formatter has been set, the format can be specified by means of the argument
format_spec
:sage: c = Components(QQ, (QQ^3).basis(), 2, ....: output_formatter=Rational.numerical_approx) sage: c[0,1] = 1/3 sage: c[2,1] = 2/7 sage: c.display('C') # default format (53 bits of precision) C_01 = 0.333333333333333 C_21 = 0.285714285714286 sage: c.display('C', format_spec=10) # 10 bits of precision C_01 = 0.33 C_21 = 0.29
Check that the bug reported in trac ticket #22520 is fixed:
sage: c = Components(SR, [1, 2], 1) sage: c[0] = SR.var('t', domain='real') sage: c.display('c') c_0 = t

index_generator
()¶ Generator of indices.
OUTPUT:
 an iterable index
EXAMPLES:
Indices on a 3dimensional vector space:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ,3) sage: c = Components(QQ, V.basis(), 1) sage: list(c.index_generator()) [(0,), (1,), (2,)] sage: c = Components(QQ, V.basis(), 1, start_index=1) sage: list(c.index_generator()) [(1,), (2,), (3,)] sage: c = Components(QQ, V.basis(), 2) sage: list(c.index_generator()) [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

is_zero
()¶ Return
True
if all the components are zero andFalse
otherwise.EXAMPLES:
A justcreated set of components is initialized to zero:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ,3) sage: c = Components(QQ, V.basis(), 1) sage: c.is_zero() True sage: c[:] [0, 0, 0] sage: c[0] = 1 ; c[:] [1, 0, 0] sage: c.is_zero() False sage: c[0] = 0 ; c[:] [0, 0, 0] sage: c.is_zero() True
It is equivalent to use the operator == to compare to zero:
sage: c == 0 True sage: c != 0 False
Comparing to a nonzero number is meaningless:
sage: c == 1 Traceback (most recent call last): ... TypeError: cannot compare a set of components to a number

non_redundant_index_generator
()¶ Generator of non redundant indices.
In the absence of declared symmetries, all possible indices are generated. So this method is equivalent to
index_generator()
. Only versions for derived classes with symmetries or antisymmetries are not trivial.OUTPUT:
 an iterable index
EXAMPLES:
Indices on a 3dimensional vector space:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ,3) sage: c = Components(QQ, V.basis(), 2) sage: list(c.non_redundant_index_generator()) [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: c = Components(QQ, V.basis(), 2, start_index=1) sage: list(c.non_redundant_index_generator()) [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]

swap_adjacent_indices
(pos1, pos2, pos3)¶ Swap two adjacent sets of indices.
This method is essentially required to reorder the covariant and contravariant indices in the computation of a tensor product.
INPUT:
pos1
– position of the first index of set 1 (with the conventionposition=0
for the first slot)pos2
– position of the first index of set 2 equals 1 plus the position of the last index of set 1 (since the two sets are adjacent)pos3
– 1 plus position of the last index of set 2
OUTPUT:
 Components with index set 1 permuted with index set 2.
EXAMPLES:
Swap of the two indices of a 2indices set of components:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ, 3) sage: c = Components(QQ, V.basis(), 2) sage: c[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: c1 = c.swap_adjacent_indices(0,1,2) sage: c[:], c1[:] ( [1 2 3] [1 4 7] [4 5 6] [2 5 8] [7 8 9], [3 6 9] )
Swap of two pairs of indices on a 4indices set of components:
sage: d = c*c1 ; d 4indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: d1 = d.swap_adjacent_indices(0,2,4) sage: d[0,1,1,2] 16 sage: d1[1,2,0,1] 16 sage: d1[0,1,1,2] 24 sage: d[1,2,0,1] 24

symmetrize
(*pos)¶ Symmetrization over the given index positions.
INPUT:
pos
– list of index positions involved in the symmetrization (with the convention position=0 for the first slot); if none, the symmetrization is performed over all the indices
OUTPUT:
 an instance of
CompWithSym
describing the symmetrized components
EXAMPLES:
Symmetrization of 2indices components:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ, 3) sage: c = Components(QQ, V.basis(), 2) sage: c[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: s = c.symmetrize() ; s Fully symmetric 2indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c[:], s[:] ( [1 2 3] [1 3 5] [4 5 6] [3 5 7] [7 8 9], [5 7 9] ) sage: c.symmetrize() == c.symmetrize(0,1) True
Full symmetrization of 3indices components:
sage: c = Components(QQ, V.basis(), 3) sage: c[:] = [[[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 = c.symmetrize() ; s Fully symmetric 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: c[:], s[:] ([[[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]]], [[[1, 16/3, 29/3], [16/3, 29/3, 14], [29/3, 14, 55/3]], [[16/3, 29/3, 14], [29/3, 14, 55/3], [14, 55/3, 68/3]], [[29/3, 14, 55/3], [14, 55/3, 68/3], [55/3, 68/3, 27]]]) sage: all(s[i,j,k] == (c[i,j,k]+c[i,k,j]+c[j,k,i]+c[j,i,k]+c[k,i,j]+c[k,j,i])/6 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: c.symmetrize() == c.symmetrize(0,1,2) True
Partial symmetrization of 3indices components:
sage: s = c.symmetrize(0,1) ; s # symmetrization on the first two indices 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 1) sage: c[:], s[:] ([[[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]]], [[[1, 2, 3], [7, 8, 9], [13, 14, 15]], [[7, 8, 9], [13, 14, 15], [19, 20, 21]], [[13, 14, 15], [19, 20, 21], [25, 26, 27]]]) sage: all(s[i,j,k] == (c[i,j,k]+c[j,i,k])/2 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s = c.symmetrize(1,2) ; s # symmetrization on the last two indices 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (1, 2) sage: c[:], s[:] ([[[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]]], [[[1, 3, 5], [3, 5, 7], [5, 7, 9]], [[10, 12, 14], [12, 14, 16], [14, 16, 18]], [[19, 21, 23], [21, 23, 25], [23, 25, 27]]]) sage: all(s[i,j,k] == (c[i,j,k]+c[i,k,j])/2 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True sage: s = c.symmetrize(0,2) ; s # symmetrization on the first and last indices 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with symmetry on the index positions (0, 2) sage: c[:], s[:] ([[[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]]], [[[1, 6, 11], [4, 9, 14], [7, 12, 17]], [[6, 11, 16], [9, 14, 19], [12, 17, 22]], [[11, 16, 21], [14, 19, 24], [17, 22, 27]]]) sage: all(s[i,j,k] == (c[i,j,k]+c[k,j,i])/2 # Check of the result: ....: for i in range(3) for j in range(3) for k in range(3)) True

trace
(pos1, pos2)¶ Index contraction.
INPUT:
pos1
– position of the first index for the contraction (with the convention position=0 for the first slot)pos2
– position of the second index for the contraction
OUTPUT:
 set of components resulting from the (pos1, pos2) contraction
EXAMPLES:
Selfcontraction of a set of components with 2 indices:
sage: from sage.tensor.modules.comp import Components sage: V = VectorSpace(QQ, 3) sage: c = Components(QQ, V.basis(), 2) sage: c[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: c.trace(0,1) 15 sage: c[0,0] + c[1,1] + c[2,2] # check 15
Three selfcontractions of a set of components with 3 indices:
sage: v = Components(QQ, V.basis(), 1) sage: v[:] = (1,2,3) sage: a = c*v ; a 3indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s = a.trace(0,1) ; s # contraction on the first two indices 1index components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s[:] [15, 30, 45] sage: [sum(a[j,j,i] for j in range(3)) for i in range(3)] # check [15, 30, 45] sage: s = a.trace(0,2) ; s[:] # contraction on the first and last indices [28, 32, 36] sage: [sum(a[j,i,j] for j in range(3)) for i in range(3)] # check [28, 32, 36] sage: s = a.trace(1,2) ; s[:] # contraction on the last two indices [12, 24, 36] sage: [sum(a[i,j,j] for j in range(3)) for i in range(3)] # check [12, 24, 36]

class
sage.tensor.modules.comp.
KroneckerDelta
(ring, frame, start_index=0, output_formatter=None)¶ Bases:
sage.tensor.modules.comp.CompFullySym
Kronecker delta \(\delta_{ij}\).
INPUT:
ring
– commutative ring in which each component takes its valueframe
– frame with respect to which the components are defined; whatever typeframe
is, it should have some method__len__()
implemented, so thatlen(frame)
returns the dimension, i.e. the size of a single index rangestart_index
– (default: 0) first value of a single index; accordingly a component index i must obeystart_index <= i <= start_index + dim  1
, wheredim = len(frame)
.output_formatter
– (default:None
) function or unbound method called to format the output of the component access operator[...]
(method__getitem__
);output_formatter
must take 1 or 2 arguments: the first argument must be an instance ofring
and the second one, if any, some format specification
EXAMPLES:
The Kronecker delta on a 3dimensional space:
sage: from sage.tensor.modules.comp import KroneckerDelta sage: V = VectorSpace(QQ,3) sage: d = KroneckerDelta(QQ, V.basis()) ; d Kronecker delta of size 3x3 sage: d[:] [1 0 0] [0 1 0] [0 0 1]
One can read, but not set, the components of a Kronecker delta:
sage: d[1,1] 1 sage: d[1,1] = 2 Traceback (most recent call last): ... TypeError: the components of a Kronecker delta cannot be changed
Examples of use with output formatters:
sage: d = KroneckerDelta(QQ, V.basis(), output_formatter=Rational.numerical_approx) sage: d[:] # default format (53 bits of precision) [ 1.00000000000000 0.000000000000000 0.000000000000000] [0.000000000000000 1.00000000000000 0.000000000000000] [0.000000000000000 0.000000000000000 1.00000000000000] sage: d[:,10] # format = 10 bits of precision [ 1.0 0.00 0.00] [0.00 1.0 0.00] [0.00 0.00 1.0] sage: d = KroneckerDelta(QQ, V.basis(), output_formatter=str) sage: d[:] [['1', '0', '0'], ['0', '1', '0'], ['0', '0', '1']]