def test_BlockMatrix_inverse(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', n, n) C = MatrixSymbol('C', m, m) D = MatrixSymbol('D', m, n) X = BlockMatrix([[A, B], [C, D]]) assert X.is_square assert isinstance(block_collapse(X.inverse()), Inverse) # Can't inverse when A, D aren't square # test code path for non-invertible D matrix A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = OneMatrix(m, m) X = BlockMatrix([[A, B], [C, D]]) assert block_collapse(X.inverse()) == BlockMatrix([ [ A.I + A.I * B * (D - C * A.I * B).I * C * A.I, -A.I * B * (D - C * A.I * B).I ], [-(D - C * A.I * B).I * C * A.I, (D - C * A.I * B).I], ]) # test code path for non-invertible A matrix A = OneMatrix(n, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) assert block_collapse(X.inverse()) == BlockMatrix([ [(A - B * D.I * C).I, -(A - B * D.I * C).I * B * D.I], [ -D.I * C * (A - B * D.I * C).I, D.I + D.I * C * (A - B * D.I * C).I * B * D.I ], ])
def test_squareBlockMatrix(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) Y = BlockMatrix([[A]]) assert X.is_square assert (block_collapse(X + Identity(m + n)) == BlockMatrix( [[A + Identity(n), B], [C, D + Identity(m)]])) Q = X + Identity(m + n) assert (X + MatrixSymbol('Q', n + m, n + m)).is_MatAdd assert (X * MatrixSymbol('Q', n + m, n + m)).is_MatMul assert block_collapse(Y.I) == A.I assert block_collapse(X.inverse()) == BlockMatrix([[ (-B * D.I * C + A).I, -A.I * B * (D + -C * A.I * B).I ], [-(D - C * A.I * B).I * C * A.I, (D - C * A.I * B).I]]) assert isinstance(X.inverse(), Inverse) assert not X.is_Identity Z = BlockMatrix([[Identity(n), B], [C, D]]) assert not Z.is_Identity
def test_BlockMatrix_Determinant(): A, B, C, D = [MatrixSymbol(s, 3, 3) for s in 'ABCD'] X = BlockMatrix([[A, B], [C, D]]) from sympy import assuming, Q with assuming(Q.invertible(A)): assert det(X) == det(A) * det(D - C * A.I * B) assert isinstance(det(X), Expr) assert det(BlockMatrix([A])) == det(A) assert det(BlockMatrix([ZeroMatrix(n, n)])) == 0
def test_BlockMatrix_Determinant(): A, B, C, D = [MatrixSymbol(s, 3, 3) for s in 'ABCD'] X = BlockMatrix([[A, B], [C, D]]) from sympy.assumptions.ask import Q from sympy.assumptions.assume import assuming with assuming(Q.invertible(A)): assert det(X) == det(A) * det(X.schur('A')) assert isinstance(det(X), Expr) assert det(BlockMatrix([A])) == det(A) assert det(BlockMatrix([ZeroMatrix(n, n)])) == 0
def test_BlockMatrix(): A = MatrixSymbol("A", n, m) B = MatrixSymbol("B", n, k) C = MatrixSymbol("C", l, m) D = MatrixSymbol("D", l, k) M = MatrixSymbol("M", m + k, p) N = MatrixSymbol("N", l + n, k + m) X = BlockMatrix(Matrix([[A, B], [C, D]])) assert X.__class__(*X.args) == X # block_collapse does nothing on normal inputs E = MatrixSymbol("E", n, m) assert block_collapse(A + 2 * E) == A + 2 * E F = MatrixSymbol("F", m, m) assert block_collapse(E.T * A * F) == E.T * A * F assert X.shape == (l + n, k + m) assert X.blockshape == (2, 2) assert transpose(X) == BlockMatrix(Matrix([[A.T, C.T], [B.T, D.T]])) assert transpose(X).shape == X.shape[::-1] # Test that BlockMatrices and MatrixSymbols can still mix assert (X * M).is_MatMul assert X._blockmul(M).is_MatMul assert (X * M).shape == (n + l, p) assert (X + N).is_MatAdd assert X._blockadd(N).is_MatAdd assert (X + N).shape == X.shape E = MatrixSymbol("E", m, 1) F = MatrixSymbol("F", k, 1) Y = BlockMatrix(Matrix([[E], [F]])) assert (X * Y).shape == (l + n, 1) assert block_collapse(X * Y).blocks[0, 0] == A * E + B * F assert block_collapse(X * Y).blocks[1, 0] == C * E + D * F # block_collapse passes down into container objects, transposes, and inverse assert block_collapse(transpose(X * Y)) == transpose(block_collapse(X * Y)) assert block_collapse(Tuple(X * Y, 2 * X)) == ( block_collapse(X * Y), block_collapse(2 * X), ) # Make sure that MatrixSymbols will enter 1x1 BlockMatrix if it simplifies Ab = BlockMatrix([[A]]) Z = MatrixSymbol("Z", *A.shape) assert block_collapse(Ab + Z) == A + Z
def test_issue_21866(): n = 10 I = Identity(n) O = ZeroMatrix(n, n) A = BlockMatrix([[ I, O, O, O ], [ O, I, O, O ], [ O, O, I, O ], [ I, O, O, I ]]) Ainv = block_collapse(A.inv()) AinvT = BlockMatrix([[ I, O, O, O ], [ O, I, O, O ], [ O, O, I, O ], [ -I, O, O, I ]]) assert Ainv == AinvT
def test_invalid_block_matrix(): raises(ValueError, lambda: BlockMatrix([ [Identity(2), Identity(5)], ])) raises(ValueError, lambda: BlockMatrix([ [Identity(n), Identity(m)], ])) raises(ValueError, lambda: BlockMatrix([ [ZeroMatrix(n, n), ZeroMatrix(n, n)], [ZeroMatrix(n, n - 1), ZeroMatrix(n, n + 1)], ])) raises(ValueError, lambda: BlockMatrix([ [ZeroMatrix(n - 1, n), ZeroMatrix(n, n)], [ZeroMatrix(n + 1, n), ZeroMatrix(n, n)], ]))
def test_block_index(): I = Identity(3) Z = ZeroMatrix(3, 3) B = BlockMatrix([[I, I], [I, I]]) e3 = ImmutableMatrix(eye(3)) BB = BlockMatrix([[e3, e3], [e3, e3]]) assert B[0, 0] == B[3, 0] == B[0, 3] == B[3, 3] == 1 assert B[4, 3] == B[5, 1] == 0 BB = BlockMatrix([[e3, e3], [e3, e3]]) assert B.as_explicit() == BB.as_explicit() BI = BlockMatrix([[I, Z], [Z, I]]) assert BI.as_explicit().equals(eye(6))
def test_BlockMatrix_2x2_inverse_symbolic(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', n, k - m) C = MatrixSymbol('C', k - n, m) D = MatrixSymbol('D', k - n, k - m) X = BlockMatrix([[A, B], [C, D]]) assert X.is_square and X.shape == (k, k) assert isinstance(block_collapse( X.I), Inverse) # Can't invert when none of the blocks is square # test code path where only A is invertible A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = ZeroMatrix(m, m) X = BlockMatrix([[A, B], [C, D]]) assert block_collapse(X.inverse()) == BlockMatrix([ [A.I + A.I * B * X.schur('A').I * C * A.I, -A.I * B * X.schur('A').I], [-X.schur('A').I * C * A.I, X.schur('A').I], ]) # test code path where only B is invertible A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', n, n) C = ZeroMatrix(m, m) D = MatrixSymbol('D', m, n) X = BlockMatrix([[A, B], [C, D]]) assert block_collapse(X.inverse()) == BlockMatrix([ [-X.schur('B').I * D * B.I, X.schur('B').I], [B.I + B.I * A * X.schur('B').I * D * B.I, -B.I * A * X.schur('B').I], ]) # test code path where only C is invertible A = MatrixSymbol('A', n, m) B = ZeroMatrix(n, n) C = MatrixSymbol('C', m, m) D = MatrixSymbol('D', m, n) X = BlockMatrix([[A, B], [C, D]]) assert block_collapse(X.inverse()) == BlockMatrix([ [-C.I * D * X.schur('C').I, C.I + C.I * D * X.schur('C').I * A * C.I], [X.schur('C').I, -X.schur('C').I * A * C.I], ]) # test code path where only D is invertible A = ZeroMatrix(n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) assert block_collapse(X.inverse()) == BlockMatrix([ [X.schur('D').I, -X.schur('D').I * B * D.I], [-D.I * C * X.schur('D').I, D.I + D.I * C * X.schur('D').I * B * D.I], ])
def lstm_recursive(x, *limits): (W, ), (Wh, ), (b, ), (t, ) = limits hc = lstm[W, Wh, b, t - 1](x) xt = x[t] h = Indexed(hc, 0) c = Indexed(hc, 1) d = h.shape[-1] Wi = W[:, :d] Wf = W[:, d:2 * d] Wc = W[:, 2 * d:3 * d] Wo = W[:, -d:] Whi = Wh[:, :d] Whf = Wh[:, d:2 * d] Whc = Wh[:, 2 * d:3 * d] Who = Wh[:, -d:] bi = b[:d] bf = b[d:2 * d] bc = b[2 * d:3 * d] bo = b[-d:] i = sigmoid(xt @ Wi + h @ Whi + bi) f = sigmoid(xt @ Wf + h @ Whf + bf) c = f * c + i * tanh(xt @ Wc + h @ Whc + bc) o = sigmoid(xt @ Wo + h @ Who + bo) return Piecewise((BlockMatrix(o * tanh(c), c), t > 0), (ZeroMatrix(*hc.shape), True))
def test_sympy__matrices__expressions__blockmatrix__BlockMatrix(): from sympy.matrices.expressions.blockmatrix import BlockMatrix from sympy.matrices.expressions import MatrixSymbol, ZeroMatrix X = MatrixSymbol('X', x, x) Y = MatrixSymbol('Y', y, y) Z = MatrixSymbol('Z', x, y) O = ZeroMatrix(y, x) assert _test_args(BlockMatrix([[X, Z], [O, Y]]))
def test_block_plus_ident(): A = MatrixSymbol("A", n, n) B = MatrixSymbol("B", n, m) C = MatrixSymbol("C", m, n) D = MatrixSymbol("D", m, m) X = BlockMatrix([[A, B], [C, D]]) assert (bc_block_plus_ident(X + Identity(m + n)) == BlockDiagMatrix( Identity(n), Identity(m)) + X)
def test_block_index_symbolic_fail(): # To make this work, symbolic matrix dimensions would need to be somehow assumed nonnegative # even if the symbols aren't specified as such. Then 2 * n < n would correctly evaluate to # False in BlockMatrix._entry() A1 = MatrixSymbol('A1', n, 1) A2 = MatrixSymbol('A2', m, 1) A = BlockMatrix([[A1], [A2]]) assert A[2 * n, 0] == A2[n, 0]
def test_block_plus_ident(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) assert bc_block_plus_ident(X+Identity(m+n)) == \ BlockDiagMatrix(Identity(n), Identity(m)) + X
def test_BlockMatrix_Determinant(): A, B, C, D = map(lambda s: MatrixSymbol(s, 3, 3), 'ABCD') X = BlockMatrix([[A, B], [C, D]]) from sympy import assuming, Q with assuming(Q.invertible(A)): assert det(X) == det(A) * det(D - C * A.I * B) assert isinstance(det(X), Expr)
def __new__(cls, mat, evaluate=False): from sympy.matrices.expressions.blockmatrix import BlockMatrix if isinstance(mat, (list, tuple)): mat = BlockMatrix(*mat) mat = sympify(mat) assert mat.is_square, "Det of a non-square matrix" return Basic.__new__(cls, mat)
def test_BlockMatrix_3x3_symbolic(): # Only test one of these, instead of all permutations, because it's slow rowblocksizes = (n, m, k) colblocksizes = (m, k, n) K = BlockMatrix([ [MatrixSymbol('M%s%s' % (rows, cols), rows, cols) for cols in colblocksizes] for rows in rowblocksizes ]) collapse = block_collapse(K.I) assert isinstance(collapse, BlockMatrix)
def test_reblock_2x2(): B = BlockMatrix([[MatrixSymbol('A_%d%d' % (i, j), 2, 2) for j in range(3)] for i in range(3)]) assert B.blocks.shape == (3, 3) BB = reblock_2x2(B) assert BB.blocks.shape == (2, 2) assert B.shape == BB.shape assert B.as_explicit() == BB.as_explicit()
def test_blockcut(): A = MatrixSymbol('A', n, m) B = blockcut(A, (n/2, n/2), (m/2, m/2)) assert B == BlockMatrix([[A[:n/2, :m/2], A[:n/2, m/2:]], [A[n/2:, :m/2], A[n/2:, m/2:]]]) M = ImmutableMatrix(4, 4, range(16)) B = blockcut(M, (2, 2), (2, 2)) assert M == ImmutableMatrix(B) B = blockcut(M, (1, 3), (2, 2)) assert ImmutableMatrix(B.blocks[0, 1]) == ImmutableMatrix([[2, 3]])
def test_BlockMatrix_2x2_inverse_numeric(): """Test 2x2 block matrix inversion numerically for all 4 formulas""" M = Matrix([[1, 2], [3, 4]]) # rank deficient matrices that have full rank when two of them combined D1 = Matrix([[1, 2], [2, 4]]) D2 = Matrix([[1, 3], [3, 9]]) D3 = Matrix([[1, 4], [4, 16]]) assert D1.rank() == D2.rank() == D3.rank() == 1 assert (D1 + D2).rank() == (D2 + D3).rank() == (D3 + D1).rank() == 2 # Only A is invertible K = BlockMatrix([[M, D1], [D2, D3]]) assert block_collapse(K.inv()).as_explicit() == K.as_explicit().inv() # Only B is invertible K = BlockMatrix([[D1, M], [D2, D3]]) assert block_collapse(K.inv()).as_explicit() == K.as_explicit().inv() # Only C is invertible K = BlockMatrix([[D1, D2], [M, D3]]) assert block_collapse(K.inv()).as_explicit() == K.as_explicit().inv() # Only D is invertible K = BlockMatrix([[D1, D2], [D3, M]]) assert block_collapse(K.inv()).as_explicit() == K.as_explicit().inv()
def test_16857(): if not np: skip("NumPy not installed") a_1 = MatrixSymbol('a_1', 10, 3) a_2 = MatrixSymbol('a_2', 10, 3) a_3 = MatrixSymbol('a_3', 10, 3) a_4 = MatrixSymbol('a_4', 10, 3) A = BlockMatrix([[a_1, a_2], [a_3, a_4]]) assert A.shape == (20, 6) printer = NumPyPrinter() assert printer.doprint(A) == 'numpy.block([[a_1, a_2], [a_3, a_4]])'
def test_block_index_large(): n, m, k = symbols('n m k', integer=True, positive=True) i = symbols('i', integer=True, nonnegative=True) A1 = MatrixSymbol('A1', n, n) A2 = MatrixSymbol('A2', n, m) A3 = MatrixSymbol('A3', n, k) A4 = MatrixSymbol('A4', m, n) A5 = MatrixSymbol('A5', m, m) A6 = MatrixSymbol('A6', m, k) A7 = MatrixSymbol('A7', k, n) A8 = MatrixSymbol('A8', k, m) A9 = MatrixSymbol('A9', k, k) A = BlockMatrix([[A1, A2, A3], [A4, A5, A6], [A7, A8, A9]]) assert A[n + i, n + i] == MatrixElement(A, n + i, n + i)
def test_block_index_symbolic(): # Note that these matrices may be zero-sized and indices may be negative, which causes # all naive simplifications given in the comments to be invalid A1 = MatrixSymbol('A1', n, k) A2 = MatrixSymbol('A2', n, l) A3 = MatrixSymbol('A3', m, k) A4 = MatrixSymbol('A4', m, l) A = BlockMatrix([[A1, A2], [A3, A4]]) assert A[0, 0] == MatrixElement(A, 0, 0) # Cannot be A1[0, 0] assert A[n - 1, k - 1] == A1[n - 1, k - 1] assert A[n, k] == A4[0, 0] assert A[n + m - 1, 0] == MatrixElement(A, n + m - 1, 0) # Cannot be A3[m - 1, 0] assert A[0, k + l - 1] == MatrixElement(A, 0, k + l - 1) # Cannot be A2[0, l - 1] assert A[n + m - 1, k + l - 1] == MatrixElement(A, n + m - 1, k + l - 1) # Cannot be A4[m - 1, l - 1] assert A[i, j] == MatrixElement(A, i, j) assert A[n + i, k + j] == MatrixElement(A, n + i, k + j) # Cannot be A4[i, j] 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]
def test_block_lu_decomposition(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) #LDU decomposition L, D, U = X.LDUdecomposition() assert block_collapse(L*D*U) == X #UDL decomposition U, D, L = X.UDLdecomposition() assert block_collapse(U*D*L) == X #LU decomposition L, U = X.LUdecomposition() assert block_collapse(L*U) == X
def test_block_index_symbolic_nonzero(): # All invalid simplifications from test_block_index_symbolic() that become valid if all # matrices have nonzero size and all indices are nonnegative k, l, m, n = symbols('k l m n', integer=True, positive=True) i, j = symbols('i j', integer=True, nonnegative=True) A1 = MatrixSymbol('A1', n, k) A2 = MatrixSymbol('A2', n, l) A3 = MatrixSymbol('A3', m, k) A4 = MatrixSymbol('A4', m, l) A = BlockMatrix([[A1, A2], [A3, A4]]) assert A[0, 0] == A1[0, 0] assert A[n + m - 1, 0] == A3[m - 1, 0] assert A[0, k + l - 1] == A2[0, l - 1] assert A[n + m - 1, k + l - 1] == A4[m - 1, l - 1] assert A[i, j] == MatrixElement(A, i, j) assert A[n + i, k + j] == A4[i, j] assert A[n - i - 1, k - j - 1] == A1[n - i - 1, k - j - 1] assert A[2 * n, 2 * k] == A4[n, k]
def _inv_block(M, iszerofunc=_iszero): """Calculates the inverse using BLOCKWISE inversion. See Also ======== inv inverse_ADJ inverse_GE inverse_CH inverse_LDL """ from sympy.matrices.expressions.blockmatrix import BlockMatrix i = M.shape[0] if i <= 20 : return M.inv(method="LU", iszerofunc=_iszero) A = M[:i // 2, :i //2] B = M[:i // 2, i // 2:] C = M[i // 2:, :i // 2] D = M[i // 2:, i // 2:] try: D_inv = _inv_block(D) except NonInvertibleMatrixError: return M.inv(method="LU", iszerofunc=_iszero) B_D_i = B*D_inv BDC = B_D_i*C A_n = A - BDC try: A_n = _inv_block(A_n) except NonInvertibleMatrixError: return M.inv(method="LU", iszerofunc=_iszero) B_n = -A_n*B_D_i dc = D_inv*C C_n = -dc*A_n D_n = D_inv + dc*-B_n nn = BlockMatrix([[A_n, B_n], [C_n, D_n]]).as_explicit() return nn
def test_bc_transpose(): assert bc_transpose(Transpose(BlockMatrix([[A, B], [C, D]]))) == \ BlockMatrix([[A.T, C.T], [B.T, D.T]])
def test_bc_matadd(): assert bc_matadd(BlockMatrix([[G, H]]) + BlockMatrix([[H, H]])) == \ BlockMatrix([[G+H, H+H]])
def test_bc_matmul(): assert bc_matmul(H * b1 * b2 * G) == BlockMatrix([[ (H * G * G + H * H * H) * G ]])
def test_deblock(): B = BlockMatrix([[MatrixSymbol('A_%d%d' % (i, j), n, n) for j in range(4)] for i in range(4)]) assert deblock(reblock_2x2(B)) == B