17

I have three same-size square matrices in NumPy. I would like to combine these to a block-diagonal matrix.

Example:

a1 = np.array([[1,1,1],[1,1,1],[1,1,1]])
a2 = np.array([[2,2,2],[2,2,2],[2,2,2]])
a3 = np.array([[3,3,3],[3,3,3],[3,3,3]])

r = np.array([[1,1,1,0,0,0,0,0,0],[1,1,1,0,0,0,0,0,0],[1,1,1,0,0,0,0,0,0],[0,0,0,2,2,2,0,0,0],[0,0,0,2,2,2,0,0,0],[0,0,0,2,2,2,0,0,0],[0,0,0,0,0,0,3,3,3],[0,0,0,0,0,0,3,3,3],[0,0,0,0,0,0,3,3,3]])

What is the best way to do this?

Peter Smit
  • 27,696
  • 33
  • 111
  • 170

4 Answers4

21

scipy.linalg has a block_diag function to do this automatically

>>> a1 = np.array([[1,1,1],[1,1,1],[1,1,1]])
>>> a2 = np.array([[2,2,2],[2,2,2],[2,2,2]])
>>> a3 = np.array([[3,3,3],[3,3,3],[3,3,3]])
>>> import scipy.linalg
>>> scipy.linalg.block_diag(a1, a2, a3)
array([[1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 2, 2, 2, 0, 0, 0],
       [0, 0, 0, 2, 2, 2, 0, 0, 0],
       [0, 0, 0, 2, 2, 2, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 3, 3, 3],
       [0, 0, 0, 0, 0, 0, 3, 3, 3],
       [0, 0, 0, 0, 0, 0, 3, 3, 3]])
>>> r = np.array([[1,1,1,0,0,0,0,0,0],[1,1,1,0,0,0,0,0,0],[1,1,1,0,0,0,0,0,0], [0,0,0,2,2,2,0,0,0],[0,0,0,2,2,2,0,0,0],[0,0,0,2,2,2,0,0,0],[0,0,0,0,0,0,3,3,3],[0,0,0,0,0,0,3,3,3],[0,0,0,0,0,0,3,3,3]])
>>> (scipy.linalg.block_diag(a1, a2, a3)  == r).all()
True
Josef
  • 21,998
  • 3
  • 54
  • 67
  • It would be nice if this were available in numpy (without requiring another dependency). – amcnabb Feb 25 '13 at 22:43
  • scipy is built on top of numpy. I think `scipy.array` should work in substantially the same fashion as `numpy.array`. – hBy2Py Mar 04 '15 at 19:05
4

Since these answers, numpy has added a block function

In [695]: A=np.arange(1,10).reshape(3,3)
In [696]: B=np.arange(10,14).reshape(2,2)
In [698]: C = np.zeros((3,2),int)

In [699]: np.block([[A,C],[C.T,B]])
Out[699]: 
array([[ 1,  2,  3,  0,  0],
       [ 4,  5,  6,  0,  0],
       [ 7,  8,  9,  0,  0],
       [ 0,  0,  0, 10, 11],
       [ 0,  0,  0, 12, 13]])
hpaulj
  • 221,503
  • 14
  • 230
  • 353
2

If you want this particular array r, perhaps the easiest way would be:

r=np.kron(np.diag([1,2,3]),np.ones((3,3),dtype='int'))

If a1, a2, a3 can be arbitrary 2-dimensional arrays, then perhaps the easiest way is:

a1=np.asmatrix(a1)
a2=np.asmatrix(a2)
a3=np.asmatrix(a3)
z=np.asmatrix(np.zeros((3,3)))
r=np.asarray(np.bmat('a1, z, z; z, a2, z; z, z, a3'))

An alternative slower method is:

r=(np.kron([[1,0,0],[0,0,0],[0,0,0]],a1)   
   +np.kron([[0,0,0],[0,1,0],[0,0,0]],a2)
   +np.kron([[0,0,0],[0,0,0],[0,0,1]],a3))
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
1

For those that want to make diagonal matrix containing M for N times:

from scipy.linalg import block_diag

M = np.array([[1,1,1],[1,1,1],[1,1,1]])
N = 3

block_diag(*(M for _ in range(N)))
Muhammad Yasirroni
  • 1,512
  • 12
  • 22