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/vector-space 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 non-tensorial 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 (2014-2015): initial version
Joris Vankerschaver (2010): for the idea of storing only the non-zero components as dictionaries, whose keys are the component indices (implemented in the old class
DifferentialForm
; see trac ticket #24444)Marco Mancini (2015) : parallelization of some computations
EXAMPLES:
Set of components with 2 indices on a 3-dimensional 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
2-indices 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
2-indices 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
2-indices 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 \(n-1\), 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 output 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 built-in 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, 2-indices 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 non-zero 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 non-redundant 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:
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 non-tensorial 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 3-dimensional 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 non-redundant and non-zero 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 3-dimensional 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 3-indices 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: 3-indices 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 3-indices 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,..., p-1, other, 0,..., p-1)
(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 4-dimensional 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:
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 non-tensorial 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 3-dimensional 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 non-redundant and non-zero 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 3-dimensional 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 3-indices 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: 3-indices 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 3-indices 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:
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 non-tensorial 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 non-redundant 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 non-symmetric 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 2-indices 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 2-indices 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 non-symmetric 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 2-indices 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 2-indices 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 3-indices components on a 3-dimensional 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 3-indices 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 3-indices 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 3-indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s1 == s True sage: c1 = c.antisymmetrize(1,2) ; c1 3-indices 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: 3-indices 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 4-indices 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 4-indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ], with antisymmetry on the index positions (0, 2), with antisymmetry on the index positions (1, 3) sage: s._antisym # the antisymmetry (0,1,2) has been reduced to (0,2), since 1 is involved in the new antisymmetry (1,3): ((0, 2), (1, 3))
Partial antisymmetrization of 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 non-redundant indices are generated.
OUTPUT:
an iterable index
EXAMPLES:
Indices on a 2-dimensional 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 3-dimensional 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), (1, 0, 0), (2, 0, 0), (0, 0, 1), (1, 0, 1), (2, 0, 1), (0, 0, 2), (1, 0, 2), (2, 0, 2), (0, 1, 1), (1, 1, 1), (2, 1, 1), (0, 1, 2), (1, 1, 2), (2, 1, 2), (0, 2, 2), (1, 2, 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), (1, 0, 1), (2, 0, 1), (0, 0, 2), (1, 0, 2), (2, 0, 2), (0, 1, 2), (1, 1, 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 4-dimensional 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 3-indices components on a 3-dimensional 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 3-indices 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 3-indices 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 3-indices 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 3-indices 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 3-indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s2 == c.symmetrize() True
Symmetrization alters pre-existing 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 3-indices 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: 3-indices 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 4-indices 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 4-indices 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): 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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 4-indices 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:
Self-contraction of symmetric 2-indices 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
Self-contraction of antisymmetric 2-indices 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
Self-contraction of 3-indices components with one symmetry:
sage: v = Components(QQ, V.basis(), 1) sage: v[:] = (-2, 4, -8) sage: c = v*b ; c 3-indices 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 1-index 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 1-index 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 3-indices 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]
Self-contraction of 4-indices components with two symmetries:
sage: c = a*b ; c 4-indices 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 2-indices 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 2-indices 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:
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 non-tensorial 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 3-dimensional 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 2-indices 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 2-indices 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 2-indices components w.r.t. ['a', 'b', 'c']
By default, the indices range from \(0\) to \(n-1\), 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 output 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 built-in 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, 2-indices 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 non-zero 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 2-indices 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 3-indices 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 2-indices 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 2-indices 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 3-indices 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 3-indices 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 3-indices components:
sage: s = c.antisymmetrize(0,1) ; s # antisymmetrization on the first two indices 3-indices 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 3-indices 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 3-indices 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 1-index set of components with a 2-index 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 1-index 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 1-index 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 3-indices components w.r.t. [ (1, 0, 0), (0, 1, 0), (0, 0, 1) ] sage: s = c.contract(1,2, b, 0,1) ; s 1-index 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 3-indices 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 1-index 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 2-indices 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 1-index 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 text-formatted (console mode) or LaTeX-formatted (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 3-indices 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 specified:
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 3-indices 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) # optional - sage.symbolic sage: c[0] = SR.var('t', domain='real') # optional - sage.symbolic sage: c.display('c') # optional - sage.symbolic c_0 = t
- index_generator()#
Generator of indices.
OUTPUT:
an iterable index
EXAMPLES:
Indices on a 3-dimensional 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 just-created 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
- items()#
Return an iterable of
(indices, value)
elements.This may (but is not guaranteed to) suppress zero values.
EXAMPLES:
sage: from sage.tensor.modules.comp import Components, CompWithSym 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: list(c.items()) [((0, 1, 0), -2), ((1, 0, 1), 5), ((1, 1, 1), 3)] sage: c = CompWithSym(ZZ, (ZZ^2).basis(), 3, sym=((1, 2))) sage: c[0,1,0], c[1,0,1], c[1,1,1] = -2, 5, 3 sage: list(c.items()) [((0, 0, 1), -2), ((0, 1, 0), -2), ((1, 0, 1), 5), ((1, 1, 0), 5), ((1, 1, 1), 3)]
- 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 3-dimensional 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 2-indices 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 4-indices set of components:
sage: d = c*c1 ; d 4-indices 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 2-indices 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 2-indices 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 3-indices 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 3-indices 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 3-indices components:
sage: s = c.symmetrize(0,1) ; s # symmetrization on the first two indices 3-indices 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 3-indices 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 3-indices 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:
Self-contraction 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 self-contractions 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 3-indices 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 1-index 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:
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 3-dimensional 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']]