Reputation: 689
A is NxN, B is NxM. They are the matrices for a dynamical system x=Ax+Bu. I could propagate the matrix using np.block(), but I hope there's a way of forming this matrix can scale based on the size of N. I was thinking maybe kronecker product np.kron() can help, but I can't think of a way. Any advice would help, and if anyone knows what this matrix is called please let me know, thanks.
EDIT: I can do the following to generate a hardcoded solution.
N = 3
A = np.random.random((N, N))
B = np.random.random((N, 1))
print("A:", A)
print("B:", B)
np.block([ [B, np.zeros((B.shape[0], B.shape[1]*(N-1)))],
[A@B, B, np.zeros((B.shape[0], B.shape[1]*(N-1-1)))],
[np.linalg.matrix_power(A,N-1)@B, np.linalg.matrix_power(A,N-1-1)@B, B]
] )
input:
A: [[0.35227098 0.98853832 0.42468451]
[0.49288462 0.34457356 0.79954007]
[0.52171255 0.63167711 0.27997043]]
B: [[0.53736681]
[0.05086935]
[0.42558792]]
output:
array([[0.53736681, 0. , 0. ],
[0.05086935, 0. , 0. ],
[0.42558792, 0. , 0. ],
[0.42032564, 0.53736681, 0. ],
[0.62266266, 0.05086935, 0. ],
[0.43163605, 0.42558792, 0. ],
[0.94690357, 0.42032564, 0.53736681],
[0.76683544, 0.62266266, 0.05086935],
[0.73345624, 0.43163605, 0.42558792]])
What I am hoping to achieve is with an efficient way to generate such a matrix that scales with N, and I am able to do it with np.block() and list but it just seems to be not efficient for me.
mat_list = []
for i in range(N): # generate row
tmp = []
for j in range(N): # loop through A^j*B
if j <= i:
tmp.append(np.linalg.matrix_power(A,i-j)@B)
if i < N-1:
tmp.append(np.zeros((B.shape[0], B.shape[1]*(N-1-i))))
mat_list.append(tmp)
np.block(mat_list)
output:
array([[0.53736681, 0. , 0. ],
[0.05086935, 0. , 0. ],
[0.42558792, 0. , 0. ],
[0.42032564, 0.53736681, 0. ],
[0.62266266, 0.05086935, 0. ],
[0.43163605, 0.42558792, 0. ],
[0.94690357, 0.42032564, 0.53736681],
[0.76683544, 0.62266266, 0.05086935],
[0.73345624, 0.43163605, 0.42558792]])
Upvotes: 0
Views: 320
Reputation: 36289
You can use itertools.accumulate
to compute the block matrices which reoccur throughout the array:
import itertools as it
AB = list(it.accumulate(it.chain([B], it.repeat(A, N-1)), lambda x, y: y @ x))
zero = np.zeros_like(B)
result = np.block([AB[i::-1] + [zero]*(N-1-i) for i in range(N)])
Upvotes: 3