The code proposed by the OP can indeed made be more efficient, mainly by noting the fact that to form the sequence , with
you do not have to compute
at each step, but you can exploit the fact that
, reusing the result of the previous step.
My proposed implementation is
import numpy as np
N = 3
M = 1
A = np.random.random((N, N))
B = np.random.random((N, M))
X = np.zeros((N ** 2, N * M))
rsl = slice(0, N)
X[rsl, :M] = B
for i in range(1, N):
rsl_p, rsl = rsl, slice(i * N, (i + 1) * N)
X[rsl, :M] = A @ X[rsl_p, :M]
X[rsl, M : (i + 1) * M] = X[rsl_p, : i * M]
This question is more related to programming that computational science, so I add some general remarks, useful for a novice programmer.
never blindly translate mathematical expressions into code: think algorithmically.
X = np.linalg.matrix_power(A,i)@Bis much slower than
X=B; for j in range(i): X = A*Xif
(exercise: compute operation count for both code fragments).
whenever possible reuse previous calculations.
pre-allocate output matrices, and do not store intermediate results in temp. matrices
Is there an efficient way to form this block matrix with numpy or scipy? - Computational Science Stack Exchange
Forming a particular (averaged) block matrix with numpy - Computational Science Stack Exchange
python - Better way to create block matrices out of individual blocks in numpy? - Stack Overflow
python - How do numpy block matrices work? - Stack Overflow
The code proposed by the OP can indeed made be more efficient, mainly by noting the fact that to form the sequence , with
you do not have to compute
at each step, but you can exploit the fact that
, reusing the result of the previous step.
My proposed implementation is
import numpy as np
N = 3
M = 1
A = np.random.random((N, N))
B = np.random.random((N, M))
X = np.zeros((N ** 2, N * M))
rsl = slice(0, N)
X[rsl, :M] = B
for i in range(1, N):
rsl_p, rsl = rsl, slice(i * N, (i + 1) * N)
X[rsl, :M] = A @ X[rsl_p, :M]
X[rsl, M : (i + 1) * M] = X[rsl_p, : i * M]
This question is more related to programming that computational science, so I add some general remarks, useful for a novice programmer.
never blindly translate mathematical expressions into code: think algorithmically.
X = np.linalg.matrix_power(A,i)@Bis much slower than
X=B; for j in range(i): X = A*Xif
(exercise: compute operation count for both code fragments).
whenever possible reuse previous calculations.
pre-allocate output matrices, and do not store intermediate results in temp. matrices
If you need to explicitly construct the entire matrix, then Stefano M's answer is your best bet. If, however, you don't really need the whole matrix, but just need to be able to perform a matrix-vector-product (MVP), then you might want to consider the following approach. Based on your comments it's unclear to me if this helps you, and I realize this doesn't technically answer your question, but I thought I'd post it just in case.
Suppose you want to calculate the MVP:
where 's are size
and known and we want to calculate
's which
are size
and unknown. This
can be expressed block-by-block as
for . (The last step works if we assume
.)
This should be much faster than using the full dense matrix.
Example Python code:
import numpy as np
def mvp(A,B,x):
n=A.shape[0]
m=B.shape[1]
# assume that x is 1D array of size [n*m]
y=np.zeros( (n*n) )
y[:n] = B @ x[:m]
for i in range(1,n):
y[i*n:(i+1)*n] = B @ x[i*m:(i+1)*m] + A @ y[(i-1)*n:i*n]
return y
N = 3
M = 1
A = np.random.random((N, N))
B = np.random.random((N, M))
x=np.random.random( (N*M) )
y=mvp(A,B,x)
bmat returns a numpy.matrix instance, as in those things you should never use because they cause all kinds of weird incompatibilities. numpy.matrix always tries to preserve at least two dimensions, so b.dot(np.zeros(4)) is 2D instead of 1D.
Make a numpy.array:
b = np.bmat([[a, a], [a, a]]).A
# ^
Or as of NumPy 1.13,
b = np.block([[a, a], [a, a]])
bmat doesn't do anything exotic or fancy; basically it's just a couple of levels on concatenation:
In [308]: np.bmat([[a,a],[a,a]]).A
Out[308]:
array([[0, 1, 0, 1],
[2, 3, 2, 3],
[0, 1, 0, 1],
[2, 3, 2, 3]])
In [309]: alist = [[a,a],[a,a]]
In [310]: np.concatenate([np.concatenate(sublist, axis=1) for sublist in alist], axis=0)
Out[310]:
array([[0, 1, 0, 1],
[2, 3, 2, 3],
[0, 1, 0, 1],
[2, 3, 2, 3]])
Hey,
I have the following problem: I have a 2D numpy array (256 x 256 shape) and I want to divide it into equally sized blocks (for example 8 x 8 elements blocks) and apply a custom function to each block (which should reflect changes made in this function onto the original array).
What I first tried was simply (I know basically one should never do this) looping with a double for loop over the array (with step size being the blocks shape) and applying the function on each block. This worked, but it was really, really slow and barely worked for smaller block sizes like 2 x 2.
Next, I used the following (which is basically the view_as_blocks function from skimage.util.shape.
from numpy.lib.stride_tricks import as_strided
def chunk(arr_in: np.ndarray, block_shape:tuple) -> np.ndarray:
block_shape = np.array(block_shape)
arr_shape = np.array(arr_in.shape)
new_shape = tuple(arr_shape // block_shape) + tuple(block_shape)
new_strides = tuple(arr_in.strides * block_shape) + arr_in.strides
arr_out = as_strided(arr_in, shape=new_shape, strides=new_strides)
return arr_outAnd then looped through the resulting blocks one by one and assembling it into a new array like this:
def my_main_fun() -> None:
# the blocks
s = chunk(img, (chunk_size, chunk_size))
# the new array
t = np.zeros_like(img)
for i in range(s.shape[0]):
for j in range(s.shape[1]):
o_i = i * chunk_size
o_j = j * chunk_size
_, vals = myCustomFunction(s[i][j])
t[o_i:o_i + chunk_size, o_j:o_j + chunk_size] = valsThis was faster, but it also takes quite a lot time (I assume due to the double for loop in the reassembling (second) function).
Now my question: How can I do this much more efficiently?
Note that its guaranteed that the block shape (which is always a square) is evenly divisible by the arrays shape and I only care about 2D arrays for now.
Many thanks in advance ;)
IMO, np.eye already has everything you need, as you can define number of rows and columns separately.
So your function should simply look like
def fct(k):
return np.eye(k**2-k, k**2)
If I understand you correctly, this should work:
a = np.concatenate((np.eye((k-1)*k),np.zeros([(k-1)*k,k])), axis=1)
(at least, when I set k=3 and compare with the np.block(...) expression you gave, both results are identical)