i like this post (click again to cancel)
1
i dont like this post (click again to cancel) remove favorite mark from this question (click again to restore mark)

Has anybody seen/implemented some python code that will identify whether or not a square matrix is block diagonal, and return the blocks if it is?

Thanks, ~Luke

flag offensive community wiki
add comment
4 Answers:
i like this answer (click again to cancel)
2
i dont like this answer (click again to cancel)

In some way, all block matrices are block diagonal (a block on 1), so you're probably trying to figure out what the smallest possible block size B is.

for that, I would do something like a binary search through all possible values of B

start with

B=1
Bmax = M.shape[0]/2
Bmin = 0
while True:
    if not blockDiagonal(M,B)):
        Bmin = B
        B += (Bmax - B)/2
    else:
        Bmax = B
        B -= (B - Bmin)/2

That way you keep halving your search space with each iteration. I haven't thought very hard about this, but maybe it's more efficient to always just start with B=Bmax/2

additionally, you might want to scan through the first row and get an initial value for Bmin.

permanent link | flag offensive
asked 2009-08-21 18:28:13
pi's gravatar image
71
add comment
i like this answer (click again to cancel)
1
i dont like this answer (click again to cancel)

Thanks for the response and for clarifying the problem statement.

I'm not just trying to find what the smallest possible block size is, rather, I am trying to extract each of the smallest blocks. For example:

>>> A = array([[1, 0, 0, 0, 0, 0], [0, 2, 0, 1, 0, 0], [0, 3, 5, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 4, 0], [0, 0, 0, 0, 0, 0])

The function I imagine would behave as:

>>> sub_blocks(A)

1,

array([[2, 0, 1], [3, 5, 0], [0, 0, 0]])

4,

0

So even though the 3x3 block has a row of zeros, it is fine. Additionally, the row and column of zeros doesn't bugger things up either.

I'm using this for some symbolic matrix inversions and they start to really choke when the matrices get too big so I would like to minimize the size of the matrices that I pass to the inversion routine.

~Luke

permanent link | flag offensive
asked 2009-08-21 19:28:42
Luke's gravatar image
21
comments (1)
i like this answer (click again to cancel)
0
i dont like this answer (click again to cancel)

Ondrej and I implemented the following method to the Sympy Matrix class last night:

def get_diag_blocks(self):
    sub_blocks = []
    def recurse_sub_blocks(M):
        i = 1
        while i <= M.shape[0]:
            if i == 1:
                to_the_right = M[0, i:]
                to_the_bottom = M[i:, 0]
            else:
                to_the_right = M[0:i, i:]
                to_the_bottom = M[i:, 0:i]
            if any(to_the_right) or any(to_the_bottom):
                i += 1
                continue
            else:
                sub_blocks.append(M[0:i, 0:i])
                if M.shape == M[0:i, 0:i].shape:
                    return
                else:
                    recurse_sub_blocks(M[i:, i:])
                    return
    recurse_sub_blocks(self)
    return sub_blocks

So it essentially finds the upper left sub-block, then recursively calls its self on the remainder of the lower left of the matrix. Our search for subblocks just increments by 1 at each iteration, and this should probably be improved to be a bisect method, but we didn't get that done yet.

This would also be nice to put into numpy, along with a standalone function which creates a block diagonal matrix given a list of square matrices, like this one we also implemented last night:

def block_diag(matrices):
    """
    Constructs a block diagonal matrix.

    Example:
    >>> from sympy import block_diag, symbols
    >>> x, y, z = symbols("x y z")
    >>> a = Matrix([[1, 2], [2, 3]])
    >>> b = Matrix([[3, x], [y, 3]])
    >>> block_diag([a, b, b])
    [1, 2, 0, 0, 0, 0]
    [2, 3, 0, 0, 0, 0]
    [0, 0, 3, x, 0, 0]
    [0, 0, y, 3, 0, 0]
    [0, 0, 0, 0, 3, x]
    [0, 0, 0, 0, y, 3]

    """
    rows = 0
    for m in matrices:
        rows += m.rows
    A = zeros((rows, rows))
    i = 0
    for m in matrices:
        A[i+0:i+m.rows, i+0:i+m.cols] = m
        i += m.rows
    return A

~Luke

permanent link | flag offensive
asked 2009-08-22 12:58:24
Luke's gravatar image
21
add comment
i like this answer (click again to cancel)
0
i dont like this answer (click again to cancel)

Luke, also have a look at scipy.linalg.block_diag.

permanent link | flag offensive
asked 2010-03-05 06:56:57
Stefan's gravatar image
31
add comment
Your answer:
You are now not logged in but you can answer first and then login
toggle preview

Made with Django.