v923z / micropython-ulab

a numpy-like fast vector module for micropython, circuitpython, and their derivatives
https://micropython-ulab.readthedocs.io/en/latest
MIT License
432 stars 117 forks source link

[FEATURE REQUEST] numpy.block #537

Closed thetazero closed 2 years ago

thetazero commented 2 years ago

Describe the solution you'd like Here is the function in numpy: https://numpy.org/doc/stable/reference/generated/numpy.block.html I think a function like this would be very helpful for generating matrices

Additional context Here's an example of what numpy.block does:

A = np.eye(2) * 2
B = np.eye(3) * 3
np.block([
    [A,               np.zeros((2, 3))],
    [np.ones((3, 2)), B               ]
])

returns

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

If there's a more ulab friendly way of doing this I'd really appreciate knowing how to do this. If not, please let me know if you think this has a place in this project as I'd like to make a contribution.

v923z commented 2 years ago

You can already do this:

m = zeros((5,5))
m[::2,::2] = A
m[3::2,::2] = np.ones((3,2))
m[3::,3::] = B

I believe, instead of implementing this at the C level, we should add a python function in https://github.com/v923z/micropython-ulab/tree/master/snippets

thetazero commented 2 years ago

Thanks, I'll look into creating a snippet.

I think there is a lot of value in this more readable syntax

v923z commented 2 years ago

I think there is a lot of value in this more readable syntax

I definitely agree with this. The only question is, whether there is enough flash to implement this in C.

thetazero commented 2 years ago

Here's a super simple snippet that achieves the intended functionality of just setting the matrix to the desired value

def block(S):
    w = 0
    h = 0
    for m in S[0]:
        w += len(m[0])  # Take width of each element in first row
    for row in S:
        h += len(row[0])  # Take heigh of element in first column
    M = zeros((h, w))
    i = 0
    j = 0
    for row in S:
        di = len(row[0])
        for matrix in row:
            dj = len(matrix[0])
            M[i:i + di, j:j + dj] = matrix
            j += dj
        i += di
        j = 0
    return M

Honestly the numpy implementation seems overengineered, something like this might just be suitable for ulab.

What do you think?

v923z commented 2 years ago

Here's a super simple snippet that achieves the intended functionality of just setting the matrix to the desired value

Many thanks for sharing! You can actually cut the clutter a bit, if you use the shape property of the matrix:

w = sum([m.shape[0] for m in S[0]])

and likewise for the other dimension.

You could also check, whether the matrices are compatible, otherwise, you might end up with undesired results.

Honestly the numpy implementation seems overengineered, something like this might just be suitable for ulab.

What do you think?

Despite my two comments above, I would be happy to add this as it is. I believe, this is a useful addition, and putting it into the snippets collection would increase its visibility.

Can you just create a pull request? By doing that, you get brownie points: https://github.com/v923z/micropython-ulab/graphs/contributors

thetazero commented 2 years ago

Really appreciate your feedback, I've cleaned up the clutter with your suggestion. Definitely would like to implement your suggestion of matrix compatibility checks. I've added some test cases too the pull as well. I just need to make sure they are running correctly and add the matrix compatibility checks. Here's the pull: #539