modelparameters.sympy.tensor package

Subpackages

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 structure

By dummy we mean indices that are summation indices.

The stucture of the expression is determined and described as follows:

  1. 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.

  2. 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 to 0 and range - 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 and oo to specify an unbounded range. For all other cases, both label and bounds must be declared as integers, e.g. if n is given as an argument then n.is_integer must return True.

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 both None:

>>> 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
exception modelparameters.sympy.tensor.indexed.IndexException[source]

Bases: Exception

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 via IndexedBase:

>>> A = IndexedBase('A')
>>> Indexed('A', i, j) == A[i, j]
True
property base

Returns the IndexedBase of the Indexed 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 are

WARNING: 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

  1. TensorIndex object

  2. position inside the component

  3. 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

  1. position inside the component of the first index.

  2. position inside the component of the second index.

  3. component number of the first index.

  4. component number of the second index.

canon_args()[source]

Returns (g, dummies, msym, v), the entries of canonicalize

see canonicalize in tensor_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 into free, dum for single component tensor

free 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 from components and indices

components TensorHead objects representing the components

of the tensor expression.

indices TensorIndex objects, the indices. Contractions are

detected 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 same TIDS 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 permutation g

g permutation corresponding to the tensor in the representation used in canonicalization

canon_bp if True, then g is the permutation corresponding to the canonical form of the tensor

sorted_components()[source]

Returns a TIDS with sorted components

The sorting is done taking into account the commutation group of the component tensors.

to_indices()[source]
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_delta(delta)[source]
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}
equals(other)[source]
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 a TensAdd or a TensMul.

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), where icomp1 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 to A, 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 of TensorHead of the component tensors.

args[1] list of (ind, ipos, icomp) where ind is a free index, ipos is the slot position of ind in the icomp-th component tensor.

args[2] list of tuples representing dummy indices. (ipos1, ipos2, icomp1, icomp2) indicates that the contravariant dummy index is the ipos1-th slot position in the icomp1-th component tensor; the corresponding covariant index is in the ipos2 slot position in the icomp2-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_delta(delta)[source]
contract_metric(g)[source]

Raise or lower indices with the metric g

Parameters:

g (metric) –

Notes

see the TensorIndexType docstring for the contraction conventions

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(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
equals(other)[source]
property ext_rank
property free
property free_args
property free_in_args
static from_data(coeff, components, free, dum, **kw_args)[source]
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
sorted_components()[source]

Returns a tensor product with sorted components.

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)]
substitute_indices(*index_tuples)[source]
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)
as_base_exp()[source]
canon_bp()[source]
property coeff
commutes_with(other)[source]
Parameters:

other

Returns:

0 commute 1 anticommute None neither commute nor anticommute

property component
property components
contract_delta(metric)[source]
contract_metric(g)[source]
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
equals(other)[source]
expand()[source]
property ext_rank
property free
property free_args
property free_in_args
get_free_indices()[source]

Get a list of free indices, corresponding to those of the tensor.

get_indices()[source]

Get a list of indices, corresponding to those of the tensor.

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
sorted_components()[source]
split()[source]
substitute_indices(*index_tuples)[source]
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 number comm (see _TensorManager.set_comm); tensors in a commutation group have the same commutation properties; by default comm is 0, 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 if self and other commute, 1 if they anticommute.

Returns None if self and other 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 the name 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 metric

metric can be an object having name and antisym 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 the Kronecker delta (see TensorIndex 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’; for N > 2 they are linearly independent.

eps_dim is by default equal to dim, if the latter is an integer; else it can be assigned (for use in naive dimensional regularization); if eps_dim is not an integer epsilon is None.

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
get_epsilon()[source]
get_kronecker_delta()[source]
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.canon_bp(p)[source]

Butler-Portugal canonicalization

modelparameters.sympy.tensor.tensor.components_canon_args(components)[source]
modelparameters.sympy.tensor.tensor.contract_metric(t, g)[source]
modelparameters.sympy.tensor.tensor.get_coeff(t)[source]
modelparameters.sympy.tensor.tensor.get_free_indices(t)[source]
modelparameters.sympy.tensor.tensor.get_index_structure(t)[source]
modelparameters.sympy.tensor.tensor.get_indices(t)[source]
modelparameters.sympy.tensor.tensor.get_lines(ex, index_type)[source]

returns (lines, traces, rest) for an index type, where lines 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.tensor_mul(*a)[source]

product of tensors

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 in tensorsymmetry) –

  • 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 BSGS args[0] base args[1] sgs

Usually tensors are in (direct products of) representations of the symmetric group; args can be a list of lists representing the shapes of Young tableaux

Notes

For instance: [[1]] vector [[1]*n] symmetric tensor of rank n [[n]] antisymmetric tensor of rank n [[2, 2]] monoterm slot symmetry of the Riemann tensor [[1],[1]] vector*vector [[2],[1],[1] (antisymmetric tensor)*vector*vector

Notice 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