modelparameters.sympy.tensor package¶
Subpackages¶
- modelparameters.sympy.tensor.array package
- Submodules
- modelparameters.sympy.tensor.array.arrayop module
- modelparameters.sympy.tensor.array.dense_ndim_array module
- modelparameters.sympy.tensor.array.mutable_ndim_array module
- modelparameters.sympy.tensor.array.ndim_array module
- modelparameters.sympy.tensor.array.sparse_ndim_array module
- Module contents
Submodules¶
modelparameters.sympy.tensor.index_methods module¶
Module with functions operating on IndexedBase, Indexed and Idx objects
Check shape conformance
Determine indices in resulting expression
etc.
Methods in this module could be implemented by calling methods on Expr objects instead. When things stabilize this could be a useful refactoring.
- exception modelparameters.sympy.tensor.index_methods.IndexConformanceException[source]¶
Bases:
Exception
- modelparameters.sympy.tensor.index_methods.get_contraction_structure(expr)[source]¶
Determine dummy indices of
expr
and describe its structureBy dummy we mean indices that are summation indices.
The stucture of the expression is determined and described as follows:
A conforming summation of Indexed objects is described with a dict where the keys are summation indices and the corresponding values are sets containing all terms for which the summation applies. All Add objects in the SymPy expression tree are described like this.
For all nodes in the SymPy expression tree that are not of type Add, the following applies:
If a node discovers contractions in one of its arguments, the node itself will be stored as a key in the dict. For that key, the corresponding value is a list of dicts, each of which is the result of a recursive call to get_contraction_structure(). The list contains only dicts for the non-trivial deeper contractions, ommitting dicts with None as the one and only key.
Note
The presence of expressions among the dictinary keys indicates multiple levels of index contractions. A nested dict displays nested contractions and may itself contain dicts from a deeper level. In practical calculations the summation in the deepest nested level must be calculated first so that the outer expression can access the resulting indexed object.
Examples
>>> from .index_methods import get_contraction_structure >>> from .. import symbols, default_sort_key >>> from ..tensor import IndexedBase, Idx >>> x, y, A = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, k, l = map(Idx, ['i', 'j', 'k', 'l']) >>> get_contraction_structure(x[i]*y[i] + A[j, j]) {(i,): {x[i]*y[i]}, (j,): {A[j, j]}} >>> get_contraction_structure(x[i]*y[j]) {None: {x[i]*y[j]}}
A multiplication of contracted factors results in nested dicts representing the internal contractions.
>>> d = get_contraction_structure(x[i, i]*y[j, j]) >>> sorted(d.keys(), key=default_sort_key) [None, x[i, i]*y[j, j]]
In this case, the product has no contractions:
>>> d[None] {x[i, i]*y[j, j]}
Factors are contracted “first”:
>>> sorted(d[x[i, i]*y[j, j]], key=default_sort_key) [{(i,): {x[i, i]}}, {(j,): {y[j, j]}}]
A parenthesized Add object is also returned as a nested dictionary. The term containing the parenthesis is a Mul with a contraction among the arguments, so it will be found as a key in the result. It stores the dictionary resulting from a recursive call on the Add expression.
>>> d = get_contraction_structure(x[i]*(y[i] + A[i, j]*x[j])) >>> sorted(d.keys(), key=default_sort_key) [(A[i, j]*x[j] + y[i])*x[i], (i,)] >>> d[(i,)] {(A[i, j]*x[j] + y[i])*x[i]} >>> d[x[i]*(A[i, j]*x[j] + y[i])] [{None: {y[i]}, (j,): {A[i, j]*x[j]}}]
Powers with contractions in either base or exponent will also be found as keys in the dictionary, mapping to a list of results from recursive calls:
>>> d = get_contraction_structure(A[j, j]**A[i, i]) >>> d[None] {A[j, j]**A[i, i]} >>> nested_contractions = d[A[j, j]**A[i, i]] >>> nested_contractions[0] {(j,): {A[j, j]}} >>> nested_contractions[1] {(i,): {A[i, i]}}
The description of the contraction structure may appear complicated when represented with a string in the above examples, but it is easy to iterate over:
>>> from .. import Expr >>> for key in d: ... if isinstance(key, Expr): ... continue ... for term in d[key]: ... if term in d: ... # treat deepest contraction first ... pass ... # treat outermost contactions here
- modelparameters.sympy.tensor.index_methods.get_indices(expr)[source]¶
Determine the outer indices of expression
expr
By outer we mean indices that are not summation indices. Returns a set and a dict. The set contains outer indices and the dict contains information about index symmetries.
Examples
>>> from .index_methods import get_indices >>> from .. import symbols >>> from ..tensor import IndexedBase, Idx >>> x, y, A = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, a, z = symbols('i j a z', integer=True)
The indices of the total expression is determined, Repeated indices imply a summation, for instance the trace of a matrix A:
>>> get_indices(A[i, i]) (set(), {})
In the case of many terms, the terms are required to have identical outer indices. Else an IndexConformanceException is raised.
>>> get_indices(x[i] + A[i, j]*y[j]) ({i}, {})
- Exceptions:
An IndexConformanceException means that the terms ar not compatible, e.g.
>>> get_indices(x[i] + y[j]) (...) IndexConformanceException: Indices are not consistent: x(i) + y(j)
Warning
The concept of outer indices applies recursively, starting on the deepest level. This implies that dummies inside parenthesis are assumed to be summed first, so that the following expression is handled gracefully:
>>> get_indices((x[i] + A[i, j]*y[j])*x[j]) ({i, j}, {})
This is correct and may appear convenient, but you need to be careful with this as SymPy will happily .expand() the product, if requested. The resulting expression would mix the outer
j
with the dummies inside the parenthesis, which makes it a different expression. To be on the safe side, it is best to avoid such ambiguities by using unique indices for all contractions that should be held separate.
modelparameters.sympy.tensor.indexed module¶
Module that defines indexed objects
The classes IndexedBase
, Indexed
, and Idx
represent a
matrix element M[i, j]
as in the following diagram:
1) The Indexed class represents the entire indexed object.
|
___|___
' '
M[i, j]
/ \__\______
| |
| |
| 2) The Idx class represents indices; each Idx can
| optionally contain information about its range.
|
3) IndexedBase represents the 'stem' of an indexed object, here `M`.
The stem used by itself is usually taken to represent the entire
array.
There can be any number of indices on an Indexed object. No
transformation properties are implemented in these Base objects, but
implicit contraction of repeated indices is supported.
Note that the support for complicated (i.e. non-atomic) integer
expressions as indices is limited. (This should be improved in
future releases.)
Examples
========
To express the above matrix element example you would write:
>>> from .. import symbols, IndexedBase, Idx
>>> M = IndexedBase('M')
>>> i, j = symbols('i j', cls=Idx)
>>> M[i, j]
M[i, j]
Repeated indices in a product implies a summation, so to express a
matrix-vector product in terms of Indexed objects:
>>> x = IndexedBase('x')
>>> M[i, j]*x[j]
M[i, j]*x[j]
If the indexed objects will be converted to component based arrays, e.g.
with the code printers or the autowrap framework, you also need to provide
(symbolic or numerical) dimensions. This can be done by passing an
optional shape parameter to IndexedBase upon construction:
>>> dim1, dim2 = symbols('dim1 dim2', integer=True)
>>> A = IndexedBase('A', shape=(dim1, 2*dim1, dim2))
>>> A.shape
(dim1, 2*dim1, dim2)
>>> A[i, j, 3].shape
(dim1, 2*dim1, dim2)
If an IndexedBase object has no shape information, it is assumed that the
array is as large as the ranges of its indices:
>>> n, m = symbols('n m', integer=True)
>>> i = Idx('i', m)
>>> j = Idx('j', n)
>>> M[i, j].shape
(m, n)
>>> M[i, j].ranges
[(0, m - 1), (0, n - 1)]
The above can be compared with the following:
>>> A[i, 2, j].shape
(dim1, 2*dim1, dim2)
>>> A[i, 2, j].ranges
[(0, m - 1), None, (0, n - 1)]
To analyze the structure of indexed expressions, you can use the methods
get_indices() and get_contraction_structure():
>>> from ..tensor import get_indices, get_contraction_structure
>>> get_indices(A[i, j, j])
({i}, {})
>>> get_contraction_structure(A[i, j, j])
{(j,): {A[i, j, j]}}
See the appropriate docstrings for a detailed explanation of the output.
- class modelparameters.sympy.tensor.indexed.Idx(label, range=None, **kw_args)[source]¶
Bases:
Expr
Represents an integer index as an
Integer
or integer expression.There are a number of ways to create an
Idx
object. The constructor takes two arguments:label
An integer or a symbol that labels the index.
range
Optionally you can specify a range as either
Symbol
or integer: This is interpreted as a dimension. Lower and upper bounds are set to0
andrange - 1
, respectively.tuple
: The two elements are interpreted as the lower and upper bounds of the range, respectively.
Note: the
Idx
constructor is rather pedantic in that it only accepts integer arguments. The only exception is that you can use-oo
andoo
to specify an unbounded range. For all other cases, both label and bounds must be declared as integers, e.g. ifn
is given as an argument thenn.is_integer
must returnTrue
.For convenience, if the label is given as a string it is automatically converted to an integer symbol. (Note: this conversion is not done for range or dimension arguments.)
Examples
>>> from .. import IndexedBase, Idx, symbols, oo >>> n, i, L, U = symbols('n i L U', integer=True)
If a string is given for the label an integer
Symbol
is created and the bounds are bothNone
:>>> idx = Idx('qwerty'); idx qwerty >>> idx.lower, idx.upper (None, None)
Both upper and lower bounds can be specified:
>>> idx = Idx(i, (L, U)); idx i >>> idx.lower, idx.upper (L, U)
When only a single bound is given it is interpreted as the dimension and the lower bound defaults to 0:
>>> idx = Idx(i, n); idx.lower, idx.upper (0, n - 1) >>> idx = Idx(i, 4); idx.lower, idx.upper (0, 3) >>> idx = Idx(i, oo); idx.lower, idx.upper (0, oo)
- default_assumptions = {'algebraic': True, 'commutative': True, 'complex': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'integer': True, 'irrational': False, 'noninteger': False, 'rational': True, 'real': True, 'transcendental': False}¶
- property free_symbols¶
Return from the atoms of self those which are free symbols.
For most expressions, all symbols are free symbols. For some classes this is not true. e.g. Integrals use Symbols for the dummy variables which are bound variables, so Integral has a method to return all symbols except those. Derivative keeps track of symbols with respect to which it will perform a derivative; those are bound variables, too, so it has its own free_symbols method.
Any other method that uses bound variables should implement a free_symbols method.
- is_Atom = True¶
- is_Symbol = True¶
- is_algebraic = True¶
- is_commutative = True¶
- is_complex = True¶
- is_finite = True¶
- is_hermitian = True¶
- is_imaginary = False¶
- is_infinite = False¶
- is_integer = True¶
- is_irrational = False¶
- is_noninteger = False¶
- is_rational = True¶
- is_real = True¶
- is_symbol = True¶
- is_transcendental = False¶
- property label¶
Returns the label (Integer or integer expression) of the Idx object.
Examples
>>> from .. import Idx, Symbol >>> x = Symbol('x', integer=True) >>> Idx(x).label x >>> j = Symbol('j', integer=True) >>> Idx(j).label j >>> Idx(j + 1).label j + 1
- property lower¶
Returns the lower bound of the
Idx
.Examples
>>> from .. import Idx >>> Idx('j', 2).lower 0 >>> Idx('j', 5).lower 0 >>> Idx('j').lower is None True
- property upper¶
Returns the upper bound of the
Idx
.Examples
>>> from .. import Idx >>> Idx('j', 2).upper 1 >>> Idx('j', 5).upper 4 >>> Idx('j').upper is None True
- class modelparameters.sympy.tensor.indexed.Indexed(base, *args, **kw_args)[source]¶
Bases:
Expr
Represents a mathematical object with indices.
>>> from .. import Indexed, IndexedBase, Idx, symbols >>> i, j = symbols('i j', cls=Idx) >>> Indexed('A', i, j) A[i, j]
It is recommended that
Indexed
objects be created viaIndexedBase
:>>> A = IndexedBase('A') >>> Indexed('A', i, j) == A[i, j] True
- property base¶
Returns the
IndexedBase
of theIndexed
object.Examples
>>> from .. import Indexed, IndexedBase, Idx, symbols >>> i, j = symbols('i j', cls=Idx) >>> Indexed('A', i, j).base A >>> B = IndexedBase('B') >>> B == B[i, j].base True
- default_assumptions = {'commutative': True}¶
- property indices¶
Returns the indices of the
Indexed
object.Examples
>>> from .. import Indexed, Idx, symbols >>> i, j = symbols('i j', cls=Idx) >>> Indexed('A', i, j).indices (i, j)
- is_Atom = True¶
- is_Indexed = True¶
- is_Symbol = True¶
- is_commutative = True¶
- is_symbol = True¶
- property ranges¶
Returns a list of tuples with lower and upper range of each index.
If an index does not define the data members upper and lower, the corresponding slot in the list contains
None
instead of a tuple.Examples
>>> from .. import Indexed,Idx, symbols >>> Indexed('A', Idx('i', 2), Idx('j', 4), Idx('k', 8)).ranges [(0, 1), (0, 3), (0, 7)] >>> Indexed('A', Idx('i', 3), Idx('j', 3), Idx('k', 3)).ranges [(0, 2), (0, 2), (0, 2)] >>> x, y, z = symbols('x y z', integer=True) >>> Indexed('A', x, y, z).ranges [None, None, None]
- property rank¶
Returns the rank of the
Indexed
object.Examples
>>> from .. import Indexed, Idx, symbols >>> i, j, k, l, m = symbols('i:m', cls=Idx) >>> Indexed('A', i, j).rank 2 >>> q = Indexed('A', i, j, k, l, m) >>> q.rank 5 >>> q.rank == len(q.indices) True
- property shape¶
Returns a list with dimensions of each index.
Dimensions is a property of the array, not of the indices. Still, if the
IndexedBase
does not define a shape attribute, it is assumed that the ranges of the indices correspond to the shape of the array.>>> from .. import IndexedBase, Idx, symbols >>> n, m = symbols('n m', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', m) >>> A = IndexedBase('A', shape=(n, n)) >>> B = IndexedBase('B') >>> A[i, j].shape (n, n) >>> B[i, j].shape (m, m)
- class modelparameters.sympy.tensor.indexed.IndexedBase(label, shape=None, **kw_args)[source]¶
Bases:
Expr
,NotIterable
Represent the base or stem of an indexed object
The IndexedBase class represent an array that contains elements. The main purpose of this class is to allow the convenient creation of objects of the Indexed class. The __getitem__ method of IndexedBase returns an instance of Indexed. Alone, without indices, the IndexedBase class can be used as a notation for e.g. matrix equations, resembling what you could do with the Symbol class. But, the IndexedBase class adds functionality that is not available for Symbol instances:
An IndexedBase object can optionally store shape information. This can be used in to check array conformance and conditions for numpy broadcasting. (TODO)
An IndexedBase object implements syntactic sugar that allows easy symbolic representation of array operations, using implicit summation of repeated indices.
The IndexedBase object symbolizes a mathematical structure equivalent to arrays, and is recognized as such for code generation and automatic compilation and wrapping.
>>> from ..tensor import IndexedBase, Idx >>> from .. import symbols >>> A = IndexedBase('A'); A A >>> type(A) <class 'sympy.tensor.indexed.IndexedBase'>
When an IndexedBase object receives indices, it returns an array with named axes, represented by an Indexed object:
>>> i, j = symbols('i j', integer=True) >>> A[i, j, 2] A[i, j, 2] >>> type(A[i, j, 2]) <class 'sympy.tensor.indexed.Indexed'>
The IndexedBase constructor takes an optional shape argument. If given, it overrides any shape information in the indices. (But not the index ranges!)
>>> m, n, o, p = symbols('m n o p', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', n) >>> A[i, j].shape (m, n) >>> B = IndexedBase('B', shape=(o, p)) >>> B[i, j].shape (o, p)
- default_assumptions = {'commutative': True}¶
- is_Atom = True¶
- is_Symbol = True¶
- is_commutative = True¶
- is_symbol = True¶
- property label¶
Returns the label of the
IndexedBase
object.Examples
>>> from .. import IndexedBase >>> from ..abc import x, y >>> IndexedBase('A', shape=(x, y)).label A
- property offset¶
Returns the offset for the
IndexedBase
object.This is the value added to the resulting index when the 2D Indexed object is unrolled to a 1D form. Used in code generation.
Examples
>>> from ..printing import ccode >>> from ..tensor import IndexedBase, Idx >>> from .. import symbols >>> l, m, n, o = symbols('l m n o', integer=True) >>> A = IndexedBase('A', strides=(l, m, n), offset=o) >>> i, j, k = map(Idx, 'ijk') >>> ccode(A[i, j, k]) 'A[l*i + m*j + n*k + o]'
- property shape¶
Returns the shape of the
IndexedBase
object.Examples
>>> from .. import IndexedBase, Idx, Symbol >>> from ..abc import x, y >>> IndexedBase('A', shape=(x, y)).shape (x, y)
Note: If the shape of the
IndexedBase
is specified, it will override any shape information given by the indices.>>> A = IndexedBase('A', shape=(x, y)) >>> B = IndexedBase('B') >>> i = Idx('i', 2) >>> j = Idx('j', 1) >>> A[i, j].shape (x, y) >>> B[i, j].shape (2, 1)
- property strides¶
Returns the strided scheme for the
IndexedBase
object.Normally this is a tuple denoting the number of steps to take in the respective dimension when traversing an array. For code generation purposes strides=’C’ and strides=’F’ can also be used.
strides=’C’ would mean that code printer would unroll in row-major order and ‘F’ means unroll in column major order.
modelparameters.sympy.tensor.tensor module¶
This module defines tensors with abstract index notation.
The abstract index notation has been first formalized by Penrose.
Tensor indices are formal objects, with a tensor type; there is no notion of index range, it is only possible to assign the dimension, used to trace the Kronecker delta; the dimension can be a Symbol.
The Einstein summation convention is used. The covariant indices are indicated with a minus sign in front of the index.
For instance the tensor t = p(a)*A(b,c)*q(-c)
has the index c
contracted.
A tensor expression t
can be called; called with its
indices in sorted order it is equal to itself:
in the above example t(a, b) == t
;
one can call t
with different indices; t(c, d) == p(c)*A(d,a)*q(-a)
.
The contracted indices are dummy indices, internally they have no name, the indices being represented by a graph-like structure.
Tensors are put in canonical form using canon_bp
, which uses
the Butler-Portugal algorithm for canonicalization using the monoterm
symmetries of the tensors.
If there is a (anti)symmetric metric, the indices can be raised and lowered when the tensor is put in canonical form.
- class modelparameters.sympy.tensor.tensor.TIDS(components, free, dum)[source]¶
Bases:
CantSympify
DEPRECATED CLASS: DO NOT USE.
Tensor-index data structure. This contains internal data structures about components of a tensor expression, its free and dummy indices.
To create a
TIDS
object via the standard constructor, the required arguments areWARNING: this class is meant as an internal representation of tensor data structures and should not be directly accessed by end users.
- Parameters:
components (
TensorHead
objects representing the components of the tensor expression.) –free (Free indices in their internal representation.) –
dum (Dummy indices in their internal representation.) –
Examples
>>> from .tensor import TensorIndexType, tensor_indices, TIDS, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> TIDS([T], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)]) TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz)], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)])
Notes
In short, this has created the components, free and dummy indices for the internal representation of a tensor T(m0, m1, -m1, m3).
Free indices are represented as a list of triplets. The elements of each triplet identify a single free index and are
TensorIndex object
position inside the component
component number
Dummy indices are represented as a list of 4-plets. Each 4-plet stands for couple for contracted indices, their original TensorIndex is not stored as it is no longer required. The four elements of the 4-plet are
position inside the component of the first index.
position inside the component of the second index.
component number of the first index.
component number of the second index.
- canon_args()[source]¶
Returns
(g, dummies, msym, v)
, the entries ofcanonicalize
see
canonicalize
intensor_can.py
- contract_metric(g)[source]¶
Returns new TIDS and sign.
Sign is either 1 or -1, to correct the sign after metric contraction (for spinor indices).
- static free_dum_from_indices(*indices)[source]¶
Convert
indices
intofree
,dum
for single component tensorfree
list of tuples(index, pos, 0)
,where
pos
is the position of index in the list of indices formed by the component tensors
dum
list of tuples(pos_contr, pos_cov, 0, 0)
Examples
>>> from .tensor import TensorIndexType, tensor_indices, TIDS >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> TIDS.free_dum_from_indices(m0, m1, -m1, m3) ([(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)])
- static from_components_and_indices(components, indices)[source]¶
Create a new
TIDS
object fromcomponents
andindices
components
TensorHead
objects representing the componentsof the tensor expression.
indices
TensorIndex
objects, the indices. Contractions aredetected upon construction.
- get_components_with_free_indices()[source]¶
Get a list of components with their associated indices.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, TIDS, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> A = tensorhead('A', [Lorentz], [[1]]) >>> t = TIDS.from_components_and_indices([T], [m0, m1, -m1, m3]) >>> t.get_components_with_free_indices() [(T(Lorentz,Lorentz,Lorentz,Lorentz), [(m0, 0, 0), (m3, 3, 0)])]
- get_indices()[source]¶
Get a list of indices, creating new tensor indices to complete dummy indices.
- get_tensors()[source]¶
Get a list of
Tensor
objects having the sameTIDS
if multiplied by one another.
- static mul(f, g)[source]¶
The algorithms performing the multiplication of two
TIDS
instances.In short, it forms a new
TIDS
object, joining components and indices, checking that abstract indices are compatible, and possibly contracting them.
- perm2tensor(g, canon_bp=False)[source]¶
Returns a
TIDS
instance corresponding to the permutationg
g
permutation corresponding to the tensor in the representation used in canonicalizationcanon_bp
if True, theng
is the permutation corresponding to the canonical form of the tensor
- class modelparameters.sympy.tensor.tensor.TensAdd(*args, **kw_args)[source]¶
Bases:
TensExpr
Sum of tensors
- Parameters:
free_args (list of the free indices) –
- ``args``
- Type:
tuple of addends
- ``rank``
- Type:
rank of the tensor
- ``free_args``
- Type:
list of the free indices in sorted order
Notes
Sum of more than one tensor are put automatically in canonical form.
Examples
>>> from .tensor import TensorIndexType, tensorhead, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(a) + q(a); t p(a) + q(a) >>> t(b) p(b) + q(b)
Examples with components data added to the tensor expression:
>>> Lorentz.data = [1, -1, -1, -1] >>> a, b = tensor_indices('a, b', Lorentz) >>> p.data = [2, 3, -2, 7] >>> q.data = [2, 3, -2, 7] >>> t = p(a) + q(a); t p(a) + q(a) >>> t(b) p(b) + q(b)
The following are: 2**2 - 3**2 - 2**2 - 7**2 ==> -58
>>> (p(a)*p(-a)).data -58 >>> p(a)**2 -58
- canon_bp()[source]¶
canonicalize using the Butler-Portugal algorithm for canonicalization under monoterm symmetries.
- contract_metric(g)[source]¶
Raise or lower indices with the metric
g
- Parameters:
g (metric) –
contract_all (if True, eliminate all
g
which are contracted) –
Notes
see the
TensorIndexType
docstring for the contraction conventions
- property data¶
- default_assumptions = {'algebraic': False, 'commutative': False, 'complex': False, 'composite': False, 'even': False, 'imaginary': False, 'integer': False, 'irrational': False, 'negative': False, 'noninteger': False, 'nonnegative': False, 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': False, 'prime': False, 'rational': False, 'real': False, 'transcendental': False, 'zero': False}¶
- property free_args¶
- fun_eval(*index_tuples)[source]¶
Return a tensor with free indices substituted according to
index_tuples
- Parameters:
index_types (list of tuples
(old_index, new_index)
) –
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j) + A(i, -j) >>> t.fun_eval((i, k),(-j, l)) A(k, L_0)*B(l, -L_0) + A(k, l)
- is_algebraic = False¶
- is_commutative = False¶
- is_complex = False¶
- is_composite = False¶
- is_even = False¶
- is_imaginary = False¶
- is_integer = False¶
- is_irrational = False¶
- is_negative = False¶
- is_noninteger = False¶
- is_nonnegative = False¶
- is_nonpositive = False¶
- is_nonzero = False¶
- is_odd = False¶
- is_positive = False¶
- is_prime = False¶
- is_rational = False¶
- is_real = False¶
- is_transcendental = False¶
- is_zero = False¶
- property rank¶
- substitute_indices(*index_tuples)[source]¶
Return a tensor with free indices substituted according to
index_tuples
- Parameters:
index_types (list of tuples
(old_index, new_index)
) –
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.substitute_indices((i,j), (j, k)) A(j, L_0)*B(-L_0, -k)
- class modelparameters.sympy.tensor.tensor.TensExpr(*args)[source]¶
Bases:
Basic
Abstract base class for tensor expressions
Notes
A tensor expression is an expression formed by tensors; currently the sums of tensors are distributed.
A
TensExpr
can be aTensAdd
or aTensMul
.TensAdd
objects are put in canonical form using the Butler-Portugal algorithm for canonicalization under monoterm symmetries.TensMul
objects are formed by products of component tensors, and include a coefficient, which is a SymPy expression.In the internal representation contracted indices are represented by
(ipos1, ipos2, icomp1, icomp2)
, whereicomp1
is the position of the component tensor with contravariant index,ipos1
is the slot which the index occupies in that component tensor.Contracted indices are therefore nameless in the internal representation.
- default_assumptions = {'algebraic': False, 'commutative': False, 'complex': False, 'composite': False, 'even': False, 'imaginary': False, 'integer': False, 'irrational': False, 'negative': False, 'noninteger': False, 'nonnegative': False, 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': False, 'prime': False, 'rational': False, 'real': False, 'transcendental': False, 'zero': False}¶
- fun_eval(*index_tuples)[source]¶
Return a tensor with free indices substituted according to
index_tuples
index_types
list of tuples(old_index, new_index)
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.fun_eval((i, k),(-j, l)) A(k, L_0)*B(-L_0, l)
- get_matrix()[source]¶
Returns ndarray components data as a matrix, if components data are available and ndarray dimension does not exceed 2.
Examples
>>> from .tensor import TensorIndexType, tensorsymmetry, TensorType >>> from .. import ones >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1]*2) >>> S2 = TensorType([Lorentz]*2, sym2) >>> A = S2('A')
The tensor
A
is symmetric in its indices, as can be deduced by the[1, 1]
Young tableau when constructing sym2. One has to be careful to assign symmetric component data toA
, as the symmetry properties of data are currently not checked to be compatible with the defined tensor symmetry.>>> from .tensor import tensor_indices, tensorhead >>> Lorentz.data = [1, -1, -1, -1] >>> i0, i1 = tensor_indices('i0:2', Lorentz) >>> A.data = [[j+i for j in range(4)] for i in range(4)] >>> A(i0, i1).get_matrix() Matrix([ [0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]])
It is possible to perform usual operation on matrices, such as the matrix multiplication:
>>> A(i0, i1).get_matrix()*ones(4, 1) Matrix([ [ 6], [10], [14], [18]])
- is_algebraic = False¶
- is_commutative = False¶
- is_complex = False¶
- is_composite = False¶
- is_even = False¶
- is_imaginary = False¶
- is_integer = False¶
- is_irrational = False¶
- is_negative = False¶
- is_noninteger = False¶
- is_nonnegative = False¶
- is_nonpositive = False¶
- is_nonzero = False¶
- is_odd = False¶
- is_positive = False¶
- is_prime = False¶
- is_rational = False¶
- is_real = False¶
- is_transcendental = False¶
- is_zero = False¶
- class modelparameters.sympy.tensor.tensor.TensMul(*args, **kw_args)[source]¶
Bases:
TensExpr
Product of tensors
- Parameters:
coeff (SymPy coefficient of the tensor) –
args –
- ``components``
- Type:
list of
TensorHead
of the component tensors
- ``types``
- Type:
list of nonrepeated
TensorIndexType
- ``free``
- Type:
list of
(ind, ipos, icomp)
, see Notes
- ``dum``
- Type:
list of
(ipos1, ipos2, icomp1, icomp2)
, see Notes
- ``ext_rank``
- Type:
rank of the tensor counting the dummy indices
- ``rank``
- Type:
rank of the tensor
- ``coeff``
- Type:
SymPy coefficient of the tensor
- ``free_args``
- Type:
list of the free indices in sorted order
- ``is_canon_bp``
- Type:
True
if the tensor in in canonical form
Notes
args[0]
list ofTensorHead
of the component tensors.args[1]
list of(ind, ipos, icomp)
whereind
is a free index,ipos
is the slot position ofind
in theicomp
-th component tensor.args[2]
list of tuples representing dummy indices.(ipos1, ipos2, icomp1, icomp2)
indicates that the contravariant dummy index is theipos1
-th slot position in theicomp1
-th component tensor; the corresponding covariant index is in theipos2
slot position in theicomp2
-th component tensor.- canon_bp()[source]¶
Canonicalize using the Butler-Portugal algorithm for canonicalization under monoterm symmetries.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> A = tensorhead('A', [Lorentz]*2, [[2]]) >>> t = A(m0,-m1)*A(m1,-m0) >>> t.canon_bp() -A(L_0, L_1)*A(-L_0, -L_1) >>> t = A(m0,-m1)*A(m1,-m2)*A(m2,-m0) >>> t.canon_bp() 0
- property coeff¶
- property components¶
- contract_metric(g)[source]¶
Raise or lower indices with the metric
g
- Parameters:
g (metric) –
Notes
see the
TensorIndexType
docstring for the contraction conventionsExamples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(m0)*q(m1)*g(-m0, -m1) >>> t.canon_bp() metric(L_0, L_1)*p(-L_0)*q(-L_1) >>> t.contract_metric(g).canon_bp() p(L_0)*q(-L_0)
- property data¶
- default_assumptions = {'algebraic': False, 'commutative': False, 'complex': False, 'composite': False, 'even': False, 'imaginary': False, 'integer': False, 'irrational': False, 'negative': False, 'noninteger': False, 'nonnegative': False, 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': False, 'prime': False, 'rational': False, 'real': False, 'transcendental': False, 'zero': False}¶
- property dum¶
- property dum_in_args¶
- property ext_rank¶
- property free¶
- property free_args¶
- property free_in_args¶
- get_free_indices()[source]¶
Returns the list of free indices of the tensor
The indices are listed in the order in which they appear in the component tensors.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(m1)*g(m0,m2) >>> t.get_free_indices() [m1, m0, m2] >>> t2 = p(m1)*g(-m1, m2) >>> t2.get_free_indices() [m2]
- get_indices()[source]¶
Returns the list of indices of the tensor
The indices are listed in the order in which they appear in the component tensors. The dummy indices are given a name which does not collide with the names of the free indices.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(m1)*g(m0,m2) >>> t.get_indices() [m1, m0, m2] >>> t2 = p(m1)*g(-m1, m2) >>> t2.get_indices() [L_0, -L_0, m2]
- property index_types¶
- is_algebraic = False¶
- is_commutative = False¶
- is_complex = False¶
- is_composite = False¶
- is_even = False¶
- is_imaginary = False¶
- is_integer = False¶
- is_irrational = False¶
- is_negative = False¶
- is_noninteger = False¶
- is_nonnegative = False¶
- is_nonpositive = False¶
- is_nonzero = False¶
- is_odd = False¶
- is_positive = False¶
- is_prime = False¶
- is_rational = False¶
- is_real = False¶
- is_transcendental = False¶
- is_zero = False¶
- property nocoeff¶
- perm2tensor(g, is_canon_bp=False)[source]¶
Returns the tensor corresponding to the permutation
g
For further details, see the method in
TIDS
with the same name.
- property rank¶
- split()[source]¶
Returns a list of tensors, whose product is
self
Dummy indices contracted among different tensor components become free indices with the same name as the one used to represent the dummy indices.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(a,b)*B(-b,c) >>> t A(a, L_0)*B(-L_0, c) >>> t.split() [A(a, L_0), B(-L_0, c)]
- class modelparameters.sympy.tensor.tensor.Tensor(tensor_head, indices, **kw_args)[source]¶
Bases:
TensExpr
Base tensor class, i.e. this represents a tensor, the single unit to be put into an expression.
This object is usually created from a
TensorHead
, by attaching indices to it. Indices preceded by a minus sign are considered contravariant, otherwise covariant.Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType("Lorentz", dummy_fmt="L") >>> mu, nu = tensor_indices('mu nu', Lorentz) >>> A = tensorhead("A", [Lorentz, Lorentz], [[1], [1]]) >>> A(mu, -nu) A(mu, -nu) >>> A(mu, -mu) A(L_0, -L_0)
- property coeff¶
- commutes_with(other)[source]¶
- Parameters:
other –
- Returns:
0 commute 1 anticommute None neither commute nor anticommute
- property component¶
- property components¶
- property data¶
- default_assumptions = {'algebraic': False, 'commutative': False, 'complex': False, 'composite': False, 'even': False, 'imaginary': False, 'integer': False, 'irrational': False, 'negative': False, 'noninteger': False, 'nonnegative': False, 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': False, 'prime': False, 'rational': False, 'real': False, 'transcendental': False, 'zero': False}¶
- property dum¶
- property dum_in_args¶
- property ext_rank¶
- property free¶
- property free_args¶
- property free_in_args¶
- property index_types¶
- property indices¶
- is_algebraic = False¶
- property is_canon_bp¶
- is_commutative = False¶
- is_complex = False¶
- is_composite = False¶
- is_even = False¶
- is_imaginary = False¶
- is_integer = False¶
- is_irrational = False¶
- is_negative = False¶
- is_noninteger = False¶
- is_nonnegative = False¶
- is_nonpositive = False¶
- is_nonzero = False¶
- is_odd = False¶
- is_positive = False¶
- is_prime = False¶
- is_rational = False¶
- is_real = False¶
- is_transcendental = False¶
- is_zero = False¶
- property nocoeff¶
- perm2tensor(g, is_canon_bp=False)[source]¶
Returns the tensor corresponding to the permutation
g
For further details, see the method in
TIDS
with the same name.
- property rank¶
- class modelparameters.sympy.tensor.tensor.TensorHead(name, typ, comm=0, **kw_args)[source]¶
Bases:
Basic
Tensor head of the tensor
- Parameters:
name (name of the tensor) –
typ (list of TensorIndexType) –
comm (commutation group number) –
- ``name``
- ``index_types``
- ``rank``
- ``types``
- Type:
equal to
typ.types
- ``symmetry``
- Type:
equal to
typ.symmetry
- ``comm``
- Type:
commutation group
Notes
A
TensorHead
belongs to a commutation group, defined by a symbol on numbercomm
(see_TensorManager.set_comm
); tensors in a commutation group have the same commutation properties; by defaultcomm
is0
, the group of the commuting tensors.Examples
>>> from .tensor import TensorIndexType, tensorhead, TensorType >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> A = tensorhead('A', [Lorentz, Lorentz], [[1],[1]])
Examples with ndarray values, the components data assigned to the
TensorHead
object are assumed to be in a fully-contravariant representation. In case it is necessary to assign components data which represents the values of a non-fully covariant tensor, see the other examples.>>> from .tensor import tensor_indices, tensorhead >>> Lorentz.data = [1, -1, -1, -1] >>> i0, i1 = tensor_indices('i0:2', Lorentz) >>> A.data = [[j+2*i for j in range(4)] for i in range(4)]
in order to retrieve data, it is also necessary to specify abstract indices enclosed by round brackets, then numerical indices inside square brackets.
>>> A(i0, i1)[0, 0] 0 >>> A(i0, i1)[2, 3] == 3+2*2 True
Notice that square brackets create a valued tensor expression instance:
>>> A(i0, i1) A(i0, i1)
To view the data, just type:
>>> A.data [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7], [6, 7, 8, 9]]
Turning to a tensor expression, covariant indices get the corresponding components data corrected by the metric:
>>> A(i0, -i1).data [[0, -1, -2, -3], [2, -3, -4, -5], [4, -5, -6, -7], [6, -7, -8, -9]]
>>> A(-i0, -i1).data [[0, -1, -2, -3], [-2, 3, 4, 5], [-4, 5, 6, 7], [-6, 7, 8, 9]]
while if all indices are contravariant, the
ndarray
remains the same>>> A(i0, i1).data [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7], [6, 7, 8, 9]]
When all indices are contracted and components data are added to the tensor, accessing the data will return a scalar, no array object. In fact, arrays are dropped to scalars if they contain only one element.
>>> A(i0, -i0) A(L_0, -L_0) >>> A(i0, -i0).data -18
It is also possible to assign components data to an indexed tensor, i.e. a tensor with specified covariant and contravariant components. In this example, the covariant components data of the Electromagnetic tensor are injected into A:
>>> from .. import symbols >>> Ex, Ey, Ez, Bx, By, Bz = symbols('E_x E_y E_z B_x B_y B_z') >>> c = symbols('c', positive=True)
Let’s define F, an antisymmetric tensor, we have to assign an antisymmetric matrix to it, because [[2]] stands for the Young tableau representation of an antisymmetric set of two elements:
>>> F = tensorhead('A', [Lorentz, Lorentz], [[2]]) >>> F(-i0, -i1).data = [ ... [0, Ex/c, Ey/c, Ez/c], ... [-Ex/c, 0, -Bz, By], ... [-Ey/c, Bz, 0, -Bx], ... [-Ez/c, -By, Bx, 0]]
Now it is possible to retrieve the contravariant form of the Electromagnetic tensor:
>>> F(i0, i1).data [[0, -E_x/c, -E_y/c, -E_z/c], [E_x/c, 0, -B_z, B_y], [E_y/c, B_z, 0, -B_x], [E_z/c, -B_y, B_x, 0]]
and the mixed contravariant-covariant form:
>>> F(i0, -i1).data [[0, E_x/c, E_y/c, E_z/c], [E_x/c, 0, B_z, -B_y], [E_y/c, -B_z, 0, B_x], [E_z/c, B_y, -B_x, 0]]
To convert the darray to a SymPy matrix, just cast:
>>> F.data.tomatrix() Matrix([ [ 0, -E_x/c, -E_y/c, -E_z/c], [E_x/c, 0, -B_z, B_y], [E_y/c, B_z, 0, -B_x], [E_z/c, -B_y, B_x, 0]])
Still notice, in this last example, that accessing components data from a tensor without specifying the indices is equivalent to assume that all indices are contravariant.
It is also possible to store symbolic components data inside a tensor, for example, define a four-momentum-like tensor:
>>> from .. import symbols >>> P = tensorhead('P', [Lorentz], [[1]]) >>> E, px, py, pz = symbols('E p_x p_y p_z', positive=True) >>> P.data = [E, px, py, pz]
The contravariant and covariant components are, respectively:
>>> P(i0).data [E, p_x, p_y, p_z] >>> P(-i0).data [E, -p_x, -p_y, -p_z]
The contraction of a 1-index tensor by itself is usually indicated by a power by two:
>>> P(i0)**2 E**2 - p_x**2 - p_y**2 - p_z**2
As the power by two is clearly identical to P_mu P^mu, it is possible to simply contract the
TensorHead
object, without specifying the indices>>> P**2 E**2 - p_x**2 - p_y**2 - p_z**2
- property comm¶
- commutes_with(other)[source]¶
Returns
0
ifself
andother
commute,1
if they anticommute.Returns
None
ifself
andother
neither commute nor anticommute.
- property data¶
- default_assumptions = {'algebraic': False, 'commutative': False, 'complex': False, 'composite': False, 'even': False, 'imaginary': False, 'integer': False, 'irrational': False, 'negative': False, 'noninteger': False, 'nonnegative': False, 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': False, 'prime': False, 'rational': False, 'real': False, 'transcendental': False, 'zero': False}¶
- property index_types¶
- is_algebraic = False¶
- is_commutative = False¶
- is_complex = False¶
- is_composite = False¶
- is_even = False¶
- is_imaginary = False¶
- is_integer = False¶
- is_irrational = False¶
- is_negative = False¶
- is_noninteger = False¶
- is_nonnegative = False¶
- is_nonpositive = False¶
- is_nonzero = False¶
- is_odd = False¶
- is_positive = False¶
- is_prime = False¶
- is_rational = False¶
- is_real = False¶
- is_transcendental = False¶
- is_zero = False¶
- property name¶
- property rank¶
- property symmetry¶
- property typ¶
- property types¶
- class modelparameters.sympy.tensor.tensor.TensorIndex(name, tensortype, is_up=True)[source]¶
Bases:
Basic
Represents an abstract tensor index.
- Parameters:
name (name of the index, or
True
if you want it to be automatically assigned) –tensortype (
TensorIndexType
of the index) –is_up (flag for contravariant index) –
- ``name``
- ``tensortype``
- ``is_up``
Notes
Tensor indices are contracted with the Einstein summation convention.
An index can be in contravariant or in covariant form; in the latter case it is represented prepending a
-
to the index name.Dummy indices have a name with head given by
tensortype._dummy_fmt
Examples
>>> from .tensor import TensorIndexType, TensorIndex, TensorSymmetry, TensorType, get_symmetric_group_sgs >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i = TensorIndex('i', Lorentz); i i >>> sym1 = TensorSymmetry(*get_symmetric_group_sgs(1)) >>> S1 = TensorType([Lorentz], sym1) >>> A, B = S1('A,B') >>> A(i)*B(-i) A(L_0)*B(-L_0)
If you want the index name to be automatically assigned, just put
True
in thename
field, it will be generated using the reserved character_
in front of its name, in order to avoid conflicts with possible existing indices:>>> i0 = TensorIndex(True, Lorentz) >>> i0 _i0 >>> i1 = TensorIndex(True, Lorentz) >>> i1 _i1 >>> A(i0)*B(-i1) A(_i0)*B(-_i1) >>> A(i0)*B(-i0) A(L_0)*B(-L_0)
- default_assumptions = {}¶
- property is_up¶
- property name¶
- property tensor_index_type¶
- property tensortype¶
- class modelparameters.sympy.tensor.tensor.TensorIndexType(name, metric=False, dim=None, eps_dim=None, dummy_fmt=None)[source]¶
Bases:
Basic
A TensorIndexType is characterized by its name and its metric.
- Parameters:
name (name of the tensor type) –
metric (metric symmetry or metric object or
None
) –
dim : dimension, it can be a symbol or an integer or
None
eps_dim : dimension of the epsilon tensor
dummy_fmt : name of the head of dummy indices
- ``name``
- ``metric_name``
- Type:
it is ‘metric’ or metric.name
- ``metric_antisym``
- ``metric``
- Type:
the metric tensor
- ``delta``
- Type:
Kronecker delta
- ``epsilon``
- Type:
the
Levi-Civita epsilon
tensor
- ``dim``
- ``dim_eps``
- ``dummy_fmt``
- ``data``
- Type:
a property to add
ndarray
values, to work in a specified basis.
Notes
The
metric
parameter can be:metric = False
symmetric metric (in Riemannian geometry)metric = True
antisymmetric metric (for spinor calculus)metric = None
there is no metricmetric
can be an object havingname
andantisym
attributes.If there is a metric the metric is used to raise and lower indices.
In the case of antisymmetric metric, the following raising and lowering conventions will be adopted:
psi(a) = g(a, b)*psi(-b); chi(-a) = chi(b)*g(-b, -a)
g(-a, b) = delta(-a, b); g(b, -a) = -delta(a, -b)
where
delta(-a, b) = delta(b, -a)
is theKronecker delta
(seeTensorIndex
for the conventions on indices).If there is no metric it is not possible to raise or lower indices; e.g. the index of the defining representation of
SU(N)
is ‘covariant’ and the conjugate representation is ‘contravariant’; forN > 2
they are linearly independent.eps_dim
is by default equal todim
, if the latter is an integer; else it can be assigned (for use in naive dimensional regularization); ifeps_dim
is not an integerepsilon
isNone
.Examples
>>> from .tensor import TensorIndexType >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> Lorentz.metric metric(Lorentz,Lorentz)
Examples with metric components data added, this means it is working on a fixed basis:
>>> Lorentz.data = [1, -1, -1, -1] >>> Lorentz TensorIndexType(Lorentz, 0) >>> Lorentz.data [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]]
- property auto_index¶
- property auto_left¶
- property auto_right¶
- property data¶
- default_assumptions = {}¶
- property delta¶
- property dim¶
- property dummy_fmt¶
- property eps_dim¶
- property epsilon¶
- property name¶
- class modelparameters.sympy.tensor.tensor.TensorSymmetry(*args, **kw_args)[source]¶
Bases:
Basic
Monoterm symmetry of a tensor
- Parameters:
bsgs (tuple
(base, sgs)
BSGS of the symmetry of the tensor) –
- ``base``
- Type:
base of the BSGS
- ``generators``
- Type:
generators of the BSGS
- ``rank``
- Type:
rank of the tensor
Notes
A tensor can have an arbitrary monoterm symmetry provided by its BSGS. Multiterm symmetries, like the cyclic symmetry of the Riemann tensor, are not covered.
See also
sympy.combinatorics.tensor_can.get_symmetric_group_sgs
Examples
Define a symmetric tensor
>>> from .tensor import TensorIndexType, TensorSymmetry, TensorType, get_symmetric_group_sgs >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = TensorSymmetry(get_symmetric_group_sgs(2)) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V')
- property base¶
- default_assumptions = {}¶
- property generators¶
- property rank¶
- class modelparameters.sympy.tensor.tensor.TensorType(index_types, symmetry, **kw_args)[source]¶
Bases:
Basic
Class of tensor types.
- Parameters:
index_types (list of
TensorIndexType
of the tensor indices) –symmetry (
TensorSymmetry
of the tensor) –
- ``index_types``
- ``symmetry``
- ``types``
- Type:
list of
TensorIndexType
without repetitions
Examples
Define a symmetric tensor
>>> from .tensor import TensorIndexType, tensorsymmetry, TensorType >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1, 1]) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V')
- default_assumptions = {'algebraic': False, 'commutative': False, 'complex': False, 'composite': False, 'even': False, 'imaginary': False, 'integer': False, 'irrational': False, 'negative': False, 'noninteger': False, 'nonnegative': False, 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': False, 'prime': False, 'rational': False, 'real': False, 'transcendental': False, 'zero': False}¶
- property index_types¶
- is_algebraic = False¶
- is_commutative = False¶
- is_complex = False¶
- is_composite = False¶
- is_even = False¶
- is_imaginary = False¶
- is_integer = False¶
- is_irrational = False¶
- is_negative = False¶
- is_noninteger = False¶
- is_nonnegative = False¶
- is_nonpositive = False¶
- is_nonzero = False¶
- is_odd = False¶
- is_positive = False¶
- is_prime = False¶
- is_rational = False¶
- is_real = False¶
- is_transcendental = False¶
- is_zero = False¶
- property symmetry¶
- property types¶
- modelparameters.sympy.tensor.tensor.get_lines(ex, index_type)[source]¶
returns
(lines, traces, rest)
for an index type, wherelines
is the list of list of positions of a matrix line,traces
is the list of list of traced matrix lines,rest
is the rest of the elements ot the tensor.
- modelparameters.sympy.tensor.tensor.perm2tensor(t, g, is_canon_bp=False)[source]¶
Returns the tensor corresponding to the permutation
g
For further details, see the method in
TIDS
with the same name.
- modelparameters.sympy.tensor.tensor.riemann_cyclic(t2)[source]¶
replace each Riemann tensor with an equivalent expression satisfying the cyclic identity.
This trick is discussed in the reference guide to Cadabra.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead, riemann_cyclic >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> R = tensorhead('R', [Lorentz]*4, [[2, 2]]) >>> t = R(i,j,k,l)*(R(-i,-j,-k,-l) - 2*R(-i,-k,-j,-l)) >>> riemann_cyclic(t) 0
- modelparameters.sympy.tensor.tensor.riemann_cyclic_replace(t_r)[source]¶
replace Riemann tensor with an equivalent expression
R(m,n,p,q) -> 2/3*R(m,n,p,q) - 1/3*R(m,q,n,p) + 1/3*R(m,p,n,q)
- modelparameters.sympy.tensor.tensor.substitute_indices(t, *index_tuples)[source]¶
Return a tensor with free indices substituted according to
index_tuples
index_types
list of tuples(old_index, new_index)
Note: this method will neither raise or lower the indices, it will just replace their symbol.
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.substitute_indices((i,j), (j, k)) A(j, L_0)*B(-L_0, -k)
- modelparameters.sympy.tensor.tensor.tensor_indices(s, typ)[source]¶
Returns list of tensor indices given their names and their types
- Parameters:
s (string of comma separated names of indices) –
typ (
TensorIndexType
of the indices) –
Examples
>>> from .tensor import TensorIndexType, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz)
- modelparameters.sympy.tensor.tensor.tensorhead(name, typ, sym, comm=0)[source]¶
Function generating tensorhead(s).
- Parameters:
name (name or sequence of names (as in
symbol
)) –typ (index types) –
sym (same as
*args
intensorsymmetry
) –comm (commutation group number) –
_TensorManager.set_comm (see) –
Examples
>>> from .tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> A = tensorhead('A', [Lorentz]*2, [[1]*2]) >>> A(a, -b) A(a, -b)
- modelparameters.sympy.tensor.tensor.tensorsymmetry(*args)[source]¶
Return a
TensorSymmetry
object.One can represent a tensor with any monoterm slot symmetry group using a BSGS.
args
can be a BSGSargs[0]
baseargs[1]
sgsUsually tensors are in (direct products of) representations of the symmetric group;
args
can be a list of lists representing the shapes of Young tableauxNotes
For instance:
[[1]]
vector[[1]*n]
symmetric tensor of rankn
[[n]]
antisymmetric tensor of rankn
[[2, 2]]
monoterm slot symmetry of the Riemann tensor[[1],[1]]
vector*vector[[2],[1],[1]
(antisymmetric tensor)*vector*vectorNotice that with the shape
[2, 2]
we associate only the monoterm symmetries of the Riemann tensor; this is an abuse of notation, since the shape[2, 2]
corresponds usually to the irreducible representation characterized by the monoterm symmetries and by the cyclic symmetry.Examples
Symmetric tensor using a Young tableau
>>> from .tensor import TensorIndexType, TensorType, tensorsymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1, 1]) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V')
Symmetric tensor using a
BSGS
(base, strong generator set)>>> from .tensor import get_symmetric_group_sgs >>> sym2 = tensorsymmetry(*get_symmetric_group_sgs(2)) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V')
Module contents¶
A module to manipulate symbolic objects with indices including tensors