v923z / micropython-ulab

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

shape order #673

Closed iabdalkader closed 3 months ago

iabdalkader commented 3 months ago

Hi!! Not really a bug but I was wondering if storing the array's shape in reverse is by design ?

For example, with 1x36 array, NDMIS=4 the shape looks like this:

$23 = {base = {type = 0x80195330 <ulab_ndarray_type>}, dtype = 98 'b', itemsize = 1 '\001', boolean = 0 '\000', 
  ndim = 2 '\002', len = 36, shape = {0, 0, 1, 36}, strides = {0, 0, 36, 1}, array = 0x80098a0, origin = 0x80098a0}

Naturally I expected the shape to be {1, 36, 0, 0}. Is there an option to reverse it back ?

import ulab
print(ulab.__version__)

6.5.2-(3)D

To Reproduce Build with NDIMS > 1 Create an array and print shape.

v923z commented 3 months ago

This is the convention adopted by numpy:

>>> import numpy as np
>>> np.ones((1, 36), dtype=np.uint8).strides
(36, 1)
>>> np.ones((1, 36), dtype=np.uint8).shape
(1, 36)

and ulab follows that:

>>> from ulab import numpy as np
>>> np.ones((1, 36), dtype=np.uint8).strides
(36, 1)
>>> np.ones((1, 36), dtype=np.uint8).shape
(1, 36)

The reason is that it's much easier to deal with the nested loops, if the last dimension of the array (the one that has the smallest stride) is in the innermost loop. This is especially true in the case of ulab, where, for the sake of performance, storage etc., the dimension is fixed at compile time.

iabdalkader commented 3 months ago

Ah I see. Thanks for the clarification! I've updated my code to something like this:

for (size_t i = 0; i < input_array->ndim; i++) {
    size_t ulab_offset = ULAB_MAX_DIMS - input_array->ndim;
    if (input_array->shape[ulab_offset + i] != mp_obj_get_int(input_shape->items[i])) {