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 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 Issue #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)
]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ,Integer(3))
>>> basis = V.basis() ; basis
[
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> c = Components(QQ, basis, Integer(2)) ; c
2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ,3)
basis = V.basis() ; basis
c = Components(QQ, basis, 2) ; c

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']
>>> from sage.all import *
>>> basis1 = V.gens() ; basis1
((1, 0, 0), (0, 1, 0), (0, 0, 1))
>>> c1 = Components(QQ, basis1, Integer(2)) ; c1
2-indices components w.r.t. ((1, 0, 0), (0, 1, 0), (0, 0, 1))
>>> basis2 = ['a', 'b' , 'c']
>>> c2 = Components(QQ, basis2, Integer(2)) ; c2
2-indices components w.r.t. ['a', 'b', 'c']
basis1 = V.gens() ; basis1
c1 = Components(QQ, basis1, 2) ; c1
basis2 = ['a', 'b' , 'c']
c2 = Components(QQ, basis2, 2) ; c2

A just created set of components is initialized to zero:

sage: c.is_zero()
True
sage: c == 0
True
>>> from sage.all import *
>>> c.is_zero()
True
>>> c == Integer(0)
True
c.is_zero()
c == 0

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]
>>> from sage.all import *
>>> c[:]
[0 0 0]
[0 0 0]
[0 0 0]
c[:]

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
>>> from sage.all import *
>>> c[Integer(1),Integer(2)] = -Integer(3)
>>> c[:]
[ 0  0  0]
[ 0  0 -3]
[ 0  0  0]
>>> v = Components(QQ, basis, Integer(1))
>>> v[:]
[0, 0, 0]
>>> v[Integer(0)]
0
>>> v[:] = (-Integer(1),Integer(3),Integer(2))
>>> v[:]
[-1, 3, 2]
>>> v[Integer(0)]
-1
c[1,2] = -3
c[:]
v = Components(QQ, basis, 1)
v[:]
v[0]
v[:] = (-1,3,2)
v[:]
v[0]

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
>>> from sage.all import *
>>> matrix(c)
[ 0  0  0]
[ 0  0 -3]
[ 0  0  0]
>>> matrix(c).parent()
Full MatrixSpace of 3 by 3 dense matrices over Rational Field
matrix(c)
matrix(c).parent()

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)
>>> from sage.all import *
>>> v1 = Components(QQ, basis, Integer(1), start_index=Integer(1))
>>> v1[:]
[0, 0, 0]
>>> v1[Integer(0)]
Traceback (most recent call last):
...
IndexError: index out of range: 0 not in [1, 3]
>>> v1[Integer(1)]
0
>>> v1[:] = v[:]  # list copy of all components
>>> v1[:]
[-1, 3, 2]
>>> v1[Integer(1)], v1[Integer(2)], v1[Integer(3)]
(-1, 3, 2)
>>> v[Integer(0)], v[Integer(1)], v[Integer(2)]
(-1, 3, 2)
v1 = Components(QQ, basis, 1, start_index=1)
v1[:]
v1[0]
v1[1]
v1[:] = v[:]  # list copy of all components
v1[:]
v1[1], v1[2], v1[3]
v[0], v[1], v[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
>>> from sage.all import *
>>> a = Components(QQ, basis, Integer(2), output_formatter=Rational.numerical_approx)
>>> a[Integer(1),Integer(2)] = Integer(1)/Integer(3)
>>> a[Integer(1),Integer(2)]
0.333333333333333
a = Components(QQ, basis, 2, output_formatter=Rational.numerical_approx)
a[1,2] = 1/3
a[1,2]

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
>>> from sage.all import *
>>> a[Integer(1),Integer(2),Integer(10)] # here the format is 10, for 10 bits of precision
0.33
>>> a[Integer(1),Integer(2),Integer(100)]
0.33333333333333333333333333333
a[1,2,10] # here the format is 10, for 10 bits of precision
a[1,2,100]

The raw (unformatted) components are then accessed by the double bracket operator:

sage: a[[1,2]]
1/3
>>> from sage.all import *
>>> a[[Integer(1),Integer(2)]]
1/3
a[[1,2]]

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)
>>> from sage.all import *
>>> c[Integer(1),Integer(2)] = Integer(1)/Integer(3)
>>> c[Integer(1),Integer(2)], c[[Integer(1),Integer(2)]]
(1/3, 1/3)
c[1,2] = 1/3
c[1,2], c[[1,2]]

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]
>>> from sage.all import *
>>> a[:]
[0.000000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000 0.000000000000000 0.333333333333333]
[0.000000000000000 0.000000000000000 0.000000000000000]
>>> a[:,Integer(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]
a[:]
a[:,10] # with a format different from the default one (53 bits)

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]
>>> from sage.all import *
>>> a[[slice(None)]]
[  0   0   0]
[  0   0 1/3]
[  0   0   0]
a[[slice(None)]]

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']
>>> from sage.all import *
>>> b = Components(QQ, V.basis(), Integer(1), output_formatter=str)
>>> b[:] = (Integer(1), Integer(0), -Integer(4))
>>> b[:]
['1', '0', '-4']
b = Components(QQ, V.basis(), 1, output_formatter=str)
b[:] = (1, 0, -4)
b[:]

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']]
>>> from sage.all import *
>>> b = Components(QQ, basis, Integer(2), output_formatter=str)
>>> b[Integer(0),Integer(1)] = Integer(1)/Integer(3)
>>> b[:]
[['0', '1/3', '0'], ['0', '0', '0'], ['0', '0', '0']]
b = Components(QQ, basis, 2, output_formatter=str)
b[0,1] = 1/3
b[:]

But unformatted outputs still are:

sage: b[[slice(None)]]
[  0 1/3   0]
[  0   0   0]
[  0   0   0]
>>> from sage.all import *
>>> b[[slice(None)]]
[  0 1/3   0]
[  0   0   0]
[  0   0   0]
b[[slice(None)]]

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}
>>> from sage.all import *
>>> a[:]
[0.000000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000 0.000000000000000 0.333333333333333]
[0.000000000000000 0.000000000000000 0.000000000000000]
>>> a._comp
{(1, 2): 1/3}
>>> v[:] = (-Integer(1), Integer(0), Integer(3))
>>> v._comp  # random output order of the component dictionary
{(0,): -1, (2,): 3}
a[:]
a._comp
v[:] = (-1, 0, 3)
v._comp  # random output order of the component dictionary

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}
>>> from sage.all import *
>>> from sage.tensor.modules.comp import CompFullyAntiSym
>>> c = CompFullyAntiSym(QQ, basis, Integer(2))
>>> c[Integer(0),Integer(1)] = Integer(3)
>>> c[:]
[ 0  3  0]
[-3  0  0]
[ 0  0  0]
>>> c._comp
{(0, 1): 3}
from sage.tensor.modules.comp import CompFullyAntiSym
c = CompFullyAntiSym(QQ, basis, 2)
c[0,1] = 3
c[:]
c._comp
class sage.tensor.modules.comp.CompFullyAntiSym(ring, frame, nb_indices, start_index=0, output_formatter=None)[source]

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 value

  • frame – frame with respect to which the components are defined; whatever type frame is, it should have some method __len__() implemented, so that len(frame) returns the dimension, i.e. the size of a single index range

  • nb_indices – number of indices labeling the components

  • start_index – (default: 0) first value of a single index; accordingly a component index i must obey start_index <= i <= start_index + dim - 1, where dim = 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 of ring 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]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import CompWithSym, CompFullyAntiSym
>>> V = VectorSpace(QQ, Integer(3))
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> c[Integer(0),Integer(1)], c[Integer(0),Integer(2)], c[Integer(1),Integer(2)] = Integer(3), Integer(1)/Integer(2), -Integer(1)
>>> c[:]  # note that all components have been set according to the antisymmetry
[   0    3  1/2]
[  -3    0   -1]
[-1/2    1    0]
from sage.tensor.modules.comp import CompWithSym, CompFullyAntiSym
V = VectorSpace(QQ, 3)
c = CompFullyAntiSym(QQ, V.basis(), 2)
c[0,1], c[0,2], c[1,2] = 3, 1/2, -1
c[:]  # note that all components have been set according to the antisymmetry

Internally, only non-redundant 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}
>>> from sage.all import *
>>> c._comp # random output order of the component dictionary
{(0, 1): 3, (0, 2): 1/2, (1, 2): -1}
c._comp # random output order of the component dictionary

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]
>>> from sage.all import *
>>> c1 = CompFullyAntiSym(QQ, V.basis(), Integer(2), start_index=Integer(1))
>>> c1[Integer(1),Integer(2)], c1[Integer(1),Integer(3)], c1[Integer(2),Integer(3)] = Integer(3), Integer(1)/Integer(2), -Integer(1)
>>> c1[:]
[   0    3  1/2]
[  -3    0   -1]
[-1/2    1    0]
c1 = CompFullyAntiSym(QQ, V.basis(), 2, start_index=1)
c1[1,2], c1[1,3], c1[2,3] = 3, 1/2, -1
c1[:]

The values stored in c and c1 are equal:

sage: c1[:] == c[:]
True
>>> from sage.all import *
>>> c1[:] == c[:]
True
c1[:] == c[:]

but not c and c1, since their starting indices differ:

sage: c1 == c
False
>>> from sage.all import *
>>> c1 == c
False
c1 == c

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]]]
>>> from sage.all import *
>>> a = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> a[Integer(0),Integer(1),Integer(2)] = Integer(3)  # the only independent component in dimension 3
>>> 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]]]
a = CompFullyAntiSym(QQ, V.basis(), 3)
a[0,1,2] = 3  # the only independent component in dimension 3
a[:]

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
>>> from sage.all import *
>>> a[Integer(0),Integer(1),Integer(0)] = Integer(4)
Traceback (most recent call last):
...
ValueError: by antisymmetry, the component cannot have a nonzero value for the indices (0, 1, 0)
>>> a[Integer(0),Integer(1),Integer(0)] = Integer(0)   # OK
>>> a[Integer(2),Integer(0),Integer(1)] = Integer(3)   # OK
a[0,1,0] = 4
a[0,1,0] = 0   # OK
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]]])
>>> from sage.all import *
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> b[Integer(0),Integer(1),Integer(2)] = -Integer(4)
>>> s = a + Integer(2)*b ; s
Fully antisymmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]])
b = CompFullyAntiSym(QQ, V.basis(), 3)
b[0,1,2] = -4
s = a + 2*b ; s
a[:], b[:], s[:]

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
>>> from sage.all import *
>>> b1 = CompWithSym(QQ, V.basis(), Integer(3), antisym=(Integer(0),Integer(1)))  # b1 has only antisymmetry on index positions (0,1)
>>> b1[Integer(0),Integer(1),Integer(2)] = -Integer(4)
>>> s = a + Integer(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)
>>> 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]]])
>>> s = Integer(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)
>>> Integer(2)*b1 + a == a + Integer(2)*b1
True
b1 = CompWithSym(QQ, V.basis(), 3, antisym=(0,1))  # b1 has only antisymmetry on index positions (0,1)
b1[0,1,2] = -4
s = a + 2*b1 ; s  # the result has the same symmetry as b1:
a[:], b1[:], s[:]
s = 2*b1 + a ; s
2*b1 + a == a + 2*b1
interior_product(other)[source]

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 of other, assuming that the number \(q\) of indices of other obeys \(q\geq p\).

Note

self.interior_product(other) yields the same result as self.contract(0,..., p-1, other, 0,..., p-1) (cf. contract()), but interior_product is more efficient, the antisymmetry of self being not used to reduce the computation in contract().

INPUT:

  • other – fully antisymmetric components defined on the same frame as self and with a number of indices at least equal to that of self

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 of other

EXAMPLES:

Interior product of a set of components a with p indices with a set of components b with q indices on a 4-dimensional vector space.

Case p=2 and q=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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import CompFullyAntiSym
>>> V = VectorSpace(QQ, Integer(4))
>>> a = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> a[Integer(0),Integer(1)], a[Integer(0),Integer(2)], a[Integer(0),Integer(3)] = -Integer(2), Integer(4), Integer(3)
>>> a[Integer(1),Integer(2)], a[Integer(1),Integer(3)], a[Integer(2),Integer(3)] = Integer(5), -Integer(3), Integer(1)
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> b[Integer(0),Integer(1)], b[Integer(0),Integer(2)], b[Integer(0),Integer(3)] = Integer(3), -Integer(4), Integer(2)
>>> b[Integer(1),Integer(2)], b[Integer(1),Integer(3)], b[Integer(2),Integer(3)] = Integer(2), Integer(5), Integer(1)
>>> c = a.interior_product(b)
>>> c
-40
>>> c == a.contract(Integer(0), Integer(1), b, Integer(0), Integer(1))
True
from sage.tensor.modules.comp import CompFullyAntiSym
V = VectorSpace(QQ, 4)
a = CompFullyAntiSym(QQ, V.basis(), 2)
a[0,1], a[0,2], a[0,3] = -2, 4, 3
a[1,2], a[1,3], a[2,3] = 5, -3, 1
b = CompFullyAntiSym(QQ, V.basis(), 2)
b[0,1], b[0,2], b[0,3] = 3, -4, 2
b[1,2], b[1,3], b[2,3] = 2, 5, 1
c = a.interior_product(b)
c
c == a.contract(0, 1, b, 0, 1)

Case p=2 and q=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
>>> from sage.all import *
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> b[Integer(0),Integer(1),Integer(2)], b[Integer(0),Integer(1),Integer(3)], b[Integer(0),Integer(2),Integer(3)], b[Integer(1),Integer(2),Integer(3)] = Integer(3), -Integer(4), Integer(2), Integer(5)
>>> c = a.interior_product(b)
>>> c[:]
[58, 10, 6, 82]
>>> c == a.contract(Integer(0), Integer(1), b, Integer(0), Integer(1))
True
b = CompFullyAntiSym(QQ, V.basis(), 3)
b[0,1,2], b[0,1,3], b[0,2,3], b[1,2,3] = 3, -4, 2, 5
c = a.interior_product(b)
c[:]
c == a.contract(0, 1, b, 0, 1)

Case p=2 and q=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
>>> from sage.all import *
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(4))
>>> b[Integer(0),Integer(1),Integer(2),Integer(3)] = Integer(5)
>>> c = a.interior_product(b)
>>> c[:]
[  0  10  30  50]
[-10   0  30 -40]
[-30 -30   0 -20]
[-50  40  20   0]
>>> c == a.contract(Integer(0), Integer(1), b, Integer(0), Integer(1))
True
b = CompFullyAntiSym(QQ, V.basis(), 4)
b[0,1,2,3] = 5
c = a.interior_product(b)
c[:]
c == a.contract(0, 1, b, 0, 1)

Case p=3 and q=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
>>> from sage.all import *
>>> a = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> a[Integer(0),Integer(1),Integer(2)], a[Integer(0),Integer(1),Integer(3)], a[Integer(0),Integer(2),Integer(3)], a[Integer(1),Integer(2),Integer(3)] = Integer(2), -Integer(1), Integer(3), Integer(5)
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> b[Integer(0),Integer(1),Integer(2)], b[Integer(0),Integer(1),Integer(3)], b[Integer(0),Integer(2),Integer(3)], b[Integer(1),Integer(2),Integer(3)] = -Integer(2), Integer(1), Integer(4), Integer(2)
>>> c = a.interior_product(b)
>>> c
102
>>> c == a.contract(Integer(0), Integer(1), Integer(2), b, Integer(0), Integer(1), Integer(2))
True
a = CompFullyAntiSym(QQ, V.basis(), 3)
a[0,1,2], a[0,1,3], a[0,2,3], a[1,2,3] = 2, -1, 3, 5
b = CompFullyAntiSym(QQ, V.basis(), 3)
b[0,1,2], b[0,1,3], b[0,2,3], b[1,2,3] = -2, 1, 4, 2
c = a.interior_product(b)
c
c == a.contract(0, 1, 2, b, 0, 1, 2)

Case p=3 and q=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
>>> from sage.all import *
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(4))
>>> b[Integer(0),Integer(1),Integer(2),Integer(3)] = Integer(5)
>>> c = a.interior_product(b)
>>> c[:]
[-150, 90, 30, 60]
>>> c == a.contract(Integer(0), Integer(1), Integer(2), b, Integer(0), Integer(1), Integer(2))
True
b = CompFullyAntiSym(QQ, V.basis(), 4)
b[0,1,2,3] = 5
c = a.interior_product(b)
c[:]
c == a.contract(0, 1, 2, b, 0, 1, 2)

Case p=4 and q=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
>>> from sage.all import *
>>> a = CompFullyAntiSym(QQ, V.basis(), Integer(4))
>>> a[Integer(0),Integer(1),Integer(2),Integer(3)] = Integer(3)
>>> c = a.interior_product(b)
>>> c
360
>>> c == a.contract(Integer(0), Integer(1), Integer(2), Integer(3), b, Integer(0), Integer(1), Integer(2), Integer(3))
True
a = CompFullyAntiSym(QQ, V.basis(), 4)
a[0,1,2,3] = 3
c = a.interior_product(b)
c
c == a.contract(0, 1, 2, 3, b, 0, 1, 2, 3)
class sage.tensor.modules.comp.CompFullySym(ring, frame, nb_indices, start_index=0, output_formatter=None)[source]

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 value

  • frame – frame with respect to which the components are defined; whatever type frame is, it should have some method __len__() implemented, so that len(frame) returns the dimension, i.e. the size of a single index range

  • nb_indices – number of indices labeling the components

  • start_index – (default: 0) first value of a single index; accordingly a component index i must obey start_index <= i <= start_index + dim - 1, where dim = 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 of ring 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]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import CompFullySym, CompWithSym
>>> V = VectorSpace(QQ, Integer(3))
>>> c = CompFullySym(QQ, V.basis(), Integer(2))
>>> c[Integer(0),Integer(0)], c[Integer(0),Integer(1)], c[Integer(1),Integer(2)] = Integer(1), -Integer(2), Integer(3)
>>> 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]
from sage.tensor.modules.comp import CompFullySym, CompWithSym
V = VectorSpace(QQ, 3)
c = CompFullySym(QQ, V.basis(), 2)
c[0,0], c[0,1], c[1,2] = 1, -2, 3
c[:] # note that c[1,0] and c[2,1] have been updated automatically (by symmetry)

Internally, only non-redundant and nonzero components are stored:

sage: c._comp  # random output order of the component dictionary
{(0, 0): 1, (0, 1): -2, (1, 2): 3}
>>> from sage.all import *
>>> c._comp  # random output order of the component dictionary
{(0, 0): 1, (0, 1): -2, (1, 2): 3}
c._comp  # random output order of the component dictionary

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]
>>> from sage.all import *
>>> c1 = CompFullySym(QQ, V.basis(), Integer(2), start_index=Integer(1))
>>> c1[Integer(1),Integer(1)], c1[Integer(1),Integer(2)], c1[Integer(2),Integer(3)] = Integer(1), -Integer(2), Integer(3)
>>> c1[:]
[ 1 -2  0]
[-2  0  3]
[ 0  3  0]
c1 = CompFullySym(QQ, V.basis(), 2, start_index=1)
c1[1,1], c1[1,2], c1[2,3] = 1, -2, 3
c1[:]

The values stored in c and c1 are equal:

sage: c1[:] == c[:]
True
>>> from sage.all import *
>>> c1[:] == c[:]
True
c1[:] == c[:]

but not c and c1, since their starting indices differ:

sage: c1 == c
False
>>> from sage.all import *
>>> c1 == c
False
c1 == c

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]]]
>>> from sage.all import *
>>> a = CompFullySym(QQ, V.basis(), Integer(3))
>>> a[Integer(0),Integer(1),Integer(2)] = Integer(3)
>>> 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]]]
>>> a[Integer(0),Integer(1),Integer(0)] = Integer(4)
>>> 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]]]
a = CompFullySym(QQ, V.basis(), 3)
a[0,1,2] = 3
a[:]
a[0,1,0] = 4
a[:]

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]]])
>>> from sage.all import *
>>> b = CompFullySym(QQ, V.basis(), Integer(3))
>>> b[Integer(0),Integer(0),Integer(0)], b[Integer(0),Integer(1),Integer(0)], b[Integer(1),Integer(0),Integer(2)], b[Integer(1),Integer(2),Integer(2)] = -Integer(2), Integer(3), Integer(1), -Integer(5)
>>> s = a + Integer(2)*b ; s
Fully symmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]])
b = CompFullySym(QQ, V.basis(), 3)
b[0,0,0], b[0,1,0], b[1,0,2], b[1,2,2] = -2, 3, 1, -5
s = a + 2*b ; s
a[:], b[:], s[:]

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
>>> from sage.all import *
>>> b1 = CompWithSym(QQ, V.basis(), Integer(3), sym=(Integer(0),Integer(1)))  # b1 has only symmetry on index positions (0,1)
>>> b1[Integer(0),Integer(0),Integer(0)], b1[Integer(0),Integer(1),Integer(0)], b1[Integer(1),Integer(0),Integer(2)], b1[Integer(1),Integer(2),Integer(2)] = -Integer(2), Integer(3), Integer(1), -Integer(5)
>>> s = a + Integer(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)
>>> 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]]])
>>> s = Integer(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)
>>> Integer(2)*b1 + a == a + Integer(2)*b1
True
b1 = CompWithSym(QQ, V.basis(), 3, sym=(0,1))  # b1 has only symmetry on index positions (0,1)
b1[0,0,0], b1[0,1,0], b1[1,0,2], b1[1,2,2] = -2, 3, 1, -5
s = a + 2*b1 ; s  # the result has the same symmetry as b1:
a[:], b1[:], s[:]
s = 2*b1 + a ; s
2*b1 + a == a + 2*b1
class sage.tensor.modules.comp.CompWithSym(ring, frame, nb_indices, start_index=0, output_formatter=None, sym=None, antisym=None)[source]

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 are

INPUT:

  • ring – commutative ring in which each component takes its value

  • frame – frame with respect to which the components are defined; whatever type frame is, it should have some method __len__() implemented, so that len(frame) returns the dimension, i.e. the size of a single index range

  • nb_indices – number of indices labeling the components

  • start_index – (default: 0) first value of a single index; accordingly a component index i must obey start_index <= i <= start_index + dim - 1, where dim = 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 of ring 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 convention position=0 for the first slot; for instance:

    • sym = (0, 1) for a symmetry between the 1st and 2nd indices

    • sym = [(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 for sym

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]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components, CompWithSym
>>> V = VectorSpace(QQ,Integer(3))
>>> c = CompWithSym(QQ, V.basis(), Integer(2), sym=(Integer(0),Integer(1)))  # for demonstration only: it is preferable to use CompFullySym in this case
>>> c[Integer(0),Integer(1)] = Integer(3)
>>> c[:]  # note that c[1,0] has been set automatically
[0 3 0]
[3 0 0]
[0 0 0]
from sage.tensor.modules.comp import Components, CompWithSym
V = VectorSpace(QQ,3)
c = CompWithSym(QQ, V.basis(), 2, sym=(0,1))  # for demonstration only: it is preferable to use CompFullySym in this case
c[0,1] = 3
c[:]  # note that c[1,0] has been set automatically

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]
>>> from sage.all import *
>>> c = CompWithSym(QQ, V.basis(), Integer(2), antisym=(Integer(0),Integer(1)))  # for demonstration only: it is preferable to use CompFullyAntiSym in this case
>>> c[Integer(0),Integer(1)] = Integer(3)
>>> c[:]  # note that c[1,0] has been set automatically
[ 0  3  0]
[-3  0  0]
[ 0  0  0]
c = CompWithSym(QQ, V.basis(), 2, antisym=(0,1))  # for demonstration only: it is preferable to use CompFullyAntiSym in this case
c[0,1] = 3
c[:]  # note that c[1,0] has been set automatically

Internally, only non-redundant components are stored:

sage: c._comp
{(0, 1): 3}
>>> from sage.all import *
>>> c._comp
{(0, 1): 3}
c._comp

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
>>> from sage.all import *
>>> c = CompWithSym(QQ, V.basis(), Integer(6), sym=(Integer(0),Integer(1),Integer(5)), antisym=(Integer(2),Integer(4)))
>>> c[Integer(0),Integer(1),Integer(2),Integer(0),Integer(1),Integer(2)] = Integer(3)
>>> c[Integer(1),Integer(0),Integer(2),Integer(0),Integer(1),Integer(2)]  # symmetry between indices in position 0 and 1
3
>>> c[Integer(2),Integer(1),Integer(2),Integer(0),Integer(1),Integer(0)]  # symmetry between indices in position 0 and 5
3
>>> c[Integer(0),Integer(2),Integer(2),Integer(0),Integer(1),Integer(1)]  # symmetry between indices in position 1 and 5
3
>>> c[Integer(0),Integer(1),Integer(1),Integer(0),Integer(2),Integer(2)]  # antisymmetry between indices in position 2 and 4
-3
c = CompWithSym(QQ, V.basis(), 6, sym=(0,1,5), antisym=(2,4))
c[0,1,2,0,1,2] = 3
c[1,0,2,0,1,2]  # symmetry between indices in position 0 and 1
c[2,1,2,0,1,0]  # symmetry between indices in position 0 and 5
c[0,2,2,0,1,1]  # symmetry between indices in position 1 and 5
c[0,1,1,0,2,2]  # antisymmetry between indices in position 2 and 4

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
>>> from sage.all import *
>>> c = CompWithSym(QQ, V.basis(), Integer(4), antisym=[(Integer(0),Integer(1)),(Integer(2),Integer(3))])
>>> c[Integer(0),Integer(1),Integer(0),Integer(1)] = Integer(3)
>>> c[Integer(1),Integer(0),Integer(0),Integer(1)]  # antisymmetry on the first pair of indices
-3
>>> c[Integer(0),Integer(1),Integer(1),Integer(0)]  # antisymmetry on the second pair of indices
-3
>>> c[Integer(1),Integer(0),Integer(1),Integer(0)]  # consequence of the above
3
c = CompWithSym(QQ, V.basis(), 4, antisym=[(0,1),(2,3)])
c[0,1,0,1] = 3
c[1,0,0,1]  # antisymmetry on the first pair of indices
c[0,1,1,0]  # antisymmetry on the second pair of indices
c[1,0,1,0]  # consequence of the above

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
>>> from sage.all import *
>>> V = VectorSpace(QQ, Integer(3))
>>> a = Components(QQ, V.basis(), Integer(2))
>>> a[:] = [[Integer(1),-Integer(2),Integer(3)], [Integer(4),Integer(5),-Integer(6)], [-Integer(7),Integer(8),Integer(9)]]
>>> b = CompWithSym(QQ, V.basis(), Integer(2), sym=(Integer(0),Integer(1)))  # for demonstration only: it is preferable to declare b = CompFullySym(QQ, V.basis(), 2)
>>> b[Integer(0),Integer(0)], b[Integer(0),Integer(1)], b[Integer(0),Integer(2)] = Integer(1), Integer(2), Integer(3)
>>> b[Integer(1),Integer(1)], b[Integer(1),Integer(2)] = Integer(5), Integer(7)
>>> b[Integer(2),Integer(2)] = Integer(11)
>>> s = a + b ; s
2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]
)
>>> a + b == b + a
True
V = VectorSpace(QQ, 3)
a = Components(QQ, V.basis(), 2)
a[:] = [[1,-2,3], [4,5,-6], [-7,8,9]]
b = CompWithSym(QQ, V.basis(), 2, sym=(0,1))  # for demonstration only: it is preferable to declare b = CompFullySym(QQ, V.basis(), 2)
b[0,0], b[0,1], b[0,2] = 1, 2, 3
b[1,1], b[1,2] = 5, 7
b[2,2] = 11
s = a + b ; s
a[:], b[:], s[:]
a + b == b + a

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
>>> from sage.all import *
>>> c = CompWithSym(QQ, V.basis(), Integer(2), sym=(Integer(0),Integer(1))) # for demonstration only: it is preferable to declare c = CompFullySym(QQ, V.basis(), 2)
>>> c[Integer(0),Integer(0)], c[Integer(0),Integer(1)], c[Integer(0),Integer(2)] = -Integer(4), Integer(7), -Integer(8)
>>> c[Integer(1),Integer(1)], c[Integer(1),Integer(2)] = Integer(2), -Integer(4)
>>> c[Integer(2),Integer(2)] = Integer(2)
>>> 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)
>>> 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]
)
>>> b + c == c + b
True
c = CompWithSym(QQ, V.basis(), 2, sym=(0,1)) # for demonstration only: it is preferable to declare c = CompFullySym(QQ, V.basis(), 2)
c[0,0], c[0,1], c[0,2] = -4, 7, -8
c[1,1], c[1,2] = 2, -4
c[2,2] = 2
s = b + c ; s
b[:], c[:], s[:]
b + c == c + b

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
>>> from sage.all import *
>>> bn = Components(QQ, V.basis(), Integer(2))
>>> bn[:] = b[:]
>>> bn == b
True
>>> cn = Components(QQ, V.basis(), Integer(2))
>>> cn[:] = c[:]
>>> cn == c
True
>>> bn + cn == b + c
True
bn = Components(QQ, V.basis(), 2)
bn[:] = b[:]
bn == b
cn = Components(QQ, V.basis(), 2)
cn[:] = c[:]
cn == c
bn + cn == b + c

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
>>> from sage.all import *
>>> d = CompWithSym(QQ, V.basis(), Integer(2), antisym=(Integer(0),Integer(1)))  # for demonstration only: it is preferable to declare d = CompFullyAntiSym(QQ, V.basis(), 2)
>>> d[Integer(0),Integer(1)], d[Integer(0),Integer(2)], d[Integer(1),Integer(2)] = Integer(4), -Integer(1), Integer(3)
>>> s = a + d ; s
2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]
)
>>> d + a == a + d
True
d = CompWithSym(QQ, V.basis(), 2, antisym=(0,1))  # for demonstration only: it is preferable to declare d = CompFullyAntiSym(QQ, V.basis(), 2)
d[0,1], d[0,2], d[1,2] = 4, -1, 3
s = a + d ; s
a[:], d[:], s[:]
d + a == a + d

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
>>> from sage.all import *
>>> e = CompWithSym(QQ, V.basis(), Integer(2), antisym=(Integer(0),Integer(1)))  # for demonstration only: it is preferable to declare e = CompFullyAntiSym(QQ, V.basis(), 2)
>>> e[Integer(0),Integer(1)], e[Integer(0),Integer(2)], e[Integer(1),Integer(2)] = Integer(2), Integer(3), -Integer(1)
>>> 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)
>>> 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]
)
>>> e + d == d + e
True
e = CompWithSym(QQ, V.basis(), 2, antisym=(0,1))  # for demonstration only: it is preferable to declare e = CompFullyAntiSym(QQ, V.basis(), 2)
e[0,1], e[0,2], e[1,2] = 2, 3, -1
s = d + e ; s
d[:], e[:], s[:]
e + d == d + e
antisymmetrize(*pos)[source]

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 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]]])
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components, CompWithSym,  CompFullySym, CompFullyAntiSym
>>> V = VectorSpace(QQ, Integer(3))
>>> a = Components(QQ, V.basis(), Integer(1))
>>> a[:] = (-Integer(2),Integer(1),Integer(3))
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> b[Integer(0),Integer(1)], b[Integer(0),Integer(2)], b[Integer(1),Integer(2)] = (Integer(4),Integer(1),Integer(2))
>>> 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)
>>> s = c.antisymmetrize() ; s
Fully antisymmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]])
from sage.tensor.modules.comp import Components, CompWithSym, \
 CompFullySym, CompFullyAntiSym
V = VectorSpace(QQ, 3)
a = Components(QQ, V.basis(), 1)
a[:] = (-2,1,3)
b = CompFullyAntiSym(QQ, V.basis(), 2)
b[0,1], b[0,2], b[1,2] = (4,1,2)
c = a*b ; c   # tensor product of a by b
s = c.antisymmetrize() ; s
c[:], s[:]

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
>>> from sage.all import *
>>> 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])/Integer(6)
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
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))

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
>>> from sage.all import *
>>> s1 = s.antisymmetrize(Integer(1),Integer(2)) ; s1
Fully antisymmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s1 == s
True
>>> c1 = c.antisymmetrize(Integer(1),Integer(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)
>>> c1 == c
True
s1 = s.antisymmetrize(1,2) ; s1
s1 == s
c1 = c.antisymmetrize(1,2) ; c1
c1 == c

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))
>>> from sage.all import *
>>> c2 = c.antisymmetrize(Integer(0),Integer(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)
>>> c2 == c
False
>>> 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)
>>> s = c.antisymmetrize(Integer(1),Integer(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)
>>> 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))
c2 = c.antisymmetrize(0,1) ; c2  # the antisymmetry (2,3) is lost:
c2 == c
c = s*a ; c
s = c.antisymmetrize(1,3) ; s
s._antisym  # the antisymmetry (0,1,2) has been reduced to (0,2), since 1 is involved in the new antisymmetry (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)
>>> from sage.all import *
>>> a = CompFullySym(QQ, V.basis(), Integer(2))
>>> a[:] = [[-Integer(2),Integer(1),Integer(3)], [Integer(1),Integer(0),-Integer(5)], [Integer(3),-Integer(5),Integer(4)]]
>>> b = Components(QQ, V.basis(), Integer(2))
>>> b[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(5),Integer(7),Integer(11)], [Integer(13),Integer(17),Integer(19)]]
>>> 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)
>>> s = c.antisymmetrize(Integer(2),Integer(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)
a = CompFullySym(QQ, V.basis(), 2)
a[:] = [[-2,1,3], [1,0,-5], [3,-5,4]]
b = Components(QQ, V.basis(), 2)
b[:] = [[1,2,3], [5,7,11], [13,17,19]]
c = a*b ; c
s = c.antisymmetrize(2,3) ; s

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
>>> from sage.all import *
>>> all(s[Integer(2),Integer(2),i,j] == (c[Integer(2),Integer(2),i,j] - c[Integer(2),Integer(2),j,i])/Integer(2)
...     for i in range(Integer(3)) for j in range(i,Integer(3)))
True
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))

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
>>> from sage.all import *
>>> s = c.antisymmetrize() ; s
Fully antisymmetric 4-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s == Integer(0)
True
s = c.antisymmetrize() ; s
s == 0

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
>>> from sage.all import *
>>> s = c.antisymmetrize(Integer(0),Integer(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)
>>> s == Integer(0)
True
s = c.antisymmetrize(0,1) ; s
s == 0

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
>>> from sage.all import *
>>> s = c.antisymmetrize(Integer(0),Integer(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)
>>> s != Integer(0)
True
>>> s[Integer(0),Integer(1),Integer(2),Integer(1)]
27/2
>>> s[Integer(1),Integer(0),Integer(2),Integer(1)]  # the symmetry (0,1) is lost
-2
>>> s[Integer(2),Integer(1),Integer(0),Integer(1)]  # the antisymmetry (0,2) holds
-27/2
s = c.antisymmetrize(0,2) ; s
s != 0
s[0,1,2,1]
s[1,0,2,1]  # the symmetry (0,1) is lost
s[2,1,0,1]  # the antisymmetry (0,2) holds
non_redundant_index_generator()[source]

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)]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components, CompWithSym,  CompFullySym, CompFullyAntiSym
>>> V = VectorSpace(QQ, Integer(2))
>>> c = CompFullySym(QQ, V.basis(), Integer(2))
>>> list(c.non_redundant_index_generator())
[(0, 0), (0, 1), (1, 1)]
>>> c = CompFullySym(QQ, V.basis(), Integer(2), start_index=Integer(1))
>>> list(c.non_redundant_index_generator())
[(1, 1), (1, 2), (2, 2)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> list(c.non_redundant_index_generator())
[(0, 1)]
from sage.tensor.modules.comp import Components, CompWithSym, \
 CompFullySym, CompFullyAntiSym
V = VectorSpace(QQ, 2)
c = CompFullySym(QQ, V.basis(), 2)
list(c.non_redundant_index_generator())
c = CompFullySym(QQ, V.basis(), 2, start_index=1)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 2)
list(c.non_redundant_index_generator())

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)]
>>> from sage.all import *
>>> V = VectorSpace(QQ, Integer(3))
>>> c = CompFullySym(QQ, V.basis(), Integer(2))
>>> list(c.non_redundant_index_generator())
[(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]
>>> c = CompFullySym(QQ, V.basis(), Integer(2), start_index=Integer(1))
>>> list(c.non_redundant_index_generator())
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> list(c.non_redundant_index_generator())
[(0, 1), (0, 2), (1, 2)]
>>> c = CompWithSym(QQ, V.basis(), Integer(3), sym=(Integer(1),Integer(2)))  # symmetry on the last two indices
>>> 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)]
>>> c = CompWithSym(QQ, V.basis(), Integer(3), antisym=(Integer(1),Integer(2)))  # antisymmetry on the last two indices
>>> 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)]
>>> c = CompFullySym(QQ, V.basis(), Integer(3))
>>> 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)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> list(c.non_redundant_index_generator())
[(0, 1, 2)]
V = VectorSpace(QQ, 3)
c = CompFullySym(QQ, V.basis(), 2)
list(c.non_redundant_index_generator())
c = CompFullySym(QQ, V.basis(), 2, start_index=1)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 2)
list(c.non_redundant_index_generator())
c = CompWithSym(QQ, V.basis(), 3, sym=(1,2))  # symmetry on the last two indices
list(c.non_redundant_index_generator())
c = CompWithSym(QQ, V.basis(), 3, antisym=(1,2))  # antisymmetry on the last two indices
list(c.non_redundant_index_generator())
c = CompFullySym(QQ, V.basis(), 3)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 3)
list(c.non_redundant_index_generator())

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)
[]
>>> from sage.all import *
>>> V = VectorSpace(QQ, Integer(4))
>>> c = Components(QQ, V.basis(), Integer(1))
>>> list(c.non_redundant_index_generator())
[(0,), (1,), (2,), (3,)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> list(c.non_redundant_index_generator())
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> list(c.non_redundant_index_generator())
[(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(4))
>>> list(c.non_redundant_index_generator())
[(0, 1, 2, 3)]
>>> c = CompFullyAntiSym(QQ, V.basis(), Integer(5))
>>> list(c.non_redundant_index_generator())  # nothing since c is identically zero in this case (for 5 > 4)
[]
V = VectorSpace(QQ, 4)
c = Components(QQ, V.basis(), 1)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 2)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 3)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 4)
list(c.non_redundant_index_generator())
c = CompFullyAntiSym(QQ, V.basis(), 5)
list(c.non_redundant_index_generator())  # nothing since c is identically zero in this case (for 5 > 4)
swap_adjacent_indices(pos1, pos2, pos3)[source]

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import CompWithSym
>>> V = VectorSpace(QQ, Integer(3))
>>> c = CompWithSym(QQ, V.basis(), Integer(3), antisym=(Integer(1),Integer(2)))
>>> c[Integer(0),Integer(0),Integer(1)], c[Integer(0),Integer(0),Integer(2)], c[Integer(0),Integer(1),Integer(2)] = (Integer(1),Integer(2),Integer(3))
>>> c[Integer(1),Integer(0),Integer(1)], c[Integer(1),Integer(0),Integer(2)], c[Integer(1),Integer(1),Integer(2)] = (Integer(4),Integer(5),Integer(6))
>>> c[Integer(2),Integer(0),Integer(1)], c[Integer(2),Integer(0),Integer(2)], c[Integer(2),Integer(1),Integer(2)] = (Integer(7),Integer(8),Integer(9))
>>> 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]]]
>>> c1 = c.swap_adjacent_indices(Integer(0),Integer(1),Integer(3))
>>> c._antisym   # c is antisymmetric with respect to the last pair of indices...
((1, 2),)
>>> c1._antisym  #...while c1 is antisymmetric with respect to the first pair of indices
((0, 1),)
>>> c[Integer(0),Integer(1),Integer(2)]
3
>>> c1[Integer(1),Integer(2),Integer(0)]
3
>>> c1[Integer(2),Integer(1),Integer(0)]
-3
from sage.tensor.modules.comp import CompWithSym
V = VectorSpace(QQ, 3)
c = CompWithSym(QQ, V.basis(), 3, antisym=(1,2))
c[0,0,1], c[0,0,2], c[0,1,2] = (1,2,3)
c[1,0,1], c[1,0,2], c[1,1,2] = (4,5,6)
c[2,0,1], c[2,0,2], c[2,1,2] = (7,8,9)
c[:]
c1 = c.swap_adjacent_indices(0,1,3)
c._antisym   # c is antisymmetric with respect to the last pair of indices...
c1._antisym  #...while c1 is antisymmetric with respect to the first pair of indices
c[0,1,2]
c1[1,2,0]
c1[2,1,0]
symmetrize(*pos)[source]

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 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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components, CompWithSym,   CompFullySym, CompFullyAntiSym
>>> V = VectorSpace(QQ, Integer(3))
>>> c = Components(QQ, V.basis(), Integer(3))
>>> c[:] = [[[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]], [[Integer(10),Integer(11),Integer(12)], [Integer(13),Integer(14),Integer(15)], [Integer(16),Integer(17),Integer(18)]], [[Integer(19),Integer(20),Integer(21)], [Integer(22),Integer(23),Integer(24)], [Integer(25),Integer(26),Integer(27)]]]
>>> cs = c.symmetrize(Integer(0),Integer(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)
>>> s = cs.symmetrize() ; s
Fully symmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]])
>>> s == c.symmetrize() # should be true
True
>>> s1 = cs.symmetrize(Integer(0),Integer(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)
>>> s1 == cs    # check that s1 is a copy of cs
True
from sage.tensor.modules.comp import Components, CompWithSym, \
  CompFullySym, CompFullyAntiSym
V = VectorSpace(QQ, 3)
c = Components(QQ, V.basis(), 3)
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]]]
cs = c.symmetrize(0,1) ; cs
s = cs.symmetrize() ; s
cs[:], s[:]
s == c.symmetrize() # should be true
s1 = cs.symmetrize(0,1) ; s1   # should return a copy of cs
s1 == cs    # check that s1 is a copy of cs

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
>>> from sage.all import *
>>> cs1 = c.symmetrize(Integer(1),Integer(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)
>>> s2 = cs1.symmetrize() ; s2
Fully symmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s2 == c.symmetrize()
True
cs1 = c.symmetrize(1,2) ; cs1
s2 = cs1.symmetrize() ; s2
s2 == c.symmetrize()

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
>>> from sage.all import *
>>> cs = c.symmetrize(Integer(0),Integer(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)
>>> css = cs.symmetrize(Integer(1),Integer(2))
>>> 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)
>>> 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]]]
>>> 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]]]
>>> css == c.symmetrize() # css differs from the full symmetrized version
False
>>> css.symmetrize() == c.symmetrize() # one has to symmetrize css over all indices to recover it
True
cs = c.symmetrize(0,1) ; cs
css = cs.symmetrize(1,2)
css # the symmetry (0,1) has been lost:
css[:]
cs[:]
css == c.symmetrize() # css differs from the full symmetrized version
css.symmetrize() == c.symmetrize() # one has to symmetrize css over all indices to recover it

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
>>> from sage.all import *
>>> v = Components(QQ, V.basis(), Integer(1))
>>> v[:] = (-Integer(2),Integer(1),Integer(4))
>>> 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)
>>> a1 = a.symmetrize(Integer(0),Integer(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)
>>> a1._sym  # a1 has two distinct symmetries:
((0, 1), (2, 3))
>>> a[Integer(0),Integer(1),Integer(2),Integer(0)] == a[Integer(0),Integer(0),Integer(2),Integer(1)]  # a is symmetric w.r.t. positions 1 and 3
True
>>> a1[Integer(0),Integer(1),Integer(2),Integer(0)] == a1[Integer(0),Integer(0),Integer(2),Integer(1)] # a1 is not
False
>>> a1[Integer(0),Integer(1),Integer(2),Integer(0)] == a1[Integer(1),Integer(0),Integer(2),Integer(0)] # but it is symmetric w.r.t. position 0 and 1
True
>>> a[Integer(0),Integer(1),Integer(2),Integer(0)] == a[Integer(1),Integer(0),Integer(2),Integer(0)] # while a is not
False
v = Components(QQ, V.basis(), 1)
v[:] = (-2,1,4)
a = v*s ; a
a1 = a.symmetrize(0,1) ; a1 # the symmetry (1,2,3) has been reduced to (2,3):
a1._sym  # a1 has two distinct symmetries:
a[0,1,2,0] == a[0,0,2,1]  # a is symmetric w.r.t. positions 1 and 3
a1[0,1,2,0] == a1[0,0,2,1] # a1 is not
a1[0,1,2,0] == a1[1,0,2,0] # but it is symmetric w.r.t. position 0 and 1
a[0,1,2,0] == a[1,0,2,0] # while a is not

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
>>> from sage.all import *
>>> a = Components(QQ, V.basis(), Integer(2))
>>> a[:] = [[-Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),-Integer(6)], [Integer(7),Integer(8),Integer(9)]]
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> b[Integer(0),Integer(1)], b[Integer(0),Integer(2)], b[Integer(1),Integer(2)] = (Integer(2), Integer(4), Integer(8))
>>> 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)
>>> s = c.symmetrize(Integer(0),Integer(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)
>>> s[Integer(0),Integer(1),Integer(2),Integer(1)] == (c[Integer(0),Integer(1),Integer(2),Integer(1)] + c[Integer(1),Integer(0),Integer(2),Integer(1)]) / Integer(2) # check of the symmetrization
True
>>> 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)
]
>>> s == Integer(0)    # the full symmetrization results in zero due to the antisymmetry on the last two indices
True
>>> s = c.symmetrize(Integer(2),Integer(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)
>>> s == Integer(0)    # must be zero since the symmetrization has been performed on the antisymmetric indices
True
>>> s = c.symmetrize(Integer(0),Integer(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)
>>> s != Integer(0)  # s is not zero, but the antisymmetry on (2,3) is lost because the position 2 is involved in the new symmetry
True
a = Components(QQ, V.basis(), 2)
a[:] = [[-1,2,3], [4,5,-6], [7,8,9]]
b = CompFullyAntiSym(QQ, V.basis(), 2)
b[0,1], b[0,2], b[1,2] = (2, 4, 8)
c = a*b ; c
s = c.symmetrize(0,1) ; s  # symmetrization on the first two indices
s[0,1,2,1] == (c[0,1,2,1] + c[1,0,2,1]) / 2 # check of the symmetrization
s = c.symmetrize() ; s  # symmetrization over all the indices
s == 0    # the full symmetrization results in zero due to the antisymmetry on the last two indices
s = c.symmetrize(2,3) ; s
s == 0    # must be zero since the symmetrization has been performed on the antisymmetric indices
s = c.symmetrize(0,2) ; s
s != 0  # s is not zero, but the antisymmetry on (2,3) is lost because the position 2 is involved in the new symmetry

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)
>>> from sage.all import *
>>> a = Components(QQ, V.basis(), Integer(1))
>>> a[:] = (Integer(1), -Integer(2), Integer(3))
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(3))
>>> b[Integer(0),Integer(1),Integer(2)] = Integer(4)
>>> 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)
>>> s = c.symmetrize(Integer(0),Integer(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)
a = Components(QQ, V.basis(), 1)
a[:] = (1, -2, 3)
b = CompFullyAntiSym(QQ, V.basis(), 3)
b[0,1,2] = 4
c = a*b ; c
s = c.symmetrize(0,1) ; s

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
>>> from sage.all import *
>>> s = c.symmetrize(Integer(1),Integer(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)
>>> s == Integer(0) # because (1,2) are involved in the original antisymmetry
True
s = c.symmetrize(1,2) ; s
s == 0 # because (1,2) are involved in the original antisymmetry
trace(pos1, pos2)[source]

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components, CompWithSym,   CompFullySym, CompFullyAntiSym
>>> V = VectorSpace(QQ, Integer(3))
>>> a = CompFullySym(QQ, V.basis(), Integer(2))
>>> a[:] = [[Integer(1),Integer(2),Integer(3)],[Integer(2),Integer(4),Integer(5)],[Integer(3),Integer(5),Integer(6)]]
>>> a.trace(Integer(0),Integer(1))
11
>>> a[Integer(0),Integer(0)] + a[Integer(1),Integer(1)] + a[Integer(2),Integer(2)]
11
from sage.tensor.modules.comp import Components, CompWithSym, \
  CompFullySym, CompFullyAntiSym
V = VectorSpace(QQ, 3)
a = CompFullySym(QQ, V.basis(), 2)
a[:] = [[1,2,3],[2,4,5],[3,5,6]]
a.trace(0,1)
a[0,0] + a[1,1] + a[2,2]

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
>>> from sage.all import *
>>> b = CompFullyAntiSym(QQ, V.basis(), Integer(2))
>>> b[Integer(0),Integer(1)], b[Integer(0),Integer(2)], b[Integer(1),Integer(2)] = (Integer(3), -Integer(2), Integer(1))
>>> b.trace(Integer(0),Integer(1))  # must be zero by antisymmetry
0
b = CompFullyAntiSym(QQ, V.basis(), 2)
b[0,1], b[0,2], b[1,2] = (3, -2, 1)
b.trace(0,1)  # must be zero by antisymmetry

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]
>>> from sage.all import *
>>> v = Components(QQ, V.basis(), Integer(1))
>>> v[:] = (-Integer(2), Integer(4), -Integer(8))
>>> 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)
>>> s = c.trace(Integer(0),Integer(1)) ; s
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s[:]
[-28, 2, 8]
>>> [sum(v[k]*b[k,i] for k in range(Integer(3))) for i in range(Integer(3))] # check
[-28, 2, 8]
>>> s = c.trace(Integer(1),Integer(2)) ; s
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s[:] # is zero by antisymmetry
[0, 0, 0]
>>> 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)
>>> s = c.trace(Integer(0),Integer(1))
>>> s[:]  # is zero by antisymmetry
[0, 0, 0]
>>> s = c.trace(Integer(1),Integer(2)) ; s[:]
[28, -2, -8]
>>> [sum(b[i,k]*v[k] for k in range(Integer(3))) for i in range(Integer(3))]  # check
[28, -2, -8]
v = Components(QQ, V.basis(), 1)
v[:] = (-2, 4, -8)
c = v*b ; c
s = c.trace(0,1) ; s
s[:]
[sum(v[k]*b[k,i] for k in range(3)) for i in range(3)] # check
s = c.trace(1,2) ; s
s[:] # is zero by antisymmetry
c = b*v ; c
s = c.trace(0,1)
s[:]  # is zero by antisymmetry
s = c.trace(1,2) ; s[:]
[sum(b[i,k]*v[k] for k in range(3)) for i in range(3)]  # check

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]]
>>> from sage.all import *
>>> 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)
>>> s = c.trace(Integer(0),Integer(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)
]
>>> s[:]
[  0  33 -22]
[-33   0  11]
[ 22 -11   0]
>>> [[sum(c[k,k,i,j] for k in range(Integer(3))) for j in range(Integer(3))] for i in range(Integer(3))]  # check
[[0, 33, -22], [-33, 0, 11], [22, -11, 0]]
>>> s = c.trace(Integer(1),Integer(2)) ; s  # both symmetries are lost by this contraction
2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s[:]
[ 0  0  0]
[-2  1  0]
[-3  3 -1]
>>> [[sum(c[i,k,k,j] for k in range(Integer(3))) for j in range(Integer(3))] for i in range(Integer(3))]  # check
[[0, 0, 0], [-2, 1, 0], [-3, 3, -1]]
c = a*b ; c
s = c.trace(0,1) ; s  # the symmetry on (0,1) is lost:
s[:]
[[sum(c[k,k,i,j] for k in range(3)) for j in range(3)] for i in range(3)]  # check
s = c.trace(1,2) ; s  # both symmetries are lost by this contraction
s[:]
[[sum(c[i,k,k,j] for k in range(3)) for j in range(3)] for i in range(3)]  # check
class sage.tensor.modules.comp.Components(ring, frame, nb_indices, start_index=0, output_formatter=None)[source]

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 value

  • frame – frame with respect to which the components are defined; whatever type frame is, it should have a method __len__() implemented, so that len(frame) returns the dimension, i.e. the size of a single index range

  • nb_indices – number of integer indices labeling the components

  • start_index – (default: 0) first value of a single index; accordingly a component index i must obey start_index <= i <= start_index + dim - 1, where dim = 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 of ring 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)
]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ,Integer(3))
>>> basis = V.basis() ; basis
[
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> c = Components(QQ, basis, Integer(2)) ; c
2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ,3)
basis = V.basis() ; basis
c = Components(QQ, basis, 2) ; c

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']
>>> from sage.all import *
>>> basis1 = V.gens() ; basis1
((1, 0, 0), (0, 1, 0), (0, 0, 1))
>>> c1 = Components(QQ, basis1, Integer(2)) ; c1
2-indices components w.r.t. ((1, 0, 0), (0, 1, 0), (0, 0, 1))
>>> basis2 = ['a', 'b' , 'c']
>>> c2 = Components(QQ, basis2, Integer(2)) ; c2
2-indices components w.r.t. ['a', 'b', 'c']
basis1 = V.gens() ; basis1
c1 = Components(QQ, basis1, 2) ; c1
basis2 = ['a', 'b' , 'c']
c2 = Components(QQ, basis2, 2) ; c2

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
>>> from sage.all import *
>>> c1 = Components(QQ, basis, Integer(2), start_index=Integer(1))
>>> c1[Integer(0),Integer(1)]
Traceback (most recent call last):
...
IndexError: index out of range: 0 not in [1, 3]
>>> c[Integer(0),Integer(1)]  # for c, the index 0 is OK
0
>>> c[Integer(0),Integer(1)] = -Integer(3)
>>> c1[:] = c[:] # list copy of all components
>>> c1[Integer(1),Integer(2)]  # (1,2) = (0,1) shifted by 1
-3
c1 = Components(QQ, basis, 2, start_index=1)
c1[0,1]
c[0,1]  # for c, the index 0 is OK
c[0,1] = -3
c1[:] = c[:] # list copy of all components
c1[1,2]  # (1,2) = (0,1) shifted by 1

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
>>> from sage.all import *
>>> a = Components(QQ, basis, Integer(2), output_formatter=Rational.numerical_approx)
>>> a[Integer(1),Integer(2)] = Integer(1)/Integer(3)
>>> a[Integer(1),Integer(2)]
0.333333333333333
a = Components(QQ, basis, 2, output_formatter=Rational.numerical_approx)
a[1,2] = 1/3
a[1,2]

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
>>> from sage.all import *
>>> a[Integer(1),Integer(2),Integer(10)] # here the format is 10, for 10 bits of precision
0.33
>>> a[Integer(1),Integer(2),Integer(100)]
0.33333333333333333333333333333
a[1,2,10] # here the format is 10, for 10 bits of precision
a[1,2,100]

The raw (unformatted) components are then accessed by the double bracket operator:

sage: a[[1,2]]
1/3
>>> from sage.all import *
>>> a[[Integer(1),Integer(2)]]
1/3
a[[1,2]]

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)
>>> from sage.all import *
>>> c[Integer(1),Integer(2)] = Integer(1)/Integer(3)
>>> c[Integer(1),Integer(2)], c[[Integer(1),Integer(2)]]
(1/3, 1/3)
c[1,2] = 1/3
c[1,2], c[[1,2]]

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]
>>> from sage.all import *
>>> a[:]
[0.000000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000 0.000000000000000 0.333333333333333]
[0.000000000000000 0.000000000000000 0.000000000000000]
>>> a[:,Integer(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]
a[:]
a[:,10] # with a format different from the default one (53 bits)

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]
>>> from sage.all import *
>>> a[[slice(None)]]
[  0   0   0]
[  0   0 1/3]
[  0   0   0]
a[[slice(None)]]

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']
>>> from sage.all import *
>>> b = Components(QQ, V.basis(), Integer(1), output_formatter=str)
>>> b[:] = (Integer(1), Integer(0), -Integer(4))
>>> b[:]
['1', '0', '-4']
b = Components(QQ, V.basis(), 1, output_formatter=str)
b[:] = (1, 0, -4)
b[:]

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']]
>>> from sage.all import *
>>> b = Components(QQ, basis, Integer(2), output_formatter=str)
>>> b[Integer(0),Integer(1)] = Integer(1)/Integer(3)
>>> b[:]
[['0', '1/3', '0'], ['0', '0', '0'], ['0', '0', '0']]
b = Components(QQ, basis, 2, output_formatter=str)
b[0,1] = 1/3
b[:]

But unformatted outputs still are:

sage: b[[slice(None)]]
[  0 1/3   0]
[  0   0   0]
[  0   0   0]
>>> from sage.all import *
>>> b[[slice(None)]]
[  0 1/3   0]
[  0   0   0]
[  0   0   0]
b[[slice(None)]]

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}
>>> from sage.all import *
>>> a[:]
[0.000000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000 0.000000000000000 0.333333333333333]
[0.000000000000000 0.000000000000000 0.000000000000000]
>>> a._comp
{(1, 2): 1/3}
>>> v = Components(QQ, basis, Integer(1))
>>> v[:] = (-Integer(1), Integer(0), Integer(3))
>>> v._comp  # random output order of the component dictionary
{(0,): -1, (2,): 3}
a[:]
a._comp
v = Components(QQ, basis, 1)
v[:] = (-1, 0, 3)
v._comp  # random output order of the component dictionary

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
>>> from sage.all import *
>>> a = Components(QQ, basis, Integer(1))
>>> a[:] = (-Integer(1), Integer(0), Integer(3))
>>> s = +a ; s[:]
[-1, 0, 3]
>>> +a == a
True
a = Components(QQ, basis, 1)
a[:] = (-1, 0, 3)
s = +a ; s[:]
+a == a

Unary minus operator:

sage: s = -a ; s[:]
[1, 0, -3]
>>> from sage.all import *
>>> s = -a ; s[:]
[1, 0, -3]
s = -a ; s[:]

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
>>> from sage.all import *
>>> b = Components(QQ, basis, Integer(1))
>>> b[:] = (Integer(2), Integer(1), Integer(4))
>>> s = a + b ; s[:]
[1, 1, 7]
>>> a + b == b + a
True
>>> a + (-a) == Integer(0)
True
b = Components(QQ, basis, 1)
b[:] = (2, 1, 4)
s = a + b ; s[:]
a + b == b + a
a + (-a) == 0

Subtraction:

sage: s = a - b ; s[:]
[-3, -1, -1]
sage: s + b == a
True
sage: a - b == - (b - a)
True
>>> from sage.all import *
>>> s = a - b ; s[:]
[-3, -1, -1]
>>> s + b == a
True
>>> a - b == - (b - a)
True
s = a - b ; s[:]
s + b == a
a - b == - (b - a)

Multiplication by a scalar:

sage: s = 2*a ; s[:]
[-2, 0, 6]
>>> from sage.all import *
>>> s = Integer(2)*a ; s[:]
[-2, 0, 6]
s = 2*a ; s[:]

Division by a scalar:

sage: s = a/2 ; s[:]
[-1/2, 0, 3/2]
sage: 2*(a/2) == a
True
>>> from sage.all import *
>>> s = a/Integer(2) ; s[:]
[-1/2, 0, 3/2]
>>> Integer(2)*(a/Integer(2)) == a
True
s = a/2 ; s[:]
2*(a/2) == a

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
>>> from sage.all import *
>>> c = a*b ; c
2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> a[:], b[:]
([-1, 0, 3], [2, 1, 4])
>>> c[:]
[-2 -1 -4]
[ 0  0  0]
[ 6  3 12]
>>> d = c*a ; d
3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]]
>>> d[Integer(0),Integer(1),Integer(2)] == a[Integer(0)]*b[Integer(1)]*a[Integer(2)]
True
c = a*b ; c
a[:], b[:]
c[:]
d = c*a ; d
d[:]
d[0,1,2] == a[0]*b[1]*a[2]
antisymmetrize(*pos)[source]

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ, Integer(3))
>>> c = Components(QQ, V.basis(), Integer(2))
>>> c[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]]
>>> s = c.antisymmetrize() ; s
Fully antisymmetric 2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> c[:], s[:]
(
[1 2 3]  [ 0 -1 -2]
[4 5 6]  [ 1  0 -1]
[7 8 9], [ 2  1  0]
)
>>> c.antisymmetrize() == c.antisymmetrize(Integer(0),Integer(1))
True
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ, 3)
c = Components(QQ, V.basis(), 2)
c[:] = [[1,2,3], [4,5,6], [7,8,9]]
s = c.antisymmetrize() ; s
c[:], s[:]
c.antisymmetrize() == c.antisymmetrize(0,1)

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
>>> from sage.all import *
>>> c = Components(QQ, V.basis(), Integer(3))
>>> c[:] = [[[-Integer(1),-Integer(2),Integer(3)], [Integer(4),-Integer(5),Integer(4)], [-Integer(7),Integer(8),Integer(9)]], [[Integer(10),Integer(10),Integer(12)], [Integer(13),-Integer(14),Integer(15)], [-Integer(16),Integer(17),Integer(19)]], [[-Integer(19),Integer(20),Integer(21)], [Integer(1),Integer(2),Integer(3)], [-Integer(25),Integer(26),Integer(27)]]]
>>> s = c.antisymmetrize() ; s
Fully antisymmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]])
>>> 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])/Integer(6)  # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
>>> c.symmetrize() == c.symmetrize(Integer(0),Integer(1),Integer(2))
True
c = Components(QQ, V.basis(), 3)
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]]]
s = c.antisymmetrize() ; s
c[:], s[:]
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))
c.symmetrize() == c.symmetrize(0,1,2)

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
>>> from sage.all import *
>>> s = c.antisymmetrize(Integer(0),Integer(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)
>>> 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]]])
>>> all(s[i,j,k] == (c[i,j,k]-c[j,i,k])/Integer(2)  # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
>>> s = c.antisymmetrize(Integer(1),Integer(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)
>>> 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]]])
>>> all(s[i,j,k] == (c[i,j,k]-c[i,k,j])/Integer(2)  # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
>>> s = c.antisymmetrize(Integer(0),Integer(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)
>>> 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]]])
>>> all(s[i,j,k] == (c[i,j,k]-c[k,j,i])/Integer(2)  # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
s = c.antisymmetrize(0,1) ; s  # antisymmetrization on the first two indices
c[:], s[:]
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))
s = c.antisymmetrize(1,2) ; s  # antisymmetrization on the last two indices
c[:], s[:]
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))
s = c.antisymmetrize(0,2) ; s  # antisymmetrization on the first and last indices
c[:], s[:]
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))

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
>>> from sage.all import *
>>> c.antisymmetrize(Integer(1),Integer(0)) == c.antisymmetrize(Integer(0),Integer(1))
True
>>> c.antisymmetrize(Integer(2),Integer(1)) == c.antisymmetrize(Integer(1),Integer(2))
True
>>> c.antisymmetrize(Integer(2),Integer(0)) == c.antisymmetrize(Integer(0),Integer(2))
True
c.antisymmetrize(1,0) == c.antisymmetrize(0,1)
c.antisymmetrize(2,1) == c.antisymmetrize(1,2)
c.antisymmetrize(2,0) == c.antisymmetrize(0,2)
contract(*args)[source]

Contraction on one or many indices with another instance of Components.

INPUT:

  • pos1 – positions of the indices in self 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. If pos1 is not provided, a single contraction on the last index position of self is assumed

  • other – the set of components to contract with

  • pos2 – positions of the indices in other involved in the contraction, with the same conventions as for pos1. If pos2 is not provided, a single contraction on the first index position of other 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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ, Integer(3))
>>> a = Components(QQ, V.basis(), Integer(1))
>>> a[:] = (-Integer(1), Integer(2), Integer(3))
>>> b = Components(QQ, V.basis(), Integer(2))
>>> b[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]]
>>> s0 = a.contract(Integer(0), b, Integer(0)) ; s0
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s0[:]
[28, 32, 36]
>>> s0[:] == [sum(a[j]*b[j,i] for j in range(Integer(3))) for i in range(Integer(3))]  # check
True
>>> s1 = a.contract(Integer(0), b, Integer(1)) ; s1[:]
[12, 24, 36]
>>> s1[:] == [sum(a[j]*b[i,j] for j in range(Integer(3))) for i in range(Integer(3))]  # check
True
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ, 3)
a = Components(QQ, V.basis(), 1)
a[:] = (-1, 2, 3)
b = Components(QQ, V.basis(), 2)
b[:] = [[1,2,3], [4,5,6], [7,8,9]]
s0 = a.contract(0, b, 0) ; s0
s0[:]
s0[:] == [sum(a[j]*b[j,i] for j in range(3)) for i in range(3)]  # check
s1 = a.contract(0, b, 1) ; s1[:]
s1[:] == [sum(a[j]*b[i,j] for j in range(3)) for i in range(3)]  # check

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
>>> from sage.all import *
>>> Parallelism().set('tensor', nproc=Integer(2))
>>> Parallelism().get('tensor')
2
>>> s0_par = a.contract(Integer(0), b, Integer(0)) ; s0_par
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s0_par[:]
[28, 32, 36]
>>> s0_par == s0
True
>>> s1_par = a.contract(Integer(0), b, Integer(1)) ; s1_par[:]
[12, 24, 36]
>>> s1_par == s1
True
>>> Parallelism().set('tensor', nproc = Integer(1))  # switch off parallelization
Parallelism().set('tensor', nproc=2)
Parallelism().get('tensor')
s0_par = a.contract(0, b, 0) ; s0_par
s0_par[:]
s0_par == s0
s1_par = a.contract(0, b, 1) ; s1_par[:]
s1_par == s1
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]
>>> from sage.all import *
>>> c = a*b ; c
3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s = c.contract(Integer(1),Integer(2), b, Integer(0),Integer(1)) ; s
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s[:]
[-285, 570, 855]
>>> [sum(sum(c[i,j,k]*b[j,k] for k in range(Integer(3))) # check
...      for j in range(Integer(3))) for i in range(Integer(3))]
[-285, 570, 855]
c = a*b ; c
s = c.contract(1,2, b, 0,1) ; s
s[:]
[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)]

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
>>> from sage.all import *
>>> Parallelism().set('tensor', nproc=Integer(2))
>>> c_par = a*b ; c_par
3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> c_par == c
True
>>> s_par = c_par.contract(Integer(1),Integer(2), b, Integer(0),Integer(1)) ; s_par
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s_par[:]
[-285, 570, 855]
>>> s_par == s
True
>>> Parallelism().set('tensor', nproc=Integer(1))  # switch off parallelization
Parallelism().set('tensor', nproc=2)
c_par = a*b ; c_par
c_par == c
s_par = c_par.contract(1,2, b, 0,1) ; s_par
s_par[:]
s_par == s
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
>>> from sage.all import *
>>> 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)
]
>>> b[:]
[ 1 -2 -3]
[-2  4  6]
[-3  6  9]
>>> b.trace(Integer(0),Integer(1))
14
>>> a.contract(Integer(0), a, Integer(0)) == b.trace(Integer(0),Integer(1))
True
b = a*a ; b   # the tensor product of a with itself
b[:]
b.trace(0,1)
a.contract(0, a, 0) == b.trace(0,1)
copy()[source]

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ,Integer(3))
>>> a = Components(QQ, V.basis(), Integer(1))
>>> a[:] = -Integer(2), Integer(1), Integer(5)
>>> b = a.copy() ; b
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> b[:]
[-2, 1, 5]
>>> b == a
True
>>> b is a  # b is a distinct object
False
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ,3)
a = Components(QQ, V.basis(), 1)
a[:] = -2, 1, 5
b = a.copy() ; b
b[:]
b == a
b is a  # b is a distinct object
display(symbol, latex_symbol=None, index_positions=None, index_labels=None, index_latex_labels=None, format_spec=None, only_nonzero=True, only_nonredundant=False)[source]

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 components

  • latex_symbol – (default: None) string specifying the LaTeX symbol for the components; if None, symbol is used

  • index_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. If index_positions is None, all indices are printed as subscripts

  • index_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; if None, integer labels are used

  • index_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; if None, integers labels are used

  • format_spec – (default: None) format specification passed to the output formatter declared at the construction of the object

  • only_nonzero – boolean (default: True); if True, only nonzero components are displayed

  • only_nonredundant – boolean (default: False); if True, only nonredundant components are displayed in case of symmetries

EXAMPLES:

Display of 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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> c = Components(ZZ, (ZZ**Integer(2)).basis(), Integer(3))
>>> c[Integer(0),Integer(1),Integer(0)], c[Integer(1),Integer(0),Integer(1)], c[Integer(1),Integer(1),Integer(1)] = -Integer(2), Integer(5), Integer(3)
>>> c.display('c')
c_010 = -2
c_101 = 5
c_111 = 3
from sage.tensor.modules.comp import Components
c = Components(ZZ, (ZZ^2).basis(), 3)
c[0,1,0], c[1,0,1], c[1,1,1] = -2, 5, 3
c.display('c')

By default, only nonzero components are shown; to display all the components, it suffices to set the parameter only_nonzero to False:

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
>>> from sage.all import *
>>> 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
c.display('c', only_nonzero=False)

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
>>> from sage.all import *
>>> c.display('c', index_positions='udd')
c^0_10 = -2
c^1_01 = 5
c^1_11 = 3
>>> c.display('c', index_positions='udu')
c^0_1^0 = -2
c^1_0^1 = 5
c^1_1^1 = 3
>>> c.display('c', index_positions='ddu')
c_01^0 = -2
c_10^1 = 5
c_11^1 = 3
c.display('c', index_positions='udd')
c.display('c', index_positions='udu')
c.display('c', index_positions='ddu')

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}
>>> from sage.all import *
>>> 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}
latex(c.display('c', latex_symbol=r'\Gamma', index_positions='udd'))

The index labels can differ from integers:

sage: c.display('c', index_labels=['x','y'])
c_xyx = -2
c_yxy = 5
c_yyy = 3
>>> from sage.all import *
>>> c.display('c', index_labels=['x','y'])
c_xyx = -2
c_yxy = 5
c_yyy = 3
c.display('c', index_labels=['x','y'])

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
>>> from sage.all import *
>>> c.display('c', index_labels=['r', 'th'])
c_r,th,r = -2
c_th,r,th = 5
c_th,th,th = 3
c.display('c', index_labels=['r', 'th'])

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
>>> from sage.all import *
>>> 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
c.display('c', index_labels=['r', 'th'],
          index_latex_labels=['r', r'\theta'])

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import CompWithSym
>>> c = CompWithSym(ZZ, (ZZ**Integer(2)).basis(), Integer(3), sym=(Integer(1),Integer(2))) ; c
3-indices components w.r.t. [
(1, 0),
(0, 1)
], with symmetry on the index positions (1, 2)
>>> c[Integer(0),Integer(0),Integer(1)] = Integer(2)
>>> c.display('c')
c_001 = 2
c_010 = 2
>>> c.display('c', only_nonredundant=True)
c_001 = 2
from sage.tensor.modules.comp import CompWithSym
c = CompWithSym(ZZ, (ZZ^2).basis(), 3, sym=(1,2)) ; c
c[0,0,1] = 2
c.display('c')
c.display('c', only_nonredundant=True)

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
>>> from sage.all import *
>>> c = Components(QQ, (QQ**Integer(3)).basis(), Integer(2),
...                output_formatter=Rational.numerical_approx)
>>> c[Integer(0),Integer(1)] = Integer(1)/Integer(3)
>>> c[Integer(2),Integer(1)] = Integer(2)/Integer(7)
>>> c.display('C')  # default format (53 bits of precision)
C_01 = 0.333333333333333
C_21 = 0.285714285714286
>>> c.display('C', format_spec=Integer(10))  # 10 bits of precision
C_01 = 0.33
C_21 = 0.29
c = Components(QQ, (QQ^3).basis(), 2,
               output_formatter=Rational.numerical_approx)
c[0,1] = 1/3
c[2,1] = 2/7
c.display('C')  # default format (53 bits of precision)
c.display('C', format_spec=10)  # 10 bits of precision

Check that the bug reported in Issue #22520 is fixed:

sage: c = Components(SR, [1, 2], 1)                                         # needs sage.symbolic
sage: c[0] = SR.var('t', domain='real')                                     # needs sage.symbolic
sage: c.display('c')                                                        # needs sage.symbolic
c_0 = t
>>> from sage.all import *
>>> c = Components(SR, [Integer(1), Integer(2)], Integer(1))                                         # needs sage.symbolic
>>> c[Integer(0)] = SR.var('t', domain='real')                                     # needs sage.symbolic
>>> c.display('c')                                                        # needs sage.symbolic
c_0 = t
c = Components(SR, [1, 2], 1)                                         # needs sage.symbolic
c[0] = SR.var('t', domain='real')                                     # needs sage.symbolic
c.display('c')                                                        # needs sage.symbolic
index_generator()[source]

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)]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ,Integer(3))
>>> c = Components(QQ, V.basis(), Integer(1))
>>> list(c.index_generator())
[(0,), (1,), (2,)]
>>> c = Components(QQ, V.basis(), Integer(1), start_index=Integer(1))
>>> list(c.index_generator())
[(1,), (2,), (3,)]
>>> c = Components(QQ, V.basis(), Integer(2))
>>> list(c.index_generator())
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0),
 (2, 1), (2, 2)]
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ,3)
c = Components(QQ, V.basis(), 1)
list(c.index_generator())
c = Components(QQ, V.basis(), 1, start_index=1)
list(c.index_generator())
c = Components(QQ, V.basis(), 2)
list(c.index_generator())
is_zero()[source]

Return True if all the components are zero and False 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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ,Integer(3))
>>> c = Components(QQ, V.basis(), Integer(1))
>>> c.is_zero()
True
>>> c[:]
[0, 0, 0]
>>> c[Integer(0)] = Integer(1) ; c[:]
[1, 0, 0]
>>> c.is_zero()
False
>>> c[Integer(0)] = Integer(0) ; c[:]
[0, 0, 0]
>>> c.is_zero()
True
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ,3)
c = Components(QQ, V.basis(), 1)
c.is_zero()
c[:]
c[0] = 1 ; c[:]
c.is_zero()
c[0] = 0 ; c[:]
c.is_zero()

It is equivalent to use the operator == to compare to zero:

sage: c == 0
True
sage: c != 0
False
>>> from sage.all import *
>>> c == Integer(0)
True
>>> c != Integer(0)
False
c == 0
c != 0

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
>>> from sage.all import *
>>> c == Integer(1)
Traceback (most recent call last):
...
TypeError: cannot compare a set of components to a number
c == 1
items()[source]

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)]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components, CompWithSym

>>> c = Components(ZZ, (ZZ**Integer(2)).basis(), Integer(3))
>>> c[Integer(0),Integer(1),Integer(0)], c[Integer(1),Integer(0),Integer(1)], c[Integer(1),Integer(1),Integer(1)] = -Integer(2), Integer(5), Integer(3)
>>> list(c.items())
[((0, 1, 0), -2), ((1, 0, 1), 5), ((1, 1, 1), 3)]

>>> c = CompWithSym(ZZ, (ZZ**Integer(2)).basis(), Integer(3), sym=((Integer(1), Integer(2))))
>>> c[Integer(0),Integer(1),Integer(0)], c[Integer(1),Integer(0),Integer(1)], c[Integer(1),Integer(1),Integer(1)] = -Integer(2), Integer(5), Integer(3)
>>> list(c.items())
[((0, 0, 1), -2),
((0, 1, 0), -2),
((1, 0, 1), 5),
((1, 1, 0), 5),
((1, 1, 1), 3)]
from sage.tensor.modules.comp import Components, CompWithSym
c = Components(ZZ, (ZZ^2).basis(), 3)
c[0,1,0], c[1,0,1], c[1,1,1] = -2, 5, 3
list(c.items())
c = CompWithSym(ZZ, (ZZ^2).basis(), 3, sym=((1, 2)))
c[0,1,0], c[1,0,1], c[1,1,1] = -2, 5, 3
list(c.items())
non_redundant_index_generator()[source]

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)]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ,Integer(3))
>>> c = Components(QQ, V.basis(), Integer(2))
>>> list(c.non_redundant_index_generator())
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0),
 (2, 1), (2, 2)]
>>> c = Components(QQ, V.basis(), Integer(2), start_index=Integer(1))
>>> list(c.non_redundant_index_generator())
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1),
 (3, 2), (3, 3)]
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ,3)
c = Components(QQ, V.basis(), 2)
list(c.non_redundant_index_generator())
c = Components(QQ, V.basis(), 2, start_index=1)
list(c.non_redundant_index_generator())
swap_adjacent_indices(pos1, pos2, pos3)[source]

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 convention position=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]
)
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ, Integer(3))
>>> c = Components(QQ, V.basis(), Integer(2))
>>> c[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]]
>>> c1 = c.swap_adjacent_indices(Integer(0),Integer(1),Integer(2))
>>> c[:], c1[:]
(
[1 2 3]  [1 4 7]
[4 5 6]  [2 5 8]
[7 8 9], [3 6 9]
)
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ, 3)
c = Components(QQ, V.basis(), 2)
c[:] = [[1,2,3], [4,5,6], [7,8,9]]
c1 = c.swap_adjacent_indices(0,1,2)
c[:], c1[:]

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
>>> from sage.all import *
>>> d = c*c1 ; d
4-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> d1 = d.swap_adjacent_indices(Integer(0),Integer(2),Integer(4))
>>> d[Integer(0),Integer(1),Integer(1),Integer(2)]
16
>>> d1[Integer(1),Integer(2),Integer(0),Integer(1)]
16
>>> d1[Integer(0),Integer(1),Integer(1),Integer(2)]
24
>>> d[Integer(1),Integer(2),Integer(0),Integer(1)]
24
d = c*c1 ; d
d1 = d.swap_adjacent_indices(0,2,4)
d[0,1,1,2]
d1[1,2,0,1]
d1[0,1,1,2]
d[1,2,0,1]
symmetrize(*pos)[source]

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ, Integer(3))
>>> c = Components(QQ, V.basis(), Integer(2))
>>> c[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]]
>>> s = c.symmetrize() ; s
Fully symmetric 2-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> c[:], s[:]
(
[1 2 3]  [1 3 5]
[4 5 6]  [3 5 7]
[7 8 9], [5 7 9]
)
>>> c.symmetrize() == c.symmetrize(Integer(0),Integer(1))
True
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ, 3)
c = Components(QQ, V.basis(), 2)
c[:] = [[1,2,3], [4,5,6], [7,8,9]]
s = c.symmetrize() ; s
c[:], s[:]
c.symmetrize() == c.symmetrize(0,1)

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
>>> from sage.all import *
>>> c = Components(QQ, V.basis(), Integer(3))
>>> c[:] = [[[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]], [[Integer(10),Integer(11),Integer(12)], [Integer(13),Integer(14),Integer(15)], [Integer(16),Integer(17),Integer(18)]], [[Integer(19),Integer(20),Integer(21)], [Integer(22),Integer(23),Integer(24)], [Integer(25),Integer(26),Integer(27)]]]
>>> s = c.symmetrize() ; s
Fully symmetric 3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> 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]]])
>>> 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])/Integer(6)  # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
>>> c.symmetrize() == c.symmetrize(Integer(0),Integer(1),Integer(2))
True
c = Components(QQ, V.basis(), 3)
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]]]
s = c.symmetrize() ; s
c[:], s[:]
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))
c.symmetrize() == c.symmetrize(0,1,2)

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
>>> from sage.all import *
>>> s = c.symmetrize(Integer(0),Integer(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)
>>> 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]]])
>>> all(s[i,j,k] == (c[i,j,k]+c[j,i,k])/Integer(2)   # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
>>> s = c.symmetrize(Integer(1),Integer(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)
>>> 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]]])
>>> all(s[i,j,k] == (c[i,j,k]+c[i,k,j])/Integer(2)   # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
>>> s = c.symmetrize(Integer(0),Integer(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)
>>> 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]]])
>>> all(s[i,j,k] == (c[i,j,k]+c[k,j,i])/Integer(2)   # Check of the result:
...     for i in range(Integer(3)) for j in range(Integer(3)) for k in range(Integer(3)))
True
s = c.symmetrize(0,1) ; s   # symmetrization on the first two indices
c[:], s[:]
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))
s = c.symmetrize(1,2) ; s   # symmetrization on the last two indices
c[:], s[:]
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))
s = c.symmetrize(0,2) ; s   # symmetrization on the first and last indices
c[:], s[:]
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))
trace(pos1, pos2)[source]

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
>>> from sage.all import *
>>> from sage.tensor.modules.comp import Components
>>> V = VectorSpace(QQ, Integer(3))
>>> c = Components(QQ, V.basis(), Integer(2))
>>> c[:] = [[Integer(1),Integer(2),Integer(3)], [Integer(4),Integer(5),Integer(6)], [Integer(7),Integer(8),Integer(9)]]
>>> c.trace(Integer(0),Integer(1))
15
>>> c[Integer(0),Integer(0)] + c[Integer(1),Integer(1)] + c[Integer(2),Integer(2)]  # check
15
from sage.tensor.modules.comp import Components
V = VectorSpace(QQ, 3)
c = Components(QQ, V.basis(), 2)
c[:] = [[1,2,3], [4,5,6], [7,8,9]]
c.trace(0,1)
c[0,0] + c[1,1] + c[2,2]  # check

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]
>>> from sage.all import *
>>> v = Components(QQ, V.basis(), Integer(1))
>>> v[:] = (-Integer(1),Integer(2),Integer(3))
>>> a = c*v ; a
3-indices components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s = a.trace(Integer(0),Integer(1)) ; s  # contraction on the first two indices
1-index components w.r.t. [
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
>>> s[:]
[-15, 30, 45]
>>> [sum(a[j,j,i] for j in range(Integer(3))) for i in range(Integer(3))]  # check
[-15, 30, 45]
>>> s = a.trace(Integer(0),Integer(2)) ; s[:]  # contraction on the first and last indices
[28, 32, 36]
>>> [sum(a[j,i,j] for j in range(Integer(3))) for i in range(Integer(3))]  # check
[28, 32, 36]
>>> s = a.trace(Integer(1),Integer(2)) ; s[:] # contraction on the last two indices
[12, 24, 36]
>>> [sum(a[i,j,j] for j in range(Integer(3))) for i in range(Integer(3))]  # check
[12, 24, 36]
v = Components(QQ, V.basis(), 1)
v[:] = (-1,2,3)
a = c*v ; a
s = a.trace(0,1) ; s  # contraction on the first two indices
s[:]
[sum(a[j,j,i] for j in range(3)) for i in range(3)]  # check
s = a.trace(0,2) ; s[:]  # contraction on the first and last indices
[sum(a[j,i,j] for j in range(3)) for i in range(3)]  # check
s = a.trace(1,2) ; s[:] # contraction on the last two indices
[sum(a[i,j,j] for j in range(3)) for i in range(3)]  # check
class sage.tensor.modules.comp.KroneckerDelta(ring, frame, start_index=0, output_formatter=None)[source]

Bases: CompFullySym

Kronecker delta \(\delta_{ij}\).

INPUT:

  • ring – commutative ring in which each component takes its value

  • frame – frame with respect to which the components are defined; whatever type frame is, it should have some method __len__() implemented, so that len(frame) returns the dimension, i.e. the size of a single index range

  • start_index – (default: 0) first value of a single index; accordingly a component index i must obey start_index <= i <= start_index + dim - 1, where dim = 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 of ring 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]
>>> from sage.all import *
>>> from sage.tensor.modules.comp import KroneckerDelta
>>> V = VectorSpace(QQ,Integer(3))
>>> d = KroneckerDelta(QQ, V.basis()) ; d
Kronecker delta of size 3x3
>>> d[:]
[1 0 0]
[0 1 0]
[0 0 1]
from sage.tensor.modules.comp import KroneckerDelta
V = VectorSpace(QQ,3)
d = KroneckerDelta(QQ, V.basis()) ; d
d[:]

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
>>> from sage.all import *
>>> d[Integer(1),Integer(1)]
1
>>> d[Integer(1),Integer(1)] = Integer(2)
Traceback (most recent call last):
...
TypeError: the components of a Kronecker delta cannot be changed
d[1,1]
d[1,1] = 2

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']]
>>> from sage.all import *
>>> d = KroneckerDelta(QQ, V.basis(), output_formatter=Rational.numerical_approx)
>>> 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]
>>> d[:,Integer(10)] # format = 10 bits of precision
[ 1.0 0.00 0.00]
[0.00  1.0 0.00]
[0.00 0.00  1.0]
>>> d = KroneckerDelta(QQ, V.basis(), output_formatter=str)
>>> d[:]
[['1', '0', '0'], ['0', '1', '0'], ['0', '0', '1']]
d = KroneckerDelta(QQ, V.basis(), output_formatter=Rational.numerical_approx)
d[:]  # default format (53 bits of precision)
d[:,10] # format = 10 bits of precision
d = KroneckerDelta(QQ, V.basis(), output_formatter=str)
d[:]