test_indexing.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. from sympy.concrete.summations import Sum
  2. from sympy.core.symbol import symbols, Symbol, Dummy
  3. from sympy.functions.elementary.miscellaneous import sqrt
  4. from sympy.functions.special.tensor_functions import KroneckerDelta
  5. from sympy.matrices.dense import eye
  6. from sympy.matrices.expressions.blockmatrix import BlockMatrix
  7. from sympy.matrices.expressions.hadamard import HadamardPower
  8. from sympy.matrices.expressions.matexpr import (MatrixSymbol,
  9. MatrixExpr, MatrixElement)
  10. from sympy.matrices.expressions.matpow import MatPow
  11. from sympy.matrices.expressions.special import (ZeroMatrix, Identity,
  12. OneMatrix)
  13. from sympy.matrices.expressions.trace import Trace, trace
  14. from sympy.matrices.immutable import ImmutableMatrix
  15. from sympy.tensor.array.expressions.array_expressions import ArrayTensorProduct
  16. from sympy.testing.pytest import XFAIL, raises
  17. k, l, m, n = symbols('k l m n', integer=True)
  18. i, j = symbols('i j', integer=True)
  19. W = MatrixSymbol('W', k, l)
  20. X = MatrixSymbol('X', l, m)
  21. Y = MatrixSymbol('Y', l, m)
  22. Z = MatrixSymbol('Z', m, n)
  23. X1 = MatrixSymbol('X1', m, m)
  24. X2 = MatrixSymbol('X2', m, m)
  25. X3 = MatrixSymbol('X3', m, m)
  26. X4 = MatrixSymbol('X4', m, m)
  27. A = MatrixSymbol('A', 2, 2)
  28. B = MatrixSymbol('B', 2, 2)
  29. x = MatrixSymbol('x', 1, 2)
  30. y = MatrixSymbol('x', 2, 1)
  31. def test_symbolic_indexing():
  32. x12 = X[1, 2]
  33. assert all(s in str(x12) for s in ['1', '2', X.name])
  34. # We don't care about the exact form of this. We do want to make sure
  35. # that all of these features are present
  36. def test_add_index():
  37. assert (X + Y)[i, j] == X[i, j] + Y[i, j]
  38. def test_mul_index():
  39. assert (A*y)[0, 0] == A[0, 0]*y[0, 0] + A[0, 1]*y[1, 0]
  40. assert (A*B).as_mutable() == (A.as_mutable() * B.as_mutable())
  41. X = MatrixSymbol('X', n, m)
  42. Y = MatrixSymbol('Y', m, k)
  43. result = (X*Y)[4,2]
  44. expected = Sum(X[4, i]*Y[i, 2], (i, 0, m - 1))
  45. assert result.args[0].dummy_eq(expected.args[0], i)
  46. assert result.args[1][1:] == expected.args[1][1:]
  47. def test_pow_index():
  48. Q = MatPow(A, 2)
  49. assert Q[0, 0] == A[0, 0]**2 + A[0, 1]*A[1, 0]
  50. n = symbols("n")
  51. Q2 = A**n
  52. assert Q2[0, 0] == 2*(
  53. -sqrt((A[0, 0] + A[1, 1])**2 - 4*A[0, 0]*A[1, 1] +
  54. 4*A[0, 1]*A[1, 0])/2 + A[0, 0]/2 + A[1, 1]/2
  55. )**n * \
  56. A[0, 1]*A[1, 0]/(
  57. (sqrt(A[0, 0]**2 - 2*A[0, 0]*A[1, 1] + 4*A[0, 1]*A[1, 0] +
  58. A[1, 1]**2) + A[0, 0] - A[1, 1])*
  59. sqrt(A[0, 0]**2 - 2*A[0, 0]*A[1, 1] + 4*A[0, 1]*A[1, 0] + A[1, 1]**2)
  60. ) - 2*(
  61. sqrt((A[0, 0] + A[1, 1])**2 - 4*A[0, 0]*A[1, 1] +
  62. 4*A[0, 1]*A[1, 0])/2 + A[0, 0]/2 + A[1, 1]/2
  63. )**n * A[0, 1]*A[1, 0]/(
  64. (-sqrt(A[0, 0]**2 - 2*A[0, 0]*A[1, 1] + 4*A[0, 1]*A[1, 0] +
  65. A[1, 1]**2) + A[0, 0] - A[1, 1])*
  66. sqrt(A[0, 0]**2 - 2*A[0, 0]*A[1, 1] + 4*A[0, 1]*A[1, 0] + A[1, 1]**2)
  67. )
  68. def test_transpose_index():
  69. assert X.T[i, j] == X[j, i]
  70. def test_Identity_index():
  71. I = Identity(3)
  72. assert I[0, 0] == I[1, 1] == I[2, 2] == 1
  73. assert I[1, 0] == I[0, 1] == I[2, 1] == 0
  74. assert I[i, 0].delta_range == (0, 2)
  75. raises(IndexError, lambda: I[3, 3])
  76. def test_block_index():
  77. I = Identity(3)
  78. Z = ZeroMatrix(3, 3)
  79. B = BlockMatrix([[I, I], [I, I]])
  80. e3 = ImmutableMatrix(eye(3))
  81. BB = BlockMatrix([[e3, e3], [e3, e3]])
  82. assert B[0, 0] == B[3, 0] == B[0, 3] == B[3, 3] == 1
  83. assert B[4, 3] == B[5, 1] == 0
  84. BB = BlockMatrix([[e3, e3], [e3, e3]])
  85. assert B.as_explicit() == BB.as_explicit()
  86. BI = BlockMatrix([[I, Z], [Z, I]])
  87. assert BI.as_explicit().equals(eye(6))
  88. def test_block_index_symbolic():
  89. # Note that these matrices may be zero-sized and indices may be negative, which causes
  90. # all naive simplifications given in the comments to be invalid
  91. A1 = MatrixSymbol('A1', n, k)
  92. A2 = MatrixSymbol('A2', n, l)
  93. A3 = MatrixSymbol('A3', m, k)
  94. A4 = MatrixSymbol('A4', m, l)
  95. A = BlockMatrix([[A1, A2], [A3, A4]])
  96. assert A[0, 0] == MatrixElement(A, 0, 0) # Cannot be A1[0, 0]
  97. assert A[n - 1, k - 1] == A1[n - 1, k - 1]
  98. assert A[n, k] == A4[0, 0]
  99. assert A[n + m - 1, 0] == MatrixElement(A, n + m - 1, 0) # Cannot be A3[m - 1, 0]
  100. assert A[0, k + l - 1] == MatrixElement(A, 0, k + l - 1) # Cannot be A2[0, l - 1]
  101. assert A[n + m - 1, k + l - 1] == MatrixElement(A, n + m - 1, k + l - 1) # Cannot be A4[m - 1, l - 1]
  102. assert A[i, j] == MatrixElement(A, i, j)
  103. assert A[n + i, k + j] == MatrixElement(A, n + i, k + j) # Cannot be A4[i, j]
  104. assert A[n - i - 1, k - j - 1] == MatrixElement(A, n - i - 1, k - j - 1) # Cannot be A1[n - i - 1, k - j - 1]
  105. def test_block_index_symbolic_nonzero():
  106. # All invalid simplifications from test_block_index_symbolic() that become valid if all
  107. # matrices have nonzero size and all indices are nonnegative
  108. k, l, m, n = symbols('k l m n', integer=True, positive=True)
  109. i, j = symbols('i j', integer=True, nonnegative=True)
  110. A1 = MatrixSymbol('A1', n, k)
  111. A2 = MatrixSymbol('A2', n, l)
  112. A3 = MatrixSymbol('A3', m, k)
  113. A4 = MatrixSymbol('A4', m, l)
  114. A = BlockMatrix([[A1, A2], [A3, A4]])
  115. assert A[0, 0] == A1[0, 0]
  116. assert A[n + m - 1, 0] == A3[m - 1, 0]
  117. assert A[0, k + l - 1] == A2[0, l - 1]
  118. assert A[n + m - 1, k + l - 1] == A4[m - 1, l - 1]
  119. assert A[i, j] == MatrixElement(A, i, j)
  120. assert A[n + i, k + j] == A4[i, j]
  121. assert A[n - i - 1, k - j - 1] == A1[n - i - 1, k - j - 1]
  122. assert A[2 * n, 2 * k] == A4[n, k]
  123. def test_block_index_large():
  124. n, m, k = symbols('n m k', integer=True, positive=True)
  125. i = symbols('i', integer=True, nonnegative=True)
  126. A1 = MatrixSymbol('A1', n, n)
  127. A2 = MatrixSymbol('A2', n, m)
  128. A3 = MatrixSymbol('A3', n, k)
  129. A4 = MatrixSymbol('A4', m, n)
  130. A5 = MatrixSymbol('A5', m, m)
  131. A6 = MatrixSymbol('A6', m, k)
  132. A7 = MatrixSymbol('A7', k, n)
  133. A8 = MatrixSymbol('A8', k, m)
  134. A9 = MatrixSymbol('A9', k, k)
  135. A = BlockMatrix([[A1, A2, A3], [A4, A5, A6], [A7, A8, A9]])
  136. assert A[n + i, n + i] == MatrixElement(A, n + i, n + i)
  137. @XFAIL
  138. def test_block_index_symbolic_fail():
  139. # To make this work, symbolic matrix dimensions would need to be somehow assumed nonnegative
  140. # even if the symbols aren't specified as such. Then 2 * n < n would correctly evaluate to
  141. # False in BlockMatrix._entry()
  142. A1 = MatrixSymbol('A1', n, 1)
  143. A2 = MatrixSymbol('A2', m, 1)
  144. A = BlockMatrix([[A1], [A2]])
  145. assert A[2 * n, 0] == A2[n, 0]
  146. def test_slicing():
  147. A.as_explicit()[0, :] # does not raise an error
  148. def test_errors():
  149. raises(IndexError, lambda: Identity(2)[1, 2, 3, 4, 5])
  150. raises(IndexError, lambda: Identity(2)[[1, 2, 3, 4, 5]])
  151. def test_matrix_expression_to_indices():
  152. i, j = symbols("i, j")
  153. i1, i2, i3 = symbols("i_1:4")
  154. def replace_dummies(expr):
  155. repl = {i: Symbol(i.name) for i in expr.atoms(Dummy)}
  156. return expr.xreplace(repl)
  157. expr = W*X*Z
  158. assert replace_dummies(expr._entry(i, j)) == \
  159. Sum(W[i, i1]*X[i1, i2]*Z[i2, j], (i1, 0, l-1), (i2, 0, m-1))
  160. assert MatrixExpr.from_index_summation(expr._entry(i, j)) == expr
  161. expr = Z.T*X.T*W.T
  162. assert replace_dummies(expr._entry(i, j)) == \
  163. Sum(W[j, i2]*X[i2, i1]*Z[i1, i], (i1, 0, m-1), (i2, 0, l-1))
  164. assert MatrixExpr.from_index_summation(expr._entry(i, j), i) == expr
  165. expr = W*X*Z + W*Y*Z
  166. assert replace_dummies(expr._entry(i, j)) == \
  167. Sum(W[i, i1]*X[i1, i2]*Z[i2, j], (i1, 0, l-1), (i2, 0, m-1)) +\
  168. Sum(W[i, i1]*Y[i1, i2]*Z[i2, j], (i1, 0, l-1), (i2, 0, m-1))
  169. assert MatrixExpr.from_index_summation(expr._entry(i, j)) == expr
  170. expr = 2*W*X*Z + 3*W*Y*Z
  171. assert replace_dummies(expr._entry(i, j)) == \
  172. 2*Sum(W[i, i1]*X[i1, i2]*Z[i2, j], (i1, 0, l-1), (i2, 0, m-1)) +\
  173. 3*Sum(W[i, i1]*Y[i1, i2]*Z[i2, j], (i1, 0, l-1), (i2, 0, m-1))
  174. assert MatrixExpr.from_index_summation(expr._entry(i, j)) == expr
  175. expr = W*(X + Y)*Z
  176. assert replace_dummies(expr._entry(i, j)) == \
  177. Sum(W[i, i1]*(X[i1, i2] + Y[i1, i2])*Z[i2, j], (i1, 0, l-1), (i2, 0, m-1))
  178. assert MatrixExpr.from_index_summation(expr._entry(i, j)) == expr
  179. expr = A*B**2*A
  180. #assert replace_dummies(expr._entry(i, j)) == \
  181. # Sum(A[i, i1]*B[i1, i2]*B[i2, i3]*A[i3, j], (i1, 0, 1), (i2, 0, 1), (i3, 0, 1))
  182. # Check that different dummies are used in sub-multiplications:
  183. expr = (X1*X2 + X2*X1)*X3
  184. assert replace_dummies(expr._entry(i, j)) == \
  185. Sum((Sum(X1[i, i2] * X2[i2, i1], (i2, 0, m - 1)) + Sum(X1[i3, i1] * X2[i, i3], (i3, 0, m - 1))) * X3[
  186. i1, j], (i1, 0, m - 1))
  187. def test_matrix_expression_from_index_summation():
  188. from sympy.abc import a,b,c,d
  189. A = MatrixSymbol("A", k, k)
  190. B = MatrixSymbol("B", k, k)
  191. C = MatrixSymbol("C", k, k)
  192. w1 = MatrixSymbol("w1", k, 1)
  193. i0, i1, i2, i3, i4 = symbols("i0:5", cls=Dummy)
  194. expr = Sum(W[a,b]*X[b,c]*Z[c,d], (b, 0, l-1), (c, 0, m-1))
  195. assert MatrixExpr.from_index_summation(expr, a) == W*X*Z
  196. expr = Sum(W.T[b,a]*X[b,c]*Z[c,d], (b, 0, l-1), (c, 0, m-1))
  197. assert MatrixExpr.from_index_summation(expr, a) == W*X*Z
  198. expr = Sum(A[b, a]*B[b, c]*C[c, d], (b, 0, k-1), (c, 0, k-1))
  199. assert MatrixSymbol.from_index_summation(expr, a) == A.T*B*C
  200. expr = Sum(A[b, a]*B[c, b]*C[c, d], (b, 0, k-1), (c, 0, k-1))
  201. assert MatrixSymbol.from_index_summation(expr, a) == A.T*B.T*C
  202. expr = Sum(C[c, d]*A[b, a]*B[c, b], (b, 0, k-1), (c, 0, k-1))
  203. assert MatrixSymbol.from_index_summation(expr, a) == A.T*B.T*C
  204. expr = Sum(A[a, b] + B[a, b], (a, 0, k-1), (b, 0, k-1))
  205. assert MatrixExpr.from_index_summation(expr, a) == OneMatrix(1, k)*A*OneMatrix(k, 1) + OneMatrix(1, k)*B*OneMatrix(k, 1)
  206. expr = Sum(A[a, b]**2, (a, 0, k - 1), (b, 0, k - 1))
  207. assert MatrixExpr.from_index_summation(expr, a) == Trace(A * A.T)
  208. expr = Sum(A[a, b]**3, (a, 0, k - 1), (b, 0, k - 1))
  209. assert MatrixExpr.from_index_summation(expr, a) == Trace(HadamardPower(A.T, 2) * A)
  210. expr = Sum((A[a, b] + B[a, b])*C[b, c], (b, 0, k-1))
  211. assert MatrixExpr.from_index_summation(expr, a) == (A+B)*C
  212. expr = Sum((A[a, b] + B[b, a])*C[b, c], (b, 0, k-1))
  213. assert MatrixExpr.from_index_summation(expr, a) == (A+B.T)*C
  214. expr = Sum(A[a, b]*A[b, c]*A[c, d], (b, 0, k-1), (c, 0, k-1))
  215. assert MatrixExpr.from_index_summation(expr, a) == A**3
  216. expr = Sum(A[a, b]*A[b, c]*B[c, d], (b, 0, k-1), (c, 0, k-1))
  217. assert MatrixExpr.from_index_summation(expr, a) == A**2*B
  218. # Parse the trace of a matrix:
  219. expr = Sum(A[a, a], (a, 0, k-1))
  220. assert MatrixExpr.from_index_summation(expr, None) == trace(A)
  221. expr = Sum(A[a, a]*B[b, c]*C[c, d], (a, 0, k-1), (c, 0, k-1))
  222. assert MatrixExpr.from_index_summation(expr, b) == trace(A)*B*C
  223. # Check wrong sum ranges (should raise an exception):
  224. ## Case 1: 0 to m instead of 0 to m-1
  225. expr = Sum(W[a,b]*X[b,c]*Z[c,d], (b, 0, l-1), (c, 0, m))
  226. raises(ValueError, lambda: MatrixExpr.from_index_summation(expr, a))
  227. ## Case 2: 1 to m-1 instead of 0 to m-1
  228. expr = Sum(W[a,b]*X[b,c]*Z[c,d], (b, 0, l-1), (c, 1, m-1))
  229. raises(ValueError, lambda: MatrixExpr.from_index_summation(expr, a))
  230. # Parse nested sums:
  231. expr = Sum(A[a, b]*Sum(B[b, c]*C[c, d], (c, 0, k-1)), (b, 0, k-1))
  232. assert MatrixExpr.from_index_summation(expr, a) == A*B*C
  233. # Test Kronecker delta:
  234. expr = Sum(A[a, b]*KroneckerDelta(b, c)*B[c, d], (b, 0, k-1), (c, 0, k-1))
  235. assert MatrixExpr.from_index_summation(expr, a) == A*B
  236. expr = Sum(KroneckerDelta(i1, m)*KroneckerDelta(i2, n)*A[i, i1]*A[j, i2], (i1, 0, k-1), (i2, 0, k-1))
  237. assert MatrixExpr.from_index_summation(expr, m) == ArrayTensorProduct(A.T, A)
  238. # Test numbered indices:
  239. expr = Sum(A[i1, i2]*w1[i2, 0], (i2, 0, k-1))
  240. assert MatrixExpr.from_index_summation(expr, i1) == MatrixElement(A*w1, i1, 0)
  241. expr = Sum(A[i1, i2]*B[i2, 0], (i2, 0, k-1))
  242. assert MatrixExpr.from_index_summation(expr, i1) == MatrixElement(A*B, i1, 0)