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

1r""" 

2N-dim array module for SymPy. 

3 

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

8 

9Examples 

10======== 

11 

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. 

16 

17Array construction can detect the shape of nested lists and tuples: 

18 

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 

35 

36Otherwise one could pass a 1-dim array followed by a shape tuple: 

37 

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]] 

48 

49Slice support: 

50 

51>>> m2[:, 1, 1] 

52[3, 7, 11] 

53 

54Elementwise derivative: 

55 

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] 

62 

63Multiplication with other SymPy expressions is applied elementwisely: 

64 

65>>> (1+x)*m3 

66[x**3*(x + 1), x*y*(x + 1), z*(x + 1)] 

67 

68To apply a function to each element of the N-dim array, use ``applyfunc``: 

69 

70>>> m3.applyfunc(lambda x: x/2) 

71[x**3/2, x*y/2, z/2] 

72 

73N-dim arrays can be converted to nested lists by the ``tolist()`` method: 

74 

75>>> m2.tolist() 

76[[[0, 1], [2, 3]], [[4, 5], [6, 7]], [[8, 9], [10, 11]]] 

77>>> isinstance(m2.tolist(), list) 

78True 

79 

80If the rank is 2, it is possible to convert them to matrices with ``tomatrix()``: 

81 

82>>> m1.tomatrix() 

83Matrix([ 

84[0, 1, 2, 3], 

85[4, 5, 6, 7], 

86[8, 9, 10, 11]]) 

87 

88Products and contractions 

89------------------------- 

90 

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 

93 

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}.` 

95 

96It is available through ``tensorproduct(...)``: 

97 

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]] 

104 

105In case you don't want to evaluate the tensor product immediately, you can use 

106``ArrayTensorProduct``, which creates an unevaluated tensor product expression: 

107 

108>>> from sympy.tensor.array.expressions import ArrayTensorProduct 

109>>> ArrayTensorProduct(A, B) 

110ArrayTensorProduct([x, y, z, t], [1, 2, 3, 4]) 

111 

112Calling ``.as_explicit()`` on ``ArrayTensorProduct`` is equivalent to just calling 

113``tensorproduct(...)``: 

114 

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]] 

117 

118Tensor product between a rank-1 array and a matrix creates a rank-3 array: 

119 

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]]] 

124 

125Now, to get back `A_0 \otimes \mathbf{1}` one can access `p_{0,m,n}` by slicing: 

126 

127>>> p1[0,:,:] 

128[[x, 0, 0, 0], [0, x, 0, 0], [0, 0, x, 0], [0, 0, 0, x]] 

129 

130Tensor contraction sums over the specified axes, for example contracting 

131positions `a` and `b` means 

132 

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}` 

134 

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` 

137 

138>>> from sympy import tensorcontraction 

139>>> C = Array([[x, y], [z, t]]) 

140 

141The matrix trace is equivalent to the contraction of a rank-2 array: 

142 

143`A_{m,n} \implies \sum_k A_{k,k}` 

144 

145>>> tensorcontraction(C, (0, 1)) 

146t + x 

147 

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()``: 

151 

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 

157 

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

160 

161`A_{m,n}\cdot B_{i,j} \implies \sum_k A_{m, k}\cdot B_{k, j}` 

162 

163>>> D = Array([[2, 1], [0, -1]]) 

164>>> tensorcontraction(tensorproduct(C, D), (1, 2)) 

165[[2*x, x - y], [2*z, -t + z]] 

166 

167One may verify that the matrix product is equivalent: 

168 

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]]) 

174 

175or equivalently 

176 

177>>> C.tomatrix()*D.tomatrix() 

178Matrix([ 

179[2*x, x - y], 

180[2*z, -t + z]]) 

181 

182Diagonal operator 

183----------------- 

184 

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 

188 

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}` 

191 

192where `\tilde{A}` is the array equivalent to the diagonal of `A` at positions 

193`a` and `b` moved to the last index slot. 

194 

195Compare the difference between contraction and diagonal operators: 

196 

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] 

204 

205In short, no summation occurs with ``tensordiagonal``. 

206 

207 

208Derivatives by array 

209-------------------- 

210 

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. 

214 

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 

218 

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}}` 

220 

221The function ``derive_by_array`` performs such an operation: 

222 

223>>> from sympy import derive_by_array 

224>>> from sympy.abc import x, y, z, t 

225>>> from sympy import sin, exp 

226 

227With scalars, it behaves exactly as the ordinary derivative: 

228 

229>>> derive_by_array(sin(x*y), x) 

230y*cos(x*y) 

231 

232Scalar derived by an array basis: 

233 

234>>> derive_by_array(sin(x*y), [x, y, z]) 

235[y*cos(x*y), x*cos(x*y), 0] 

236 

237Deriving array by an array basis: `B^{nm} := \frac{\partial A^m}{\partial x^n}` 

238 

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]] 

243 

244Contraction of the resulting array: `\sum_m \frac{\partial A^m}{\partial x^m}` 

245 

246>>> tensorcontraction(ax, (0, 1)) 

247z*cos(y*z) + exp(x) 

248 

249""" 

250 

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 

256 

257Array = ImmutableDenseNDimArray 

258 

259__all__ = [ 

260 'MutableDenseNDimArray', 'ImmutableDenseNDimArray', 'DenseNDimArray', 

261 

262 'MutableSparseNDimArray', 'ImmutableSparseNDimArray', 'SparseNDimArray', 

263 

264 'NDimArray', 'ArrayKind', 

265 

266 'tensorproduct', 'tensorcontraction', 'tensordiagonal', 'derive_by_array', 

267 

268 'permutedims', 'ArrayComprehension', 'ArrayComprehensionMap', 

269 

270 'Array', 

271]