Coverage for /usr/lib/python3/dist-packages/sympy/tensor/array/__init__.py: 100%
7 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1r"""
2N-dim array module for SymPy.
4Four classes are provided to handle N-dim arrays, given by the combinations
5dense/sparse (i.e. whether to store all elements or only the non-zero ones in
6memory) and mutable/immutable (immutable classes are SymPy objects, but cannot
7change after they have been created).
9Examples
10========
12The following examples show the usage of ``Array``. This is an abbreviation for
13``ImmutableDenseNDimArray``, that is an immutable and dense N-dim array, the
14other classes are analogous. For mutable classes it is also possible to change
15element values after the object has been constructed.
17Array construction can detect the shape of nested lists and tuples:
19>>> from sympy import Array
20>>> a1 = Array([[1, 2], [3, 4], [5, 6]])
21>>> a1
22[[1, 2], [3, 4], [5, 6]]
23>>> a1.shape
24(3, 2)
25>>> a1.rank()
262
27>>> from sympy.abc import x, y, z
28>>> a2 = Array([[[x, y], [z, x*z]], [[1, x*y], [1/x, x/y]]])
29>>> a2
30[[[x, y], [z, x*z]], [[1, x*y], [1/x, x/y]]]
31>>> a2.shape
32(2, 2, 2)
33>>> a2.rank()
343
36Otherwise one could pass a 1-dim array followed by a shape tuple:
38>>> m1 = Array(range(12), (3, 4))
39>>> m1
40[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
41>>> m2 = Array(range(12), (3, 2, 2))
42>>> m2
43[[[0, 1], [2, 3]], [[4, 5], [6, 7]], [[8, 9], [10, 11]]]
44>>> m2[1,1,1]
457
46>>> m2.reshape(4, 3)
47[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
49Slice support:
51>>> m2[:, 1, 1]
52[3, 7, 11]
54Elementwise derivative:
56>>> from sympy.abc import x, y, z
57>>> m3 = Array([x**3, x*y, z])
58>>> m3.diff(x)
59[3*x**2, y, 0]
60>>> m3.diff(z)
61[0, 0, 1]
63Multiplication with other SymPy expressions is applied elementwisely:
65>>> (1+x)*m3
66[x**3*(x + 1), x*y*(x + 1), z*(x + 1)]
68To apply a function to each element of the N-dim array, use ``applyfunc``:
70>>> m3.applyfunc(lambda x: x/2)
71[x**3/2, x*y/2, z/2]
73N-dim arrays can be converted to nested lists by the ``tolist()`` method:
75>>> m2.tolist()
76[[[0, 1], [2, 3]], [[4, 5], [6, 7]], [[8, 9], [10, 11]]]
77>>> isinstance(m2.tolist(), list)
78True
80If the rank is 2, it is possible to convert them to matrices with ``tomatrix()``:
82>>> m1.tomatrix()
83Matrix([
84[0, 1, 2, 3],
85[4, 5, 6, 7],
86[8, 9, 10, 11]])
88Products and contractions
89-------------------------
91Tensor product between arrays `A_{i_1,\ldots,i_n}` and `B_{j_1,\ldots,j_m}`
92creates the combined array `P = A \otimes B` defined as
94`P_{i_1,\ldots,i_n,j_1,\ldots,j_m} := A_{i_1,\ldots,i_n}\cdot B_{j_1,\ldots,j_m}.`
96It is available through ``tensorproduct(...)``:
98>>> from sympy import Array, tensorproduct
99>>> from sympy.abc import x,y,z,t
100>>> A = Array([x, y, z, t])
101>>> B = Array([1, 2, 3, 4])
102>>> tensorproduct(A, B)
103[[x, 2*x, 3*x, 4*x], [y, 2*y, 3*y, 4*y], [z, 2*z, 3*z, 4*z], [t, 2*t, 3*t, 4*t]]
105In case you don't want to evaluate the tensor product immediately, you can use
106``ArrayTensorProduct``, which creates an unevaluated tensor product expression:
108>>> from sympy.tensor.array.expressions import ArrayTensorProduct
109>>> ArrayTensorProduct(A, B)
110ArrayTensorProduct([x, y, z, t], [1, 2, 3, 4])
112Calling ``.as_explicit()`` on ``ArrayTensorProduct`` is equivalent to just calling
113``tensorproduct(...)``:
115>>> ArrayTensorProduct(A, B).as_explicit()
116[[x, 2*x, 3*x, 4*x], [y, 2*y, 3*y, 4*y], [z, 2*z, 3*z, 4*z], [t, 2*t, 3*t, 4*t]]
118Tensor product between a rank-1 array and a matrix creates a rank-3 array:
120>>> from sympy import eye
121>>> p1 = tensorproduct(A, eye(4))
122>>> p1
123[[[x, 0, 0, 0], [0, x, 0, 0], [0, 0, x, 0], [0, 0, 0, x]], [[y, 0, 0, 0], [0, y, 0, 0], [0, 0, y, 0], [0, 0, 0, y]], [[z, 0, 0, 0], [0, z, 0, 0], [0, 0, z, 0], [0, 0, 0, z]], [[t, 0, 0, 0], [0, t, 0, 0], [0, 0, t, 0], [0, 0, 0, t]]]
125Now, to get back `A_0 \otimes \mathbf{1}` one can access `p_{0,m,n}` by slicing:
127>>> p1[0,:,:]
128[[x, 0, 0, 0], [0, x, 0, 0], [0, 0, x, 0], [0, 0, 0, x]]
130Tensor contraction sums over the specified axes, for example contracting
131positions `a` and `b` means
133`A_{i_1,\ldots,i_a,\ldots,i_b,\ldots,i_n} \implies \sum_k A_{i_1,\ldots,k,\ldots,k,\ldots,i_n}`
135Remember that Python indexing is zero starting, to contract the a-th and b-th
136axes it is therefore necessary to specify `a-1` and `b-1`
138>>> from sympy import tensorcontraction
139>>> C = Array([[x, y], [z, t]])
141The matrix trace is equivalent to the contraction of a rank-2 array:
143`A_{m,n} \implies \sum_k A_{k,k}`
145>>> tensorcontraction(C, (0, 1))
146t + x
148To create an expression representing a tensor contraction that does not get
149evaluated immediately, use ``ArrayContraction``, which is equivalent to
150``tensorcontraction(...)`` if it is followed by ``.as_explicit()``:
152>>> from sympy.tensor.array.expressions import ArrayContraction
153>>> ArrayContraction(C, (0, 1))
154ArrayContraction([[x, y], [z, t]], (0, 1))
155>>> ArrayContraction(C, (0, 1)).as_explicit()
156t + x
158Matrix product is equivalent to a tensor product of two rank-2 arrays, followed
159by a contraction of the 2nd and 3rd axes (in Python indexing axes number 1, 2).
161`A_{m,n}\cdot B_{i,j} \implies \sum_k A_{m, k}\cdot B_{k, j}`
163>>> D = Array([[2, 1], [0, -1]])
164>>> tensorcontraction(tensorproduct(C, D), (1, 2))
165[[2*x, x - y], [2*z, -t + z]]
167One may verify that the matrix product is equivalent:
169>>> from sympy import Matrix
170>>> Matrix([[x, y], [z, t]])*Matrix([[2, 1], [0, -1]])
171Matrix([
172[2*x, x - y],
173[2*z, -t + z]])
175or equivalently
177>>> C.tomatrix()*D.tomatrix()
178Matrix([
179[2*x, x - y],
180[2*z, -t + z]])
182Diagonal operator
183-----------------
185The ``tensordiagonal`` function acts in a similar manner as ``tensorcontraction``,
186but the joined indices are not summed over, for example diagonalizing
187positions `a` and `b` means
189`A_{i_1,\ldots,i_a,\ldots,i_b,\ldots,i_n} \implies A_{i_1,\ldots,k,\ldots,k,\ldots,i_n}
190\implies \tilde{A}_{i_1,\ldots,i_{a-1},i_{a+1},\ldots,i_{b-1},i_{b+1},\ldots,i_n,k}`
192where `\tilde{A}` is the array equivalent to the diagonal of `A` at positions
193`a` and `b` moved to the last index slot.
195Compare the difference between contraction and diagonal operators:
197>>> from sympy import tensordiagonal
198>>> from sympy.abc import a, b, c, d
199>>> m = Matrix([[a, b], [c, d]])
200>>> tensorcontraction(m, [0, 1])
201a + d
202>>> tensordiagonal(m, [0, 1])
203[a, d]
205In short, no summation occurs with ``tensordiagonal``.
208Derivatives by array
209--------------------
211The usual derivative operation may be extended to support derivation with
212respect to arrays, provided that all elements in the that array are symbols or
213expressions suitable for derivations.
215The definition of a derivative by an array is as follows: given the array
216`A_{i_1, \ldots, i_N}` and the array `X_{j_1, \ldots, j_M}`
217the derivative of arrays will return a new array `B` defined by
219`B_{j_1,\ldots,j_M,i_1,\ldots,i_N} := \frac{\partial A_{i_1,\ldots,i_N}}{\partial X_{j_1,\ldots,j_M}}`
221The function ``derive_by_array`` performs such an operation:
223>>> from sympy import derive_by_array
224>>> from sympy.abc import x, y, z, t
225>>> from sympy import sin, exp
227With scalars, it behaves exactly as the ordinary derivative:
229>>> derive_by_array(sin(x*y), x)
230y*cos(x*y)
232Scalar derived by an array basis:
234>>> derive_by_array(sin(x*y), [x, y, z])
235[y*cos(x*y), x*cos(x*y), 0]
237Deriving array by an array basis: `B^{nm} := \frac{\partial A^m}{\partial x^n}`
239>>> basis = [x, y, z]
240>>> ax = derive_by_array([exp(x), sin(y*z), t], basis)
241>>> ax
242[[exp(x), 0, 0], [0, z*cos(y*z), 0], [0, y*cos(y*z), 0]]
244Contraction of the resulting array: `\sum_m \frac{\partial A^m}{\partial x^m}`
246>>> tensorcontraction(ax, (0, 1))
247z*cos(y*z) + exp(x)
249"""
251from .dense_ndim_array import MutableDenseNDimArray, ImmutableDenseNDimArray, DenseNDimArray
252from .sparse_ndim_array import MutableSparseNDimArray, ImmutableSparseNDimArray, SparseNDimArray
253from .ndim_array import NDimArray, ArrayKind
254from .arrayop import tensorproduct, tensorcontraction, tensordiagonal, derive_by_array, permutedims
255from .array_comprehension import ArrayComprehension, ArrayComprehensionMap
257Array = ImmutableDenseNDimArray
259__all__ = [
260 'MutableDenseNDimArray', 'ImmutableDenseNDimArray', 'DenseNDimArray',
262 'MutableSparseNDimArray', 'ImmutableSparseNDimArray', 'SparseNDimArray',
264 'NDimArray', 'ArrayKind',
266 'tensorproduct', 'tensorcontraction', 'tensordiagonal', 'derive_by_array',
268 'permutedims', 'ArrayComprehension', 'ArrayComprehensionMap',
270 'Array',
271]