예제 #1
0
def block_collapse(expr):
    """Evaluates a block matrix expression

    >>> from sympy import MatrixSymbol, BlockMatrix, symbols, Identity, Matrix, ZeroMatrix, block_collapse
    >>> n,m,l = symbols('n m l')
    >>> X = MatrixSymbol('X', n, n)
    >>> Y = MatrixSymbol('Y', m ,m)
    >>> Z = MatrixSymbol('Z', n, m)
    >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m, n), Y]])
    >>> print B
    [X, Z]
    [0, Y]

    >>> C = BlockMatrix([[Identity(n), Z]])
    >>> print C
    [I, Z]

    >>> print block_collapse(C*B)
    [X, Z + Z*Y]
    """
    if expr.__class__ in [tuple, list, set, frozenset]:
        return expr.__class__([block_collapse(arg) for arg in expr])
    if expr.__class__ in [Tuple, FiniteSet]:
        return expr.__class__(*[block_collapse(arg) for arg in expr])

    if not expr.is_Matrix or (not expr.is_Add and not expr.is_Mul
            and not expr.is_Transpose and not expr.is_Pow
            and not expr.is_Inverse):
        return expr

    if expr.is_Transpose:
        expr = Transpose(block_collapse(expr.arg))
        if expr.is_Transpose and expr.arg.is_BlockMatrix:
            expr = expr.arg.eval_transpose()
        return expr

    if expr.is_Inverse:
        return Inverse(block_collapse(expr.arg))

    # Recurse on the subargs
    args = list(expr.args)
    for i in range(len(args)):
        arg = args[i]
        newarg = block_collapse(arg)
        while(newarg != arg): # Repeat until no new changes
            arg = newarg
            newarg = block_collapse(arg)
        args[i] = newarg

    if tuple(args) != expr.args:
        expr = expr.__class__(*args)

    # Turn  -[X, Y] into [-X, -Y]
    if (expr.is_Mul and len(expr.args)==2 and not expr.args[0].is_Matrix
            and expr.args[1].is_BlockMatrix):
        if expr.args[1].is_BlockDiagMatrix:
            return BlockDiagMatrix(
                    *[expr.args[0]*arg for arg in expr.args[1].diag])
        else:
            return BlockMatrix(expr.args[0]*expr.args[1].mat)

    if expr.is_Add:
        nonblocks = [arg for arg in expr.args if not arg.is_BlockMatrix]
        blocks = [arg for arg in expr.args if arg.is_BlockMatrix]
        if not blocks:
            return MatAdd(*nonblocks)
        block = blocks[0]
        for b in blocks[1:]:
            block = block._blockadd(b)
        if block.blockshape == (1,1):
            # Bring all the non-blocks into the block_matrix
            mat = Matrix(1, 1, (block.blocks[0,0] + MatAdd(*nonblocks), ))
            return BlockMatrix(mat)
        # Add identities to the blocks as block identities
        for i, mat in enumerate(nonblocks):
            c, M = mat.as_coeff_Mul()
            if M.is_Identity and block.is_structurally_symmetric:
                block_id = BlockDiagMatrix(
                        *[c*Identity(k) for k in block.rowblocksizes])
                nonblocks.pop(i)
                block = block._blockadd(block_id)


        return MatAdd(*(nonblocks+[block]))

    if expr.is_Mul:
        nonmatrices = [arg for arg in expr.args if not arg.is_Matrix]
        matrices = [arg for arg in expr.args if arg.is_Matrix]
        i = 0
        while (i+1 < len(matrices)):
            A, B = matrices[i:i+2]
            if A.is_BlockMatrix and B.is_BlockMatrix:
                matrices[i] = A._blockmul(B)
                matrices.pop(i+1)
            else:
                i+=1
        return MatMul(*(nonmatrices + matrices))

    if expr.is_Pow:
        rv = expr.base
        for i in range(1, expr.exp):
            rv = rv._blockmul(expr.base)
        return rv