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 Y.I.blocks[0, 0] == A.I assert X.inverse(expand=True) == 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(expand=False), Inverse) assert isinstance(X.inverse(), Inverse) assert not X.is_Identity Z = BlockMatrix([[Identity(n), B], [C, D]]) assert not Z.is_Identity
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_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_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_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_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_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)
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 __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_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_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 Q = X + Identity(m + n) assert (block_collapse(Q) == BlockMatrix([[A + Identity(n), B], [C, D + Identity(m)]])) 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 isinstance(X.inverse(), Inverse) assert not X.is_Identity Z = BlockMatrix([[Identity(n), B], [C, D]]) assert not Z.is_Identity
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_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_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_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
def test_BlockMatrix_Trace(): A, B, C, D = map(lambda s: MatrixSymbol(s, 3, 3), 'ABCD') X = BlockMatrix([[A, B], [C, D]]) assert Trace(X) == Trace(A) + Trace(D)
BlockMatrix, bc_dist, bc_matadd, bc_transpose, blockcut, reblock_2x2, deblock) from sympy.matrices.expressions import (MatrixSymbol, Identity, MatMul, Inverse, Trace, Transpose, det) from sympy.matrices import Matrix, ImmutableMatrix from sympy.core import Tuple, symbols, Expr from sympy.functions import transpose i, j, k, l, m, n, p = symbols('i:n, p', integer=True) A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, n) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) G = MatrixSymbol('G', n, n) H = MatrixSymbol('H', n, n) b1 = BlockMatrix([[G, H]]) b2 = BlockMatrix([[G], [H]]) def test_bc_matmul(): assert bc_matmul(H * b1 * b2 * G) == BlockMatrix([[ (H * G * G + H * H * H) * G ]]) def test_bc_matadd(): assert bc_matadd(BlockMatrix([[G, H]]) + BlockMatrix([[H, H]])) == \ BlockMatrix([[G+H, H+H]]) def test_bc_transpose():
def test_bc_matmul(): assert bc_matmul(H * b1 * b2 * G) == H * BlockMatrix([[G * G + H * H]]) * G
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 ALGO11(As, Bs, Cs, Ds, do_test): # =======================STEP 1====================================== r = As.rows m = Bs.cols p = Cs.rows Ps = BlockMatrix([[As, Bs], [-Cs, Ds]]).as_mutable() List_P = mc.matrix_coeffs(Ps, s) List_P.reverse() q = len(List_P) k = List_P[0].rows l = List_P[0].cols Zero_Mat = zeros(List_P[0].rows, List_P[0].cols) # list of zeroes # ==================END==STEP 1====================================== # =======================STEP 2====================================== # Haskel matrices #wikipedia ??? # PiE A = [] for i in range(2, q): A.append([List_P[j] for j in range(i, q)]) for i in range(1, len(A)): A[i] = A[i] + [Zero_Mat] * i PiE = BlockMatrix(A).as_mutable() A = [] for i in range(3, q + 1): A.append([List_P[j] for j in range(i, q)]) A[i - 3] = A[i - 3] + [Zero_Mat] * (i - 2) PiA = BlockMatrix(A) PiB = BlockMatrix((q - 2), 1, List_P[2:]) PiC = BlockMatrix(1, q - 2, List_P[2:]) rE = PiE.rank() # ===================END=====STEP 2================================== # =======================STEP 3====================================== J = PiE.rref()[1] # pivot columns I = (PiE.transpose()).rref()[1] # pivot rows PE = Matrix(mat(PiE)[ix_(I, J)]) # mat and ix are Numpy functions PA = Matrix(mat(PiA)[ix_(I, J)]) PB = Matrix(mat(PiB)[ix_(I, range(l))]) PC = Matrix(mat(PiC)[ix_(range(k), J)]) # ===================END=STEP 3====================================== # =======================STEP 4====================================== Lambda = r + rE + p + m E = BlockMatrix( [[List_P[1], PC], [PB, PA], [zeros(Lambda - k - PB.rows, PB.cols), zeros(Lambda - k - PB.rows, PA.cols)]] ).as_mutable() E = E.row_join(zeros(Lambda, Lambda - l - PC.cols)) A = BlockMatrix( [ [-List_P[0], zeros(k, PE.cols), zeros(k - p, Lambda - l - PE.cols).col_join(-eye(p))], [zeros(PE.rows, l), PE, zeros(PE.rows, p)], [zeros(m, l - m).row_join(eye(m)), zeros(m, PE.cols), zeros(m, p)], ] ).as_mutable() B = BlockMatrix([[zeros(r + p + rE, m)], [eye(m)]]).as_mutable() C = BlockMatrix([[zeros(p, r + m + rE), eye(p)]]).as_mutable() D = zeros(p, m) # ===============END==STEP 4========================================= if do_test == True: test_result = test(rE, r, p, m, PE, PA, PC, As, Bs, Cs, Ds, E, A, B, C, D) else: test_result = "not done" return E, A, B, C, D, test_result
def test_BlockMatrix_trace(): A, B, C, D = [MatrixSymbol(s, 3, 3) for s in 'ABCD'] X = BlockMatrix([[A, B], [C, D]]) assert trace(X) == trace(A) + trace(D) assert trace(BlockMatrix([ZeroMatrix(n, n)])) == 0
def test_BlockMatrix_trace(): A, B, C, D = [MatrixSymbol(s, 3, 3) for s in "ABCD"] X = BlockMatrix([[A, B], [C, D]]) assert trace(X) == trace(A) + trace(D)
def test_issue_17624(): a = MatrixSymbol("a", 2, 2) z = ZeroMatrix(2, 2) b = BlockMatrix([[a, z], [z, z]]) assert block_collapse(b * b) == BlockMatrix([[a**2, z], [z, z]]) assert block_collapse(b * b * b) == BlockMatrix([[a**3, z], [z, z]])
def test_block_collapse_explicit_matrices(): A = Matrix([[1, 2], [3, 4]]) assert block_collapse(BlockMatrix([[A]])) == A A = ImmutableSparseMatrix([[1, 2], [3, 4]]) assert block_collapse(BlockMatrix([[A]])) == A
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 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_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_bc_matadd(): assert bc_matadd(BlockMatrix([[G, H]]) + BlockMatrix([[H, H]])) == \ BlockMatrix([[G+H, H+H]])
def test_bc_transpose(): assert bc_transpose(Transpose(BlockMatrix([[A, B], [C, D]]))) == \ BlockMatrix([[A.T, C.T], [B.T, D.T]])
def _strongly_connected_components_decomposition(M, lower=True): """Decomposes a square matrix into block triangular form only using the permutations. Explanation =========== The decomposition is in a form of $A = P^{-1} B P$ where $P$ is a permutation matrix and $B$ is a block diagonal matrix. Parameters ========== lower : bool Makes $B$ lower block triangular when ``True``. Otherwise, makes $B$ upper block triangular. Returns ======= P, B : PermutationMatrix, BlockMatrix *P* is a permutation matrix for the similarity transform as in the explanation. And *B* is the block triangular matrix of the result of the permutation. Examples ======== >>> from sympy import Matrix, pprint >>> A = Matrix([ ... [44, 0, 0, 0, 43, 0, 45, 0, 0], ... [0, 66, 62, 61, 0, 68, 0, 60, 67], ... [0, 0, 22, 21, 0, 0, 0, 20, 0], ... [0, 0, 12, 11, 0, 0, 0, 10, 0], ... [34, 0, 0, 0, 33, 0, 35, 0, 0], ... [0, 86, 82, 81, 0, 88, 0, 80, 87], ... [54, 0, 0, 0, 53, 0, 55, 0, 0], ... [0, 0, 2, 1, 0, 0, 0, 0, 0], ... [0, 76, 72, 71, 0, 78, 0, 70, 77]]) A lower block triangular decomposition: >>> P, B = A.strongly_connected_components_decomposition() >>> pprint(P) PermutationMatrix((8)(1 4 3 2 6)(5 7)) >>> pprint(B) [[44 43 45] [0 0 0] [0 0 0] ] [[ ] [ ] [ ] ] [[34 33 35] [0 0 0] [0 0 0] ] [[ ] [ ] [ ] ] [[54 53 55] [0 0 0] [0 0 0] ] [ ] [ [0 0 0] [22 21 20] [0 0 0] ] [ [ ] [ ] [ ] ] [ [0 0 0] [12 11 10] [0 0 0] ] [ [ ] [ ] [ ] ] [ [0 0 0] [2 1 0 ] [0 0 0] ] [ ] [ [0 0 0] [62 61 60] [66 68 67]] [ [ ] [ ] [ ]] [ [0 0 0] [82 81 80] [86 88 87]] [ [ ] [ ] [ ]] [ [0 0 0] [72 71 70] [76 78 77]] >>> P = P.as_explicit() >>> B = B.as_explicit() >>> P.T * B * P == A True An upper block triangular decomposition: >>> P, B = A.strongly_connected_components_decomposition(lower=False) >>> pprint(P) PermutationMatrix((0 1 5 7 4 3 2 8 6)) >>> pprint(B) [[66 68 67] [62 61 60] [0 0 0] ] [[ ] [ ] [ ] ] [[86 88 87] [82 81 80] [0 0 0] ] [[ ] [ ] [ ] ] [[76 78 77] [72 71 70] [0 0 0] ] [ ] [ [0 0 0] [22 21 20] [0 0 0] ] [ [ ] [ ] [ ] ] [ [0 0 0] [12 11 10] [0 0 0] ] [ [ ] [ ] [ ] ] [ [0 0 0] [2 1 0 ] [0 0 0] ] [ ] [ [0 0 0] [0 0 0] [44 43 45]] [ [ ] [ ] [ ]] [ [0 0 0] [0 0 0] [34 33 35]] [ [ ] [ ] [ ]] [ [0 0 0] [0 0 0] [54 53 55]] >>> P = P.as_explicit() >>> B = B.as_explicit() >>> P.T * B * P == A True """ from sympy.combinatorics.permutations import Permutation from sympy.matrices.expressions.blockmatrix import BlockMatrix from sympy.matrices.expressions.permutation import PermutationMatrix iblocks = M.strongly_connected_components() if not lower: iblocks = list(reversed(iblocks)) p = Permutation(flatten(iblocks)) P = PermutationMatrix(p) rows = [] for a in iblocks: cols = [] for b in iblocks: cols.append(M[a, b]) rows.append(cols) B = BlockMatrix(rows) return P, B