How to pack matrices into cells and then form a diagonal matrix with python

176 Views Asked by At

I need to create a diagonal matrix in python from an X matrix which is repeated 3 times. In matlab I do it in the following way:

  X=[1 2 3;
     4 5 6;
     7 8 9]

for i=1:1:3
  Brep{i}=X;      
end    
Mdiag=blkdiag(Brep{:})

Mdiag =

 1     2     3     0     0     0     0     0     0
 4     5     6     0     0     0     0     0     0
 7     8     9     0     0     0     0     0     0
 0     0     0     1     2     3     0     0     0
 0     0     0     4     5     6     0     0     0
 0     0     0     7     8     9     0     0     0
 0     0     0     0     0     0     1     2     3
 0     0     0     0     0     0     4     5     6
 0     0     0     0     0     0     7     8     9

But I don't know how to do this in Pyhton.

I would appreciate any help.

--------------------------------- ANSWER:--------------------------------------

Taking into account the answer provided by Fabrizio Bernini, I modified his code in order to do it more general. The code can now repeat the matrix X (any dimension), n times on the diagonal. For example:

import numpy as np

n=5

x = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])

Mdiag = np.zeros((n*x.shape[0], n*x.shape[1]))

for i in range(n):

Mdiag[x.shape[0]*i:x.shape[0]*i+x.shape[0],x.shape[1]*i:x.shape[1]*i+x.shape[1]]= x

Mdiag =[1., 2., 3., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [4., 5., 6., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [7., 8., 9., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 2., 3., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 4., 5., 6., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 7., 8., 9., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 1., 2., 3., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 4., 5., 6., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 7., 8., 9., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 2., 3., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 4., 5., 6., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 7., 8., 9., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 2., 3.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 4., 5., 6.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 7., 8., 9.]

Thanks you all for the answers provided.

5

There are 5 best solutions below

0
Fab On BEST ANSWER

you can do something like this using numpy:

import numpy
x = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
Mdiag = np.zeros((9, 9))
for i in range(3):
    Mdiag[3*i:3*i+3, 3*i:3*i+3] = x

you can also generate your initial matrix using np.reshape:

x = np.arange(1, 10)
x = x.reshape((3, 3))

More in general, if you have a nxn submatrix and want to create a MxM diagonal block matrix, where M = c*n, you can do:

import numpy
x = ... define here your nxn matrix
Mdiag = np.zeros((M, M))
for i in range(n):
    Mdiag[n*i:n*i+n, n*i:n*i+n] = x

There are numpy in-built functions to copy block matrices and generate block matrices, but I am not aware of one generating a diagonal block matrix with just one command. See also this for reference:

[1]: https://numpy.org/doc/stable/reference/generated/numpy.matlib.repmat.html#numpy.matlib.repmat and [2]: https://numpy.org/doc/stable/reference/generated/numpy.block.html?highlight=block%20matrix

0
Gilseung Ahn On

Try this code.

import numpy as np
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
M = np.zeros(shape = (9, 9))
for i in range(3):
  M[3*i:3*(i+1), 3*i:3*(i+1)] = X
0
DYZ On

One way to do this is to create a big zero matrix and directly paste x into the diagonal locations:

N = 3
y = np.zeros((x.shape[0] * N, x.shape[1] * N))
for i in range(N):
    y[i * x.shape[0] : (i + 1) * x.shape[0], 
      i * x.shape[1] : (i + 1) * x.shape[1]] = x

Another solution: create a horizontal "belt" of x and as many 0's as needed. Then stack three copies of that belt, shifted:

belt = np.hstack([x, np.zeros((x.shape[0], (x.shape[1] - 1) * N))])
np.vstack([np.roll(belt, i) for i in range(0, x.shape[0] * N, x.shape[0])])
0
MrNobody33 On

You just can use scipy.linalg.block_diag:

from scipy.linalg import block_diag
X=[[1, 2, 3],
     [4,5,6],
     [7, 8, 9]]
block_diag(X, X, X)

Output:

array([[1, 2, 3, 0, 0, 0, 0, 0, 0],
       [4, 5, 6, 0, 0, 0, 0, 0, 0],
       [7, 8, 9, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 2, 3, 0, 0, 0],
       [0, 0, 0, 4, 5, 6, 0, 0, 0],
       [0, 0, 0, 7, 8, 9, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 2, 3],
       [0, 0, 0, 0, 0, 0, 4, 5, 6],
       [0, 0, 0, 0, 0, 0, 7, 8, 9]], dtype=int32)

Another option, using np.bmat, np.asmatrix and np.asarray, could be this:

import numpy as np

X=np.asmatrix([[1, 2, 3],
     [4,5,6],
     [7, 8, 9]])
y = np.asmatrix(np.zeros((3, 3)))

r=np.asarray(np.bmat('X, y, y; y, X, y; y, y, X'))
print(r)
1
Patrick Artner On

You did not tag any "advanced" module (scipy, numpy, ...) so here we go:

You can create a function that does that yourself. Matrices are presented as lists of lists in python (unless you go into numpy etc.):

def diag_size(what, size = 4):
    """Create a diagonal matrix of size len(what)*size where the list of list
    'what' is on the diagonal of the embiggended result"""
    rv = [[]]
    s = len(what) # quadratic => len(what) == len(what[0])
    
    for row in range(size*s):
        for col in range(size*s):
            if row//s == col//s:
                rv[-1].append(base[row%s][col%s])
            else:
                rv[-1].append(0)
        rv.append([])

    return rv[:-1]

n = 3 # size of the initial "matrix"
# create base case
base = [list(range(i+1,i+n+1)) for i in range(0,n*n,n)]

# create the diagonal one
print(*diag_size(base),sep="\n")

Output:

# with size=4 you get a 4 wide diagonal matrix
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 4, 5, 6, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 9]