From 4dc9afc1f8154de2c82c13ba6ff1370406e31052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 8 Feb 2022 17:37:00 +0100 Subject: [PATCH 001/113] fix np.diag --- code/numpy/create.c | 71 ++++++++++++++++++++--------------------- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++ 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index 7d5f7e90..70f6f21c 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -367,52 +367,49 @@ mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("input must be an ndarray")); - } - ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj); - if(source->ndim == 1) { // return a rank-2 tensor with the prescribed diagonal - ndarray_obj_t *target = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, source->len, source->len), source->dtype); + ndarray_obj_t *source = ndarray_from_iterable(args[0].u_obj, NDARRAY_FLOAT); + ndarray_obj_t *target = NULL; + + if(source->ndim == 2) { // return the diagonal + size_t len = MIN(source->shape[ULAB_MAX_DIMS - 1], source->shape[ULAB_MAX_DIMS - 2]); + target = ndarray_new_linear_array(len, source->dtype); uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; - for(size_t i=0; i < source->len; i++) { + for(size_t i=0; i < len; i++) { memcpy(tarray, sarray, source->itemsize); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - tarray += (source->len + 1) * target->itemsize; + sarray += (source->strides[ULAB_MAX_DIMS - 1] + source->strides[ULAB_MAX_DIMS - 2]); + tarray += target->itemsize; } - return MP_OBJ_FROM_PTR(target); - } - if(source->ndim > 2) { - mp_raise_TypeError(translate("input must be a tensor of rank 2")); - } - int32_t k = args[1].u_int; - size_t len = 0; - uint8_t *sarray = (uint8_t *)source->array; - if(k < 0) { // move the pointer "vertically" - if(-k < (int32_t)source->shape[ULAB_MAX_DIMS - 2]) { - sarray -= k * source->strides[ULAB_MAX_DIMS - 2]; - len = MIN(source->shape[ULAB_MAX_DIMS - 2] + k, source->shape[ULAB_MAX_DIMS - 1]); + } else if(source->ndim == 1) { // return a rank-2 tensor with the prescribed diagonal + int32_t k = args[1].u_int; + size_t len = source->len; + if(k < 0) { + len -= k; + } else { + len += k; } - } else { // move the pointer "horizontally" - if(k < (int32_t)source->shape[ULAB_MAX_DIMS - 1]) { - sarray += k * source->strides[ULAB_MAX_DIMS - 1]; - len = MIN(source->shape[ULAB_MAX_DIMS - 1] - k, source->shape[ULAB_MAX_DIMS - 2]); + target = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, len, len), source->dtype); + uint8_t *sarray = (uint8_t *)source->array; + uint8_t *tarray = (uint8_t *)target->array; + + if(k < 0) { + k = -k; + tarray += len * k * target->itemsize; + } else { + tarray += k * target->itemsize; + } + for(size_t i = 0; i < source->len; i++) { + memcpy(tarray, sarray, source->itemsize); + sarray += source->strides[ULAB_MAX_DIMS - 1]; + tarray += (len + 1) * target->itemsize; } } - - if(len == 0) { - mp_raise_ValueError(translate("offset is too large")); + #if ULAB_MAX_DIMS > 2 + else { + mp_raise_ValueError(translate("input must be 1- or 2-d")); } + #endif - ndarray_obj_t *target = ndarray_new_linear_array(len, source->dtype); - uint8_t *tarray = (uint8_t *)target->array; - - for(size_t i=0; i < len; i++) { - memcpy(tarray, sarray, source->itemsize); - sarray += source->strides[ULAB_MAX_DIMS - 2]; - sarray += source->strides[ULAB_MAX_DIMS - 1]; - tarray += source->itemsize; - } return MP_OBJ_FROM_PTR(target); } diff --git a/code/ulab.c b/code/ulab.c index 68985986..d88aac35 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.1 +#define ULAB_VERSION 5.0.2 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 1eea62fc..c744cb5a 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Tue, 8 Feb 2022 + +version 5.0.2 + + fix np.diag + Thu, 3 Feb 2022 version 5.0.1 From 72f7df520a01d58c29942e650a4c7067f8b41979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 9 Feb 2022 07:06:54 +0100 Subject: [PATCH 002/113] fix diag for the ndim = 2 case --- code/numpy/create.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index 70f6f21c..e0934284 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -370,33 +370,43 @@ mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) ndarray_obj_t *source = ndarray_from_iterable(args[0].u_obj, NDARRAY_FLOAT); ndarray_obj_t *target = NULL; + int32_t k = args[1].u_int; + size_t k_abs = k >= 0 ? (size_t)k : (size_t)(-k); if(source->ndim == 2) { // return the diagonal - size_t len = MIN(source->shape[ULAB_MAX_DIMS - 1], source->shape[ULAB_MAX_DIMS - 2]); + size_t len; + if(k >= 0) { + len = (k_abs <= source->shape[ULAB_MAX_DIMS - 1]) ? source->shape[ULAB_MAX_DIMS - 1] - k_abs : 0; + } else { + len = (k_abs <= source->shape[ULAB_MAX_DIMS - 2]) ? source->shape[ULAB_MAX_DIMS - 2] - k_abs : 0; + } target = ndarray_new_linear_array(len, source->dtype); + + if(len == 0) { + return MP_OBJ_FROM_PTR(target); + } + uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; + if(k >= 0) { + sarray += source->strides[ULAB_MAX_DIMS - 1] * k; + } else { + sarray += source->strides[ULAB_MAX_DIMS - 2] * k_abs; + } for(size_t i=0; i < len; i++) { memcpy(tarray, sarray, source->itemsize); sarray += (source->strides[ULAB_MAX_DIMS - 1] + source->strides[ULAB_MAX_DIMS - 2]); tarray += target->itemsize; } } else if(source->ndim == 1) { // return a rank-2 tensor with the prescribed diagonal - int32_t k = args[1].u_int; - size_t len = source->len; - if(k < 0) { - len -= k; - } else { - len += k; - } + size_t len = source->len + k_abs; target = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, len, len), source->dtype); uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; if(k < 0) { - k = -k; - tarray += len * k * target->itemsize; + tarray += len * k_abs * target->itemsize; } else { - tarray += k * target->itemsize; + tarray += k_abs * target->itemsize; } for(size_t i = 0; i < source->len; i++) { memcpy(tarray, sarray, source->itemsize); From f2502fa0377861043f1a2b84092d534eb3e7fc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 9 Feb 2022 07:29:26 +0100 Subject: [PATCH 003/113] update diag doc, re-organise ulab-ndarray docs --- docs/manual/source/conf.py | 3 +- docs/manual/source/ulab-ndarray.rst | 177 ++++++++++++++++---------- docs/ulab-convert.ipynb | 14 +- docs/ulab-ndarray.ipynb | 190 +++++++++++++++++----------- 4 files changed, 239 insertions(+), 145 deletions(-) diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index f74f8885..2c245b0f 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,8 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '5.0.0' +release = '5.0.2' + # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index a37cef7c..ff8e6eb7 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -250,7 +250,7 @@ and will, therefore, be faster. Keep this in mind, whenever the output type, or performance is important. Array initialisation functions ------------------------------- +============================== There are nine functions that can be used for initialising an array. Starred functions accept ``complex`` as the value of the ``dtype``, if @@ -269,7 +269,7 @@ the firmware was compiled with complex support. 11. `numpy.zeros\* <#zeros>`__ arange -~~~~~~ +------ ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.arange.html @@ -300,7 +300,7 @@ keyword argument. concatenate -~~~~~~~~~~~ +----------- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.concatenate.html @@ -387,14 +387,19 @@ https://numpy.org/doc/stable/reference/generated/numpy.diag.html Extract a diagonal, or construct a diagonal array. -The function takes two arguments, an ``ndarray``, and a shift. If the -first argument is a two-dimensional array, the function returns a -one-dimensional array containing the diagonal entries. The diagonal can -be shifted by an amount given in the second argument. +The function takes a positional argument, an ``ndarray``, or any +``micropython`` iterable, and an optional keyword argument, a shift, +with a default value of 0. If the first argument is a two-dimensional +array (or a two-dimensional iterable, e.g., a list of lists), the +function returns a one-dimensional array containing the diagonal +entries. The diagonal can be shifted by an amount given in the second +argument. If the shift is larger than the length of the corresponding +axis, an empty array is returned. If the first argument is a one-dimensional array, the function returns a -two-dimensional tensor with its diagonal elements given by the first -argument. +two-dimensional square tensor with its diagonal elements given by the +first argument. Again, the diagonal be shifted by an amount given by the +keyword argument. The ``diag`` function can accept a complex array, if the firmware was compiled with complex support. @@ -405,15 +410,34 @@ compiled with complex support. from ulab import numpy as np - a = np.array([1, 2, 3, 4]) + a = np.array([1, 2, 3], dtype=np.uint8) print(np.diag(a)) + + print('\ndiagonal shifted by 2') + print(np.diag(a, k=2)) + + print('\ndiagonal shifted by -2') + print(np.diag(a, k=-2)) .. parsed-literal:: - array([[1.0, 0.0, 0.0, 0.0], - [0.0, 2.0, 0.0, 0.0], - [0.0, 0.0, 3.0, 0.0], - [0.0, 0.0, 0.0, 4.0]], dtype=float64) + array([[1, 0, 0], + [0, 2, 0], + [0, 0, 3]], dtype=uint8) + + diagonal shifted by 2 + array([[0, 0, 1, 0, 0], + [0, 0, 0, 2, 0], + [0, 0, 0, 0, 3], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]], dtype=uint8) + + diagonal shifted by -2 + array([[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [0, 2, 0, 0, 0], + [0, 0, 3, 0, 0]], dtype=uint8) @@ -424,19 +448,38 @@ compiled with complex support. from ulab import numpy as np - a = np.array(range(16)).reshape((4, 4)) - print('a: ', a) - print() - print('diagonal of a: ', np.diag(a)) + a = np.arange(16).reshape((4, 4)) + print(a) + print('\ndiagonal of a:') + print(np.diag(a)) + + print('\ndiagonal of a:') + print(np.diag(a)) + + print('\ndiagonal of a, shifted by 2') + print(np.diag(a, k=2)) + + print('\ndiagonal of a, shifted by 5') + print(np.diag(a, k=5)) .. parsed-literal:: - a: array([[0.0, 1.0, 2.0, 3.0], - [4.0, 5.0, 6.0, 7.0], - [8.0, 9.0, 10.0, 11.0], - [12.0, 13.0, 14.0, 15.0]], dtype=float64) + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=int16) + + diagonal of a: + array([0, 5, 10, 15], dtype=int16) - diagonal of a: array([0.0, 5.0, 10.0, 15.0], dtype=float64) + diagonal of a: + array([0, 5, 10, 15], dtype=int16) + + diagonal of a, shifted by 2 + array([2, 7], dtype=int16) + + diagonal of a, shifted by 5 + array([], dtype=int16) @@ -454,7 +497,7 @@ The ``empty`` function can accept complex as the value of the dtype, if the firmware was compiled with complex support. eye -~~~ +--- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.eye.html @@ -475,7 +518,7 @@ The ``eye`` function can accept ``complex`` as the value of the ``dtype``, if the firmware was compiled with complex support. With a single argument -^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~ .. code:: @@ -497,7 +540,7 @@ With a single argument Specifying the dimensions of the matrix -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: @@ -536,7 +579,7 @@ Specifying the dimensions of the matrix frombuffer -~~~~~~~~~~ +---------- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.frombuffer.html @@ -582,7 +625,7 @@ a default of -1, meaning that all data are taken in. full -~~~~ +---- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.full.html @@ -622,7 +665,7 @@ value of ``dtype``, if the firmware was compiled with complex support. linspace -~~~~~~~~ +-------- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html @@ -671,7 +714,7 @@ complex scalar. logspace -~~~~~~~~ +-------- ``linspace``\ ’ equivalent for logarithmically spaced data is ``logspace``. This function produces a sequence of numbers, in which the @@ -723,7 +766,7 @@ also accepts the ``base`` argument. The default value is 10. ones, zeros -~~~~~~~~~~~ +----------- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html @@ -793,7 +836,7 @@ larger than the maximum dimension of your firmware. Customising array printouts ---------------------------- +=========================== ``ndarray``\ s are pretty-printed, i.e., if the number of entries along the last axis is larger than 10 (default value), then only the first and @@ -817,7 +860,7 @@ last three entries will be printed. Also note that, as opposed to set_printoptions -~~~~~~~~~~~~~~~~ +---------------- The default values can be overwritten by means of the ``set_printoptions`` function @@ -855,7 +898,7 @@ the ellipsis, if the array is longer than ``threshold``. get_printoptions -~~~~~~~~~~~~~~~~ +---------------- The set value of the ``threshold`` and ``edgeitems`` can be retrieved by calling the ``get_printoptions`` function with no arguments. The @@ -878,7 +921,7 @@ function returns a *dictionary* with two keys. Methods and properties of ndarrays ----------------------------------- +================================== Arrays have several *properties* that can queried, and some methods that can be called. With the exception of the flatten and transpose @@ -905,7 +948,7 @@ compiled with complex support. 16. `.sort <#.sort>`__ .byteswap -~~~~~~~~~ +--------- ``numpy`` https://numpy.org/doc/stable/reference/generated/numpy.char.chararray.byteswap.html @@ -951,7 +994,7 @@ value of ``inplace``. .copy -~~~~~ +----- The ``.copy`` method creates a new *deep copy* of an array, i.e., the entries of the source array are *copied* into the target array. @@ -978,7 +1021,7 @@ entries of the source array are *copied* into the target array. .dtype -~~~~~~ +------ ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.dtype.htm @@ -1048,7 +1091,7 @@ the type code for signed 8-bit integers. The object definition adds around 600 bytes to the firmware. .flat -~~~~~ +----- numpy: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flat.htm @@ -1103,7 +1146,7 @@ iterator itself, while flattening produces a new copy. .flatten -~~~~~~~~ +-------- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flatten.htm @@ -1142,7 +1185,7 @@ https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flatten.htm .imag -~~~~~ +----- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.imag.html @@ -1179,7 +1222,7 @@ always ``float``, irrespective of the values. .itemsize -~~~~~~~~~ +--------- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.itemsize.html @@ -1216,7 +1259,7 @@ the array. .real -~~~~~ +----- numpy: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.real.html @@ -1250,7 +1293,7 @@ with complex support, and returns a copy with the real part of an array. .reshape -~~~~~~~~ +-------- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html @@ -1291,7 +1334,7 @@ consistent with the old, a ``ValueError`` exception will be raised. Note that `ndarray.reshape()` can also be called by assigning to `ndarray.shape`. .shape -~~~~~~ +------ ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.shape.html @@ -1356,7 +1399,7 @@ By assigning a tuple to the ``.shape`` property, the array can be .size -~~~~~ +----- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.size.html @@ -1398,7 +1441,7 @@ The ``.T`` property of the ``ndarray`` is equivalent to `.transpose <#.transpose>`__. .tobytes -~~~~~~~~ +-------- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tobytes.html @@ -1444,7 +1487,7 @@ not dense (i.e., it has already been sliced). .tolist -~~~~~~~ +------- ``numpy``: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tolist.html @@ -1482,7 +1525,7 @@ into a (nested) ``python`` lists. .transpose -~~~~~~~~~~ +---------- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html @@ -1550,7 +1593,7 @@ property: .sort -~~~~~ +----- ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.sort.html @@ -1605,13 +1648,13 @@ In-place sorting of an ``ndarray``. For a more detailed exposition, see Unary operators ---------------- +=============== With the exception of ``len``, which returns a single number, all unary operators manipulate the underlying data element-wise. len -~~~ +--- This operator takes a single argument, the array, and returns either the length of the first axis. @@ -1652,7 +1695,7 @@ The number returned by ``len`` is also the length of the iterations, when the array supplies the elements for an iteration (see later). invert -~~~~~~ +------ The function is defined for integer data types (``uint8``, ``int8``, ``uint16``, and ``int16``) only, takes a single argument, and returns @@ -1688,7 +1731,7 @@ unexpected, as in the example below: abs -~~~ +--- This function takes a single argument, and returns the element-by-element absolute value of the array. When the data type is @@ -1714,7 +1757,7 @@ returned immediately, and no calculation takes place. neg -~~~ +--- This operator takes a single argument, and changes the sign of each element in the array. Unsigned values are wrapped. @@ -1745,7 +1788,7 @@ element in the array. Unsigned values are wrapped. pos -~~~ +--- This function takes a single argument, and simply returns a copy of the array. @@ -1769,7 +1812,7 @@ array. Binary operators ----------------- +================ ``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, ``/=``, @@ -1826,7 +1869,7 @@ exception: on `array comparison <#Comparison-of-arrays>`__ for details. Upcasting -~~~~~~~~~ +--------- Binary operations require special attention, because two arrays with different typecodes can be the operands of an operation, in which case @@ -1849,7 +1892,9 @@ conventions. ``dtype``. Thus, e.g., if the scalar is 123, it will be converted into an array of ``dtype`` ``uint8``, while -1000 will be converted into ``int16``. An ``mp_obj_float``, will always be promoted to - ``dtype`` ``float``. Other micropython types (e.g., lists, tuples, + ``dtype`` ``float``. Similarly, if ``ulab`` supports complex arrays, + the result of a binary operation involving a ``complex`` array is + always complex. Other ``micropython`` types (e.g., lists, tuples, etc.) raise a ``TypeError`` exception. 4. @@ -1905,7 +1950,7 @@ Upcasting can be seen in action in the following snippet: Benchmarks -~~~~~~~~~~ +---------- The following snippet compares the performance of binary operations to a possible implementation in python. For the time measurement, we will @@ -1998,7 +2043,7 @@ on graphical displays `__. Comparison operators --------------------- +==================== The smaller than, greater than, smaller or equal, and greater or equal operators return a vector of Booleans indicating the positions @@ -2046,7 +2091,7 @@ following code will not work: Iterating over arrays ---------------------- +===================== ``ndarray``\ s are iterable, which means that their elements can also be accessed as can the elements of a list, tuple, etc. If the array is @@ -2094,10 +2139,10 @@ reduced-dimensional *view* is created and returned. Slicing and indexing --------------------- +==================== Views vs. copies -~~~~~~~~~~~~~~~~ +---------------- ``numpy`` has a very important concept called *views*, which is a powerful extension of ``python``\ ’s own notion of slicing. Slices are @@ -2210,7 +2255,7 @@ section `Slicing and assigning to slices <#Slicing-and-assigning-to-slices>`__ should clarify the issue. Indexing -~~~~~~~~ +-------- The simplest form of indexing is specifying a single integer between the square brackets as in @@ -2373,7 +2418,7 @@ On the right hand side of the assignment we can even have another array. Slicing and assigning to slices -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------- You can also generate sub-arrays by specifying slices as the index of an array. Slices are special python objects of the form diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index 12bb8540..2d603930 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -17,8 +17,8 @@ "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2022-02-01T17:41:38.040350Z", - "start_time": "2022-02-01T17:41:38.023988Z" + "end_time": "2022-02-09T06:27:15.118699Z", + "start_time": "2022-02-09T06:27:15.100980Z" } }, "outputs": [ @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '5.0.0'\n", + "release = '5.0.2'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -218,8 +218,8 @@ "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2022-02-01T17:41:42.215395Z", - "start_time": "2022-02-01T17:41:40.650763Z" + "end_time": "2022-02-09T06:27:21.647179Z", + "start_time": "2022-02-09T06:27:20.019520Z" } }, "outputs": [], @@ -259,8 +259,8 @@ "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2022-01-29T20:52:14.453177Z", - "start_time": "2022-01-29T20:52:04.020858Z" + "end_time": "2022-02-09T06:27:42.024028Z", + "start_time": "2022-02-09T06:27:36.109093Z" } }, "outputs": [], diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index 7524e35f..bf016b1a 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -34,8 +34,8 @@ "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2022-01-07T18:46:22.666663Z", - "start_time": "2022-01-07T18:46:22.663583Z" + "end_time": "2022-02-09T06:10:18.391925Z", + "start_time": "2022-02-09T06:10:18.388146Z" } }, "outputs": [], @@ -52,8 +52,8 @@ "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2022-01-07T18:46:29.198681Z", - "start_time": "2022-01-07T18:46:29.177654Z" + "end_time": "2022-02-09T06:10:19.000982Z", + "start_time": "2022-02-09T06:10:18.979322Z" } }, "outputs": [], @@ -505,7 +505,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Array initialisation functions\n", + "# Array initialisation functions\n", "\n", "There are nine functions that can be used for initialising an array. Starred functions accept `complex` as the value of the `dtype`, if the firmware was compiled with complex support.\n", "\n", @@ -526,7 +526,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### arange\n", + "## arange\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.arange.html\n", "\n", @@ -571,7 +571,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### concatenate\n", + "## concatenate\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.concatenate.html\n", "\n", @@ -684,26 +684,44 @@ "\n", "Extract a diagonal, or construct a diagonal array.\n", "\n", - "The function takes two arguments, an `ndarray`, and a shift. If the first argument is a two-dimensional array, the function returns a one-dimensional array containing the diagonal entries. The diagonal can be shifted by an amount given in the second argument.\n", + "The function takes a positional argument, an `ndarray`, or any `micropython` iterable, and an optional keyword argument, a shift, with a default value of 0. If the first argument is a two-dimensional array (or a two-dimensional iterable, e.g., a list of lists), the function returns a one-dimensional array containing the diagonal entries. The diagonal can be shifted by an amount given in the second argument. If the shift is larger than the length of the corresponding axis, an empty array is returned.\n", "\n", - "If the first argument is a one-dimensional array, the function returns a two-dimensional tensor with its diagonal elements given by the first argument.\n", + "If the first argument is a one-dimensional array, the function returns a two-dimensional square tensor with its diagonal elements given by the first argument. Again, the diagonal be shifted by an amount given by the keyword argument.\n", "\n", "The `diag` function can accept a complex array, if the firmware was compiled with complex support." ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2022-02-09T06:24:38.290495Z", + "start_time": "2022-02-09T06:24:38.273075Z" + } + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "array([[1.0, 0.0, 0.0, 0.0],\n", - " [0.0, 2.0, 0.0, 0.0],\n", - " [0.0, 0.0, 3.0, 0.0],\n", - " [0.0, 0.0, 0.0, 4.0]], dtype=float64)\n", + "array([[1, 0, 0],\n", + " [0, 2, 0],\n", + " [0, 0, 3]], dtype=uint8)\n", + "\n", + "diagonal shifted by 2\n", + "array([[0, 0, 1, 0, 0],\n", + " [0, 0, 0, 2, 0],\n", + " [0, 0, 0, 0, 3],\n", + " [0, 0, 0, 0, 0],\n", + " [0, 0, 0, 0, 0]], dtype=uint8)\n", + "\n", + "diagonal shifted by -2\n", + "array([[0, 0, 0, 0, 0],\n", + " [0, 0, 0, 0, 0],\n", + " [1, 0, 0, 0, 0],\n", + " [0, 2, 0, 0, 0],\n", + " [0, 0, 3, 0, 0]], dtype=uint8)\n", "\n", "\n" ] @@ -714,25 +732,46 @@ "\n", "from ulab import numpy as np\n", "\n", - "a = np.array([1, 2, 3, 4])\n", - "print(np.diag(a))" + "a = np.array([1, 2, 3], dtype=np.uint8)\n", + "print(np.diag(a))\n", + "\n", + "print('\\ndiagonal shifted by 2')\n", + "print(np.diag(a, k=2))\n", + "\n", + "print('\\ndiagonal shifted by -2')\n", + "print(np.diag(a, k=-2))" ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2022-02-09T06:26:39.213828Z", + "start_time": "2022-02-09T06:26:39.199294Z" + } + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "a: array([[0.0, 1.0, 2.0, 3.0],\n", - " [4.0, 5.0, 6.0, 7.0],\n", - " [8.0, 9.0, 10.0, 11.0],\n", - " [12.0, 13.0, 14.0, 15.0]], dtype=float64)\n", + "array([[0, 1, 2, 3],\n", + " [4, 5, 6, 7],\n", + " [8, 9, 10, 11],\n", + " [12, 13, 14, 15]], dtype=int16)\n", + "\n", + "diagonal of a:\n", + "array([0, 5, 10, 15], dtype=int16)\n", + "\n", + "diagonal of a:\n", + "array([0, 5, 10, 15], dtype=int16)\n", "\n", - "diagonal of a: array([0.0, 5.0, 10.0, 15.0], dtype=float64)\n", + "diagonal of a, shifted by 2\n", + "array([2, 7], dtype=int16)\n", + "\n", + "diagonal of a, shifted by 5\n", + "array([], dtype=int16)\n", "\n", "\n" ] @@ -743,10 +782,19 @@ "\n", "from ulab import numpy as np\n", "\n", - "a = np.array(range(16)).reshape((4, 4))\n", - "print('a: ', a)\n", - "print()\n", - "print('diagonal of a: ', np.diag(a))" + "a = np.arange(16).reshape((4, 4))\n", + "print(a)\n", + "print('\\ndiagonal of a:')\n", + "print(np.diag(a))\n", + "\n", + "print('\\ndiagonal of a:')\n", + "print(np.diag(a))\n", + "\n", + "print('\\ndiagonal of a, shifted by 2')\n", + "print(np.diag(a, k=2))\n", + "\n", + "print('\\ndiagonal of a, shifted by 5')\n", + "print(np.diag(a, k=5))" ] }, { @@ -766,7 +814,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### eye\n", + "## eye\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.eye.html\n", "\n", @@ -784,7 +832,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### With a single argument" + "### With a single argument" ] }, { @@ -823,7 +871,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Specifying the dimensions of the matrix" + "### Specifying the dimensions of the matrix" ] }, { @@ -892,7 +940,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### frombuffer\n", + "## frombuffer\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.frombuffer.html\n", "\n", @@ -946,7 +994,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### full\n", + "## full\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.full.html\n", "\n", @@ -998,7 +1046,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### linspace\n", + "## linspace\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html\n", "\n", @@ -1052,7 +1100,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### logspace\n", + "## logspace\n", "\n", "`linspace`' equivalent for logarithmically spaced data is `logspace`. This function produces a sequence of numbers, in which the quotient of consecutive numbers is constant. This is a geometric sequence.\n", "\n", @@ -1108,7 +1156,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### ones, zeros\n", + "## ones, zeros\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html\n", "\n", @@ -1207,7 +1255,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Customising array printouts" + "# Customising array printouts" ] }, { @@ -1250,7 +1298,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### set_printoptions\n", + "## set_printoptions\n", "\n", "The default values can be overwritten by means of the `set_printoptions` function [numpy.set_printoptions](https://numpy.org/doc/1.18/reference/generated/numpy.set_printoptions.html), which accepts two keywords arguments, the `threshold`, and the `edgeitems`. The first of these arguments determines the length of the longest array that will be printed in full, while the second is the number of items that will be printed on the left and right hand side of the ellipsis, if the array is longer than `threshold`." ] @@ -1298,7 +1346,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### get_printoptions\n", + "## get_printoptions\n", "\n", "The set value of the `threshold` and `edgeitems` can be retrieved by calling the `get_printoptions` function with no arguments. The function returns a *dictionary* with two keys." ] @@ -1336,7 +1384,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Methods and properties of ndarrays\n", + "# Methods and properties of ndarrays\n", "\n", "Arrays have several *properties* that can queried, and some methods that can be called. With the exception of the flatten and transpose operators, properties return an object that describe some feature of the array, while the methods return a new array-like object. The `imag`, and `real` properties are included in the firmware only, when it was compiled with complex support.\n", "\n", @@ -1362,7 +1410,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .byteswap\n", + "## .byteswap\n", "\n", "`numpy` https://numpy.org/doc/stable/reference/generated/numpy.char.chararray.byteswap.html\n", "\n", @@ -1413,7 +1461,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .copy\n", + "## .copy\n", "\n", "The `.copy` method creates a new *deep copy* of an array, i.e., the entries of the source array are *copied* into the target array." ] @@ -1456,7 +1504,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .dtype\n", + "## .dtype\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.dtype.htm\n", "\n", @@ -1556,7 +1604,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .flat\n", + "## .flat\n", "\n", "numpy: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flat.htm\n", "\n", @@ -1617,7 +1665,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .flatten\n", + "## .flatten\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flatten.htm\n", "\n", @@ -1670,7 +1718,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .imag\n", + "## .imag\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.imag.html\n", "\n", @@ -1719,7 +1767,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .itemsize\n", + "## .itemsize\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.itemsize.html\n", "\n", @@ -1771,7 +1819,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .real\n", + "## .real\n", "\n", "numpy: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.real.html\n", "\n", @@ -1820,7 +1868,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .reshape\n", + "## .reshape\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html\n", "\n", @@ -1877,7 +1925,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .shape\n", + "## .shape\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.shape.html\n", "\n", @@ -1969,7 +2017,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .size\n", + "## .size\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.size.html\n", "\n", @@ -2030,7 +2078,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .tobytes\n", + "## .tobytes\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tobytes.html\n", "\n", @@ -2085,7 +2133,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .tolist\n", + "## .tolist\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tolist.html\n", "\n", @@ -2138,7 +2186,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .transpose\n", + "## .transpose\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html\n", "\n", @@ -2233,7 +2281,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### .sort\n", + "## .sort\n", "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.sort.html\n", "\n", @@ -2303,7 +2351,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Unary operators\n", + "# Unary operators\n", "\n", "With the exception of `len`, which returns a single number, all unary operators manipulate the underlying data element-wise. " ] @@ -2312,7 +2360,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### len\n", + "## len\n", "\n", "This operator takes a single argument, the array, and returns either the length of the first axis." ] @@ -2373,7 +2421,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### invert\n", + "## invert\n", "\n", "The function is defined for integer data types (`uint8`, `int8`, `uint16`, and `int16`) only, takes a single argument, and returns the element-by-element, bit-wise inverse of the array. If a `float` is supplied, the function raises a `ValueError` exception.\n", "\n", @@ -2422,7 +2470,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### abs\n", + "## abs\n", "\n", "This function takes a single argument, and returns the element-by-element absolute value of the array. When the data type is unsigned (`uint8`, or `uint16`), a copy of the array will be returned immediately, and no calculation takes place." ] @@ -2462,7 +2510,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### neg\n", + "## neg\n", "\n", "This operator takes a single argument, and changes the sign of each element in the array. Unsigned values are wrapped. " ] @@ -2509,7 +2557,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### pos\n", + "## pos\n", "\n", "This function takes a single argument, and simply returns a copy of the array." ] @@ -2549,7 +2597,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Binary operators\n", + "# Binary operators\n", "\n", "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators that work element-wise. Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", "A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n", @@ -2635,7 +2683,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Upcasting\n", + "## Upcasting\n", "\n", "Binary operations require special attention, because two arrays with different typecodes can be the operands of an operation, in which case it is not trivial, what the typecode of the result is. This decision on the result's typecode is called upcasting. Since the number of typecodes in `ulab` is significantly smaller than in `numpy`, we have to define new upcasting rules. Where possible, I followed `numpy`'s conventions. \n", "\n", @@ -2645,7 +2693,7 @@ "\n", "2. if either of the operands is a float, the result is automatically a float\n", "\n", - "3. When one of the operands is a scalar, it will internally be turned into a single-element `ndarray` with the *smallest* possible `dtype`. Thus, e.g., if the scalar is 123, it will be converted into an array of `dtype` `uint8`, while -1000 will be converted into `int16`. An `mp_obj_float`, will always be promoted to `dtype` `float`. Other micropython types (e.g., lists, tuples, etc.) raise a `TypeError` exception. \n", + "3. When one of the operands is a scalar, it will internally be turned into a single-element `ndarray` with the *smallest* possible `dtype`. Thus, e.g., if the scalar is 123, it will be converted into an array of `dtype` `uint8`, while -1000 will be converted into `int16`. An `mp_obj_float`, will always be promoted to `dtype` `float`. Similarly, if `ulab` supports complex arrays, the result of a binary operation involving a `complex` array is always complex. Other `micropython` types (e.g., lists, tuples, etc.) raise a `TypeError` exception. \n", "\n", "4. \n", " \n", @@ -2712,7 +2760,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Benchmarks\n", + "## Benchmarks\n", "\n", "The following snippet compares the performance of binary operations to a possible implementation in python. For the time measurement, we will take the following snippet from the micropython manual:" ] @@ -2830,7 +2878,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Comparison operators\n", + "# Comparison operators\n", "\n", "The smaller than, greater than, smaller or equal, and greater or equal operators return a vector of Booleans indicating the positions (`True`), where the condition is satisfied. " ] @@ -2903,7 +2951,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Iterating over arrays\n", + "# Iterating over arrays\n", "\n", "`ndarray`s are iterable, which means that their elements can also be accessed as can the elements of a list, tuple, etc. If the array is one-dimensional, the iterator returns scalars, otherwise a new reduced-dimensional *view* is created and returned." ] @@ -2965,10 +3013,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Slicing and indexing\n", + "# Slicing and indexing\n", "\n", "\n", - "### Views vs. copies\n", + "## Views vs. copies\n", "\n", "`numpy` has a very important concept called *views*, which is a powerful extension of `python`'s own notion of slicing. Slices are special python objects of the form\n", "\n", @@ -3114,7 +3162,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Indexing\n", + "## Indexing\n", "\n", "The simplest form of indexing is specifying a single integer between the square brackets as in " ] @@ -3373,7 +3421,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Slicing and assigning to slices\n", + "## Slicing and assigning to slices\n", "\n", "You can also generate sub-arrays by specifying slices as the index of an array. Slices are special python objects of the form " ] From 4705c95fb2ad405dba735464b40ae0477f46cfc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 9 Feb 2022 07:45:15 +0100 Subject: [PATCH 004/113] add diag tests --- tests/2d/numpy/diag.py | 29 ++++++++++++++++++++++++ tests/2d/numpy/diag.py.exp | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 tests/2d/numpy/diag.py create mode 100644 tests/2d/numpy/diag.py.exp diff --git a/tests/2d/numpy/diag.py b/tests/2d/numpy/diag.py new file mode 100644 index 00000000..5eed1633 --- /dev/null +++ b/tests/2d/numpy/diag.py @@ -0,0 +1,29 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +a = np.arange(25).reshape((5,5)) + +print(np.diag(a)) +print(np.diag(a, k=2)) +print(np.diag(a, k=-2)) +print(np.diag(a, k=10)) +print(np.diag(a, k=-10)) + +a = np.arange(4) + +print(np.diag(a)) +print(np.diag(a, k=2)) +print(np.diag(a, k=-2)) + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + print(np.diag(a)) + +for dtype in dtypes: + a = np.array(range(16), dtype=dtype).reshape((4,4)) + print(np.diag(a)) diff --git a/tests/2d/numpy/diag.py.exp b/tests/2d/numpy/diag.py.exp new file mode 100644 index 00000000..127b129a --- /dev/null +++ b/tests/2d/numpy/diag.py.exp @@ -0,0 +1,46 @@ +array([0, 6, 12, 18, 24], dtype=int16) +array([2, 8, 14], dtype=int16) +array([10, 16, 22], dtype=int16) +array([], dtype=int16) +array([], dtype=int16) +array([[0, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 2, 0], + [0, 0, 0, 3]], dtype=int16) +array([[0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 2, 0], + [0, 0, 0, 0, 0, 3], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0]], dtype=int16) +array([[0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0], + [0, 0, 2, 0, 0, 0], + [0, 0, 0, 3, 0, 0]], dtype=int16) +array([[0, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 2, 0], + [0, 0, 0, 3]], dtype=uint8) +array([[0, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 2, 0], + [0, 0, 0, 3]], dtype=int8) +array([[0, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 2, 0], + [0, 0, 0, 3]], dtype=uint16) +array([[0, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 2, 0], + [0, 0, 0, 3]], dtype=int16) +array([[0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 3.0]], dtype=float64) +array([0, 5, 10, 15], dtype=uint8) +array([0, 5, 10, 15], dtype=int8) +array([0, 5, 10, 15], dtype=uint16) +array([0, 5, 10, 15], dtype=int16) +array([0.0, 5.0, 10.0, 15.0], dtype=float64) From 0ef234138681db6e9dc52358a0a886216c158dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 10 Feb 2022 19:57:19 +0100 Subject: [PATCH 005/113] fix complex slicing --- code/ndarray.c | 8 ++++++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index d18d9f27..d4ae3aa8 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -387,6 +387,14 @@ MP_DEFINE_CONST_FUN_OBJ_0(ndarray_get_printoptions_obj, ndarray_get_printoptions mp_obj_t ndarray_get_item(ndarray_obj_t *ndarray, void *array) { // returns a proper micropython object from an array if(!ndarray->boolean) { + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + mp_float_t *c = (mp_float_t *)array; + mp_float_t real = *c++; + mp_float_t imag = *c; + return mp_obj_new_complex(real, imag); + } + #endif return mp_binary_get_val_array(ndarray->dtype, array, 0); } else { if(*(uint8_t *)array) { diff --git a/code/ulab.c b/code/ulab.c index d88aac35..bdb066b2 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.2 +#define ULAB_VERSION 5.0.3 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index c744cb5a..d3be998a 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Thu, 10 Feb 2020 + +version 5.0.3 + + fix complex slicing + Tue, 8 Feb 2022 version 5.0.2 From 607fd303bbb6654a84796fae5430f003d1e6f9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 12 Feb 2022 18:40:44 +0100 Subject: [PATCH 006/113] remove printf --- code/ndarray.c | 1 - 1 file changed, 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index d4ae3aa8..658e5cd6 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -737,7 +737,6 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { if(source->boolean) { dtype = NDARRAY_BOOL; } - // printf("%d\n", dtype); ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype); ndarray_copy_array(source, ndarray, 0); return ndarray; From d2939dbf228f739c9f9bc1687c4e23443df2398a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 18 Feb 2022 19:34:30 +0100 Subject: [PATCH 007/113] fix binary ops on empty arrays --- code/ndarray.c | 2 +- code/ulab.c | 2 +- docs/ulab-change-log.md | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 658e5cd6..c8238033 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1865,7 +1865,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { // the empty arrays have to be treated separately uint8_t dtype = NDARRAY_INT16; ndarray_obj_t *nd; - if((lhs->ndim == 0) || (rhs->ndim == 0)) { + if((lhs->len == 0) || (rhs->len == 0)) { switch(op) { case MP_BINARY_OP_INPLACE_ADD: case MP_BINARY_OP_INPLACE_MULTIPLY: diff --git a/code/ulab.c b/code/ulab.c index bdb066b2..ab72135e 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.3 +#define ULAB_VERSION 5.0.4 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index d3be998a..b660af68 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,4 +1,10 @@ -Thu, 10 Feb 2020 +Fri, 18 Feb 2022 + +version 5.0.4 + + fix the handling of empty arrays in binary_op + +Thu, 10 Feb 2022 version 5.0.3 From 15d9df16eefd37184965ece6ec31209c1e6f383e Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 17 Apr 2022 15:28:11 -0500 Subject: [PATCH 008/113] update cp pip dependencies --- .github/workflows/build.yml | 2 +- requirements_cp_dev.txt | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d8352f9..9ec616ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -84,7 +84,7 @@ jobs: echo >>$GITHUB_PATH /usr/local/opt/gettext/bin echo >>$GITHUB_PATH /usr/local/opt/librsvg/bin fi - python3 -mpip install -r requirements_cp_dev.txt + python3 -mpip install --upgrade -r requirements_cp_dev.txt - name: Run build-cp.sh run: ./build-cp.sh ${{ matrix.dims }} diff --git a/requirements_cp_dev.txt b/requirements_cp_dev.txt index 2b7114ee..06f35fbd 100644 --- a/requirements_cp_dev.txt +++ b/requirements_cp_dev.txt @@ -1,15 +1,19 @@ -# for extract-pyi -isort +# For docs +mypy black +isort +astroid +setuptools +setuptools_scm -# For docs -Sphinx<4 -sphinx-rtd-theme -myst-parser +Sphinx>=4.0.0 sphinx-autoapi +sphinx-rtd-theme sphinxcontrib-svg2pdfconverter readthedocs-sphinx-search +myst-parser + +# For stubs and annotations +adafruit-circuitpython-typing -# for check-stubs -mypy From 48b3b7f6414f1f5d5308a67d12d3b2c3c7b0cd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 18 Apr 2022 10:18:19 +0200 Subject: [PATCH 009/113] fix sorting on empty arrays --- code/numpy/numerical.c | 14 ++++++++------ code/numpy/numerical.h | 1 + code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index d6983c0a..e6f53cf9 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -793,12 +793,14 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw // reset the array iarray = indices->array; - if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { - HEAP_ARGSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); - } else if((ndarray->dtype == NDARRAY_UINT16) || (ndarray->dtype == NDARRAY_INT16)) { - HEAP_ARGSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); - } else { - HEAP_ARGSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + if(ndarray->shape[ax]) { + if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { + HEAP_ARGSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + } else if((ndarray->dtype == NDARRAY_UINT16) || (ndarray->dtype == NDARRAY_INT16)) { + HEAP_ARGSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + } else { + HEAP_ARGSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + } } return MP_OBJ_FROM_PTR(indices); } diff --git a/code/numpy/numerical.h b/code/numpy/numerical.h index 8d2971cd..186c817b 100644 --- a/code/numpy/numerical.h +++ b/code/numpy/numerical.h @@ -155,6 +155,7 @@ type *_array = (type *)array;\ type tmp;\ uint16_t itmp, c, q = (N), p, r = (N) >> 1;\ + assert(N);\ for (;;) {\ if (r > 0) {\ r--;\ diff --git a/code/ulab.c b/code/ulab.c index ab72135e..534ff0a6 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.4 +#define ULAB_VERSION 5.0.5 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index b660af68..e9cd3717 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Thu, 14 Apr 2022 + +version 5.0.5 + + fix sorting on empty arrays + Fri, 18 Feb 2022 version 5.0.4 From dde8d24461667ef2ca7ea2593e7af3a553f0d866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 18 Apr 2022 10:34:02 +0200 Subject: [PATCH 010/113] add sort tests --- code/numpy/numerical.c | 14 ++++++++------ tests/2d/numpy/sort.py | 18 ++++++++++++++++++ tests/2d/numpy/sort.py.exp | 25 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/2d/numpy/sort.py create mode 100644 tests/2d/numpy/sort.py.exp diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index e6f53cf9..f7df534d 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -646,12 +646,14 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla int32_t increment = ndarray->strides[ax] / ndarray->itemsize; uint8_t *array = (uint8_t *)ndarray->array; - if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { - HEAPSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax]); - } else if((ndarray->dtype == NDARRAY_INT16) || (ndarray->dtype == NDARRAY_INT16)) { - HEAPSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax]); - } else { - HEAPSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + if(ndarray->shape[ax]) { + if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { + HEAPSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + } else if((ndarray->dtype == NDARRAY_INT16) || (ndarray->dtype == NDARRAY_INT16)) { + HEAPSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + } else { + HEAPSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + } } if(inplace == 1) { return mp_const_none; diff --git a/tests/2d/numpy/sort.py b/tests/2d/numpy/sort.py new file mode 100644 index 00000000..d91bb6fd --- /dev/null +++ b/tests/2d/numpy/sort.py @@ -0,0 +1,18 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +for dtype in dtypes: + print() + a = np.array([], dtype=dtype) + print(np.sort(a, axis=0)) + print(list(np.argsort(a, axis=0))) + + a = np.array([4, 1, 3, 2], dtype=dtype) + print(np.sort(a, axis=0)) + print(list(np.argsort(a, axis=0))) + + diff --git a/tests/2d/numpy/sort.py.exp b/tests/2d/numpy/sort.py.exp new file mode 100644 index 00000000..83e6d698 --- /dev/null +++ b/tests/2d/numpy/sort.py.exp @@ -0,0 +1,25 @@ + +array([], dtype=uint8) +[] +array([1, 2, 3, 4], dtype=uint8) +[1, 3, 2, 0] + +array([], dtype=int8) +[] +array([1, 2, 3, 4], dtype=int8) +[1, 3, 2, 0] + +array([], dtype=uint16) +[] +array([0, 0, 0, 0], dtype=uint16) +[1, 3, 2, 0] + +array([], dtype=int16) +[] +array([1, 2, 3, 4], dtype=int16) +[1, 3, 2, 0] + +array([], dtype=float64) +[] +array([1.0, 2.0, 3.0, 4.0], dtype=float64) +[1, 3, 2, 0] From 2772fd8e9169d6b50dceec6505d4161fce1c97a1 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Tue, 19 Apr 2022 21:45:34 -0400 Subject: [PATCH 011/113] Update reference to spectrum --- code/numpy/fft/fft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 8b73bddd..c1fbbba1 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -36,7 +36,7 @@ //| //| Perform a Fast Fourier Transform from the time domain into the frequency domain //| -//| See also ~ulab.extras.spectrum, which computes the magnitude of the fft, +//| See also `~ulab.scipy.signal.spectrum`, which computes the magnitude of the fft, //| rather than separately returning its real and imaginary parts.""" //| ... //| From 8c2cd49ffa0ef30b9803bd6de5571251621f1f43 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Wed, 20 Apr 2022 18:08:57 -0400 Subject: [PATCH 012/113] Fix reference to location of spectrum() --- code/numpy/fft/fft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index c1fbbba1..b70aec01 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -36,7 +36,7 @@ //| //| Perform a Fast Fourier Transform from the time domain into the frequency domain //| -//| See also `~ulab.scipy.signal.spectrum`, which computes the magnitude of the fft, +//| See also `~ulab.utils.spectrum`, which computes the magnitude of the fft, //| rather than separately returning its real and imaginary parts.""" //| ... //| From 8da81e123ef154b2d96b530e2014e1ff012a224f Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 22 Apr 2022 11:30:49 -0400 Subject: [PATCH 013/113] Fix typo of name from spectrum to spectrogram --- code/numpy/fft/fft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index b70aec01..38b0beb9 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -36,7 +36,7 @@ //| //| Perform a Fast Fourier Transform from the time domain into the frequency domain //| -//| See also `~ulab.utils.spectrum`, which computes the magnitude of the fft, +//| See also `~ulab.utils.spectrogram`, which computes the magnitude of the fft, //| rather than separately returning its real and imaginary parts.""" //| ... //| From a715eed7d9ea8500c577c46ca13ae3f06308704d Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Fri, 22 Apr 2022 12:10:42 -0400 Subject: [PATCH 014/113] Fix reference --- code/numpy/fft/fft.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 38b0beb9..31a8712e 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -26,6 +26,7 @@ //| """Frequency-domain functions""" //| //| import ulab.numpy +//| import ulab.utils //| def fft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]: @@ -36,7 +37,7 @@ //| //| Perform a Fast Fourier Transform from the time domain into the frequency domain //| -//| See also `~ulab.utils.spectrogram`, which computes the magnitude of the fft, +//| See also `ulab.utils.spectrogram`, which computes the magnitude of the fft, //| rather than separately returning its real and imaginary parts.""" //| ... //| From 53bc8d6b0ea368abac519a138cc467f075b7e46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 22 Apr 2022 22:10:01 +0200 Subject: [PATCH 015/113] replace m_new with m_new0, wherever reasonable (#521) replace m_new with m_new0, wherever reasonable, and remove dangling memory fragments created by m_new0 --- code/ndarray.c | 33 ++++++-------- code/numpy/create.c | 7 ++- code/numpy/io/io.c | 10 ++-- code/numpy/linalg/linalg_tools.c | 17 ++++--- code/numpy/numerical.c | 73 +++++++++++++++++------------- code/numpy/transform.c | 23 +++++++--- code/ulab.c | 2 +- docs/ulab-change-log.md | 5 ++ tests/2d/numpy/constructors.py | 13 ------ tests/2d/numpy/constructors.py.exp | 15 ------ 10 files changed, 94 insertions(+), 104 deletions(-) delete mode 100644 tests/2d/numpy/constructors.py delete mode 100644 tests/2d/numpy/constructors.py.exp diff --git a/code/ndarray.c b/code/ndarray.c index c8238033..6fc9ac6c 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -163,8 +163,7 @@ void ndarray_fill_array_iterable(mp_float_t *array, mp_obj_t iterable) { #if ULAB_HAS_FUNCTION_ITERATOR size_t *ndarray_new_coords(uint8_t ndim) { - size_t *coords = m_new(size_t, ndim); - memset(coords, 0, ndim*sizeof(size_t)); + size_t *coords = m_new0(size_t, ndim); return coords; } @@ -618,10 +617,9 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides // if the length is 0, still allocate a single item, so that contractions can be handled size_t len = ndarray->itemsize * MAX(1, ndarray->len); - uint8_t *array = m_new(byte, len); + uint8_t *array = m_new0(byte, len); // this should set all elements to 0, irrespective of the of the dtype (all bits are zero) // we could, perhaps, leave this step out, and initialise the array only, when needed - memset(array, 0, len); ndarray->array = array; ndarray->origin = array; return ndarray; @@ -1060,8 +1058,7 @@ bool ndarray_can_broadcast(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t *ndim // // 1. the two shapes are either equal // 2. one of the shapes is 1 - memset(lstrides, 0, sizeof(size_t)*ULAB_MAX_DIMS); - memset(rstrides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + lstrides[ULAB_MAX_DIMS - 1] = lhs->strides[ULAB_MAX_DIMS - 1]; rstrides[ULAB_MAX_DIMS - 1] = rhs->strides[ULAB_MAX_DIMS - 1]; for(uint8_t i=ULAB_MAX_DIMS; i > 0; i--) { @@ -1094,7 +1091,7 @@ bool ndarray_can_broadcast_inplace(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32 // // 1. the two shapes are either equal // 2. the shapes on the right hand side is 1 - memset(rstrides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + rstrides[ULAB_MAX_DIMS - 1] = rhs->strides[ULAB_MAX_DIMS - 1]; for(uint8_t i=ULAB_MAX_DIMS; i > 0; i--) { if((lhs->shape[i-1] == rhs->shape[i-1]) || (rhs->shape[i-1] == 0) || (rhs->shape[i-1] == 1)) { @@ -1142,10 +1139,8 @@ static mp_bound_slice_t generate_slice(mp_int_t n, mp_obj_t index) { } static ndarray_obj_t *ndarray_view_from_slices(ndarray_obj_t *ndarray, mp_obj_tuple_t *tuple) { - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); uint8_t ndim = ndarray->ndim; @@ -1187,9 +1182,9 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { return; } uint8_t ndim = 0; - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); - int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); if(!ndarray_can_broadcast(view, values, &ndim, shape, lstrides, rstrides)) { mp_raise_ValueError(translate("operands could not be broadcast together")); m_del(size_t, shape, ULAB_MAX_DIMS); @@ -1846,9 +1841,9 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { } uint8_t ndim = 0; - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); - int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); uint8_t broadcastable; if((op == MP_BINARY_OP_INPLACE_ADD) || (op == MP_BINARY_OP_INPLACE_MULTIPLY) || (op == MP_BINARY_OP_INPLACE_POWER) || (op == MP_BINARY_OP_INPLACE_SUBTRACT) || (op == MP_BINARY_OP_INPLACE_TRUE_DIVIDE)) { @@ -2157,8 +2152,8 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { if(shape->len > ULAB_MAX_DIMS) { mp_raise_ValueError(translate("maximum number of dimensions is 4")); } - size_t *new_shape = m_new(size_t, ULAB_MAX_DIMS); - memset(new_shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + size_t *new_shape = m_new0(size_t, ULAB_MAX_DIMS); + size_t new_length = 1; for(uint8_t i=0; i < shape->len; i++) { new_shape[ULAB_MAX_DIMS - i - 1] = mp_obj_get_int(shape->items[shape->len - i - 1]); diff --git a/code/numpy/create.c b/code/numpy/create.c index e0934284..883fc326 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -35,8 +35,8 @@ static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t if(len > ULAB_MAX_DIMS) { mp_raise_TypeError(translate("too many dimensions")); } - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, ULAB_MAX_DIMS * sizeof(size_t)); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + size_t i = 0; mp_obj_iter_buf_t iter_buf; mp_obj_t item, iterable = mp_getiter(oshape, &iter_buf); @@ -245,8 +245,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_raise_TypeError(translate("first argument must be a tuple of ndarrays")); } int8_t axis = (int8_t)args[1].u_int; - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); mp_obj_tuple_t *ndarrays = MP_OBJ_TO_PTR(args[0].u_obj); // first check, whether the arrays are compatible diff --git a/code/numpy/io/io.c b/code/numpy/io/io.c index 8f8840ad..0d029457 100644 --- a/code/numpy/io/io.c +++ b/code/numpy/io/io.c @@ -131,8 +131,7 @@ static mp_obj_t io_load(mp_obj_t file) { io_read_(stream, stream_p, buffer, "', 'fortran_order': False, 'shape': (", 37, &error); - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t) * ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); uint16_t bytes_to_read = MIN(ULAB_IO_BUFFER_SIZE, header_length - 51); // bytes_to_read is 128 at most. This should be enough to contain a @@ -230,6 +229,8 @@ static mp_obj_t io_load(mp_obj_t file) { m_del(char, tmpbuff, sz); } + m_del(size_t, shape, ULAB_MAX_DIMS); + return MP_OBJ_FROM_PTR(ndarray); } @@ -386,8 +387,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw mp_raise_ValueError(translate("usecols is too high")); } - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t) * ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); #if ULAB_MAX_DIMS == 1 shape[0] = rows; @@ -480,6 +480,8 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } while((read > 0) && (rows < max_rows)); stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); + + m_del(size_t, shape, ULAB_MAX_DIMS); m_del(char, buffer, ULAB_IO_BUFFER_SIZE); m_del(char, clipboard, ULAB_IO_CLIPBOARD_SIZE); m_del(uint16_t, cols, used_columns); diff --git a/code/numpy/linalg/linalg_tools.c b/code/numpy/linalg/linalg_tools.c index 5e03a50a..7ae97d21 100644 --- a/code/numpy/linalg/linalg_tools.c +++ b/code/numpy/linalg/linalg_tools.c @@ -14,8 +14,8 @@ #include "linalg_tools.h" -/* - * The following function inverts a matrix, whose entries are given in the input array +/* + * The following function inverts a matrix, whose entries are given in the input array * The function has no dependencies beyond micropython itself (for the definition of mp_float_t), * and can be used independent of ulab. */ @@ -26,10 +26,9 @@ bool linalg_invert_matrix(mp_float_t *data, size_t N) { // initially, this is the unit matrix: the contents of this matrix is what // will be returned after all the transformations - mp_float_t *unit = m_new(mp_float_t, N*N); + mp_float_t *unit = m_new0(mp_float_t, N*N); mp_float_t elem = 1.0; - // initialise the unit matrix - memset(unit, 0, sizeof(mp_float_t)*N*N); + for(size_t m=0; m < N; m++) { memcpy(&unit[m * (N+1)], &elem, sizeof(mp_float_t)); } @@ -78,9 +77,9 @@ bool linalg_invert_matrix(mp_float_t *data, size_t N) { return true; } -/* - * The following function calculates the eigenvalues and eigenvectors of a symmetric - * real matrix, whose entries are given in the input array. +/* + * The following function calculates the eigenvalues and eigenvectors of a symmetric + * real matrix, whose entries are given in the input array. * The function has no dependencies beyond micropython itself (for the definition of mp_float_t), * and can be used independent of ulab. */ @@ -166,6 +165,6 @@ size_t linalg_jacobi_rotations(mp_float_t *array, mp_float_t *eigvectors, size_t eigvectors[m * S + N] = s * vm + c * vn; } } while(iterations > 0); - + return iterations; } diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index f7df534d..b33d0262 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -515,10 +515,9 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t int8_t ax = tools_get_axis(axis, ndarray->ndim); uint8_t *array = (uint8_t *)ndarray->array; - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax; @@ -543,6 +542,9 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t } else { RUN_ARGMIN(ndarray, mp_float_t, array, results, rarray, shape, strides, index, optype); } + + m_del(int32_t, strides, ULAB_MAX_DIMS); + if(results->len == 1) { return mp_binary_get_val_array(results->dtype, results->array, 0); } @@ -636,10 +638,9 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla ax = tools_get_axis(axis, ndarray->ndim); } - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); ax = ULAB_MAX_DIMS - ndarray->ndim + ax; // we work with the typed array, so re-scale the stride @@ -655,6 +656,9 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla HEAPSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax]); } } + + m_del(int32_t, strides, ULAB_MAX_DIMS); + if(inplace == 1) { return mp_const_none; } else { @@ -733,17 +737,15 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim); - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(ndarray, ax, shape, strides); // We could return an NDARRAY_UINT8 array, if all lengths are shorter than 256 ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16); - int32_t *istrides = m_new(int32_t, ULAB_MAX_DIMS); - memset(istrides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + int32_t *istrides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(indices, ax, shape, istrides); + for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { istrides[i] /= sizeof(uint16_t); } @@ -804,6 +806,11 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw HEAP_ARGSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); } } + + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, strides, ULAB_MAX_DIMS); + m_del(int32_t, istrides, ULAB_MAX_DIMS); + return MP_OBJ_FROM_PTR(indices); } @@ -931,13 +938,12 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar int8_t *stencil = m_new(int8_t, N+1); stencil[0] = 1; - for(uint8_t i=1; i < N+1; i++) { + for(uint8_t i = 1; i < N+1; i++) { stencil[i] = -stencil[i-1]*(N-i+1)/i; } - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { shape[i] = ndarray->shape[i]; if(i == index) { shape[i] -= N; @@ -948,8 +954,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar uint8_t *rarray = (uint8_t *)results->array; memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(int32_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(ndarray, ax, shape, strides); if(ndarray->dtype == NDARRAY_UINT8) { @@ -1083,13 +1088,14 @@ mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ } else { int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim); - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(ndarray, ax, shape, strides); + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim-1, shape, NDARRAY_FLOAT); + m_del(size_t, shape, ULAB_MAX_DIMS); + mp_float_t *rarray = (mp_float_t *)results->array; uint8_t *array = (uint8_t *)ndarray->array; @@ -1237,16 +1243,12 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } else if(mp_obj_is_int(args[2].u_obj)){ int8_t ax = tools_get_axis(args[2].u_obj, ndarray->ndim); - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - memset(strides, 0, sizeof(int32_t)*ULAB_MAX_DIMS); + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(ndarray, ax, shape, strides); - size_t *rshape = m_new(size_t, ULAB_MAX_DIMS); - memset(rshape, 0, sizeof(size_t)*ULAB_MAX_DIMS); - int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); - memset(rstrides, 0, sizeof(int32_t)*ULAB_MAX_DIMS); + size_t *rshape = m_new0(size_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(results, ax, rshape, rstrides); ax = ULAB_MAX_DIMS - ndarray->ndim + ax; @@ -1307,9 +1309,16 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar i++; } while(i < shape[ULAB_MAX_DIMS - 3]); #endif + + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, strides, ULAB_MAX_DIMS); + m_del(size_t, rshape, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + } else { mp_raise_TypeError(translate("wrong axis index")); } + return results; } diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 3aeb685b..842c16b6 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -80,11 +80,11 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t)); - int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); if(axis == mp_const_none) { result = ndarray_new_linear_array(true_count, ndarray->dtype); - memset(rstrides, 0, ndarray->ndim * sizeof(int32_t)); + rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize; rshape[ULAB_MAX_DIMS - 1] = 0; } else { @@ -152,6 +152,11 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m } while(i < shape[ULAB_MAX_DIMS - 4]); #endif + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(size_t, rshape, ULAB_MAX_DIMS); + m_del(int32_t, strides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + return result; } @@ -245,16 +250,14 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t)); - int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); ndarray_obj_t *result = NULL; if(axis == mp_const_none) { result = ndarray_new_linear_array(ndarray->len - index_len, ndarray->dtype); - memset(rstrides, 0, ndarray->ndim * sizeof(int32_t)); rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize; memset(rshape, 0, sizeof(size_t) * ULAB_MAX_DIMS); - // rshape[ULAB_MAX_DIMS - 1] = 0; } else { rshape[shift_ax] = shape[shift_ax] - index_len; @@ -319,12 +322,18 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map #if ULAB_MAX_DIMS > 3 array -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS - 3]; array += strides[ULAB_MAX_DIMS - 4]; - rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2]; - rarray += rstrides[ULAB_MAX_DIMS - 3]; + rarray -= rstrides[ULAB_MAX_DIMS - 3] * rshape[ULAB_MAX_DIMS - 3]; + rarray += rstrides[ULAB_MAX_DIMS - 4]; i++; } while(i < shape[ULAB_MAX_DIMS - 4]); #endif + // TODO: deleting shape generates a seg fault + // m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(size_t, rshape, ULAB_MAX_DIMS); + m_del(int32_t, strides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + return MP_OBJ_FROM_PTR(result); } diff --git a/code/ulab.c b/code/ulab.c index 534ff0a6..97e535c6 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.5 +#define ULAB_VERSION 5.0.6 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index e9cd3717..5c7bbccc 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,4 +1,9 @@ Thu, 14 Apr 2022 +version 5.0.6 + + use m_new0 conditionally + +Thu, 14 Apr 2022 version 5.0.5 diff --git a/tests/2d/numpy/constructors.py b/tests/2d/numpy/constructors.py deleted file mode 100644 index dad3db23..00000000 --- a/tests/2d/numpy/constructors.py +++ /dev/null @@ -1,13 +0,0 @@ -from ulab import numpy as np - -print(np.ones(3)) -print(np.ones((2,3))) -print(np.zeros(3)) -print(np.zeros((2,3))) -print(np.eye(3)) -print(np.ones(1, dtype=np.int8)) -print(np.ones(2, dtype=np.uint8)) -print(np.ones(3, dtype=np.int16)) -print(np.ones(4, dtype=np.uint16)) -print(np.ones(5, dtype=np.float)) -print(np.linspace(0, 1, 9)) diff --git a/tests/2d/numpy/constructors.py.exp b/tests/2d/numpy/constructors.py.exp deleted file mode 100644 index 07dd7b0a..00000000 --- a/tests/2d/numpy/constructors.py.exp +++ /dev/null @@ -1,15 +0,0 @@ -array([1.0, 1.0, 1.0], dtype=float64) -array([[1.0, 1.0, 1.0], - [1.0, 1.0, 1.0]], dtype=float64) -array([0.0, 0.0, 0.0], dtype=float64) -array([[0.0, 0.0, 0.0], - [0.0, 0.0, 0.0]], dtype=float64) -array([[1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0]], dtype=float64) -array([1], dtype=int8) -array([1, 1], dtype=uint8) -array([1, 1, 1], dtype=int16) -array([1, 1, 1, 1], dtype=uint16) -array([1.0, 1.0, 1.0, 1.0, 1.0], dtype=float64) -array([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0], dtype=float64) From d438344943ab4383dae86b4620075ea877dec535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 17 May 2022 21:25:20 +0200 Subject: [PATCH 016/113] fix in-place assignment from slices (#524) * fix in-place assignment from slices --- build-cp.sh | 2 +- code/ndarray.c | 167 +++++++++++----------------------------- code/ndarray.h | 101 ------------------------ code/ulab.c | 2 +- docs/ulab-change-log.md | 7 ++ 5 files changed, 56 insertions(+), 223 deletions(-) diff --git a/build-cp.sh b/build-cp.sh index 1e665500..6193ad0d 100755 --- a/build-cp.sh +++ b/build-cp.sh @@ -37,7 +37,7 @@ readlinkf_posix() { } NPROC=$(python -c 'import multiprocessing; print(multiprocessing.cpu_count())') HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )" -[ -e circuitpython/py/py.mk ] || (git clone --no-recurse-submodules --depth 100 --branch main https://github.com/adafruit/circuitpython && cd circuitpython && git submodule update --init lib/uzlib tools) +[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-submodules && git submodule update --init lib/uzlib tools) rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab dims=${1-2} make -C circuitpython/mpy-cross -j$NPROC diff --git a/code/ndarray.c b/code/ndarray.c index 6fc9ac6c..5a7abce1 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1187,139 +1187,66 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); if(!ndarray_can_broadcast(view, values, &ndim, shape, lstrides, rstrides)) { mp_raise_ValueError(translate("operands could not be broadcast together")); - m_del(size_t, shape, ULAB_MAX_DIMS); - m_del(int32_t, lstrides, ULAB_MAX_DIMS); - m_del(int32_t, rstrides, ULAB_MAX_DIMS); - } + } else { - uint8_t *rarray = (uint8_t *)values->array; + ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(values, view->dtype); + // re-calculate rstrides, since the copy operation might have changed the directions of the strides + ndarray_can_broadcast(view, ndarray, &ndim, shape, lstrides, rstrides); + uint8_t *rarray = (uint8_t *)ndarray->array; - #if ULAB_SUPPORTS_COMPLEX - if(values->dtype == NDARRAY_COMPLEX) { - if(view->dtype != NDARRAY_COMPLEX) { - mp_raise_TypeError(translate("cannot convert complex to dtype")); - } else { - uint8_t *larray = (uint8_t *)view->array; - #if ULAB_MAX_DIMS > 3 - size_t i = 0; + uint8_t *larray = (uint8_t *)view->array; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; do { #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; + #if ULAB_MAX_DIMS > 1 + size_t k = 0; do { #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; + size_t l = 0; do { - #endif - size_t l = 0; - do { - memcpy(larray, rarray, view->itemsize); - larray += lstrides[ULAB_MAX_DIMS - 1]; - rarray += rstrides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < view->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - larray -= lstrides[ULAB_MAX_DIMS - 1] * view->shape[ULAB_MAX_DIMS-1]; - larray += lstrides[ULAB_MAX_DIMS - 2]; - rarray -= rstrides[ULAB_MAX_DIMS - 1] * view->shape[ULAB_MAX_DIMS-1]; - rarray += rstrides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < view->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - larray -= lstrides[ULAB_MAX_DIMS - 2] * view->shape[ULAB_MAX_DIMS-2]; - larray += lstrides[ULAB_MAX_DIMS - 3]; - rarray -= rstrides[ULAB_MAX_DIMS - 2] * view->shape[ULAB_MAX_DIMS-2]; - rarray += rstrides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < view->shape[ULAB_MAX_DIMS - 3]); + memcpy(larray, rarray, view->itemsize); + larray += lstrides[ULAB_MAX_DIMS - 1]; + rarray += rstrides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < view->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + larray -= lstrides[ULAB_MAX_DIMS - 1] * view->shape[ULAB_MAX_DIMS-1]; + larray += lstrides[ULAB_MAX_DIMS - 2]; + rarray -= rstrides[ULAB_MAX_DIMS - 1] * view->shape[ULAB_MAX_DIMS-1]; + rarray += rstrides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < view->shape[ULAB_MAX_DIMS - 2]); #endif - #if ULAB_MAX_DIMS > 3 - larray -= lstrides[ULAB_MAX_DIMS - 3] * view->shape[ULAB_MAX_DIMS-3]; - larray += lstrides[ULAB_MAX_DIMS - 4]; - rarray -= rstrides[ULAB_MAX_DIMS - 3] * view->shape[ULAB_MAX_DIMS-3]; - rarray += rstrides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < view->shape[ULAB_MAX_DIMS - 4]); + #if ULAB_MAX_DIMS > 2 + larray -= lstrides[ULAB_MAX_DIMS - 2] * view->shape[ULAB_MAX_DIMS-2]; + larray += lstrides[ULAB_MAX_DIMS - 3]; + rarray -= rstrides[ULAB_MAX_DIMS - 2] * view->shape[ULAB_MAX_DIMS-2]; + rarray += rstrides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < view->shape[ULAB_MAX_DIMS - 3]); #endif - } - return; - } - #endif - - // since in ASSIGNMENT_LOOP the array has a type, we have to divide the strides by the itemsize - for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { - lstrides[i] /= view->itemsize; - #if ULAB_SUPPORTS_COMPLEX - if(view->dtype == NDARRAY_COMPLEX) { - lstrides[i] *= 2; - } + #if ULAB_MAX_DIMS > 3 + larray -= lstrides[ULAB_MAX_DIMS - 3] * view->shape[ULAB_MAX_DIMS-3]; + larray += lstrides[ULAB_MAX_DIMS - 4]; + rarray -= rstrides[ULAB_MAX_DIMS - 3] * view->shape[ULAB_MAX_DIMS-3]; + rarray += rstrides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < view->shape[ULAB_MAX_DIMS - 4]); #endif } - if(view->dtype == NDARRAY_UINT8) { - if(values->dtype == NDARRAY_UINT8) { - ASSIGNMENT_LOOP(view, uint8_t, uint8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT8) { - ASSIGNMENT_LOOP(view, uint8_t, int8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_UINT16) { - ASSIGNMENT_LOOP(view, uint8_t, uint16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT16) { - ASSIGNMENT_LOOP(view, uint8_t, int16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_FLOAT) { - ASSIGNMENT_LOOP(view, uint8_t, mp_float_t, lstrides, rarray, rstrides); - } - } else if(view->dtype == NDARRAY_INT8) { - if(values->dtype == NDARRAY_UINT8) { - ASSIGNMENT_LOOP(view, int8_t, uint8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT8) { - ASSIGNMENT_LOOP(view, int8_t, int8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_UINT16) { - ASSIGNMENT_LOOP(view, int8_t, uint16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT16) { - ASSIGNMENT_LOOP(view, int8_t, int16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_FLOAT) { - ASSIGNMENT_LOOP(view, int8_t, mp_float_t, lstrides, rarray, rstrides); - } - } else if(view->dtype == NDARRAY_UINT16) { - if(values->dtype == NDARRAY_UINT8) { - ASSIGNMENT_LOOP(view, uint16_t, uint8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT8) { - ASSIGNMENT_LOOP(view, uint16_t, int8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_UINT16) { - ASSIGNMENT_LOOP(view, uint16_t, uint16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT16) { - ASSIGNMENT_LOOP(view, uint16_t, int16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_FLOAT) { - ASSIGNMENT_LOOP(view, uint16_t, mp_float_t, lstrides, rarray, rstrides); - } - } else if(view->dtype == NDARRAY_INT16) { - if(values->dtype == NDARRAY_UINT8) { - ASSIGNMENT_LOOP(view, int16_t, uint8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT8) { - ASSIGNMENT_LOOP(view, int16_t, int8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_UINT16) { - ASSIGNMENT_LOOP(view, int16_t, uint16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT16) { - ASSIGNMENT_LOOP(view, int16_t, int16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_FLOAT) { - ASSIGNMENT_LOOP(view, int16_t, mp_float_t, lstrides, rarray, rstrides); - } - } else { // the dtype must be an mp_float_t or complex now - if(values->dtype == NDARRAY_UINT8) { - ASSIGNMENT_LOOP(view, mp_float_t, uint8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT8) { - ASSIGNMENT_LOOP(view, mp_float_t, int8_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_UINT16) { - ASSIGNMENT_LOOP(view, mp_float_t, uint16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_INT16) { - ASSIGNMENT_LOOP(view, mp_float_t, int16_t, lstrides, rarray, rstrides); - } else if(values->dtype == NDARRAY_FLOAT) { - ASSIGNMENT_LOOP(view, mp_float_t, mp_float_t, lstrides, rarray, rstrides); - } - } + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, lstrides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + + return; } static mp_obj_t ndarray_from_boolean_index(ndarray_obj_t *ndarray, ndarray_obj_t *index) { diff --git a/code/ndarray.h b/code/ndarray.h index c6eec21d..7fc4dc2c 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -646,105 +646,4 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); #endif /* ULAB_MAX_DIMS == 4 */ #endif /* ULAB_HAS_FUNCTION_ITERATOR */ - -#if ULAB_MAX_DIMS == 1 -#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ - type_left *larray = (type_left *)(results)->array;\ - size_t l = 0;\ - do {\ - *larray = (type_left)(*((type_right *)(rarray)));\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ - -#endif /* ULAB_MAX_DIMS == 1 */ - -#if ULAB_MAX_DIMS == 2 -#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ - type_left *larray = (type_left *)(results)->array;\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - *larray = (type_left)(*((type_right *)(rarray)));\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ - (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ - -#endif /* ULAB_MAX_DIMS == 2 */ - -#if ULAB_MAX_DIMS == 3 -#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ - type_left *larray = (type_left *)(results)->array;\ - size_t j = 0;\ - do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - *larray = (type_left)(*((type_right *)(rarray)));\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ - (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ - (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ - j++;\ - } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ - -#endif /* ULAB_MAX_DIMS == 3 */ - -#if ULAB_MAX_DIMS == 4 -#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ - type_left *larray = (type_left *)(results)->array;\ - size_t i = 0;\ - do {\ - size_t j = 0;\ - do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - *larray = (type_left)(*((type_right *)(rarray)));\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ - (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ - (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ - j++;\ - } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ - (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ - i++;\ - } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ - -#endif /* ULAB_MAX_DIMS == 4 */ - #endif diff --git a/code/ulab.c b/code/ulab.c index 97e535c6..73e9aa32 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.6 +#define ULAB_VERSION 5.0.7 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 5c7bbccc..fd6d096b 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,4 +1,11 @@ +Mon, 16 May 2022 + +version 5.0.7 + + fix in-place assignment from slices + Thu, 14 Apr 2022 + version 5.0.6 use m_new0 conditionally From 00139bdd9684bd9d10b0d89fa8fbb0906f2d05af Mon Sep 17 00:00:00 2001 From: Philip Howard Date: Thu, 16 Jun 2022 13:26:10 +0100 Subject: [PATCH 017/113] Drop third argument from MP_REGISTER_MODULE. (#529) Adopt pattern from https://github.com/micropython/micropython/blob/efe23aca7154d21a9e8f283313c7a1ac29e05d86/docs/develop/cmodules.rst Signed-off-by: Phil Howard --- code/ulab.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/ulab.c b/code/ulab.c index 73e9aa32..9785a8dd 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -188,4 +188,10 @@ const mp_obj_module_t ulab_user_cmodule = { .globals = (mp_obj_dict_t*)&mp_module_ulab_globals, }; +// Use old three-argument MP_REGISTER_MODULE for +// MicroPython <= v1.18.0: (1 << 16) | (18 << 8) | 0 +#if MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule); +#endif From 1898c2fc71429a83d4084331961281e4c41e5811 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 6 Jul 2022 13:55:46 +1000 Subject: [PATCH 018/113] Fix error message for maximum number of dimensions (#532) Signed-off-by: Damien George --- code/ndarray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index 5a7abce1..7f94f3b8 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -2077,7 +2077,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(_shape); if(shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(translate("maximum number of dimensions is 4")); + mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); } size_t *new_shape = m_new0(size_t, ULAB_MAX_DIMS); From 1347694672dc76ae2d6d74fd2c76cccab7dfc311 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 7 Jul 2022 04:38:20 +1000 Subject: [PATCH 019/113] Fix build for MICROPY_OBJ_REPR_D configurations (#531) * Fix use of object pointers so code builds with MICROPY_OBJ_REPR_D Signed-off-by: Damien George * Fix use of float constants so they work with MICROPY_OBJ_REPR_D Signed-off-by: Damien George * Use new float-const macros to simplify definitions of e,inf,nan,pi. Signed-off-by: Damien George * Add support for MICROPY_OBJ_REPR_C Signed-off-by: Damien George * Add unix-nanbox build to build.sh script Building nanbox requires gcc-multilib because it forces 32-bit mode. Signed-off-by: Damien George * Bump version to 5.0.8 Signed-off-by: Damien George --- .github/workflows/build.yml | 6 + build.sh | 7 + code/ndarray.c | 38 +++--- code/ndarray.h | 44 +++++++ code/ndarray_operators.c | 32 ++--- code/numpy/approx.c | 18 +-- code/numpy/compare.c | 4 +- code/numpy/create.c | 44 +++---- code/numpy/fft/fft.c | 6 +- code/numpy/fft/fft_tools.c | 6 +- code/numpy/filter.c | 4 +- code/numpy/io/io.c | 20 +-- code/numpy/linalg/linalg.c | 26 ++-- code/numpy/ndarray/ndarray_iter.c | 4 +- code/numpy/numerical.c | 42 +++--- code/numpy/numpy.c | 212 +++++++++++++----------------- code/numpy/transform.c | 18 +-- code/numpy/vector.c | 8 +- code/scipy/linalg/linalg.c | 12 +- code/scipy/optimize/optimize.c | 48 +++---- code/scipy/scipy.c | 2 +- code/scipy/signal/signal.c | 12 +- code/scipy/special/special.c | 10 +- code/ulab.c | 10 +- code/utils/utils.c | 18 +-- 25 files changed, 340 insertions(+), 311 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ec616ae..8c0ba8b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,6 +33,12 @@ jobs: with: python-version: 3.8 + - name: Install requirements + run: | + if type -path apt-get; then + sudo apt-get install gcc-multilib + fi + - name: Versions run: | gcc --version diff --git a/build.sh b/build.sh index daf0a036..7f59ded4 100755 --- a/build.sh +++ b/build.sh @@ -38,6 +38,7 @@ readlinkf_posix() { return 1 } NPROC=`python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())'` +PLATFORM=`python3 -c 'import sys; print(sys.platform)'` set -e HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )" [ -e micropython/py/py.mk ] || git clone --no-recurse-submodules https://github.com/micropython/micropython @@ -47,5 +48,11 @@ make -C micropython/mpy-cross -j${NPROC} make -C micropython/ports/unix -j${NPROC} axtls make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-$dims PROG=micropython-$dims +# The unix nanbox variant builds as a 32-bit executable and requires gcc-multilib. +# macOS doesn't support i386 builds so only build on linux. +if [ $PLATFORM = linux ]; then + make -C micropython/ports/unix -j${NPROC} VARIANT=nanbox USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims +fi + bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" diff --git a/code/ndarray.c b/code/ndarray.c index 7f94f3b8..984fa188 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -272,7 +272,7 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args); @@ -317,7 +317,7 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t } dtype->dtype = _dtype; } - return dtype; + return MP_OBJ_FROM_PTR(dtype); } mp_obj_t ndarray_dtype(mp_obj_t self_in) { @@ -325,7 +325,7 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) { dtype_obj_t *dtype = m_new_obj(dtype_obj_t); dtype->base.type = &ulab_dtype_type; dtype->dtype = self->dtype; - return dtype; + return MP_OBJ_FROM_PTR(dtype); } #else @@ -356,17 +356,17 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) { #if ULAB_HAS_PRINTOPTIONS mp_obj_t ndarray_set_printoptions(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_threshold, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, - { MP_QSTR_edgeitems, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_threshold, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_edgeitems, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - if(args[0].u_rom_obj != mp_const_none) { - ndarray_print_threshold = mp_obj_get_int(args[0].u_rom_obj); + if(args[0].u_obj != mp_const_none) { + ndarray_print_threshold = mp_obj_get_int(args[0].u_obj); } - if(args[1].u_rom_obj != mp_const_none) { - ndarray_print_edgeitems = mp_obj_get_int(args[1].u_rom_obj); + if(args[1].u_obj != mp_const_none) { + ndarray_print_edgeitems = mp_obj_get_int(args[1].u_obj); } return mp_const_none; } @@ -816,7 +816,7 @@ ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *source, uint8_t dty i++; } while(i < source->shape[ULAB_MAX_DIMS - 4]); #endif - return MP_OBJ_FROM_PTR(ndarray); + return ndarray; } #if NDARRAY_HAS_BYTESWAP @@ -824,8 +824,8 @@ mp_obj_t ndarray_byteswap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ // changes the endiannes of an array // if the dtype of the input uint8/int8/bool, simply return a copy or view static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_inplace, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_inplace, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1004,8 +1004,8 @@ ndarray_obj_t *ndarray_from_iterable(mp_obj_t obj, uint8_t dtype) { STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1413,7 +1413,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra } else { mp_obj_t *items = m_new(mp_obj_t, 1); items[0] = index; - tuple = mp_obj_new_tuple(1, items); + tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(1, items)); } ndarray_obj_t *view = ndarray_view_from_slices(ndarray, tuple); if(values == NULL) { // return value(s) @@ -1724,7 +1724,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { mp_float_t *array = (mp_float_t *)ndarray->array; array[0] = mp_obj_get_float(obj); } else if(mp_obj_is_type(obj, &ulab_ndarray_type)){ - return obj; + return MP_OBJ_TO_PTR(obj); } #if ULAB_SUPPORTS_COMPLEX else if(mp_obj_is_type(obj, &mp_type_complex)) { @@ -1949,7 +1949,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(self->dtype == NDARRAY_COMPLEX) { int32_t *strides = strides_from_shape(self->shape, NDARRAY_FLOAT); ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT); - ndarray = carray_abs(self, target); + ndarray = MP_OBJ_TO_PTR(carray_abs(self, target)); } else { #endif ndarray = ndarray_copy_view(self); @@ -2121,10 +2121,10 @@ MP_DEFINE_CONST_FUN_OBJ_2(ndarray_reshape_obj, ndarray_reshape); #if ULAB_NUMPY_HAS_NDINFO mp_obj_t ndarray_info(mp_obj_t obj_in) { - ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj_in); - if(!mp_obj_is_type(ndarray, &ulab_ndarray_type)) { + if(!mp_obj_is_type(obj_in, &ulab_ndarray_type)) { mp_raise_TypeError(translate("function is defined for ndarrays only")); } + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj_in); mp_printf(MP_PYTHON_PRINTER, "class: ndarray\n"); mp_printf(MP_PYTHON_PRINTER, "shape: ("); if(ndarray->ndim == 1) { diff --git a/code/ndarray.h b/code/ndarray.h index 7fc4dc2c..815e4481 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -33,12 +33,56 @@ #define FLOAT_TYPECODE 'd' #endif +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B + +// For object representations A and B a Python float object is allocated as a +// concrete object in a struct, with the first entry pointing to &mp_type_float. +// Constant float objects are a struct in ROM and are referenced via their pointer. + +// Use ULAB_DEFINE_FLOAT_CONST to define a constant float object. +// id is the name of the constant, num is it's floating point value. +// hex32 is computed as: hex(int.from_bytes(array.array('f', [num]), 'little')) +// hex64 is computed as: hex(int.from_bytes(array.array('d', [num]), 'little')) + +// Use ULAB_REFERENCE_FLOAT_CONST to reference a constant float object in code. + +#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \ + const mp_obj_float_t id##_obj = {{&mp_type_float}, MICROPY_FLOAT_CONST(num)} + +#define ULAB_REFERENCE_FLOAT_CONST(id) MP_ROM_PTR(&id##_obj) + // this typedef is lifted from objfloat.c, because mp_obj_float_t is not exposed typedef struct _mp_obj_float_t { mp_obj_base_t base; mp_float_t value; } mp_obj_float_t; +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + +// For object representation C a Python float object is stored directly in the +// mp_obj_t value. + +// See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST. + +#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \ + const uint32_t id = (((((uint32_t)hex32) & ~3) | 2) + 0x80800000) + +#define ULAB_REFERENCE_FLOAT_CONST(id) ((mp_obj_t)(id)) + +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + +// For object representation D (nan-boxing) a Python float object is stored +// directly in the mp_obj_t value. + +// See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST. + +#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \ + const uint64_t id = (((uint64_t)hex64) + 0x8004000000000000ULL) + +#define ULAB_REFERENCE_FLOAT_CONST(id) {id} + +#endif + #if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 11 typedef struct _mp_obj_slice_t { mp_obj_base_t base; diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index de1042cc..b941daf5 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -77,7 +77,7 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_UINT16) { if(rhs->dtype == NDARRAY_UINT16) { @@ -87,7 +87,7 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_INT16) { if(rhs->dtype == NDARRAY_INT16) { @@ -95,13 +95,13 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_FLOAT) { if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } } @@ -131,7 +131,7 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_UINT16) { if(rhs->dtype == NDARRAY_UINT16) { @@ -141,7 +141,7 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_INT16) { if(rhs->dtype == NDARRAY_INT16) { @@ -149,13 +149,13 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_FLOAT) { if(rhs->dtype == NDARRAY_FLOAT) { EQUALITY_LOOP(results, array, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); } else { - return ndarray_binary_op(op, rhs, lhs); + return ndarray_binary_op(op, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } } @@ -210,7 +210,7 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, +); } else { - return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_ADD, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_UINT16) { if(rhs->dtype == NDARRAY_UINT16) { @@ -223,7 +223,7 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, +); } else { - return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_ADD, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_INT16) { if(rhs->dtype == NDARRAY_INT16) { @@ -233,14 +233,14 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, +); } else { - return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_ADD, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_FLOAT) { if(rhs->dtype == NDARRAY_FLOAT) { results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, +); } else { - return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_ADD, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } @@ -293,7 +293,7 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, *); } else { - return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_UINT16) { if(rhs->dtype == NDARRAY_UINT16) { @@ -306,7 +306,7 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, *); } else { - return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_INT16) { if(rhs->dtype == NDARRAY_INT16) { @@ -316,14 +316,14 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, *); } else { - return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } else if(lhs->dtype == NDARRAY_FLOAT) { if(rhs->dtype == NDARRAY_FLOAT) { results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); BINARY_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, *); } else { - return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); } } diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 85cdbf78..27fe4bc3 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -25,7 +25,7 @@ //| """Numerical approximation methods""" //| -const mp_obj_float_t approx_trapz_dx = {{&mp_type_float}, MICROPY_FLOAT_CONST(1.0)}; +ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, 1.0, 0x3f800000UL, 0x3ff0000000000000ULL); #if ULAB_NUMPY_HAS_INTERP //| def interp( @@ -49,11 +49,11 @@ const mp_obj_float_t approx_trapz_dx = {{&mp_type_float}, MICROPY_FLOAT_CONST(1. STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, - { MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -153,9 +153,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp); STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&approx_trapz_dx)} }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(approx_trapz_dx)} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); diff --git a/code/numpy/compare.c b/code/numpy/compare.c index 5a820725..d47b2aa7 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -213,7 +213,7 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { // ...so flip all values in the array, if the function was called from isfinite memset(rarray, 1, results->len); } - return results; + return MP_OBJ_FROM_PTR(results); } uint8_t *xarray = (uint8_t *)x->array; @@ -259,7 +259,7 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { } while(i < x->shape[ULAB_MAX_DIMS - 4]); #endif - return results; + return MP_OBJ_FROM_PTR(results); } else { mp_raise_TypeError(translate("wrong input type")); } diff --git a/code/numpy/create.c b/code/numpy/create.c index 883fc326..98cfacc0 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -118,10 +118,10 @@ static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -170,8 +170,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_arange_obj, 1, create_arange); #if ULAB_NUMPY_HAS_ASARRAY mp_obj_t create_asarray(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -234,7 +234,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_asarray_obj, 1, create_asarray); mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, }; @@ -359,7 +359,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_concatenate_obj, 1, create_concatenate); mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, }; @@ -450,7 +450,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_diag_obj, 1, create_diag); mp_obj_t create_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, { .u_int = 0 } }, - { MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, }; @@ -461,10 +461,10 @@ mp_obj_t create_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) size_t n = args[0].u_int, m; size_t k = args[2].u_int > 0 ? (size_t)args[2].u_int : (size_t)(-args[2].u_int); uint8_t dtype = args[3].u_int; - if(args[1].u_rom_obj == mp_const_none) { + if(args[1].u_obj == mp_const_none) { m = n; } else { - m = mp_obj_get_int(args[1].u_rom_obj); + m = mp_obj_get_int(args[1].u_obj); } ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, n, m), dtype); if(dtype == NDARRAY_BOOL) { @@ -557,11 +557,11 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_full_obj, 0, create_full); mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_num, MP_ARG_INT, { .u_int = 50 } }, - { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_true } }, - { MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_TRUE } }, + { MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, }; @@ -626,7 +626,7 @@ mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a return MP_OBJ_FROM_PTR(ndarray); } else { mp_obj_t tuple[2]; - tuple[0] = ndarray; + tuple[0] = MP_OBJ_FROM_PTR(ndarray); #if ULAB_SUPPORTS_COMPLEX if(complex_out) { tuple[1] = mp_obj_new_complex(step_real, step_imag); @@ -676,15 +676,15 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace); //| ... //| -const mp_obj_float_t create_float_const_ten = {{&mp_type_float}, MICROPY_FLOAT_CONST(10.0)}; +ULAB_DEFINE_FLOAT_CONST(const_ten, 10.0, 0x41200000UL, 0x4024000000000000ULL); mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_num, MP_ARG_INT, { .u_int = 50 } }, - { MP_QSTR_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&create_float_const_ten) } }, - { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_true } }, + { MP_QSTR_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(const_ten) } }, + { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_TRUE } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, }; @@ -791,7 +791,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_zeros_obj, 0, create_zeros); #if ULAB_NUMPY_HAS_FROMBUFFER mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, { MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } }, { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } }, diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 31a8712e..a1cd27de 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -89,9 +89,9 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft); #endif STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fft) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_fft), (mp_obj_t)&fft_fft_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fft) }, + { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&fft_fft_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifft), MP_ROM_PTR(&fft_ifft_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table); diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 8a55927e..4f36bb2c 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -276,11 +276,11 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i } } if(type == FFT_SPECTROGRAM) { - return MP_OBJ_TO_PTR(out_re); + return MP_OBJ_FROM_PTR(out_re); } else { mp_obj_t tuple[2]; - tuple[0] = out_re; - tuple[1] = out_im; + tuple[0] = MP_OBJ_FROM_PTR(out_re); + tuple[1] = MP_OBJ_FROM_PTR(out_im); return mp_obj_new_tuple(2, tuple); } } diff --git a/code/numpy/filter.c b/code/numpy/filter.c index 057cd6dc..92290388 100644 --- a/code/numpy/filter.c +++ b/code/numpy/filter.c @@ -28,8 +28,8 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/code/numpy/io/io.c b/code/numpy/io/io.c index 0d029457..23b73d09 100644 --- a/code/numpy/io/io.c +++ b/code/numpy/io/io.c @@ -249,11 +249,11 @@ static void io_assign_value(const char *clipboard, uint8_t len, ndarray_obj_t *n static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_max_rows, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = -1 } }, - { MP_QSTR_usecols, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_usecols, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, { MP_QSTR_skiprows, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, }; @@ -713,12 +713,12 @@ static int8_t io_format_float(ndarray_obj_t *ndarray, mp_float_t (*func)(void *) static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_header, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_footer, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_header, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_footer, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 478503cf..e8e10c40 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -231,7 +231,7 @@ static mp_obj_t linalg_eig(mp_obj_t oin) { mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues); tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors); - return tuple; + return MP_OBJ_FROM_PTR(tuple); } MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig); @@ -285,8 +285,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv); static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , - { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -364,7 +364,7 @@ static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k if(results->ndim == 0) { return mp_obj_new_float(*rarray); } - return results; + return MP_OBJ_FROM_PTR(results); } return mp_const_none; // we should never reach this point } @@ -384,7 +384,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(linalg_norm_obj, 1, linalg_norm); static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_mode, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_QSTR(MP_QSTR_reduced) } }, }; @@ -500,33 +500,33 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ } else { mp_raise_ValueError(translate("mode must be complete, or reduced")); } - return tuple; + return MP_OBJ_FROM_PTR(tuple); } MP_DEFINE_CONST_FUN_OBJ_KW(linalg_qr_obj, 1, linalg_qr); #endif STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) }, #if ULAB_MAX_DIMS > 1 #if ULAB_LINALG_HAS_CHOLESKY - { MP_ROM_QSTR(MP_QSTR_cholesky), (mp_obj_t)&linalg_cholesky_obj }, + { MP_ROM_QSTR(MP_QSTR_cholesky), MP_ROM_PTR(&linalg_cholesky_obj) }, #endif #if ULAB_LINALG_HAS_DET - { MP_ROM_QSTR(MP_QSTR_det), (mp_obj_t)&linalg_det_obj }, + { MP_ROM_QSTR(MP_QSTR_det), MP_ROM_PTR(&linalg_det_obj) }, #endif #if ULAB_LINALG_HAS_EIG - { MP_ROM_QSTR(MP_QSTR_eig), (mp_obj_t)&linalg_eig_obj }, + { MP_ROM_QSTR(MP_QSTR_eig), MP_ROM_PTR(&linalg_eig_obj) }, #endif #if ULAB_LINALG_HAS_INV - { MP_ROM_QSTR(MP_QSTR_inv), (mp_obj_t)&linalg_inv_obj }, + { MP_ROM_QSTR(MP_QSTR_inv), MP_ROM_PTR(&linalg_inv_obj) }, #endif #if ULAB_LINALG_HAS_QR - { MP_ROM_QSTR(MP_QSTR_qr), (mp_obj_t)&linalg_qr_obj }, + { MP_ROM_QSTR(MP_QSTR_qr), MP_ROM_PTR(&linalg_qr_obj) }, #endif #endif #if ULAB_LINALG_HAS_NORM - { MP_ROM_QSTR(MP_QSTR_norm), (mp_obj_t)&linalg_norm_obj }, + { MP_ROM_QSTR(MP_QSTR_norm), MP_ROM_PTR(&linalg_norm_obj) }, #endif }; diff --git a/code/numpy/ndarray/ndarray_iter.c b/code/numpy/ndarray/ndarray_iter.c index 8704836a..423e4a05 100644 --- a/code/numpy/ndarray/ndarray_iter.c +++ b/code/numpy/ndarray/ndarray_iter.c @@ -24,9 +24,9 @@ mp_obj_t ndarray_flatiter_make_new(mp_obj_t self_in) { ndarray_flatiter_t *flatiter = m_new_obj(ndarray_flatiter_t); flatiter->base.type = &ndarray_flatiter_type; flatiter->iternext = ndarray_flatiter_next; - flatiter->ndarray = MP_OBJ_TO_PTR(self_in); + flatiter->ndarray = self_in; flatiter->cur = 0; - return flatiter; + return MP_OBJ_FROM_PTR(flatiter); } mp_obj_t ndarray_flatiter_next(mp_obj_t self_in) { diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index b33d0262..686843ed 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -227,7 +227,7 @@ static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { return mp_const_true; } } - return results; + return MP_OBJ_FROM_PTR(results); } else if(mp_obj_is_int(oin) || mp_obj_is_float(oin)) { return mp_obj_is_true(oin) ? mp_const_true : mp_const_false; } else { @@ -556,8 +556,8 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t optype) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , - { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -713,8 +713,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin); mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -906,7 +906,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(numerical_cross_obj, numerical_cross); mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } }, { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } }, }; @@ -986,8 +986,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff); mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1016,7 +1016,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } else { mp_raise_TypeError(translate("wrong axis index")); } - return results; + return MP_OBJ_FROM_PTR(results); } MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip); @@ -1056,8 +1056,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean); mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1071,7 +1071,7 @@ mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return mp_obj_new_float(MICROPY_FLOAT_C_FUN(nan)("")); } - ndarray = numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0); + ndarray = MP_OBJ_TO_PTR(numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0)); if((args[1].u_obj == mp_const_none) || (ndarray->ndim == 1)) { // at this point, the array holding the sorted values should be flat @@ -1170,9 +1170,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min); mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1319,7 +1319,7 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(translate("wrong axis index")); } - return results; + return MP_OBJ_FROM_PTR(results); } MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll); @@ -1334,8 +1334,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll); mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1351,7 +1351,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_obj, 1, numerical_sort); // method of an ndarray static mp_obj_t numerical_sort_inplace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } }, }; @@ -1372,8 +1372,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj, 1, numerical_sort_inplace mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } } , - { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } } , + { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index e1de9e6d..cc4564af 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -57,68 +57,40 @@ // math constants #if ULAB_NUMPY_HAS_E -#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C -#define ulab_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000)) -#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D -#define ulab_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))} -#else -mp_obj_float_t ulab_const_float_e_obj = {{&mp_type_float}, MP_E}; -#define ulab_const_float_e MP_ROM_PTR(&ulab_const_float_e_obj) -#endif +ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E, 0x402df854UL, 0x4005bf0a8b145769ULL); #endif #if ULAB_NUMPY_HAS_INF -#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C -#define numpy_const_float_inf MP_ROM_PTR((mp_obj_t)(0x7f800002 + 0x80800000)) -#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D -#define numpy_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))} -#else -mp_obj_float_t numpy_const_float_inf_obj = {{&mp_type_float}, (mp_float_t)INFINITY}; -#define numpy_const_float_inf MP_ROM_PTR(&numpy_const_float_inf_obj) -#endif +ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY, 0x7f800000UL, 0x7ff0000000000000ULL); #endif #if ULAB_NUMPY_HAS_NAN -#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C -#define numpy_const_float_nan MP_ROM_PTR((mp_obj_t)(0x7fc00002 + 0x80800000)) -#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D -#define numpy_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))} -#else -mp_obj_float_t numpy_const_float_nan_obj = {{&mp_type_float}, (mp_float_t)NAN}; -#define numpy_const_float_nan MP_ROM_PTR(&numpy_const_float_nan_obj) -#endif +ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN, 0x7fc00000UL, 0x7ff8000000000000ULL); #endif #if ULAB_NUMPY_HAS_PI -#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C -#define ulab_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000)) -#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D -#define ulab_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))} -#else -mp_obj_float_t ulab_const_float_pi_obj = {{&mp_type_float}, MP_PI}; -#define ulab_const_float_pi MP_ROM_PTR(&ulab_const_float_pi_obj) -#endif +ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, MP_PI, 0x40490fdbUL, 0x400921fb54442d18ULL); #endif static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_numpy) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ndarray), (mp_obj_t)&ulab_ndarray_type }, - { MP_OBJ_NEW_QSTR(MP_QSTR_array), MP_ROM_PTR(&ndarray_array_constructor_obj) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_numpy) }, + { MP_ROM_QSTR(MP_QSTR_ndarray), MP_ROM_PTR(&ulab_ndarray_type) }, + { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&ndarray_array_constructor_obj) }, #if ULAB_NUMPY_HAS_FROMBUFFER { MP_ROM_QSTR(MP_QSTR_frombuffer), MP_ROM_PTR(&create_frombuffer_obj) }, #endif // math constants #if ULAB_NUMPY_HAS_E - { MP_ROM_QSTR(MP_QSTR_e), ulab_const_float_e }, + { MP_ROM_QSTR(MP_QSTR_e), ULAB_REFERENCE_FLOAT_CONST(ulab_const_float_e) }, #endif #if ULAB_NUMPY_HAS_INF - { MP_ROM_QSTR(MP_QSTR_inf), numpy_const_float_inf }, + { MP_ROM_QSTR(MP_QSTR_inf), ULAB_REFERENCE_FLOAT_CONST(numpy_const_float_inf) }, #endif #if ULAB_NUMPY_HAS_NAN - { MP_ROM_QSTR(MP_QSTR_nan), numpy_const_float_nan }, + { MP_ROM_QSTR(MP_QSTR_nan), ULAB_REFERENCE_FLOAT_CONST(numpy_const_float_nan) }, #endif #if ULAB_NUMPY_HAS_PI - { MP_ROM_QSTR(MP_QSTR_pi), ulab_const_float_pi }, + { MP_ROM_QSTR(MP_QSTR_pi), ULAB_REFERENCE_FLOAT_CONST(ulab_const_float_pi) }, #endif // class constants, always included { MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_INT(NDARRAY_BOOL) }, @@ -138,259 +110,259 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) }, #endif #if ULAB_HAS_PRINTOPTIONS - { MP_ROM_QSTR(MP_QSTR_set_printoptions), (mp_obj_t)&ndarray_set_printoptions_obj }, - { MP_ROM_QSTR(MP_QSTR_get_printoptions), (mp_obj_t)&ndarray_get_printoptions_obj }, + { MP_ROM_QSTR(MP_QSTR_set_printoptions), MP_ROM_PTR(&ndarray_set_printoptions_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_printoptions), MP_ROM_PTR(&ndarray_get_printoptions_obj) }, #endif #if ULAB_NUMPY_HAS_NDINFO - { MP_ROM_QSTR(MP_QSTR_ndinfo), (mp_obj_t)&ndarray_info_obj }, + { MP_ROM_QSTR(MP_QSTR_ndinfo), MP_ROM_PTR(&ndarray_info_obj) }, #endif #if ULAB_NUMPY_HAS_ARANGE - { MP_ROM_QSTR(MP_QSTR_arange), (mp_obj_t)&create_arange_obj }, + { MP_ROM_QSTR(MP_QSTR_arange), MP_ROM_PTR(&create_arange_obj) }, #endif #if ULAB_NUMPY_HAS_COMPRESS - { MP_ROM_QSTR(MP_QSTR_compress), (mp_obj_t)&transform_compress_obj }, + { MP_ROM_QSTR(MP_QSTR_compress), MP_ROM_PTR(&transform_compress_obj) }, #endif #if ULAB_NUMPY_HAS_CONCATENATE - { MP_ROM_QSTR(MP_QSTR_concatenate), (mp_obj_t)&create_concatenate_obj }, + { MP_ROM_QSTR(MP_QSTR_concatenate), MP_ROM_PTR(&create_concatenate_obj) }, #endif #if ULAB_NUMPY_HAS_DELETE - { MP_ROM_QSTR(MP_QSTR_delete), (mp_obj_t)&transform_delete_obj }, + { MP_ROM_QSTR(MP_QSTR_delete), MP_ROM_PTR(&transform_delete_obj) }, #endif #if ULAB_NUMPY_HAS_DIAG #if ULAB_MAX_DIMS > 1 - { MP_ROM_QSTR(MP_QSTR_diag), (mp_obj_t)&create_diag_obj }, + { MP_ROM_QSTR(MP_QSTR_diag), MP_ROM_PTR(&create_diag_obj) }, #endif #endif #if ULAB_NUMPY_HAS_EMPTY - { MP_ROM_QSTR(MP_QSTR_empty), (mp_obj_t)&create_zeros_obj }, + { MP_ROM_QSTR(MP_QSTR_empty), MP_ROM_PTR(&create_zeros_obj) }, #endif #if ULAB_MAX_DIMS > 1 #if ULAB_NUMPY_HAS_EYE - { MP_ROM_QSTR(MP_QSTR_eye), (mp_obj_t)&create_eye_obj }, + { MP_ROM_QSTR(MP_QSTR_eye), MP_ROM_PTR(&create_eye_obj) }, #endif #endif /* ULAB_MAX_DIMS */ // functions of the approx sub-module #if ULAB_NUMPY_HAS_INTERP - { MP_OBJ_NEW_QSTR(MP_QSTR_interp), (mp_obj_t)&approx_interp_obj }, + { MP_ROM_QSTR(MP_QSTR_interp), MP_ROM_PTR(&approx_interp_obj) }, #endif #if ULAB_NUMPY_HAS_TRAPZ - { MP_OBJ_NEW_QSTR(MP_QSTR_trapz), (mp_obj_t)&approx_trapz_obj }, + { MP_ROM_QSTR(MP_QSTR_trapz), MP_ROM_PTR(&approx_trapz_obj) }, #endif // functions of the create sub-module #if ULAB_NUMPY_HAS_FULL - { MP_ROM_QSTR(MP_QSTR_full), (mp_obj_t)&create_full_obj }, + { MP_ROM_QSTR(MP_QSTR_full), MP_ROM_PTR(&create_full_obj) }, #endif #if ULAB_NUMPY_HAS_LINSPACE - { MP_ROM_QSTR(MP_QSTR_linspace), (mp_obj_t)&create_linspace_obj }, + { MP_ROM_QSTR(MP_QSTR_linspace), MP_ROM_PTR(&create_linspace_obj) }, #endif #if ULAB_NUMPY_HAS_LOGSPACE - { MP_ROM_QSTR(MP_QSTR_logspace), (mp_obj_t)&create_logspace_obj }, + { MP_ROM_QSTR(MP_QSTR_logspace), MP_ROM_PTR(&create_logspace_obj) }, #endif #if ULAB_NUMPY_HAS_ONES - { MP_ROM_QSTR(MP_QSTR_ones), (mp_obj_t)&create_ones_obj }, + { MP_ROM_QSTR(MP_QSTR_ones), MP_ROM_PTR(&create_ones_obj) }, #endif #if ULAB_NUMPY_HAS_ZEROS - { MP_ROM_QSTR(MP_QSTR_zeros), (mp_obj_t)&create_zeros_obj }, + { MP_ROM_QSTR(MP_QSTR_zeros), MP_ROM_PTR(&create_zeros_obj) }, #endif // functions of the compare sub-module #if ULAB_NUMPY_HAS_CLIP - { MP_OBJ_NEW_QSTR(MP_QSTR_clip), (mp_obj_t)&compare_clip_obj }, + { MP_ROM_QSTR(MP_QSTR_clip), MP_ROM_PTR(&compare_clip_obj) }, #endif #if ULAB_NUMPY_HAS_EQUAL - { MP_OBJ_NEW_QSTR(MP_QSTR_equal), (mp_obj_t)&compare_equal_obj }, + { MP_ROM_QSTR(MP_QSTR_equal), MP_ROM_PTR(&compare_equal_obj) }, #endif #if ULAB_NUMPY_HAS_NOTEQUAL - { MP_OBJ_NEW_QSTR(MP_QSTR_not_equal), (mp_obj_t)&compare_not_equal_obj }, + { MP_ROM_QSTR(MP_QSTR_not_equal), MP_ROM_PTR(&compare_not_equal_obj) }, #endif #if ULAB_NUMPY_HAS_ISFINITE - { MP_OBJ_NEW_QSTR(MP_QSTR_isfinite), (mp_obj_t)&compare_isfinite_obj }, + { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&compare_isfinite_obj) }, #endif #if ULAB_NUMPY_HAS_ISINF - { MP_OBJ_NEW_QSTR(MP_QSTR_isinf), (mp_obj_t)&compare_isinf_obj }, + { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&compare_isinf_obj) }, #endif #if ULAB_NUMPY_HAS_MAXIMUM - { MP_OBJ_NEW_QSTR(MP_QSTR_maximum), (mp_obj_t)&compare_maximum_obj }, + { MP_ROM_QSTR(MP_QSTR_maximum), MP_ROM_PTR(&compare_maximum_obj) }, #endif #if ULAB_NUMPY_HAS_MINIMUM - { MP_OBJ_NEW_QSTR(MP_QSTR_minimum), (mp_obj_t)&compare_minimum_obj }, + { MP_ROM_QSTR(MP_QSTR_minimum), MP_ROM_PTR(&compare_minimum_obj) }, #endif #if ULAB_NUMPY_HAS_WHERE - { MP_OBJ_NEW_QSTR(MP_QSTR_where), (mp_obj_t)&compare_where_obj }, + { MP_ROM_QSTR(MP_QSTR_where), MP_ROM_PTR(&compare_where_obj) }, #endif // functions of the filter sub-module #if ULAB_NUMPY_HAS_CONVOLVE - { MP_OBJ_NEW_QSTR(MP_QSTR_convolve), (mp_obj_t)&filter_convolve_obj }, + { MP_ROM_QSTR(MP_QSTR_convolve), MP_ROM_PTR(&filter_convolve_obj) }, #endif // functions of the numerical sub-module #if ULAB_NUMPY_HAS_ALL - { MP_OBJ_NEW_QSTR(MP_QSTR_all), (mp_obj_t)&numerical_all_obj }, + { MP_ROM_QSTR(MP_QSTR_all), MP_ROM_PTR(&numerical_all_obj) }, #endif #if ULAB_NUMPY_HAS_ANY - { MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&numerical_any_obj }, + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&numerical_any_obj) }, #endif #if ULAB_NUMPY_HAS_ARGMINMAX - { MP_OBJ_NEW_QSTR(MP_QSTR_argmax), (mp_obj_t)&numerical_argmax_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_argmin), (mp_obj_t)&numerical_argmin_obj }, + { MP_ROM_QSTR(MP_QSTR_argmax), MP_ROM_PTR(&numerical_argmax_obj) }, + { MP_ROM_QSTR(MP_QSTR_argmin), MP_ROM_PTR(&numerical_argmin_obj) }, #endif #if ULAB_NUMPY_HAS_ARGSORT - { MP_OBJ_NEW_QSTR(MP_QSTR_argsort), (mp_obj_t)&numerical_argsort_obj }, + { MP_ROM_QSTR(MP_QSTR_argsort), MP_ROM_PTR(&numerical_argsort_obj) }, #endif #if ULAB_NUMPY_HAS_ASARRAY - { MP_OBJ_NEW_QSTR(MP_QSTR_asarray), (mp_obj_t)&create_asarray_obj }, + { MP_ROM_QSTR(MP_QSTR_asarray), MP_ROM_PTR(&create_asarray_obj) }, #endif #if ULAB_NUMPY_HAS_CROSS - { MP_OBJ_NEW_QSTR(MP_QSTR_cross), (mp_obj_t)&numerical_cross_obj }, + { MP_ROM_QSTR(MP_QSTR_cross), MP_ROM_PTR(&numerical_cross_obj) }, #endif #if ULAB_NUMPY_HAS_DIFF - { MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj }, + { MP_ROM_QSTR(MP_QSTR_diff), MP_ROM_PTR(&numerical_diff_obj) }, #endif #if ULAB_NUMPY_HAS_DOT #if ULAB_MAX_DIMS > 1 - { MP_OBJ_NEW_QSTR(MP_QSTR_dot), (mp_obj_t)&transform_dot_obj }, + { MP_ROM_QSTR(MP_QSTR_dot), MP_ROM_PTR(&transform_dot_obj) }, #endif #endif #if ULAB_NUMPY_HAS_TRACE #if ULAB_MAX_DIMS > 1 - { MP_ROM_QSTR(MP_QSTR_trace), (mp_obj_t)&stats_trace_obj }, + { MP_ROM_QSTR(MP_QSTR_trace), MP_ROM_PTR(&stats_trace_obj) }, #endif #endif #if ULAB_NUMPY_HAS_FLIP - { MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj }, + { MP_ROM_QSTR(MP_QSTR_flip), MP_ROM_PTR(&numerical_flip_obj) }, #endif #if ULAB_NUMPY_HAS_LOAD - { MP_OBJ_NEW_QSTR(MP_QSTR_load), (mp_obj_t)&io_load_obj }, + { MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&io_load_obj) }, #endif #if ULAB_NUMPY_HAS_LOADTXT - { MP_OBJ_NEW_QSTR(MP_QSTR_loadtxt), (mp_obj_t)&io_loadtxt_obj }, + { MP_ROM_QSTR(MP_QSTR_loadtxt), MP_ROM_PTR(&io_loadtxt_obj) }, #endif #if ULAB_NUMPY_HAS_MINMAX - { MP_OBJ_NEW_QSTR(MP_QSTR_max), (mp_obj_t)&numerical_max_obj }, + { MP_ROM_QSTR(MP_QSTR_max), MP_ROM_PTR(&numerical_max_obj) }, #endif #if ULAB_NUMPY_HAS_MEAN - { MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&numerical_mean_obj }, + { MP_ROM_QSTR(MP_QSTR_mean), MP_ROM_PTR(&numerical_mean_obj) }, #endif #if ULAB_NUMPY_HAS_MEDIAN - { MP_OBJ_NEW_QSTR(MP_QSTR_median), (mp_obj_t)&numerical_median_obj }, + { MP_ROM_QSTR(MP_QSTR_median), MP_ROM_PTR(&numerical_median_obj) }, #endif #if ULAB_NUMPY_HAS_MINMAX - { MP_OBJ_NEW_QSTR(MP_QSTR_min), (mp_obj_t)&numerical_min_obj }, + { MP_ROM_QSTR(MP_QSTR_min), MP_ROM_PTR(&numerical_min_obj) }, #endif #if ULAB_NUMPY_HAS_ROLL - { MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj }, + { MP_ROM_QSTR(MP_QSTR_roll), MP_ROM_PTR(&numerical_roll_obj) }, #endif #if ULAB_NUMPY_HAS_SAVE - { MP_OBJ_NEW_QSTR(MP_QSTR_save), (mp_obj_t)&io_save_obj }, + { MP_ROM_QSTR(MP_QSTR_save), MP_ROM_PTR(&io_save_obj) }, #endif #if ULAB_NUMPY_HAS_SAVETXT - { MP_OBJ_NEW_QSTR(MP_QSTR_savetxt), (mp_obj_t)&io_savetxt_obj }, + { MP_ROM_QSTR(MP_QSTR_savetxt), MP_ROM_PTR(&io_savetxt_obj) }, #endif #if ULAB_NUMPY_HAS_SIZE - { MP_OBJ_NEW_QSTR(MP_QSTR_size), (mp_obj_t)&transform_size_obj }, + { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&transform_size_obj) }, #endif #if ULAB_NUMPY_HAS_SORT - { MP_OBJ_NEW_QSTR(MP_QSTR_sort), (mp_obj_t)&numerical_sort_obj }, + { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_obj) }, #endif #if ULAB_NUMPY_HAS_STD - { MP_OBJ_NEW_QSTR(MP_QSTR_std), (mp_obj_t)&numerical_std_obj }, + { MP_ROM_QSTR(MP_QSTR_std), MP_ROM_PTR(&numerical_std_obj) }, #endif #if ULAB_NUMPY_HAS_SUM - { MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&numerical_sum_obj }, + { MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&numerical_sum_obj) }, #endif // functions of the poly sub-module #if ULAB_NUMPY_HAS_POLYFIT - { MP_OBJ_NEW_QSTR(MP_QSTR_polyfit), (mp_obj_t)&poly_polyfit_obj }, + { MP_ROM_QSTR(MP_QSTR_polyfit), MP_ROM_PTR(&poly_polyfit_obj) }, #endif #if ULAB_NUMPY_HAS_POLYVAL - { MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj }, + { MP_ROM_QSTR(MP_QSTR_polyval), MP_ROM_PTR(&poly_polyval_obj) }, #endif // functions of the vector sub-module #if ULAB_NUMPY_HAS_ACOS - { MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vector_acos_obj }, + { MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&vector_acos_obj) }, #endif #if ULAB_NUMPY_HAS_ACOSH - { MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vector_acosh_obj }, + { MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&vector_acosh_obj) }, #endif #if ULAB_NUMPY_HAS_ARCTAN2 - { MP_OBJ_NEW_QSTR(MP_QSTR_arctan2), (mp_obj_t)&vector_arctan2_obj }, + { MP_ROM_QSTR(MP_QSTR_arctan2), MP_ROM_PTR(&vector_arctan2_obj) }, #endif #if ULAB_NUMPY_HAS_AROUND - { MP_OBJ_NEW_QSTR(MP_QSTR_around), (mp_obj_t)&vector_around_obj }, + { MP_ROM_QSTR(MP_QSTR_around), MP_ROM_PTR(&vector_around_obj) }, #endif #if ULAB_NUMPY_HAS_ASIN - { MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vector_asin_obj }, + { MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&vector_asin_obj) }, #endif #if ULAB_NUMPY_HAS_ASINH - { MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vector_asinh_obj }, + { MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&vector_asinh_obj) }, #endif #if ULAB_NUMPY_HAS_ATAN - { MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vector_atan_obj }, + { MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&vector_atan_obj) }, #endif #if ULAB_NUMPY_HAS_ATANH - { MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vector_atanh_obj }, + { MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&vector_atanh_obj) }, #endif #if ULAB_NUMPY_HAS_CEIL - { MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vector_ceil_obj }, + { MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&vector_ceil_obj) }, #endif #if ULAB_NUMPY_HAS_COS - { MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vector_cos_obj }, + { MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&vector_cos_obj) }, #endif #if ULAB_NUMPY_HAS_COSH - { MP_OBJ_NEW_QSTR(MP_QSTR_cosh), (mp_obj_t)&vector_cosh_obj }, + { MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&vector_cosh_obj) }, #endif #if ULAB_NUMPY_HAS_DEGREES - { MP_OBJ_NEW_QSTR(MP_QSTR_degrees), (mp_obj_t)&vector_degrees_obj }, + { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&vector_degrees_obj) }, #endif #if ULAB_NUMPY_HAS_EXP - { MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vector_exp_obj }, + { MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&vector_exp_obj) }, #endif #if ULAB_NUMPY_HAS_EXPM1 - { MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vector_expm1_obj }, + { MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&vector_expm1_obj) }, #endif #if ULAB_NUMPY_HAS_FLOOR - { MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vector_floor_obj }, + { MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&vector_floor_obj) }, #endif #if ULAB_NUMPY_HAS_LOG - { MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vector_log_obj }, + { MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&vector_log_obj) }, #endif #if ULAB_NUMPY_HAS_LOG10 - { MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vector_log10_obj }, + { MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&vector_log10_obj) }, #endif #if ULAB_NUMPY_HAS_LOG2 - { MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vector_log2_obj }, + { MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&vector_log2_obj) }, #endif #if ULAB_NUMPY_HAS_RADIANS - { MP_OBJ_NEW_QSTR(MP_QSTR_radians), (mp_obj_t)&vector_radians_obj }, + { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&vector_radians_obj) }, #endif #if ULAB_NUMPY_HAS_SIN - { MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vector_sin_obj }, + { MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&vector_sin_obj) }, #endif #if ULAB_NUMPY_HAS_SINH - { MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vector_sinh_obj }, + { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&vector_sinh_obj) }, #endif #if ULAB_NUMPY_HAS_SQRT - { MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vector_sqrt_obj }, + { MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&vector_sqrt_obj) }, #endif #if ULAB_NUMPY_HAS_TAN - { MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vector_tan_obj }, + { MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&vector_tan_obj) }, #endif #if ULAB_NUMPY_HAS_TANH - { MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vector_tanh_obj }, + { MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&vector_tanh_obj) }, #endif #if ULAB_NUMPY_HAS_VECTORIZE - { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vector_vectorize_obj }, + { MP_ROM_QSTR(MP_QSTR_vectorize), MP_ROM_PTR(&vector_vectorize_obj) }, #endif #if ULAB_SUPPORTS_COMPLEX #if ULAB_NUMPY_HAS_REAL - { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, + { MP_ROM_QSTR(MP_QSTR_real), MP_ROM_PTR(&carray_real_obj) }, #endif #if ULAB_NUMPY_HAS_IMAG - { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + { MP_ROM_QSTR(MP_QSTR_imag), MP_ROM_PTR(&carray_imag_obj) }, #endif #if ULAB_NUMPY_HAS_CONJUGATE - { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj }, + { MP_ROM_QSTR(MP_QSTR_conjugate), MP_ROM_PTR(&carray_conjugate_obj) }, #endif #if ULAB_NUMPY_HAS_SORT_COMPLEX - { MP_ROM_QSTR(MP_QSTR_sort_complex), (mp_obj_t)&carray_sort_complex_obj }, + { MP_ROM_QSTR(MP_QSTR_sort_complex), MP_ROM_PTR(&carray_sort_complex_obj) }, #endif #endif }; diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 842c16b6..c36d162a 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -27,9 +27,9 @@ #if ULAB_NUMPY_HAS_COMPRESS static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -157,7 +157,7 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m m_del(int32_t, strides, ULAB_MAX_DIMS); m_del(int32_t, rstrides, ULAB_MAX_DIMS); - return result; + return MP_OBJ_FROM_PTR(result); } MP_DEFINE_CONST_FUN_OBJ_KW(transform_compress_obj, 2, transform_compress); @@ -166,9 +166,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(transform_compress_obj, 2, transform_compress); #if ULAB_NUMPY_HAS_DELETE static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -416,8 +416,8 @@ MP_DEFINE_CONST_FUN_OBJ_2(transform_dot_obj, transform_dot); #if ULAB_NUMPY_HAS_SIZE static mp_obj_t transform_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 97ab66d2..693d6628 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -159,7 +159,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh); mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } } }; @@ -583,7 +583,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh); #if ULAB_SUPPORTS_COMPLEX mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, }; @@ -808,8 +808,8 @@ const mp_obj_type_t vector_function_type = { static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, - { MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} } + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} } }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); diff --git a/code/scipy/linalg/linalg.c b/code/scipy/linalg/linalg.c index a7266a4a..87aef194 100644 --- a/code/scipy/linalg/linalg.c +++ b/code/scipy/linalg/linalg.c @@ -50,9 +50,9 @@ static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map size_t i, j; static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , - { MP_QSTR_lower, MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_lower, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_TRUE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -258,13 +258,13 @@ MP_DEFINE_CONST_FUN_OBJ_2(linalg_cho_solve_obj, cho_solve); #endif static const mp_rom_map_elem_t ulab_scipy_linalg_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) }, #if ULAB_MAX_DIMS > 1 #if ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR - { MP_ROM_QSTR(MP_QSTR_solve_triangular), (mp_obj_t)&linalg_solve_triangular_obj }, + { MP_ROM_QSTR(MP_QSTR_solve_triangular), MP_ROM_PTR(&linalg_solve_triangular_obj) }, #endif #if ULAB_SCIPY_LINALG_HAS_CHO_SOLVE - { MP_ROM_QSTR(MP_QSTR_cho_solve), (mp_obj_t)&linalg_cho_solve_obj }, + { MP_ROM_QSTR(MP_QSTR_cho_solve), MP_ROM_PTR(&linalg_cho_solve_obj) }, #endif #endif }; diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index c2ed6fff..0a69bf3a 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -22,8 +22,8 @@ #include "../../ulab_tools.h" #include "optimize.h" -const mp_obj_float_t xtolerance = {{&mp_type_float}, MICROPY_FLOAT_CONST(2.4e-7)}; -const mp_obj_float_t rtolerance = {{&mp_type_float}, MICROPY_FLOAT_CONST(0.0)}; +ULAB_DEFINE_FLOAT_CONST(xtolerance, 2.4e-7, 0x3480d959UL, 0x3e901b2b29a4692bULL); +ULAB_DEFINE_FLOAT_CONST(rtolerance, 0.0, 0UL, 0ULL); static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) { // Helper function for calculating the value of f(x, a, b, c, ...), @@ -58,10 +58,10 @@ static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // Simple bisection routine static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_xtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_xtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} }, { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, }; @@ -128,10 +128,10 @@ MP_DEFINE_CONST_FUN_OBJ_KW(optimize_bisect_obj, 3, optimize_bisect); STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // downhill simplex method in 1D static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, - { MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} }, + { MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} }, { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 200} }, }; @@ -262,13 +262,13 @@ mp_obj_t optimize_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k // Levenberg-Marquardt non-linear fit // The implementation follows the introductory discussion in Mark Tanstrum's paper, https://arxiv.org/abs/1201.5885 static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_p0, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_p0, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, { MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, - { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -353,10 +353,10 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_ // is not accepted as an argument. The function whose root we want to solve for // must depend on a single variable without parameters, i.e., f(x) static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&xtolerance) } }, - { MP_QSTR_rtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&rtolerance) } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance) } }, + { MP_QSTR_rtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(rtolerance) } }, { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 50 } }, }; @@ -391,18 +391,18 @@ MP_DEFINE_CONST_FUN_OBJ_KW(optimize_newton_obj, 2, optimize_newton); #endif static const mp_rom_map_elem_t ulab_scipy_optimize_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_optimize) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_optimize) }, #if ULAB_SCIPY_OPTIMIZE_HAS_BISECT - { MP_OBJ_NEW_QSTR(MP_QSTR_bisect), (mp_obj_t)&optimize_bisect_obj }, + { MP_ROM_QSTR(MP_QSTR_bisect), MP_ROM_PTR(&optimize_bisect_obj) }, #endif #if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT - { MP_OBJ_NEW_QSTR(MP_QSTR_curve_fit), (mp_obj_t)&optimize_curve_fit_obj }, + { MP_ROM_QSTR(MP_QSTR_curve_fit), MP_ROM_PTR(&optimize_curve_fit_obj) }, #endif #if ULAB_SCIPY_OPTIMIZE_HAS_FMIN - { MP_OBJ_NEW_QSTR(MP_QSTR_fmin), (mp_obj_t)&optimize_fmin_obj }, + { MP_ROM_QSTR(MP_QSTR_fmin), MP_ROM_PTR(&optimize_fmin_obj) }, #endif #if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON - { MP_OBJ_NEW_QSTR(MP_QSTR_newton), (mp_obj_t)&optimize_newton_obj }, + { MP_ROM_QSTR(MP_QSTR_newton), MP_ROM_PTR(&optimize_newton_obj) }, #endif }; diff --git a/code/scipy/scipy.c b/code/scipy/scipy.c index a7683e2d..de2073ec 100644 --- a/code/scipy/scipy.c +++ b/code/scipy/scipy.c @@ -27,7 +27,7 @@ //| static const mp_rom_map_elem_t ulab_scipy_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_scipy) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_scipy) }, #if ULAB_SCIPY_HAS_LINALG_MODULE { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) }, #endif diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 60dbad07..84d43bec 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -34,9 +34,9 @@ static void signal_sosfilt_array(mp_float_t *x, const mp_float_t *coeffs, mp_flo mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_sos, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, - { MP_QSTR_zi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_sos, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_zi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -113,7 +113,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); tuple->items[0] = MP_OBJ_FROM_PTR(y); tuple->items[1] = MP_OBJ_FROM_PTR(zf); - return tuple; + return MP_OBJ_FROM_PTR(tuple); } } @@ -121,9 +121,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(signal_sosfilt_obj, 2, signal_sosfilt); #endif /* ULAB_SCIPY_SIGNAL_HAS_SOSFILT */ static const mp_rom_map_elem_t ulab_scipy_signal_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_signal) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_signal) }, #if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1 - { MP_OBJ_NEW_QSTR(MP_QSTR_sosfilt), (mp_obj_t)&signal_sosfilt_obj }, + { MP_ROM_QSTR(MP_QSTR_sosfilt), MP_ROM_PTR(&signal_sosfilt_obj) }, #endif }; diff --git a/code/scipy/special/special.c b/code/scipy/special/special.c index 79d9b77f..4d9b8cb8 100644 --- a/code/scipy/special/special.c +++ b/code/scipy/special/special.c @@ -19,18 +19,18 @@ #include "../../numpy/vector.h" static const mp_rom_map_elem_t ulab_scipy_special_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_special) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_special) }, #if ULAB_SCIPY_SPECIAL_HAS_ERF - { MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vector_erf_obj }, + { MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&vector_erf_obj) }, #endif #if ULAB_SCIPY_SPECIAL_HAS_ERFC - { MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vector_erfc_obj }, + { MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&vector_erfc_obj) }, #endif #if ULAB_SCIPY_SPECIAL_HAS_GAMMA - { MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vector_gamma_obj }, + { MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&vector_gamma_obj) }, #endif #if ULAB_SCIPY_SPECIAL_HAS_GAMMALN - { MP_OBJ_NEW_QSTR(MP_QSTR_gammaln), (mp_obj_t)&vector_lgamma_obj }, + { MP_ROM_QSTR(MP_QSTR_gammaln), MP_ROM_PTR(&vector_lgamma_obj) }, #endif }; diff --git a/code/ulab.c b/code/ulab.c index 9785a8dd..89e19083 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.7 +#define ULAB_VERSION 5.0.8 #define xstr(s) str(s) #define str(s) #s @@ -149,17 +149,17 @@ const mp_obj_type_t ndarray_flatiter_type = { }; #endif -STATIC const mp_map_elem_t ulab_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ulab) }, +STATIC const mp_rom_map_elem_t ulab_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) }, { MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) }, #ifdef ULAB_HASH { MP_ROM_QSTR(MP_QSTR___sha__), MP_ROM_PTR(&ulab_sha_obj) }, #endif #if ULAB_HAS_DTYPE_OBJECT - { MP_OBJ_NEW_QSTR(MP_QSTR_dtype), (mp_obj_t)&ulab_dtype_type }, + { MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ulab_dtype_type) }, #else #if NDARRAY_HAS_DTYPE - { MP_OBJ_NEW_QSTR(MP_QSTR_dtype), (mp_obj_t)&ndarray_dtype_obj }, + { MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) }, #endif /* NDARRAY_HAS_DTYPE */ #endif /* ULAB_HAS_DTYPE_OBJECT */ { MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) }, diff --git a/code/utils/utils.c b/code/utils/utils.c index 31b6893c..dd8f7a66 100644 --- a/code/utils/utils.c +++ b/code/utils/utils.c @@ -30,11 +30,11 @@ enum UTILS_BUFFER_TYPE { #if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t buffer_type) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } } , + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } } , { MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } }, { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } }, - { MP_QSTR_out, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, - { MP_QSTR_byteswap, MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + { MP_QSTR_out, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_byteswap, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -225,21 +225,21 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 2, utils_spectrogr static const mp_rom_map_elem_t ulab_utils_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_utils) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utils) }, #if ULAB_UTILS_HAS_FROM_INT16_BUFFER - { MP_OBJ_NEW_QSTR(MP_QSTR_from_int16_buffer), (mp_obj_t)&utils_from_int16_buffer_obj }, + { MP_ROM_QSTR(MP_QSTR_from_int16_buffer), MP_ROM_PTR(&utils_from_int16_buffer_obj) }, #endif #if ULAB_UTILS_HAS_FROM_UINT16_BUFFER - { MP_OBJ_NEW_QSTR(MP_QSTR_from_uint16_buffer), (mp_obj_t)&utils_from_uint16_buffer_obj }, + { MP_ROM_QSTR(MP_QSTR_from_uint16_buffer), MP_ROM_PTR(&utils_from_uint16_buffer_obj) }, #endif #if ULAB_UTILS_HAS_FROM_INT32_BUFFER - { MP_OBJ_NEW_QSTR(MP_QSTR_from_int32_buffer), (mp_obj_t)&utils_from_int32_buffer_obj }, + { MP_ROM_QSTR(MP_QSTR_from_int32_buffer), MP_ROM_PTR(&utils_from_int32_buffer_obj) }, #endif #if ULAB_UTILS_HAS_FROM_UINT32_BUFFER - { MP_OBJ_NEW_QSTR(MP_QSTR_from_uint32_buffer), (mp_obj_t)&utils_from_uint32_buffer_obj }, + { MP_ROM_QSTR(MP_QSTR_from_uint32_buffer), MP_ROM_PTR(&utils_from_uint32_buffer_obj) }, #endif #if ULAB_UTILS_HAS_SPECTROGRAM - { MP_OBJ_NEW_QSTR(MP_QSTR_spectrogram), (mp_obj_t)&utils_spectrogram_obj }, + { MP_ROM_QSTR(MP_QSTR_spectrogram), MP_ROM_PTR(&utils_spectrogram_obj) }, #endif }; From 308627c9aa862698725b2ba22fa2e8b2300e6803 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jul 2022 13:17:49 -0500 Subject: [PATCH 020/113] Fix several build errors in CircuitPython (#533) * Properly register submodules of ulab This is related to * https://github.com/adafruit/circuitpython/issues/6066 in which, after the merge of 1.18 into CircuitPython, we lost the ability to import submodules of built-in modules. While reconstructing the changes we had made locally to enable this, I discovered that there was an easier way: simply register the dotted module names via MP_REGISTER_MODULE. * Fix finding processor count when no `python` executable is installed debian likes to install only `python3`, and not `python` (which was, for many decades, python2). This was previously done for `build.sh` but not for `build-cp.sh`. * Only use this submodule feature in CircuitPython .. as it does not work properly in MicroPython. Also, modules to be const. This saves a small amount of RAM * Fix -Werror=undef diagnostic Most CircuitPython ports build with -Werror=undef, so that use of an undefined preprocessor flag is an error. Also, CircuitPython's micropython version is old enough that MICROPY_VERSION is not (ever) defined. Defensively check for this macro being defined, and use the older style of MP_REGISTER_MODULE when it is not. * Fix -Werror=discarded-qualifiers diagnostics Most CircuitPython ports build with -Werror=discarded-qualifiers. This detected a problem where string constants were passed to functions with non-constant parameter types. * bump version number * Use MicroPython-compatible registration of submodules * straggler * Remove spurious casts these were build errors for micropython * Run tests for both nanbox and regular variant during CI --- build-cp.sh | 2 +- build.sh | 5 +++-- code/numpy/fft/fft.c | 9 ++++++++- code/numpy/fft/fft.h | 2 +- code/numpy/io/io.c | 6 +++--- code/numpy/linalg/linalg.c | 10 ++++++++-- code/numpy/linalg/linalg.h | 2 +- code/numpy/numpy.c | 10 +++++++++- code/numpy/numpy.h | 2 +- code/scipy/linalg/linalg.c | 10 ++++++++-- code/scipy/linalg/linalg.h | 2 +- code/scipy/optimize/optimize.c | 9 ++++++++- code/scipy/optimize/optimize.h | 2 +- code/scipy/scipy.c | 9 ++++++++- code/scipy/scipy.h | 2 +- code/scipy/signal/signal.c | 9 ++++++++- code/scipy/signal/signal.h | 2 +- code/scipy/special/special.c | 9 ++++++++- code/scipy/special/special.h | 2 +- code/ulab.c | 4 ++-- code/user/user.c | 9 ++++++++- code/user/user.h | 2 +- code/utils/utils.c | 9 ++++++++- code/utils/utils.h | 2 +- 24 files changed, 100 insertions(+), 30 deletions(-) diff --git a/build-cp.sh b/build-cp.sh index 6193ad0d..3705591c 100755 --- a/build-cp.sh +++ b/build-cp.sh @@ -35,7 +35,7 @@ readlinkf_posix() { done return 1 } -NPROC=$(python -c 'import multiprocessing; print(multiprocessing.cpu_count())') +NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())') HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )" [ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-submodules && git submodule update --init lib/uzlib tools) rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab diff --git a/build.sh b/build.sh index 7f59ded4..aa58e359 100755 --- a/build.sh +++ b/build.sh @@ -48,11 +48,12 @@ make -C micropython/mpy-cross -j${NPROC} make -C micropython/ports/unix -j${NPROC} axtls make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-$dims PROG=micropython-$dims +bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" + # The unix nanbox variant builds as a 32-bit executable and requires gcc-multilib. # macOS doesn't support i386 builds so only build on linux. if [ $PLATFORM = linux ]; then make -C micropython/ports/unix -j${NPROC} VARIANT=nanbox USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims + bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" fi -bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" - diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index a1cd27de..ad4996e3 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -96,7 +96,14 @@ STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = { STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table); -mp_obj_module_t ulab_fft_module = { +const mp_obj_module_t ulab_fft_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module); +#endif +#endif diff --git a/code/numpy/fft/fft.h b/code/numpy/fft/fft.h index 7a166984..1e50a8da 100644 --- a/code/numpy/fft/fft.h +++ b/code/numpy/fft/fft.h @@ -17,7 +17,7 @@ #include "../../ndarray.h" #include "fft_tools.h" -extern mp_obj_module_t ulab_fft_module; +extern const mp_obj_module_t ulab_fft_module; #if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE MP_DECLARE_CONST_FUN_OBJ_3(fft_fft_obj); diff --git a/code/numpy/io/io.c b/code/numpy/io/io.c index 23b73d09..b9e5d367 100644 --- a/code/numpy/io/io.c +++ b/code/numpy/io/io.c @@ -32,7 +32,7 @@ #define ULAB_IO_BIG_ENDIAN 2 #if ULAB_NUMPY_HAS_LOAD -static void io_read_(mp_obj_t stream, const mp_stream_p_t *stream_p, char *buffer, char *string, uint16_t len, int *error) { +static void io_read_(mp_obj_t stream, const mp_stream_p_t *stream_p, char *buffer, const char *string, uint16_t len, int *error) { size_t read = stream_p->read(stream, buffer, len, error); bool fail = false; if(read == len) { @@ -668,7 +668,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(io_save_obj, io_save); #endif /* ULAB_NUMPY_HAS_SAVE */ #if ULAB_NUMPY_HAS_SAVETXT -static int8_t io_format_float(ndarray_obj_t *ndarray, mp_float_t (*func)(void *), uint8_t *array, char *buffer, char *delimiter) { +static int8_t io_format_float(ndarray_obj_t *ndarray, mp_float_t (*func)(void *), uint8_t *array, char *buffer, const char *delimiter) { // own implementation of float formatting for platforms that don't have sprintf int8_t offset = 0; @@ -814,4 +814,4 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } MP_DEFINE_CONST_FUN_OBJ_KW(io_savetxt_obj, 2, io_savetxt); -#endif /* ULAB_NUMPY_HAS_SAVETXT */ \ No newline at end of file +#endif /* ULAB_NUMPY_HAS_SAVETXT */ diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index e8e10c40..0243a5d9 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -532,9 +532,15 @@ STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = { STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table); -mp_obj_module_t ulab_linalg_module = { +const mp_obj_module_t ulab_linalg_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals, }; - +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module); +#endif +#endif #endif diff --git a/code/numpy/linalg/linalg.h b/code/numpy/linalg/linalg.h index cf6b0adc..35fc4035 100644 --- a/code/numpy/linalg/linalg.h +++ b/code/numpy/linalg/linalg.h @@ -16,7 +16,7 @@ #include "../../ndarray.h" #include "linalg_tools.h" -extern mp_obj_module_t ulab_linalg_module; +extern const mp_obj_module_t ulab_linalg_module; MP_DECLARE_CONST_FUN_OBJ_1(linalg_cholesky_obj); MP_DECLARE_CONST_FUN_OBJ_1(linalg_det_obj); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index cc4564af..453f7e46 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -369,7 +369,15 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table); -mp_obj_module_t ulab_numpy_module = { +const mp_obj_module_t ulab_numpy_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_globals, }; + +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module); +#endif +#endif diff --git a/code/numpy/numpy.h b/code/numpy/numpy.h index cccebde1..f1348f38 100644 --- a/code/numpy/numpy.h +++ b/code/numpy/numpy.h @@ -16,6 +16,6 @@ #include "../ulab.h" #include "../ndarray.h" -extern mp_obj_module_t ulab_numpy_module; +extern const mp_obj_module_t ulab_numpy_module; #endif /* _NUMPY_ */ diff --git a/code/scipy/linalg/linalg.c b/code/scipy/linalg/linalg.c index 87aef194..52c04afe 100644 --- a/code/scipy/linalg/linalg.c +++ b/code/scipy/linalg/linalg.c @@ -271,9 +271,15 @@ static const mp_rom_map_elem_t ulab_scipy_linalg_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_linalg_globals, ulab_scipy_linalg_globals_table); -mp_obj_module_t ulab_scipy_linalg_module = { +const mp_obj_module_t ulab_scipy_linalg_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_linalg_globals, }; - +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module); +#endif +#endif #endif diff --git a/code/scipy/linalg/linalg.h b/code/scipy/linalg/linalg.h index 7396affd..628051f4 100644 --- a/code/scipy/linalg/linalg.h +++ b/code/scipy/linalg/linalg.h @@ -13,7 +13,7 @@ #ifndef _SCIPY_LINALG_ #define _SCIPY_LINALG_ -extern mp_obj_module_t ulab_scipy_linalg_module; +extern const mp_obj_module_t ulab_scipy_linalg_module; MP_DECLARE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj); MP_DECLARE_CONST_FUN_OBJ_2(linalg_cho_solve_obj); diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index 0a69bf3a..5e90cc7c 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -408,7 +408,14 @@ static const mp_rom_map_elem_t ulab_scipy_optimize_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_optimize_globals, ulab_scipy_optimize_globals_table); -mp_obj_module_t ulab_scipy_optimize_module = { +const mp_obj_module_t ulab_scipy_optimize_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_optimize_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module); +#endif +#endif diff --git a/code/scipy/optimize/optimize.h b/code/scipy/optimize/optimize.h index 5d956df4..174b3863 100644 --- a/code/scipy/optimize/optimize.h +++ b/code/scipy/optimize/optimize.h @@ -31,7 +31,7 @@ #define OPTIMIZE_GAMMA MICROPY_FLOAT_CONST(0.5) #define OPTIMIZE_DELTA MICROPY_FLOAT_CONST(0.5) -extern mp_obj_module_t ulab_scipy_optimize_module; +extern const mp_obj_module_t ulab_scipy_optimize_module; MP_DECLARE_CONST_FUN_OBJ_KW(optimize_bisect_obj); MP_DECLARE_CONST_FUN_OBJ_KW(optimize_curve_fit_obj); diff --git a/code/scipy/scipy.c b/code/scipy/scipy.c index de2073ec..c9f51df5 100644 --- a/code/scipy/scipy.c +++ b/code/scipy/scipy.c @@ -44,8 +44,15 @@ static const mp_rom_map_elem_t ulab_scipy_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_globals, ulab_scipy_globals_table); -mp_obj_module_t ulab_scipy_module = { +const mp_obj_module_t ulab_scipy_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module); +#endif +#endif #endif /* ULAB_HAS_SCIPY */ diff --git a/code/scipy/scipy.h b/code/scipy/scipy.h index ff287a5a..ec8c8042 100644 --- a/code/scipy/scipy.h +++ b/code/scipy/scipy.h @@ -16,6 +16,6 @@ #include "../ulab.h" #include "../ndarray.h" -extern mp_obj_module_t ulab_scipy_module; +extern const mp_obj_module_t ulab_scipy_module; #endif /* _SCIPY_ */ diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 84d43bec..6afa05f6 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -129,7 +129,14 @@ static const mp_rom_map_elem_t ulab_scipy_signal_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_signal_globals, ulab_scipy_signal_globals_table); -mp_obj_module_t ulab_scipy_signal_module = { +const mp_obj_module_t ulab_scipy_signal_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_signal_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module); +#endif +#endif diff --git a/code/scipy/signal/signal.h b/code/scipy/signal/signal.h index 3c2343a8..033f6e4c 100644 --- a/code/scipy/signal/signal.h +++ b/code/scipy/signal/signal.h @@ -16,7 +16,7 @@ #include "../../ulab.h" #include "../../ndarray.h" -extern mp_obj_module_t ulab_scipy_signal_module; +extern const mp_obj_module_t ulab_scipy_signal_module; MP_DECLARE_CONST_FUN_OBJ_KW(signal_sosfilt_obj); diff --git a/code/scipy/special/special.c b/code/scipy/special/special.c index 4d9b8cb8..9d5ca629 100644 --- a/code/scipy/special/special.c +++ b/code/scipy/special/special.c @@ -36,7 +36,14 @@ static const mp_rom_map_elem_t ulab_scipy_special_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_special_globals, ulab_scipy_special_globals_table); -mp_obj_module_t ulab_scipy_special_module = { +const mp_obj_module_t ulab_scipy_special_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_special_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module); +#endif +#endif diff --git a/code/scipy/special/special.h b/code/scipy/special/special.h index ca0bac58..bb34e27e 100644 --- a/code/scipy/special/special.h +++ b/code/scipy/special/special.h @@ -16,6 +16,6 @@ #include "../../ulab.h" #include "../../ndarray.h" -extern mp_obj_module_t ulab_scipy_special_module; +extern const mp_obj_module_t ulab_scipy_special_module; #endif /* _SCIPY_SPECIAL_ */ diff --git a/code/ulab.c b/code/ulab.c index 89e19083..c8222814 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.8 +#define ULAB_VERSION 5.0.9 #define xstr(s) str(s) #define str(s) #s @@ -190,7 +190,7 @@ const mp_obj_module_t ulab_user_cmodule = { // Use old three-argument MP_REGISTER_MODULE for // MicroPython <= v1.18.0: (1 << 16) | (18 << 8) | 0 -#if MICROPY_VERSION <= 70144 +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED); #else MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule); diff --git a/code/user/user.c b/code/user/user.c index 835c091c..dfc3fcde 100644 --- a/code/user/user.c +++ b/code/user/user.c @@ -87,9 +87,16 @@ static const mp_rom_map_elem_t ulab_user_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_user_globals, ulab_user_globals_table); -mp_obj_module_t ulab_user_module = { +const mp_obj_module_t ulab_user_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module, ULAB_HAS_USER_MODULE); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module); +#endif +#endif #endif diff --git a/code/user/user.h b/code/user/user.h index 4ec366af..ff274f43 100644 --- a/code/user/user.h +++ b/code/user/user.h @@ -15,6 +15,6 @@ #include "../ulab.h" #include "../ndarray.h" -extern mp_obj_module_t ulab_user_module; +extern const mp_obj_module_t ulab_user_module; #endif diff --git a/code/utils/utils.c b/code/utils/utils.c index dd8f7a66..fee67d47 100644 --- a/code/utils/utils.c +++ b/code/utils/utils.c @@ -245,9 +245,16 @@ static const mp_rom_map_elem_t ulab_utils_globals_table[] = { static MP_DEFINE_CONST_DICT(mp_module_ulab_utils_globals, ulab_utils_globals_table); -mp_obj_module_t ulab_utils_module = { +const mp_obj_module_t ulab_utils_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals, }; +#if CIRCUITPY_ULAB +#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module, MODULE_ULAB_ENABLED); +#else +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module); +#endif +#endif #endif /* ULAB_HAS_UTILS_MODULE */ diff --git a/code/utils/utils.h b/code/utils/utils.h index 3b258bf2..b2155c38 100644 --- a/code/utils/utils.h +++ b/code/utils/utils.h @@ -14,6 +14,6 @@ #include "../ulab.h" #include "../ndarray.h" -extern mp_obj_module_t ulab_utils_module; +extern const mp_obj_module_t ulab_utils_module; #endif From fc6e200afe6858a13bf24fd5ccf059dc1cf5d33f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jul 2022 17:13:14 -0500 Subject: [PATCH 021/113] Fix INF, NAN constants on REPR_A REPR_A is used for e.g., broadcom build raspberrypi_pi4b. This is a trick, so that the appended 'f' is attached to the "0.". Infinity plus zero is still zero, and nan plus zero is still zero. Without this, a diagnostic occurred: ``` ../../extmod/ulab/code/numpy/numpy.c:64:60: error: pasting ")" and "F" does not give a valid preprocessing token ``` --- code/numpy/numpy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 453f7e46..2c0670c1 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -61,11 +61,11 @@ ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E, 0x402df854UL, 0x4005bf0a8b1457 #endif #if ULAB_NUMPY_HAS_INF -ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY, 0x7f800000UL, 0x7ff0000000000000ULL); +ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY+0., 0x7f800000UL, 0x7ff0000000000000ULL); #endif #if ULAB_NUMPY_HAS_NAN -ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN, 0x7fc00000UL, 0x7ff8000000000000ULL); +ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN+0., 0x7fc00000UL, 0x7ff8000000000000ULL); #endif #if ULAB_NUMPY_HAS_PI From 346c936e14c6ea3a8d3d65cb1fa46202dc92999d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jul 2022 17:14:19 -0500 Subject: [PATCH 022/113] Fix E, PI constants on REPR_A REPR_A is used for e.g., broadcom build raspberrypi_pi4b. This is a trick, so that the appended 'f' is attached to the "0.". Without this, a diagnostic occurred: ``` ../../extmod/ulab/code/numpy/../ndarray.h:27:34: error: invalid suffix "FF" on floating constant ``` --- code/numpy/numpy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 2c0670c1..9d0eec84 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -57,7 +57,7 @@ // math constants #if ULAB_NUMPY_HAS_E -ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E, 0x402df854UL, 0x4005bf0a8b145769ULL); +ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E+0., 0x402df854UL, 0x4005bf0a8b145769ULL); #endif #if ULAB_NUMPY_HAS_INF @@ -69,7 +69,7 @@ ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN+0., 0x7fc00000UL, #endif #if ULAB_NUMPY_HAS_PI -ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, MP_PI, 0x40490fdbUL, 0x400921fb54442d18ULL); +ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, (mp_float_t)MP_PI+0., 0x40490fdbUL, 0x400921fb54442d18ULL); #endif static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { From 35b58d003742fbd1d72d8cc629951cc7e769b5fc Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Jul 2022 15:13:05 +1000 Subject: [PATCH 023/113] Remove MICROPY_FLOAT_CONST from ULAB_DEFINE_FLOAT_CONST A given use of ULAB_DEFINE_FLOAT_CONST may already have the correct form of the float constant, so wrapping it in MICROPY_FLOAT_CONST may be the wrong thing to do. So let the user of ULAB_DEFINE_FLOAT_CONST control how the constant is formed. Signed-off-by: Damien George --- code/ndarray.h | 2 +- code/numpy/approx.c | 2 +- code/numpy/create.c | 2 +- code/numpy/numpy.c | 8 ++++---- code/scipy/optimize/optimize.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/code/ndarray.h b/code/ndarray.h index 815e4481..5466120d 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -47,7 +47,7 @@ // Use ULAB_REFERENCE_FLOAT_CONST to reference a constant float object in code. #define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \ - const mp_obj_float_t id##_obj = {{&mp_type_float}, MICROPY_FLOAT_CONST(num)} + const mp_obj_float_t id##_obj = {{&mp_type_float}, (num)} #define ULAB_REFERENCE_FLOAT_CONST(id) MP_ROM_PTR(&id##_obj) diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 27fe4bc3..6088173d 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -25,7 +25,7 @@ //| """Numerical approximation methods""" //| -ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, 1.0, 0x3f800000UL, 0x3ff0000000000000ULL); +ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL); #if ULAB_NUMPY_HAS_INTERP //| def interp( diff --git a/code/numpy/create.c b/code/numpy/create.c index 98cfacc0..aa631742 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -676,7 +676,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace); //| ... //| -ULAB_DEFINE_FLOAT_CONST(const_ten, 10.0, 0x41200000UL, 0x4024000000000000ULL); +ULAB_DEFINE_FLOAT_CONST(const_ten, MICROPY_FLOAT_CONST(10.0), 0x41200000UL, 0x4024000000000000ULL); mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 9d0eec84..453f7e46 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -57,19 +57,19 @@ // math constants #if ULAB_NUMPY_HAS_E -ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E+0., 0x402df854UL, 0x4005bf0a8b145769ULL); +ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E, 0x402df854UL, 0x4005bf0a8b145769ULL); #endif #if ULAB_NUMPY_HAS_INF -ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY+0., 0x7f800000UL, 0x7ff0000000000000ULL); +ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY, 0x7f800000UL, 0x7ff0000000000000ULL); #endif #if ULAB_NUMPY_HAS_NAN -ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN+0., 0x7fc00000UL, 0x7ff8000000000000ULL); +ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN, 0x7fc00000UL, 0x7ff8000000000000ULL); #endif #if ULAB_NUMPY_HAS_PI -ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, (mp_float_t)MP_PI+0., 0x40490fdbUL, 0x400921fb54442d18ULL); +ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, MP_PI, 0x40490fdbUL, 0x400921fb54442d18ULL); #endif static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index 5e90cc7c..72afc083 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -22,8 +22,8 @@ #include "../../ulab_tools.h" #include "optimize.h" -ULAB_DEFINE_FLOAT_CONST(xtolerance, 2.4e-7, 0x3480d959UL, 0x3e901b2b29a4692bULL); -ULAB_DEFINE_FLOAT_CONST(rtolerance, 0.0, 0UL, 0ULL); +ULAB_DEFINE_FLOAT_CONST(xtolerance, MICROPY_FLOAT_CONST(2.4e-7), 0x3480d959UL, 0x3e901b2b29a4692bULL); +ULAB_DEFINE_FLOAT_CONST(rtolerance, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL); static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) { // Helper function for calculating the value of f(x, a, b, c, ...), From 99f6fa66a12aa4c6516540f4aa96bdbb42483b05 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Jul 2022 15:14:46 +1000 Subject: [PATCH 024/113] Add single-precision float to build.sh Signed-off-by: Damien George --- build.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.sh b/build.sh index aa58e359..36b69281 100755 --- a/build.sh +++ b/build.sh @@ -50,6 +50,9 @@ make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" +# Build with single-precision float. +make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT CFLAGS_EXTRA+=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims + # The unix nanbox variant builds as a 32-bit executable and requires gcc-multilib. # macOS doesn't support i386 builds so only build on linux. if [ $PLATFORM = linux ]; then From 2b4292abcc4f22acf242b2b4656a65cd124f1a5e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Jul 2022 03:18:40 +1000 Subject: [PATCH 025/113] Make optimize's bisect,fmin,newton use C stack instead of heap (#536) Signed-off-by: Damien George --- code/scipy/optimize/optimize.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index 72afc083..d75b8e4d 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -74,7 +74,7 @@ STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_raise_TypeError(translate("first argument must be a function")); } mp_float_t xtol = mp_obj_get_float(args[3].u_obj); - mp_obj_t *fargs = m_new(mp_obj_t, 1); + mp_obj_t fargs[1]; mp_float_t left, right; mp_float_t x_mid; mp_float_t a = mp_obj_get_float(args[1].u_obj); @@ -154,7 +154,7 @@ STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_float_t x0 = mp_obj_get_float(args[1].u_obj); mp_float_t x1 = MICROPY_FLOAT_C_FUN(fabs)(x0) > OPTIMIZE_EPSILON ? (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_NONZDELTA) * x0 : OPTIMIZE_ZDELTA; - mp_obj_t *fargs = m_new(mp_obj_t, 1); + mp_obj_t fargs[1]; mp_float_t f0 = optimize_python_call(type, fun, x0, fargs, 0); mp_float_t f1 = optimize_python_call(type, fun, x1, fargs, 0); if(f1 < f0) { @@ -373,7 +373,7 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_float_t rtol = mp_obj_get_float(args[3].u_obj); mp_float_t dx, df, fx; dx = x > MICROPY_FLOAT_CONST(0.0) ? OPTIMIZE_EPS * x : -OPTIMIZE_EPS * x; - mp_obj_t *fargs = m_new(mp_obj_t, 1); + mp_obj_t fargs[1]; if(args[4].u_int <= 0) { mp_raise_ValueError(translate("maxiter must be > 0")); } From a2c5ece99108735792cba29bd993108d81079422 Mon Sep 17 00:00:00 2001 From: thetazero Date: Sat, 16 Jul 2022 09:00:10 -0700 Subject: [PATCH 026/113] Add numpy.block snippet (#539) --- snippets/numpy/lib/__init__.py | 3 ++- snippets/numpy/lib/block.py | 17 +++++++++++++++++ snippets/tests/numpy/lib/block.py | 19 +++++++++++++++++++ snippets/tests/numpy/lib/block.py.exp | 9 +++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 snippets/numpy/lib/block.py create mode 100644 snippets/tests/numpy/lib/block.py create mode 100644 snippets/tests/numpy/lib/block.py.exp diff --git a/snippets/numpy/lib/__init__.py b/snippets/numpy/lib/__init__.py index 9f5d9d48..698d29fb 100644 --- a/snippets/numpy/lib/__init__.py +++ b/snippets/numpy/lib/__init__.py @@ -1,4 +1,5 @@ from .function_base import * from .polynomial import * -from .type_check import * \ No newline at end of file +from .type_check import * +from .block import * \ No newline at end of file diff --git a/snippets/numpy/lib/block.py b/snippets/numpy/lib/block.py new file mode 100644 index 00000000..eacacc1f --- /dev/null +++ b/snippets/numpy/lib/block.py @@ -0,0 +1,17 @@ +from ulab.numpy import zeros + +def block(S): + w = sum([len(m[0]) for m in S[0]]) + h = sum([len(row[0]) for row in S]) + 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 \ No newline at end of file diff --git a/snippets/tests/numpy/lib/block.py b/snippets/tests/numpy/lib/block.py new file mode 100644 index 00000000..71bfb920 --- /dev/null +++ b/snippets/tests/numpy/lib/block.py @@ -0,0 +1,19 @@ +from ulab.numpy import array, zeros, eye, ones +from snippets import numpy + +a = array([[1, 1]]) +b = array([[2]]) +c = array( + [[3, 3], + [3, 3], + [3, 3]]) +d = array( + [[4], + [4], + [4]]) +print(numpy.block([[a, b], [c, d]])) +a = zeros((2, 3)) +b = eye(2) * 2 +c = eye(3) * 5 +d = ones((3, 2)) +print(numpy.block([[a, b], [c, d]])) diff --git a/snippets/tests/numpy/lib/block.py.exp b/snippets/tests/numpy/lib/block.py.exp new file mode 100644 index 00000000..8a5a101e --- /dev/null +++ b/snippets/tests/numpy/lib/block.py.exp @@ -0,0 +1,9 @@ +array([[1, 1, 2], + [3, 3, 4], + [3, 3, 4], + [3, 3, 4]]) +array([[0, 0, 0, 2, 0], + [0, 0, 0, 0, 2], + [5, 0, 0, 1, 1], + [0, 5, 0, 1, 1], + [0, 0, 5, 1, 1]]) \ No newline at end of file From dfed7a844a9bf16c14e7e04282422fad03632dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 3 Aug 2022 20:56:45 +0200 Subject: [PATCH 027/113] implement nonzero (#540) * implement nonzero for Boolean arrays * remove axtls from build script * extend nonzero to ndarrays of arbitrary dtype, and iterable, fix float tests * temporarily disable circuitpython tests * add nonzero documentation * Added test script for np.nonzero() Co-authored-by: Tejal Ashwini Barnwal <64950661+tejalbarnwal@users.noreply.github.com> --- build-cp.sh | 2 +- build.sh | 10 +- code/numpy/compare.c | 130 +++++++++++++++++++++++++ code/numpy/compare.h | 1 + code/numpy/numpy.c | 4 + code/ulab.c | 2 +- code/ulab.h | 4 + docs/manual/source/conf.py | 2 +- docs/manual/source/numpy-functions.rst | 82 ++++++++++++---- docs/numpy-functions.ipynb | 64 +++++++++++- docs/ulab-change-log.md | 6 ++ docs/ulab-convert.ipynb | 12 ++- test-common.sh | 14 ++- tests/1d/numpy/gc.py.exp | 4 +- tests/2d/numpy/methods.py.exp | 6 +- tests/2d/numpy/nonzero.py | 16 +++ tests/2d/numpy/nonzero.py.exp | 24 +++++ tests/2d/numpy/numericals.py.exp | 30 +++--- tests/2d/scipy/sosfilt.py.exp | 2 +- tests/2d/utils/from_buffer.py.exp | 2 +- 20 files changed, 356 insertions(+), 61 deletions(-) create mode 100644 tests/2d/numpy/nonzero.py create mode 100644 tests/2d/numpy/nonzero.py.exp diff --git a/build-cp.sh b/build-cp.sh index 3705591c..0f75487e 100755 --- a/build-cp.sh +++ b/build-cp.sh @@ -44,7 +44,7 @@ make -C circuitpython/mpy-cross -j$NPROC sed -e '/MICROPY_PY_UHASHLIB/s/1/0/' < circuitpython/ports/unix/mpconfigport.h > circuitpython/ports/unix/mpconfigport_ulab.h make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-DMP_CONFIGFILE=\"\" -Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims -bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims" +# bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims" # Docs don't depend on the dimensionality, so only do it once if [ "$dims" -eq 2 ]; then diff --git a/build.sh b/build.sh index 36b69281..c643b0ee 100755 --- a/build.sh +++ b/build.sh @@ -41,13 +41,17 @@ NPROC=`python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())'` PLATFORM=`python3 -c 'import sys; print(sys.platform)'` set -e HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )" -[ -e micropython/py/py.mk ] || git clone --no-recurse-submodules https://github.com/micropython/micropython -[ -e micropython/lib/axtls/README ] || (cd micropython && git submodule update --init lib/axtls ) dims=${1-2} +if [ ! -d "micropython" ] ; then + git clone https://github.com/micropython/micropython +else + git -C micropython pull +fi make -C micropython/mpy-cross -j${NPROC} -make -C micropython/ports/unix -j${NPROC} axtls +make -C micropython/ports/unix submodules make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-$dims PROG=micropython-$dims + bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" # Build with single-precision float. diff --git a/code/numpy/compare.c b/code/numpy/compare.c index d47b2aa7..b03d3e56 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -312,6 +312,136 @@ mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) { MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum); #endif +#if ULAB_NUMPY_HAS_NONZERO + +mp_obj_t compare_nonzero(mp_obj_t x) { + ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); + // since ndarray_new_linear_array calls m_new0, the content of zero is a single zero + ndarray_obj_t *zero = ndarray_new_linear_array(1, NDARRAY_UINT8); + + uint8_t ndim = 0; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + int32_t *x_strides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *zero_strides = m_new(int32_t, ULAB_MAX_DIMS); + // we don't actually have to inspect the outcome of ndarray_can_broadcast, + // because the right hand side is a linear array with a single element + ndarray_can_broadcast(ndarray_x, zero, &ndim, shape, x_strides, zero_strides); + + // equal_obj is a Boolean ndarray + mp_obj_t equal_obj = ndarray_binary_equality(ndarray_x, zero, ndim, shape, x_strides, zero_strides, MP_BINARY_OP_NOT_EQUAL); + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(equal_obj); + + // these are no longer needed, get rid of them + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, x_strides, ULAB_MAX_DIMS); + m_del(int32_t, zero_strides, ULAB_MAX_DIMS); + + uint8_t *array = (uint8_t *)ndarray->array; + uint8_t *origin = (uint8_t *)ndarray->array; + + // First, count the number of Trues: + uint16_t count = 0; + size_t indices[ULAB_MAX_DIMS]; + + #if ULAB_MAX_DIMS > 3 + indices[3] = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + indices[2] = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + indices[1] = 0; + do { + #endif + indices[0] = 0; + do { + if(*array != 0) { + count++; + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + indices[0]++; + } while(indices[0] < ndarray->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + indices[1]++; + } while(indices[1] < ndarray->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; + array += ndarray->strides[ULAB_MAX_DIMS - 3]; + indices[2]++; + } while(indices[2] < ndarray->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; + array += ndarray->strides[ULAB_MAX_DIMS - 4]; + indices[3]++; + } while(indices[3] < ndarray->shape[ULAB_MAX_DIMS - 4]); + #endif + + mp_obj_t *items = m_new(mp_obj_t, ndarray->ndim); + uint16_t *arrays[ULAB_MAX_DIMS]; + + for(uint8_t i = 0; i < ndarray->ndim; i++) { + ndarray_obj_t *item_array = ndarray_new_linear_array(count, NDARRAY_UINT16); + uint16_t *iarray = (uint16_t *)item_array->array; + arrays[ULAB_MAX_DIMS - 1 - i] = iarray; + items[ndarray->ndim - 1 - i] = MP_OBJ_FROM_PTR(item_array); + } + array = origin; + count = 0; + + #if ULAB_MAX_DIMS > 3 + indices[3] = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + indices[2] = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + indices[1] = 0; + do { + #endif + indices[0] = 0; + do { + if(*array != 0) { + for(uint8_t d = 0; d < ndarray->ndim; d++) { + arrays[ULAB_MAX_DIMS - 1 - d][count] = indices[d]; + } + count++; + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + indices[0]++; + } while(indices[0] < ndarray->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + indices[1]++; + } while(indices[1] < ndarray->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; + array += ndarray->strides[ULAB_MAX_DIMS - 3]; + indices[2]++; + } while(indices[2] < ndarray->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; + array += ndarray->strides[ULAB_MAX_DIMS - 4]; + indices[3]++; + } while(indices[3] < ndarray->shape[ULAB_MAX_DIMS - 4]); + #endif + + return mp_obj_new_tuple(ndarray->ndim, items); +} + +MP_DEFINE_CONST_FUN_OBJ_1(compare_nonzero_obj, compare_nonzero); +#endif /* ULAB_NUMPY_HAS_NONZERO */ + #if ULAB_NUMPY_HAS_WHERE mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { diff --git a/code/numpy/compare.h b/code/numpy/compare.h index 90ceaf7c..de3d7e65 100644 --- a/code/numpy/compare.h +++ b/code/numpy/compare.h @@ -29,6 +29,7 @@ MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj); MP_DECLARE_CONST_FUN_OBJ_2(compare_isinf_obj); MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj); MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj); +MP_DECLARE_CONST_FUN_OBJ_1(compare_nonzero_obj); MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj); MP_DECLARE_CONST_FUN_OBJ_3(compare_where_obj); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 453f7e46..a53a32cf 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -186,6 +186,10 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_MINIMUM { MP_ROM_QSTR(MP_QSTR_minimum), MP_ROM_PTR(&compare_minimum_obj) }, #endif + #if ULAB_NUMPY_HAS_NONZERO + { MP_ROM_QSTR(MP_QSTR_nonzero), MP_ROM_PTR(&compare_nonzero_obj) }, + #endif + #if ULAB_NUMPY_HAS_WHERE { MP_ROM_QSTR(MP_QSTR_where), MP_ROM_PTR(&compare_where_obj) }, #endif diff --git a/code/ulab.c b/code/ulab.c index c8222814..30c6616c 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.0.9 +#define ULAB_VERSION 5.1.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index d97c7e8a..f3fdeb45 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -349,6 +349,10 @@ #define ULAB_NUMPY_HAS_MINIMUM (1) #endif +#ifndef ULAB_NUMPY_HAS_NONZERO +#define ULAB_NUMPY_HAS_NONZERO (1) +#endif + #ifndef ULAB_NUMPY_HAS_NOTEQUAL #define ULAB_NUMPY_HAS_NOTEQUAL (1) #endif diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 2c245b0f..3f4d5eb4 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,7 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '5.0.2' +release = '5.1.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/numpy-functions.rst b/docs/manual/source/numpy-functions.rst index 5894bea4..aac19bde 100644 --- a/docs/manual/source/numpy-functions.rst +++ b/docs/manual/source/numpy-functions.rst @@ -33,21 +33,22 @@ the firmware was compiled with complex support. 25. `numpy.median <#median>`__ 26. `numpy.min <#min>`__ 27. `numpy.minimum <#minimum>`__ -28. `numpy.not_equal <#equal>`__ -29. `numpy.polyfit <#polyfit>`__ -30. `numpy.polyval <#polyval>`__ -31. `numpy.real\* <#real>`__ -32. `numpy.roll <#roll>`__ -33. `numpy.save <#save>`__ -34. `numpy.savetxt <#savetxt>`__ -35. `numpy.size <#size>`__ -36. `numpy.sort <#sort>`__ -37. `numpy.sort_complex\* <#sort_complex>`__ -38. `numpy.std <#std>`__ -39. `numpy.sum <#sum>`__ -40. `numpy.trace <#trace>`__ -41. `numpy.trapz <#trapz>`__ -42. `numpy.where <#where>`__ +28. `numpy.nozero <#nonzero>`__ +29. `numpy.not_equal <#equal>`__ +30. `numpy.polyfit <#polyfit>`__ +31. `numpy.polyval <#polyval>`__ +32. `numpy.real\* <#real>`__ +33. `numpy.roll <#roll>`__ +34. `numpy.save <#save>`__ +35. `numpy.savetxt <#savetxt>`__ +36. `numpy.size <#size>`__ +37. `numpy.sort <#sort>`__ +38. `numpy.sort_complex\* <#sort_complex>`__ +39. `numpy.std <#std>`__ +40. `numpy.sum <#sum>`__ +41. `numpy.trace <#trace>`__ +42. `numpy.trapz <#trapz>`__ +43. `numpy.where <#where>`__ all --- @@ -1289,6 +1290,46 @@ implemented. +nonzero +------- + +``numpy``: +https://docs.scipy.org/doc/numpy/reference/generated/numpy.nonzero.html + +``nonzero`` returns the indices of the elements of an array that are not +zero. If the number of dimensions of the array is larger than one, a +tuple of arrays is returned, one for each dimension, containing the +indices of the non-zero elements in that dimension. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array(range(9)) - 5 + print('a:\n', a) + print(np.nonzero(a)) + + a = a.reshape((3,3)) + print('\na:\n', a) + print(np.nonzero(a)) + +.. parsed-literal:: + + a: + array([-5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0], dtype=float64) + (array([0, 1, 2, 3, 4, 6, 7, 8], dtype=uint16),) + + a: + array([[-5.0, -4.0, -3.0], + [-2.0, -1.0, 0.0], + [1.0, 2.0, 3.0]], dtype=float64) + (array([0, 0, 0, 1, 1, 2, 2, 2], dtype=uint16), array([0, 1, 2, 0, 1, 0, 1, 2], dtype=uint16)) + + + + not_equal --------- @@ -1300,11 +1341,12 @@ polyfit ``numpy``: https://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html -polyfit takes two, or three arguments. The last one is the degree of the -polynomial that will be fitted, the last but one is an array or iterable -with the ``y`` (dependent) values, and the first one, an array or -iterable with the ``x`` (independent) values, can be dropped. If that is -the case, ``x`` will be generated in the function as ``range(len(y))``. +``polyfit`` takes two, or three arguments. The last one is the degree of +the polynomial that will be fitted, the last but one is an array or +iterable with the ``y`` (dependent) values, and the first one, an array +or iterable with the ``x`` (independent) values, can be dropped. If that +is the case, ``x`` will be generated in the function as +``range(len(y))``. If the lengths of ``x``, and ``y`` are not the same, the function raises a ``ValueError``. diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index 4c2d009f..c57225f9 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2021-02-13T08:28:06.727371Z", @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-02-01T17:37:25.505687Z", @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2022-02-01T17:37:25.717714Z", @@ -259,6 +259,7 @@ "1. [numpy.median](#median)\n", "1. [numpy.min](#min)\n", "1. [numpy.minimum](#minimum)\n", + "1. [numpy.nozero](#nonzero)\n", "1. [numpy.not_equal](#equal)\n", "1. [numpy.polyfit](#polyfit)\n", "1. [numpy.polyval](#polyval)\n", @@ -1800,6 +1801,54 @@ "print(np.maximum(1, 5.5))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## nonzero\n", + "\n", + "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.nonzero.html\n", + "\n", + "`nonzero` returns the indices of the elements of an array that are not zero. If the number of dimensions of the array is larger than one, a tuple of arrays is returned, one for each dimension, containing the indices of the non-zero elements in that dimension." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\n", + " array([-5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0], dtype=float64)\n", + "(array([0, 1, 2, 3, 4, 6, 7, 8], dtype=uint16),)\n", + "\n", + "a:\n", + " array([[-5.0, -4.0, -3.0],\n", + " [-2.0, -1.0, 0.0],\n", + " [1.0, 2.0, 3.0]], dtype=float64)\n", + "(array([0, 0, 0, 1, 1, 2, 2, 2], dtype=uint16), array([0, 1, 2, 0, 1, 0, 1, 2], dtype=uint16))\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(9)) - 5\n", + "print('a:\\n', a)\n", + "print(np.nonzero(a))\n", + "\n", + "a = a.reshape((3,3))\n", + "print('\\na:\\n', a)\n", + "print(np.nonzero(a))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1817,7 +1866,7 @@ "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html\n", "\n", - "polyfit takes two, or three arguments. The last one is the degree of the polynomial that will be fitted, the last but one is an array or iterable with the `y` (dependent) values, and the first one, an array or iterable with the `x` (independent) values, can be dropped. If that is the case, `x` will be generated in the function as `range(len(y))`.\n", + "`polyfit` takes two, or three arguments. The last one is the degree of the polynomial that will be fitted, the last but one is an array or iterable with the `y` (dependent) values, and the first one, an array or iterable with the `x` (independent) values, can be dropped. If that is the case, `x` will be generated in the function as `range(len(y))`.\n", "\n", "If the lengths of `x`, and `y` are not the same, the function raises a `ValueError`." ] @@ -2723,7 +2772,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3.8.5 ('base')", "language": "python", "name": "python3" }, @@ -2785,6 +2834,11 @@ "_Feature" ], "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10" + } } }, "nbformat": 4, diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index fd6d096b..b9d925f8 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Mon, 25 July 2022 + +version 5.1.0 + + add nonzero + Mon, 16 May 2022 version 5.0.7 diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index 2d603930..bd587912 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '5.0.2'\n", + "release = '5.1.0'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -434,11 +434,8 @@ } ], "metadata": { - "interpreter": { - "hash": "ce9a02f9f7db620716422019cafa4bc1786ca85daa298b819f6da075e7993842" - }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3.8.5 ('base')", "language": "python", "name": "python3" }, @@ -500,6 +497,11 @@ "_Feature" ], "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10" + } } }, "nbformat": 4, diff --git a/test-common.sh b/test-common.sh index d4e4d1e7..45b9e94a 100755 --- a/test-common.sh +++ b/test-common.sh @@ -6,14 +6,22 @@ for level1 in $(printf "%dd " $(seq 1 ${dims})) do for level2 in numpy scipy utils complex; do rm -f *.exp - if ! env MICROPY_MICROPYTHON="$micropython" ./run-tests -d tests/"$level1"/"$level2"; then + if [ ! -d tests/"$level1"/"$level2" ]; then + break; + fi + for file in tests/"$level1"/"$level2"/*.py; do + if [ ! -f "$file"".exp" ]; then + echo "" > "$file"".exp" + fi + done + if ! env MICROPY_MICROPYTHON="$micropython" ./run-tests -d tests/"$level1"/"$level2"; then for exp in *.exp; do testbase=$(basename $exp .exp); - echo -e "\nFAILURE $testbase"; + echo "\nFAILURE $testbase"; diff -u $testbase.exp $testbase.out; done exit 1 - fi + fi done done diff --git a/tests/1d/numpy/gc.py.exp b/tests/1d/numpy/gc.py.exp index f14e5c8b..2cfafc9d 100644 --- a/tests/1d/numpy/gc.py.exp +++ b/tests/1d/numpy/gc.py.exp @@ -1,4 +1,4 @@ -988.0000000000001 +988.0 array([1.0, 1.0, 1.0, ..., 1.0, 1.0, 1.0], dtype=float64) -988.0000000000001 +988.0 array([1.0, 1.0, 1.0, ..., 1.0, 1.0, 1.0], dtype=float64) diff --git a/tests/2d/numpy/methods.py.exp b/tests/2d/numpy/methods.py.exp index fb035dae..9173278b 100644 --- a/tests/2d/numpy/methods.py.exp +++ b/tests/2d/numpy/methods.py.exp @@ -30,6 +30,6 @@ array([256, 512, 768, 1024, 1280, 1536], dtype=uint16) array([256, 512, 768, 1024, 1280, 1536], dtype=uint16) array([256, 512, 768, 1024, 1280, 1536], dtype=uint16) array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=float64) -array([3.038651941617419e-319, 3.162020133383978e-322, 1.043466644016713e-320, 2.055313086699586e-320, 2.561236308041022e-320, 3.067159529382458e-320], dtype=float64) -array([3.038651941617419e-319, 3.162020133383978e-322, 1.043466644016713e-320, 2.055313086699586e-320, 2.561236308041022e-320, 3.067159529382458e-320], dtype=float64) -array([3.038651941617419e-319, 3.162020133383978e-322, 1.043466644016713e-320, 2.055313086699586e-320, 2.561236308041022e-320, 3.067159529382458e-320], dtype=float64) +array([1e-318, 1e-321, 1e-319, 1e-319, 1e-319, 1e-319], dtype=float64) +array([1e-318, 1e-321, 1e-319, 1e-319, 1e-319, 1e-319], dtype=float64) +array([1e-318, 1e-321, 1e-319, 1e-319, 1e-319, 1e-319], dtype=float64) diff --git a/tests/2d/numpy/nonzero.py b/tests/2d/numpy/nonzero.py new file mode 100644 index 00000000..510ef415 --- /dev/null +++ b/tests/2d/numpy/nonzero.py @@ -0,0 +1,16 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +array = np.array(range(16)).reshape((4,4)) +print(array) +print(array < 5) +print(np.nonzero(array < 5)) + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +for dtype in dtypes: + array = (np.arange(2, 12, 3, dtype=dtype)).reshape((2,2)) - 2 + print(array) + print(np.nonzero(array)) \ No newline at end of file diff --git a/tests/2d/numpy/nonzero.py.exp b/tests/2d/numpy/nonzero.py.exp new file mode 100644 index 00000000..c2ea54e4 --- /dev/null +++ b/tests/2d/numpy/nonzero.py.exp @@ -0,0 +1,24 @@ +array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0], + [12.0, 13.0, 14.0, 15.0]], dtype=float64) +array([[True, True, True, True], + [True, False, False, False], + [False, False, False, False], + [False, False, False, False]], dtype=bool) +(array([0, 0, 0, 0, 1], dtype=uint16), array([0, 1, 2, 3, 0], dtype=uint16)) +array([[0, 3], + [6, 9]], dtype=uint8) +(array([0, 1, 1], dtype=uint16), array([1, 0, 1], dtype=uint16)) +array([[0, 3], + [6, 9]], dtype=int8) +(array([0, 1, 1], dtype=uint16), array([1, 0, 1], dtype=uint16)) +array([[0, 3], + [6, 9]], dtype=uint16) +(array([0, 1, 1], dtype=uint16), array([1, 0, 1], dtype=uint16)) +array([[0, 3], + [6, 9]], dtype=int16) +(array([0, 1, 1], dtype=uint16), array([1, 0, 1], dtype=uint16)) +array([[0.0, 3.0], + [6.0, 9.0]], dtype=float64) +(array([0, 1, 1], dtype=uint16), array([1, 0, 1], dtype=uint16)) diff --git a/tests/2d/numpy/numericals.py.exp b/tests/2d/numpy/numericals.py.exp index 957b4ba3..e325a9d9 100644 --- a/tests/2d/numpy/numericals.py.exp +++ b/tests/2d/numpy/numericals.py.exp @@ -18,7 +18,7 @@ array([235, 236, 237, 238, 239], dtype=int16) array([250, 235, 245], dtype=int16) 253.0 array([253.0, 254.0, 255.0], dtype=float64) -array([7.205759403792793e+16, 65533.0, 253.0], dtype=float64) +array([7.205759403792794e+16, 65533.0, 253.0], dtype=float64) Testing np.max: 1 1.0 @@ -37,9 +37,9 @@ array([254, 239, 249], dtype=uint16) 254 array([250, 251, 252, 253, 254], dtype=int16) array([254, 239, 249], dtype=int16) -7.205759403792793e+16 -array([7.205759403792793e+16, 7.205759403792793e+16, 7.205759403792793e+16], dtype=float64) -array([7.205759403792793e+16, 65535.00000000001, 255.0], dtype=float64) +7.205759403792794e+16 +array([7.205759403792794e+16, 7.205759403792794e+16, 7.205759403792794e+16], dtype=float64) +array([7.205759403792794e+16, 65535.0, 255.0], dtype=float64) Testing np.argmin: 0 0 @@ -89,35 +89,35 @@ array([[252.0, 253.0, 254.0], [237.0, 238.0, 239.0], [247.0, 248.0, 249.0]], dtype=float64) Testing np.maximum: -array([[7.205759403792793e+16, 7.205759403792793e+16, 7.205759403792793e+16], - [65533.0, 65534.0, 65535.00000000001], +array([[7.205759403792794e+16, 7.205759403792794e+16, 7.205759403792794e+16], + [65533.0, 65534.0, 65535.0], [253.0, 254.0, 255.0]], dtype=float64) 10 10.0 -array([[7.205759403792793e+16, 7.205759403792793e+16, 7.205759403792793e+16], - [65533.0, 65534.0, 65535.00000000001], +array([[7.205759403792794e+16, 7.205759403792794e+16, 7.205759403792794e+16], + [65533.0, 65534.0, 65535.0], [253.0, 254.0, 255.0]], dtype=float64) Testing np.sort: array([237, 238, 239, 247, 248, 249, 252, 253, 254], dtype=uint8) -array([253.0, 254.0, 255.0, 65533.0, 65534.0, 65535.00000000001, 7.205759403792793e+16, 7.205759403792793e+16, 7.205759403792793e+16], dtype=float64) +array([253.0, 254.0, 255.0, 65533.0, 65534.0, 65535.0, 7.205759403792794e+16, 7.205759403792794e+16, 7.205759403792794e+16], dtype=float64) array([[237, 238, 239], [247, 248, 249], [252, 253, 254]], dtype=uint8) array([[253.0, 254.0, 255.0], - [65533.0, 65534.0, 65535.00000000001], - [7.205759403792793e+16, 7.205759403792793e+16, 7.205759403792793e+16]], dtype=float64) + [65533.0, 65534.0, 65535.0], + [7.205759403792794e+16, 7.205759403792794e+16, 7.205759403792794e+16]], dtype=float64) array([[252, 253, 254], [237, 238, 239], [247, 248, 249]], dtype=uint8) -array([[7.205759403792793e+16, 7.205759403792793e+16, 7.205759403792793e+16], - [65533.0, 65534.0, 65535.00000000001], +array([[7.205759403792794e+16, 7.205759403792794e+16, 7.205759403792794e+16], + [65533.0, 65534.0, 65535.0], [253.0, 254.0, 255.0]], dtype=float64) Testing np.sum: 762 250 2217.0 -array([736.0, 739.0000000000001, 742.0], dtype=float64) -array([759.0, 714.0000000000001, 744.0], dtype=float64) +array([736.0, 739.0, 742.0], dtype=float64) +array([759.0, 714.0, 744.0], dtype=float64) Testing np.mean: 254.0 254.0 diff --git a/tests/2d/scipy/sosfilt.py.exp b/tests/2d/scipy/sosfilt.py.exp index d7187848..6cc437a8 100644 --- a/tests/2d/scipy/sosfilt.py.exp +++ b/tests/2d/scipy/sosfilt.py.exp @@ -1,4 +1,4 @@ -y: array([9.0, -47.0, 224.0, -987.0000000000001, 4129.0, -16549.0, 64149.0, -241937.0, 892121.0, -3228165.0], dtype=float64) +y: array([9.0, -47.0, 224.0, -987.0, 4129.0, -16549.0, 64149.0, -241937.0, 892121.0, -3228165.0], dtype=float64) zo: array([[37242.0, 74835.0], [1026187.0, 1936542.0], [10433318.0, 18382017.0]], dtype=float64) diff --git a/tests/2d/utils/from_buffer.py.exp b/tests/2d/utils/from_buffer.py.exp index de9f7436..6899e34f 100644 --- a/tests/2d/utils/from_buffer.py.exp +++ b/tests/2d/utils/from_buffer.py.exp @@ -1,4 +1,4 @@ -array([1.0, 256.0, 65280.0, 65535.00000000001], dtype=float64) +array([1.0, 256.0, 65280.0, 65535.0], dtype=float64) array([1.0, 256.0, -256.0, -1.0], dtype=float64) array([16777217.0, 4294967040.0], dtype=float64) array([16777217.0, -256.0], dtype=float64) From 57de23c1fb434ba99aaafe1d00bd77d5cdf5d66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 4 Aug 2022 07:35:20 +0200 Subject: [PATCH 028/113] fix how arctan2 treats scalars (#546) --- code/numpy/vector.c | 7 +++++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 693d6628..1f40a7f1 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -240,6 +240,13 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan); //| mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) { + if((mp_obj_is_float(y) || mp_obj_is_int(y)) && + (mp_obj_is_float(x) || mp_obj_is_int(x))) { + mp_float_t _y = mp_obj_get_float(y); + mp_float_t _x = mp_obj_get_float(x); + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(_y, _x)); + } + ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype) diff --git a/code/ulab.c b/code/ulab.c index 30c6616c..b66e9e72 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.1.0 +#define ULAB_VERSION 5.1.1 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index b9d925f8..1dafb520 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Thu, 4 Aug 2022 + +version 5.1.1 + + fix how arctan2 treats scalars + Mon, 25 July 2022 version 5.1.0 From 42f396a992408eb4c81838cb7bf23c875aa5225e Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 22 Sep 2022 03:46:39 +1000 Subject: [PATCH 029/113] Update mp_obj_type_t definitions for latest MicroPython. (#549) * build.sh: Fix unix executable path. This was updated recently to no longer copy to the ports/unix directory. Use the version in the build directory instead if available. Signed-off-by: Jim Mussared * Update to new style mp_obj_type_t definitions. Signed-off-by: Jim Mussared Signed-off-by: Jim Mussared --- build.sh | 7 +++- code/ndarray.h | 15 ++++++-- code/ndarray_properties.c | 12 +++---- code/numpy/vector.c | 17 ++++++--- code/scipy/optimize/optimize.c | 10 +++--- code/ulab.c | 64 ++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 19 deletions(-) diff --git a/build.sh b/build.sh index c643b0ee..c92d1888 100755 --- a/build.sh +++ b/build.sh @@ -51,8 +51,13 @@ make -C micropython/mpy-cross -j${NPROC} make -C micropython/ports/unix submodules make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-$dims PROG=micropython-$dims +PROG="micropython/ports/unix/build-$dims/micropython-$dims" +if [ ! -e "$PROG" ]; then + # Older MicroPython revision, executable is still in ports/unix. + PROG="micropython/ports/unix/micropython-$dims" +fi -bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" +bash test-common.sh "${dims}" "$PROG" # Build with single-precision float. make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT CFLAGS_EXTRA+=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims diff --git a/code/ndarray.h b/code/ndarray.h index 5466120d..3daf5b83 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -93,12 +93,21 @@ typedef struct _mp_obj_slice_t { #define MP_ERROR_TEXT(x) x #endif -#if !defined(MP_TYPE_FLAG_EXTENDED) -#define MP_TYPE_CALL call -#define mp_type_get_call_slot(t) t->call +#if !defined(MP_OBJ_TYPE_GET_SLOT) +#if defined(MP_TYPE_FLAG_EXTENDED) +// Provide MP_OBJ_TYPE_{HAS,GET}_SLOT for CircuitPython. +#define MP_OBJ_TYPE_HAS_SLOT(t, f) (mp_type_get_##f##_slot(t) != NULL) +#define MP_OBJ_TYPE_GET_SLOT(t, f) mp_type_get_##f##_slot(t) +#else +// Provide MP_OBJ_TYPE_{HAS,GET}_SLOT for older revisions of MicroPython. +#define MP_OBJ_TYPE_HAS_SLOT(t, f) ((t)->f != NULL) +#define MP_OBJ_TYPE_GET_SLOT(t, f) (t)->f + +// Also allow CiruitPython-style mp_obj_type_t definitions. #define MP_TYPE_FLAG_EXTENDED (0) #define MP_TYPE_EXTENDED_FIELDS(...) __VA_ARGS__ #endif +#endif #if !CIRCUITPY #define translate(x) MP_ERROR_TEXT(x) diff --git a/code/ndarray_properties.c b/code/ndarray_properties.c index 5464b31d..547b1912 100644 --- a/code/ndarray_properties.c +++ b/code/ndarray_properties.c @@ -31,18 +31,18 @@ STATIC void call_local_method(mp_obj_t obj, qstr attr, mp_obj_t *dest) { const mp_obj_type_t *type = mp_obj_get_type(obj); - while (type->locals_dict != NULL) { - assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now - mp_map_t *locals_map = &type->locals_dict->map; + while (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { + assert(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->base.type == &mp_type_dict); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); if (elem != NULL) { mp_convert_member_lookup(obj, type, elem->value, dest); break; } - if (type->parent == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { break; } - type = type->parent; + type = MP_OBJ_TYPE_GET_SLOT(type, parent); } } @@ -120,4 +120,4 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -#endif /* CIRCUITPY */ \ No newline at end of file +#endif /* CIRCUITPY */ diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 1f40a7f1..a5df5c4a 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -762,7 +762,7 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, self->otypes); for(size_t i=0; i < source->len; i++) { avalue[0] = mp_binary_get_val_array(source->dtype, source->array, i); - fvalue = self->type->MP_TYPE_CALL(self->fun, 1, 0, avalue); + fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue); ndarray_set_value(self->otypes, ndarray->array, i, fvalue); } return MP_OBJ_FROM_PTR(ndarray); @@ -774,14 +774,14 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, mp_obj_t iterable = mp_getiter(args[0], &iter_buf); size_t i=0; while ((avalue[0] = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - fvalue = self->type->MP_TYPE_CALL(self->fun, 1, 0, avalue); + fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue); ndarray_set_value(self->otypes, ndarray->array, i, fvalue); i++; } return MP_OBJ_FROM_PTR(ndarray); } else if(mp_obj_is_int(args[0]) || mp_obj_is_float(args[0])) { ndarray_obj_t *ndarray = ndarray_new_linear_array(1, self->otypes); - fvalue = self->type->MP_TYPE_CALL(self->fun, 1, 0, args); + fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, args); ndarray_set_value(self->otypes, ndarray->array, 0, fvalue); return MP_OBJ_FROM_PTR(ndarray); } else { @@ -790,6 +790,14 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, return mp_const_none; } +#if defined(MP_DEFINE_CONST_OBJ_TYPE) +MP_DEFINE_CONST_OBJ_TYPE( + vector_function_type, + MP_QSTR_, + MP_TYPE_FLAG_NONE, + call, vector_vectorized_function_call +); +#else const mp_obj_type_t vector_function_type = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EXTENDED, @@ -798,6 +806,7 @@ const mp_obj_type_t vector_function_type = { .call = vector_vectorized_function_call, ) }; +#endif //| def vectorize( //| f: Union[Callable[[int], _float], Callable[[_float], _float]], @@ -821,7 +830,7 @@ static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj); - if(mp_type_get_call_slot(type) == NULL) { + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { mp_raise_TypeError(translate("first argument must be a callable")); } mp_obj_t _otypes = args[1].u_obj; diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index d75b8e4d..7fc6b670 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -30,7 +30,7 @@ static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, // where f is defined in python. Takes a float, returns a float. // The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams fargs[0] = mp_obj_new_float(x); - return mp_obj_get_float(type->MP_TYPE_CALL(fun, nparams+1, 0, fargs)); + return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs)); } #if ULAB_SCIPY_OPTIMIZE_HAS_BISECT @@ -70,7 +70,7 @@ STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); - if(mp_type_get_call_slot(type) == NULL) { + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { mp_raise_TypeError(translate("first argument must be a function")); } mp_float_t xtol = mp_obj_get_float(args[3].u_obj); @@ -140,7 +140,7 @@ STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); - if(mp_type_get_call_slot(type) == NULL) { + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { mp_raise_TypeError(translate("first argument must be a function")); } @@ -276,7 +276,7 @@ mp_obj_t optimize_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); - if(mp_type_get_call_slot(type) == NULL) { + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { mp_raise_TypeError(translate("first argument must be a function")); } @@ -365,7 +365,7 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); - if(mp_type_get_call_slot(type) == NULL) { + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { mp_raise_TypeError(translate("first argument must be a function")); } mp_float_t x = mp_obj_get_float(args[1].u_obj); diff --git a/code/ulab.c b/code/ulab.c index b66e9e72..441a4557 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -100,6 +100,49 @@ STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table); +#if defined(MP_DEFINE_CONST_OBJ_TYPE) +// MicroPython after-b41aaaa (Sept 19 2022). + +#if NDARRAY_IS_SLICEABLE +#define NDARRAY_TYPE_SUBSCR subscr, ndarray_subscr, +#else +#define NDARRAY_TYPE_SUBSCR +#endif +#if NDARRAY_IS_ITERABLE +#define NDARRAY_TYPE_ITER iter, ndarray_getiter, +#define NDARRAY_TYPE_ITER_FLAGS MP_TYPE_FLAG_ITER_IS_GETITER +#else +#define NDARRAY_TYPE_ITER +#define NDARRAY_TYPE_ITER_FLAGS 0 +#endif +#if NDARRAY_HAS_UNARY_OPS +#define NDARRAY_TYPE_UNARY_OP unary_op, ndarray_unary_op, +#else +#define NDARRAY_TYPE_UNARY_OP +#endif +#if NDARRAY_HAS_BINARY_OPS +#define NDARRAY_TYPE_BINARY_OP binary_op, ndarray_binary_op, +#else +#define NDARRAY_TYPE_BINARY_OP +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + ulab_ndarray_type, + MP_QSTR_ndarray, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST | NDARRAY_TYPE_ITER_FLAGS, + print, ndarray_print, + make_new, ndarray_make_new, + locals_dict, &ulab_ndarray_locals_dict, + NDARRAY_TYPE_SUBSCR + NDARRAY_TYPE_ITER + NDARRAY_TYPE_UNARY_OP + NDARRAY_TYPE_BINARY_OP + attr, ndarray_properties_attr, + buffer, ndarray_get_buffer +); + +#else +// CircuitPython and earlier MicroPython revisions. const mp_obj_type_t ulab_ndarray_type = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EXTENDED @@ -129,8 +172,19 @@ const mp_obj_type_t ulab_ndarray_type = { .buffer_p = { .get_buffer = ndarray_get_buffer, }, ) }; +#endif #if ULAB_HAS_DTYPE_OBJECT + +#if defined(MP_DEFINE_CONST_OBJ_TYPE) +MP_DEFINE_CONST_OBJ_TYPE( + ulab_dtype_type, + MP_QSTR_dtype, + MP_TYPE_FLAG_NONE, + print, ndarray_dtype_print + make_new, ndarray_dtype_make_new +); +#else const mp_obj_type_t ulab_dtype_type = { { &mp_type_type }, .name = MP_QSTR_dtype, @@ -138,8 +192,17 @@ const mp_obj_type_t ulab_dtype_type = { .make_new = ndarray_dtype_make_new, }; #endif +#endif #if NDARRAY_HAS_FLATITER +#if defined(MP_DEFINE_CONST_OBJ_TYPE) +MP_DEFINE_CONST_OBJ_TYPE( + ndarray_flatiter_type, + MP_QSTR_flatiter, + MP_TYPE_FLAG_ITER_IS_GETITER, + iter, ndarray_get_flatiterator +); +#else const mp_obj_type_t ndarray_flatiter_type = { { &mp_type_type }, .name = MP_QSTR_flatiter, @@ -148,6 +211,7 @@ const mp_obj_type_t ndarray_flatiter_type = { ) }; #endif +#endif STATIC const mp_rom_map_elem_t ulab_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) }, From 294f9d9ef24603a62de403d683747cd96c5b1994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 21 Sep 2022 19:59:34 +0200 Subject: [PATCH 030/113] fix test script, bring ulab in line with micropython, bump version number to 6.0.0 --- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ tests/2d/numpy/methods.py | 14 +++++++------- tests/2d/numpy/methods.py.exp | 4 ---- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/code/ulab.c b/code/ulab.c index 441a4557..47ac6ea5 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 5.1.1 +#define ULAB_VERSION 6.0.0 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 1dafb520..51a69b1d 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Wed, 21 Sep 2022 + +version 6.0.0 + + bring ulab in line with the latest version of micropython + Thu, 4 Aug 2022 version 5.1.1 diff --git a/tests/2d/numpy/methods.py b/tests/2d/numpy/methods.py index 0fa7912e..02c9a730 100644 --- a/tests/2d/numpy/methods.py +++ b/tests/2d/numpy/methods.py @@ -42,10 +42,10 @@ c = a.byteswap(inplace=True) print(a) print(c) -a = np.array([1, 2, 3, 4, 5, 6], dtype=np.float) -b = a.byteswap(inplace=False) -print(a) -print(b) -c = a.byteswap(inplace=True) -print(a) -print(c) +# a = np.array([1, 2, 3, 4, 5, 6], dtype=np.float) +# b = a.byteswap(inplace=False) +# print(a) +# print(b) +# c = a.byteswap(inplace=True) +# print(a) +# print(c) diff --git a/tests/2d/numpy/methods.py.exp b/tests/2d/numpy/methods.py.exp index 9173278b..47ec0d21 100644 --- a/tests/2d/numpy/methods.py.exp +++ b/tests/2d/numpy/methods.py.exp @@ -29,7 +29,3 @@ array([1, 2, 3, 4, 5, 6], dtype=uint16) array([256, 512, 768, 1024, 1280, 1536], dtype=uint16) array([256, 512, 768, 1024, 1280, 1536], dtype=uint16) array([256, 512, 768, 1024, 1280, 1536], dtype=uint16) -array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=float64) -array([1e-318, 1e-321, 1e-319, 1e-319, 1e-319, 1e-319], dtype=float64) -array([1e-318, 1e-321, 1e-319, 1e-319, 1e-319, 1e-319], dtype=float64) -array([1e-318, 1e-321, 1e-319, 1e-319, 1e-319, 1e-319], dtype=float64) From 0c807bd9125ad841caecc45c1eb8fa478352a11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 21 Sep 2022 20:00:03 +0200 Subject: [PATCH 031/113] remove comments from methods.py --- tests/2d/numpy/methods.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/2d/numpy/methods.py b/tests/2d/numpy/methods.py index 02c9a730..1c687d13 100644 --- a/tests/2d/numpy/methods.py +++ b/tests/2d/numpy/methods.py @@ -42,10 +42,3 @@ c = a.byteswap(inplace=True) print(a) print(c) -# a = np.array([1, 2, 3, 4, 5, 6], dtype=np.float) -# b = a.byteswap(inplace=False) -# print(a) -# print(b) -# c = a.byteswap(inplace=True) -# print(a) -# print(c) From 5ccfa5cdd9040c2c4219c07b005256427d31ed1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 21 Sep 2022 20:10:18 +0200 Subject: [PATCH 032/113] Update build.sh remove tests from nanbox --- build.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/build.sh b/build.sh index c92d1888..7927d4ac 100755 --- a/build.sh +++ b/build.sh @@ -66,6 +66,5 @@ make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP # macOS doesn't support i386 builds so only build on linux. if [ $PLATFORM = linux ]; then make -C micropython/ports/unix -j${NPROC} VARIANT=nanbox USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims - bash test-common.sh "${dims}" "micropython/ports/unix/micropython-$dims" fi From 41fcf1d4cffa640f33ef081b567b61cf0fd8b58d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 30 Sep 2022 18:42:49 +1000 Subject: [PATCH 033/113] Minor compile fixes: comma and new-line at end of files (#550) * Fix missing comma in type definition Signed-off-by: Damien George * Make sure all files have a new-line at the end Some very old compilers don't like files without a new-line at the end. Signed-off-by: Damien George * Use math.isclose for universal_functions expm1 test Signed-off-by: Damien George Signed-off-by: Damien George --- code/numpy/io/io.h | 2 +- code/numpy/ndarray/ndarray_iter.h | 2 +- code/ulab.c | 3 ++- code/ulab_tools.c | 2 +- tests/1d/numpy/universal_functions.py | 2 +- tests/1d/numpy/universal_functions.py.exp | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/code/numpy/io/io.h b/code/numpy/io/io.h index d0141e78..33f1b687 100644 --- a/code/numpy/io/io.h +++ b/code/numpy/io/io.h @@ -16,4 +16,4 @@ MP_DECLARE_CONST_FUN_OBJ_KW(io_loadtxt_obj); MP_DECLARE_CONST_FUN_OBJ_2(io_save_obj); MP_DECLARE_CONST_FUN_OBJ_KW(io_savetxt_obj); -#endif \ No newline at end of file +#endif diff --git a/code/numpy/ndarray/ndarray_iter.h b/code/numpy/ndarray/ndarray_iter.h index b3fc48db..f740f416 100644 --- a/code/numpy/ndarray/ndarray_iter.h +++ b/code/numpy/ndarray/ndarray_iter.h @@ -33,4 +33,4 @@ mp_obj_t ndarray_get_flatiterator(mp_obj_t , mp_obj_iter_buf_t *); mp_obj_t ndarray_flatiter_make_new(mp_obj_t ); mp_obj_t ndarray_flatiter_next(mp_obj_t ); -#endif \ No newline at end of file +#endif diff --git a/code/ulab.c b/code/ulab.c index 47ac6ea5..4f22bbb7 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -147,8 +147,9 @@ const mp_obj_type_t ulab_ndarray_type = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EXTENDED #if defined(MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) && defined(MP_TYPE_FLAG_EQ_HAS_NEQ_TEST) - | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST, + | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST #endif + , .name = MP_QSTR_ndarray, .print = ndarray_print, .make_new = ndarray_make_new, diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 68f14bf5..514721f7 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -273,4 +273,4 @@ bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) { return false; } #endif -} \ No newline at end of file +} diff --git a/tests/1d/numpy/universal_functions.py b/tests/1d/numpy/universal_functions.py index 1dc3b60e..c58d9333 100644 --- a/tests/1d/numpy/universal_functions.py +++ b/tests/1d/numpy/universal_functions.py @@ -58,7 +58,7 @@ print(np.log2(2**1)) print(np.log10(10**1)) -print(np.exp(1) - np.expm1(1)) +print(math.isclose(np.exp(1) - np.expm1(1), 1)) x = np.array([-1, +1, +1, -1]) y = np.array([-1, -1, +1, +1]) diff --git a/tests/1d/numpy/universal_functions.py.exp b/tests/1d/numpy/universal_functions.py.exp index 2931d6c2..d881bf68 100644 --- a/tests/1d/numpy/universal_functions.py.exp +++ b/tests/1d/numpy/universal_functions.py.exp @@ -18,7 +18,7 @@ True 1.0 1.0 1.0 -1.0 +True [True, True, True, True] [True, True, True, True, True] [True, True, True, True, True] From 38e99c69af207e2b8eb63a07826ae99aa8929666 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 10 Oct 2022 17:48:17 +1100 Subject: [PATCH 034/113] code/ulab.c: Fix missing comma in ulab_dtype_type definition. (#553) Signed-off-by: Jim Mussared Signed-off-by: Jim Mussared --- code/ulab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ulab.c b/code/ulab.c index 4f22bbb7..7ceeac62 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -182,7 +182,7 @@ MP_DEFINE_CONST_OBJ_TYPE( ulab_dtype_type, MP_QSTR_dtype, MP_TYPE_FLAG_NONE, - print, ndarray_dtype_print + print, ndarray_dtype_print, make_new, ndarray_dtype_make_new ); #else From 155dad9994a49b5972a496cfeb3c03b9a3989247 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 6 Nov 2022 03:39:10 -0600 Subject: [PATCH 035/113] Use python 3.10 during ci (#558) CircuitPython now requires 3.9 as a minumum --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c0ba8b6..d6587f5b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,10 +28,10 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: "3.10" - name: Install requirements run: | @@ -68,10 +68,10 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: "3.10" - name: Versions run: | From 25a825e41c26cfcee018b762416741d0d63aeabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 7 Nov 2022 17:23:04 +0100 Subject: [PATCH 036/113] fix segmentation fault bug in fft.ifft (#557) --- code/numpy/fft/fft_tools.c | 6 +++--- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 4f36bb2c..595b866b 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -118,9 +118,9 @@ mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) { if(in->dtype == NDARRAY_COMPLEX) { uint8_t sz = 2 * sizeof(mp_float_t); - uint8_t *data_ = (uint8_t *)out->array; for(size_t i = 0; i < len; i++) { - memcpy(data_, array, sz); + memcpy(data, array, sz); + data += 2; array += in->strides[ULAB_MAX_DIMS - 1]; } } else { @@ -149,7 +149,7 @@ mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) { } else { // inverse transform fft_kernel_complex(data, len, -1); // TODO: numpy accepts the norm keyword argument - for(size_t i = 0; i < len; i++) { + for(size_t i = 0; i < 2 * len; i++) { *data++ /= len; } } diff --git a/code/ulab.c b/code/ulab.c index 7ceeac62..c8965918 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.0 +#define ULAB_VERSION 6.0.1 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 51a69b1d..cc0fe9bc 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sat, 5 Nov 2022 + +version 6.0.1 + + fix fft.ifft + Wed, 21 Sep 2022 version 6.0.0 From 1a440d7d124a20a3dbaa0909000741eb28a623b3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Nov 2022 18:02:35 +1100 Subject: [PATCH 037/113] Fix sort when dtype is uint16 (#563) Prior to this fix the code was using the mp_float_t data type for uint16 and producing incorrect sort results. Signed-off-by: Damien George Signed-off-by: Damien George --- code/numpy/numerical.c | 2 +- tests/2d/numpy/sort.py.exp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 686843ed..f92162a1 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -650,7 +650,7 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla if(ndarray->shape[ax]) { if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { HEAPSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax]); - } else if((ndarray->dtype == NDARRAY_INT16) || (ndarray->dtype == NDARRAY_INT16)) { + } else if((ndarray->dtype == NDARRAY_UINT16) || (ndarray->dtype == NDARRAY_INT16)) { HEAPSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax]); } else { HEAPSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax]); diff --git a/tests/2d/numpy/sort.py.exp b/tests/2d/numpy/sort.py.exp index 83e6d698..3b80dded 100644 --- a/tests/2d/numpy/sort.py.exp +++ b/tests/2d/numpy/sort.py.exp @@ -11,7 +11,7 @@ array([1, 2, 3, 4], dtype=int8) array([], dtype=uint16) [] -array([0, 0, 0, 0], dtype=uint16) +array([1, 2, 3, 4], dtype=uint16) [1, 3, 2, 0] array([], dtype=int16) From 42172c657598ccf6561f2462d192ac7e0e4a1326 Mon Sep 17 00:00:00 2001 From: HaydenS3 <30916857+HaydenS3@users.noreply.github.com> Date: Tue, 3 Jan 2023 17:04:52 -0600 Subject: [PATCH 038/113] Added note of dimensions argument for ./build.sh :crying_cat_face: (#567) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a62fd5e..99c47ad3 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ git clone https://github.com/v923z/micropython-ulab.git ulab and then run ```bash -./build.sh +./build.sh [matrix.dims] # Dimensions is 2 by default ``` This command will clone `micropython`, and build the `unix` port automatically, as well as run the test scripts. If you want an interactive `unix` session, you can launch it in From e68bb707b20ee326d84ab75fc9fb35f2e85b87e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 4 Jan 2023 00:27:41 +0100 Subject: [PATCH 039/113] fix vectorize (#568) --- code/numpy/vector.c | 49 +++++++++++++++++++++++++++++---- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++ tests/2d/numpy/vectorize.py | 18 ++++++++++++ tests/2d/numpy/vectorize.py.exp | 40 +++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/2d/numpy/vectorize.py create mode 100644 tests/2d/numpy/vectorize.py.exp diff --git a/code/numpy/vector.c b/code/numpy/vector.c index a5df5c4a..01e209ea 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -759,12 +759,51 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]); COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype) + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, self->otypes); - for(size_t i=0; i < source->len; i++) { - avalue[0] = mp_binary_get_val_array(source->dtype, source->array, i); - fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue); - ndarray_set_value(self->otypes, ndarray->array, i, fvalue); - } + uint8_t *sarray = (uint8_t *)source->array; + uint8_t *narray = (uint8_t *)ndarray->array; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0); + fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue); + ndarray_set_value(self->otypes, narray, 0, fvalue); + sarray += source->strides[ULAB_MAX_DIMS - 1]; + narray += ndarray->itemsize; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS - 1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif /* ULAB_MAX_DIMS > 1 */ + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS - 2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif /* ULAB_MAX_DIMS > 2 */ + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS - 3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif /* ULAB_MAX_DIMS > 3 */ + return MP_OBJ_FROM_PTR(ndarray); } else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) || mp_obj_is_type(args[0], &mp_type_range)) { // i.e., the input is a generic iterable diff --git a/code/ulab.c b/code/ulab.c index c8965918..04729e7a 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.1 +#define ULAB_VERSION 6.0.2 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index cc0fe9bc..9f211ab5 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Tue, 3 Jan 2023 + +version 6.0.2 + + fix vectorize + Sat, 5 Nov 2022 version 6.0.1 diff --git a/tests/2d/numpy/vectorize.py b/tests/2d/numpy/vectorize.py new file mode 100644 index 00000000..8cb6e104 --- /dev/null +++ b/tests/2d/numpy/vectorize.py @@ -0,0 +1,18 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +square = np.vectorize(lambda n: n*n) + +for dtype in dtypes: + a = np.array(range(9), dtype=dtype).reshape((3, 3)) + print(a) + print(square(a)) + + b = a[:,2] + print(square(b)) + print() diff --git a/tests/2d/numpy/vectorize.py.exp b/tests/2d/numpy/vectorize.py.exp new file mode 100644 index 00000000..e3cc8883 --- /dev/null +++ b/tests/2d/numpy/vectorize.py.exp @@ -0,0 +1,40 @@ +array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]], dtype=uint8) +array([[0.0, 1.0, 4.0], + [9.0, 16.0, 25.0], + [36.0, 49.0, 64.0]], dtype=float64) +array([4.0, 25.0, 64.0], dtype=float64) + +array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]], dtype=int8) +array([[0.0, 1.0, 4.0], + [9.0, 16.0, 25.0], + [36.0, 49.0, 64.0]], dtype=float64) +array([4.0, 25.0, 64.0], dtype=float64) + +array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]], dtype=uint16) +array([[0.0, 1.0, 4.0], + [9.0, 16.0, 25.0], + [36.0, 49.0, 64.0]], dtype=float64) +array([4.0, 25.0, 64.0], dtype=float64) + +array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]], dtype=int16) +array([[0.0, 1.0, 4.0], + [9.0, 16.0, 25.0], + [36.0, 49.0, 64.0]], dtype=float64) +array([4.0, 25.0, 64.0], dtype=float64) + +array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [6.0, 7.0, 8.0]], dtype=float64) +array([[0.0, 1.0, 4.0], + [9.0, 16.0, 25.0], + [36.0, 49.0, 64.0]], dtype=float64) +array([4.0, 25.0, 64.0], dtype=float64) + From 5be42be128864d70fee064cf9037c695f0a32a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 14 Jan 2023 09:49:01 +0100 Subject: [PATCH 040/113] Update bug_report.md correct misleading instruction on version number --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e1c37902..23520f2f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,8 +11,8 @@ assignees: '' A clear and concise description of what the bug is. Give the `ulab` version ```python -from ulab import numpy as np -print(np.__version__) +import ulab +print(ulab.__version__) ``` **To Reproduce** From 7124eaac742bb04bb5c88eafed11315f8db53e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 14 Jan 2023 11:02:33 +0100 Subject: [PATCH 041/113] fix concatenate (#575) --- code/numpy/create.c | 8 ++++++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ tests/2d/numpy/concatenate.py | 12 ++++++++++++ tests/2d/numpy/concatenate.py.exp | 6 ++++++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index aa631742..29e25ff0 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -248,6 +248,14 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); mp_obj_tuple_t *ndarrays = MP_OBJ_TO_PTR(args[0].u_obj); + // first check, whether + + for(uint8_t i = 0; i < ndarrays->len; i++) { + if(!mp_obj_is_type(ndarrays->items[i], &ulab_ndarray_type)) { + mp_raise_ValueError(translate("only ndarrays can be concatenated")); + } + } + // first check, whether the arrays are compatible ndarray_obj_t *_ndarray = MP_OBJ_TO_PTR(ndarrays->items[0]); uint8_t dtype = _ndarray->dtype; diff --git a/code/ulab.c b/code/ulab.c index 04729e7a..0eaa08ef 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.2 +#define ULAB_VERSION 6.0.3 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 9f211ab5..2e8f3438 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sat, 14 Jan 2023 + +version 6.0.3 + + fix how concatenate deals with scalar inputs + Tue, 3 Jan 2023 version 6.0.2 diff --git a/tests/2d/numpy/concatenate.py b/tests/2d/numpy/concatenate.py index bcae97a0..1a7a440a 100644 --- a/tests/2d/numpy/concatenate.py +++ b/tests/2d/numpy/concatenate.py @@ -3,6 +3,18 @@ except: import numpy as np +# test input types; the following should raise ValueErrors +objects = [([1, 2], [3, 4]), + ((1, 2), (3, 4)), + (1, 2, 3)] + +for obj in objects: + try: + np.concatenate(obj) + except ValueError as e: + print('ValueError: {}; failed with object {}\n'.format(e, obj)) + + a = np.array([1,2,3], dtype=np.float) b = np.array([4,5,6], dtype=np.float) diff --git a/tests/2d/numpy/concatenate.py.exp b/tests/2d/numpy/concatenate.py.exp index 4310f352..7b522e53 100644 --- a/tests/2d/numpy/concatenate.py.exp +++ b/tests/2d/numpy/concatenate.py.exp @@ -1,3 +1,9 @@ +ValueError: only ndarrays can be concatenated; failed with object ([1, 2], [3, 4]) + +ValueError: only ndarrays can be concatenated; failed with object ((1, 2), (3, 4)) + +ValueError: only ndarrays can be concatenated; failed with object (1, 2, 3) + array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=float64) array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=float64) array([[1.0, 2.0, 3.0], From 6e6d24ea4573974f2d5fd02e1980a0c00808cf77 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 15 Jan 2023 03:58:18 -0600 Subject: [PATCH 042/113] Allocate 1d results of correct size (#577) Before, it was erroneously allocated as a 1-element array instead of a 6-element array in the test case. Closes #574 --- code/numpy/transform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/transform.c b/code/numpy/transform.c index c36d162a..6a849b94 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -380,7 +380,7 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { if(ndim == 2) { // matrix times matrix -> matrix shape = ndarray_shape_vector(0, 0, shape1, shape2); } else { // matrix times vector -> vector, vector times vector -> vector (size 1) - shape = ndarray_shape_vector(0, 0, 0, shape1); + shape = ndarray_shape_vector(0, 0, 0, shape1 * shape2); } ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); mp_float_t *rarray = (mp_float_t *)results->array; From 6fcfeda58da8632bb7774858a9bf974afe65d5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 15 Jan 2023 11:01:15 +0100 Subject: [PATCH 043/113] increment version number after dot fix --- code/ulab.c | 2 +- docs/ulab-change-log.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/code/ulab.c b/code/ulab.c index 0eaa08ef..427230fb 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.3 +#define ULAB_VERSION 6.0.4 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 2e8f3438..0786d49f 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,10 @@ + +Sun, 15 Jan 2023 + +version 6.0.4 + + fix dot function + Sat, 14 Jan 2023 version 6.0.3 From 315c988393ec9527364110e8a5e5dee94f7d1557 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 19 Jan 2023 07:13:14 -0600 Subject: [PATCH 044/113] Check that array size doesn't overflow at construction time Now, requesting to allocate an array that is too big gives the exception 'array is too big', like numpy. This does depend on a gcc extension, `__builtin_mul_overflow`, present since at least version 5. This extension is also supported in clang. msvc is probably the only compiler of note that does not support it. Closes: #576 --- code/ndarray.c | 11 +++++++++-- tests/2d/numpy/zeros.py | 15 +++++++++++++++ tests/2d/numpy/zeros.py.exp | 3 +++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 984fa188..bade22cf 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -593,6 +593,13 @@ bool ndarray_is_dense(ndarray_obj_t *ndarray) { return stride == ndarray->strides[ULAB_MAX_DIMS-ndarray->ndim] ? true : false; } +static size_t multiply_size(size_t a, size_t b) { + size_t result; + if (__builtin_mul_overflow(a, b, &result)) { + mp_raise_ValueError(translate("array is too big")); + } + return result; +} ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype) { // Creates the base ndarray with shape, and initialises the values to straight 0s @@ -612,11 +619,11 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides for(uint8_t i=ULAB_MAX_DIMS; i > ULAB_MAX_DIMS-ndim; i--) { ndarray->shape[i-1] = shape[i-1]; ndarray->strides[i-1] = _strides[i-1]; - ndarray->len *= shape[i-1]; + ndarray->len = multiply_size(ndarray->len, shape[i-1]); } // if the length is 0, still allocate a single item, so that contractions can be handled - size_t len = ndarray->itemsize * MAX(1, ndarray->len); + size_t len = multiply_size(ndarray->itemsize, MAX(1, ndarray->len)); uint8_t *array = m_new0(byte, len); // this should set all elements to 0, irrespective of the of the dtype (all bits are zero) // we could, perhaps, leave this step out, and initialise the array only, when needed diff --git a/tests/2d/numpy/zeros.py b/tests/2d/numpy/zeros.py index af9bd0f4..8e86f9d3 100644 --- a/tests/2d/numpy/zeros.py +++ b/tests/2d/numpy/zeros.py @@ -11,3 +11,18 @@ for dtype in dtypes: print(np.zeros((3,3), dtype=dtype)) print(np.zeros((4,2), dtype=dtype)) + +try: + np.zeros((1<<31, 1<<31)) +except ValueError: + print("ValueError") + +try: + np.zeros((2147483653, 2147483649)) +except ValueError: + print("ValueError") + +try: + np.zeros((194899806, 189294637612)) +except ValueError: + print("ValueError") diff --git a/tests/2d/numpy/zeros.py.exp b/tests/2d/numpy/zeros.py.exp index ec61a9ca..99e2a21a 100644 --- a/tests/2d/numpy/zeros.py.exp +++ b/tests/2d/numpy/zeros.py.exp @@ -37,3 +37,6 @@ array([[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], dtype=float64) +ValueError +ValueError +ValueError From 20766d16e116693fe13013636cfc830afd4d5a9a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 21 Jan 2023 08:03:06 -0600 Subject: [PATCH 045/113] bump version --- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/code/ulab.c b/code/ulab.c index 427230fb..dd67dac3 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.4 +#define ULAB_VERSION 6.0.5 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 0786d49f..1436566d 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,4 +1,10 @@ +Sun, 21 Jan 2023 + +version 6.0.5 + + fix ones()/zeros() method when the amount of memory to allocate overflows + Sun, 15 Jan 2023 version 6.0.4 From 578ca6670d0ca28aca7c5427adee43ed3e7eb862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 23 Jan 2023 21:52:37 +0100 Subject: [PATCH 046/113] raise exception in arange, if step size is 0 (#582) --- code/numpy/create.c | 6 ++++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 5 +++++ tests/2d/numpy/arange.py | 8 +++++++- tests/2d/numpy/arange.py.exp | 1 + 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index 29e25ff0..9ca5f26a 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -152,6 +152,12 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg if(args[3].u_obj != mp_const_none) { dtype = (uint8_t)mp_obj_get_int(args[3].u_obj); } + + // bail out, if the range cannot be constructed + if(step == MICROPY_FLOAT_CONST(0.0)) { + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); + } + ndarray_obj_t *ndarray; if((stop - start)/step < 0) { ndarray = ndarray_new_linear_array(0, dtype); diff --git a/code/ulab.c b/code/ulab.c index dd67dac3..466920df 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.5 +#define ULAB_VERSION 6.0.6 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 1436566d..ae3f161e 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,4 +1,9 @@ +Sun, 21 Jan 2023 + +version 6.0.6 + raise proper exception in arange + Sun, 21 Jan 2023 version 6.0.5 diff --git a/tests/2d/numpy/arange.py b/tests/2d/numpy/arange.py index 91d21fe5..0d222192 100644 --- a/tests/2d/numpy/arange.py +++ b/tests/2d/numpy/arange.py @@ -8,4 +8,10 @@ for dtype in dtypes: print(np.arange(10, dtype=dtype)) print(np.arange(2, 10, dtype=dtype)) - print(np.arange(2, 10, 3, dtype=dtype)) \ No newline at end of file + print(np.arange(2, 10, 3, dtype=dtype)) + +# test for ZeroDivisionError exception +try: + np.arange(0, 10, 0) +except ZeroDivisionError as e: + print('ZeroDivisionError: ', e) \ No newline at end of file diff --git a/tests/2d/numpy/arange.py.exp b/tests/2d/numpy/arange.py.exp index 894e0085..a004df02 100644 --- a/tests/2d/numpy/arange.py.exp +++ b/tests/2d/numpy/arange.py.exp @@ -13,3 +13,4 @@ array([2, 5, 8], dtype=int16) array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64) array([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64) array([2.0, 5.0, 8.0], dtype=float64) +ZeroDivisionError: divide by zero From f2dd2230c4fdf1aa5c7a160782efdde18e8204bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 23 Jan 2023 21:53:41 +0100 Subject: [PATCH 047/113] fix sorting of empty arrays in sort_complex (#583) --- code/numpy/carray/carray.c | 8 ++++++-- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ tests/2d/complex/sort_complex.py | 2 ++ tests/2d/complex/sort_complex.py.exp | 6 ++++++ 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index a5f8a2b1..44e9ab12 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -191,8 +191,12 @@ mp_obj_t carray_sort_complex(mp_obj_t _source) { } ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(source, NDARRAY_COMPLEX); - mp_float_t *array = (mp_float_t *)ndarray->array; - carray_sort_complex_(array, ndarray->len); + + if(ndarray->len != 0) { + mp_float_t *array = (mp_float_t *)ndarray->array; + carray_sort_complex_(array, ndarray->len); + } + return MP_OBJ_FROM_PTR(ndarray); } diff --git a/code/ulab.c b/code/ulab.c index 466920df..1bd83ce1 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.6 +#define ULAB_VERSION 6.0.7 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index ae3f161e..dc829940 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -6,6 +6,12 @@ version 6.0.6 Sun, 21 Jan 2023 +version 6.0.7 + + treat empty arrays in sort_complex correctly + +Sun, 21 Jan 2023 + version 6.0.5 fix ones()/zeros() method when the amount of memory to allocate overflows diff --git a/tests/2d/complex/sort_complex.py b/tests/2d/complex/sort_complex.py index 1ac1edc1..a4154730 100644 --- a/tests/2d/complex/sort_complex.py +++ b/tests/2d/complex/sort_complex.py @@ -7,6 +7,8 @@ for dtype in dtypes: print(np.sort_complex(np.array(range(5, 0, -1), dtype=dtype))) + # these should all return an empty complex array + print(np.sort_complex(np.array(range(5, 0, 1), dtype=dtype))) print() n = 6 diff --git a/tests/2d/complex/sort_complex.py.exp b/tests/2d/complex/sort_complex.py.exp index 9026e4ae..fd2f56d0 100644 --- a/tests/2d/complex/sort_complex.py.exp +++ b/tests/2d/complex/sort_complex.py.exp @@ -1,9 +1,15 @@ array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) +array([], dtype=complex) array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) +array([], dtype=complex) array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) +array([], dtype=complex) array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) +array([], dtype=complex) array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) +array([], dtype=complex) array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) +array([], dtype=complex) array([1.0+1.0j, 1.0+2.0j, 1.0+3.0j, 1.0+4.0j, 1.0+5.0j, 1.0+6.0j], dtype=complex) array([1.0+0.0j, 1.0+1.0j, 1.0+2.0j, 1.0+3.0j, 1.0+4.0j, 1.0+5.0j], dtype=complex) From 8585407df9b7a25aba19b6a6ac7136f577f3d1e6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 4 Mar 2023 18:28:01 +1100 Subject: [PATCH 048/113] Use enum instead of const for float consts with repr C (#591) Older compilers (eg for esp8266) cannot see that "const uint32_t" is a true constant at compile time, so use an assigned enum instead. Don't do this for object representation D because that requires a 64-bit integer. Signed-off-by: Damien George --- code/ndarray.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/ndarray.h b/code/ndarray.h index 3daf5b83..6d5cf1f3 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -65,7 +65,9 @@ typedef struct _mp_obj_float_t { // See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST. #define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \ - const uint32_t id = (((((uint32_t)hex32) & ~3) | 2) + 0x80800000) + enum { \ + id = (((((uint32_t)hex32) & ~3) | 2) + 0x80800000) \ + } #define ULAB_REFERENCE_FLOAT_CONST(id) ((mp_obj_t)(id)) From 5fa9b707660177aff615a7dc0bf508a98e9e0f07 Mon Sep 17 00:00:00 2001 From: HugoNumworks <67104016+HugoNumworks@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:43:09 +0200 Subject: [PATCH 049/113] Add missing constant constraints (#594) --- code/ndarray.h | 16 +++++++++++++++- code/ndarray_properties.h | 17 +++++++++++++++++ code/numpy/numerical.c | 2 ++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/code/ndarray.h b/code/ndarray.h index 6d5cf1f3..4564f772 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -230,11 +230,25 @@ mp_obj_t ndarray_flatten(size_t , const mp_obj_t *, mp_map_t *); MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_flatten_obj); #endif +#if NDARRAY_HAS_DTYPE mp_obj_t ndarray_dtype(mp_obj_t ); +#endif + +#if NDARRAY_HAS_ITEMSIZE mp_obj_t ndarray_itemsize(mp_obj_t ); +#endif + +#if NDARRAY_HAS_SIZE mp_obj_t ndarray_size(mp_obj_t ); +#endif + +#if NDARRAY_HAS_SHAPE mp_obj_t ndarray_shape(mp_obj_t ); +#endif + +#if NDARRAY_HAS_STRIDES mp_obj_t ndarray_strides(mp_obj_t ); +#endif #if NDARRAY_HAS_RESHAPE mp_obj_t ndarray_reshape_core(mp_obj_t , mp_obj_t , bool ); @@ -247,7 +261,7 @@ mp_obj_t ndarray_tobytes(mp_obj_t ); MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tobytes_obj); #endif -#if NDARRAY_HAS_TOBYTES +#if NDARRAY_HAS_TOLIST mp_obj_t ndarray_tolist(mp_obj_t ); MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tolist_obj); #endif diff --git a/code/ndarray_properties.h b/code/ndarray_properties.h index 28da7c06..a074ec1f 100644 --- a/code/ndarray_properties.h +++ b/code/ndarray_properties.h @@ -92,12 +92,29 @@ STATIC const mp_obj_property_t ndarray_strides_obj = { void ndarray_properties_attr(mp_obj_t , qstr , mp_obj_t *); +#if NDARRAY_HAS_DTYPE MP_DEFINE_CONST_FUN_OBJ_1(ndarray_dtype_obj, ndarray_dtype); +#endif + +#if NDARRAY_HAS_FLATITER MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_new); +#endif + +#if NDARRAY_HAS_ITEMSIZE MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize); +#endif + +#if NDARRAY_HAS_SHAPE MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape); +#endif + +#if NDARRAY_HAS_SIZE MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size); +#endif + +#if NDARRAY_HAS_STRIDES MP_DEFINE_CONST_FUN_OBJ_1(ndarray_strides_obj, ndarray_strides); +#endif #endif /* CIRCUITPY */ diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index f92162a1..01bd0bd9 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -569,9 +569,11 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m mp_raise_TypeError(translate("axis must be None, or an integer")); } +#if ULAB_NUMPY_HAS_ALL | ULAB_NUMPY_HAS_ANY if((optype == NUMERICAL_ALL) || (optype == NUMERICAL_ANY)) { return numerical_all_any(oin, axis, optype); } +#endif if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || mp_obj_is_type(oin, &mp_type_range)) { switch(optype) { From 4407f8c404eacca2333b759b823d926406596bc9 Mon Sep 17 00:00:00 2001 From: yamamotomas <103046478+yamamotomas@users.noreply.github.com> Date: Fri, 21 Apr 2023 00:46:16 +0900 Subject: [PATCH 050/113] Add a build script for Raspberry Pi Pico W (#598) * make a build script for pico W * add build scipt for pico W --- README.md | 2 +- build/rp2w.sh | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 build/rp2w.sh diff --git a/README.md b/README.md index 99c47ad3..fa3831a4 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,7 @@ This file can be found in `$BUILD_DIR/micropython/ports/esp32/boards/TINYPICO/`. ### RP2-based boards -RP2 firmware can be compiled either by downloading and running the single [build script](https://github.com/v923z/micropython-ulab/blob/master/build/rp2.sh), or executing the commands below. +RP2 firmware can be compiled either by downloading and running the single [build script](https://github.com/v923z/micropython-ulab/blob/master/build/rp2.sh)/[build script for Pico W](https://github.com/v923z/micropython-ulab/blob/master/build/rp2w.sh), or executing the commands below. First, clone `micropython`: diff --git a/build/rp2w.sh b/build/rp2w.sh new file mode 100644 index 00000000..a06e62ce --- /dev/null +++ b/build/rp2w.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +export BOARD=PICO_W +export BUILD_DIR=$(pwd) +export MPY_DIR=$BUILD_DIR/micropython +export ULAB_DIR=$BUILD_DIR/../code + +if [ ! -d $ULAB_DIR ]; then + printf "Cloning ulab\n" + ULAB_DIR=$BUILD_DIR/ulab/code + git clone https://github.com/v923z/micropython-ulab.git ulab +fi + +if [ ! -d $MPY_DIR ]; then + printf "Cloning MicroPython\n" + git clone https://github.com/micropython/micropython.git micropython +fi + +cd $MPY_DIR +git submodule update --init +cd ./mpy-cross && make # build cross-compiler (required) + +cd $MPY_DIR/ports/rp2 +make BOARD=$BOARD clean +make USER_C_MODULES=$ULAB_DIR/micropython.cmake BOARD=$BOARD From 47ad73ab57ded2bea7685b9ef7ba23c275a6d788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 23 Apr 2023 21:14:59 +0200 Subject: [PATCH 051/113] Floordiv (#593) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * implement floor division * fix 3D, 4D loops * add missing array declaration in 3D, and 4D * Add test cases for floor division and fix it for ints (#599) * Add test cases for floor division * Fix define name in comment * Fix floor division of ints --------- Co-authored-by: Maciej Sokołowski --- code/ndarray.c | 6 + code/ndarray_operators.c | 98 +++++++++++- code/ndarray_operators.h | 263 +++++++++++++++++++++++++++++++- code/ulab.h | 4 + tests/2d/numpy/operators.py | 12 ++ tests/2d/numpy/operators.py.exp | 11 ++ 6 files changed, 392 insertions(+), 2 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index bade22cf..ae219d28 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1936,6 +1936,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { return ndarray_binary_power(lhs, rhs, ndim, shape, lstrides, rstrides); break; #endif + #if NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE + case MP_BINARY_OP_FLOOR_DIVIDE: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); + return ndarray_binary_floor_divide(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif default: return MP_OBJ_NULL; // op not supported break; diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index b941daf5..dd2b24d6 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -673,6 +673,102 @@ mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } #endif /* NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE */ +#if NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE +mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + FLOOR_DIVIDE_LOOP_UINT(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + FLOOR_DIVIDE_LOOP_UINT(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + FLOOR_DIVIDE_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + FLOOR_DIVIDE_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + FLOOR_DIVIDE_LOOP_UINT(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + FLOOR_DIVIDE_LOOP(results, uint16_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + FLOOR_DIVIDE_LOOP_UINT(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + FLOOR_DIVIDE_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + if(rhs->dtype == NDARRAY_UINT8) { + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + FLOOR_DIVIDE_LOOP_FLOAT(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } + + return MP_OBJ_FROM_PTR(results); + +} +#endif /* NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE */ + #if NDARRAY_HAS_BINARY_OP_POWER mp_obj_t ndarray_binary_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { @@ -812,7 +908,7 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t } return MP_OBJ_FROM_PTR(lhs); } -#endif /* NDARRAY_HAS_INPLACE_DIVIDE */ +#endif /* NDARRAY_HAS_INPLACE_TRUE_DIVIDE */ #if NDARRAY_HAS_INPLACE_POWER mp_obj_t ndarray_inplace_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { diff --git a/code/ndarray_operators.h b/code/ndarray_operators.h index 7849e030..b8f080fa 100644 --- a/code/ndarray_operators.h +++ b/code/ndarray_operators.h @@ -5,7 +5,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020-2021 Zoltán Vörös + * Copyright (c) 2020-2023 Zoltán Vörös */ #include "ndarray.h" @@ -17,6 +17,7 @@ mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t ); mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *); @@ -275,3 +276,263 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *); } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ }) #endif /* ULAB_MAX_DIMS == 4 */ + +#define FLOOR_DIVIDE_UINT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\ +({\ + size_t l = 0;\ + do {\ + *(array)++ = *((type_left *)(larray)) / *((type_right *)(rarray));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + +#define FLOOR_DIVIDE1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\ +({\ + size_t l = 0;\ + int16_t num;\ + int16_t denom = (int16_t)*((type_right *)(rarray));\ + do {\ + num = (int16_t)*((type_left *)(larray));\ + if(num >= 0) {\ + if(denom < 0) {\ + num += -denom - 1;\ + }\ + } else {\ + if(denom >= 0) {\ + num += -denom + 1;\ + }\ + }\ + *(array)++ = num / denom;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + +#define FLOOR_DIVIDE_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\ +({\ + size_t l = 0;\ + do {\ + *(array)++ = MICROPY_FLOAT_C_FUN(floor)(*((type_left *)(larray)) / *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + +#if ULAB_MAX_DIMS == 1 +#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ +} while(0) + +#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ +} while(0) + +#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) + +#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) + +#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) + +#endif /* ULAB_MAX_DIMS == 4 */ diff --git a/code/ulab.h b/code/ulab.h index f3fdeb45..eedddc5e 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -101,6 +101,10 @@ #define NDARRAY_HAS_BINARY_OP_EQUAL (1) #endif +#ifndef NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE +#define NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE (1) +#endif + #ifndef NDARRAY_HAS_BINARY_OP_LESS #define NDARRAY_HAS_BINARY_OP_LESS (1) #endif diff --git a/tests/2d/numpy/operators.py b/tests/2d/numpy/operators.py index acc316b1..42d00432 100644 --- a/tests/2d/numpy/operators.py +++ b/tests/2d/numpy/operators.py @@ -81,6 +81,10 @@ print(np.array([1,2,3], dtype=np.float) / np.array([4,5,6], dtype=np.uint16)) print(np.array([1,2,3], dtype=np.float) / np.array([4,5,6], dtype=np.int16)) +print(np.array([10,20,30], dtype=np.float) // np.array([4,5,6], dtype=np.float)) +print(np.array([10,20,30], dtype=np.float) // np.array([4,5,6], dtype=np.uint16)) +print(np.array([10,20,30], dtype=np.float) // np.array([4,5,6], dtype=np.int16)) + print(np.array([1,2,3], dtype=np.float) - 4) print(np.array([1,2,3], dtype=np.float) - 4.0) print(np.array([1,2,3], dtype=np.float) + 4) @@ -93,6 +97,14 @@ print(np.array([1,2,3], dtype=np.float) / 4) print(np.array([1,2,3], dtype=np.float) / 4.0) +print(np.array([10,20,30], dtype=np.float) // 4) +print(np.array([10,20,30], dtype=np.float) // 4.0) +print(np.array([10,20,30], dtype=np.int8) // 4) +print(np.array([10,20,30], dtype=np.int8) // 4.0) +print(np.array([10,20,30], dtype=np.uint16) // 4) +print(np.array([10,20,30], dtype=np.uint16) // 4.0) +print(np.array([10,20,30], dtype=np.int16) // 4) +print(np.array([10,20,30], dtype=np.int16) // 4.0) a = np.array([1,2,3], dtype=np.float) a -= np.array([4,5,6], dtype=np.float) diff --git a/tests/2d/numpy/operators.py.exp b/tests/2d/numpy/operators.py.exp index 9749509d..319c2073 100644 --- a/tests/2d/numpy/operators.py.exp +++ b/tests/2d/numpy/operators.py.exp @@ -60,6 +60,9 @@ array([1.0, 32.0, 729.0], dtype=float64) array([0.25, 0.4, 0.5], dtype=float64) array([0.25, 0.4, 0.5], dtype=float64) array([0.25, 0.4, 0.5], dtype=float64) +array([2.0, 4.0, 5.0], dtype=float64) +array([2.0, 4.0, 5.0], dtype=float64) +array([2.0, 4.0, 5.0], dtype=float64) array([-3.0, -2.0, -1.0], dtype=float64) array([-3.0, -2.0, -1.0], dtype=float64) array([5.0, 6.0, 7.0], dtype=float64) @@ -70,6 +73,14 @@ array([1.0, 16.0, 81.0], dtype=float64) array([1.0, 16.0, 81.0], dtype=float64) array([0.25, 0.5, 0.75], dtype=float64) array([0.25, 0.5, 0.75], dtype=float64) +array([2.0, 5.0, 7.0], dtype=float64) +array([2.0, 5.0, 7.0], dtype=float64) +array([2, 5, 7], dtype=int8) +array([2.0, 5.0, 7.0], dtype=float64) +array([2, 5, 7], dtype=uint16) +array([2.0, 5.0, 7.0], dtype=float64) +array([2, 5, 7], dtype=int16) +array([2.0, 5.0, 7.0], dtype=float64) array([-3.0, -3.0, -3.0], dtype=float64) array([-3.0, -3.0, -3.0], dtype=float64) array([-3.0, -3.0, -3.0], dtype=float64) From afc8e4e165bb2c90c9614776c04df51c30eb1145 Mon Sep 17 00:00:00 2001 From: HugoNumworks <67104016+HugoNumworks@users.noreply.github.com> Date: Mon, 24 Apr 2023 14:58:38 +0200 Subject: [PATCH 052/113] Fix unused function compare_equal_helper warning (#603) --- code/numpy/compare.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/numpy/compare.c b/code/numpy/compare.c index b03d3e56..54647d6f 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -125,6 +125,7 @@ static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) { return mp_const_none; // we should never reach this point } +#if ULAB_NUMPY_HAS_EQUAL | ULAB_NUMPY_HAS_NOTEQUAL static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) { // scalar comparisons should return a single object of mp_obj_t type mp_obj_t result = compare_function(x1, x2, comptype); @@ -136,6 +137,7 @@ static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) } return result; } +#endif #if ULAB_NUMPY_HAS_CLIP From ad1a1c54aaf940cce274f3679273489a1ff675fd Mon Sep 17 00:00:00 2001 From: FelixNumworks <99270678+FelixNumworks@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:46:48 +0200 Subject: [PATCH 053/113] Fix create_arange empty range (#604) * Fix create_arange crashing when stop == start This is due to len being equal to zero, which made (len - 1) equal to MAX_UINT16 * trailing whitespaces --- code/numpy/create.c | 6 +++--- code/ulab.c | 2 +- tests/2d/numpy/arange.py | 4 +++- tests/2d/numpy/arange.py.exp | 5 +++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index 9ca5f26a..281f029c 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -159,7 +159,7 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg } ndarray_obj_t *ndarray; - if((stop - start)/step < 0) { + if((stop - start)/step <= 0) { ndarray = ndarray_new_linear_array(0, dtype); } else { size_t len = (size_t)(MICROPY_FLOAT_C_FUN(ceil)((stop - start) / step)); @@ -254,14 +254,14 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); mp_obj_tuple_t *ndarrays = MP_OBJ_TO_PTR(args[0].u_obj); - // first check, whether + // first check, whether for(uint8_t i = 0; i < ndarrays->len; i++) { if(!mp_obj_is_type(ndarrays->items[i], &ulab_ndarray_type)) { mp_raise_ValueError(translate("only ndarrays can be concatenated")); } } - + // first check, whether the arrays are compatible ndarray_obj_t *_ndarray = MP_OBJ_TO_PTR(ndarrays->items[0]); uint8_t dtype = _ndarray->dtype; diff --git a/code/ulab.c b/code/ulab.c index 1bd83ce1..e10e33b1 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.7 +#define ULAB_VERSION 6.0.8 #define xstr(s) str(s) #define str(s) #s diff --git a/tests/2d/numpy/arange.py b/tests/2d/numpy/arange.py index 0d222192..065a8332 100644 --- a/tests/2d/numpy/arange.py +++ b/tests/2d/numpy/arange.py @@ -9,9 +9,11 @@ print(np.arange(10, dtype=dtype)) print(np.arange(2, 10, dtype=dtype)) print(np.arange(2, 10, 3, dtype=dtype)) + # test empty range + print(np.arange(0, 0, dtype=dtype)) # test for ZeroDivisionError exception try: np.arange(0, 10, 0) except ZeroDivisionError as e: - print('ZeroDivisionError: ', e) \ No newline at end of file + print('ZeroDivisionError: ', e) diff --git a/tests/2d/numpy/arange.py.exp b/tests/2d/numpy/arange.py.exp index a004df02..0e73c648 100644 --- a/tests/2d/numpy/arange.py.exp +++ b/tests/2d/numpy/arange.py.exp @@ -1,16 +1,21 @@ array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8) array([2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8) array([2, 5, 8], dtype=uint8) +array([], dtype=uint8) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int8) array([2, 3, 4, 5, 6, 7, 8, 9], dtype=int8) array([2, 5, 8], dtype=int8) +array([], dtype=int8) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint16) array([2, 3, 4, 5, 6, 7, 8, 9], dtype=uint16) array([2, 5, 8], dtype=uint16) +array([], dtype=uint16) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int16) array([2, 3, 4, 5, 6, 7, 8, 9], dtype=int16) array([2, 5, 8], dtype=int16) +array([], dtype=int16) array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64) array([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64) array([2.0, 5.0, 8.0], dtype=float64) +array([], dtype=float64) ZeroDivisionError: divide by zero From 8c3e1058d441c2d075febadc8b7993b03e152bf9 Mon Sep 17 00:00:00 2001 From: FelixNumworks <99270678+FelixNumworks@users.noreply.github.com> Date: Thu, 27 Apr 2023 13:26:04 +0200 Subject: [PATCH 054/113] Fix arange crashing when start, stop or step is nan (#605) --- code/numpy/create.c | 6 ++++-- code/ulab.c | 2 +- tests/2d/numpy/arange.py | 6 ++++++ tests/2d/numpy/arange.py.exp | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index 281f029c..0f5bce00 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -158,6 +158,10 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); } + if(isnan(start) || isnan(stop) || isnan(step)) { + mp_raise_ValueError(translate("arange: cannot compute length")); + } + ndarray_obj_t *ndarray; if((stop - start)/step <= 0) { ndarray = ndarray_new_linear_array(0, dtype); @@ -254,8 +258,6 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); mp_obj_tuple_t *ndarrays = MP_OBJ_TO_PTR(args[0].u_obj); - // first check, whether - for(uint8_t i = 0; i < ndarrays->len; i++) { if(!mp_obj_is_type(ndarrays->items[i], &ulab_ndarray_type)) { mp_raise_ValueError(translate("only ndarrays can be concatenated")); diff --git a/code/ulab.c b/code/ulab.c index e10e33b1..6c40db97 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.8 +#define ULAB_VERSION 6.0.9 #define xstr(s) str(s) #define str(s) #s diff --git a/tests/2d/numpy/arange.py b/tests/2d/numpy/arange.py index 065a8332..79cd0b80 100644 --- a/tests/2d/numpy/arange.py +++ b/tests/2d/numpy/arange.py @@ -17,3 +17,9 @@ np.arange(0, 10, 0) except ZeroDivisionError as e: print('ZeroDivisionError: ', e) + +# test for NAN length exception +try: + np.arange(0, np.nan) +except ValueError as e: + print('ValueError: ', e) diff --git a/tests/2d/numpy/arange.py.exp b/tests/2d/numpy/arange.py.exp index 0e73c648..b0b6d29b 100644 --- a/tests/2d/numpy/arange.py.exp +++ b/tests/2d/numpy/arange.py.exp @@ -19,3 +19,4 @@ array([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64) array([2.0, 5.0, 8.0], dtype=float64) array([], dtype=float64) ZeroDivisionError: divide by zero +ValueError: arange: cannot compute length From 412b13fb1cf2a12b0c096fd1a987fa8cade02a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 6 May 2023 20:40:06 +0200 Subject: [PATCH 055/113] fix binary division (#611) --- code/ndarray_operators.c | 2 +- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index dd2b24d6..3983dabc 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -652,7 +652,7 @@ mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } else if(rhs->dtype == NDARRAY_INT16) { BINARY_LOOP(results, mp_float_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, /); } else if(rhs->dtype == NDARRAY_FLOAT) { - BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, /); + BINARY_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, /); } } else if(lhs->dtype == NDARRAY_FLOAT) { if(rhs->dtype == NDARRAY_UINT8) { diff --git a/code/ulab.c b/code/ulab.c index 6c40db97..02b7cb3a 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.9 +#define ULAB_VERSION 6.0.10 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index dc829940..efe55e7b 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sat, 6 May 2023 + +version 6.0.10 + + fix binary division + Sun, 21 Jan 2023 version 6.0.6 From beda4c19f8ec1475d0f3bb76a29d27c41c893c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 7 May 2023 00:21:16 +0200 Subject: [PATCH 056/113] implement unknown shape dimension in reshape (#612) --- code/ndarray.c | 49 +++++++++++++++++++++++++++++++++-------- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 +++++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index ae219d28..f51d1bd0 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -2084,24 +2084,51 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_transpose_obj, ndarray_transpose); #if NDARRAY_HAS_RESHAPE mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { ndarray_obj_t *source = MP_OBJ_TO_PTR(oin); - if(!mp_obj_is_type(_shape, &mp_type_tuple)) { - mp_raise_TypeError(translate("shape must be a tuple")); + if(!mp_obj_is_type(_shape, &mp_type_tuple) && !mp_obj_is_int(_shape)) { + mp_raise_TypeError(translate("shape must be integer or tuple of integers")); + } + + mp_obj_tuple_t *shape; + + if(mp_obj_is_int(_shape)) { + mp_obj_t *items = m_new(mp_obj_t, 1); + items[0] = _shape; + shape = mp_obj_new_tuple(1, items); + } else { + shape = MP_OBJ_TO_PTR(_shape); } - mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(_shape); if(shape->len > ULAB_MAX_DIMS) { mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); } - size_t *new_shape = m_new0(size_t, ULAB_MAX_DIMS); size_t new_length = 1; - for(uint8_t i=0; i < shape->len; i++) { - new_shape[ULAB_MAX_DIMS - i - 1] = mp_obj_get_int(shape->items[shape->len - i - 1]); - new_length *= new_shape[ULAB_MAX_DIMS - i - 1]; + size_t *new_shape = m_new0(size_t, ULAB_MAX_DIMS); + uint8_t unknown_dim = 0; + uint8_t unknown_index = 0; + + for(uint8_t i = 0; i < shape->len; i++) { + int32_t ax_len = mp_obj_get_int(shape->items[shape->len - i - 1]); + if(ax_len >= 0) { + new_shape[ULAB_MAX_DIMS - i - 1] = (size_t)ax_len; + new_length *= new_shape[ULAB_MAX_DIMS - i - 1]; + } else { + unknown_dim++; + unknown_index = ULAB_MAX_DIMS - i - 1; + } + } + + if(unknown_dim > 1) { + mp_raise_ValueError(translate("can only specify one unknown dimension")); + } else if(unknown_dim == 1) { + new_shape[unknown_index] = source->len / new_length; + new_length = source->len; } + if(source->len != new_length) { - mp_raise_ValueError(translate("input and output shapes are not compatible")); + mp_raise_ValueError(translate("cannot reshape array")); } + ndarray_obj_t *ndarray; if(ndarray_is_dense(source)) { int32_t *new_strides = strides_from_shape(new_shape, source->dtype); @@ -2118,7 +2145,11 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { if(inplace) { mp_raise_ValueError(translate("cannot assign new shape")); } - ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); + if(mp_obj_is_type(_shape, &mp_type_tuple)) { + ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); + } else { + ndarray = ndarray_new_linear_array(source->len, source->dtype); + } ndarray_copy_array(source, ndarray, 0); } return MP_OBJ_FROM_PTR(ndarray); diff --git a/code/ulab.c b/code/ulab.c index 02b7cb3a..a7842569 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.10 +#define ULAB_VERSION 6.0.11 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index efe55e7b..910055ff 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,5 +1,11 @@ Sat, 6 May 2023 +version 6.0.11 + + .reshape can now interpret unknown shape dimension + +Sat, 6 May 2023 + version 6.0.10 fix binary division From ac2e9954edc185ec212e437e73e4b5e4290de35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 7 May 2023 12:09:02 +0200 Subject: [PATCH 057/113] ndarray_from_mp_obj correctly treats Booleans (#613) --- code/ndarray.c | 6 ++++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index f51d1bd0..4b340b23 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1730,6 +1730,12 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { ndarray = ndarray_new_linear_array(1, NDARRAY_FLOAT); mp_float_t *array = (mp_float_t *)ndarray->array; array[0] = mp_obj_get_float(obj); + } else if(mp_obj_is_bool(obj)) { + ndarray = ndarray_new_linear_array(1, NDARRAY_BOOLEAN); + uint8_t *array = (uint8_t *)ndarray->array; + if(obj == mp_const_true) { + *array = 1; + } } else if(mp_obj_is_type(obj, &ulab_ndarray_type)){ return MP_OBJ_TO_PTR(obj); } diff --git a/code/ulab.c b/code/ulab.c index a7842569..a389831a 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.11 +#define ULAB_VERSION 6.0.12 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 910055ff..f4aba8b6 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sun, 7 May 2023 + +version 6.0.12 + + ndarray_from_mp_obj correctly treats Boolean arguments + Sat, 6 May 2023 version 6.0.11 From 1150554ad5835e614095b83985e0c577f47d791f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 15 May 2023 18:00:59 -0500 Subject: [PATCH 058/113] ulab.numpy: implement sinc for creating audio filters This is useful for generating FIR filters using code snippets generated at https://fiiir.com/ (at least those with a rectangular window type; other window types need additional functions but we can revisit it later if needed) I think this will come in handy for folks who are using the advanced features of our audio synthesizer module, synthio. e.g., the following block now gives highly similar results on ulab or numpy: ```py try: import numpy as np except: from ulab import numpy as np # Example code, computes the coefficients of a low-pass windowed-sinc filter. # Configuration. fS = 48000 # Sampling rate. fL = 4000 # Cutoff frequency. N = 23 # Filter length, must be odd. # Compute sinc filter. h = np.sinc(2 * fL / fS * (np.arange(N) - (N - 1) / 2)) # Normalize to get unity gain. h /= np.sum(h) # Applying the filter to a signal s can be as simple as writing # s = np.convolve(s, h) --- code/numpy/numpy.c | 3 +++ code/numpy/vector.c | 21 +++++++++++++++++++++ code/numpy/vector.h | 1 + code/ulab.h | 4 ++++ 4 files changed, 29 insertions(+) diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index a53a32cf..7c35f0e1 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -340,6 +340,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_SIN { MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&vector_sin_obj) }, #endif + #if ULAB_NUMPY_HAS_SINC + { MP_ROM_QSTR(MP_QSTR_sinc), MP_ROM_PTR(&vector_sinc_obj) }, + #endif #if ULAB_NUMPY_HAS_SINH { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&vector_sinh_obj) }, #endif diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 01e209ea..914ab014 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -570,6 +570,27 @@ MATH_FUN_1(sin, sin); MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); #endif +#if ULAB_NUMPY_HAS_SINC +//| def sin(a: _ArrayLike) -> ulab.numpy.ndarray: +//| """Computes the sine function""" +//| ... +//| + +static mp_float_t ulab_sinc1(mp_float_t x) { + if (fpclassify(x) == FP_ZERO) { + return MICROPY_FLOAT_CONST(1.); + } + x *= MP_PI; + return MICROPY_FLOAT_C_FUN(sin)(x) / x; +} + +static mp_obj_t vector_sinc(mp_obj_t x_obj) { + return vector_generic_vector(x_obj, ulab_sinc1); +} + +MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc); +#endif + #if ULAB_NUMPY_HAS_SINH //| def sinh(a: _ArrayLike) -> ulab.numpy.ndarray: //| """Computes the hyperbolic sine""" diff --git a/code/numpy/vector.h b/code/numpy/vector.h index ea38b0fd..c688e95c 100644 --- a/code/numpy/vector.h +++ b/code/numpy/vector.h @@ -39,6 +39,7 @@ MP_DECLARE_CONST_FUN_OBJ_1(vector_log10_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_log2_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_radians_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_sin_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_sinc_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_sinh_obj); #if ULAB_SUPPORTS_COMPLEX MP_DECLARE_CONST_FUN_OBJ_KW(vector_sqrt_obj); diff --git a/code/ulab.h b/code/ulab.h index eedddc5e..1d493ab3 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -616,6 +616,10 @@ #define ULAB_NUMPY_HAS_SIN (1) #endif +#ifndef ULAB_NUMPY_HAS_SINC +#define ULAB_NUMPY_HAS_SINC (1) +#endif + #ifndef ULAB_NUMPY_HAS_SINH #define ULAB_NUMPY_HAS_SINH (1) #endif From 6000743c456c165f1a68939fb03d6a1250928116 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 16 May 2023 07:32:25 -0500 Subject: [PATCH 059/113] fix docstring of sinc --- code/numpy/vector.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 914ab014..cc44904b 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -571,8 +571,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); #endif #if ULAB_NUMPY_HAS_SINC -//| def sin(a: _ArrayLike) -> ulab.numpy.ndarray: -//| """Computes the sine function""" +//| def sinc(a: _ArrayLike) -> ulab.numpy.ndarray: +//| """Computes the normalized sinc function""" //| ... //| From f3e6e1c6d2308cc75e16e351e36f337bb3c93df9 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 16 May 2023 07:33:20 -0500 Subject: [PATCH 060/113] include new requirement of circuitpython build --- requirements_cp_dev.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements_cp_dev.txt b/requirements_cp_dev.txt index 06f35fbd..8a4cdba3 100644 --- a/requirements_cp_dev.txt +++ b/requirements_cp_dev.txt @@ -15,5 +15,4 @@ myst-parser # For stubs and annotations adafruit-circuitpython-typing - - +build From 3e996d9bd93005edbbb9763c4a0a525fe52dec3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 17 May 2023 20:45:07 +0200 Subject: [PATCH 061/113] fix ndarray subscription method (#619) --- code/ndarray.c | 3 +++ code/ulab.c | 2 +- docs/ulab-change-log.md | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index 4b340b23..88186759 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1438,6 +1438,9 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra } mp_obj_t ndarray_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if(value == MP_OBJ_NULL) { + mp_raise_ValueError(translate("cannot delete array elements")); + } ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); if (value == MP_OBJ_SENTINEL) { // return value(s) diff --git a/code/ulab.c b/code/ulab.c index a389831a..8482233b 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.0.12 +#define ULAB_VERSION 6.1.1 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index f4aba8b6..08cd51e7 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,15 @@ +Wed, 17 May 2023 + +version 6.1.1 + + fix ndarray subscription, when value is NULL + +Tue, 16 May 2023 + +version 6.1.0 + + add sinc function + Sun, 7 May 2023 version 6.0.12 From 73dbbf79bb768dd6894c3343157bb13ccb7f8108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 28 May 2023 17:33:24 +0200 Subject: [PATCH 062/113] add the out keyword argument to universal functions (#621) * add optional out keyword argument to math functions * fix the keyword handling in sqrt * run micropython build on ubuntu 20.04 instead of latest * fix unused variable error in vector_generic_vector --- .github/workflows/build.yml | 2 +- code/numpy/vector.c | 343 ++++++++++++++++++++++++++++++------ code/numpy/vector.h | 224 ++++++++++++++++++----- code/ulab.c | 2 +- code/ulab.h | 6 + 5 files changed, 479 insertions(+), 98 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d6587f5b..e5689025 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: os: - - ubuntu-latest + - ubuntu-20.04 - macOS-latest dims: [1, 2, 3, 4] runs-on: ${{ matrix.os }} diff --git a/code/numpy/vector.c b/code/numpy/vector.c index cc44904b..4dffa30c 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -6,8 +6,8 @@ * * The MIT License (MIT) * - * Copyright (c) 2019-2021 Zoltán Vörös - * 2020 Jeff Epler for Adafruit Industries + * Copyright (c) 2019-2023 Zoltán Vörös + * 2020-2023 Jeff Epler for Adafruit Industries * 2020 Scott Shawcroft for Adafruit Industries * 2020 Taku Fukada */ @@ -32,6 +32,85 @@ //| much more efficient than expressing the same operation as a Python loop.""" //| + +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, mp_float_t (*f)(mp_float_t)) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + // this keyword argument is not used; it's only here, so that functions that + // support the complex dtype can call vector_generic_vector directly + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t o_in = args[0].u_obj; + + // Return a single value, if o_in is not iterable + if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) { + return mp_obj_new_float(f(mp_obj_get_float(o_in))); + } + mp_obj_t out = args[1].u_obj; + + ndarray_obj_t *target = NULL; + ndarray_obj_t *source = NULL; + + if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { + source = MP_OBJ_TO_PTR(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype) + if(out == mp_const_none) { + target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + } else { + if(!mp_obj_is_type(out, &ulab_ndarray_type)) { + mp_raise_ValueError(translate("out must be an ndarray")); + } + target = MP_OBJ_TO_PTR(out); + if(target->dtype != NDARRAY_FLOAT) { + mp_raise_ValueError(translate("out must be of float dtype")); + } + if(target->ndim != source->ndim) { + mp_raise_ValueError(translate("input and output dimensions differ")); + } + for(uint8_t d = 0; d < target->ndim; d++) { + if(target->shape[ULAB_MAX_DIMS - 1 - d] != source->shape[ULAB_MAX_DIMS - 1 - d]) { + mp_raise_ValueError(translate("input and output shapes differ")); + } + } + } + mp_float_t *tarray = (mp_float_t *)target->array; + int32_t *tstrides = m_new(int32_t, ULAB_MAX_DIMS); + for(uint8_t d = 0; d < target->ndim; d++) { + tstrides[ULAB_MAX_DIMS - 1 - d] = target->strides[ULAB_MAX_DIMS - 1 - d] / target->itemsize; + } + + uint8_t *sarray = (uint8_t *)source->array; + + if(source->dtype == NDARRAY_UINT8) { + ITERATE_VECTOR(uint8_t, target, tarray, tstrides, source, sarray); + } else if(source->dtype == NDARRAY_INT8) { + ITERATE_VECTOR(int8_t, target, tarray, tstrides, source, sarray); + } else if(source->dtype == NDARRAY_UINT16) { + ITERATE_VECTOR(uint16_t, target, tarray, tstrides, source, sarray); + } else if(source->dtype == NDARRAY_INT16) { + ITERATE_VECTOR(int16_t, target, tarray, tstrides, source, sarray); + } else { + ITERATE_VECTOR(mp_float_t, target, tarray, tstrides, source, sarray); + } + } else { + target = ndarray_from_mp_obj(o_in, 0); + mp_float_t *tarray = (mp_float_t *)target->array; + for(size_t i = 0; i < target->len; i++) { + *tarray = f(*tarray); + tarray++; + } + } + return MP_OBJ_FROM_PTR(target); +} + +#else + static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) { // Return a single value, if o_in is not iterable if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) { @@ -109,6 +188,8 @@ static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t) } return MP_OBJ_FROM_PTR(ndarray); } +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ + #if ULAB_NUMPY_HAS_ACOS //| def acos(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -117,8 +198,12 @@ static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t) //| MATH_FUN_1(acos, acos); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_acos_obj, 1, vector_acos); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_ACOS */ #if ULAB_NUMPY_HAS_ACOSH //| def acosh(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -127,8 +212,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos); //| MATH_FUN_1(acosh, acosh); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_acosh_obj, 1, vector_acosh); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_ACOSH */ #if ULAB_NUMPY_HAS_ASIN //| def asin(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -137,8 +226,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh); //| MATH_FUN_1(asin, asin); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_asin_obj, 1, vector_asin); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_ASIN */ #if ULAB_NUMPY_HAS_ASINH //| def asinh(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -147,8 +240,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin); //| MATH_FUN_1(asinh, asinh); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_asinh_obj, 1, vector_asinh); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_ASINH */ #if ULAB_NUMPY_HAS_AROUND //| def around(a: _ArrayLike, *, decimals: int = 0) -> ulab.numpy.ndarray: @@ -160,7 +257,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh); mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } } + { MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } }, + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } } + #endif }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -172,6 +272,12 @@ mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_float_t mul = MICROPY_FLOAT_C_FUN(pow)(10.0, n); ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype) + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + mp_obj_t out = args[2].u_obj; + if(out != mp_const_none) { + mp_raise_ValueError(translate("out keyword is not supported for function")); + } + #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); mp_float_t *narray = (mp_float_t *)ndarray->array; uint8_t *sarray = (uint8_t *)source->array; @@ -219,7 +325,7 @@ mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg } MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around); -#endif +#endif /* ULAB_NUMPY_HAS_AROUND */ #if ULAB_NUMPY_HAS_ATAN //| def atan(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -229,8 +335,27 @@ MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around); //| MATH_FUN_1(atan, atan); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_atan_obj, 1, vector_atan); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_ATAN */ + + +#if ULAB_NUMPY_HAS_ATANH +//| def atanh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| """Computes the inverse hyperbolic tangent function""" +//| ... +//| + +MATH_FUN_1(atanh, atanh); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_atanh_obj, 1, vector_atanh); +#else +MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh); +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_ATANH */ #if ULAB_NUMPY_HAS_ARCTAN2 //| def arctan2(ya: _ArrayLike, xa: _ArrayLike) -> ulab.numpy.ndarray: @@ -325,16 +450,6 @@ mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) { MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2); #endif /* ULAB_VECTORISE_HAS_ARCTAN2 */ -#if ULAB_NUMPY_HAS_ATANH -//| def atanh(a: _ArrayLike) -> ulab.numpy.ndarray: -//| """Computes the inverse hyperbolic tangent function""" -//| ... -//| - -MATH_FUN_1(atanh, atanh); -MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh); -#endif - #if ULAB_NUMPY_HAS_CEIL //| def ceil(a: _ArrayLike) -> ulab.numpy.ndarray: //| """Rounds numbers up to the next whole number""" @@ -342,8 +457,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh); //| MATH_FUN_1(ceil, ceil); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_ceil_obj, 1, vector_ceil); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_CEIL */ #if ULAB_NUMPY_HAS_COS //| def cos(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -352,8 +471,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil); //| MATH_FUN_1(cos, cos); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_cos_obj, 1, vector_cos); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_COS */ #if ULAB_NUMPY_HAS_COSH //| def cosh(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -362,8 +485,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos); //| MATH_FUN_1(cosh, cosh); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_cosh_obj, 1, vector_cosh); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_COSH */ #if ULAB_NUMPY_HAS_DEGREES //| def degrees(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -375,12 +502,18 @@ static mp_float_t vector_degrees_(mp_float_t value) { return value * MICROPY_FLOAT_CONST(180.0) / MP_PI; } +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +static mp_obj_t vector_degrees(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return vector_generic_vector(n_args, pos_args, kw_args, vector_degrees_); +} +MP_DEFINE_CONST_FUN_OBJ_KW(vector_degrees_obj, 1, vector_degrees); +#else static mp_obj_t vector_degrees(mp_obj_t x_obj) { return vector_generic_vector(x_obj, vector_degrees_); } - MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_DEGREES */ #if ULAB_SCIPY_SPECIAL_HAS_ERF //| def erf(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -389,8 +522,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees); //| MATH_FUN_1(erf, erf); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_erf_obj, 1, vector_erf); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_SCIPY_SPECIAL_HAS_ERF */ #if ULAB_SCIPY_SPECIAL_HAS_ERFC //| def erfc(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -399,17 +536,41 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf); //| MATH_FUN_1(erfc, erfc); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_erfc_obj, 1, vector_erfc); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_SCIPY_SPECIAL_HAS_ERFC */ #if ULAB_NUMPY_HAS_EXP //| def exp(a: _ArrayLike) -> ulab.numpy.ndarray: //| """Computes the exponent function.""" //| ... //| - +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +static mp_obj_t vector_exp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +#else static mp_obj_t vector_exp(mp_obj_t o_in) { +#endif #if ULAB_SUPPORTS_COMPLEX + + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + // since the complex case is dissimilar to the rest, we've got to do the parsing of the keywords here + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + mp_obj_t o_in = args[0].u_obj; + mp_obj_t out = args[1].u_obj; + if(out != mp_const_none) { + mp_raise_ValueError(translate("out keyword is not supported for complex dtype")); + } + #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ + if(mp_obj_is_type(o_in, &mp_type_complex)) { mp_float_t real, imag; mp_obj_get_complex(o_in, &real, &imag); @@ -466,12 +627,20 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { return MP_OBJ_FROM_PTR(ndarray); } } - #endif + #endif /* ULAB_SUPPORTS_COMPLEX */ + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(exp)); + #else return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp)); + #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ } +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_exp_obj, 1, vector_exp); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_EXP */ #if ULAB_NUMPY_HAS_EXPM1 //| def expm1(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -480,8 +649,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp); //| MATH_FUN_1(expm1, expm1); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_expm1_obj, 1, vector_expm1); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_EXPM1 */ #if ULAB_NUMPY_HAS_FLOOR //| def floor(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -490,8 +663,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1); //| MATH_FUN_1(floor, floor); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_floor_obj, 1, vector_floor); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_FLOOR */ #if ULAB_SCIPY_SPECIAL_HAS_GAMMA //| def gamma(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -500,8 +677,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor); //| MATH_FUN_1(gamma, tgamma); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_gamma_obj, 1, vector_gamma); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_SCIPY_SPECIAL_HAS_GAMMA */ #if ULAB_SCIPY_SPECIAL_HAS_GAMMALN //| def lgamma(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -510,8 +691,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma); //| MATH_FUN_1(lgamma, lgamma); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_lgamma_obj, 1, vector_lgamma); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_SCIPY_SEPCIAL_HAS_GAMMALN */ #if ULAB_NUMPY_HAS_LOG //| def log(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -520,8 +705,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma); //| MATH_FUN_1(log, log); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_log_obj, 1, vector_log); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_LOG */ #if ULAB_NUMPY_HAS_LOG10 //| def log10(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -530,8 +719,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log); //| MATH_FUN_1(log10, log10); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_log10_obj, 1, vector_log10); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_LOG10 */ #if ULAB_NUMPY_HAS_LOG2 //| def log2(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -540,8 +733,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10); //| MATH_FUN_1(log2, log2); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_log2_obj, 1, vector_log2); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_LOG2 */ #if ULAB_NUMPY_HAS_RADIANS //| def radians(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -553,12 +750,18 @@ static mp_float_t vector_radians_(mp_float_t value) { return value * MP_PI / MICROPY_FLOAT_CONST(180.0); } +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +static mp_obj_t vector_radians(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return vector_generic_vector(n_args, pos_args, kw_args, vector_radians_); +} +MP_DEFINE_CONST_FUN_OBJ_KW(vector_radians_obj, 1, vector_radians); +#else static mp_obj_t vector_radians(mp_obj_t x_obj) { return vector_generic_vector(x_obj, vector_radians_); } - MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_RADIANS */ #if ULAB_NUMPY_HAS_SIN //| def sin(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -567,8 +770,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians); //| MATH_FUN_1(sin, sin); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_sin_obj, 1, vector_sin); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_SIN */ #if ULAB_NUMPY_HAS_SINC //| def sinc(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -576,7 +783,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); //| ... //| -static mp_float_t ulab_sinc1(mp_float_t x) { +static mp_float_t vector_sinc1(mp_float_t x) { if (fpclassify(x) == FP_ZERO) { return MICROPY_FLOAT_CONST(1.); } @@ -584,12 +791,18 @@ static mp_float_t ulab_sinc1(mp_float_t x) { return MICROPY_FLOAT_C_FUN(sin)(x) / x; } +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +static mp_obj_t vector_sinc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return vector_generic_vector(n_args, pos_args, kw_args, vector_sinc1); +} +MP_DEFINE_CONST_FUN_OBJ_KW(vector_sinc_obj, 1, vector_sinc); +#else static mp_obj_t vector_sinc(mp_obj_t x_obj) { - return vector_generic_vector(x_obj, ulab_sinc1); + return vector_generic_vector(x_obj, vector_sinc1); } - MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_SINC */ #if ULAB_NUMPY_HAS_SINH //| def sinh(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -598,8 +811,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc); //| MATH_FUN_1(sinh, sinh); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_sinh_obj, 1, vector_sinh); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_SINH */ #if ULAB_NUMPY_HAS_SQRT @@ -608,22 +825,29 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh); //| ... //| -#if ULAB_SUPPORTS_COMPLEX +#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + #endif + #if ULAB_SUPPORTS_COMPLEX { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, + #endif }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_obj_t o_in = args[0].u_obj; - uint8_t dtype = mp_obj_get_int(args[1].u_obj); + + #if ULAB_SUPPORTS_COMPLEX + uint8_t dtype = mp_obj_get_int(args[2].u_obj); if((dtype != NDARRAY_FLOAT) && (dtype != NDARRAY_COMPLEX)) { mp_raise_TypeError(translate("dtype must be float, or complex")); } - + if(mp_obj_is_type(o_in, &mp_type_complex)) { mp_float_t real, imag; mp_obj_get_complex(o_in, &real, &imag); @@ -638,6 +862,12 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) } if(dtype == NDARRAY_COMPLEX) { + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + mp_obj_t out = args[1].u_obj; + if(out != mp_const_none) { + mp_raise_ValueError(translate("out keyword is not supported for complex dtype")); + } + #endif if(source->dtype == NDARRAY_COMPLEX) { uint8_t *sarray = (uint8_t *)source->array; ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX); @@ -741,14 +971,15 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) } } } - return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(sqrt)); + #endif /* ULAB_SUPPORTS_COMPLEX */ + return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(sqrt)); } + MP_DEFINE_CONST_FUN_OBJ_KW(vector_sqrt_obj, 1, vector_sqrt); #else MATH_FUN_1(sqrt, sqrt); MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt); -#endif /* ULAB_SUPPORTS_COMPLEX */ - +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD | ULAB_SUPPORTS_COMPLEX */ #endif /* ULAB_NUMPY_HAS_SQRT */ #if ULAB_NUMPY_HAS_TAN @@ -758,8 +989,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt); //| MATH_FUN_1(tan, tan); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_tan_obj, 1, vector_tan); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_TAN */ #if ULAB_NUMPY_HAS_TANH //| def tanh(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -767,8 +1002,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan); //| ... MATH_FUN_1(tanh, tanh); +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DEFINE_CONST_FUN_OBJ_KW(vector_tanh_obj, 1, vector_tanh); +#else MP_DEFINE_CONST_FUN_OBJ_1(vector_tanh_obj, vector_tanh); -#endif +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#endif /* ULAB_NUMPY_HAS_TANH */ #if ULAB_NUMPY_HAS_VECTORIZE static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/code/numpy/vector.h b/code/numpy/vector.h index c688e95c..ade12a40 100644 --- a/code/numpy/vector.h +++ b/code/numpy/vector.h @@ -15,10 +15,37 @@ #include "../ulab.h" #include "../ndarray.h" + +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +MP_DECLARE_CONST_FUN_OBJ_KW(vector_acos_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_acosh_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_asin_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_asinh_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_atan_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_atanh_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_ceil_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_cos_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_cosh_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_degrees_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_erf_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_erfc_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_exp_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_expm1_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_floor_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_gamma_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_lgamma_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_log_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_log10_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_log2_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_radians_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_sin_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinc_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinh_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_tan_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_tanh_obj); +#else MP_DECLARE_CONST_FUN_OBJ_1(vector_acos_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_acosh_obj); -MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj); -MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_asin_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_asinh_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_atan_obj); @@ -41,13 +68,19 @@ MP_DECLARE_CONST_FUN_OBJ_1(vector_radians_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_sin_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_sinc_obj); MP_DECLARE_CONST_FUN_OBJ_1(vector_sinh_obj); -#if ULAB_SUPPORTS_COMPLEX +MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj); +#endif + +MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj); + +#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD MP_DECLARE_CONST_FUN_OBJ_KW(vector_sqrt_obj); #else MP_DECLARE_CONST_FUN_OBJ_1(vector_sqrt_obj); #endif -MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj); + MP_DECLARE_CONST_FUN_OBJ_KW(vector_vectorize_obj); typedef struct _vectorized_function_obj_t { @@ -70,12 +103,73 @@ typedef struct _vectorized_function_obj_t { ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\ }\ }) +#endif /* ULAB_HAS_FUNCTION_ITERATOR */ -#else +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + +#if ULAB_MAX_DIMS == 1 +#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\ + size_t l = 0;\ + do {\ + *(tarray) = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(tarray) = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + (tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(tarray) = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + (tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\ + (tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 3 */ #if ULAB_MAX_DIMS == 4 -#define ITERATE_VECTOR(type, array, source, sarray) do {\ - size_t i=0;\ +#define ITERATE_VECTOR(type, target, tshape, tstrides, source, sarray) do {\ + size_t i = 0;\ do {\ size_t j = 0;\ do {\ @@ -83,25 +177,67 @@ typedef struct _vectorized_function_obj_t { do {\ size_t l = 0;\ do {\ - *(array)++ = f(*((type *)(sarray)));\ + *(tarray) = f(*((type *)(sarray)));\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\ l++;\ } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ - (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + (tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\ k++;\ } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ - (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\ + (tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\ j++;\ - } while(j < (source)->shape[ULAB_MAX_DIMS-3]);\ - (sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS-3];\ + } while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\ + (tarray) -= (tstrides)[ULAB_MAX_DIMS - 3] * (target)->shape[ULAB_MAX_DIMS - 3];\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 4];\ i++;\ - } while(i < (source)->shape[ULAB_MAX_DIMS-4]);\ + } while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\ } while(0) #endif /* ULAB_MAX_DIMS == 4 */ +#define MATH_FUN_1(py_name, c_name) \ + static mp_obj_t vector_ ## py_name(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { \ + return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(c_name)); \ +} + +#else /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ + +#if ULAB_MAX_DIMS == 1 +#define ITERATE_VECTOR(type, array, source, sarray) do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define ITERATE_VECTOR(type, array, source, sarray) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + #if ULAB_MAX_DIMS == 3 #define ITERATE_VECTOR(type, array, source, sarray) do {\ size_t j = 0;\ @@ -113,50 +249,50 @@ typedef struct _vectorized_function_obj_t { *(array)++ = f(*((type *)(sarray)));\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ l++;\ - } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ - (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\ + } while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ k++;\ - } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ - (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\ + } while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\ j++;\ - } while(j < (source)->shape[ULAB_MAX_DIMS-3]);\ + } while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\ } while(0) #endif /* ULAB_MAX_DIMS == 3 */ -#if ULAB_MAX_DIMS == 2 +#if ULAB_MAX_DIMS == 4 #define ITERATE_VECTOR(type, array, source, sarray) do {\ - size_t k = 0;\ + size_t i = 0;\ do {\ - size_t l = 0;\ + size_t j = 0;\ do {\ - *(array)++ = f(*((type *)(sarray)));\ - (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ - (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\ - (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ -} while(0) -#endif /* ULAB_MAX_DIMS == 2 */ - -#if ULAB_MAX_DIMS == 1 -#define ITERATE_VECTOR(type, array, source, sarray) do {\ - size_t l = 0;\ - do {\ - *(array)++ = f(*((type *)(sarray)));\ - (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\ } while(0) -#endif /* ULAB_MAX_DIMS == 1 */ -#endif /* ULAB_HAS_FUNCTION_ITERATOR */ +#endif /* ULAB_MAX_DIMS == 4 */ #define MATH_FUN_1(py_name, c_name) \ static mp_obj_t vector_ ## py_name(mp_obj_t x_obj) { \ return vector_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \ } - +#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ #endif /* _VECTOR_ */ diff --git a/code/ulab.c b/code/ulab.c index 8482233b..a1057e02 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.1.1 +#define ULAB_VERSION 6.2.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 1d493ab3..c3c184a2 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -536,6 +536,12 @@ // vectorised versions of the functions of the math python module, with // the exception of the functions listed in scipy.special + +// if this constant is set, math functions support the out keyword argument +#ifndef ULAB_MATH_FUNCTIONS_OUT_KEYWORD +#define ULAB_MATH_FUNCTIONS_OUT_KEYWORD (1) +#endif + #ifndef ULAB_NUMPY_HAS_ACOS #define ULAB_NUMPY_HAS_ACOS (1) #endif From 38a4976b58220f6af58bfe22b91391f9d26f0246 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 20 Jun 2023 06:09:01 -0500 Subject: [PATCH 063/113] fix fetching submodules (cp build process change) (#627) --- build-cp.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-cp.sh b/build-cp.sh index 0f75487e..ee6a95ef 100755 --- a/build-cp.sh +++ b/build-cp.sh @@ -37,7 +37,7 @@ readlinkf_posix() { } NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())') HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )" -[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-submodules && git submodule update --init lib/uzlib tools) +[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-all-submodules && git submodule update --init lib/uzlib tools) rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab dims=${1-2} make -C circuitpython/mpy-cross -j$NPROC From ef248b684d14e8c21ddba33bb455190c04ece763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 20 Jun 2023 21:44:58 +0200 Subject: [PATCH 064/113] add bitwise operators (#616) * add bitwise operators * add build to requirements --- code/micropython.mk | 1 + code/numpy/bitwise.c | 431 ++++++++++++++++++++++++++++++ code/numpy/bitwise.h | 32 +++ code/numpy/numpy.c | 19 +- code/ulab.c | 2 +- code/ulab.h | 21 ++ docs/ulab-change-log.md | 6 + tests/2d/numpy/bitwise_and.py | 14 + tests/2d/numpy/bitwise_and.py.exp | 16 ++ tests/2d/numpy/bitwise_or.py | 14 + tests/2d/numpy/bitwise_or.py.exp | 16 ++ tests/2d/numpy/bitwise_xor.py | 14 + tests/2d/numpy/bitwise_xor.py.exp | 16 ++ tests/2d/numpy/left_shift.py | 14 + tests/2d/numpy/left_shift.py.exp | 16 ++ tests/2d/numpy/right_shift.py | 14 + tests/2d/numpy/right_shift.py.exp | 16 ++ 17 files changed, 659 insertions(+), 3 deletions(-) create mode 100644 code/numpy/bitwise.c create mode 100644 code/numpy/bitwise.h create mode 100644 tests/2d/numpy/bitwise_and.py create mode 100644 tests/2d/numpy/bitwise_and.py.exp create mode 100644 tests/2d/numpy/bitwise_or.py create mode 100644 tests/2d/numpy/bitwise_or.py.exp create mode 100644 tests/2d/numpy/bitwise_xor.py create mode 100644 tests/2d/numpy/bitwise_xor.py.exp create mode 100644 tests/2d/numpy/left_shift.py create mode 100644 tests/2d/numpy/left_shift.py.exp create mode 100644 tests/2d/numpy/right_shift.py create mode 100644 tests/2d/numpy/right_shift.py.exp diff --git a/code/micropython.mk b/code/micropython.mk index f36d1d61..eb517926 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -12,6 +12,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/ndarray.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/ndarray/ndarray_iter.c SRC_USERMOD += $(USERMODULES_DIR)/ndarray_properties.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/approx.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/bitwise.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/compare.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c diff --git a/code/numpy/bitwise.c b/code/numpy/bitwise.c new file mode 100644 index 00000000..d02e0970 --- /dev/null +++ b/code/numpy/bitwise.c @@ -0,0 +1,431 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Zoltán Vörös + * +*/ + +#include +#include +#include +#include +#include "py/obj.h" +#include "py/runtime.h" + +#include "bitwise.h" + + +#if ULAB_NUMPY_HAS_BITWISE_AND +ndarray_obj_t *bitwise_bitwise_and_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + // AND is commutative, so simply swap the order, if a particular combination has already been inspected + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, &); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, &); + } else { + return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, &); + } else { + return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, &); + } else { + return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } + return results; +} +#endif + +#if ULAB_NUMPY_HAS_BITWISE_OR +ndarray_obj_t *bitwise_bitwise_or_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + // OR is commutative, so simply swap the order, if a particular combination has already been inspected + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, |); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, |); + } else { + return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, |); + } else { + return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, |); + } else { + return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } + return results; +} +#endif + + +#if ULAB_NUMPY_HAS_BITWISE_XOR +ndarray_obj_t *bitwise_bitwise_xor_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + // OR is commutative, so simply swap the order, if a particular combination has already been inspected + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } else { + return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } else { + return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } else { + return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides); + } + } + return results; +} +#endif + +#if ULAB_NUMPY_HAS_LEFT_SHIFT +ndarray_obj_t *bitwise_left_shift_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, <<); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, <<); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, <<); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, <<); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, <<); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, <<); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, <<); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, uint16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, <<); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, <<); + } + } + return results; +} +#endif + +#if ULAB_NUMPY_HAS_RIGHT_SHIFT +ndarray_obj_t *bitwise_right_shift_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, >>); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, >>); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, >>); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, >>); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, >>); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >>); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, >>); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, uint16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >>); + } else { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, >>); + } + } + return results; +} +#endif + +mp_obj_t *bitwise_binary_operators(mp_obj_t x1, mp_obj_t x2, uint8_t optype) { + ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0); + ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0); + + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT) || (lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + mp_raise_ValueError(translate("not supported for input types")); + } + #else + if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { + mp_raise_ValueError(translate("not supported for input types")); + } + #endif + + uint8_t ndim = 0; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); + + if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) { + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, lstrides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + mp_raise_ValueError(translate("operands could not be broadcast together")); + } + + ndarray_obj_t *results = NULL; + + switch(optype) { + #if ULAB_NUMPY_HAS_BITWISE_AND + case BITWISE_AND: + results = bitwise_bitwise_and_loop(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if ULAB_NUMPY_HAS_BITWISE_OR + case BITWISE_OR: + results = bitwise_bitwise_or_loop(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if ULAB_NUMPY_HAS_BITWISE_XOR + case BITWISE_XOR: + results = bitwise_bitwise_xor_loop(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if ULAB_NUMPY_HAS_LEFT_SHIFT + case BITWISE_LEFT_SHIFT: + results = bitwise_left_shift_loop(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if ULAB_NUMPY_HAS_RIGHT_SHIFT + case BITWISE_RIGHT_SHIFT: + results = bitwise_right_shift_loop(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + default: + break; + } + + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, lstrides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + + return MP_OBJ_FROM_PTR(results); +} + +#if ULAB_NUMPY_HAS_BITWISE_AND +mp_obj_t bitwise_bitwise_and(mp_obj_t x1, mp_obj_t x2) { + return bitwise_binary_operators(x1, x2, BITWISE_AND); +} + +MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_and_obj, bitwise_bitwise_and); +#endif + +#if ULAB_NUMPY_HAS_BITWISE_OR +mp_obj_t bitwise_bitwise_or(mp_obj_t x1, mp_obj_t x2) { + return bitwise_binary_operators(x1, x2, BITWISE_OR); +} + +MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_or_obj, bitwise_bitwise_or); +#endif + +#if ULAB_NUMPY_HAS_BITWISE_XOR +mp_obj_t bitwise_bitwise_xor(mp_obj_t x1, mp_obj_t x2) { + return bitwise_binary_operators(x1, x2, BITWISE_XOR); +} + +MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_xor_obj, bitwise_bitwise_xor); +#endif + +#if ULAB_NUMPY_HAS_LEFT_SHIFT +mp_obj_t bitwise_left_shift(mp_obj_t x1, mp_obj_t x2) { + return bitwise_binary_operators(x1, x2, BITWISE_LEFT_SHIFT); +} + +MP_DEFINE_CONST_FUN_OBJ_2(left_shift_obj, bitwise_left_shift); +#endif + +#if ULAB_NUMPY_HAS_RIGHT_SHIFT +mp_obj_t bitwise_right_shift(mp_obj_t x1, mp_obj_t x2) { + return bitwise_binary_operators(x1, x2, BITWISE_RIGHT_SHIFT); +} + +MP_DEFINE_CONST_FUN_OBJ_2(right_shift_obj, bitwise_right_shift); +#endif \ No newline at end of file diff --git a/code/numpy/bitwise.h b/code/numpy/bitwise.h new file mode 100644 index 00000000..bddd8b4a --- /dev/null +++ b/code/numpy/bitwise.h @@ -0,0 +1,32 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Zoltán Vörös +*/ + +#ifndef _BITWISE_ +#define _BITWISE_ + +#include "../ulab.h" +#include "../ndarray.h" + +enum BITWISE_FUNCTION_TYPE { + BITWISE_AND, + BITWISE_OR, + BITWISE_XOR, + BITWISE_LEFT_SHIFT, + BITWISE_RIGHT_SHIFT, +}; + +MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_and_obj); +MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_or_obj); +MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_xor_obj); +MP_DECLARE_CONST_FUN_OBJ_2(left_shift_obj); +MP_DECLARE_CONST_FUN_OBJ_2(right_shift_obj); + +#endif /* _BITWISE_ */ diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 7c35f0e1..d530638e 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -18,6 +18,7 @@ #include "numpy.h" #include "approx.h" +#include "bitwise.h" #include "carray/carray.h" #include "compare.h" #include "create.h" @@ -164,7 +165,6 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_ZEROS { MP_ROM_QSTR(MP_QSTR_zeros), MP_ROM_PTR(&create_zeros_obj) }, #endif - // functions of the compare sub-module #if ULAB_NUMPY_HAS_CLIP { MP_ROM_QSTR(MP_QSTR_clip), MP_ROM_PTR(&compare_clip_obj) }, #endif @@ -189,10 +189,25 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_NONZERO { MP_ROM_QSTR(MP_QSTR_nonzero), MP_ROM_PTR(&compare_nonzero_obj) }, #endif - #if ULAB_NUMPY_HAS_WHERE { MP_ROM_QSTR(MP_QSTR_where), MP_ROM_PTR(&compare_where_obj) }, #endif + // bitwise operators + #if ULAB_NUMPY_HAS_BITWISE_AND + { MP_ROM_QSTR(MP_QSTR_bitwise_and), MP_ROM_PTR(&bitwise_bitwise_and_obj) }, + #endif + #if ULAB_NUMPY_HAS_BITWISE_OR + { MP_ROM_QSTR(MP_QSTR_bitwise_or), MP_ROM_PTR(&bitwise_bitwise_or_obj) }, + #endif + #if ULAB_NUMPY_HAS_BITWISE_XOR + { MP_ROM_QSTR(MP_QSTR_bitwise_xor), MP_ROM_PTR(&bitwise_bitwise_xor_obj) }, + #endif + #if ULAB_NUMPY_HAS_LEFT_SHIFT + { MP_ROM_QSTR(MP_QSTR_left_shift), MP_ROM_PTR(&left_shift_obj) }, + #endif + #if ULAB_NUMPY_HAS_RIGHT_SHIFT + { MP_ROM_QSTR(MP_QSTR_right_shift), MP_ROM_PTR(&right_shift_obj) }, + #endif // functions of the filter sub-module #if ULAB_NUMPY_HAS_CONVOLVE { MP_ROM_QSTR(MP_QSTR_convolve), MP_ROM_PTR(&filter_convolve_obj) }, diff --git a/code/ulab.c b/code/ulab.c index a1057e02..21d72b80 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.2.0 +#define ULAB_VERSION 6.3.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index c3c184a2..b063bcac 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -165,6 +165,27 @@ #define NDARRAY_HAS_INPLACE_TRUE_DIVIDE (1) #endif +// bitwise operators +#ifndef ULAB_NUMPY_HAS_BITWISE_AND +#define ULAB_NUMPY_HAS_BITWISE_AND (1) +#endif + +#ifndef ULAB_NUMPY_HAS_BITWISE_OR +#define ULAB_NUMPY_HAS_BITWISE_OR (1) +#endif + +#ifndef ULAB_NUMPY_HAS_BITWISE_XOR +#define ULAB_NUMPY_HAS_BITWISE_XOR (1) +#endif + +#ifndef ULAB_NUMPY_HAS_LEFT_SHIFT +#define ULAB_NUMPY_HAS_LEFT_SHIFT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_RIGHT_SHIFT +#define ULAB_NUMPY_HAS_RIGHT_SHIFT (1) +#endif + // the ndarray unary operators #ifndef NDARRAY_HAS_UNARY_OPS #define NDARRAY_HAS_UNARY_OPS (1) diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 08cd51e7..7cc0b11d 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Fri, 12 May 2023 + +version 6.3.0 + + add bitwise operators + Wed, 17 May 2023 version 6.1.1 diff --git a/tests/2d/numpy/bitwise_and.py b/tests/2d/numpy/bitwise_and.py new file mode 100644 index 00000000..98259351 --- /dev/null +++ b/tests/2d/numpy/bitwise_and.py @@ -0,0 +1,14 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype1 in dtypes: + x1 = np.array(range(5), dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(range(5, 0, -1), dtype=dtype2) + + print(np.bitwise_and(x1, x2)) diff --git a/tests/2d/numpy/bitwise_and.py.exp b/tests/2d/numpy/bitwise_and.py.exp new file mode 100644 index 00000000..ae0b72c6 --- /dev/null +++ b/tests/2d/numpy/bitwise_and.py.exp @@ -0,0 +1,16 @@ +array([0, 0, 2, 2, 0], dtype=uint8) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=uint16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=int8) +array([0, 0, 2, 2, 0], dtype=uint16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=uint16) +array([0, 0, 2, 2, 0], dtype=uint16) +array([0, 0, 2, 2, 0], dtype=uint16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=int16) +array([0, 0, 2, 2, 0], dtype=int16) diff --git a/tests/2d/numpy/bitwise_or.py b/tests/2d/numpy/bitwise_or.py new file mode 100644 index 00000000..c52a5cd1 --- /dev/null +++ b/tests/2d/numpy/bitwise_or.py @@ -0,0 +1,14 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype1 in dtypes: + x1 = np.array(range(5), dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(range(5, 0, -1), dtype=dtype2) + + print(np.bitwise_or(x1, x2)) diff --git a/tests/2d/numpy/bitwise_or.py.exp b/tests/2d/numpy/bitwise_or.py.exp new file mode 100644 index 00000000..039603a5 --- /dev/null +++ b/tests/2d/numpy/bitwise_or.py.exp @@ -0,0 +1,16 @@ +array([5, 5, 3, 3, 5], dtype=uint8) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=uint16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=int8) +array([5, 5, 3, 3, 5], dtype=uint16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=uint16) +array([5, 5, 3, 3, 5], dtype=uint16) +array([5, 5, 3, 3, 5], dtype=uint16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=int16) +array([5, 5, 3, 3, 5], dtype=int16) diff --git a/tests/2d/numpy/bitwise_xor.py b/tests/2d/numpy/bitwise_xor.py new file mode 100644 index 00000000..5e7af037 --- /dev/null +++ b/tests/2d/numpy/bitwise_xor.py @@ -0,0 +1,14 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype1 in dtypes: + x1 = np.array(range(5), dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(range(5, 0, -1), dtype=dtype2) + + print(np.bitwise_xor(x1, x2)) diff --git a/tests/2d/numpy/bitwise_xor.py.exp b/tests/2d/numpy/bitwise_xor.py.exp new file mode 100644 index 00000000..7796e3a9 --- /dev/null +++ b/tests/2d/numpy/bitwise_xor.py.exp @@ -0,0 +1,16 @@ +array([5, 5, 1, 1, 5], dtype=uint8) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=uint16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=int8) +array([5, 5, 1, 1, 5], dtype=uint16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=uint16) +array([5, 5, 1, 1, 5], dtype=uint16) +array([5, 5, 1, 1, 5], dtype=uint16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=int16) +array([5, 5, 1, 1, 5], dtype=int16) diff --git a/tests/2d/numpy/left_shift.py b/tests/2d/numpy/left_shift.py new file mode 100644 index 00000000..18703ddd --- /dev/null +++ b/tests/2d/numpy/left_shift.py @@ -0,0 +1,14 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype1 in dtypes: + x1 = np.array(range(5), dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(range(5, 0, -1), dtype=dtype2) + + print(np.left_shift(x1, x2)) diff --git a/tests/2d/numpy/left_shift.py.exp b/tests/2d/numpy/left_shift.py.exp new file mode 100644 index 00000000..a993b06f --- /dev/null +++ b/tests/2d/numpy/left_shift.py.exp @@ -0,0 +1,16 @@ +array([0, 16, 16, 12, 8], dtype=uint8) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=uint16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=int8) +array([0, 16, 16, 12, 8], dtype=uint16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=uint16) +array([0, 16, 16, 12, 8], dtype=int8) +array([0, 16, 16, 12, 8], dtype=uint16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=int16) +array([0, 16, 16, 12, 8], dtype=int16) diff --git a/tests/2d/numpy/right_shift.py b/tests/2d/numpy/right_shift.py new file mode 100644 index 00000000..08737ede --- /dev/null +++ b/tests/2d/numpy/right_shift.py @@ -0,0 +1,14 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype1 in dtypes: + x1 = np.array(range(5), dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(range(5, 0, -1), dtype=dtype2) + + print(np.right_shift(x1, x2)) diff --git a/tests/2d/numpy/right_shift.py.exp b/tests/2d/numpy/right_shift.py.exp new file mode 100644 index 00000000..55700511 --- /dev/null +++ b/tests/2d/numpy/right_shift.py.exp @@ -0,0 +1,16 @@ +array([0, 0, 0, 0, 2], dtype=uint8) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=uint16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=int8) +array([0, 0, 0, 0, 2], dtype=uint16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=uint16) +array([0, 0, 0, 0, 2], dtype=int8) +array([0, 0, 0, 0, 2], dtype=uint16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=int16) +array([0, 0, 0, 0, 2], dtype=int16) From 2cde1280a4577b1fc116f99d9bc943ea2e226895 Mon Sep 17 00:00:00 2001 From: Derfies Date: Wed, 21 Jun 2023 21:46:30 +1000 Subject: [PATCH 065/113] Bitwise (#628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add bitwise operators * add build to requirements * [EDIT] - Tweaked test data and saved test results. * Tweaked test values for and / or * [EDIT] - Setting print options to be verbose for test comparisons. * [EDIT] - Removed call to set_printoptions and added output from ulab instead of numpy. Of note - there seems to be a discrepancy between the numpy and ulab output for one of the left_shift cases. * [EDIT] - Added newline at end of file for diffing purposes. * [EDIT] - Added print options back in as output seemed truncated. --------- Co-authored-by: Zoltán Vörös Co-authored-by: JamieDouugh --- docs/ulab-change-log.md | 6 +++ tests/2d/numpy/bitwise_and.py | 8 ++-- tests/2d/numpy/bitwise_and.py.exp | 32 ++++++++-------- tests/2d/numpy/bitwise_or.py | 8 ++-- tests/2d/numpy/bitwise_or.py.exp | 32 ++++++++-------- tests/2d/numpy/left_shift.py | 17 +++++--- tests/2d/numpy/left_shift.py.exp | 64 +++++++++++++++++++++++-------- tests/2d/numpy/right_shift.py | 17 +++++--- tests/2d/numpy/right_shift.py.exp | 64 +++++++++++++++++++++++-------- 9 files changed, 168 insertions(+), 80 deletions(-) diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 7cc0b11d..6cddfc5c 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -16,6 +16,12 @@ version 6.1.0 add sinc function +Fri, 12 May 2023 + +version 6.0.13 + + add bitwise operators + Sun, 7 May 2023 version 6.0.12 diff --git a/tests/2d/numpy/bitwise_and.py b/tests/2d/numpy/bitwise_and.py index 98259351..77b784d8 100644 --- a/tests/2d/numpy/bitwise_and.py +++ b/tests/2d/numpy/bitwise_and.py @@ -5,10 +5,12 @@ dtypes = (np.uint8, np.int8, np.uint16, np.int16) +test_values1 = (0, 1, 0, 1, 2, 5) +test_values2 = (0, 1, 1, 0, 2, 7) + for dtype1 in dtypes: - x1 = np.array(range(5), dtype=dtype1) + x1 = np.array(test_values1, dtype=dtype1) for dtype2 in dtypes: - x2 = np.array(range(5, 0, -1), dtype=dtype2) - + x2 = np.array(test_values2, dtype=dtype2) print(np.bitwise_and(x1, x2)) diff --git a/tests/2d/numpy/bitwise_and.py.exp b/tests/2d/numpy/bitwise_and.py.exp index ae0b72c6..0ceb27d9 100644 --- a/tests/2d/numpy/bitwise_and.py.exp +++ b/tests/2d/numpy/bitwise_and.py.exp @@ -1,16 +1,16 @@ -array([0, 0, 2, 2, 0], dtype=uint8) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=uint16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=int8) -array([0, 0, 2, 2, 0], dtype=uint16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=uint16) -array([0, 0, 2, 2, 0], dtype=uint16) -array([0, 0, 2, 2, 0], dtype=uint16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=int16) -array([0, 0, 2, 2, 0], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=uint8) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=uint16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=int8) +array([0, 1, 0, 0, 2, 5], dtype=uint16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=uint16) +array([0, 1, 0, 0, 2, 5], dtype=uint16) +array([0, 1, 0, 0, 2, 5], dtype=uint16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=int16) +array([0, 1, 0, 0, 2, 5], dtype=int16) diff --git a/tests/2d/numpy/bitwise_or.py b/tests/2d/numpy/bitwise_or.py index c52a5cd1..6c13fccf 100644 --- a/tests/2d/numpy/bitwise_or.py +++ b/tests/2d/numpy/bitwise_or.py @@ -5,10 +5,12 @@ dtypes = (np.uint8, np.int8, np.uint16, np.int16) +test_values1 = (0, 1, 0, 1, 2, 5) +test_values2 = (0, 1, 1, 0, 2, 7) + for dtype1 in dtypes: - x1 = np.array(range(5), dtype=dtype1) + x1 = np.array(test_values1, dtype=dtype1) for dtype2 in dtypes: - x2 = np.array(range(5, 0, -1), dtype=dtype2) - + x2 = np.array(test_values2, dtype=dtype2) print(np.bitwise_or(x1, x2)) diff --git a/tests/2d/numpy/bitwise_or.py.exp b/tests/2d/numpy/bitwise_or.py.exp index 039603a5..22bd18ae 100644 --- a/tests/2d/numpy/bitwise_or.py.exp +++ b/tests/2d/numpy/bitwise_or.py.exp @@ -1,16 +1,16 @@ -array([5, 5, 3, 3, 5], dtype=uint8) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=uint16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=int8) -array([5, 5, 3, 3, 5], dtype=uint16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=uint16) -array([5, 5, 3, 3, 5], dtype=uint16) -array([5, 5, 3, 3, 5], dtype=uint16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=int16) -array([5, 5, 3, 3, 5], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=uint8) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=uint16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=int8) +array([0, 1, 1, 1, 2, 7], dtype=uint16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=uint16) +array([0, 1, 1, 1, 2, 7], dtype=uint16) +array([0, 1, 1, 1, 2, 7], dtype=uint16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=int16) +array([0, 1, 1, 1, 2, 7], dtype=int16) diff --git a/tests/2d/numpy/left_shift.py b/tests/2d/numpy/left_shift.py index 18703ddd..5deb7701 100644 --- a/tests/2d/numpy/left_shift.py +++ b/tests/2d/numpy/left_shift.py @@ -2,13 +2,20 @@ from ulab import numpy as np except: import numpy as np +np.set_printoptions(threshold=100) +shift_values = ( + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), +) dtypes = (np.uint8, np.int8, np.uint16, np.int16) -for dtype1 in dtypes: - x1 = np.array(range(5), dtype=dtype1) - for dtype2 in dtypes: - x2 = np.array(range(5, 0, -1), dtype=dtype2) - print(np.left_shift(x1, x2)) +for shift_value in shift_values: + for dtype1 in dtypes: + x1 = np.array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(shift_value, dtype=dtype2) + print(np.left_shift(x1, x2)) diff --git a/tests/2d/numpy/left_shift.py.exp b/tests/2d/numpy/left_shift.py.exp index a993b06f..d1b1dceb 100644 --- a/tests/2d/numpy/left_shift.py.exp +++ b/tests/2d/numpy/left_shift.py.exp @@ -1,16 +1,48 @@ -array([0, 16, 16, 12, 8], dtype=uint8) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=uint16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=int8) -array([0, 16, 16, 12, 8], dtype=uint16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=uint16) -array([0, 16, 16, 12, 8], dtype=int8) -array([0, 16, 16, 12, 8], dtype=uint16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=int16) -array([0, 16, 16, 12, 8], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint8) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint8) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int8) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int8) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint8) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, -128, 12, 20, 28, 44, 52], dtype=int8) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16) +array([0, 4, 8, 16, 32, 64, -128, 12, 20, 28, 44, 52], dtype=int8) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) +array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16) diff --git a/tests/2d/numpy/right_shift.py b/tests/2d/numpy/right_shift.py index 08737ede..f5099f34 100644 --- a/tests/2d/numpy/right_shift.py +++ b/tests/2d/numpy/right_shift.py @@ -2,13 +2,20 @@ from ulab import numpy as np except: import numpy as np +np.set_printoptions(threshold=100) +shift_values = ( + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), +) dtypes = (np.uint8, np.int8, np.uint16, np.int16) -for dtype1 in dtypes: - x1 = np.array(range(5), dtype=dtype1) - for dtype2 in dtypes: - x2 = np.array(range(5, 0, -1), dtype=dtype2) - print(np.right_shift(x1, x2)) +for shift_value in shift_values: + for dtype1 in dtypes: + x1 = np.array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=dtype1) + for dtype2 in dtypes: + x2 = np.array(shift_value, dtype=dtype2) + print(np.right_shift(x1, x2)) diff --git a/tests/2d/numpy/right_shift.py.exp b/tests/2d/numpy/right_shift.py.exp index 55700511..cd5a4815 100644 --- a/tests/2d/numpy/right_shift.py.exp +++ b/tests/2d/numpy/right_shift.py.exp @@ -1,16 +1,48 @@ -array([0, 0, 0, 0, 2], dtype=uint8) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=uint16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=int8) -array([0, 0, 0, 0, 2], dtype=uint16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=uint16) -array([0, 0, 0, 0, 2], dtype=int8) -array([0, 0, 0, 0, 2], dtype=uint16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=int16) -array([0, 0, 0, 0, 2], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint8) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint8) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int8) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int8) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint8) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int8) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int8) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) +array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16) From 26051d70d296fbec1ee468d7531fcbb771bd8ef5 Mon Sep 17 00:00:00 2001 From: FelixNumworks <99270678+FelixNumworks@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:55:16 +0200 Subject: [PATCH 066/113] Int overflow (#629) * Prevent ndarray from overflowing size_t * Use size_t for polyval array len * Fix infinite arange * 6.3.1 version --- code/ndarray.c | 4 ++++ code/numpy/create.c | 2 +- code/numpy/poly.c | 2 +- code/ulab.c | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 88186759..0741edc4 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -622,6 +622,10 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray->len = multiply_size(ndarray->len, shape[i-1]); } + if (SIZE_MAX / ndarray->itemsize <= ndarray->len) { + mp_raise_ValueError(translate("ndarray length overflows")); + } + // if the length is 0, still allocate a single item, so that contractions can be handled size_t len = multiply_size(ndarray->itemsize, MAX(1, ndarray->len)); uint8_t *array = m_new0(byte, len); diff --git a/code/numpy/create.c b/code/numpy/create.c index 0f5bce00..6c1c75fd 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -158,7 +158,7 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); } - if(isnan(start) || isnan(stop) || isnan(step)) { + if(!isfinite(start) || !isfinite(stop) || !isfinite(step)) { mp_raise_ValueError(translate("arange: cannot compute length")); } diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 97ee5c75..62eb1688 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -161,7 +161,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { } #endif // p had better be a one-dimensional standard iterable - uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p)); + size_t plen = (size_t)mp_obj_get_int(mp_obj_len_maybe(o_p)); mp_float_t *p = m_new(mp_float_t, plen); mp_obj_iter_buf_t p_buf; mp_obj_t p_item, p_iterable = mp_getiter(o_p, &p_buf); diff --git a/code/ulab.c b/code/ulab.c index 21d72b80..ff3b5831 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.3.0 +#define ULAB_VERSION 6.3.1 #define xstr(s) str(s) #define str(s) #s From 319df10cfe9cb8f6e5e24627bda3fe1556d8b06b Mon Sep 17 00:00:00 2001 From: FelixNumworks <99270678+FelixNumworks@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:21:50 +0200 Subject: [PATCH 067/113] [ndarray] Fix ndarray_from_tuple reading out of _shape->items bounds (#630) --- code/ndarray.c | 6 +++--- code/ulab.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 0741edc4..45418be2 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -652,10 +652,10 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt // the function should work in the general n-dimensional case size_t *shape = m_new(size_t, ULAB_MAX_DIMS); for(size_t i=0; i < ULAB_MAX_DIMS; i++) { - if(i < ULAB_MAX_DIMS - _shape->len) { - shape[i] = 0; + if(i >= _shape->len) { + shape[ULAB_MAX_DIMS - i] = 0; } else { - shape[i] = mp_obj_get_int(_shape->items[i]); + shape[ULAB_MAX_DIMS - i] = mp_obj_get_int(_shape->items[i]); } } return ndarray_new_dense_ndarray(_shape->len, shape, dtype); diff --git a/code/ulab.c b/code/ulab.c index ff3b5831..fc770e9b 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.3.1 +#define ULAB_VERSION 6.3.2 #define xstr(s) str(s) #define str(s) #s From 112d4f82d33e938775cabf8aee6844717d54c538 Mon Sep 17 00:00:00 2001 From: HugoNumworks <67104016+HugoNumworks@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:13:53 +0200 Subject: [PATCH 068/113] Polyval handles non-array as second argument (#601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Factorize polynomial evaluation * Polyval handles non-array as second argument --------- Co-authored-by: Zoltán Vörös --- code/numpy/poly.c | 33 +++++++++++++++++---------------- code/ulab.c | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 62eb1688..ff4965d8 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -145,9 +145,18 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit); #if ULAB_NUMPY_HAS_POLYVAL +static mp_float_t poly_eval(mp_float_t x, mp_float_t *p, uint8_t plen) { + mp_float_t y = p[0]; + for(uint8_t j=0; j < plen-1; j++) { + y *= x; + y += p[j+1]; + } + return y; +} + mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { - if(!ndarray_object_is_array_like(o_p) || !ndarray_object_is_array_like(o_x)) { - mp_raise_TypeError(translate("inputs are not iterable")); + if(!ndarray_object_is_array_like(o_p)) { + mp_raise_TypeError(translate("input is not iterable")); } #if ULAB_SUPPORTS_COMPLEX ndarray_obj_t *input; @@ -171,6 +180,10 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { i++; } + if(!ndarray_object_is_array_like(o_x)) { + return mp_obj_new_float(poly_eval(mp_obj_get_float(o_x), p, plen)); + } + // polynomials are going to be of type float, except, when both // the coefficients and the independent variable are integers ndarray_obj_t *ndarray; @@ -198,13 +211,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { #endif size_t l = 0; do { - mp_float_t y = p[0]; - mp_float_t _x = func(sarray); - for(uint8_t m=0; m < plen-1; m++) { - y *= _x; - y += p[m+1]; - } - *array++ = y; + *array++ = poly_eval(func(sarray), p, plen); sarray += source->strides[ULAB_MAX_DIMS - 1]; l++; } while(l < source->shape[ULAB_MAX_DIMS - 1]); @@ -233,13 +240,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { mp_obj_iter_buf_t x_buf; mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf); while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) { - mp_float_t _x = mp_obj_get_float(x_item); - mp_float_t y = p[0]; - for(uint8_t j=0; j < plen-1; j++) { - y *= _x; - y += p[j+1]; - } - *array++ = y; + *array++ = poly_eval(mp_obj_get_float(x_item), p, plen); } } m_del(mp_float_t, p, plen); diff --git a/code/ulab.c b/code/ulab.c index fc770e9b..07113d18 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.3.2 +#define ULAB_VERSION 6.3.3 #define xstr(s) str(s) #define str(s) #s From 38caf84b2d8ab16bbab51991dc47e8276ec5a55f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 28 Jun 2023 14:52:04 -0500 Subject: [PATCH 069/113] Fix -Wunused-variable diagnostic when !ULAB_SUPPORTS_COMPLEX (#631) * Fix -Wunused-variable diagnostic when !ULAB_SUPPORTS_COMPLEX `o_in` is only used in the SUPPORTS_COMPLEX case, so the variable definition needs to be moved accordingly. * update version and changelog --- code/numpy/vector.c | 2 +- code/ulab.c | 2 +- docs/ulab-change-log.md | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 4dffa30c..3cb1a7b9 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -840,9 +840,9 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t o_in = args[0].u_obj; #if ULAB_SUPPORTS_COMPLEX + mp_obj_t o_in = args[0].u_obj; uint8_t dtype = mp_obj_get_int(args[2].u_obj); if((dtype != NDARRAY_FLOAT) && (dtype != NDARRAY_COMPLEX)) { mp_raise_TypeError(translate("dtype must be float, or complex")); diff --git a/code/ulab.c b/code/ulab.c index 07113d18..5a335b3e 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.3.3 +#define ULAB_VERSION 6.3.4 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 6cddfc5c..d5993072 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,5 +1,21 @@ Fri, 12 May 2023 +version 6.3.4 + + fix compile error when COMPLEX support not enabled + +version 6.3.3 + + Polyval handles non-array as second argument (#601) + +version 6.3.2 + + fix out of bound read + +version 6.3.1 + + fix integer overflows + version 6.3.0 add bitwise operators From d072086c56d32d687507edeadd856012a31134dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 2 Jul 2023 10:02:29 +0200 Subject: [PATCH 070/113] allow function iterator in math functions (#633) * allow function iterator in math functions * increment version number --- code/numpy/vector.c | 43 +++++++++++++++++++++++++++++++++++++++++ code/numpy/vector.h | 35 ++++++++++++++++++++++++++------- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 4 files changed, 78 insertions(+), 8 deletions(-) diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 3cb1a7b9..3e042b10 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -87,6 +87,48 @@ static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, m uint8_t *sarray = (uint8_t *)source->array; + #if ULAB_VECTORISE_USES_FUN_POINTER + + mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t value = func(sarray); + *tarray++ = f(value); + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif /* ULAB_MAX_DIMS > 1 */ + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif /* ULAB_MAX_DIMS > 2 */ + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif /* ULAB_MAX_DIMS > 3 */ + #else if(source->dtype == NDARRAY_UINT8) { ITERATE_VECTOR(uint8_t, target, tarray, tstrides, source, sarray); } else if(source->dtype == NDARRAY_INT8) { @@ -98,6 +140,7 @@ static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, m } else { ITERATE_VECTOR(mp_float_t, target, tarray, tstrides, source, sarray); } + #endif /* ULAB_VECTORISE_USES_FUN_POINTER */ } else { target = ndarray_from_mp_obj(o_in, 0); mp_float_t *tarray = (mp_float_t *)target->array; diff --git a/code/numpy/vector.h b/code/numpy/vector.h index ade12a40..2d22af8f 100644 --- a/code/numpy/vector.h +++ b/code/numpy/vector.h @@ -90,22 +90,24 @@ typedef struct _vectorized_function_obj_t { const mp_obj_type_t *type; } vectorized_function_obj_t; + +#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + #if ULAB_HAS_FUNCTION_ITERATOR -#define ITERATE_VECTOR(type, array, source, sarray, shift)\ +#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray)\ ({\ size_t *scoords = ndarray_new_coords((source)->ndim);\ - for(size_t i=0; i < (source)->len/(source)->shape[ULAB_MAX_DIMS -1]; i++) {\ - for(size_t l=0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\ - *(array) = f(*((type *)(sarray)));\ - (array) += (shift);\ + for(size_t i = 0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\ + for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\ + *(tarray) = f(*((type *)(sarray)));\ + (tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ }\ ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\ }\ }) -#endif /* ULAB_HAS_FUNCTION_ITERATOR */ -#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD +#else #if ULAB_MAX_DIMS == 1 #define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\ @@ -202,6 +204,7 @@ typedef struct _vectorized_function_obj_t { } while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\ } while(0) #endif /* ULAB_MAX_DIMS == 4 */ +#endif /* ULAB_HAS_FUNCTION_ITERATOR */ #define MATH_FUN_1(py_name, c_name) \ static mp_obj_t vector_ ## py_name(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { \ @@ -210,6 +213,22 @@ typedef struct _vectorized_function_obj_t { #else /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ +#if ULAB_HAS_FUNCTION_ITERATOR +#define ITERATE_VECTOR(type, array, source, sarray, shift)\ +({\ + size_t *scoords = ndarray_new_coords((source)->ndim);\ + for(size_t i=0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\ + for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\ + *(array) = f(*((type *)(sarray)));\ + (array)++;\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + }\ + ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\ + }\ +}) + +#else + #if ULAB_MAX_DIMS == 1 #define ITERATE_VECTOR(type, array, source, sarray) do {\ size_t l = 0;\ @@ -290,6 +309,8 @@ typedef struct _vectorized_function_obj_t { } while(0) #endif /* ULAB_MAX_DIMS == 4 */ +#endif /* ULAB_HAS_FUNCTION_ITERATOR */ + #define MATH_FUN_1(py_name, c_name) \ static mp_obj_t vector_ ## py_name(mp_obj_t x_obj) { \ return vector_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \ diff --git a/code/ulab.c b/code/ulab.c index 5a335b3e..d731aac4 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.3.4 +#define ULAB_VERSION 6.3.5 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index d5993072..9dea370b 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sat, 1 Jul 2023 + +version 6.3.5 + + allow function itertor in math functions with the out keyword + Fri, 12 May 2023 version 6.3.4 From d025aa3cf6a919e32e68f7d849b31f22aee49109 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 17 Jul 2023 03:10:23 -0500 Subject: [PATCH 071/113] Per github docs, run apt-get update (#637) https://docs.github.com/en/actions/using-github-hosted-runners/customizing-github-hosted-runners --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5689025..8900bc71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: - name: Install requirements run: | if type -path apt-get; then - sudo apt-get install gcc-multilib + sudo apt update && sudo apt-get install gcc-multilib fi - name: Versions @@ -84,7 +84,7 @@ jobs: - name: Install requirements run: | if type -path apt-get; then - sudo apt-get install gettext librsvg2-bin + sudo apt update && sudo apt-get install gettext librsvg2-bin else brew install gettext librsvg echo >>$GITHUB_PATH /usr/local/opt/gettext/bin From 84f99f17fc02b03c13f99485d9cf68bc9d17c600 Mon Sep 17 00:00:00 2001 From: KB Sriram Date: Mon, 17 Jul 2023 13:03:30 -0700 Subject: [PATCH 072/113] numpy/vector.c: remove usage of fpclassify (#636) Fixes https://github.com/v923z/micropython-ulab/issues/635 Verified by re-compiling circuitpython with this change. --- code/numpy/vector.c | 2 +- tests/1d/numpy/universal_functions.py | 7 +++++++ tests/1d/numpy/universal_functions.py.exp | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 3e042b10..b9a4cc68 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -827,7 +827,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); //| static mp_float_t vector_sinc1(mp_float_t x) { - if (fpclassify(x) == FP_ZERO) { + if (x == MICROPY_FLOAT_CONST(0.)) { return MICROPY_FLOAT_CONST(1.); } x *= MP_PI; diff --git a/tests/1d/numpy/universal_functions.py b/tests/1d/numpy/universal_functions.py index c58d9333..c41cf948 100644 --- a/tests/1d/numpy/universal_functions.py +++ b/tests/1d/numpy/universal_functions.py @@ -112,6 +112,13 @@ cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) print(cmp_result) +result = np.sinc(x) +ref_result = np.array([0.03935584386392389, -0.04359862862918773, 1.0, -0.04359862862918773, 0.03935584386392389]) +cmp_result = [] +for i in range(len(x)): + cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) +print(cmp_result) + result = (spy.special.erf(np.linspace(-3, 3, num=5))) ref_result = np.array([-0.9999779095030014, -0.9661051464753108, 0.0, 0.9661051464753108, 0.9999779095030014], dtype=np.float) cmp_result = [] diff --git a/tests/1d/numpy/universal_functions.py.exp b/tests/1d/numpy/universal_functions.py.exp index d881bf68..919c94a4 100644 --- a/tests/1d/numpy/universal_functions.py.exp +++ b/tests/1d/numpy/universal_functions.py.exp @@ -28,5 +28,6 @@ True [True, True, True, True, True] [True, True, True, True, True] [True, True, True, True, True] +[True, True, True, True, True] [True, True, True, True] [True, True, True] From 5279de73abddf75f702622661346a79f39896aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 21 Jul 2023 21:57:31 +0200 Subject: [PATCH 073/113] implement AND, OR, XOR binary operators (#639) * implement AND, OR, XOR binary operators * fix unterminated if * add missing linebreak * add more linebreaks * remove leading linebreak --- code/ndarray.c | 6 ++ code/ndarray_operators.c | 188 ++++++++++++++++++++++++++++++++++++++ code/ndarray_operators.h | 1 + code/ulab.c | 2 +- code/ulab.h | 12 +++ docs/ulab-change-log.md | 6 ++ tests/2d/numpy/and.py | 21 +++++ tests/2d/numpy/and.py.exp | 20 ++++ tests/2d/numpy/or.py | 21 +++++ tests/2d/numpy/or.py.exp | 20 ++++ tests/2d/numpy/xor.py | 21 +++++ tests/2d/numpy/xor.py.exp | 20 ++++ 12 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 tests/2d/numpy/and.py create mode 100644 tests/2d/numpy/and.py.exp create mode 100644 tests/2d/numpy/or.py create mode 100644 tests/2d/numpy/or.py.exp create mode 100644 tests/2d/numpy/xor.py create mode 100644 tests/2d/numpy/xor.py.exp diff --git a/code/ndarray.c b/code/ndarray.c index 45418be2..dd4161f1 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1949,6 +1949,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { return ndarray_binary_power(lhs, rhs, ndim, shape, lstrides, rstrides); break; #endif + #if NDARRAY_HAS_BINARY_OP_OR | NDARRAY_HAS_BINARY_OP_XOR | NDARRAY_HAS_BINARY_OP_AND + case MP_BINARY_OP_OR: + case MP_BINARY_OP_XOR: + case MP_BINARY_OP_AND: + return ndarray_binary_logical(lhs, rhs, ndim, shape, lstrides, rstrides, op); + #endif #if NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE case MP_BINARY_OP_FLOOR_DIVIDE: COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index 3983dabc..f96f981f 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -857,6 +857,194 @@ mp_obj_t ndarray_binary_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } #endif /* NDARRAY_HAS_BINARY_OP_POWER */ +#if NDARRAY_HAS_BINARY_OP_OR | NDARRAY_HAS_BINARY_OP_XOR | NDARRAY_HAS_BINARY_OP_AND +mp_obj_t ndarray_binary_logical(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) { + + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX) || (lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("operation not supported for the input types")); + } + #else + if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("operation not supported for the input types")); + } + #endif + + // bail out, if both inputs are of 16-bit types, but differ in sign; + // numpy promotes the result to int32 + if(((lhs->dtype == NDARRAY_INT16) && (rhs->dtype == NDARRAY_UINT16)) || + ((lhs->dtype == NDARRAY_UINT16) && (rhs->dtype == NDARRAY_INT16))) { + mp_raise_TypeError(translate("dtype of int32 is not supported")); + } + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + + switch(op) { + case MP_BINARY_OP_XOR: + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + if(lhs->boolean & rhs->boolean) { + results->boolean = 1; + } + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } else { + return ndarray_binary_op(MP_BINARY_OP_XOR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ^); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } else { + return ndarray_binary_op(MP_BINARY_OP_XOR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ^); + } else { + return ndarray_binary_op(MP_BINARY_OP_XOR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } + break; + + case MP_BINARY_OP_OR: + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + if(lhs->boolean & rhs->boolean) { + results->boolean = 1; + } + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, |); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, |); + } else { + return ndarray_binary_op(MP_BINARY_OP_OR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, |); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, |); + } else { + return ndarray_binary_op(MP_BINARY_OP_OR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, |); + } else { + return ndarray_binary_op(MP_BINARY_OP_OR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } + break; + + case MP_BINARY_OP_AND: + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + if(lhs->boolean & rhs->boolean) { + results->boolean = 1; + } + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, &); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, &); + } else { + return ndarray_binary_op(MP_BINARY_OP_AND, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, &); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, &); + } else { + return ndarray_binary_op(MP_BINARY_OP_AND, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, &); + } else { + return ndarray_binary_op(MP_BINARY_OP_AND, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs)); + } + } + break; + default: + return MP_OBJ_NULL; // op not supported + break; + } + return MP_OBJ_FROM_PTR(results); +} + +#endif /* NDARRAY_HAS_BINARY_OP_OR | NDARRAY_HAS_BINARY_OP_XOR | NDARRAY_HAS_BINARY_OP_AND */ + #if NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides, uint8_t optype) { diff --git a/code/ndarray_operators.h b/code/ndarray_operators.h index b8f080fa..f0f3c89d 100644 --- a/code/ndarray_operators.h +++ b/code/ndarray_operators.h @@ -17,6 +17,7 @@ mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t ); diff --git a/code/ulab.c b/code/ulab.c index d731aac4..f8e35bb9 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.3.5 +#define ULAB_VERSION 6.4.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index b063bcac..26df82fb 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -97,6 +97,10 @@ #define NDARRAY_HAS_BINARY_OP_ADD (1) #endif +#ifndef NDARRAY_HAS_BINARY_OP_AND +#define NDARRAY_HAS_BINARY_OP_AND (1) +#endif + #ifndef NDARRAY_HAS_BINARY_OP_EQUAL #define NDARRAY_HAS_BINARY_OP_EQUAL (1) #endif @@ -129,6 +133,10 @@ #define NDARRAY_HAS_BINARY_OP_NOT_EQUAL (1) #endif +#ifndef NDARRAY_HAS_BINARY_OP_OR +#define NDARRAY_HAS_BINARY_OP_OR (1) +#endif + #ifndef NDARRAY_HAS_BINARY_OP_POWER #define NDARRAY_HAS_BINARY_OP_POWER (1) #endif @@ -141,6 +149,10 @@ #define NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE (1) #endif +#ifndef NDARRAY_HAS_BINARY_OP_XOR +#define NDARRAY_HAS_BINARY_OP_XOR (1) +#endif + #ifndef NDARRAY_HAS_INPLACE_OPS #define NDARRAY_HAS_INPLACE_OPS (1) #endif diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 9dea370b..ad4021a4 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Thu, 20 Jul 2023 + +version 6.4.0 + + implement AND, OR, and XOR binary operators + Sat, 1 Jul 2023 version 6.3.5 diff --git a/tests/2d/numpy/and.py b/tests/2d/numpy/and.py new file mode 100644 index 00000000..0c881a19 --- /dev/null +++ b/tests/2d/numpy/and.py @@ -0,0 +1,21 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype_a in dtypes: + a = np.array(range(5), dtype=dtype_a) + for dtype_b in dtypes: + b = np.array(range(250, 255), dtype=dtype_b) + try: + print('a & b: ', a & b) + except Exception as e: + print(e) + + b = np.array([False, True, False, True, False], dtype=np.bool) + try: + print('a & b (bool): ', a & b) + except Exception as e: + print(e) diff --git a/tests/2d/numpy/and.py.exp b/tests/2d/numpy/and.py.exp new file mode 100644 index 00000000..43a7cd1d --- /dev/null +++ b/tests/2d/numpy/and.py.exp @@ -0,0 +1,20 @@ +a & b: array([0, 1, 0, 1, 4], dtype=uint8) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b: array([0, 1, 0, 1, 4], dtype=uint16) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b (bool): array([0, 1, 0, 1, 0], dtype=uint8) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b: array([0, 1, 0, 1, 4], dtype=int8) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b (bool): array([0, 1, 0, 1, 0], dtype=int16) +a & b: array([0, 1, 0, 1, 4], dtype=uint16) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b: array([0, 1, 0, 1, 4], dtype=uint16) +dtype of int32 is not supported +a & b (bool): array([0, 1, 0, 1, 0], dtype=uint16) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b: array([0, 1, 0, 1, 4], dtype=int16) +dtype of int32 is not supported +a & b: array([0, 1, 0, 1, 4], dtype=int16) +a & b (bool): array([0, 1, 0, 1, 0], dtype=int16) diff --git a/tests/2d/numpy/or.py b/tests/2d/numpy/or.py new file mode 100644 index 00000000..5788843e --- /dev/null +++ b/tests/2d/numpy/or.py @@ -0,0 +1,21 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype_a in dtypes: + a = np.array(range(5), dtype=dtype_a) + for dtype_b in dtypes: + b = np.array(range(250, 255), dtype=dtype_b) + try: + print('a | b: ', a | b) + except Exception as e: + print(e) + + b = np.array([False, True, False, True, False], dtype=np.bool) + try: + print('a | b (bool): ', a | b) + except Exception as e: + print(e) \ No newline at end of file diff --git a/tests/2d/numpy/or.py.exp b/tests/2d/numpy/or.py.exp new file mode 100644 index 00000000..d3395dd3 --- /dev/null +++ b/tests/2d/numpy/or.py.exp @@ -0,0 +1,20 @@ +a | b: array([250, 251, 254, 255, 254], dtype=uint8) +a | b: array([-6, -5, -2, -1, -2], dtype=int16) +a | b: array([250, 251, 254, 255, 254], dtype=uint16) +a | b: array([250, 251, 254, 255, 254], dtype=int16) +a | b (bool): array([0, 1, 2, 3, 4], dtype=uint8) +a | b: array([250, 251, 254, 255, 254], dtype=int16) +a | b: array([-6, -5, -2, -1, -2], dtype=int8) +a | b: array([250, 251, 254, 255, 254], dtype=int16) +a | b: array([250, 251, 254, 255, 254], dtype=int16) +a | b (bool): array([0, 1, 2, 3, 4], dtype=int16) +a | b: array([250, 251, 254, 255, 254], dtype=uint16) +a | b: array([-6, -5, -2, -1, -2], dtype=int16) +a | b: array([250, 251, 254, 255, 254], dtype=uint16) +dtype of int32 is not supported +a | b (bool): array([0, 1, 2, 3, 4], dtype=uint16) +a | b: array([250, 251, 254, 255, 254], dtype=int16) +a | b: array([-6, -5, -2, -1, -2], dtype=int16) +dtype of int32 is not supported +a | b: array([250, 251, 254, 255, 254], dtype=int16) +a | b (bool): array([0, 1, 2, 3, 4], dtype=int16) diff --git a/tests/2d/numpy/xor.py b/tests/2d/numpy/xor.py new file mode 100644 index 00000000..f571dce3 --- /dev/null +++ b/tests/2d/numpy/xor.py @@ -0,0 +1,21 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) + +for dtype_a in dtypes: + a = np.array(range(5), dtype=dtype_a) + for dtype_b in dtypes: + b = np.array(range(250, 255), dtype=dtype_b) + try: + print('a ^ b: ', a ^ b) + except Exception as e: + print(e) + + b = np.array([False, True, False, True, False], dtype=np.bool) + try: + print('a ^ b (bool): ', a ^ b) + except Exception as e: + print(e) \ No newline at end of file diff --git a/tests/2d/numpy/xor.py.exp b/tests/2d/numpy/xor.py.exp new file mode 100644 index 00000000..d6352d1a --- /dev/null +++ b/tests/2d/numpy/xor.py.exp @@ -0,0 +1,20 @@ +a ^ b: array([250, 250, 254, 254, 250], dtype=uint8) +a ^ b: array([-6, -6, -2, -2, -6], dtype=int16) +a ^ b: array([250, 250, 254, 254, 250], dtype=uint16) +a ^ b: array([250, 250, 254, 254, 250], dtype=int16) +a ^ b (bool): array([0, 0, 2, 2, 4], dtype=uint8) +a ^ b: array([250, 250, 254, 254, 250], dtype=int16) +a ^ b: array([-6, -6, -2, -2, -6], dtype=int8) +a ^ b: array([250, 250, 254, 254, 250], dtype=int16) +a ^ b: array([250, 250, 254, 254, 250], dtype=int16) +a ^ b (bool): array([0, 0, 2, 2, 4], dtype=int16) +a ^ b: array([250, 250, 254, 254, 250], dtype=uint16) +a ^ b: array([-6, -6, -2, -2, -6], dtype=int16) +a ^ b: array([250, 250, 254, 254, 250], dtype=uint16) +dtype of int32 is not supported +a ^ b (bool): array([0, 0, 2, 2, 4], dtype=uint16) +a ^ b: array([250, 250, 254, 254, 250], dtype=int16) +a ^ b: array([-6, -6, -2, -2, -6], dtype=int16) +dtype of int32 is not supported +a ^ b: array([250, 250, 254, 254, 250], dtype=int16) +a ^ b (bool): array([0, 0, 2, 2, 4], dtype=int16) From a05ec05351260cf48fefc347265b8d8bf29c03f1 Mon Sep 17 00:00:00 2001 From: Xuebin Ruan <39629131+qqice@users.noreply.github.com> Date: Thu, 10 Aug 2023 16:25:19 +0800 Subject: [PATCH 074/113] Fix #643 (#645) * Fix #643 * Update to version 6.4.1 --- code/ndarray.c | 2 +- code/ulab.c | 2 +- docs/ulab-change-log.md | 24 ++++++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index dd4161f1..e7aeb88c 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1738,7 +1738,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { mp_float_t *array = (mp_float_t *)ndarray->array; array[0] = mp_obj_get_float(obj); } else if(mp_obj_is_bool(obj)) { - ndarray = ndarray_new_linear_array(1, NDARRAY_BOOLEAN); + ndarray = ndarray_new_linear_array(1, NDARRAY_BOOL); uint8_t *array = (uint8_t *)ndarray->array; if(obj == mp_const_true) { *array = 1; diff --git a/code/ulab.c b/code/ulab.c index f8e35bb9..7487144b 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.4.0 +#define ULAB_VERSION 6.4.1 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index ad4021a4..ff921249 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,11 @@ +Thu, 10 Aug 2023 + +version 6.4.1 + +``` +fix BOOLEAN issue, which would cause numpy.where funciton abnormally on RP2040(#643) +``` + Thu, 20 Jul 2023 version 6.4.0 @@ -31,13 +39,13 @@ version 6.3.1 version 6.3.0 add bitwise operators - + Wed, 17 May 2023 version 6.1.1 fix ndarray subscription, when value is NULL - + Tue, 16 May 2023 version 6.1.0 @@ -55,7 +63,7 @@ Sun, 7 May 2023 version 6.0.12 ndarray_from_mp_obj correctly treats Boolean arguments - + Sat, 6 May 2023 version 6.0.11 @@ -67,19 +75,19 @@ Sat, 6 May 2023 version 6.0.10 fix binary division - + Sun, 21 Jan 2023 version 6.0.6 raise proper exception in arange - + Sun, 21 Jan 2023 version 6.0.7 treat empty arrays in sort_complex correctly - + Sun, 21 Jan 2023 version 6.0.5 @@ -91,7 +99,7 @@ Sun, 15 Jan 2023 version 6.0.4 fix dot function - + Sat, 14 Jan 2023 version 6.0.3 @@ -227,7 +235,7 @@ version 4.2.0 Wed, 12 Jan 2022 version 4.2.0 - + implement numpy.save, numpy.load Wed, 12 Jan 2022 From 2df210f87a79deedc9003b1bbd6015a2fea7c03e Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 22 Sep 2023 14:47:02 -0500 Subject: [PATCH 075/113] Drop certain CircuitPython workarounds that are no longer needed * ndarray_set_value: in CircuitPython 9 * mp_obj_slice_indices: ditto * Use modern MP_REGISTER_MODULE calls: ditto * use MP_OBJ_SENTINEL to forward to locals dict (was never necessary?) --- code/ndarray.c | 92 ---------------------------------- code/ndarray.h | 4 +- code/ndarray_properties.c | 28 +---------- code/ndarray_properties.h | 70 -------------------------- code/numpy/fft/fft.c | 4 -- code/numpy/linalg/linalg.c | 4 -- code/numpy/numpy.c | 4 -- code/scipy/linalg/linalg.c | 4 -- code/scipy/optimize/optimize.c | 4 -- code/scipy/scipy.c | 4 -- code/scipy/signal/signal.c | 4 -- code/scipy/special/special.c | 4 -- code/ulab.c | 28 ----------- code/user/user.c | 4 -- code/utils/utils.c | 4 -- 15 files changed, 3 insertions(+), 259 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 45418be2..1d60ef87 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -61,98 +61,6 @@ void ndarray_set_complex_value(void *p, size_t index, mp_obj_t value) { } } -#ifdef CIRCUITPY -void ndarray_set_value(char typecode, void *p, size_t index, mp_obj_t val_in) { - switch (typecode) { - case NDARRAY_INT8: - ((signed char *)p)[index] = mp_obj_get_int(val_in); - break; - case NDARRAY_UINT8: - ((unsigned char *)p)[index] = mp_obj_get_int(val_in); - break; - case NDARRAY_INT16: - ((short *)p)[index] = mp_obj_get_int(val_in); - break; - case NDARRAY_UINT16: - ((unsigned short *)p)[index] = mp_obj_get_int(val_in); - break; - case NDARRAY_FLOAT: - ((mp_float_t *)p)[index] = mp_obj_get_float(val_in); - break; - #if ULAB_SUPPORTS_COMPLEX - case NDARRAY_COMPLEX: - ndarray_set_complex_value(p, index, val_in); - break; - #endif - } -} -#endif - -#if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 11 - -void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result) { - mp_obj_slice_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t start, stop, step; - - if (self->step == mp_const_none) { - step = 1; - } else { - step = mp_obj_get_int(self->step); - if (step == 0) { - mp_raise_ValueError(translate("slice step can't be zero")); - } - } - - if (step > 0) { - // Positive step - if (self->start == mp_const_none) { - start = 0; - } else { - start = mp_obj_get_int(self->start); - if (start < 0) { - start += length; - } - start = MIN(length, MAX(start, 0)); - } - - if (self->stop == mp_const_none) { - stop = length; - } else { - stop = mp_obj_get_int(self->stop); - if (stop < 0) { - stop += length; - } - stop = MIN(length, MAX(stop, 0)); - } - } else { - // Negative step - if (self->start == mp_const_none) { - start = length - 1; - } else { - start = mp_obj_get_int(self->start); - if (start < 0) { - start += length; - } - start = MIN(length - 1, MAX(start, -1)); - } - - if (self->stop == mp_const_none) { - stop = -1; - } else { - stop = mp_obj_get_int(self->stop); - if (stop < 0) { - stop += length; - } - stop = MIN(length - 1, MAX(stop, -1)); - } - } - - result->start = start; - result->stop = stop; - result->step = step; -} -#endif /* MICROPY_VERSION v1.11 */ - void ndarray_fill_array_iterable(mp_float_t *array, mp_obj_t iterable) { mp_obj_iter_buf_t x_buf; mp_obj_t x_item, x_iterable = mp_getiter(iterable, &x_buf); diff --git a/code/ndarray.h b/code/ndarray.h index 4564f772..3025c051 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -113,11 +113,9 @@ typedef struct _mp_obj_slice_t { #if !CIRCUITPY #define translate(x) MP_ERROR_TEXT(x) -#define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d) -#else -void ndarray_set_value(char , void *, size_t , mp_obj_t ); #endif +#define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d) void ndarray_set_complex_value(void *, size_t , mp_obj_t ); #define NDARRAY_NUMERIC 0 diff --git a/code/ndarray_properties.c b/code/ndarray_properties.c index 547b1912..aa297158 100644 --- a/code/ndarray_properties.c +++ b/code/ndarray_properties.c @@ -24,29 +24,6 @@ #include "numpy/carray/carray.h" #endif -#ifndef CIRCUITPY - -// a somewhat hackish implementation of property getters/setters; -// this functions is hooked into the attr member of ndarray - -STATIC void call_local_method(mp_obj_t obj, qstr attr, mp_obj_t *dest) { - const mp_obj_type_t *type = mp_obj_get_type(obj); - while (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { - assert(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->base.type == &mp_type_dict); // MicroPython restriction, for now - mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; - mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - mp_convert_member_lookup(obj, type, elem->value, dest); - break; - } - if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { - break; - } - type = MP_OBJ_TYPE_GET_SLOT(type, parent); - } -} - - void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] == MP_OBJ_NULL) { switch(attr) { @@ -98,7 +75,8 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { #endif #endif /* ULAB_SUPPORTS_COMPLEX */ default: - call_local_method(self_in, attr, dest); + // forward to locals dict + dest[1] = MP_OBJ_SENTINEL; break; } } else { @@ -119,5 +97,3 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } } - -#endif /* CIRCUITPY */ diff --git a/code/ndarray_properties.h b/code/ndarray_properties.h index a074ec1f..3e0b9a40 100644 --- a/code/ndarray_properties.h +++ b/code/ndarray_properties.h @@ -22,74 +22,6 @@ #include "ndarray.h" #include "numpy/ndarray/ndarray_iter.h" -#if CIRCUITPY -typedef struct _mp_obj_property_t { - mp_obj_base_t base; - mp_obj_t proxy[3]; // getter, setter, deleter -} mp_obj_property_t; - -#if NDARRAY_HAS_DTYPE -MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_dtype_obj, ndarray_dtype); -STATIC const mp_obj_property_t ndarray_dtype_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&ndarray_get_dtype_obj, - mp_const_none, - mp_const_none }, -}; -#endif /* NDARRAY_HAS_DTYPE */ - -#if NDARRAY_HAS_FLATITER -MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_new); -STATIC const mp_obj_property_t ndarray_flat_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&ndarray_flatiter_make_new_obj, - mp_const_none, - mp_const_none }, -}; -#endif /* NDARRAY_HAS_FLATITER */ - -#if NDARRAY_HAS_ITEMSIZE -MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_itemsize_obj, ndarray_itemsize); -STATIC const mp_obj_property_t ndarray_itemsize_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&ndarray_get_itemsize_obj, - mp_const_none, - mp_const_none }, -}; -#endif /* NDARRAY_HAS_ITEMSIZE */ - -#if NDARRAY_HAS_SHAPE -MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_shape_obj, ndarray_shape); -STATIC const mp_obj_property_t ndarray_shape_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&ndarray_get_shape_obj, - mp_const_none, - mp_const_none }, -}; -#endif /* NDARRAY_HAS_SHAPE */ - -#if NDARRAY_HAS_SIZE -MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_size_obj, ndarray_size); -STATIC const mp_obj_property_t ndarray_size_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&ndarray_get_size_obj, - mp_const_none, - mp_const_none }, -}; -#endif /* NDARRAY_HAS_SIZE */ - -#if NDARRAY_HAS_STRIDES -MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_strides_obj, ndarray_strides); -STATIC const mp_obj_property_t ndarray_strides_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&ndarray_get_strides_obj, - mp_const_none, - mp_const_none }, -}; -#endif /* NDARRAY_HAS_STRIDES */ - -#else - void ndarray_properties_attr(mp_obj_t , qstr , mp_obj_t *); #if NDARRAY_HAS_DTYPE @@ -116,6 +48,4 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size); MP_DEFINE_CONST_FUN_OBJ_1(ndarray_strides_obj, ndarray_strides); #endif -#endif /* CIRCUITPY */ - #endif diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index ad4996e3..42621f52 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -101,9 +101,5 @@ const mp_obj_module_t ulab_fft_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module); #endif -#endif diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 0243a5d9..55ac4752 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -537,10 +537,6 @@ const mp_obj_module_t ulab_linalg_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module); #endif #endif -#endif diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index d530638e..74de9088 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -397,9 +397,5 @@ const mp_obj_module_t ulab_numpy_module = { }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module); #endif -#endif diff --git a/code/scipy/linalg/linalg.c b/code/scipy/linalg/linalg.c index 52c04afe..a42392ce 100644 --- a/code/scipy/linalg/linalg.c +++ b/code/scipy/linalg/linalg.c @@ -276,10 +276,6 @@ const mp_obj_module_t ulab_scipy_linalg_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_linalg_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module); #endif #endif -#endif diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index 7fc6b670..ef10288d 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -413,9 +413,5 @@ const mp_obj_module_t ulab_scipy_optimize_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_optimize_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module); #endif -#endif diff --git a/code/scipy/scipy.c b/code/scipy/scipy.c index c9f51df5..6895127b 100644 --- a/code/scipy/scipy.c +++ b/code/scipy/scipy.c @@ -49,10 +49,6 @@ const mp_obj_module_t ulab_scipy_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module); #endif -#endif #endif /* ULAB_HAS_SCIPY */ diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 6afa05f6..db8d3cc5 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -134,9 +134,5 @@ const mp_obj_module_t ulab_scipy_signal_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_signal_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module); #endif -#endif diff --git a/code/scipy/special/special.c b/code/scipy/special/special.c index 9d5ca629..0e561b6f 100644 --- a/code/scipy/special/special.c +++ b/code/scipy/special/special.c @@ -41,9 +41,5 @@ const mp_obj_module_t ulab_scipy_special_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_special_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module); #endif -#endif diff --git a/code/ulab.c b/code/ulab.c index d731aac4..bb0f8758 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -76,26 +76,6 @@ STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { #if NDARRAY_HAS_SORT { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) }, #endif - #ifdef CIRCUITPY - #if NDARRAY_HAS_DTYPE - { MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) }, - #endif - #if NDARRAY_HAS_FLATITER - { MP_ROM_QSTR(MP_QSTR_flat), MP_ROM_PTR(&ndarray_flat_obj) }, - #endif - #if NDARRAY_HAS_ITEMSIZE - { MP_ROM_QSTR(MP_QSTR_itemsize), MP_ROM_PTR(&ndarray_itemsize_obj) }, - #endif - #if NDARRAY_HAS_SHAPE - { MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) }, - #endif - #if NDARRAY_HAS_SIZE - { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&ndarray_size_obj) }, - #endif - #if NDARRAY_HAS_STRIDES - { MP_ROM_QSTR(MP_QSTR_strides), MP_ROM_PTR(&ndarray_strides_obj) }, - #endif - #endif /* CIRCUITPY */ }; STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table); @@ -167,9 +147,7 @@ const mp_obj_type_t ulab_ndarray_type = { #if NDARRAY_HAS_BINARY_OPS .binary_op = ndarray_binary_op, #endif - #ifndef CIRCUITPY .attr = ndarray_properties_attr, - #endif .buffer_p = { .get_buffer = ndarray_get_buffer, }, ) }; @@ -253,10 +231,4 @@ const mp_obj_module_t ulab_user_cmodule = { .globals = (mp_obj_dict_t*)&mp_module_ulab_globals, }; -// Use old three-argument MP_REGISTER_MODULE for -// MicroPython <= v1.18.0: (1 << 16) | (18 << 8) | 0 -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule); -#endif diff --git a/code/user/user.c b/code/user/user.c index dfc3fcde..88775fda 100644 --- a/code/user/user.c +++ b/code/user/user.c @@ -92,11 +92,7 @@ const mp_obj_module_t ulab_user_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module, ULAB_HAS_USER_MODULE); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module); #endif #endif -#endif diff --git a/code/utils/utils.c b/code/utils/utils.c index fee67d47..6a59baf3 100644 --- a/code/utils/utils.c +++ b/code/utils/utils.c @@ -250,11 +250,7 @@ const mp_obj_module_t ulab_utils_module = { .globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals, }; #if CIRCUITPY_ULAB -#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144 -MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module, MODULE_ULAB_ENABLED); -#else MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module); #endif -#endif #endif /* ULAB_HAS_UTILS_MODULE */ From 9c9e9532ac3755de5d6f779fcdcc12ea236ddd48 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 30 Oct 2023 09:48:45 +0100 Subject: [PATCH 076/113] Switch to using MP_ERROR_TEXT in CircuitPython, change ulab accordingly --- code/ndarray.c | 52 ++++++++++++++++---------------- code/ndarray.h | 4 --- code/ndarray_operators.c | 6 ++-- code/numpy/approx.c | 6 ++-- code/numpy/bitwise.c | 6 ++-- code/numpy/carray/carray.c | 10 +++--- code/numpy/carray/carray_tools.c | 2 +- code/numpy/compare.c | 6 ++-- code/numpy/create.c | 32 ++++++++++---------- code/numpy/fft/fft_tools.c | 18 +++++------ code/numpy/filter.c | 6 ++-- code/numpy/io/io.c | 22 +++++++------- code/numpy/linalg/linalg.c | 16 +++++----- code/numpy/numerical.c | 44 +++++++++++++-------------- code/numpy/poly.c | 14 ++++----- code/numpy/transform.c | 20 ++++++------ code/numpy/vector.c | 32 ++++++++++---------- code/scipy/linalg/linalg.c | 10 +++--- code/scipy/optimize/optimize.c | 22 +++++++------- code/scipy/signal/signal.c | 14 ++++----- code/ulab_tools.c | 10 +++--- code/user/user.c | 4 +-- code/utils/utils.c | 10 +++--- 23 files changed, 181 insertions(+), 185 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 1d60ef87..8fc72a7e 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -199,7 +199,7 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t if((_dtype != NDARRAY_BOOL) && (_dtype != NDARRAY_UINT8) && (_dtype != NDARRAY_INT8) && (_dtype != NDARRAY_UINT16) && (_dtype != NDARRAY_INT16) && (_dtype != NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("data type not understood")); + mp_raise_TypeError(MP_ERROR_TEXT("data type not understood")); } } else { GET_STR_DATA_LEN(_args[0].u_obj, _dtype_, len); @@ -220,7 +220,7 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t } #endif else { - mp_raise_TypeError(translate("data type not understood")); + mp_raise_TypeError(MP_ERROR_TEXT("data type not understood")); } } dtype->dtype = _dtype; @@ -252,7 +252,7 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) { && (*_dtype != NDARRAY_COMPLEX) #endif )) { - mp_raise_TypeError(translate("data type not understood")); + mp_raise_TypeError(MP_ERROR_TEXT("data type not understood")); } dtype = *_dtype; } @@ -504,7 +504,7 @@ bool ndarray_is_dense(ndarray_obj_t *ndarray) { static size_t multiply_size(size_t a, size_t b) { size_t result; if (__builtin_mul_overflow(a, b, &result)) { - mp_raise_ValueError(translate("array is too big")); + mp_raise_ValueError(MP_ERROR_TEXT("array is too big")); } return result; } @@ -531,7 +531,7 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides } if (SIZE_MAX / ndarray->itemsize <= ndarray->len) { - mp_raise_ValueError(translate("ndarray length overflows")); + mp_raise_ValueError(MP_ERROR_TEXT("ndarray length overflows")); } // if the length is 0, still allocate a single item, so that contractions can be handled @@ -690,7 +690,7 @@ ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *source, uint8_t dty #if ULAB_SUPPORTS_COMPLEX if(source->dtype == NDARRAY_COMPLEX) { if(dtype != NDARRAY_COMPLEX) { - mp_raise_TypeError(translate("cannot convert complex type")); + mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex type")); } else { memcpy(array, sarray, complex_size); } @@ -856,7 +856,7 @@ ndarray_obj_t *ndarray_from_iterable(mp_obj_t obj, uint8_t dtype) { break; } if(ndim == ULAB_MAX_DIMS) { - mp_raise_ValueError(translate("too many dimensions")); + mp_raise_ValueError(MP_ERROR_TEXT("too many dimensions")); } shape[ndim] = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(item)); if(shape[ndim] == 0) { @@ -1046,13 +1046,13 @@ static mp_bound_slice_t generate_slice(mp_int_t n, mp_obj_t index) { _index += n; } if((_index >= n) || (_index < 0)) { - mp_raise_msg(&mp_type_IndexError, translate("index is out of bounds")); + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("index is out of bounds")); } slice.start = _index; slice.stop = _index + 1; slice.step = 1; } else { - mp_raise_msg(&mp_type_IndexError, translate("indices must be integers, slices, or Boolean lists")); + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("indices must be integers, slices, or Boolean lists")); } return slice; } @@ -1078,7 +1078,7 @@ static ndarray_obj_t *ndarray_view_from_slices(ndarray_obj_t *ndarray, mp_obj_tu k += ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i]; } if((k >= (int32_t)ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i]) || (k < 0)) { - mp_raise_msg(&mp_type_IndexError, translate("index is out of bounds")); + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("index is out of bounds")); } offset += ndarray->strides[ULAB_MAX_DIMS - ndarray->ndim + i] * k; // ... and then we have to shift the shapes to the right @@ -1105,7 +1105,7 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS); int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS); if(!ndarray_can_broadcast(view, values, &ndim, shape, lstrides, rstrides)) { - mp_raise_ValueError(translate("operands could not be broadcast together")); + mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together")); } else { ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(values, view->dtype); @@ -1171,7 +1171,7 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { static mp_obj_t ndarray_from_boolean_index(ndarray_obj_t *ndarray, ndarray_obj_t *index) { // returns a 1D array, indexed by a Boolean array if(ndarray->len != index->len) { - mp_raise_ValueError(translate("array and index length must be equal")); + mp_raise_ValueError(MP_ERROR_TEXT("array and index length must be equal")); } uint8_t *iarray = (uint8_t *)index->array; // first we have to find out how many trues there are @@ -1223,7 +1223,7 @@ static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarra #if ULAB_SUPPORTS_COMPLEX if(values->dtype == NDARRAY_COMPLEX) { if(ndarray->dtype != NDARRAY_COMPLEX) { - mp_raise_TypeError(translate("cannot convert complex to dtype")); + mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex to dtype")); } else { uint8_t *array = (uint8_t *)ndarray->array; for(size_t i = 0; i < ndarray->len; i++) { @@ -1314,7 +1314,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra if(mp_obj_is_type(index, &ulab_ndarray_type)) { ndarray_obj_t *nindex = MP_OBJ_TO_PTR(index); if((nindex->ndim > 1) || (nindex->boolean == false)) { - mp_raise_NotImplementedError(translate("operation is implemented for 1D Boolean arrays only")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("operation is implemented for 1D Boolean arrays only")); } if(values == NULL) { // return value(s) return ndarray_from_boolean_index(ndarray, nindex); @@ -1327,7 +1327,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra if(mp_obj_is_type(index, &mp_type_tuple)) { tuple = MP_OBJ_TO_PTR(index); if(tuple->len > ndarray->ndim) { - mp_raise_msg(&mp_type_IndexError, translate("too many indices")); + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("too many indices")); } } else { mp_obj_t *items = m_new(mp_obj_t, 1); @@ -1351,7 +1351,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra mp_obj_t ndarray_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { if(value == MP_OBJ_NULL) { - mp_raise_ValueError(translate("cannot delete array elements")); + mp_raise_ValueError(MP_ERROR_TEXT("cannot delete array elements")); } ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -1429,7 +1429,7 @@ mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a ndarray_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); GET_STR_DATA_LEN(args[0].u_obj, order, len); if((len != 1) || ((memcmp(order, "C", 1) != 0) && (memcmp(order, "F", 1) != 0))) { - mp_raise_ValueError(translate("flattening order must be either 'C', or 'F'")); + mp_raise_ValueError(MP_ERROR_TEXT("flattening order must be either 'C', or 'F'")); } uint8_t *sarray = (uint8_t *)self->array; @@ -1567,7 +1567,7 @@ mp_obj_t ndarray_tobytes(mp_obj_t self_in) { // Piping into a bytearray makes sense for dense arrays only, // so bail out, if that is not the case if(!ndarray_is_dense(self)) { - mp_raise_ValueError(translate("tobytes can be invoked for dense arrays only")); + mp_raise_ValueError(MP_ERROR_TEXT("tobytes can be invoked for dense arrays only")); } return mp_obj_new_bytearray_by_ref(self->itemsize * self->len, self->array); } @@ -1707,7 +1707,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { broadcastable = ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides); } if(!broadcastable) { - mp_raise_ValueError(translate("operands could not be broadcast together")); + mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together")); m_del(size_t, shape, ULAB_MAX_DIMS); m_del(int32_t, lstrides, ULAB_MAX_DIMS); m_del(int32_t, rstrides, ULAB_MAX_DIMS); @@ -1917,7 +1917,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { #else if(self->dtype == NDARRAY_FLOAT) { #endif - mp_raise_ValueError(translate("operation is not supported for given type")); + mp_raise_ValueError(MP_ERROR_TEXT("operation is not supported for given type")); } // we can invert the content byte by byte, no need to distinguish between different dtypes ndarray = ndarray_copy_view(self); // from this point, this is a dense copy @@ -2006,7 +2006,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_transpose_obj, ndarray_transpose); mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { ndarray_obj_t *source = MP_OBJ_TO_PTR(oin); if(!mp_obj_is_type(_shape, &mp_type_tuple) && !mp_obj_is_int(_shape)) { - mp_raise_TypeError(translate("shape must be integer or tuple of integers")); + mp_raise_TypeError(MP_ERROR_TEXT("shape must be integer or tuple of integers")); } mp_obj_tuple_t *shape; @@ -2020,7 +2020,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { } if(shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); } size_t new_length = 1; @@ -2040,14 +2040,14 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { } if(unknown_dim > 1) { - mp_raise_ValueError(translate("can only specify one unknown dimension")); + mp_raise_ValueError(MP_ERROR_TEXT("can only specify one unknown dimension")); } else if(unknown_dim == 1) { new_shape[unknown_index] = source->len / new_length; new_length = source->len; } if(source->len != new_length) { - mp_raise_ValueError(translate("cannot reshape array")); + mp_raise_ValueError(MP_ERROR_TEXT("cannot reshape array")); } ndarray_obj_t *ndarray; @@ -2064,7 +2064,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { } } else { if(inplace) { - mp_raise_ValueError(translate("cannot assign new shape")); + mp_raise_ValueError(MP_ERROR_TEXT("cannot assign new shape")); } if(mp_obj_is_type(_shape, &mp_type_tuple)) { ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); @@ -2087,7 +2087,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(ndarray_reshape_obj, ndarray_reshape); #if ULAB_NUMPY_HAS_NDINFO mp_obj_t ndarray_info(mp_obj_t obj_in) { if(!mp_obj_is_type(obj_in, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("function is defined for ndarrays only")); + mp_raise_TypeError(MP_ERROR_TEXT("function is defined for ndarrays only")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj_in); mp_printf(MP_PYTHON_PRINTER, "class: ndarray\n"); diff --git a/code/ndarray.h b/code/ndarray.h index 3025c051..ec8b3ee7 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -111,10 +111,6 @@ typedef struct _mp_obj_slice_t { #endif #endif -#if !CIRCUITPY -#define translate(x) MP_ERROR_TEXT(x) -#endif - #define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d) void ndarray_set_complex_value(void *, size_t , mp_obj_t ); diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index 3983dabc..5c07e8e8 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -861,7 +861,7 @@ mp_obj_t ndarray_binary_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides, uint8_t optype) { if((lhs->dtype != NDARRAY_FLOAT) && (rhs->dtype == NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("cannot cast output with casting rule")); + mp_raise_TypeError(MP_ERROR_TEXT("cannot cast output with casting rule")); } uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; @@ -890,7 +890,7 @@ mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rs mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { if((lhs->dtype != NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("results cannot be cast to specified type")); + mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type")); } uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; @@ -914,7 +914,7 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t mp_obj_t ndarray_inplace_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { if((lhs->dtype != NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("results cannot be cast to specified type")); + mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type")); } uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 6088173d..23f9da1b 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -65,7 +65,7 @@ STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t COMPLEX_DTYPE_NOT_IMPLEMENTED(xp->dtype) COMPLEX_DTYPE_NOT_IMPLEMENTED(fp->dtype) if((xp->ndim != 1) || (fp->ndim != 1) || (xp->len < 2) || (fp->len < 2) || (xp->len != fp->len)) { - mp_raise_ValueError(translate("interp is defined for 1D iterables of equal length")); + mp_raise_ValueError(MP_ERROR_TEXT("interp is defined for 1D iterables of equal length")); } ndarray_obj_t *y = ndarray_new_linear_array(x->len, NDARRAY_FLOAT); @@ -168,7 +168,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * return mp_obj_new_float(mean); } if((y->ndim != 1)) { - mp_raise_ValueError(translate("trapz is defined for 1D iterables")); + mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D iterables")); } mp_float_t (*funcy)(void *) = ndarray_get_float_function(y->dtype); @@ -181,7 +181,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * x = ndarray_from_mp_obj(args[1].u_obj, 0); // x must hold an increasing sequence of independent values COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) if((x->ndim != 1) || (y->len != x->len)) { - mp_raise_ValueError(translate("trapz is defined for 1D arrays of equal length")); + mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D arrays of equal length")); } mp_float_t (*funcx)(void *) = ndarray_get_float_function(x->dtype); diff --git a/code/numpy/bitwise.c b/code/numpy/bitwise.c index d02e0970..0aa5bac9 100644 --- a/code/numpy/bitwise.c +++ b/code/numpy/bitwise.c @@ -331,11 +331,11 @@ mp_obj_t *bitwise_binary_operators(mp_obj_t x1, mp_obj_t x2, uint8_t optype) { #if ULAB_SUPPORTS_COMPLEX if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT) || (lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { - mp_raise_ValueError(translate("not supported for input types")); + mp_raise_ValueError(MP_ERROR_TEXT("not supported for input types")); } #else if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { - mp_raise_ValueError(translate("not supported for input types")); + mp_raise_ValueError(MP_ERROR_TEXT("not supported for input types")); } #endif @@ -348,7 +348,7 @@ mp_obj_t *bitwise_binary_operators(mp_obj_t x1, mp_obj_t x2, uint8_t optype) { m_del(size_t, shape, ULAB_MAX_DIMS); m_del(int32_t, lstrides, ULAB_MAX_DIMS); m_del(int32_t, rstrides, ULAB_MAX_DIMS); - mp_raise_ValueError(translate("operands could not be broadcast together")); + mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together")); } ndarray_obj_t *results = NULL; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 44e9ab12..159c191d 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -47,7 +47,7 @@ mp_obj_t carray_real(mp_obj_t _source) { return MP_OBJ_FROM_PTR(target); } } else { - mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only")); } return mp_const_none; } @@ -73,7 +73,7 @@ mp_obj_t carray_imag(mp_obj_t _source) { return MP_OBJ_FROM_PTR(target); } } else { - mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only")); } return mp_const_none; } @@ -111,7 +111,7 @@ mp_obj_t carray_conjugate(mp_obj_t _source) { } else if(mp_obj_is_int(_source) || mp_obj_is_float(_source)) { return _source; } else { - mp_raise_TypeError(translate("input must be an ndarray, or a scalar")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray, or a scalar")); } } // this should never happen @@ -183,11 +183,11 @@ static void carray_sort_complex_(mp_float_t *array, size_t len) { mp_obj_t carray_sort_complex(mp_obj_t _source) { if(!mp_obj_is_type(_source, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("input must be a 1D ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray")); } ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->ndim != 1) { - mp_raise_TypeError(translate("input must be a 1D ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray")); } ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(source, NDARRAY_COMPLEX); diff --git a/code/numpy/carray/carray_tools.c b/code/numpy/carray/carray_tools.c index 7b623d34..d8c7b183 100644 --- a/code/numpy/carray/carray_tools.c +++ b/code/numpy/carray/carray_tools.c @@ -22,7 +22,7 @@ #if ULAB_SUPPORTS_COMPLEX void raise_complex_NotImplementedError(void) { - mp_raise_NotImplementedError(translate("not implemented for complex dtype")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("not implemented for complex dtype")); } #endif diff --git a/code/numpy/compare.c b/code/numpy/compare.c index 54647d6f..fabc33a9 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -36,7 +36,7 @@ static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) { int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) { - mp_raise_ValueError(translate("operands could not be broadcast together")); + mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together")); m_del(size_t, shape, ULAB_MAX_DIMS); m_del(int32_t, lstrides, ULAB_MAX_DIMS); m_del(int32_t, rstrides, ULAB_MAX_DIMS); @@ -263,7 +263,7 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { return MP_OBJ_FROM_PTR(results); } else { - mp_raise_TypeError(translate("wrong input type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); } return mp_const_none; } @@ -470,7 +470,7 @@ mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { if(!ndarray_can_broadcast(c, x, &ndim, oshape, cstrides, ystrides) || !ndarray_can_broadcast(c, y, &ndim, oshape, cstrides, ystrides) || !ndarray_can_broadcast(x, y, &ndim, oshape, xstrides, ystrides)) { - mp_raise_ValueError(translate("operands could not be broadcast together")); + mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together")); } ndim = MAX(MAX(c->ndim, x->ndim), y->ndim); diff --git a/code/numpy/create.c b/code/numpy/create.c index 6c1c75fd..f427e374 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -24,7 +24,7 @@ #if ULAB_NUMPY_HAS_ONES | ULAB_NUMPY_HAS_ZEROS | ULAB_NUMPY_HAS_FULL | ULAB_NUMPY_HAS_EMPTY static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t value) { if(!mp_obj_is_int(oshape) && !mp_obj_is_type(oshape, &mp_type_tuple) && !mp_obj_is_type(oshape, &mp_type_list)) { - mp_raise_TypeError(translate("input argument must be an integer, a tuple, or a list")); + mp_raise_TypeError(MP_ERROR_TEXT("input argument must be an integer, a tuple, or a list")); } ndarray_obj_t *ndarray = NULL; if(mp_obj_is_int(oshape)) { @@ -33,7 +33,7 @@ static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t } else if(mp_obj_is_type(oshape, &mp_type_tuple) || mp_obj_is_type(oshape, &mp_type_list)) { uint8_t len = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(oshape)); if(len > ULAB_MAX_DIMS) { - mp_raise_TypeError(translate("too many dimensions")); + mp_raise_TypeError(MP_ERROR_TEXT("too many dimensions")); } size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); @@ -144,7 +144,7 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg step = mp_obj_get_float(args[2].u_obj); if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj) && mp_obj_is_int(args[2].u_obj)) dtype = NDARRAY_INT16; } else { - mp_raise_TypeError(translate("wrong number of arguments")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); } if((MICROPY_FLOAT_C_FUN(fabs)(stop) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(start) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(step) > 32768)) { dtype = NDARRAY_FLOAT; @@ -159,7 +159,7 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg } if(!isfinite(start) || !isfinite(stop) || !isfinite(step)) { - mp_raise_ValueError(translate("arange: cannot compute length")); + mp_raise_ValueError(MP_ERROR_TEXT("arange: cannot compute length")); } ndarray_obj_t *ndarray; @@ -222,7 +222,7 @@ mp_obj_t create_asarray(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } return MP_OBJ_FROM_PTR(ndarray_from_iterable(args[0].u_obj, _dtype)); } else { - mp_raise_TypeError(translate("wrong input type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); } return mp_const_none; // this should never happen } @@ -252,7 +252,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) { - mp_raise_TypeError(translate("first argument must be a tuple of ndarrays")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a tuple of ndarrays")); } int8_t axis = (int8_t)args[1].u_int; size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); @@ -260,7 +260,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k for(uint8_t i = 0; i < ndarrays->len; i++) { if(!mp_obj_is_type(ndarrays->items[i], &ulab_ndarray_type)) { - mp_raise_ValueError(translate("only ndarrays can be concatenated")); + mp_raise_ValueError(MP_ERROR_TEXT("only ndarrays can be concatenated")); } } @@ -272,7 +272,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k axis += ndim; } if((axis < 0) || (axis >= ndim)) { - mp_raise_ValueError(translate("wrong axis specified")); + mp_raise_ValueError(MP_ERROR_TEXT("wrong axis specified")); } // shift axis axis = ULAB_MAX_DIMS - ndim + axis; @@ -284,14 +284,14 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k _ndarray = MP_OBJ_TO_PTR(ndarrays->items[i]); // check, whether the arrays are compatible if((dtype != _ndarray->dtype) || (ndim != _ndarray->ndim)) { - mp_raise_ValueError(translate("input arrays are not compatible")); + mp_raise_ValueError(MP_ERROR_TEXT("input arrays are not compatible")); } for(uint8_t j=0; j < ULAB_MAX_DIMS; j++) { if(j == axis) { shape[j] += _ndarray->shape[j]; } else { if(shape[j] != _ndarray->shape[j]) { - mp_raise_ValueError(translate("input arrays are not compatible")); + mp_raise_ValueError(MP_ERROR_TEXT("input arrays are not compatible")); } } } @@ -431,7 +431,7 @@ mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) } #if ULAB_MAX_DIMS > 2 else { - mp_raise_ValueError(translate("input must be 1- or 2-d")); + mp_raise_ValueError(MP_ERROR_TEXT("input must be 1- or 2-d")); } #endif @@ -585,7 +585,7 @@ mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(args[2].u_int < 2) { - mp_raise_ValueError(translate("number of points must be at least 2")); + mp_raise_ValueError(MP_ERROR_TEXT("number of points must be at least 2")); } size_t len = (size_t)args[2].u_int; mp_float_t start, step, stop; @@ -708,7 +708,7 @@ mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(args[2].u_int < 2) { - mp_raise_ValueError(translate("number of points must be at least 2")); + mp_raise_ValueError(MP_ERROR_TEXT("number of points must be at least 2")); } size_t len = (size_t)args[2].u_int; mp_float_t start, step, quotient; @@ -824,16 +824,16 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw size_t sz = ulab_binary_get_size(dtype); if(bufinfo.len < offset) { - mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length")); + mp_raise_ValueError(MP_ERROR_TEXT("offset must be non-negative and no greater than buffer length")); } size_t len = (bufinfo.len - offset) / sz; if((len * sz) != (bufinfo.len - offset)) { - mp_raise_ValueError(translate("buffer size must be a multiple of element size")); + mp_raise_ValueError(MP_ERROR_TEXT("buffer size must be a multiple of element size")); } if(mp_obj_get_int(args[2].u_obj) > 0) { size_t count = mp_obj_get_int(args[2].u_obj); if(len < count) { - mp_raise_ValueError(translate("buffer is smaller than requested size")); + mp_raise_ValueError(MP_ERROR_TEXT("buffer is smaller than requested size")); } else { len = count; } diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 595b866b..04c0b2f7 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -98,18 +98,18 @@ void fft_kernel_complex(mp_float_t *data, size_t n, int isign) { */ mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) { if(!mp_obj_is_type(data_in, &ulab_ndarray_type)) { - mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only")); } ndarray_obj_t *in = MP_OBJ_TO_PTR(data_in); #if ULAB_MAX_DIMS > 1 if(in->ndim != 1) { - mp_raise_TypeError(translate("FFT is implemented for linear arrays only")); + mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only")); } #endif size_t len = in->len; // Check if input is of length of power of 2 if((len & (len-1)) != 0) { - mp_raise_ValueError(translate("input array length must be power of 2")); + mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2")); } ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_COMPLEX); @@ -204,24 +204,24 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) { mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) { if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) { - mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only")); } if(n_args == 2) { if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) { - mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only")); } } ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re); #if ULAB_MAX_DIMS > 1 if(re->ndim != 1) { COMPLEX_DTYPE_NOT_IMPLEMENTED(re->dtype) - mp_raise_TypeError(translate("FFT is implemented for linear arrays only")); + mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only")); } #endif size_t len = re->len; // Check if input is of length of power of 2 if((len & (len-1)) != 0) { - mp_raise_ValueError(translate("input array length must be power of 2")); + mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2")); } ndarray_obj_t *out_re = ndarray_new_linear_array(len, NDARRAY_FLOAT); @@ -243,11 +243,11 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i #if ULAB_MAX_DIMS > 1 if(im->ndim != 1) { COMPLEX_DTYPE_NOT_IMPLEMENTED(im->dtype) - mp_raise_TypeError(translate("FFT is implemented for linear arrays only")); + mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only")); } #endif if (re->len != im->len) { - mp_raise_ValueError(translate("real and imaginary parts must be of equal length")); + mp_raise_ValueError(MP_ERROR_TEXT("real and imaginary parts must be of equal length")); } array = (uint8_t *)im->array; func = ndarray_get_float_function(im->dtype); diff --git a/code/numpy/filter.c b/code/numpy/filter.c index 92290388..79c1740a 100644 --- a/code/numpy/filter.c +++ b/code/numpy/filter.c @@ -36,7 +36,7 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("convolve arguments must be ndarrays")); + mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be ndarrays")); } ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); @@ -44,13 +44,13 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a // deal with linear arrays only #if ULAB_MAX_DIMS > 1 if((a->ndim != 1) || (c->ndim != 1)) { - mp_raise_TypeError(translate("convolve arguments must be linear arrays")); + mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be linear arrays")); } #endif size_t len_a = a->len; size_t len_c = c->len; if(len_a == 0 || len_c == 0) { - mp_raise_TypeError(translate("convolve arguments must not be empty")); + mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must not be empty")); } int len = len_a + len_c - 1; // convolve mode "full" diff --git a/code/numpy/io/io.c b/code/numpy/io/io.c index b9e5d367..f2479f19 100644 --- a/code/numpy/io/io.c +++ b/code/numpy/io/io.c @@ -46,13 +46,13 @@ static void io_read_(mp_obj_t stream, const mp_stream_p_t *stream_p, char *buffe } if(fail) { stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, error); - mp_raise_msg(&mp_type_RuntimeError, translate("corrupted file")); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file")); } } static mp_obj_t io_load(mp_obj_t file) { if(!mp_obj_is_str(file)) { - mp_raise_TypeError(translate("wrong input type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); } int error; @@ -126,7 +126,7 @@ static mp_obj_t io_load(mp_obj_t file) { #endif /* ULAB_SUPPORT_COPMLEX */ else { stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); - mp_raise_TypeError(translate("wrong dtype")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong dtype")); } io_read_(stream, stream_p, buffer, "', 'fortran_order': False, 'shape': (", 37, &error); @@ -169,7 +169,7 @@ static mp_obj_t io_load(mp_obj_t file) { } else { stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); - mp_raise_msg(&mp_type_RuntimeError, translate("corrupted file")); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file")); } needle++; } @@ -188,7 +188,7 @@ static mp_obj_t io_load(mp_obj_t file) { size_t read = stream_p->read(stream, array, ndarray->len * ndarray->itemsize, &error); if(read != ndarray->len * ndarray->itemsize) { stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); - mp_raise_msg(&mp_type_RuntimeError, translate("corrupted file")); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file")); } stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); @@ -303,7 +303,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw cols[0] = (uint16_t)mp_obj_get_int(args[4].u_obj); } else { #if ULAB_MAX_DIMS == 1 - mp_raise_ValueError(translate("usecols keyword must be specified")); + mp_raise_ValueError(MP_ERROR_TEXT("usecols keyword must be specified")); #else // assume that the argument is an iterable used_columns = (uint16_t)mp_obj_get_int(mp_obj_len(args[4].u_obj)); @@ -379,12 +379,12 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } while((read > 0) && (all_rows < max_rows)); if(rows == 0) { - mp_raise_ValueError(translate("empty file")); + mp_raise_ValueError(MP_ERROR_TEXT("empty file")); } uint16_t columns = items / rows; if(columns < used_columns) { - mp_raise_ValueError(translate("usecols is too high")); + mp_raise_ValueError(MP_ERROR_TEXT("usecols is too high")); } size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); @@ -526,7 +526,7 @@ static uint8_t io_sprintf(char *buffer, const char *comma, size_t x) { static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) { if(!mp_obj_is_str(file) || !mp_obj_is_type(ndarray_, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("wrong input type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(ndarray_); @@ -725,14 +725,14 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_str(args[0].u_obj) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("wrong input type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); #if ULAB_MAX_DIMS > 2 if(ndarray->ndim > 2) { - mp_raise_ValueError(translate("array has too many dimensions")); + mp_raise_ValueError(MP_ERROR_TEXT("array has too many dimensions")); } #endif diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 55ac4752..6fc79aa0 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -67,7 +67,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) { for(size_t n=m+1; n < N; n++) { // columns // compare entry (m, n) to (n, m) if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(Larray[m * N + n] - Larray[n * N + m])) { - mp_raise_ValueError(translate("input matrix is asymmetric")); + mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric")); } } } @@ -87,7 +87,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) { } if(i == j) { if(sum <= MICROPY_FLOAT_CONST(0.0)) { - mp_raise_ValueError(translate("matrix is not positive definite")); + mp_raise_ValueError(MP_ERROR_TEXT("matrix is not positive definite")); } else { Larray[i * N + i] = MICROPY_FLOAT_C_FUN(sqrt)(sum); } @@ -204,7 +204,7 @@ static mp_obj_t linalg_eig(mp_obj_t oin) { // compare entry (m, n) to (n, m) // TODO: this must probably be scaled! if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n] - array[n * S + m])) { - mp_raise_ValueError(translate("input matrix is asymmetric")); + mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric")); } } } @@ -219,7 +219,7 @@ static mp_obj_t linalg_eig(mp_obj_t oin) { if(iterations == 0) { // the computation did not converge; numpy raises LinAlgError m_del(mp_float_t, array, in->len); - mp_raise_ValueError(translate("iterations did not converge")); + mp_raise_ValueError(MP_ERROR_TEXT("iterations did not converge")); } ndarray_obj_t *eigenvalues = ndarray_new_linear_array(S, NDARRAY_FLOAT); mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array; @@ -267,7 +267,7 @@ static mp_obj_t linalg_inv(mp_obj_t o_in) { iarray -= N*N; if(!linalg_invert_matrix(iarray, N)) { - mp_raise_ValueError(translate("input matrix is singular")); + mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular")); } return MP_OBJ_FROM_PTR(inverted); } @@ -393,11 +393,11 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("operation is defined for ndarrays only")); + mp_raise_TypeError(MP_ERROR_TEXT("operation is defined for ndarrays only")); } ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj); if(source->ndim != 2) { - mp_raise_ValueError(translate("operation is defined for 2D arrays only")); + mp_raise_ValueError(MP_ERROR_TEXT("operation is defined for 2D arrays only")); } size_t m = source->shape[ULAB_MAX_DIMS - 2]; // rows @@ -498,7 +498,7 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ tuple->items[0] = MP_OBJ_FROM_PTR(q); tuple->items[1] = MP_OBJ_FROM_PTR(r); } else { - mp_raise_ValueError(translate("mode must be complete, or reduced")); + mp_raise_ValueError(MP_ERROR_TEXT("mode must be complete, or reduced")); } return MP_OBJ_FROM_PTR(tuple); } diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 01bd0bd9..b642d5a2 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -407,7 +407,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t #if ULAB_NUMPY_HAS_ARGMINMAX static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) { if(MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(oin)) == 0) { - mp_raise_ValueError(translate("attempt to get argmin/argmax of an empty sequence")); + mp_raise_ValueError(MP_ERROR_TEXT("attempt to get argmin/argmax of an empty sequence")); } size_t idx = 0, best_idx = 0; mp_obj_iter_buf_t iter_buf; @@ -442,7 +442,7 @@ static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) { static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) { // TODO: treat the flattened array if(ndarray->len == 0) { - mp_raise_ValueError(translate("attempt to get (arg)min/(arg)max of empty sequence")); + mp_raise_ValueError(MP_ERROR_TEXT("attempt to get (arg)min/(arg)max of empty sequence")); } if(axis == mp_const_none) { @@ -566,7 +566,7 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m mp_obj_t oin = args[0].u_obj; mp_obj_t axis = args[1].u_obj; if((axis != mp_const_none) && (!mp_obj_is_int(axis))) { - mp_raise_TypeError(translate("axis must be None, or an integer")); + mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer")); } #if ULAB_NUMPY_HAS_ALL | ULAB_NUMPY_HAS_ANY @@ -602,10 +602,10 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) return numerical_sum_mean_std_ndarray(ndarray, axis, optype, 0); default: - mp_raise_NotImplementedError(translate("operation is not implemented on ndarrays")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("operation is not implemented on ndarrays")); } } else { - mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be tuple, list, range, or ndarray")); } return mp_const_none; } @@ -613,7 +613,7 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m #if ULAB_NUMPY_HAS_SORT | NDARRAY_HAS_SORT static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inplace) { if(!mp_obj_is_type(oin, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("sort argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("sort argument must be an ndarray")); } ndarray_obj_t *ndarray; @@ -721,20 +721,20 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("argsort argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("argsort argument must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) if(args[1].u_obj == mp_const_none) { // bail out, though dense arrays could still be sorted - mp_raise_NotImplementedError(translate("argsort is not implemented for flattened arrays")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("argsort is not implemented for flattened arrays")); } // Since we are returning an NDARRAY_UINT16 array, bail out, // if the axis is longer than what we can hold for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { if(ndarray->shape[i] > 65535) { - mp_raise_ValueError(translate("axis too long")); + mp_raise_ValueError(MP_ERROR_TEXT("axis too long")); } } int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim); @@ -827,14 +827,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argsort_obj, 1, numerical_argsort); static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) { if (!mp_obj_is_type(_a, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("arguments must be ndarrays")); + mp_raise_TypeError(MP_ERROR_TEXT("arguments must be ndarrays")); } ndarray_obj_t *a = MP_OBJ_TO_PTR(_a); ndarray_obj_t *b = MP_OBJ_TO_PTR(_b); COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype) COMPLEX_DTYPE_NOT_IMPLEMENTED(b->dtype) if((a->ndim != 1) || (b->ndim != 1) || (a->len != b->len) || (a->len != 3)) { - mp_raise_ValueError(translate("cross is defined for 1D arrays of length 3")); + mp_raise_ValueError(MP_ERROR_TEXT("cross is defined for 1D arrays of length 3")); } mp_float_t *results = m_new(mp_float_t, 3); @@ -917,7 +917,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("diff argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("diff argument must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); @@ -926,16 +926,16 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar if(ax < 0) ax += ndarray->ndim; if((ax < 0) || (ax > ndarray->ndim - 1)) { - mp_raise_ValueError(translate("index out of range")); + mp_raise_ValueError(MP_ERROR_TEXT("index out of range")); } if((args[1].u_int < 0) || (args[1].u_int > 9)) { - mp_raise_ValueError(translate("differentiation order out of range")); + mp_raise_ValueError(MP_ERROR_TEXT("differentiation order out of range")); } uint8_t N = (uint8_t)args[1].u_int; uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax; if(N > ndarray->shape[index]) { - mp_raise_ValueError(translate("differentiation order out of range")); + mp_raise_ValueError(MP_ERROR_TEXT("differentiation order out of range")); } int8_t *stencil = m_new(int8_t, N+1); @@ -996,7 +996,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("flip argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("flip argument must be an ndarray")); } ndarray_obj_t *results = NULL; @@ -1016,7 +1016,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar results = ndarray_new_view(ndarray, ndarray->ndim, ndarray->shape, ndarray->strides, offset); results->strides[ax] = -results->strides[ax]; } else { - mp_raise_TypeError(translate("wrong axis index")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong axis index")); } return MP_OBJ_FROM_PTR(results); } @@ -1065,7 +1065,7 @@ mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("median argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("median argument must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); @@ -1181,7 +1181,7 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("roll argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("roll argument must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); uint8_t *array = ndarray->array; @@ -1318,7 +1318,7 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar m_del(int32_t, rstrides, ULAB_MAX_DIMS); } else { - mp_raise_TypeError(translate("wrong axis index")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong axis index")); } return MP_OBJ_FROM_PTR(results); @@ -1387,7 +1387,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg size_t ddof = args[2].u_int; if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) { // this seems to pass with False, and True... - mp_raise_ValueError(translate("axis must be None, or an integer")); + mp_raise_ValueError(MP_ERROR_TEXT("axis must be None, or an integer")); } if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || mp_obj_is_type(oin, &mp_type_range)) { return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof); @@ -1395,7 +1395,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); return numerical_sum_mean_std_ndarray(ndarray, axis, NUMERICAL_STD, ddof); } else { - mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be tuple, list, range, or ndarray")); } return mp_const_none; } diff --git a/code/numpy/poly.c b/code/numpy/poly.c index ff4965d8..ca5aa638 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -26,7 +26,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { if(!ndarray_object_is_array_like(args[0])) { - mp_raise_ValueError(translate("input data must be an iterable")); + mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable")); } #if ULAB_SUPPORTS_COMPLEX if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { @@ -44,7 +44,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0])); deg = (uint8_t)mp_obj_get_int(args[1]); if(leny < deg) { - mp_raise_ValueError(translate("more degrees of freedom than data points")); + mp_raise_ValueError(MP_ERROR_TEXT("more degrees of freedom than data points")); } lenx = leny; x = m_new(mp_float_t, lenx); // assume uniformly spaced data points @@ -55,16 +55,16 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { fill_array_iterable(y, args[0]); } else /* n_args == 3 */ { if(!ndarray_object_is_array_like(args[1])) { - mp_raise_ValueError(translate("input data must be an iterable")); + mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable")); } lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0])); leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1])); if(lenx != leny) { - mp_raise_ValueError(translate("input vectors must be of equal length")); + mp_raise_ValueError(MP_ERROR_TEXT("input vectors must be of equal length")); } deg = (uint8_t)mp_obj_get_int(args[2]); if(leny < deg) { - mp_raise_ValueError(translate("more degrees of freedom than data points")); + mp_raise_ValueError(MP_ERROR_TEXT("more degrees of freedom than data points")); } x = m_new(mp_float_t, lenx); fill_array_iterable(x, args[0]); @@ -104,7 +104,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { m_del(mp_float_t, x, lenx); m_del(mp_float_t, y, lenx); m_del(mp_float_t, prod, (deg+1)*(deg+1)); - mp_raise_ValueError(translate("could not invert Vandermonde matrix")); + mp_raise_ValueError(MP_ERROR_TEXT("could not invert Vandermonde matrix")); } // at this point, we have the inverse of X^T * X // y is a column vector; x is free now, we can use it for storing intermediate values @@ -156,7 +156,7 @@ static mp_float_t poly_eval(mp_float_t x, mp_float_t *p, uint8_t plen) { mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { if(!ndarray_object_is_array_like(o_p)) { - mp_raise_TypeError(translate("input is not iterable")); + mp_raise_TypeError(MP_ERROR_TEXT("input is not iterable")); } #if ULAB_SUPPORTS_COMPLEX ndarray_obj_t *input; diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 6a849b94..b6b1bebc 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -38,7 +38,7 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m mp_obj_t condition = args[0].u_obj; if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("wrong input type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); uint8_t *array = (uint8_t *)ndarray->array; @@ -55,7 +55,7 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m if(((axis == mp_const_none) && (len != ndarray->len)) || ((axis != mp_const_none) && (len != ndarray->shape[shift_ax]))) { - mp_raise_ValueError(translate("wrong length of condition array")); + mp_raise_ValueError(MP_ERROR_TEXT("wrong length of condition array")); } size_t true_count = 0; @@ -175,7 +175,7 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("first argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); uint8_t *array = (uint8_t *)ndarray->array; @@ -201,13 +201,13 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map index_len = 1; } else { if(mp_obj_len_maybe(indices) == MP_OBJ_NULL) { - mp_raise_TypeError(translate("wrong index type")); + mp_raise_TypeError(MP_ERROR_TEXT("wrong index type")); } index_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(indices)); } if(index_len > axis_len) { - mp_raise_ValueError(translate("wrong length of index array")); + mp_raise_ValueError(MP_ERROR_TEXT("wrong length of index array")); } size_t *index_array = m_new(size_t, index_len); @@ -218,7 +218,7 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map value += axis_len; } if((value < 0) || (value > (ssize_t)axis_len)) { - mp_raise_ValueError(translate("index is out of bounds")); + mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds")); } else { *index_array++ = (size_t)value; } @@ -231,7 +231,7 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map value += axis_len; } if((value < 0) || (value > (ssize_t)axis_len)) { - mp_raise_ValueError(translate("index is out of bounds")); + mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds")); } else { *index_array++ = (size_t)value; } @@ -356,7 +356,7 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { // TODO: should the results be upcast? // This implements 2D operations only! if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("arguments must be ndarrays")); + mp_raise_TypeError(MP_ERROR_TEXT("arguments must be ndarrays")); } ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1); ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2); @@ -370,7 +370,7 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { mp_float_t (*func2)(void *) = ndarray_get_float_function(m2->dtype); if(m1->shape[ULAB_MAX_DIMS - 1] != m2->shape[ULAB_MAX_DIMS - m2->ndim]) { - mp_raise_ValueError(translate("dimensions do not match")); + mp_raise_ValueError(MP_ERROR_TEXT("dimensions do not match")); } uint8_t ndim = MIN(m1->ndim, m2->ndim); size_t shape1 = m1->ndim == 2 ? m1->shape[ULAB_MAX_DIMS - m1->ndim] : 1; @@ -428,7 +428,7 @@ static mp_obj_t transform_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t } if(!ndarray_object_is_array_like(args[0].u_obj)) { - mp_raise_TypeError(translate("first argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray")); } if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { return mp_obj_len_maybe(args[0].u_obj); diff --git a/code/numpy/vector.c b/code/numpy/vector.c index b9a4cc68..cd8fef3e 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -64,18 +64,18 @@ static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, m target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); } else { if(!mp_obj_is_type(out, &ulab_ndarray_type)) { - mp_raise_ValueError(translate("out must be an ndarray")); + mp_raise_ValueError(MP_ERROR_TEXT("out must be an ndarray")); } target = MP_OBJ_TO_PTR(out); if(target->dtype != NDARRAY_FLOAT) { - mp_raise_ValueError(translate("out must be of float dtype")); + mp_raise_ValueError(MP_ERROR_TEXT("out must be of float dtype")); } if(target->ndim != source->ndim) { - mp_raise_ValueError(translate("input and output dimensions differ")); + mp_raise_ValueError(MP_ERROR_TEXT("input and output dimensions differ")); } for(uint8_t d = 0; d < target->ndim; d++) { if(target->shape[ULAB_MAX_DIMS - 1 - d] != source->shape[ULAB_MAX_DIMS - 1 - d]) { - mp_raise_ValueError(translate("input and output shapes differ")); + mp_raise_ValueError(MP_ERROR_TEXT("input and output shapes differ")); } } } @@ -309,7 +309,7 @@ mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("first argument must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray")); } int8_t n = args[1].u_int; mp_float_t mul = MICROPY_FLOAT_C_FUN(pow)(10.0, n); @@ -318,7 +318,7 @@ mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD mp_obj_t out = args[2].u_obj; if(out != mp_const_none) { - mp_raise_ValueError(translate("out keyword is not supported for function")); + mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for function")); } #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); @@ -426,7 +426,7 @@ mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) { int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS); if(!ndarray_can_broadcast(ndarray_x, ndarray_y, &ndim, shape, xstrides, ystrides)) { - mp_raise_ValueError(translate("operands could not be broadcast together")); + mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together")); m_del(size_t, shape, ULAB_MAX_DIMS); m_del(int32_t, xstrides, ULAB_MAX_DIMS); m_del(int32_t, ystrides, ULAB_MAX_DIMS); @@ -610,7 +610,7 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { mp_obj_t o_in = args[0].u_obj; mp_obj_t out = args[1].u_obj; if(out != mp_const_none) { - mp_raise_ValueError(translate("out keyword is not supported for complex dtype")); + mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype")); } #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ @@ -888,7 +888,7 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) mp_obj_t o_in = args[0].u_obj; uint8_t dtype = mp_obj_get_int(args[2].u_obj); if((dtype != NDARRAY_FLOAT) && (dtype != NDARRAY_COMPLEX)) { - mp_raise_TypeError(translate("dtype must be float, or complex")); + mp_raise_TypeError(MP_ERROR_TEXT("dtype must be float, or complex")); } if(mp_obj_is_type(o_in, &mp_type_complex)) { @@ -901,14 +901,14 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) } else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); if((source->dtype == NDARRAY_COMPLEX) && (dtype == NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("can't convert complex to float")); + mp_raise_TypeError(MP_ERROR_TEXT("can't convert complex to float")); } if(dtype == NDARRAY_COMPLEX) { #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD mp_obj_t out = args[1].u_obj; if(out != mp_const_none) { - mp_raise_ValueError(translate("out keyword is not supported for complex dtype")); + mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype")); } #endif if(source->dtype == NDARRAY_COMPLEX) { @@ -1010,7 +1010,7 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) #endif /* ULAB_MAX_DIMS > 3 */ return MP_OBJ_FROM_PTR(ndarray); } else { - mp_raise_TypeError(translate("input dtype must be float or complex")); + mp_raise_TypeError(MP_ERROR_TEXT("input dtype must be float or complex")); } } } @@ -1127,7 +1127,7 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, ndarray_set_value(self->otypes, ndarray->array, 0, fvalue); return MP_OBJ_FROM_PTR(ndarray); } else { - mp_raise_ValueError(translate("wrong input type")); + mp_raise_ValueError(MP_ERROR_TEXT("wrong input type")); } return mp_const_none; } @@ -1173,7 +1173,7 @@ static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj); if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { - mp_raise_TypeError(translate("first argument must be a callable")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable")); } mp_obj_t _otypes = args[1].u_obj; uint8_t otypes = NDARRAY_FLOAT; @@ -1184,11 +1184,11 @@ static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map otypes = mp_obj_get_int(_otypes); if(otypes != NDARRAY_FLOAT && otypes != NDARRAY_UINT8 && otypes != NDARRAY_INT8 && otypes != NDARRAY_UINT16 && otypes != NDARRAY_INT16) { - mp_raise_ValueError(translate("wrong output type")); + mp_raise_ValueError(MP_ERROR_TEXT("wrong output type")); } } else { - mp_raise_ValueError(translate("wrong output type")); + mp_raise_ValueError(MP_ERROR_TEXT("wrong output type")); } vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t); function->base.type = &vector_function_type; diff --git a/code/scipy/linalg/linalg.c b/code/scipy/linalg/linalg.c index a42392ce..d7942d31 100644 --- a/code/scipy/linalg/linalg.c +++ b/code/scipy/linalg/linalg.c @@ -59,14 +59,14 @@ static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("first two arguments must be ndarrays")); + mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays")); } ndarray_obj_t *A = MP_OBJ_TO_PTR(args[0].u_obj); ndarray_obj_t *b = MP_OBJ_TO_PTR(args[1].u_obj); if(!ndarray_is_dense(A) || !ndarray_is_dense(b)) { - mp_raise_TypeError(translate("input must be a dense ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray")); } size_t A_rows = A->shape[ULAB_MAX_DIMS - 2]; @@ -83,7 +83,7 @@ static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map // check if input matrix A is singular for (i = 0; i < A_rows; i++) { if (MICROPY_FLOAT_C_FUN(fabs)(get_A_ele(A_arr)) < LINALG_EPSILON) - mp_raise_ValueError(translate("input matrix is singular")); + mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular")); A_arr += A->strides[ULAB_MAX_DIMS - 2]; A_arr += A->strides[ULAB_MAX_DIMS - 1]; } @@ -161,14 +161,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj, 2, solve_triangular); static mp_obj_t cho_solve(mp_obj_t _L, mp_obj_t _b) { if(!mp_obj_is_type(_L, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("first two arguments must be ndarrays")); + mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays")); } ndarray_obj_t *L = MP_OBJ_TO_PTR(_L); ndarray_obj_t *b = MP_OBJ_TO_PTR(_b); if(!ndarray_is_dense(L) || !ndarray_is_dense(b)) { - mp_raise_TypeError(translate("input must be a dense ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray")); } mp_float_t (*get_L_ele)(void *) = ndarray_get_float_function(L->dtype); diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index ef10288d..9bd80878 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -71,7 +71,7 @@ STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { - mp_raise_TypeError(translate("first argument must be a function")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function")); } mp_float_t xtol = mp_obj_get_float(args[3].u_obj); mp_obj_t fargs[1]; @@ -82,12 +82,12 @@ STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_ left = optimize_python_call(type, fun, a, fargs, 0); right = optimize_python_call(type, fun, b, fargs, 0); if(left * right > 0) { - mp_raise_ValueError(translate("function has the same sign at the ends of interval")); + mp_raise_ValueError(MP_ERROR_TEXT("function has the same sign at the ends of interval")); } mp_float_t rtb = left < MICROPY_FLOAT_CONST(0.0) ? a : b; mp_float_t dx = left < MICROPY_FLOAT_CONST(0.0) ? b - a : a - b; if(args[4].u_int < 0) { - mp_raise_ValueError(translate("maxiter should be > 0")); + mp_raise_ValueError(MP_ERROR_TEXT("maxiter should be > 0")); } for(uint16_t i=0; i < args[4].u_int; i++) { dx *= MICROPY_FLOAT_CONST(0.5); @@ -141,14 +141,14 @@ STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { - mp_raise_TypeError(translate("first argument must be a function")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function")); } // parameters controlling convergence conditions mp_float_t xatol = mp_obj_get_float(args[2].u_obj); mp_float_t fatol = mp_obj_get_float(args[3].u_obj); if(args[4].u_int <= 0) { - mp_raise_ValueError(translate("maxiter must be > 0")); + mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0")); } uint16_t maxiter = (uint16_t)args[4].u_int; @@ -277,22 +277,22 @@ mp_obj_t optimize_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { - mp_raise_TypeError(translate("first argument must be a function")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function")); } mp_obj_t x_obj = args[1].u_obj; mp_obj_t y_obj = args[2].u_obj; mp_obj_t p0_obj = args[3].u_obj; if(!ndarray_object_is_array_like(x_obj) || !ndarray_object_is_array_like(y_obj)) { - mp_raise_TypeError(translate("data must be iterable")); + mp_raise_TypeError(MP_ERROR_TEXT("data must be iterable")); } if(!ndarray_object_is_nditerable(p0_obj)) { - mp_raise_TypeError(translate("initial values must be iterable")); + mp_raise_TypeError(MP_ERROR_TEXT("initial values must be iterable")); } size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(x_obj)); uint8_t lenp = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(p0_obj)); if(len != (uint16_t)mp_obj_get_int(mp_obj_len_maybe(y_obj))) { - mp_raise_ValueError(translate("data must be of equal length")); + mp_raise_ValueError(MP_ERROR_TEXT("data must be of equal length")); } mp_float_t *x = m_new(mp_float_t, len); @@ -366,7 +366,7 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_obj_t fun = args[0].u_obj; const mp_obj_type_t *type = mp_obj_get_type(fun); if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { - mp_raise_TypeError(translate("first argument must be a function")); + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function")); } mp_float_t x = mp_obj_get_float(args[1].u_obj); mp_float_t tol = mp_obj_get_float(args[2].u_obj); @@ -375,7 +375,7 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_ dx = x > MICROPY_FLOAT_CONST(0.0) ? OPTIMIZE_EPS * x : -OPTIMIZE_EPS * x; mp_obj_t fargs[1]; if(args[4].u_int <= 0) { - mp_raise_ValueError(translate("maxiter must be > 0")); + mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0")); } for(uint16_t i=0; i < args[4].u_int; i++) { fx = optimize_python_call(type, fun, x, fargs, 0); diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index db8d3cc5..f930a943 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -43,7 +43,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if(!ndarray_object_is_array_like(args[0].u_obj) || !ndarray_object_is_array_like(args[1].u_obj)) { - mp_raise_TypeError(translate("sosfilt requires iterable arguments")); + mp_raise_TypeError(MP_ERROR_TEXT("sosfilt requires iterable arguments")); } #if ULAB_SUPPORTS_COMPLEX if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { @@ -59,7 +59,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar ndarray_obj_t *inarray = MP_OBJ_TO_PTR(args[1].u_obj); #if ULAB_MAX_DIMS > 1 if(inarray->ndim > 1) { - mp_raise_ValueError(translate("input must be one-dimensional")); + mp_raise_ValueError(MP_ERROR_TEXT("input must be one-dimensional")); } #endif uint8_t *iarray = (uint8_t *)inarray->array; @@ -82,14 +82,14 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar if(args[2].u_obj != mp_const_none) { if(!mp_obj_is_type(args[2].u_obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("zi must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("zi must be an ndarray")); } else { ndarray_obj_t *zi = MP_OBJ_TO_PTR(args[2].u_obj); if((zi->shape[ULAB_MAX_DIMS - 2] != lensos) || (zi->shape[ULAB_MAX_DIMS - 1] != 2)) { - mp_raise_ValueError(translate("zi must be of shape (n_section, 2)")); + mp_raise_ValueError(MP_ERROR_TEXT("zi must be of shape (n_section, 2)")); } if(zi->dtype != NDARRAY_FLOAT) { - mp_raise_ValueError(translate("zi must be of float type")); + mp_raise_ValueError(MP_ERROR_TEXT("zi must be of float type")); } // TODO: this won't work with sparse arrays memcpy(zf_array, zi->array, 2*lensos*sizeof(mp_float_t)); @@ -97,11 +97,11 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { if(mp_obj_get_int(mp_obj_len_maybe(item)) != 6) { - mp_raise_ValueError(translate("sos array must be of shape (n_section, 6)")); + mp_raise_ValueError(MP_ERROR_TEXT("sos array must be of shape (n_section, 6)")); } else { fill_array_iterable(coeffs, item); if(coeffs[3] != MICROPY_FLOAT_CONST(1.0)) { - mp_raise_ValueError(translate("sos[:, 3] should be all ones")); + mp_raise_ValueError(MP_ERROR_TEXT("sos[:, 3] should be all ones")); } signal_sosfilt_array(yarray, coeffs, zf_array, lenx); zf_array += 2; diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 514721f7..b57f1e66 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -168,7 +168,7 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { // The shape and strides at `axis` are moved to the zeroth position, // everything else is aligned to the right if(!mp_obj_is_int(axis) & (axis != mp_const_none)) { - mp_raise_TypeError(translate("axis must be None, or an integer")); + mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer")); } shape_strides _shape_strides; @@ -194,7 +194,7 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { int8_t ax = mp_obj_get_int(axis); if(ax < 0) ax += ndarray->ndim; if((ax < 0) || (ax > ndarray->ndim - 1)) { - mp_raise_ValueError(translate("index out of range")); + mp_raise_ValueError(MP_ERROR_TEXT("index out of range")); } index = ULAB_MAX_DIMS - ndarray->ndim + ax; _shape_strides.ndim = ndarray->ndim - 1; @@ -220,7 +220,7 @@ int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) { int8_t ax = mp_obj_get_int(axis); if(ax < 0) ax += ndim; if((ax < 0) || (ax > ndim - 1)) { - mp_raise_ValueError(translate("axis is out of bounds")); + mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds")); } return ax; } @@ -230,11 +230,11 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { // Returns an ndarray, if the object is a square ndarray, // raises the appropriate exception otherwise if(!mp_obj_is_type(obj, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("size is defined for ndarrays only")); + mp_raise_TypeError(MP_ERROR_TEXT("size is defined for ndarrays only")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj); if((ndarray->shape[ULAB_MAX_DIMS - 1] != ndarray->shape[ULAB_MAX_DIMS - 2]) || (ndarray->ndim != 2)) { - mp_raise_ValueError(translate("input must be square matrix")); + mp_raise_ValueError(MP_ERROR_TEXT("input must be square matrix")); } return ndarray; } diff --git a/code/user/user.c b/code/user/user.c index 88775fda..c372092f 100644 --- a/code/user/user.c +++ b/code/user/user.c @@ -28,13 +28,13 @@ static mp_obj_t user_square(mp_obj_t arg) { // raise a TypeError exception, if the input is not an ndarray if(!mp_obj_is_type(arg, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("input must be an ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(arg); // make sure that the input is a dense array if(!ndarray_is_dense(ndarray)) { - mp_raise_TypeError(translate("input must be a dense ndarray")); + mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray")); } // if the input is a dense array, create `results` with the same number of diff --git a/code/utils/utils.c b/code/utils/utils.c index 6a59baf3..1d282d86 100644 --- a/code/utils/utils.c +++ b/code/utils/utils.c @@ -45,7 +45,7 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a if(args[3].u_obj != mp_const_none) { ndarray = MP_OBJ_TO_PTR(args[3].u_obj); if((ndarray->dtype != NDARRAY_FLOAT) || !ndarray_is_dense(ndarray)) { - mp_raise_TypeError(translate("out must be a float dense array")); + mp_raise_TypeError(MP_ERROR_TEXT("out must be a float dense array")); } } @@ -54,7 +54,7 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a mp_buffer_info_t bufinfo; if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { if(bufinfo.len < offset) { - mp_raise_ValueError(translate("offset is too large")); + mp_raise_ValueError(MP_ERROR_TEXT("offset is too large")); } uint8_t sz = sizeof(int16_t); #if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER @@ -65,12 +65,12 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a size_t len = (bufinfo.len - offset) / sz; if((len * sz) != (bufinfo.len - offset)) { - mp_raise_ValueError(translate("buffer size must be a multiple of element size")); + mp_raise_ValueError(MP_ERROR_TEXT("buffer size must be a multiple of element size")); } if(mp_obj_get_int(args[1].u_obj) > 0) { size_t count = mp_obj_get_int(args[1].u_obj); if(len < count) { - mp_raise_ValueError(translate("buffer is smaller than requested size")); + mp_raise_ValueError(MP_ERROR_TEXT("buffer is smaller than requested size")); } else { len = count; } @@ -79,7 +79,7 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT); } else { if(ndarray->len < len) { - mp_raise_ValueError(translate("out array is too small")); + mp_raise_ValueError(MP_ERROR_TEXT("out array is too small")); } } uint8_t *buffer = bufinfo.buf; From eacb0c9af47f85f5d4864b721c3b28661364e8e3 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 30 Oct 2023 09:54:14 +0100 Subject: [PATCH 077/113] fix some more translate()s --- code/ndarray_operators.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index c9c65a22..b7d61f26 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -863,11 +863,11 @@ mp_obj_t ndarray_binary_logical(ndarray_obj_t *lhs, ndarray_obj_t *rhs, #if ULAB_SUPPORTS_COMPLEX if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX) || (lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("operation not supported for the input types")); + mp_raise_TypeError(MP_ERROR_TEXT("operation not supported for the input types")); } #else if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { - mp_raise_TypeError(translate("operation not supported for the input types")); + mp_raise_TypeError(MP_ERROR_TEXT("operation not supported for the input types")); } #endif @@ -875,7 +875,7 @@ mp_obj_t ndarray_binary_logical(ndarray_obj_t *lhs, ndarray_obj_t *rhs, // numpy promotes the result to int32 if(((lhs->dtype == NDARRAY_INT16) && (rhs->dtype == NDARRAY_UINT16)) || ((lhs->dtype == NDARRAY_UINT16) && (rhs->dtype == NDARRAY_INT16))) { - mp_raise_TypeError(translate("dtype of int32 is not supported")); + mp_raise_TypeError(MP_ERROR_TEXT("dtype of int32 is not supported")); } ndarray_obj_t *results = NULL; From e32920645c8c5e6153f29e4e33ca7e3b7087aef1 Mon Sep 17 00:00:00 2001 From: sol <1731279+s-ol@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:34:21 +0100 Subject: [PATCH 078/113] Fix (u)int8 upcasting as per docs and numpy (#650) * fix wrong #if guard in ndarray_inplace_ams * implement (u)int8 upcasting rules as per documentation * bump version --- code/ndarray_operators.c | 10 +++++----- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ tests/2d/numpy/operators.py.exp | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index b7d61f26..e8de4d48 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -181,8 +181,8 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, if(lhs->dtype == NDARRAY_UINT8) { if(rhs->dtype == NDARRAY_UINT8) { - results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); - BINARY_LOOP(results, uint16_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, +); + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, +); } else if(rhs->dtype == NDARRAY_INT8) { results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, +); @@ -264,8 +264,8 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, if(lhs->dtype == NDARRAY_UINT8) { if(rhs->dtype == NDARRAY_UINT8) { - results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); - BINARY_LOOP(results, uint16_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, *); + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, *); } else if(rhs->dtype == NDARRAY_INT8) { results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, *); @@ -1059,7 +1059,7 @@ mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rs UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, +=); } #endif - #if NDARRAY_HAS_INPLACE_ADD + #if NDARRAY_HAS_INPLACE_MULTIPLY if(optype == MP_BINARY_OP_INPLACE_MULTIPLY) { UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, *=); } diff --git a/code/ulab.c b/code/ulab.c index af7ba0ba..5c5067bd 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.4.1 +#define ULAB_VERSION 6.4.2 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index ff921249..4d389f9a 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Thu, 11 Dec 2023 + +version 6.4.2 + + fix upcasting with two uint8 operands (#650) + Thu, 10 Aug 2023 version 6.4.1 diff --git a/tests/2d/numpy/operators.py.exp b/tests/2d/numpy/operators.py.exp index 319c2073..7517a390 100644 --- a/tests/2d/numpy/operators.py.exp +++ b/tests/2d/numpy/operators.py.exp @@ -94,7 +94,7 @@ array([1.0, 2.0, 3.0], dtype=float64) array([1.0, 32.0, 729.0], dtype=float64) array([1.0, 32.0, 729.0], dtype=float64) array([1.0, 32.0, 729.0], dtype=float64) -array([5, 7, 9], dtype=uint16) +array([5, 7, 9], dtype=uint8) array([5, 7, 9], dtype=int16) array([5, 7, 9], dtype=int8) array([5, 7, 9], dtype=uint16) From 7a9370612f25d394c8560cddaba19f189ec57ea3 Mon Sep 17 00:00:00 2001 From: yyyz <41409512+hiltay@users.noreply.github.com> Date: Mon, 25 Dec 2023 17:56:16 +0800 Subject: [PATCH 079/113] fix the `np.delete` bug (#653) * fix the `np.delete` bug * fix the `np.delete` bug, add unittest code * increment the version number and update the change log * update the expected file `delete.py.exp` --- code/numpy/transform.c | 5 ++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 6 +++++ tests/2d/numpy/delete.py | 2 ++ tests/2d/numpy/delete.py.exp | 50 ++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/code/numpy/transform.c b/code/numpy/transform.c index b6b1bebc..b77364ba 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -204,6 +204,11 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map mp_raise_TypeError(MP_ERROR_TEXT("wrong index type")); } index_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(indices)); + if (index_len == 0){ + // if the second positional argument is empty + // return the original array + return MP_OBJ_FROM_PTR(ndarray); + } } if(index_len > axis_len) { diff --git a/code/ulab.c b/code/ulab.c index 5c5067bd..5ff4e502 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.4.2 +#define ULAB_VERSION 6.4.3 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 4d389f9a..3650141f 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Mon, 25 Dec 2023 + +version 6.4.3 + + fix the 'np.delete' error that occurs when passing an empty iterable object as the second positional argument (#653) + Thu, 11 Dec 2023 version 6.4.2 diff --git a/tests/2d/numpy/delete.py b/tests/2d/numpy/delete.py index c7718ad5..fdb96a87 100644 --- a/tests/2d/numpy/delete.py +++ b/tests/2d/numpy/delete.py @@ -11,7 +11,9 @@ a = np.array(range(25), dtype=dtype).reshape((5,5)) print(np.delete(a, [1, 2], axis=0)) print(np.delete(a, [1, 2], axis=1)) + print(np.delete(a, [], axis=1)) print(np.delete(a, [1, 5, 10])) + print(np.delete(a, [])) for dtype in dtypes: a = np.array(range(25), dtype=dtype).reshape((5,5)) diff --git a/tests/2d/numpy/delete.py.exp b/tests/2d/numpy/delete.py.exp index 72a2a286..e678f8dc 100644 --- a/tests/2d/numpy/delete.py.exp +++ b/tests/2d/numpy/delete.py.exp @@ -6,7 +6,17 @@ array([[0, 3, 4], [10, 13, 14], [15, 18, 19], [20, 23, 24]], dtype=uint8) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=uint8) array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=uint8) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=uint8) array([[0, 1, 2, 3, 4], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]], dtype=int8) @@ -15,7 +25,17 @@ array([[0, 3, 4], [10, 13, 14], [15, 18, 19], [20, 23, 24]], dtype=int8) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=int8) array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=int8) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=int8) array([[0, 1, 2, 3, 4], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]], dtype=uint16) @@ -24,7 +44,17 @@ array([[0, 3, 4], [10, 13, 14], [15, 18, 19], [20, 23, 24]], dtype=uint16) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=uint16) array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=uint16) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=uint16) array([[0, 1, 2, 3, 4], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]], dtype=int16) @@ -33,7 +63,17 @@ array([[0, 3, 4], [10, 13, 14], [15, 18, 19], [20, 23, 24]], dtype=int16) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=int16) array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=int16) +array([[0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]], dtype=int16) array([[0.0, 1.0, 2.0, 3.0, 4.0], [15.0, 16.0, 17.0, 18.0, 19.0], [20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64) @@ -42,7 +82,17 @@ array([[0.0, 3.0, 4.0], [10.0, 13.0, 14.0], [15.0, 18.0, 19.0], [20.0, 23.0, 24.0]], dtype=float64) +array([[0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64) array([0.0, 2.0, 3.0, 4.0, 6.0, 7.0, 8.0, 9.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0], dtype=float64) +array([[0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64) array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [15, 16, 17, 18, 19], From f2fad82a9745dc563fa12b38debde9d39abd7c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 13 Jan 2024 18:42:43 +0100 Subject: [PATCH 080/113] add random module (#654) * add random module skeleton * add Generator object * add placeholder for random.random method * add rudimentary random.random implementation * generator object accept seed(s) argument * add out keyword * add support for out keyword argument * update change log * add links to header files * fix file link * fix error messages * add uniform to random module * add normal distribution * fix argument options in normal and uniform * update documentation --- code/micropython.mk | 1 + code/ndarray.c | 6 +- code/ndarray.h | 2 +- code/numpy/numpy.c | 4 + code/numpy/random/random.c | 378 +++++++++++++++++++ code/numpy/random/random.h | 37 ++ code/numpy/vector.c | 10 +- code/ulab.c | 2 +- code/ulab.h | 18 + code/ulab_tools.h | 4 + docs/manual/source/conf.py | 4 +- docs/manual/source/index.rst | 1 + docs/manual/source/numpy-functions.rst | 177 +++++++-- docs/manual/source/numpy-universal.rst | 25 +- docs/manual/source/ulab-ndarray.rst | 17 +- docs/numpy-functions.ipynb | 118 +++++- docs/numpy-random.ipynb | 492 +++++++++++++++++++++++++ docs/numpy-universal.ipynb | 35 +- docs/ulab-change-log.md | 10 +- docs/ulab-ndarray.ipynb | 4 +- 20 files changed, 1277 insertions(+), 68 deletions(-) create mode 100644 code/numpy/random/random.c create mode 100644 code/numpy/random/random.h create mode 100644 docs/numpy-random.ipynb diff --git a/code/micropython.mk b/code/micropython.mk index eb517926..4aa6f615 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -25,6 +25,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg_tools.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/numerical.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/poly.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/random/random.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/stats.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c diff --git a/code/ndarray.c b/code/ndarray.c index af881478..ffd3d621 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -559,11 +559,11 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt // creates a dense array from a tuple // the function should work in the general n-dimensional case size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - for(size_t i=0; i < ULAB_MAX_DIMS; i++) { + for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { if(i >= _shape->len) { - shape[ULAB_MAX_DIMS - i] = 0; + shape[ULAB_MAX_DIMS - 1 - i] = 0; } else { - shape[ULAB_MAX_DIMS - i] = mp_obj_get_int(_shape->items[i]); + shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]); } } return ndarray_new_dense_ndarray(_shape->len, shape, dtype); diff --git a/code/ndarray.h b/code/ndarray.h index ec8b3ee7..2ce84bed 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -40,7 +40,7 @@ // Constant float objects are a struct in ROM and are referenced via their pointer. // Use ULAB_DEFINE_FLOAT_CONST to define a constant float object. -// id is the name of the constant, num is it's floating point value. +// id is the name of the constant, num is its floating point value. // hex32 is computed as: hex(int.from_bytes(array.array('f', [num]), 'little')) // hex64 is computed as: hex(int.from_bytes(array.array('d', [num]), 'little')) diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 74de9088..c1a45e92 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -27,6 +27,7 @@ #include "io/io.h" #include "linalg/linalg.h" #include "numerical.h" +#include "random/random.h" #include "stats.h" #include "transform.h" #include "poly.h" @@ -110,6 +111,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_LINALG_MODULE { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) }, #endif + #if ULAB_NUMPY_HAS_RANDOM_MODULE + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&ulab_numpy_random_module) }, + #endif #if ULAB_HAS_PRINTOPTIONS { MP_ROM_QSTR(MP_QSTR_set_printoptions), MP_ROM_PTR(&ndarray_set_printoptions_obj) }, { MP_ROM_QSTR(MP_QSTR_get_printoptions), MP_ROM_PTR(&ndarray_get_printoptions_obj) }, diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c new file mode 100644 index 00000000..73a34f82 --- /dev/null +++ b/code/numpy/random/random.c @@ -0,0 +1,378 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Zoltán Vörös +*/ + +#include + +#include "py/builtin.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "random.h" + +ULAB_DEFINE_FLOAT_CONST(random_zero, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL); +ULAB_DEFINE_FLOAT_CONST(random_one, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL); + +// methods of the Generator object +static const mp_rom_map_elem_t random_generator_locals_dict_table[] = { + #if ULAB_NUMPY_RANDOM_HAS_NORMAL + { MP_ROM_QSTR(MP_QSTR_normal), MP_ROM_PTR(&random_normal_obj) }, + #endif + #if ULAB_NUMPY_RANDOM_HAS_RANDOM + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) }, + #endif + #if ULAB_NUMPY_RANDOM_HAS_UNIFORM + { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(random_generator_locals_dict, random_generator_locals_dict_table); + +// random's Generator object is defined here +#if defined(MP_DEFINE_CONST_OBJ_TYPE) +MP_DEFINE_CONST_OBJ_TYPE( + random_generator_type, + MP_QSTR_generator, + MP_TYPE_FLAG_NONE, + print, random_generator_print, + make_new, random_generator_make_new, + locals_dict, &random_generator_locals_dict +); +#else +const mp_obj_type_t random_generator_type = { + { &mp_type_type }, + .name = MP_QSTR_generator, + .print = random_generator_print, + .make_new = random_generator_make_new, + .locals_dict = (mp_obj_dict_t*)&random_generator_locals_dict +}; +#endif + +void random_generator_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + random_generator_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(MP_PYTHON_PRINTER, "Gnerator() at 0x%p", self); +} + +mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void) type; + mp_arg_check_num(n_args, n_kw, 0, 1, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args); + + + if(args[0] == mp_const_none) { + #ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC + mp_raise_ValueError(MP_ERROR_TEXT("no default seed")); + #endif + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + generator->state = MICROPY_PY_RANDOM_SEED_INIT_FUNC; + return MP_OBJ_FROM_PTR(generator); + } else if(mp_obj_is_int(args[0])) { + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + generator->state = (size_t)mp_obj_get_int(args[0]); + return MP_OBJ_FROM_PTR(generator); + } else if(mp_obj_is_type(args[0], &mp_type_tuple)){ + mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]); + mp_obj_t *items = m_new(mp_obj_t, seeds->len); + + for(uint8_t i = 0; i < seeds->len; i++) { + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + generator->state = (size_t)mp_obj_get_int(seeds->items[i]); + items[i] = generator; + } + return mp_obj_new_tuple(seeds->len, items); + } else { + mp_raise_TypeError(MP_ERROR_TEXT("argument must be None, an integer or a tuple of integers")); + } + // we should never end up here + return mp_const_none; +} +// END OF GENERATOR COMPONENTS + + +static inline uint32_t pcg32_next(uint64_t *state) { + uint64_t old_state = *state; + *state = old_state * PCG_MULTIPLIER_64 + PCG_INCREMENT_64; + uint32_t value = (uint32_t)((old_state ^ (old_state >> 18)) >> 27); + int rot = old_state >> 59; + return rot ? (value >> rot) | (value << (32 - rot)) : value; +} + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +static inline uint64_t pcg32_next64(uint64_t *state) { + uint64_t value = pcg32_next(state); + value <<= 32; + value |= pcg32_next(state); + return value; +} +#endif + +#if ULAB_NUMPY_RANDOM_HAS_NORMAL +static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_loc, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, + { MP_QSTR_scale, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, + { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + mp_float_t loc = mp_obj_get_float(args[1].u_obj); + mp_float_t scale = mp_obj_get_float(args[2].u_obj); + mp_obj_t size = args[3].u_obj; + + ndarray_obj_t *ndarray = NULL; + mp_float_t u, v, value; + + if(size != mp_const_none) { + if(mp_obj_is_int(size)) { + ndarray = ndarray_new_linear_array((size_t)mp_obj_get_int(size), NDARRAY_FLOAT); + } else if(mp_obj_is_type(size, &mp_type_tuple)) { + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + if(_shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); + } else { // input type not supported + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + } + } else { + // return single value + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + u = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + x = pcg32_next(&self->state); + v = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + u = (double)(int64_t)(x >> 11) * 0x1.0p-53; + x = pcg32_next64(&self->state); + v = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u)); + value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v); + return mp_obj_new_float(loc + scale * value); + } + + mp_float_t *array = (mp_float_t *)ndarray->array; + + // numpy's random supports only dense output arrays, so we can simply + // loop through the elements in a linear fashion + for(size_t i = 0; i < ndarray->len; i = i + 2) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + u = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + x = pcg32_next(&self->state); + v = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + u = (double)(int64_t)(x >> 11) * 0x1.0p-53; + x = pcg32_next64(&self->state); + v = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u)); + value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v); + *array++ = loc + scale * value; + if((i & 1) == 0) { + value = sqrt_log * MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v); + *array++ = loc + scale * value; + } + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(random_normal_obj, 1, random_normal); +#endif /* ULAB_NUMPY_RANDOM_HAS_NORMAL */ + +#if ULAB_NUMPY_RANDOM_HAS_RANDOM +static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + + mp_obj_t size = args[1].u_obj; + mp_obj_t out = args[2].u_obj; + + ndarray_obj_t *ndarray = NULL; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + uint8_t ndim = 1; + + if(size != mp_const_none) { + if(mp_obj_is_int(size)) { + shape[ULAB_MAX_DIMS - 1] = (size_t)mp_obj_get_int(size); + } else if(mp_obj_is_type(size, &mp_type_tuple)) { + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + if(_shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + ndim = _shape->len; + for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { + if(i >= ndim) { + shape[ULAB_MAX_DIMS - 1 - i] = 0; + } else { + shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]); + } + } + } else { // input type not supported + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + } + } + + if(out != mp_const_none) { + if(!mp_obj_is_type(out, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type")); + } + + ndarray = MP_OBJ_TO_PTR(out); + + if(ndarray->dtype != NDARRAY_FLOAT) { + mp_raise_TypeError(MP_ERROR_TEXT("output array has wrong type")); + } + if(size != mp_const_none) { + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + if(ndarray->shape[i] != shape[i]) { + mp_raise_ValueError(MP_ERROR_TEXT("size must match out.shape when used together")); + } + } + } + if(!ndarray_is_dense(ndarray)) { + mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous")); + } + } else { // out == None + if(size != mp_const_none) { + ndarray = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + } else { + // return single value + mp_float_t value; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + value = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + value = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + return mp_obj_new_float(value); + } + } + + mp_float_t *array = (mp_float_t *)ndarray->array; + + // numpy's random supports only dense output arrays, so we can simply + // loop through the elements in a linear fashion + for(size_t i = 0; i < ndarray->len; i++) { + + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + *array = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + *array = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + + array++; + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random); +#endif /* ULAB_NUMPY_RANDOM_HAS_RANDOM */ + +#if ULAB_NUMPY_RANDOM_HAS_UNIFORM +static mp_obj_t random_uniform(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_low, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, + { MP_QSTR_high, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, + { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + mp_float_t low = mp_obj_get_float(args[1].u_obj); + mp_float_t high = mp_obj_get_float(args[2].u_obj); + mp_obj_t size = args[3].u_obj; + + ndarray_obj_t *ndarray = NULL; + + if(size == mp_const_none) { + // return single value + mp_float_t value; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + value = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + value = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + return mp_obj_new_float(value); + } else if(mp_obj_is_type(size, &mp_type_tuple)) { + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + // TODO: this could be reduced, if the inspection was in the ndarray_new_ndarray_from_tuple function + if(_shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); + } else { // input type not supported + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + } + + mp_float_t *array = (mp_float_t *)ndarray->array; + mp_float_t diff = high - low; + for(size_t i = 0; i < ndarray->len; i++) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + *array = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + *array = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + *array = low + diff * *array; + array++; + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(random_uniform_obj, 1, random_uniform); +#endif /* ULAB_NUMPY_RANDOM_HAS_UNIFORM */ + + +static const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, + { MP_ROM_QSTR(MP_QSTR_Generator), MP_ROM_PTR(&random_generator_type) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_random_globals, ulab_numpy_random_globals_table); + +const mp_obj_module_t ulab_numpy_random_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_random_globals, +}; + diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h new file mode 100644 index 00000000..314fefa0 --- /dev/null +++ b/code/numpy/random/random.h @@ -0,0 +1,37 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Zoltán Vörös +*/ + +#include "../../ndarray.h" + +#ifndef _NUMPY_RANDOM_ +#define _NUMPY_RANDOM_ + + +#define PCG_MULTIPLIER_64 6364136223846793005ULL +#define PCG_INCREMENT_64 1442695040888963407ULL + +extern const mp_obj_module_t ulab_numpy_random_module; + +extern const mp_obj_type_t random_generator_type; + +typedef struct _random_generator_obj_t { + mp_obj_base_t base; + uint64_t state; +} random_generator_obj_t; + +mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); +void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); + + +MP_DECLARE_CONST_FUN_OBJ_KW(random_normal_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(random_random_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(random_uniform_obj); + +#endif diff --git a/code/numpy/vector.c b/code/numpy/vector.c index cd8fef3e..0df3a338 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -609,9 +609,6 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_obj_t o_in = args[0].u_obj; mp_obj_t out = args[1].u_obj; - if(out != mp_const_none) { - mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype")); - } #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ if(mp_obj_is_type(o_in, &mp_type_complex)) { @@ -621,6 +618,13 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag)); } else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); + + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + if((out != mp_const_none) && (source->dtype == NDARRAY_COMPLEX)){ + mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype")); + } + #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ + if(source->dtype == NDARRAY_COMPLEX) { uint8_t *sarray = (uint8_t *)source->array; ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX); diff --git a/code/ulab.c b/code/ulab.c index 5ff4e502..f55768bc 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.4.3 +#define ULAB_VERSION 6.5.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 26df82fb..a384bfe2 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -697,6 +697,24 @@ #define ULAB_NUMPY_HAS_SORT_COMPLEX (1) #endif +// random module +#ifndef ULAB_NUMPY_HAS_RANDOM_MODULE +#define ULAB_NUMPY_HAS_RANDOM_MODULE (1) +#endif + +#ifndef ULAB_NUMPY_RANDOM_HAS_NORMAL +#define ULAB_NUMPY_RANDOM_HAS_NORMAL (1) +#endif + +#ifndef ULAB_NUMPY_RANDOM_HAS_RANDOM +#define ULAB_NUMPY_RANDOM_HAS_RANDOM (1) +#endif + +#ifndef ULAB_NUMPY_RANDOM_HAS_UNIFORM +#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1) +#endif + + // scipy modules #ifndef ULAB_SCIPY_HAS_LINALG_MODULE #define ULAB_SCIPY_HAS_LINALG_MODULE (1) diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 5ae99df9..3e6b81e3 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -43,4 +43,8 @@ void ulab_rescale_float_strides(int32_t *); #endif bool ulab_tools_mp_obj_is_scalar(mp_obj_t ); + +#if ULAB_NUMPY_HAS_RANDOM_MODULE +ndarray_obj_t *ulab_tools_create_out(mp_obj_tuple_t , mp_obj_t , uint8_t , bool ); +#endif #endif diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 3f4d5eb4..4a2cea6e 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -23,11 +23,11 @@ # -- Project information ----------------------------------------------------- project = 'The ulab book' -copyright = '2019-2022, Zoltán Vörös and contributors' +copyright = '2019-2024, Zoltán Vörös and contributors' author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '5.1.0' +release = '6.5.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/index.rst b/docs/manual/source/index.rst index 1bae7a3d..40fbc00d 100644 --- a/docs/manual/source/index.rst +++ b/docs/manual/source/index.rst @@ -22,6 +22,7 @@ Welcome to the ulab book! numpy-universal numpy-fft numpy-linalg + numpy-random scipy-linalg scipy-optimize scipy-signal diff --git a/docs/manual/source/numpy-functions.rst b/docs/manual/source/numpy-functions.rst index aac19bde..a2a3e410 100644 --- a/docs/manual/source/numpy-functions.rst +++ b/docs/manual/source/numpy-functions.rst @@ -12,43 +12,48 @@ the firmware was compiled with complex support. 4. `numpy.argmin <#argmin>`__ 5. `numpy.argsort <#argsort>`__ 6. `numpy.asarray\* <#asarray>`__ -7. `numpy.clip <#clip>`__ -8. `numpy.compress\* <#compress>`__ -9. `numpy.conjugate\* <#conjugate>`__ -10. `numpy.convolve\* <#convolve>`__ -11. `numpy.delete <#delete>`__ -12. `numpy.diff <#diff>`__ -13. `numpy.dot <#dot>`__ -14. `numpy.equal <#equal>`__ -15. `numpy.flip\* <#flip>`__ -16. `numpy.imag\* <#imag>`__ -17. `numpy.interp <#interp>`__ -18. `numpy.isfinite <#isfinite>`__ -19. `numpy.isinf <#isinf>`__ -20. `numpy.load <#load>`__ -21. `numpy.loadtxt <#loadtxt>`__ -22. `numpy.max <#max>`__ -23. `numpy.maximum <#maximum>`__ -24. `numpy.mean <#mean>`__ -25. `numpy.median <#median>`__ -26. `numpy.min <#min>`__ -27. `numpy.minimum <#minimum>`__ -28. `numpy.nozero <#nonzero>`__ -29. `numpy.not_equal <#equal>`__ -30. `numpy.polyfit <#polyfit>`__ -31. `numpy.polyval <#polyval>`__ -32. `numpy.real\* <#real>`__ -33. `numpy.roll <#roll>`__ -34. `numpy.save <#save>`__ -35. `numpy.savetxt <#savetxt>`__ -36. `numpy.size <#size>`__ -37. `numpy.sort <#sort>`__ -38. `numpy.sort_complex\* <#sort_complex>`__ -39. `numpy.std <#std>`__ -40. `numpy.sum <#sum>`__ -41. `numpy.trace <#trace>`__ -42. `numpy.trapz <#trapz>`__ -43. `numpy.where <#where>`__ +7. `numpy.bitwise_and <#bitwise_and>`__ +8. `numpy.bitwise_or <#bitwise_and>`__ +9. `numpy.bitwise_xor <#bitwise_and>`__ +10. `numpy.clip <#clip>`__ +11. `numpy.compress\* <#compress>`__ +12. `numpy.conjugate\* <#conjugate>`__ +13. `numpy.convolve\* <#convolve>`__ +14. `numpy.delete <#delete>`__ +15. `numpy.diff <#diff>`__ +16. `numpy.dot <#dot>`__ +17. `numpy.equal <#equal>`__ +18. `numpy.flip\* <#flip>`__ +19. `numpy.imag\* <#imag>`__ +20. `numpy.interp <#interp>`__ +21. `numpy.isfinite <#isfinite>`__ +22. `numpy.isinf <#isinf>`__ +23. `numpy.left_shift <#left_shift>`__ +24. `numpy.load <#load>`__ +25. `numpy.loadtxt <#loadtxt>`__ +26. `numpy.max <#max>`__ +27. `numpy.maximum <#maximum>`__ +28. `numpy.mean <#mean>`__ +29. `numpy.median <#median>`__ +30. `numpy.min <#min>`__ +31. `numpy.minimum <#minimum>`__ +32. `numpy.nozero <#nonzero>`__ +33. `numpy.not_equal <#equal>`__ +34. `numpy.polyfit <#polyfit>`__ +35. `numpy.polyval <#polyval>`__ +36. `numpy.real\* <#real>`__ +37. `numpy.right_shift <#right_shift>`__ +38. `numpy.roll <#roll>`__ +39. `numpy.save <#save>`__ +40. `numpy.savetxt <#savetxt>`__ +41. `numpy.size <#size>`__ +42. `numpy.sort <#sort>`__ +43. `numpy.sort_complex\* <#sort_complex>`__ +44. `numpy.std <#std>`__ +45. `numpy.sum <#sum>`__ +46. `numpy.trace <#trace>`__ +47. `numpy.trapz <#trapz>`__ +48. `numpy.where <#where>`__ all --- @@ -323,6 +328,58 @@ an alias for ``array``. +bitwise_and +----------- + +``numpy``: https://numpy.org/doc/stable/reference/routines.bitwise.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html + +Each of ``bitwise_and``, ``bitwise_or``, and ``bitwise_xor`` takes two +integer-type ``ndarray``\ s as arguments, and returns the element-wise +results of the ``AND``, ``OR``, and ``XOR`` operators. Broadcasting is +supported. If the ``dtype`` of the input arrays is not an integer, and +exception will be raised. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array(range(8), dtype=np.uint8) + b = a + 1 + + print(a) + print(b) + print('\nbitwise_and:\n', np.bitwise_and(a, b)) + print('\nbitwise_or:\n', np.bitwise_or(a, b)) + print('\nbitwise_xor:\n', np.bitwise_xor(a, b)) + +.. parsed-literal:: + + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8) + array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8) + + bitwise_and: + array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8) + + bitwise_or: + array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8) + + bitwise_xor: + array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8) + + + + clip ---- @@ -987,6 +1044,52 @@ positions, where the input is infinite. Integer types return the +left_shift +---------- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html + +``left_shift``, and ``right_shift`` both take two integer-type +``ndarray``\ s, and bit-wise shift the elements of the first array by an +amount given by the second array to the left, and right, respectively. +Broadcasting is supported. If the ``dtype`` of the input arrays is not +an integer, and exception will be raised. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.ones(7, dtype=np.uint8) + b = np.zeros(7, dtype=np.uint8) + 255 + c = np.array(range(7), dtype=np.uint8) + 1 + + print('a: ', a) + print('b: ', b) + print('c: ', c) + print('\na left shifted by c:\n', np.left_shift(a, c)) + print('\nb right shifted by c:\n', np.right_shift(b, c)) + +.. parsed-literal:: + + a: array([1, 1, 1, 1, 1, 1, 1], dtype=uint8) + b: array([255, 255, 255, 255, 255, 255, 255], dtype=uint8) + c: array([1, 2, 3, 4, 5, 6, 7], dtype=uint8) + + a left shifted by c: + array([2, 4, 8, 16, 32, 64, 128], dtype=uint8) + + b right shifted by c: + array([127, 63, 31, 15, 7, 3, 1], dtype=uint8) + + + + load ---- diff --git a/docs/manual/source/numpy-universal.rst b/docs/manual/source/numpy-universal.rst index b9b7f9f1..f39ee173 100644 --- a/docs/manual/source/numpy-universal.rst +++ b/docs/manual/source/numpy-universal.rst @@ -20,12 +20,18 @@ operate on, or can return complex arrays): ``acos``, ``acosh``, ``arctan2``, ``around``, ``asin``, ``asinh``, ``atan``, ``arctan2``, ``atanh``, ``ceil``, ``cos``, ``degrees``, ``exp*``, ``expm1``, ``floor``, ``log``, ``log10``, ``log2``, -``radians``, ``sin``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``. +``radians``, ``sin``, ``sinc``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``. These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries. +In order to avoid repeated memory allocations, functions can take the +``out=None`` optional argument, which must be a floating point +``ndarray`` of the same size as the input ``array``. If these conditions +are not fulfilled, and exception will be raised. If ``out=None``, a new +array will be created upon each invocation of the function. + .. code:: # code to be run in micropython @@ -47,6 +53,13 @@ exponential of the matrix entries. c = np.array(range(9)).reshape((3, 3)) print('\n=============\nc:\n', c) print('exp(c):\n', np.exp(c)) + + # using the `out` argument + d = np.array(range(9)).reshape((3, 3)) + + print('\nd before invoking the function:\n', d) + np.exp(c, out=d) + print('\nd afteri nvoking the function:\n', d) .. parsed-literal:: @@ -69,6 +82,16 @@ exponential of the matrix entries. [20.08553692318767, 54.59815003314424, 148.4131591025766], [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64) + d before invoking the function: + array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [6.0, 7.0, 8.0]], dtype=float64) + + d afteri nvoking the function: + array([[1.0, 2.718281828459045, 7.38905609893065], + [20.08553692318767, 54.59815003314424, 148.4131591025766], + [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64) + diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index ff8e6eb7..ef54a242 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -1816,12 +1816,17 @@ Binary operators ``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, ``/=``, -``**=`` binary operators that work element-wise. Broadcasting is -available, meaning that the two operands do not even have to have the -same shape. If the lengths along the respective axes are equal, or one -of them is 1, or the axis is missing, the element-wise operation can -still be carried out. A thorough explanation of broadcasting can be -found under https://numpy.org/doc/stable/user/basics.broadcasting.html. +``**=`` binary operators, as well as the ``AND``, ``OR``, ``XOR`` +bit-wise operators that work element-wise. Note that the bit-wise +operators will raise an exception, if either of the operands is of +``float`` or ``complex`` type. + +Broadcasting is available, meaning that the two operands do not even +have to have the same shape. If the lengths along the respective axes +are equal, or one of them is 1, or the axis is missing, the element-wise +operation can still be carried out. A thorough explanation of +broadcasting can be found under +https://numpy.org/doc/stable/user/basics.broadcasting.html. **WARNING**: note that relational operators (``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``) should have the ``ndarray`` on their left hand diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index c57225f9..2ef884a2 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -77,7 +77,7 @@ " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", " with open('/dev/shm/micropython.py', 'w') as fout:\n", " fout.write(cell)\n", - " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " print(proc.stdout.read().decode(\"utf-8\"))\n", " print(proc.stderr.read().decode(\"utf-8\"))\n", @@ -238,6 +238,9 @@ "1. [numpy.argmin](#argmin)\n", "1. [numpy.argsort](#argsort)\n", "1. [numpy.asarray*](#asarray)\n", + "1. [numpy.bitwise_and](#bitwise_and)\n", + "1. [numpy.bitwise_or](#bitwise_and)\n", + "1. [numpy.bitwise_xor](#bitwise_and)\n", "1. [numpy.clip](#clip)\n", "1. [numpy.compress*](#compress)\n", "1. [numpy.conjugate*](#conjugate)\n", @@ -251,6 +254,7 @@ "1. [numpy.interp](#interp)\n", "1. [numpy.isfinite](#isfinite)\n", "1. [numpy.isinf](#isinf)\n", + "1. [numpy.left_shift](#left_shift)\n", "1. [numpy.load](#load)\n", "1. [numpy.loadtxt](#loadtxt)\n", "1. [numpy.max](#max)\n", @@ -264,6 +268,7 @@ "1. [numpy.polyfit](#polyfit)\n", "1. [numpy.polyval](#polyval)\n", "1. [numpy.real*](#real)\n", + "1. [numpy.right_shift](#right_shift)\n", "1. [numpy.roll](#roll)\n", "1. [numpy.save](#save)\n", "1. [numpy.savetxt](#savetxt)\n", @@ -606,6 +611,63 @@ "print('a == c: {}'.format(a is c))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## bitwise_and\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/routines.bitwise.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html\n", + "\n", + "Each of `bitwise_and`, `bitwise_or`, and `bitwise_xor` takes two integer-type `ndarray`s as arguments, and returns the element-wise results of the `AND`, `OR`, and `XOR` operators. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n", + "array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n", + "\n", + "bitwise_and:\n", + " array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8)\n", + "\n", + "bitwise_or:\n", + " array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8)\n", + "\n", + "bitwise_xor:\n", + " array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(8), dtype=np.uint8)\n", + "b = a + 1\n", + "\n", + "print(a)\n", + "print(b)\n", + "print('\\nbitwise_and:\\n', np.bitwise_and(a, b))\n", + "print('\\nbitwise_or:\\n', np.bitwise_or(a, b))\n", + "print('\\nbitwise_xor:\\n', np.bitwise_xor(a, b))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1423,6 +1485,58 @@ "print('\\nisinf(c):\\n', np.isinf(c))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## left_shift\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html\n", + "\n", + "`left_shift`, and `right_shift` both take two integer-type `ndarray`s, and bit-wise shift the elements of the first array by an amount given by the second array to the left, and right, respectively. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: array([1, 1, 1, 1, 1, 1, 1], dtype=uint8)\n", + "b: array([255, 255, 255, 255, 255, 255, 255], dtype=uint8)\n", + "c: array([1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n", + "\n", + "a left shifted by c:\n", + " array([2, 4, 8, 16, 32, 64, 128], dtype=uint8)\n", + "\n", + "b right shifted by c:\n", + " array([127, 63, 31, 15, 7, 3, 1], dtype=uint8)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.ones(7, dtype=np.uint8)\n", + "b = np.zeros(7, dtype=np.uint8) + 255\n", + "c = np.array(range(7), dtype=np.uint8) + 1\n", + "\n", + "print('a: ', a)\n", + "print('b: ', b)\n", + "print('c: ', c)\n", + "print('\\na left shifted by c:\\n', np.left_shift(a, c))\n", + "print('\\nb right shifted by c:\\n', np.right_shift(b, c))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2786,7 +2900,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.13" }, "toc": { "base_numbering": 1, diff --git a/docs/numpy-random.ipynb b/docs/numpy-random.ipynb new file mode 100644 index 00000000..4c9aa2a4 --- /dev/null +++ b/docs/numpy-random.ipynb @@ -0,0 +1,492 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-01T09:27:13.438054Z", + "start_time": "2020-05-01T09:27:13.191491Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + } + ], + "source": [ + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Notebook magic" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:24:48.499467Z", + "start_time": "2022-01-07T18:24:48.488004Z" + } + }, + "outputs": [], + "source": [ + "from IPython.core.magic import Magics, magics_class, line_cell_magic\n", + "from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n", + "from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n", + "import subprocess\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2020-07-23T20:31:25.296014Z", + "start_time": "2020-07-23T20:31:25.265937Z" + } + }, + "outputs": [], + "source": [ + "@magics_class\n", + "class PyboardMagic(Magics):\n", + " @cell_magic\n", + " @magic_arguments()\n", + " @argument('-skip')\n", + " @argument('-unix')\n", + " @argument('-pyboard')\n", + " @argument('-file')\n", + " @argument('-data')\n", + " @argument('-time')\n", + " @argument('-memory')\n", + " def micropython(self, line='', cell=None):\n", + " args = parse_argstring(self.micropython, line)\n", + " if args.skip: # doesn't care about the cell's content\n", + " print('skipped execution')\n", + " return None # do not parse the rest\n", + " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", + " with open('/dev/shm/micropython.py', 'w') as fout:\n", + " fout.write(cell)\n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", + " print(proc.stdout.read().decode(\"utf-8\"))\n", + " print(proc.stderr.read().decode(\"utf-8\"))\n", + " return None\n", + " if args.file: # can be used to copy the cell content onto the pyboard's flash\n", + " spaces = \" \"\n", + " try:\n", + " with open(args.file, 'w') as fout:\n", + " fout.write(cell.replace('\\t', spaces))\n", + " printf('written cell to {}'.format(args.file))\n", + " except:\n", + " print('Failed to write to disc!')\n", + " return None # do not parse the rest\n", + " if args.data: # can be used to load data from the pyboard directly into kernel space\n", + " message = pyb.exec(cell)\n", + " if len(message) == 0:\n", + " print('pyboard >>>')\n", + " else:\n", + " print(message.decode('utf-8'))\n", + " # register new variable in user namespace\n", + " self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n", + " \n", + " if args.time: # measures the time of executions\n", + " pyb.exec('import utime')\n", + " message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n", + " \"\\nprint('execution time: {:d} us'.format(delta))\")\n", + " print(message.decode('utf-8'))\n", + " \n", + " if args.memory: # prints out memory information \n", + " message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n", + " print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n", + " message = pyb.exec(cell)\n", + " print(\">>> \", message.decode('utf-8'))\n", + " message = pyb.exec('print(mem_info())')\n", + " print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n", + "\n", + " if args.pyboard:\n", + " message = pyb.exec(cell)\n", + " print(message.decode('utf-8'))\n", + "\n", + "ip = get_ipython()\n", + "ip.register_magics(PyboardMagic)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## pyboard" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:35.126401Z", + "start_time": "2020-05-07T07:35:35.105824Z" + } + }, + "outputs": [], + "source": [ + "import pyboard\n", + "pyb = pyboard.Pyboard('/dev/ttyACM0')\n", + "pyb.enter_raw_repl()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-19T19:11:18.145548Z", + "start_time": "2020-05-19T19:11:18.137468Z" + } + }, + "outputs": [], + "source": [ + "pyb.exit_raw_repl()\n", + "pyb.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:38.725924Z", + "start_time": "2020-05-07T07:35:38.645488Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "%%micropython -pyboard 1\n", + "\n", + "import utime\n", + "import ulab as np\n", + "\n", + "def timeit(n=1000):\n", + " def wrapper(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " run_times = np.zeros(n, dtype=np.uint16)\n", + " for i in range(n):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n", + " print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n", + " print('\\tbest: %d us'%np.min(run_times))\n", + " print('\\tworst: %d us'%np.max(run_times))\n", + " print('\\taverage: %d us'%np.mean(run_times))\n", + " print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n", + " return result\n", + " return new_func\n", + " return wrapper\n", + "\n", + "def timeit(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n", + " return result\n", + " return new_func" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__END_OF_DEFS__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# numpy.random\n", + "\n", + "Random numbers drawn specific distributions can be generated by instantiating a `Generator` object, and calling its methods. The module defines the following three functions:\n", + "\n", + "1. [numpy.random.Generator.normal](#normal)\n", + "1. [numpy.random.Generator.random](#random)\n", + "1. [numpy.random.Generator.uniform](#uniform)\n", + "\n", + "\n", + "The `Generator` object, when instantiated, takes a single integer as its argument. This integer is the seed, which will be fed to the 32-bit or 64-bit routine. More details can be found under https://www.pcg-random.org/index.html. The generator is a standard `python` object that keeps track of its state.\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/index.html" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## normal\n", + "\n", + "A random set of number from the `normal` distribution can be generated by calling the generator's `normal` method. The method takes three optional arguments, `loc=0.0`, the centre of the distribution, `scale=1.0`, the width of the distribution, and `size=None`, a tuple containing the shape of the returned array. In case `size` is `None`, a single floating point number is returned.\n", + "\n", + "The `normal` method of the `Generator` object is based on the [Box-Muller transform](https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform).\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.html" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-10-19T13:08:17.647416Z", + "start_time": "2019-10-19T13:08:17.597456Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gnerator() at 0x7fa9dae05340\n", + "-6.285246229407202\n", + "array([[24.95816273705659, 15.2670302229426, 14.81001577336041],\n", + " [20.17589833056986, 23.14539083787544, 26.37772041367461],\n", + " [41.94894234387275, 37.11027030608206, 25.65889562100477]], dtype=float64)\n", + "array([[21.52562779033434, 12.74685887865834, 24.08404670765186],\n", + " [4.728112596365396, 7.667757906857082, 21.61576094228444],\n", + " [2.432338873595267, 27.75945683572574, 5.730827584659245]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "rng = np.random.Generator(123456)\n", + "print(rng)\n", + "\n", + "# return single number from a distribution of scale 1, and location 0\n", + "print(rng.normal())\n", + "\n", + "print(rng.normal(loc=20.0, scale=10.0, size=(3,3)))\n", + "# same as above, with positional arguments\n", + "print(rng.normal(20.0, 10.0, (3,3)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## random\n", + "\n", + "A random set of number from the uniform distribution in the interval [0, 1] can be generated by calling the generator's `random` method. The method takes two optional arguments, `size=None`, a tuple containing the shape of the returned array, and `out`. In case `size` is `None`, a single floating point number is returned. \n", + "\n", + "`out` can be used, if a floating point array is available. An exception will be raised, if the array is not of `float` `dtype`, or if both `size` and `out` are supplied, and there is a conflict in their shapes.\n", + "\n", + "If `size` is `None`, a single floating point number will be returned.\n", + "\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.html" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gnerator() at 0x7f299de05340\n", + "6.384615058863119e-11\n", + "\n", + " array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],\n", + " [0.8738606263361598, 0.4946080034142021, 0.7765890156101152],\n", + " [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)\n", + "\n", + "buffer array before:\n", + " array([[0.0, 1.0, 2.0],\n", + " [3.0, 4.0, 5.0],\n", + " [6.0, 7.0, 8.0]], dtype=float64)\n", + "\n", + "buffer array after:\n", + " array([[0.8508024287393201, 0.9848489829156055, 0.7598167589604003],\n", + " [0.782995698302952, 0.2866337782847831, 0.7915884498022229],\n", + " [0.4614071706315902, 0.4792657443088592, 0.1581582066230718]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "rng = np.random.Generator(123456)\n", + "print(rng)\n", + "\n", + "# returning new objects\n", + "print(rng.random())\n", + "print('\\n', rng.random(size=(3,3)))\n", + "\n", + "# supplying a buffer\n", + "a = np.array(range(9), dtype=np.float).reshape((3,3))\n", + "print('\\nbuffer array before:\\n', a)\n", + "rng.random(out=a)\n", + "print('\\nbuffer array after:\\n', a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## uniform\n", + "\n", + "`uniform` is similar to `random`, except that the interval over which the numbers are distributed can be specified, while the `out` argument cannot. In addition to `size` specifying the shape of the output, `low=0.0`, and `high=1.0` are accepted arguments. With the indicated defaults, `uniform` is identical to `random`, which can be seen from the fact that the first 3-by-3 tensor below is the same as the one produced by `rng.random(size=(3,3))` above.\n", + "\n", + "\n", + "If `size` is `None`, a single floating point number will be returned.\n", + "\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.html" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gnerator() at 0x7f1891205340\n", + "6.384615058863119e-11\n", + "\n", + " array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],\n", + " [0.8738606263361598, 0.4946080034142021, 0.7765890156101152],\n", + " [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)\n", + "\n", + " array([[18.5080242873932, 19.84848982915605, 17.598167589604],\n", + " [17.82995698302952, 12.86633778284783, 17.91588449802223],\n", + " [14.6140717063159, 14.79265744308859, 11.58158206623072]], dtype=float64)\n", + "\n", + " array([[14.3380400319162, 12.72487657409978, 15.77119643621117],\n", + " [13.61835831436355, 18.96062889255558, 15.78847796795966],\n", + " [12.59435855187034, 17.68262037443622, 14.77943040598734]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "rng = np.random.Generator(123456)\n", + "print(rng)\n", + "\n", + "print(rng.uniform())\n", + "# returning numbers between 0, and 1\n", + "print('\\n', rng.uniform(size=(3,3)))\n", + "\n", + "# returning numbers between 10, and 20\n", + "print('\\n', rng.uniform(low=10, high=20, size=(3,3)))\n", + "\n", + "# same as above, without the keywords\n", + "print('\\n', rng.uniform(10, 20, (3,3)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "382.797px" + }, + "toc_section_display": true, + "toc_window_display": true + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/numpy-universal.ipynb b/docs/numpy-universal.ipynb index 8934fa6e..1d5764b8 100644 --- a/docs/numpy-universal.ipynb +++ b/docs/numpy-universal.ipynb @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:10:30.696795Z", @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:10:30.785887Z", @@ -77,7 +77,7 @@ " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", " with open('/dev/shm/micropython.py', 'w') as fout:\n", " fout.write(cell)\n", - " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " print(proc.stdout.read().decode(\"utf-8\"))\n", " print(proc.stderr.read().decode(\"utf-8\"))\n", @@ -229,14 +229,16 @@ "\n", "At present, the following functions are supported (starred functions can operate on, or can return complex arrays):\n", "\n", - "`acos`, `acosh`, `arctan2`, `around`, `asin`, `asinh`, `atan`, `arctan2`, `atanh`, `ceil`, `cos`, `degrees`, `exp*`, `expm1`, `floor`, `log`, `log10`, `log2`, `radians`, `sin`, `sinh`, `sqrt*`, `tan`, `tanh`.\n", + "`acos`, `acosh`, `arctan2`, `around`, `asin`, `asinh`, `atan`, `arctan2`, `atanh`, `ceil`, `cos`, `degrees`, `exp*`, `expm1`, `floor`, `log`, `log10`, `log2`, `radians`, `sin`, `sinc`, `sinh`, `sqrt*`, `tan`, `tanh`.\n", + "\n", + "These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries.\n", "\n", - "These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries." + "In order to avoid repeated memory allocations, functions can take the `out=None` optional argument, which must be a floating point `ndarray` of the same size as the input `array`. If these conditions are not fulfilled, and exception will be raised. If `out=None`, a new array will be created upon each invocation of the function." ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2021-01-13T19:11:07.579601Z", @@ -267,6 +269,16 @@ " [20.08553692318767, 54.59815003314424, 148.4131591025766],\n", " [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)\n", "\n", + "d before invoking the function:\n", + " array([[0.0, 1.0, 2.0],\n", + " [3.0, 4.0, 5.0],\n", + " [6.0, 7.0, 8.0]], dtype=float64)\n", + "\n", + "d afteri nvoking the function:\n", + " array([[1.0, 2.718281828459045, 7.38905609893065],\n", + " [20.08553692318767, 54.59815003314424, 148.4131591025766],\n", + " [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)\n", + "\n", "\n" ] } @@ -290,7 +302,14 @@ "# as well as with matrices\n", "c = np.array(range(9)).reshape((3, 3))\n", "print('\\n=============\\nc:\\n', c)\n", - "print('exp(c):\\n', np.exp(c))" + "print('exp(c):\\n', np.exp(c))\n", + "\n", + "# using the `out` argument\n", + "d = np.array(range(9)).reshape((3, 3))\n", + "\n", + "print('\\nd before invoking the function:\\n', d)\n", + "np.exp(c, out=d)\n", + "print('\\nd afteri nvoking the function:\\n', d)" ] }, { @@ -814,7 +833,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.13" }, "toc": { "base_numbering": 1, diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 3650141f..7113d8b3 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Tue, 9 Jan 2024 + +version 6.5.0 + + add random module + Mon, 25 Dec 2023 version 6.4.3 @@ -14,9 +20,7 @@ Thu, 10 Aug 2023 version 6.4.1 -``` -fix BOOLEAN issue, which would cause numpy.where funciton abnormally on RP2040(#643) -``` + fix BOOLEAN issue, which would cause numpy.where funciton abnormally on RP2040(#643) Thu, 20 Jul 2023 diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index bf016b1a..0e2bb1b0 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -2599,7 +2599,9 @@ "source": [ "# Binary operators\n", "\n", - "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators that work element-wise. Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", + "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n", + "\n", + "Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", "A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n", "\n", "**WARNING**: note that relational operators (`<`, `>`, `<=`, `>=`, `==`, `!=`) should have the `ndarray` on their left hand side, when compared to scalars. This means that the following works" From c3d1cbd7c2677f108bd9456d55bfa29fab8a1212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 13 Jan 2024 18:54:01 +0100 Subject: [PATCH 081/113] add random documentation rst file (#655) --- docs/manual/source/numpy-random.rst | 183 ++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 docs/manual/source/numpy-random.rst diff --git a/docs/manual/source/numpy-random.rst b/docs/manual/source/numpy-random.rst new file mode 100644 index 00000000..2a3a8ca9 --- /dev/null +++ b/docs/manual/source/numpy-random.rst @@ -0,0 +1,183 @@ + +numpy.random +============ + +Random numbers drawn specific distributions can be generated by +instantiating a ``Generator`` object, and calling its methods. The +module defines the following three functions: + +1. `numpy.random.Generator.normal <#normal>`__ +2. `numpy.random.Generator.random <#random>`__ +3. `numpy.random.Generator.uniform <#uniform>`__ + +The ``Generator`` object, when instantiated, takes a single integer as +its argument. This integer is the seed, which will be fed to the 32-bit +or 64-bit routine. More details can be found under +https://www.pcg-random.org/index.html. The generator is a standard +``python`` object that keeps track of its state. + +``numpy``: https://numpy.org/doc/stable/reference/random/index.html + +normal +------ + +A random set of number from the ``normal`` distribution can be generated +by calling the generator’s ``normal`` method. The method takes three +optional arguments, ``loc=0.0``, the centre of the distribution, +``scale=1.0``, the width of the distribution, and ``size=None``, a tuple +containing the shape of the returned array. In case ``size`` is +``None``, a single floating point number is returned. + +The ``normal`` method of the ``Generator`` object is based on the +`Box-Muller +transform `__. + +``numpy``: +https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.html + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + rng = np.random.Generator(123456) + print(rng) + + # return single number from a distribution of scale 1, and location 0 + print(rng.normal()) + + print(rng.normal(loc=20.0, scale=10.0, size=(3,3))) + # same as above, with positional arguments + print(rng.normal(20.0, 10.0, (3,3))) + +.. parsed-literal:: + + Gnerator() at 0x7fa9dae05340 + -6.285246229407202 + array([[24.95816273705659, 15.2670302229426, 14.81001577336041], + [20.17589833056986, 23.14539083787544, 26.37772041367461], + [41.94894234387275, 37.11027030608206, 25.65889562100477]], dtype=float64) + array([[21.52562779033434, 12.74685887865834, 24.08404670765186], + [4.728112596365396, 7.667757906857082, 21.61576094228444], + [2.432338873595267, 27.75945683572574, 5.730827584659245]], dtype=float64) + + + + +random +------ + +A random set of number from the uniform distribution in the interval [0, +1] can be generated by calling the generator’s ``random`` method. The +method takes two optional arguments, ``size=None``, a tuple containing +the shape of the returned array, and ``out``. In case ``size`` is +``None``, a single floating point number is returned. + +``out`` can be used, if a floating point array is available. An +exception will be raised, if the array is not of ``float`` ``dtype``, or +if both ``size`` and ``out`` are supplied, and there is a conflict in +their shapes. + +If ``size`` is ``None``, a single floating point number will be +returned. + +``numpy``: +https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.html + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + rng = np.random.Generator(123456) + print(rng) + + # returning new objects + print(rng.random()) + print('\n', rng.random(size=(3,3))) + + # supplying a buffer + a = np.array(range(9), dtype=np.float).reshape((3,3)) + print('\nbuffer array before:\n', a) + rng.random(out=a) + print('\nbuffer array after:\n', a) + +.. parsed-literal:: + + Gnerator() at 0x7f299de05340 + 6.384615058863119e-11 + + array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133], + [0.8738606263361598, 0.4946080034142021, 0.7765890156101152], + [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64) + + buffer array before: + array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [6.0, 7.0, 8.0]], dtype=float64) + + buffer array after: + array([[0.8508024287393201, 0.9848489829156055, 0.7598167589604003], + [0.782995698302952, 0.2866337782847831, 0.7915884498022229], + [0.4614071706315902, 0.4792657443088592, 0.1581582066230718]], dtype=float64) + + + + +uniform +------- + +``uniform`` is similar to ``random``, except that the interval over +which the numbers are distributed can be specified, while the ``out`` +argument cannot. In addition to ``size`` specifying the shape of the +output, ``low=0.0``, and ``high=1.0`` are accepted arguments. With the +indicated defaults, ``uniform`` is identical to ``random``, which can be +seen from the fact that the first 3-by-3 tensor below is the same as the +one produced by ``rng.random(size=(3,3))`` above. + +If ``size`` is ``None``, a single floating point number will be +returned. + +``numpy``: +https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.html + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + rng = np.random.Generator(123456) + print(rng) + + print(rng.uniform()) + # returning numbers between 0, and 1 + print('\n', rng.uniform(size=(3,3))) + + # returning numbers between 10, and 20 + print('\n', rng.uniform(low=10, high=20, size=(3,3))) + + # same as above, without the keywords + print('\n', rng.uniform(10, 20, (3,3))) + +.. parsed-literal:: + + Gnerator() at 0x7f1891205340 + 6.384615058863119e-11 + + array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133], + [0.8738606263361598, 0.4946080034142021, 0.7765890156101152], + [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64) + + array([[18.5080242873932, 19.84848982915605, 17.598167589604], + [17.82995698302952, 12.86633778284783, 17.91588449802223], + [14.6140717063159, 14.79265744308859, 11.58158206623072]], dtype=float64) + + array([[14.3380400319162, 12.72487657409978, 15.77119643621117], + [13.61835831436355, 18.96062889255558, 15.78847796795966], + [12.59435855187034, 17.68262037443622, 14.77943040598734]], dtype=float64) + + + From 9a1d03d90d9ae1c7f676941f618d0451030354f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 16 Jan 2024 20:55:21 +0100 Subject: [PATCH 082/113] Update rp2w.sh --- build/rp2w.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/rp2w.sh b/build/rp2w.sh index a06e62ce..ada620ad 100644 --- a/build/rp2w.sh +++ b/build/rp2w.sh @@ -1,6 +1,6 @@ #!/bin/bash -export BOARD=PICO_W +export BOARD=RPI_PICO_W export BUILD_DIR=$(pwd) export MPY_DIR=$BUILD_DIR/micropython export ULAB_DIR=$BUILD_DIR/../code From 1c37edbee67190d7b89a75c9a0d03fc171080a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 10 Feb 2024 18:59:06 +0100 Subject: [PATCH 083/113] add link to random module documentation in readme (#659) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa3831a4..e7feeca6 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ the `imag`, and `real` properties are automatically included. ## `numpy` and `scipy` functions -In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest). +In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`numpy.random`](https://micropython-ulab.readthedocs.io/en/latest/numpy-random.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest). ## `ulab` utilities From acfec3e9af5d351df88a7d71786f11a12f7545d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 10 Feb 2024 20:46:34 +0100 Subject: [PATCH 084/113] fix reshape (#660) --- code/ndarray.c | 18 +++++------------- code/ulab.c | 2 +- tests/2d/numpy/reshape.py | 17 +++++++++++++++++ tests/2d/numpy/reshape.py.exp | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 tests/2d/numpy/reshape.py create mode 100644 tests/2d/numpy/reshape.py.exp diff --git a/code/ndarray.c b/code/ndarray.c index ffd3d621..84ce8495 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -558,13 +558,9 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dtype) { // creates a dense array from a tuple // the function should work in the general n-dimensional case - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { - if(i >= _shape->len) { - shape[ULAB_MAX_DIMS - 1 - i] = 0; - } else { - shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]); - } + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + for(size_t i = 0; i < _shape->len; i++) { + shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[_shape->len - 1 - i]); } return ndarray_new_dense_ndarray(_shape->len, shape, dtype); } @@ -2021,7 +2017,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { mp_obj_t *items = m_new(mp_obj_t, 1); items[0] = _shape; shape = mp_obj_new_tuple(1, items); - } else { + } else { // at this point it's certain that _shape is a tuple shape = MP_OBJ_TO_PTR(_shape); } @@ -2072,11 +2068,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { if(inplace) { mp_raise_ValueError(MP_ERROR_TEXT("cannot assign new shape")); } - if(mp_obj_is_type(_shape, &mp_type_tuple)) { - ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); - } else { - ndarray = ndarray_new_linear_array(source->len, source->dtype); - } + ndarray = ndarray_new_dense_ndarray(shape->len, new_shape, source->dtype); ndarray_copy_array(source, ndarray, 0); } return MP_OBJ_FROM_PTR(ndarray); diff --git a/code/ulab.c b/code/ulab.c index f55768bc..df73f7bb 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.5.0 +#define ULAB_VERSION 6.5.1 #define xstr(s) str(s) #define str(s) #s diff --git a/tests/2d/numpy/reshape.py b/tests/2d/numpy/reshape.py new file mode 100644 index 00000000..7f4add6a --- /dev/null +++ b/tests/2d/numpy/reshape.py @@ -0,0 +1,17 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +for dtype in dtypes: + print() + print('=' * 50) + a = np.array(range(12), dtype=dtype).reshape((3, 4)) + print(a) + b = a[0,:] + print(b.reshape((1,4))) + b = a[:,0] + print(b.reshape((1,3))) + diff --git a/tests/2d/numpy/reshape.py.exp b/tests/2d/numpy/reshape.py.exp new file mode 100644 index 00000000..806a26c3 --- /dev/null +++ b/tests/2d/numpy/reshape.py.exp @@ -0,0 +1,35 @@ + +================================================== +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=uint8) +array([[0, 1, 2, 3]], dtype=uint8) +array([[0, 4, 8]], dtype=uint8) + +================================================== +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=int8) +array([[0, 1, 2, 3]], dtype=int8) +array([[0, 4, 8]], dtype=int8) + +================================================== +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=uint16) +array([[0, 1, 2, 3]], dtype=uint16) +array([[0, 4, 8]], dtype=uint16) + +================================================== +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=int16) +array([[0, 1, 2, 3]], dtype=int16) +array([[0, 4, 8]], dtype=int16) + +================================================== +array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0]], dtype=float64) +array([[0.0, 1.0, 2.0, 3.0]], dtype=float64) +array([[0.0, 4.0, 8.0]], dtype=float64) From c49110572dc47c2eeb413f610cc25ab8328e0e79 Mon Sep 17 00:00:00 2001 From: KB Sriram Date: Wed, 28 Feb 2024 11:27:29 -0800 Subject: [PATCH 085/113] Update type annotations in compare.c and vector.c (#663) - Add type annotations for functions in compare.c - Update annotations in vector.c to match behavior Fixes https://github.com/v923z/micropython-ulab/issues/662 --- code/numpy/compare.c | 123 ++++++++++++++++++++++++++++++++++++++++- code/numpy/numerical.c | 2 + code/numpy/vector.c | 62 ++++++++++----------- 3 files changed, 153 insertions(+), 34 deletions(-) diff --git a/code/numpy/compare.c b/code/numpy/compare.c index fabc33a9..b2762e41 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -140,7 +140,23 @@ static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) #endif #if ULAB_NUMPY_HAS_CLIP - +//| def clip( +//| a: _ScalarOrArrayLike, +//| a_min: _ScalarOrArrayLike, +//| a_max: _ScalarOrArrayLike, +//| ) -> _ScalarOrNdArray: +//| """ +//| Clips (limits) the values in an array. +//| +//| :param a: Scalar or array containing elements to clip. +//| :param a_min: Minimum value, it will be broadcast against ``a``. +//| :param a_max: Maximum value, it will be broadcast against ``a``. +//| :return: +//| A scalar or array with the elements of ``a``, but where +//| values < ``a_min`` are replaced with ``a_min``, and those +//| > ``a_max`` with ``a_max``. +//| """ +//| ... mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) { // Note: this function could be made faster by implementing a single-loop comparison in // RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we @@ -166,7 +182,18 @@ MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip); #endif #if ULAB_NUMPY_HAS_EQUAL - +//| def equal(x: _ScalarOrArrayLike, y: _ScalarOrArrayLike) -> _ScalarOrNdArray: +//| """ +//| Returns ``x == y`` element-wise. +//| +//| :param x, y: +//| Input scalar or array. If ``x.shape != y.shape`` they must +//| be broadcastable to a common shape (which becomes the +//| shape of the output.) +//| :return: +//| A boolean scalar or array with the element-wise result of ``x == y``. +//| """ +//| ... mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) { return compare_equal_helper(x1, x2, COMPARE_EQUAL); } @@ -175,7 +202,21 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal); #endif #if ULAB_NUMPY_HAS_NOTEQUAL - +//| def not_equal( +//| x: _ScalarOrArrayLike, +//| y: _ScalarOrArrayLike, +//| ) -> Union[_bool, ulab.numpy.ndarray]: +//| """ +//| Returns ``x != y`` element-wise. +//| +//| :param x, y: +//| Input scalar or array. If ``x.shape != y.shape`` they must +//| be broadcastable to a common shape (which becomes the +//| shape of the output.) +//| :return: +//| A boolean scalar or array with the element-wise result of ``x != y``. +//| """ +//| ... mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) { return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL); } @@ -270,6 +311,16 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { #endif #if ULAB_NUMPY_HAS_ISFINITE +//| def isfinite(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]: +//| """ +//| Tests element-wise for finiteness (i.e., it should not be infinity or a NaN). +//| +//| :param x: Input scalar or ndarray. +//| :return: +//| A boolean scalar or array with True where ``x`` is finite, and +//| False otherwise. +//| """ +//| ... mp_obj_t compare_isfinite(mp_obj_t _x) { return compare_isinf_isfinite(_x, 0); } @@ -278,6 +329,16 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite); #endif #if ULAB_NUMPY_HAS_ISINF +//| def isinf(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]: +//| """ +//| Tests element-wise for positive or negative infinity. +//| +//| :param x: Input scalar or ndarray. +//| :return: +//| A boolean scalar or array with True where ``x`` is positive or +//| negative infinity, and False otherwise. +//| """ +//| ... mp_obj_t compare_isinf(mp_obj_t _x) { return compare_isinf_isfinite(_x, 1); } @@ -286,6 +347,18 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf); #endif #if ULAB_NUMPY_HAS_MAXIMUM +//| def maximum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray: +//| """ +//| Returns the element-wise maximum. +//| +//| :param x1, x2: +//| Input scalar or array. If ``x.shape != y.shape`` they must +//| be broadcastable to a common shape (which becomes the +//| shape of the output.) +//| :return: +//| A scalar or array with the element-wise maximum of ``x1`` and ``x2``. +//| """ +//| ... mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) { // extra round, so that we can return maximum(3, 4) properly mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM); @@ -301,6 +374,18 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum); #if ULAB_NUMPY_HAS_MINIMUM +//| def minimum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray: +//| """ +//| Returns the element-wise minimum. +//| +//| :param x1, x2: +//| Input scalar or array. If ``x.shape != y.shape`` they must +//| be broadcastable to a common shape (which becomes the +//| shape of the output.) +//| :return: +//| A scalar or array with the element-wise minimum of ``x1`` and ``x2``. +//| """ +//| ... mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) { // extra round, so that we can return minimum(3, 4) properly mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM); @@ -316,6 +401,17 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum); #if ULAB_NUMPY_HAS_NONZERO +//| def nonzero(x: _ScalarOrArrayLike) -> ulab.numpy.ndarray: +//| """ +//| Returns the indices of elements that are non-zero. +//| +//| :param x: +//| Input scalar or array. If ``x`` is a scalar, it is treated +//| as a single-element 1-d array. +//| :return: +//| An array of indices that are non-zero. +//| """ +//| ... mp_obj_t compare_nonzero(mp_obj_t x) { ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); // since ndarray_new_linear_array calls m_new0, the content of zero is a single zero @@ -446,6 +542,27 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_nonzero_obj, compare_nonzero); #if ULAB_NUMPY_HAS_WHERE +//| def where( +//| condition: _ScalarOrArrayLike, +//| x: _ScalarOrArrayLike, +//| y: _ScalarOrArrayLike, +//| ) -> ulab.numpy.ndarray: +//| """ +//| Returns elements from ``x`` or ``y`` depending on ``condition``. +//| +//| :param condition: +//| Input scalar or array. If an element (or scalar) is truthy, +//| the corresponding element from ``x`` is chosen, otherwise +//| ``y`` is used. ``condition``, ``x`` and ``y`` must also be +//| broadcastable to the same shape (which becomes the output +//| shape.) +//| :param x, y: +//| Input scalar or array. +//| :return: +//| An array with elements from ``x`` when ``condition`` is +//| truthy, and ``y`` elsewhere. +//| """ +//| ... mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { // this implementation will work with ndarrays, and scalars only ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0); diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index b642d5a2..e3b42525 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -45,6 +45,8 @@ enum NUMERICAL_FUNCTION_TYPE { //| from typing import Dict //| //| _ArrayLike = Union[ndarray, List[_float], Tuple[_float], range] +//| _ScalarOrArrayLike = Union[int, _float, _ArrayLike] +//| _ScalarOrNdArray = Union[int, _float, ndarray] //| //| _DType = int //| """`ulab.numpy.int8`, `ulab.numpy.uint8`, `ulab.numpy.int16`, `ulab.numpy.uint16`, `ulab.numpy.float` or `ulab.numpy.bool`""" diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 0df3a338..95e2e207 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -235,7 +235,7 @@ static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t) #if ULAB_NUMPY_HAS_ACOS -//| def acos(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def acos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse cosine function""" //| ... //| @@ -249,7 +249,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos); #endif /* ULAB_NUMPY_HAS_ACOS */ #if ULAB_NUMPY_HAS_ACOSH -//| def acosh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def acosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse hyperbolic cosine function""" //| ... //| @@ -263,7 +263,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh); #endif /* ULAB_NUMPY_HAS_ACOSH */ #if ULAB_NUMPY_HAS_ASIN -//| def asin(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def asin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse sine function""" //| ... //| @@ -277,7 +277,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin); #endif /* ULAB_NUMPY_HAS_ASIN */ #if ULAB_NUMPY_HAS_ASINH -//| def asinh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def asinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse hyperbolic sine function""" //| ... //| @@ -291,7 +291,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh); #endif /* ULAB_NUMPY_HAS_ASINH */ #if ULAB_NUMPY_HAS_AROUND -//| def around(a: _ArrayLike, *, decimals: int = 0) -> ulab.numpy.ndarray: +//| def around(a: ulab.numpy.ndarray, *, decimals: int = 0) -> ulab.numpy.ndarray: //| """Returns a new float array in which each element is rounded to //| ``decimals`` places.""" //| ... @@ -371,7 +371,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around); #endif /* ULAB_NUMPY_HAS_AROUND */ #if ULAB_NUMPY_HAS_ATAN -//| def atan(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def atan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse tangent function; the return values are in the //| range [-pi/2,pi/2].""" //| ... @@ -387,7 +387,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan); #if ULAB_NUMPY_HAS_ATANH -//| def atanh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def atanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse hyperbolic tangent function""" //| ... //| @@ -401,7 +401,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh); #endif /* ULAB_NUMPY_HAS_ATANH */ #if ULAB_NUMPY_HAS_ARCTAN2 -//| def arctan2(ya: _ArrayLike, xa: _ArrayLike) -> ulab.numpy.ndarray: +//| def arctan2(ya: _ScalarOrArrayLike, xa: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the inverse tangent function of y/x; the return values are in //| the range [-pi, pi].""" //| ... @@ -494,7 +494,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2); #endif /* ULAB_VECTORISE_HAS_ARCTAN2 */ #if ULAB_NUMPY_HAS_CEIL -//| def ceil(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def ceil(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Rounds numbers up to the next whole number""" //| ... //| @@ -508,7 +508,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil); #endif /* ULAB_NUMPY_HAS_CEIL */ #if ULAB_NUMPY_HAS_COS -//| def cos(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def cos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the cosine function""" //| ... //| @@ -522,7 +522,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos); #endif /* ULAB_NUMPY_HAS_COS */ #if ULAB_NUMPY_HAS_COSH -//| def cosh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def cosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the hyperbolic cosine function""" //| ... //| @@ -536,7 +536,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh); #endif /* ULAB_NUMPY_HAS_COSH */ #if ULAB_NUMPY_HAS_DEGREES -//| def degrees(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def degrees(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Converts angles from radians to degrees""" //| ... //| @@ -559,7 +559,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees); #endif /* ULAB_NUMPY_HAS_DEGREES */ #if ULAB_SCIPY_SPECIAL_HAS_ERF -//| def erf(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def erf(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the error function, which has applications in statistics""" //| ... //| @@ -573,7 +573,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf); #endif /* ULAB_SCIPY_SPECIAL_HAS_ERF */ #if ULAB_SCIPY_SPECIAL_HAS_ERFC -//| def erfc(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def erfc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the complementary error function, which has applications in statistics""" //| ... //| @@ -587,7 +587,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc); #endif /* ULAB_SCIPY_SPECIAL_HAS_ERFC */ #if ULAB_NUMPY_HAS_EXP -//| def exp(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def exp(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the exponent function.""" //| ... //| @@ -690,7 +690,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp); #endif /* ULAB_NUMPY_HAS_EXP */ #if ULAB_NUMPY_HAS_EXPM1 -//| def expm1(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def expm1(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function.""" //| ... //| @@ -704,7 +704,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1); #endif /* ULAB_NUMPY_HAS_EXPM1 */ #if ULAB_NUMPY_HAS_FLOOR -//| def floor(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def floor(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Rounds numbers up to the next whole number""" //| ... //| @@ -718,7 +718,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor); #endif /* ULAB_NUMPY_HAS_FLOOR */ #if ULAB_SCIPY_SPECIAL_HAS_GAMMA -//| def gamma(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def gamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the gamma function""" //| ... //| @@ -732,7 +732,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma); #endif /* ULAB_SCIPY_SPECIAL_HAS_GAMMA */ #if ULAB_SCIPY_SPECIAL_HAS_GAMMALN -//| def lgamma(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def lgamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the natural log of the gamma function""" //| ... //| @@ -746,7 +746,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma); #endif /* ULAB_SCIPY_SEPCIAL_HAS_GAMMALN */ #if ULAB_NUMPY_HAS_LOG -//| def log(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def log(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the natural log""" //| ... //| @@ -760,7 +760,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log); #endif /* ULAB_NUMPY_HAS_LOG */ #if ULAB_NUMPY_HAS_LOG10 -//| def log10(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def log10(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the log base 10""" //| ... //| @@ -774,7 +774,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10); #endif /* ULAB_NUMPY_HAS_LOG10 */ #if ULAB_NUMPY_HAS_LOG2 -//| def log2(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def log2(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the log base 2""" //| ... //| @@ -788,7 +788,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2); #endif /* ULAB_NUMPY_HAS_LOG2 */ #if ULAB_NUMPY_HAS_RADIANS -//| def radians(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def radians(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Converts angles from degrees to radians""" //| ... //| @@ -811,7 +811,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians); #endif /* ULAB_NUMPY_HAS_RADIANS */ #if ULAB_NUMPY_HAS_SIN -//| def sin(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def sin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the sine function""" //| ... //| @@ -825,7 +825,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); #endif /* ULAB_NUMPY_HAS_SIN */ #if ULAB_NUMPY_HAS_SINC -//| def sinc(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def sinc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the normalized sinc function""" //| ... //| @@ -852,7 +852,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc); #endif /* ULAB_NUMPY_HAS_SINC */ #if ULAB_NUMPY_HAS_SINH -//| def sinh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def sinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the hyperbolic sine""" //| ... //| @@ -867,7 +867,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh); #if ULAB_NUMPY_HAS_SQRT -//| def sqrt(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def sqrt(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the square root""" //| ... //| @@ -1030,7 +1030,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt); #endif /* ULAB_NUMPY_HAS_SQRT */ #if ULAB_NUMPY_HAS_TAN -//| def tan(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def tan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the tangent""" //| ... //| @@ -1044,7 +1044,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan); #endif /* ULAB_NUMPY_HAS_TAN */ #if ULAB_NUMPY_HAS_TANH -//| def tanh(a: _ArrayLike) -> ulab.numpy.ndarray: +//| def tanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray: //| """Computes the hyperbolic tangent""" //| ... @@ -1158,12 +1158,12 @@ const mp_obj_type_t vector_function_type = { //| f: Union[Callable[[int], _float], Callable[[_float], _float]], //| *, //| otypes: Optional[_DType] = None -//| ) -> Callable[[_ArrayLike], ulab.numpy.ndarray]: +//| ) -> Callable[[_ScalarOrArrayLike], ulab.numpy.ndarray]: //| """ //| :param callable f: The function to wrap //| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float. //| -//| Wrap a Python function ``f`` so that it can be applied to arrays. +//| Wrap a Python function ``f`` so that it can be applied to arrays or scalars. A scalar passed to the wrapped function is treated as a single-element 1-D array. //| The callable must return only values of the types specified by ``otypes``, or the result is undefined.""" //| ... //| From 63dfbd178ba3b5cfbff5d656f586d7f3f3e46409 Mon Sep 17 00:00:00 2001 From: Philip Howard Date: Thu, 29 Feb 2024 20:34:50 +0000 Subject: [PATCH 086/113] Remove the STATIC macro. (#664) Reflect the changes proposed in micropython/micropython#13763. --- code/ndarray.c | 4 ++-- code/numpy/approx.c | 4 ++-- code/numpy/fft/fft.c | 4 ++-- code/numpy/linalg/linalg.c | 4 ++-- code/scipy/optimize/optimize.c | 4 ++-- code/ulab.c | 12 ++++++------ docs/manual/source/ulab-programming.rst | 2 +- docs/ulab-programming.ipynb | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 84ce8495..5e662851 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -917,7 +917,7 @@ ndarray_obj_t *ndarray_from_iterable(mp_obj_t obj, uint8_t dtype) { return ndarray; } -STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +static uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, @@ -940,7 +940,7 @@ STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_m return _dtype; } -STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args, mp_map_t *kw_args) { +static mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args, mp_map_t *kw_args) { uint8_t dtype = ndarray_init_helper(n_args, args, kw_args); if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 23f9da1b..a268bb1d 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -47,7 +47,7 @@ ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, //| ... //| -STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +static mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, @@ -151,7 +151,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp); //| ... //| -STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +static mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 42621f52..e0893c2f 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -88,13 +88,13 @@ static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft); #endif -STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = { +static const mp_rom_map_elem_t ulab_fft_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fft) }, { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&fft_fft_obj) }, { MP_ROM_QSTR(MP_QSTR_ifft), MP_ROM_PTR(&fft_ifft_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table); +static MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table); const mp_obj_module_t ulab_fft_module = { .base = { &mp_type_module }, diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 6fc79aa0..70b1d20b 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -506,7 +506,7 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ MP_DEFINE_CONST_FUN_OBJ_KW(linalg_qr_obj, 1, linalg_qr); #endif -STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = { +static const mp_rom_map_elem_t ulab_linalg_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) }, #if ULAB_MAX_DIMS > 1 #if ULAB_LINALG_HAS_CHOLESKY @@ -530,7 +530,7 @@ STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = { #endif }; -STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table); +static MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table); const mp_obj_module_t ulab_linalg_module = { .base = { &mp_type_module }, diff --git a/code/scipy/optimize/optimize.c b/code/scipy/optimize/optimize.c index 9bd80878..bd2e2c29 100644 --- a/code/scipy/optimize/optimize.c +++ b/code/scipy/optimize/optimize.c @@ -55,7 +55,7 @@ static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, //| ... //| -STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +static mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // Simple bisection routine static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, @@ -125,7 +125,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(optimize_bisect_obj, 3, optimize_bisect); //| ... //| -STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +static mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // downhill simplex method in 1D static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, diff --git a/code/ulab.c b/code/ulab.c index df73f7bb..b18d9fa3 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -43,13 +43,13 @@ #define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D) #endif -STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING); +static MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING); #ifdef ULAB_HASH -STATIC MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH)); +static MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH)); #endif -STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { +static const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { #if ULAB_MAX_DIMS > 1 #if NDARRAY_HAS_RESHAPE { MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) }, @@ -78,7 +78,7 @@ STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { #endif }; -STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table); +static MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table); #if defined(MP_DEFINE_CONST_OBJ_TYPE) // MicroPython after-b41aaaa (Sept 19 2022). @@ -192,7 +192,7 @@ const mp_obj_type_t ndarray_flatiter_type = { #endif #endif -STATIC const mp_rom_map_elem_t ulab_globals_table[] = { +static const mp_rom_map_elem_t ulab_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) }, { MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) }, #ifdef ULAB_HASH @@ -217,7 +217,7 @@ STATIC const mp_rom_map_elem_t ulab_globals_table[] = { #endif }; -STATIC MP_DEFINE_CONST_DICT ( +static MP_DEFINE_CONST_DICT ( mp_module_ulab_globals, ulab_globals_table ); diff --git a/docs/manual/source/ulab-programming.rst b/docs/manual/source/ulab-programming.rst index f8c81c9c..3abb3ac4 100644 --- a/docs/manual/source/ulab-programming.rst +++ b/docs/manual/source/ulab-programming.rst @@ -896,7 +896,7 @@ the ``user`` module: .. code:: c - STATIC const mp_rom_map_elem_t ulab_user_globals_table[] = { + static const mp_rom_map_elem_t ulab_user_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) }, { MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj }, }; diff --git a/docs/ulab-programming.ipynb b/docs/ulab-programming.ipynb index 6eabf6d5..9685991e 100644 --- a/docs/ulab-programming.ipynb +++ b/docs/ulab-programming.ipynb @@ -718,7 +718,7 @@ "Finally, we have to bind this function object in the globals table of the `user` module: \n", "\n", "```c\n", - "STATIC const mp_rom_map_elem_t ulab_user_globals_table[] = {\n", + "static const mp_rom_map_elem_t ulab_user_globals_table[] = {\n", " { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) },\n", " { MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj },\n", "};\n", From 65c941a8059afe1cfd6f4c2b15d0ade798dc24f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 6 Mar 2024 18:59:32 +0100 Subject: [PATCH 087/113] fix loadtxt for the case, when built-in complexes are not supported (#666) --- code/numpy/io/io.c | 4 ++++ code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/code/numpy/io/io.c b/code/numpy/io/io.c index f2479f19..95e62adc 100644 --- a/code/numpy/io/io.c +++ b/code/numpy/io/io.c @@ -239,7 +239,11 @@ MP_DEFINE_CONST_FUN_OBJ_1(io_load_obj, io_load); #if ULAB_NUMPY_HAS_LOADTXT static void io_assign_value(const char *clipboard, uint8_t len, ndarray_obj_t *ndarray, size_t *idx, uint8_t dtype) { + #if MICROPY_PY_BUILTINS_COMPLEX mp_obj_t value = mp_parse_num_decimal(clipboard, len, false, false, NULL); + #else + mp_obj_t value = mp_parse_num_float(clipboard, len, false, NULL); + #endif if(dtype != NDARRAY_FLOAT) { mp_float_t _value = mp_obj_get_float(value); value = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(_value)); diff --git a/code/ulab.c b/code/ulab.c index b18d9fa3..ef2b06fe 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.5.1 +#define ULAB_VERSION 6.5.2 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 7113d8b3..9f885936 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Wed, 6 Mar 2024 + +version 6.5.2 + + allow loadtxt to parse numbers, even if built-in complexes are not supported + Tue, 9 Jan 2024 version 6.5.0 From 6fb60ef9cffc85a951d11fef85cd5df70c277b0f Mon Sep 17 00:00:00 2001 From: Philip Howard Date: Thu, 6 Jun 2024 20:54:24 +0100 Subject: [PATCH 088/113] CI: Update some deprecated dependency versions (#669) * CI: Bump various build workflow versions. * CI: Continue other jobs on failure. Give a more complete overview of failures, rather than forcing the developer to play whack-a-mole. * CI: Do not modify unix mpconfigport.h. Since `MICROPY_PY_UHASHLIB` does not occur in unix/mpconfigport.h this would have simply duplicated the config file and supplied it as `MP_CONFIGFILE` triggering a bug in how this define is handled. --- .github/workflows/build.yml | 20 +++++++++++--------- build-cp.sh | 3 +-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8900bc71..c0477efb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ on: jobs: micropython: + continue-on-error: true strategy: matrix: os: @@ -28,10 +29,10 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - name: Set up Python 3.10 - uses: actions/setup-python@v1 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.12" - name: Install requirements run: | @@ -44,10 +45,10 @@ jobs: gcc --version python3 --version - name: Checkout ulab - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Checkout micropython repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: micropython/micropython path: micropython @@ -56,6 +57,7 @@ jobs: run: ./build.sh ${{ matrix.dims }} circuitpython: + continue-on-error: true strategy: matrix: os: @@ -68,10 +70,10 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - name: Set up Python 3.10 - uses: actions/setup-python@v1 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.12" - name: Versions run: | @@ -79,7 +81,7 @@ jobs: python3 --version - name: Checkout ulab - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Install requirements run: | diff --git a/build-cp.sh b/build-cp.sh index ee6a95ef..9081a618 100755 --- a/build-cp.sh +++ b/build-cp.sh @@ -41,8 +41,7 @@ HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )" rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab dims=${1-2} make -C circuitpython/mpy-cross -j$NPROC -sed -e '/MICROPY_PY_UHASHLIB/s/1/0/' < circuitpython/ports/unix/mpconfigport.h > circuitpython/ports/unix/mpconfigport_ulab.h -make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-DMP_CONFIGFILE=\"\" -Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims +make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims # bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims" From 519c48149f39ba9fa327f0cbb38b7d766a7d8ba9 Mon Sep 17 00:00:00 2001 From: Andy Piper Date: Sun, 21 Jul 2024 12:20:18 +0100 Subject: [PATCH 089/113] Removed Pycom reference (#675) UInfortunately Pycom is no longer a thing, removed from the platforms list. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e7feeca6..df406254 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,6 @@ of the user manual. 1. `MaixPy` https://github.com/sipeed/MaixPy 1. `OpenMV` https://github.com/openmv/openmv 1. `pimoroni-pico` https://github.com/pimoroni/pimoroni-pico -3. `pycom` https://pycom.io/ ## Compiling From e40a667e3c22251fba36a4b7a38d9f75635210f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Jul 2024 17:20:56 +0200 Subject: [PATCH 090/113] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df406254..5c91d671 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ulab -[![Documentation Status](https://readthedocs.org/projects/micropython-ulab-robert/badge/?version=latest)](https://micropython-ulab-robert.readthedocs.io/en/latest/?badge=latest) +[![Documentation Status](https://readthedocs.org/projects/micropython-ulab/badge/?version=latest)](https://micropython-ulab.readthedocs.io/en/latest/index.html) `ulab` is a `numpy`-like array manipulation library for [micropython](http://micropython.org/) and [CircuitPython](https://circuitpython.org/). The module is written in C, defines compact containers (`ndarray`s) for numerical data of one to four From 41c4363f11c8d639ff4676b3680a1e3f23a93327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Jul 2024 18:59:29 +0200 Subject: [PATCH 091/113] address issue raised in https://github.com/v923z/micropython-ulab/issues/676 (#677) * ndarrays can be created from buffer --- code/ndarray.c | 31 ++++++++++++++++++------------- code/ndarray.h | 2 +- code/numpy/create.c | 13 ++----------- code/numpy/numerical.c | 2 +- code/ulab.c | 2 +- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 5e662851..26ced6fc 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019-2022 Zoltán Vörös + * Copyright (c) 2019-2024 Zoltán Vörös * 2020 Jeff Epler for Adafruit Industries * 2020 Taku Fukada */ @@ -509,8 +509,9 @@ static size_t multiply_size(size_t a, size_t b) { return result; } -ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype) { +ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype, uint8_t *buffer) { // Creates the base ndarray with shape, and initialises the values to straight 0s + // optionally, values can be supplied via the last argument ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t); ndarray->base.type = &ulab_ndarray_type; ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype; @@ -536,9 +537,13 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides // if the length is 0, still allocate a single item, so that contractions can be handled size_t len = multiply_size(ndarray->itemsize, MAX(1, ndarray->len)); - uint8_t *array = m_new0(byte, len); - // this should set all elements to 0, irrespective of the of the dtype (all bits are zero) - // we could, perhaps, leave this step out, and initialise the array only, when needed + uint8_t *array; + array = buffer; + if(array == NULL) { + // this should set all elements to 0, irrespective of the of the dtype (all bits are zero) + // we could, perhaps, leave this step out, and initialise the array only, when needed + array = m_new0(byte, len); + } ndarray->array = array; ndarray->origin = array; return ndarray; @@ -547,12 +552,12 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dtype) { // creates a dense array, i.e., one, where the strides are derived directly from the shapes // the function should work in the general n-dimensional case - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - strides[ULAB_MAX_DIMS-1] = (int32_t)ulab_binary_get_size(dtype); - for(size_t i=ULAB_MAX_DIMS; i > 1; i--) { - strides[i-2] = strides[i-1] * MAX(1, shape[i-1]); - } - return ndarray_new_ndarray(ndim, shape, strides, dtype); + // int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + // strides[ULAB_MAX_DIMS - 1] = (int32_t)ulab_binary_get_size(dtype); + // for(size_t i = ULAB_MAX_DIMS; i > 1; i--) { + // strides[i-2] = strides[i-1] * MAX(1, shape[i-1]); + // } + return ndarray_new_ndarray(ndim, shape, NULL, dtype, NULL); } ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dtype) { @@ -650,7 +655,7 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { if(source->boolean) { dtype = NDARRAY_BOOL; } - ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype); + ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype, NULL); ndarray_copy_array(source, ndarray, 0); return ndarray; } @@ -1884,7 +1889,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { #if ULAB_SUPPORTS_COMPLEX if(self->dtype == NDARRAY_COMPLEX) { int32_t *strides = strides_from_shape(self->shape, NDARRAY_FLOAT); - ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT); + ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT, NULL); ndarray = MP_OBJ_TO_PTR(carray_abs(self, target)); } else { #endif diff --git a/code/ndarray.h b/code/ndarray.h index 2ce84bed..3e82b385 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -188,7 +188,7 @@ int32_t *ndarray_contract_strides(ndarray_obj_t *, uint8_t ); ndarray_obj_t *ndarray_from_iterable(mp_obj_t , uint8_t ); ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t , size_t *, uint8_t ); ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *, uint8_t ); -ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t ); +ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t , uint8_t *); ndarray_obj_t *ndarray_new_linear_array(size_t , uint8_t ); ndarray_obj_t *ndarray_new_view(ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t ); bool ndarray_is_dense(ndarray_obj_t *); diff --git a/code/numpy/create.c b/code/numpy/create.c index f427e374..e915f127 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -838,19 +838,10 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw len = count; } } - ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t); - ndarray->base.type = &ulab_ndarray_type; - ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype; - ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; - ndarray->ndim = 1; - ndarray->len = len; - ndarray->itemsize = sz; - ndarray->shape[ULAB_MAX_DIMS - 1] = len; - ndarray->strides[ULAB_MAX_DIMS - 1] = sz; + size_t *shape = ndarray_shape_vector(0, 0, 0, len); uint8_t *buffer = bufinfo.buf; - ndarray->array = buffer + offset; - return MP_OBJ_FROM_PTR(ndarray); + return ndarray_new_ndarray(1, shape, NULL, dtype, buffer + offset); } return mp_const_none; } diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index e3b42525..68cf44dd 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -746,7 +746,7 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw numerical_reduce_axes(ndarray, ax, shape, strides); // We could return an NDARRAY_UINT8 array, if all lengths are shorter than 256 - ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16); + ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16, NULL); int32_t *istrides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(indices, ax, shape, istrides); diff --git a/code/ulab.c b/code/ulab.c index ef2b06fe..24776986 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.5.2 +#define ULAB_VERSION 6.5.3 #define xstr(s) str(s) #define str(s) #s From 99cb54a4260e214b683654480aa4f5d104f1184c Mon Sep 17 00:00:00 2001 From: page200 <69221289+page200@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:07:32 +0200 Subject: [PATCH 092/113] Correct `a[a < 5]` in ulab-ndarray.ipynb (#678) --- docs/ulab-ndarray.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index 0e2bb1b0..8d67ed9b 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -3270,7 +3270,7 @@ "output_type": "stream", "text": [ "a:\t array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float)\n", - "a < 5:\t array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)\n", + "a[a < 5]:\t array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)\n", "\n", "\n" ] @@ -3283,7 +3283,7 @@ "\n", "a = np.array(range(9), dtype=np.float)\n", "print(\"a:\\t\", a)\n", - "print(\"a < 5:\\t\", a[a < 5])" + "print(\"a[a < 5]:\\t\", a[a < 5])" ] }, { From a77022dcd0de69392ffa1a1491ede44ed17020d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Mart=C3=ADnez?= <58857054+elpekenin@users.noreply.github.com> Date: Sun, 25 Aug 2024 16:09:47 +0200 Subject: [PATCH 093/113] add missing typing (#680) --- code/numpy/carray/carray.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 159c191d..bf0d7166 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -25,9 +25,11 @@ #if ULAB_SUPPORTS_COMPLEX +//| import builtins +//| //| import ulab.numpy -//| def real(val): +//| def real(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray: //| """ //| Return the real part of the complex argument, which can be //| either an ndarray, or a scalar.""" @@ -54,7 +56,7 @@ mp_obj_t carray_real(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); -//| def imag(val): +//| def imag(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray: //| """ //| Return the imaginary part of the complex argument, which can be //| either an ndarray, or a scalar.""" @@ -82,7 +84,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); #if ULAB_NUMPY_HAS_CONJUGATE -//| def conjugate(val): +//| def conjugate( +//| val: builtins.complex | ulab.numpy.ndarray +//| ) -> builtins.complex | ulab.numpy.ndarray: //| """ //| Return the conjugate of the complex argument, which can be //| either an ndarray, or a scalar.""" From 1d3ddd8f5228e2c7335dac71f4846e6abb0ec9f9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 9 Sep 2024 00:55:05 -0400 Subject: [PATCH 094/113] numpy/random.c: fix use of MICROPY_PY_RANDOM_SEED_INIT_FUNC (#684) --- code/numpy/random/random.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 73a34f82..165f11b5 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -41,7 +41,7 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_generator, MP_TYPE_FLAG_NONE, print, random_generator_print, - make_new, random_generator_make_new, + make_new, random_generator_make_new, locals_dict, &random_generator_locals_dict ); #else @@ -76,11 +76,12 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz if(args[0] == mp_const_none) { #ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC mp_raise_ValueError(MP_ERROR_TEXT("no default seed")); - #endif + #else random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); generator->base.type = &random_generator_type; generator->state = MICROPY_PY_RANDOM_SEED_INIT_FUNC; return MP_OBJ_FROM_PTR(generator); + #endif } else if(mp_obj_is_int(args[0])) { random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); generator->base.type = &random_generator_type; @@ -89,7 +90,7 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz } else if(mp_obj_is_type(args[0], &mp_type_tuple)){ mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]); mp_obj_t *items = m_new(mp_obj_t, seeds->len); - + for(uint8_t i = 0; i < seeds->len; i++) { random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); generator->base.type = &random_generator_type; @@ -175,7 +176,7 @@ static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_float_t *array = (mp_float_t *)ndarray->array; - // numpy's random supports only dense output arrays, so we can simply + // numpy's random supports only dense output arrays, so we can simply // loop through the elements in a linear fashion for(size_t i = 0; i < ndarray->len; i = i + 2) { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT @@ -248,7 +249,7 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t if(!mp_obj_is_type(out, &ulab_ndarray_type)) { mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type")); } - + ndarray = MP_OBJ_TO_PTR(out); if(ndarray->dtype != NDARRAY_FLOAT) { @@ -283,10 +284,10 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_float_t *array = (mp_float_t *)ndarray->array; - // numpy's random supports only dense output arrays, so we can simply + // numpy's random supports only dense output arrays, so we can simply // loop through the elements in a linear fashion for(size_t i = 0; i < ndarray->len; i++) { - + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT uint32_t x = pcg32_next(&self->state); *array = (float)(int32_t)(x >> 8) * 0x1.0p-24f; @@ -375,4 +376,3 @@ const mp_obj_module_t ulab_numpy_random_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_random_globals, }; - From 45f23ebc82426b3a4569377a04bb2747c447c2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 14 Sep 2024 11:38:04 +0200 Subject: [PATCH 095/113] Roll fix (#687) * fix roll, when shift is 0 --- code/numpy/numerical.c | 8 +++++++- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 68cf44dd..0961e3c0 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -1186,13 +1186,19 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(MP_ERROR_TEXT("roll argument must be an ndarray")); } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); - uint8_t *array = ndarray->array; ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype); int32_t shift = mp_obj_get_int(args[1].u_obj); + + if(shift == 0) { + ndarray_copy_array(ndarray, results, 0); + return MP_OBJ_FROM_PTR(results); + } + int32_t _shift = shift < 0 ? -shift : shift; size_t counter; + uint8_t *array = ndarray->array; uint8_t *rarray = (uint8_t *)results->array; if(args[2].u_obj == mp_const_none) { // roll the flattened array diff --git a/code/ulab.c b/code/ulab.c index 24776986..948d621f 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.5.3 +#define ULAB_VERSION 6.5.4 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 9f885936..90b86307 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sat, 14 Sep 2024 + +version 6.5.4 + + fix roll, when shift is 0 + Wed, 6 Mar 2024 version 6.5.2 From c0b3262be49de3162c9c0a7082bcd2d52907012e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 14 Sep 2024 12:18:14 +0200 Subject: [PATCH 096/113] Add keyword arguments to spectrogram (#657) * re-work spectrogram method, so that RAM can be re-used * update docs with spectrogram changes --- code/numpy/fft/fft.c | 14 +-- code/numpy/fft/fft_tools.c | 47 ++----- code/numpy/fft/fft_tools.h | 5 +- code/numpy/poly.c | 2 +- code/ulab.c | 2 +- code/ulab.h | 2 +- code/utils/utils.c | 182 ++++++++++++++++++++++++++-- docs/manual/source/conf.py | 2 +- docs/manual/source/ulab-intro.rst | 13 +- docs/manual/source/ulab-ndarray.rst | 4 +- docs/manual/source/ulab-utils.rst | 106 ++++++++++++---- docs/ulab-change-log.md | 6 + docs/ulab-convert.ipynb | 49 +++++++- docs/ulab-intro.ipynb | 15 +-- docs/ulab-utils.ipynb | 102 ++++++++++++---- tests/1d/numpy/fft.py | 16 ++- 16 files changed, 434 insertions(+), 133 deletions(-) diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index e0893c2f..d4cab9e5 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -5,7 +5,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019-2021 Zoltán Vörös + * Copyright (c) 2019-2024 Zoltán Vörös * 2020 Scott Shawcroft for Adafruit Industries * 2020 Taku Fukada */ @@ -43,16 +43,16 @@ //| #if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE static mp_obj_t fft_fft(mp_obj_t arg) { - return fft_fft_ifft_spectrogram(arg, FFT_FFT); + return fft_fft_ifft(arg, FFT_FFT); } MP_DEFINE_CONST_FUN_OBJ_1(fft_fft_obj, fft_fft); #else static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) { if(n_args == 2) { - return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_FFT); + return fft_fft_ifft(n_args, args[0], args[1], FFT_FFT); } else { - return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_FFT); + return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_FFT); } } @@ -71,7 +71,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft); #if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE static mp_obj_t fft_ifft(mp_obj_t arg) { - return fft_fft_ifft_spectrogram(arg, FFT_IFFT); + return fft_fft_ifft(arg, FFT_IFFT); } MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft); @@ -79,9 +79,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft); static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) { NOT_IMPLEMENTED_FOR_COMPLEX() if(n_args == 2) { - return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_IFFT); + return fft_fft_ifft(n_args, args[0], args[1], FFT_IFFT); } else { - return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_IFFT); + return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_IFFT); } } diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 04c0b2f7..bc98b3d3 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -5,7 +5,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019-2021 Zoltán Vörös + * Copyright (c) 2019-2024 Zoltán Vörös */ #include @@ -45,7 +45,7 @@ imag[i] = data[2i+1] */ -void fft_kernel_complex(mp_float_t *data, size_t n, int isign) { +void fft_kernel(mp_float_t *data, size_t n, int isign) { size_t j, m, mmax, istep; mp_float_t tempr, tempi; mp_float_t wtemp, wr, wpr, wpi, wi, theta; @@ -94,9 +94,9 @@ void fft_kernel_complex(mp_float_t *data, size_t n, int isign) { /* * The following function is a helper interface to the python side. * It has been factored out from fft.c, so that the same argument parsing - * routine can be called from scipy.signal.spectrogram. + * routine can be called from utils.spectrogram. */ -mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) { +mp_obj_t fft_fft_ifft(mp_obj_t data_in, uint8_t type) { if(!mp_obj_is_type(data_in, &ulab_ndarray_type)) { mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only")); } @@ -134,20 +134,10 @@ mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) { } data -= 2 * len; - if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) { - fft_kernel_complex(data, len, 1); - if(type == FFT_SPECTROGRAM) { - ndarray_obj_t *spectrum = ndarray_new_linear_array(len, NDARRAY_FLOAT); - mp_float_t *sarray = (mp_float_t *)spectrum->array; - for(size_t i = 0; i < len; i++) { - *sarray++ = MICROPY_FLOAT_C_FUN(sqrt)(data[0] * data[0] + data[1] * data[1]); - data += 2; - } - m_del(mp_float_t, data, 2 * len); - return MP_OBJ_FROM_PTR(spectrum); - } + if(type == FFT_FFT) { + fft_kernel(data, len, 1); } else { // inverse transform - fft_kernel_complex(data, len, -1); + fft_kernel(data, len, -1); // TODO: numpy accepts the norm keyword argument for(size_t i = 0; i < 2 * len; i++) { *data++ /= len; @@ -202,7 +192,7 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) { } } -mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) { +mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) { if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) { mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only")); } @@ -258,15 +248,8 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i data_im -= len; } - if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) { + if(type == FFT_FFT) { fft_kernel(data_re, data_im, len, 1); - if(type == FFT_SPECTROGRAM) { - for(size_t i=0; i < len; i++) { - *data_re = MICROPY_FLOAT_C_FUN(sqrt)(*data_re * *data_re + *data_im * *data_im); - data_re++; - data_im++; - } - } } else { // inverse transform fft_kernel(data_re, data_im, len, -1); // TODO: numpy accepts the norm keyword argument @@ -275,13 +258,9 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i *data_im++ /= len; } } - if(type == FFT_SPECTROGRAM) { - return MP_OBJ_FROM_PTR(out_re); - } else { - mp_obj_t tuple[2]; - tuple[0] = MP_OBJ_FROM_PTR(out_re); - tuple[1] = MP_OBJ_FROM_PTR(out_im); - return mp_obj_new_tuple(2, tuple); - } + mp_obj_t tuple[2]; + tuple[0] = MP_OBJ_FROM_PTR(out_re); + tuple[1] = MP_OBJ_FROM_PTR(out_im); + return mp_obj_new_tuple(2, tuple); } #endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */ diff --git a/code/numpy/fft/fft_tools.h b/code/numpy/fft/fft_tools.h index 9444232f..aa598201 100644 --- a/code/numpy/fft/fft_tools.h +++ b/code/numpy/fft/fft_tools.h @@ -14,15 +14,14 @@ enum FFT_TYPE { FFT_FFT, FFT_IFFT, - FFT_SPECTROGRAM, }; #if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE void fft_kernel(mp_float_t *, size_t , int ); -mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t , uint8_t ); +mp_obj_t fft_fft_ifft(mp_obj_t , uint8_t ); #else void fft_kernel(mp_float_t *, mp_float_t *, size_t , int ); -mp_obj_t fft_fft_ifft_spectrogram(size_t , mp_obj_t , mp_obj_t , uint8_t ); +mp_obj_t fft_fft_ifft(size_t , mp_obj_t , mp_obj_t , uint8_t ); #endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */ #endif /* _FFT_TOOLS_ */ diff --git a/code/numpy/poly.c b/code/numpy/poly.c index ca5aa638..8b8e1435 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -121,7 +121,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { ndarray_obj_t *beta = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT); mp_float_t *betav = (mp_float_t *)beta->array; // x[0..(deg+1)] contains now the product X^T * y; we can get rid of y - m_del(float, y, leny); + m_del(mp_float_t, y, leny); // now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now for(uint8_t i=0; i < deg+1; i++) { diff --git a/code/ulab.c b/code/ulab.c index 948d621f..66582693 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.5.4 +#define ULAB_VERSION 6.5.5 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index a384bfe2..36462e5b 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -440,7 +440,7 @@ // Note that in this case, the input also must be numpythonic, // i.e., the real an imaginary parts cannot be passed as two arguments #ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE -#define ULAB_FFT_IS_NUMPY_COMPATIBLE (0) +#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1) #endif #ifndef ULAB_FFT_HAS_FFT diff --git a/code/utils/utils.c b/code/utils/utils.c index 1d282d86..8477f5de 100644 --- a/code/utils/utils.c +++ b/code/utils/utils.c @@ -5,7 +5,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020-2021 Zoltán Vörös + * Copyright (c) 2020-2024 Zoltán Vörös */ #include @@ -16,6 +16,7 @@ #include "py/misc.h" #include "utils.h" +#include "../ulab_tools.h" #include "../numpy/fft/fft_tools.h" #if ULAB_HAS_UTILS_MODULE @@ -203,23 +204,180 @@ MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_bu //| ... //| -mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *args) { - #if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE - return fft_fft_ifft_spectrogram(args[0], FFT_SPECTROGRAM); +mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE }} , + #if !ULAB_FFT_IS_NUMPY_COMPATIBLE + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + #endif + { MP_QSTR_scratchpad, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_log, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only")); + } + ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj); + + #if ULAB_MAX_DIMS > 1 + if(in->ndim != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only")); + } + #endif + + size_t len = in->len; + // Check if input is of length of power of 2 + if((len & (len-1)) != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2")); + } + + ndarray_obj_t *out = NULL; + + #if ULAB_FFT_IS_NUMPY_COMPATIBLE + mp_obj_t scratchpad_object = args[1].u_obj; + mp_obj_t out_object = args[2].u_obj; + mp_obj_t log_object = args[3].u_obj; #else + mp_obj_t scratchpad_object = args[2].u_obj; + mp_obj_t out_object = args[3].u_obj; + mp_obj_t log_object = args[4].u_obj; + #endif + + if(out_object != mp_const_none) { + if(!mp_obj_is_type(out_object, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("out must be an ndarray")); + } + + out = MP_OBJ_TO_PTR(out_object); + if((out->dtype != NDARRAY_FLOAT) || (out->ndim != 1)){ + mp_raise_TypeError(MP_ERROR_TEXT("out array must be a 1D array of float type")); + } + if(len != out->len) { + mp_raise_ValueError(MP_ERROR_TEXT("input and out arrays must have same length")); + } + } else { + out = ndarray_new_linear_array(len, NDARRAY_FLOAT); + } + + ndarray_obj_t *scratchpad = NULL; + mp_float_t *tmp = NULL; + + if(scratchpad_object != mp_const_none) { + if(!mp_obj_is_type(scratchpad_object, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("scratchpad must be an ndarray")); + } + + scratchpad = MP_OBJ_TO_PTR(scratchpad_object); + if(!ndarray_is_dense(scratchpad) || (scratchpad->ndim != 1) || (scratchpad->dtype != NDARRAY_FLOAT)) { + mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be a 1D dense float array")); + } + if(scratchpad->len != 2 * len) { + mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be twice as long as input")); + } + + tmp = (mp_float_t *)scratchpad->array; + } else { + tmp = m_new0(mp_float_t, 2 * len); + } + + uint8_t *array = (uint8_t *)in->array; + + #if ULAB_FFT_IS_NUMPY_COMPATIBLE // ULAB_SUPPORTS_COMPLEX is automatically true + if(in->dtype == NDARRAY_COMPLEX) { + uint8_t sz = 2 * sizeof(mp_float_t); + for(size_t i = 0; i < len; i++) { + memcpy(tmp, array, sz); + tmp += 2; + array += in->strides[ULAB_MAX_DIMS - 1]; + } + } else { + mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype); + for(size_t i = 0; i < len; i++) { + *tmp++ = func(array); // real part + *tmp++ = 0; // imaginary part, clear + array += in->strides[ULAB_MAX_DIMS - 1]; + } + } + + tmp -= 2 * len; + fft_kernel(tmp, len, 1); + #else // we might have two real input vectors + + ndarray_obj_t *in2 = NULL; + + if(n_args == 2) { + if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only")); + } + in2 = MP_OBJ_TO_PTR(args[1].u_obj); + + #if ULAB_MAX_DIMS > 1 + if(in2->ndim != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only")); + } + #endif + if(len != in2->len) { + mp_raise_TypeError(MP_ERROR_TEXT("input arrays are not compatible")); + } + } + + mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype); + + for(size_t i = 0; i < len; i++) { + *tmp++ = func(array); // real part; imageinary will be cleared later + array += in->strides[ULAB_MAX_DIMS - 1]; + } + if(n_args == 2) { - return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_SPECTROGRAM); + mp_float_t (*func2)(void *) = ndarray_get_float_function(in2->dtype); + array = (uint8_t *)in2->array; + for(size_t i = 0; i < len; i++) { + *tmp++ = func2(array); + array += in2->strides[ULAB_MAX_DIMS - 1]; + } + tmp -= len; } else { - return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_SPECTROGRAM); + // if there is only one input argument, clear the imaginary part + memset(tmp, 0, len * sizeof(mp_float_t)); } - #endif + + tmp -= len; + + fft_kernel(tmp, tmp + len, len, 1); + #endif /* ULAB_FFT_IS_NUMPY_COMPATIBLE */ + + mp_float_t *spectrum = (mp_float_t *)out->array; + uint8_t spectrum_sz = out->strides[ULAB_MAX_DIMS - 1] / sizeof(mp_float_t); + + for(size_t i = 0; i < len; i++) { + #if ULAB_FFT_IS_NUMPY_COMPATIBLE + *spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + 1) * *(tmp + 1)); + tmp += 2; + #else + *spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + len) * *(tmp + len)); + tmp++; + #endif + if(log_object == mp_const_true) { + *spectrum = MICROPY_FLOAT_C_FUN(log)(*spectrum); + } + spectrum += spectrum_sz; + } + + if(scratchpad_object == mp_const_none) { + tmp -= len; + #if ULAB_FFT_IS_NUMPY_COMPATIBLE + tmp -= len; + #endif + m_del(mp_float_t, tmp, 2 * len); + } + return MP_OBJ_FROM_PTR(out); } -#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 1, utils_spectrogram); -#else -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 2, utils_spectrogram); -#endif +MP_DEFINE_CONST_FUN_OBJ_KW(utils_spectrogram_obj, 1, utils_spectrogram); #endif /* ULAB_UTILS_HAS_SPECTROGRAM */ diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 4a2cea6e..6bbff154 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,7 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '6.5.0' +release = '6.5.5' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/ulab-intro.rst b/docs/manual/source/ulab-intro.rst index 81019e33..42e5b260 100644 --- a/docs/manual/source/ulab-intro.rst +++ b/docs/manual/source/ulab-intro.rst @@ -8,9 +8,10 @@ Enter ulab ``ulab`` is a ``numpy``-like module for ``micropython`` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. ``ulab`` implements a small subset of ``numpy`` -and ``scipy``. The functions were chosen such that they might be useful -in the context of a microcontroller. However, the project is a living -one, and suggestions for new features are always welcome. +and ``scipy``, as well as a number of functions manipulating byte +arrays. The functions were chosen such that they might be useful in the +context of a microcontroller. However, the project is a living one, and +suggestions for new features are always welcome. This document discusses how you can use the library, starting from building your own firmware, through questions like what affects the @@ -265,9 +266,9 @@ functions that are part of ``numpy``, you have to import ``numpy`` as p = np.array([1, 2, 3]) np.polyval(p, x) -There are a couple of exceptions to this rule, namely ``fft``, and -``linalg``, which are sub-modules even in ``numpy``, thus you have to -write them out as +There are a couple of exceptions to this rule, namely ``fft``, +``linalg``, and ``random``, which are sub-modules even in ``numpy``, +thus you have to write them out as .. code:: python diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index ef54a242..04036338 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -2330,12 +2330,12 @@ future version of ``ulab``. a = np.array(range(9), dtype=np.float) print("a:\t", a) - print("a < 5:\t", a[a < 5]) + print("a[a < 5]:\t", a[a < 5]) .. parsed-literal:: a: array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float) - a < 5: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float) + a[a < 5]: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float) diff --git a/docs/manual/source/ulab-utils.rst b/docs/manual/source/ulab-utils.rst index 82476bad..4305dfb9 100644 --- a/docs/manual/source/ulab-utils.rst +++ b/docs/manual/source/ulab-utils.rst @@ -52,7 +52,9 @@ Here is an example without keyword arguments a = bytearray([1, 1, 0, 0, 0, 0, 0, 255]) print('a: ', a) print() - print('unsigned integers: ', utils.from_uint32_buffer(a)) + print('unsigned integers: ', utils.from_uint32_buffe + print('original vector:\n', y) + print('\nspectrum:\n', a)r(a)) b = bytearray([1, 1, 0, 0, 0, 0, 0, 255]) print('\nb: ', b) @@ -144,9 +146,53 @@ In addition to the Fourier transform and its inverse, ``ulab`` also sports a function called ``spectrogram``, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. -The arguments are treated in the same way as in ``fft``, and ``ifft``. -This means that, if the firmware was compiled with complex support, the -input can also be a complex array. +The positional arguments are treated in the same way as in ``fft``, and +``ifft``. This means that, if the firmware was compiled with complex +support and ``ULAB_FFT_IS_NUMPY_COMPATIBLE`` is defined to be 1 in +``ulab.h``, the input can also be a complex array. + +And easy way to find out if the FFT is ``numpy``-compatible is to check +the number of values ``fft.fft`` returns, when called with a single real +argument of length other than 2: + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + if len(np.fft.fft(np.zeros(4))) == 2: + print('FFT is NOT numpy compatible (real and imaginary parts are treated separately)') + else: + print('FFT is numpy compatible (complex inputs/outputs)') + +.. parsed-literal:: + + FFT is numpy compatible (complex inputs/outputs) + + + + +Depending on the ``numpy``-compatibility of the FFT, the ``spectrogram`` +function takes one or two positional arguments, and three keyword +arguments. If the FFT is ``numpy`` compatible, one positional argument +is allowed, and it is a 1D real or complex ``ndarray``. If the FFT is +not ``numpy``-compatible, if a single argument is supplied, it will be +treated as the real part of the input, and if two positional arguments +are supplied, they are treated as the real and imaginary parts of the +signal. + +The keyword arguments are as follows: + +1. ``scratchpad = None``: must be a 1D, dense, floating point array, + twice as long as the input array; the ``scratchpad`` will be used as + a temporary internal buffer to perform the Fourier transform; the + ``scratchpad`` can repeatedly be re-used. +2. ``out = None``: must be a 1D, not necessarily dense, floating point + array that will store the results +3. ``log = False``: must be either ``True``, or ``False``; if ``True``, + the ``spectrogram`` returns the logarithm of the absolute values of + the Fourier transform. .. code:: @@ -169,17 +215,24 @@ input can also be a complex array. array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64) spectrum: - array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64) + array([187.8635087634578, 315.3112063607119, 347.8814873399375, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64) As such, ``spectrogram`` is really just a shorthand for -``np.sqrt(a*a + b*b)``, however, it saves significant amounts of RAM: -the expression ``a*a + b*b`` has to allocate memory for ``a*a``, -``b*b``, and finally, their sum. In contrast, ``spectrogram`` calculates -the spectrum internally, and stores it in the memory segment that was -reserved for the real part of the Fourier transform. +``np.abs(np.fft.fft(signal))``, if the FFT is ``numpy``-compatible, or +``np.sqrt(a*a + b*b)`` if the FFT returns the real (``a``) and imaginary +(``b``) parts separately. However, ``spectrogram`` saves significant +amounts of RAM: the expression ``a*a + b*b`` has to allocate memory for +``a*a``, ``b*b``, and finally, their sum. Similarly, ``np.abs`` returns +a new array. This issue is compounded even more, if ``np.log()`` is used +on the absolute value. + +In contrast, ``spectrogram`` handles all calculations in the same +internal arrays, and allows one to re-use previously reserved RAM. This +can be especially useful in cases, when ``spectogram`` is called +repeatedly, as in the snippet below. .. code:: @@ -188,25 +241,34 @@ reserved for the real part of the Fourier transform. from ulab import numpy as np from ulab import utils as utils - x = np.linspace(0, 10, num=1024) - y = np.sin(x) + n = 1024 + t = np.linspace(0, 2 * np.pi, num=1024) + scratchpad = np.zeros(2 * n) - a, b = np.fft.fft(y) + for _ in range(10): + signal = np.sin(t) + utils.spectrogram(signal, out=signal, scratchpad=scratchpad, log=True) - print('\nspectrum calculated the hard way:\n', np.sqrt(a*a + b*b)) + print('signal: ', signal) - a = utils.spectrogram(y) + for _ in range(10): + signal = np.sin(t) + out = np.log(utils.spectrogram(signal)) - print('\nspectrum calculated the lazy way:\n', a) + print('out: ', out) .. parsed-literal:: - - spectrum calculated the hard way: - array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64) - - spectrum calculated the lazy way: - array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64) + signal: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64) + out: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64) + +Note that ``scratchpad`` is reserved only once, and then is re-used in +the first loop. By assigning ``signal`` to the output, we save +additional RAM. This approach avoids the usual problem of memory +fragmentation, which would happen in the second loop, where both +``spectrogram``, and ``np.log`` must reserve RAM in each iteration. + + diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 90b86307..4600c7c4 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,5 +1,11 @@ Sat, 14 Sep 2024 +version 6.5.5 + + add scratchpad, out, log keyword arguments to spectrum + +Sat, 14 Sep 2024 + version 6.5.4 fix roll, when shift is 0 diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index bd587912..ef5e24f5 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -57,11 +57,11 @@ "# -- Project information -----------------------------------------------------\n", "\n", "project = 'The ulab book'\n", - "copyright = '2019-2022, Zoltán Vörös and contributors'\n", + "copyright = '2019-2024, Zoltán Vörös and contributors'\n", "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '5.1.0'\n", + "release = '6.5.5'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -190,6 +190,7 @@ " numpy-universal\n", " numpy-fft\n", " numpy-linalg\n", + " numpy-random\n", " scipy-linalg\n", " scipy-optimize\n", " scipy-signal\n", @@ -215,7 +216,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:21.647179Z", @@ -256,14 +257,49 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:42.024028Z", "start_time": "2022-02-09T06:27:36.109093Z" } }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n" + ] + } + ], "source": [ "files = ['ulab-intro',\n", " 'ulab-ndarray',\n", @@ -271,6 +307,7 @@ " 'numpy-universal',\n", " 'numpy-fft',\n", " 'numpy-linalg',\n", + " 'numpy-random',\n", " 'scipy-linalg',\n", " 'scipy-optimize',\n", " 'scipy-signal',\n", @@ -449,7 +486,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.11.7" }, "toc": { "base_numbering": 1, diff --git a/docs/ulab-intro.ipynb b/docs/ulab-intro.ipynb index 67d6b608..30266418 100644 --- a/docs/ulab-intro.ipynb +++ b/docs/ulab-intro.ipynb @@ -10,13 +10,6 @@ } }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Matplotlib is building the font cache; this may take a moment.\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -38,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T18:13:14.590799Z", @@ -239,7 +232,7 @@ "source": [ "## Enter ulab\n", "\n", - "`ulab` is a `numpy`-like module for `micropython` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. `ulab` implements a small subset of `numpy` and `scipy`. The functions were chosen such that they might be useful in the context of a microcontroller. However, the project is a living one, and suggestions for new features are always welcome. \n", + "`ulab` is a `numpy`-like module for `micropython` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. `ulab` implements a small subset of `numpy` and `scipy`, as well as a number of functions manipulating byte arrays. The functions were chosen such that they might be useful in the context of a microcontroller. However, the project is a living one, and suggestions for new features are always welcome. \n", "\n", "This document discusses how you can use the library, starting from building your own firmware, through questions like what affects the firmware size, what are the trade-offs, and what are the most important differences to `numpy` and `scipy`, respectively. The document is organised as follows:\n", "\n", @@ -404,7 +397,7 @@ "np.polyval(p, x)\n", "```\n", "\n", - "There are a couple of exceptions to this rule, namely `fft`, and `linalg`, which are sub-modules even in `numpy`, thus you have to write them out as \n", + "There are a couple of exceptions to this rule, namely `fft`, `linalg`, and `random`, which are sub-modules even in `numpy`, thus you have to write them out as \n", "\n", "```python\n", "from ulab import numpy as np\n", @@ -842,7 +835,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.13" }, "toc": { "base_numbering": 1, diff --git a/docs/ulab-utils.ipynb b/docs/ulab-utils.ipynb index 3c5e5c66..8dce72c0 100644 --- a/docs/ulab-utils.ipynb +++ b/docs/ulab-utils.ipynb @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-01-29T16:53:11.972661Z", @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2022-01-29T16:59:24.652277Z", @@ -77,7 +77,7 @@ " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", " with open('/dev/shm/micropython.py', 'w') as fout:\n", " fout.write(cell)\n", - " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " print(proc.stdout.read().decode(\"utf-8\"))\n", " print(proc.stderr.read().decode(\"utf-8\"))\n", @@ -291,7 +291,9 @@ "a = bytearray([1, 1, 0, 0, 0, 0, 0, 255])\n", "print('a: ', a)\n", "print()\n", - "print('unsigned integers: ', utils.from_uint32_buffer(a))\n", + "print('unsigned integers: ', utils.from_uint32_buffe\n", + "print('original vector:\\n', y)\n", + "print('\\nspectrum:\\n', a)r(a))\n", "\n", "b = bytearray([1, 1, 0, 0, 0, 0, 0, 255])\n", "print('\\nb: ', b)\n", @@ -398,12 +400,53 @@ "source": [ "## spectrogram\n", "\n", - "In addition to the Fourier transform and its inverse, `ulab` also sports a function called `spectrogram`, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. The arguments are treated in the same way as in `fft`, and `ifft`. This means that, if the firmware was compiled with complex support, the input can also be a complex array." + "In addition to the Fourier transform and its inverse, `ulab` also sports a function called `spectrogram`, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. The positional arguments are treated in the same way as in `fft`, and `ifft`. This means that, if the firmware was compiled with complex support and `ULAB_FFT_IS_NUMPY_COMPATIBLE` is defined to be 1 in `ulab.h`, the input can also be a complex array. \n", + "\n", + "And easy way to find out if the FFT is `numpy`-compatible is to check the number of values `fft.fft` returns, when called with a single real argument of length other than 2: " ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "FFT is numpy compatible (complex inputs/outputs)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "if len(np.fft.fft(np.zeros(4))) == 2:\n", + " print('FFT is NOT numpy compatible (real and imaginary parts are treated separately)')\n", + "else:\n", + " print('FFT is numpy compatible (complex inputs/outputs)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Depending on the `numpy`-compatibility of the FFT, the `spectrogram` function takes one or two positional arguments, and three keyword arguments. If the FFT is `numpy` compatible, one positional argument is allowed, and it is a 1D real or complex `ndarray`. If the FFT is not `numpy`-compatible, if a single argument is supplied, it will be treated as the real part of the input, and if two positional arguments are supplied, they are treated as the real and imaginary parts of the signal.\n", + "\n", + "The keyword arguments are as follows:\n", + "\n", + "1. `scratchpad = None`: must be a 1D, dense, floating point array, twice as long as the input array; the `scratchpad` will be used as a temporary internal buffer to perform the Fourier transform; the `scratchpad` can repeatedly be re-used.\n", + "1. `out = None`: must be a 1D, not necessarily dense, floating point array that will store the results\n", + "1. `log = False`: must be either `True`, or `False`; if `True`, the `spectrogram` returns the logarithm of the absolute values of the Fourier transform." + ] + }, + { + "cell_type": "code", + "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2022-01-29T16:59:56.400603Z", @@ -419,7 +462,7 @@ " array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64)\n", "\n", "spectrum:\n", - " array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n", + " array([187.8635087634578, 315.3112063607119, 347.8814873399375, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n", "\n", "\n" ] @@ -444,12 +487,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As such, `spectrogram` is really just a shorthand for `np.sqrt(a*a + b*b)`, however, it saves significant amounts of RAM: the expression `a*a + b*b` has to allocate memory for `a*a`, `b*b`, and finally, their sum. In contrast, `spectrogram` calculates the spectrum internally, and stores it in the memory segment that was reserved for the real part of the Fourier transform." + "As such, `spectrogram` is really just a shorthand for `np.abs(np.fft.fft(signal))`, if the FFT is `numpy`-compatible, or `np.sqrt(a*a + b*b)` if the FFT returns the real (`a`) and imaginary (`b`) parts separately. However, `spectrogram` saves significant amounts of RAM: the expression `a*a + b*b` has to allocate memory for `a*a`, `b*b`, and finally, their sum. Similarly, `np.abs` returns a new array. This issue is compounded even more, if `np.log()` is used on the absolute value. \n", + "\n", + "In contrast, `spectrogram` handles all calculations in the same internal arrays, and allows one to re-use previously reserved RAM. This can be especially useful in cases, when `spectogram` is called repeatedly, as in the snippet below." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 34, "metadata": { "ExecuteTime": { "end_time": "2022-01-29T16:59:48.485610Z", @@ -461,12 +506,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "spectrum calculated the hard way:\n", - " array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n", - "\n", - "spectrum calculated the lazy way:\n", - " array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n", + "signal: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)\n", + "out: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)\n", "\n", "\n" ] @@ -478,17 +519,34 @@ "from ulab import numpy as np\n", "from ulab import utils as utils\n", "\n", - "x = np.linspace(0, 10, num=1024)\n", - "y = np.sin(x)\n", + "n = 1024\n", + "t = np.linspace(0, 2 * np.pi, num=1024)\n", + "scratchpad = np.zeros(2 * n)\n", "\n", - "a, b = np.fft.fft(y)\n", + "for _ in range(10):\n", + " signal = np.sin(t)\n", + " utils.spectrogram(signal, out=signal, scratchpad=scratchpad, log=True)\n", "\n", - "print('\\nspectrum calculated the hard way:\\n', np.sqrt(a*a + b*b))\n", + "print('signal: ', signal)\n", "\n", - "a = utils.spectrogram(y)\n", + "for _ in range(10):\n", + " signal = np.sin(t)\n", + " out = np.log(utils.spectrogram(signal))\n", "\n", - "print('\\nspectrum calculated the lazy way:\\n', a)" + "print('out: ', out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that `scratchpad` is reserved only once, and then is re-used in the first loop. By assigning `signal` to the output, we save additional RAM. This approach avoids the usual problem of memory fragmentation, which would happen in the second loop, where both `spectrogram`, and `np.log` must reserve RAM in each iteration." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] } ], "metadata": { @@ -507,7 +565,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.11.7" }, "toc": { "base_numbering": 1, diff --git a/tests/1d/numpy/fft.py b/tests/1d/numpy/fft.py index 1a1dee75..6b79f74c 100644 --- a/tests/1d/numpy/fft.py +++ b/tests/1d/numpy/fft.py @@ -10,8 +10,12 @@ y = np.sin(x) if use_ulab: - a, b = np.fft.fft(y) - c, d = np.fft.ifft(a, b) + if 'real' in dir(np): + a = np.fft.fft(y) + c = np.real(np.fft.ifft(a)) + else: + a, b = np.fft.fft(y) + c, d = np.fft.ifft(a, b) # c should be equal to y cmp_result = [] for p,q in zip(list(y), list(c)): @@ -19,8 +23,12 @@ print(cmp_result) z = np.zeros(len(x)) - a, b = np.fft.fft(y, z) - c, d = np.fft.ifft(a, b) + if 'real' in dir(np): + a = np.fft.fft(y) + c = np.real(np.fft.ifft(a)) + else: + a, b = np.fft.fft(y, z) + c, d = np.fft.ifft(a, b) # c should be equal to y cmp_result = [] for p,q in zip(list(y), list(c)): From 2b74236c8ce278d876e410c00e90de5bf5e9980e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 9 Oct 2024 21:10:25 +0200 Subject: [PATCH 097/113] Take (#688) * add numpy.take --- code/numpy/create.c | 231 ++++++++++++++++++++++++- code/numpy/create.h | 5 + code/numpy/numpy.c | 3 + code/ulab.c | 2 +- code/ulab.h | 4 + code/ulab_tools.c | 28 +++ code/ulab_tools.h | 5 +- docs/manual/source/conf.py | 3 +- docs/manual/source/numpy-functions.rst | 71 +++++++- docs/numpy-functions.ipynb | 66 ++++++- docs/ulab-change-log.md | 6 + docs/ulab-convert.ipynb | 4 +- tests/2d/numpy/take.py | 30 ++++ tests/2d/numpy/take.py.exp | 105 +++++++++++ 14 files changed, 544 insertions(+), 19 deletions(-) create mode 100644 tests/2d/numpy/take.py create mode 100644 tests/2d/numpy/take.py.exp diff --git a/code/numpy/create.c b/code/numpy/create.c index e915f127..ad957ce7 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -6,7 +6,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Jeff Epler for Adafruit Industries - * 2019-2021 Zoltán Vörös + * 2019-2024 Zoltán Vörös * 2020 Taku Fukada */ @@ -776,6 +776,235 @@ mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones); #endif +#if ULAB_NUMPY_HAS_TAKE +//| def take( +//| a: ulab.numpy.ndarray, +//| indices: _ArrayLike, +//| axis: Optional[int] = None, +//| out: Optional[ulab.numpy.ndarray] = None, +//| mode: Optional[str] = None) -> ulab.numpy.ndarray: +//| """ +//| .. param: a +//| The source array. +//| .. param: indices +//| The indices of the values to extract. +//| .. param: axis +//| The axis over which to select values. By default, the flattened input array is used. +//| .. param: out +//| If provided, the result will be placed in this array. It should be of the appropriate shape and dtype. +//| .. param: mode +//| Specifies how out-of-bounds indices will behave. +//| - `raise`: raise an error (default) +//| - `wrap`: wrap around +//| - `clip`: clip to the range +//| `clip` mode means that all indices that are too large are replaced by the +//| index that addresses the last element along that axis. Note that this disables +//| indexing with negative numbers. +//| +//| Return a new array.""" +//| ... +//| + +enum CREATE_TAKE_MODE { + CREATE_TAKE_RAISE, + CREATE_TAKE_WRAP, + CREATE_TAKE_CLIP, +}; + +mp_obj_t create_take(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("input is not an array")); + } + + ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); + int8_t axis = 0; + int8_t axis_index = 0; + int32_t axis_len; + uint8_t mode = CREATE_TAKE_RAISE; + uint8_t ndim; + + // axis keyword argument + if(args[2].u_obj == mp_const_none) { + // work with the flattened array + axis_len = a->len; + ndim = 1; + } else { // i.e., axis is an integer + // TODO: this pops up at quite a few places, write it as a function + axis = mp_obj_get_int(args[2].u_obj); + ndim = a->ndim; + if(axis < 0) axis += a->ndim; + if((axis < 0) || (axis > a->ndim - 1)) { + mp_raise_ValueError(MP_ERROR_TEXT("index out of range")); + } + axis_index = ULAB_MAX_DIMS - a->ndim + axis; + axis_len = (int32_t)a->shape[axis_index]; + } + + size_t _len; + // mode keyword argument + if(mp_obj_is_str(args[4].u_obj)) { + const char *_mode = mp_obj_str_get_data(args[4].u_obj, &_len); + if(memcmp(_mode, "raise", 5) == 0) { + mode = CREATE_TAKE_RAISE; + } else if(memcmp(_mode, "wrap", 4) == 0) { + mode = CREATE_TAKE_WRAP; + } else if(memcmp(_mode, "clip", 4) == 0) { + mode = CREATE_TAKE_CLIP; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("mode should be raise, wrap or clip")); + } + } + + size_t indices_len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj)); + + size_t *indices = m_new(size_t, indices_len); + + mp_obj_iter_buf_t buf; + mp_obj_t item, iterable = mp_getiter(args[1].u_obj, &buf); + + size_t z = 0; + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + int32_t index = mp_obj_get_int(item); + if(mode == CREATE_TAKE_RAISE) { + if(index < 0) { + index += axis_len; + } + if((index < 0) || (index > axis_len - 1)) { + m_del(size_t, indices, indices_len); + mp_raise_ValueError(MP_ERROR_TEXT("index out of range")); + } + } else if(mode == CREATE_TAKE_WRAP) { + index %= axis_len; + } else { // mode == CREATE_TAKE_CLIP + if(index < 0) { + m_del(size_t, indices, indices_len); + mp_raise_ValueError(MP_ERROR_TEXT("index must not be negative")); + } + if(index > axis_len - 1) { + index = axis_len - 1; + } + } + indices[z++] = (size_t)index; + } + + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); + if(args[2].u_obj == mp_const_none) { // flattened array + shape[ULAB_MAX_DIMS - 1] = indices_len; + } else { + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + shape[i] = a->shape[i]; + if(i == axis_index) { + shape[i] = indices_len; + } + } + } + + ndarray_obj_t *out = NULL; + if(args[3].u_obj == mp_const_none) { + // no output was supplied + out = ndarray_new_dense_ndarray(ndim, shape, a->dtype); + } else { + // TODO: deal with last argument being false! + out = ulab_tools_inspect_out(args[3].u_obj, a->dtype, ndim, shape, true); + } + + #if ULAB_MAX_DIMS > 1 // we can save the hassle, if there is only one possible dimension + if((args[2].u_obj == mp_const_none) || (a->ndim == 1)) { // flattened array + #endif + uint8_t *out_array = (uint8_t *)out->array; + for(size_t x = 0; x < indices_len; x++) { + uint8_t *a_array = (uint8_t *)a->array; + size_t remainder = indices[x]; + uint8_t q = ULAB_MAX_DIMS - 1; + do { + size_t div = (remainder / a->shape[q]); + a_array += remainder * a->strides[q]; + remainder -= div * a->shape[q]; + q--; + } while(q > ULAB_MAX_DIMS - a->ndim); + // NOTE: for floats and complexes, this might be + // better with memcpy(out_array, a_array, a->itemsize) + for(uint8_t p = 0; p < a->itemsize; p++) { + out_array[p] = a_array[p]; + } + out_array += a->itemsize; + } + #if ULAB_MAX_DIMS > 1 + } else { + // move the axis shape/stride to the leftmost position: + SWAP(size_t, a->shape[0], a->shape[axis_index]); + SWAP(size_t, out->shape[0], out->shape[axis_index]); + SWAP(int32_t, a->strides[0], a->strides[axis_index]); + SWAP(int32_t, out->strides[0], out->strides[axis_index]); + + for(size_t x = 0; x < indices_len; x++) { + uint8_t *a_array = (uint8_t *)a->array; + uint8_t *out_array = (uint8_t *)out->array; + a_array += indices[x] * a->strides[0]; + out_array += x * out->strides[0]; + + #if ULAB_MAX_DIMS > 3 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + // NOTE: for floats and complexes, this might be + // better with memcpy(out_array, a_array, a->itemsize) + for(uint8_t p = 0; p < a->itemsize; p++) { + out_array[p] = a_array[p]; + } + out_array += out->strides[ULAB_MAX_DIMS - 1]; + a_array += a->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < a->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 2 + out_array -= out->strides[ULAB_MAX_DIMS - 1] * out->shape[ULAB_MAX_DIMS - 1]; + out_array += out->strides[ULAB_MAX_DIMS - 2]; + a_array -= a->strides[ULAB_MAX_DIMS - 1] * a->shape[ULAB_MAX_DIMS - 1]; + a_array += a->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < a->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 3 + out_array -= out->strides[ULAB_MAX_DIMS - 2] * out->shape[ULAB_MAX_DIMS - 2]; + out_array += out->strides[ULAB_MAX_DIMS - 3]; + a_array -= a->strides[ULAB_MAX_DIMS - 2] * a->shape[ULAB_MAX_DIMS - 2]; + a_array += a->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < a->shape[ULAB_MAX_DIMS - 3]); + #endif + } + + // revert back to the original order + SWAP(size_t, a->shape[0], a->shape[axis_index]); + SWAP(size_t, out->shape[0], out->shape[axis_index]); + SWAP(int32_t, a->strides[0], a->strides[axis_index]); + SWAP(int32_t, out->strides[0], out->strides[axis_index]); + } + #endif /* ULAB_MAX_DIMS > 1 */ + m_del(size_t, indices, indices_len); + return MP_OBJ_FROM_PTR(out); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_take_obj, 2, create_take); +#endif /* ULAB_NUMPY_HAS_TAKE */ + #if ULAB_NUMPY_HAS_ZEROS //| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """ diff --git a/code/numpy/create.h b/code/numpy/create.h index 6e54b10e..ffa7a440 100644 --- a/code/numpy/create.h +++ b/code/numpy/create.h @@ -62,6 +62,11 @@ mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *); MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj); #endif +#if ULAB_NUMPY_HAS_TAKE +mp_obj_t create_take(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_take_obj); +#endif + #if ULAB_NUMPY_HAS_ZEROS mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *); MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index c1a45e92..eafd7728 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -291,6 +291,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_SUM { MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&numerical_sum_obj) }, #endif + #if ULAB_NUMPY_HAS_TAKE + { MP_ROM_QSTR(MP_QSTR_take), MP_ROM_PTR(&create_take_obj) }, + #endif // functions of the poly sub-module #if ULAB_NUMPY_HAS_POLYFIT { MP_ROM_QSTR(MP_QSTR_polyfit), MP_ROM_PTR(&poly_polyfit_obj) }, diff --git a/code/ulab.c b/code/ulab.c index 66582693..b9999671 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.5.5 +#define ULAB_VERSION 6.6.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 36462e5b..78b8a1ba 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -559,6 +559,10 @@ #define ULAB_NUMPY_HAS_SUM (1) #endif +#ifndef ULAB_NUMPY_HAS_TAKE +#define ULAB_NUMPY_HAS_TAKE (1) +#endif + #ifndef ULAB_NUMPY_HAS_TRACE #define ULAB_NUMPY_HAS_TRACE (1) #endif diff --git a/code/ulab_tools.c b/code/ulab_tools.c index b57f1e66..05ed1ede 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -274,3 +274,31 @@ bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) { } #endif } + +ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t out, uint8_t dtype, uint8_t ndim, size_t *shape, bool dense_only) { + if(!mp_obj_is_type(out, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type")); + } + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out); + + if(ndarray->dtype != dtype) { + mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dtype")); + } + + if(ndarray->ndim != ndim) { + mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dimension")); + } + + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + if(ndarray->shape[i] != shape[i]) { + mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong shape")); + } + } + + if(dense_only) { + if(!ndarray_is_dense(ndarray)) { + mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous")); + } + } + return ndarray; +} \ No newline at end of file diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 3e6b81e3..62170fb8 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -44,7 +44,6 @@ void ulab_rescale_float_strides(int32_t *); bool ulab_tools_mp_obj_is_scalar(mp_obj_t ); -#if ULAB_NUMPY_HAS_RANDOM_MODULE -ndarray_obj_t *ulab_tools_create_out(mp_obj_tuple_t , mp_obj_t , uint8_t , bool ); -#endif +ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t , uint8_t , uint8_t , size_t *, bool ); + #endif diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 6bbff154..c4a27e26 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,8 +27,7 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '6.5.5' - +release = '6.6.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/numpy-functions.rst b/docs/manual/source/numpy-functions.rst index a2a3e410..252f5a57 100644 --- a/docs/manual/source/numpy-functions.rst +++ b/docs/manual/source/numpy-functions.rst @@ -3,8 +3,8 @@ Numpy functions =============== This section of the manual discusses those functions that were adapted -from ``numpy``. Starred functions accept complex arrays as arguments, if -the firmware was compiled with complex support. +from ``numpy``. Functions with an asterisk accept complex arrays as +arguments, if the firmware was compiled with complex support. 1. `numpy.all\* <#all>`__ 2. `numpy.any\* <#any>`__ @@ -51,9 +51,10 @@ the firmware was compiled with complex support. 43. `numpy.sort_complex\* <#sort_complex>`__ 44. `numpy.std <#std>`__ 45. `numpy.sum <#sum>`__ -46. `numpy.trace <#trace>`__ -47. `numpy.trapz <#trapz>`__ -48. `numpy.where <#where>`__ +46. `numpy.take\* <#take>`__ +47. `numpy.trace <#trace>`__ +48. `numpy.trapz <#trapz>`__ +49. `numpy.where <#where>`__ all --- @@ -1985,6 +1986,66 @@ array. Otherwise, the calculation is along the given axis. +take +---- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.take.html + +The ``take`` method takes elements from an array along an axis. The +function accepts two positional arguments, the array, and the indices, +which is either a ``python`` iterable, or a one-dimensional ``ndarray``, +as well as three keyword arguments, the ``axis``, which can be ``None``, +or an integer, ``out``, which can be ``None``, or an ``ndarray`` with +the proper dimensions, and ``mode``, which can be one of the strings +``raise``, ``wrap``, or ``clip``. This last argument determines how +out-of-bounds indices will be treated. The default value is ``raise``, +which raises an exception. ``wrap`` takes the indices modulo the length +of the ``axis``, while ``clip`` pegs the values at the 0, and the length +of the ``axis``. If ``axis`` is ``None``, then ``take`` operates on the +flattened array. + +The function can be regarded as a method of advanced slicing: as opposed +to standard slicing, where the indices are distributed uniformly and in +either increasing or decreasing order, ``take`` can take indices in an +arbitrary order. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array(range(12)).reshape((3, 4)) + print('\na:', a) + + print('\nslices taken along first axis') + print(np.take(a, (0, 2, 2, 1), axis=0)) + + print('\nslices taken along second axis') + print(np.take(a, (0, 2, 2, 1), axis=1)) + +.. parsed-literal:: + + + a: array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0]], dtype=float64) + + slices taken along first axis + array([[0.0, 1.0, 2.0, 3.0], + [8.0, 9.0, 10.0, 11.0], + [8.0, 9.0, 10.0, 11.0], + [4.0, 5.0, 6.0, 7.0]], dtype=float64) + + slices taken along second axis + array([[0.0, 2.0, 2.0, 1.0], + [2.0, 3.0, 4.0, 5.0], + [6.0, 7.0, 8.0, 9.0]], dtype=float64) + + + + trace ----- diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index 2ef884a2..d13278cf 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2022-02-01T17:37:25.505687Z", @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-02-01T17:37:25.717714Z", @@ -230,7 +230,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This section of the manual discusses those functions that were adapted from `numpy`. Starred functions accept complex arrays as arguments, if the firmware was compiled with complex support.\n", + "This section of the manual discusses those functions that were adapted from `numpy`. Functions with an asterisk accept complex arrays as arguments, if the firmware was compiled with complex support.\n", "\n", "1. [numpy.all*](#all)\n", "1. [numpy.any*](#any)\n", @@ -277,6 +277,7 @@ "1. [numpy.sort_complex*](#sort_complex)\n", "1. [numpy.std](#std)\n", "1. [numpy.sum](#sum)\n", + "1. [numpy.take*](#take)\n", "1. [numpy.trace](#trace)\n", "1. [numpy.trapz](#trapz)\n", "1. [numpy.where](#where)" @@ -2682,6 +2683,63 @@ "print('std, vertical: ', np.sum(a, axis=0))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## take\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.take.html\n", + "\n", + "The `take` method takes elements from an array along an axis. The function accepts two positional arguments, the array, and the indices, which is either a `python` iterable, or a one-dimensional `ndarray`, as well as three keyword arguments, the `axis`, which can be `None`, or an integer, `out`, which can be `None`, or an `ndarray` with the proper dimensions, and `mode`, which can be one of the strings `raise`, `wrap`, or `clip`. This last argument determines how out-of-bounds indices will be treated. The default value is `raise`, which raises an exception. `wrap` takes the indices modulo the length of the `axis`, while `clip` pegs the values at the 0, and the length of the `axis`. If `axis` is `None`, then `take` operates on the flattened array.\n", + "\n", + "The function can be regarded as a method of advanced slicing: as opposed to standard slicing, where the indices are distributed uniformly and in either increasing or decreasing order, `take` can take indices in an arbitrary order." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "a: array([[0.0, 1.0, 2.0, 3.0],\n", + " [4.0, 5.0, 6.0, 7.0],\n", + " [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n", + "\n", + "slices taken along first axis\n", + "array([[0.0, 1.0, 2.0, 3.0],\n", + " [8.0, 9.0, 10.0, 11.0],\n", + " [8.0, 9.0, 10.0, 11.0],\n", + " [4.0, 5.0, 6.0, 7.0]], dtype=float64)\n", + "\n", + "slices taken along second axis\n", + "array([[0.0, 2.0, 2.0, 1.0],\n", + " [2.0, 3.0, 4.0, 5.0],\n", + " [6.0, 7.0, 8.0, 9.0]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(12)).reshape((3, 4))\n", + "print('\\na:', a)\n", + "\n", + "print('\\nslices taken along first axis')\n", + "print(np.take(a, (0, 2, 2, 1), axis=0))\n", + "\n", + "print('\\nslices taken along second axis')\n", + "print(np.take(a, (0, 2, 2, 1), axis=1))\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2900,7 +2958,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.11.7" }, "toc": { "base_numbering": 1, diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 4600c7c4..11e4a648 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Wed, 9 Oct 2024 + +version 6.6.0 + + add numpy.take + Sat, 14 Sep 2024 version 6.5.5 diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index ef5e24f5..4ce30e5a 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '6.5.5'\n", + "release = '6.6.0'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -294,8 +294,6 @@ "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", " _, nbc = validator.normalize(nbc)\n", "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", " _, nbc = validator.normalize(nbc)\n" ] } diff --git a/tests/2d/numpy/take.py b/tests/2d/numpy/take.py new file mode 100644 index 00000000..3bf5ab8b --- /dev/null +++ b/tests/2d/numpy/take.py @@ -0,0 +1,30 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +print('flattened array') +for dtype in dtypes: + a = np.array(range(12), dtype=dtype).reshape((3, 4)) + print(np.take(a, (0, 10))) + +print('\n1D arrays') +for dtype in dtypes: + a = np.array(range(12), dtype=dtype) + print('\na:', a) + indices = (0, 2, 2, 1) + print(np.take(a, indices)) + indices = np.array([0, 2, 2, 1], dtype=np.uint8) + print(np.take(a, indices)) + +print('\n2D arrays') +for dtype in dtypes: + a = np.array(range(12), dtype=dtype).reshape((3, 4)) + print('\na:', a) + print('\nfirst axis') + print(np.take(a, (0, 2, 2, 1), axis=0)) + print('\nsecond axis') + print(np.take(a, (0, 2, 2, 1), axis=1)) + diff --git a/tests/2d/numpy/take.py.exp b/tests/2d/numpy/take.py.exp new file mode 100644 index 00000000..081ec1b4 --- /dev/null +++ b/tests/2d/numpy/take.py.exp @@ -0,0 +1,105 @@ +flattened array +array([0, 10], dtype=uint8) +array([0, 10], dtype=int8) +array([0, 10], dtype=uint16) +array([0, 10], dtype=int16) +array([0.0, 10.0], dtype=float64) + +1D arrays + +a: array([0, 1, 2, ..., 9, 10, 11], dtype=uint8) +array([0, 2, 2, 1], dtype=uint8) +array([0, 2, 2, 1], dtype=uint8) + +a: array([0, 1, 2, ..., 9, 10, 11], dtype=int8) +array([0, 2, 2, 1], dtype=int8) +array([0, 2, 2, 1], dtype=int8) + +a: array([0, 1, 2, ..., 9, 10, 11], dtype=uint16) +array([0, 2, 2, 1], dtype=uint16) +array([0, 2, 2, 1], dtype=uint16) + +a: array([0, 1, 2, ..., 9, 10, 11], dtype=int16) +array([0, 2, 2, 1], dtype=int16) +array([0, 2, 2, 1], dtype=int16) + +a: array([0.0, 1.0, 2.0, ..., 9.0, 10.0, 11.0], dtype=float64) +array([0.0, 2.0, 2.0, 1.0], dtype=float64) +array([0.0, 2.0, 2.0, 1.0], dtype=float64) + +2D arrays + +a: array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=uint8) + +first axis +array([[0, 1, 2, 3], + [8, 9, 10, 11], + [8, 9, 10, 11], + [4, 5, 6, 7]], dtype=uint8) + +second axis +array([[0, 2, 2, 1], + [4, 6, 6, 5], + [8, 10, 10, 9]], dtype=uint8) + +a: array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=int8) + +first axis +array([[0, 1, 2, 3], + [8, 9, 10, 11], + [8, 9, 10, 11], + [4, 5, 6, 7]], dtype=int8) + +second axis +array([[0, 2, 2, 1], + [4, 6, 6, 5], + [8, 10, 10, 9]], dtype=int8) + +a: array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=uint16) + +first axis +array([[0, 1, 2, 3], + [8, 9, 10, 11], + [8, 9, 10, 11], + [4, 5, 6, 7]], dtype=uint16) + +second axis +array([[0, 2, 2, 1], + [4, 6, 6, 5], + [8, 10, 10, 9]], dtype=uint16) + +a: array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=int16) + +first axis +array([[0, 1, 2, 3], + [8, 9, 10, 11], + [8, 9, 10, 11], + [4, 5, 6, 7]], dtype=int16) + +second axis +array([[0, 2, 2, 1], + [4, 6, 6, 5], + [8, 10, 10, 9]], dtype=int16) + +a: array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0]], dtype=float64) + +first axis +array([[0.0, 1.0, 2.0, 3.0], + [8.0, 9.0, 10.0, 11.0], + [8.0, 9.0, 10.0, 11.0], + [4.0, 5.0, 6.0, 7.0]], dtype=float64) + +second axis +array([[0.0, 2.0, 2.0, 1.0], + [4.0, 6.0, 6.0, 5.0], + [8.0, 10.0, 10.0, 9.0]], dtype=float64) From 303e8d790acc6e996c6851f00fa98122b3f85000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 24 Nov 2024 19:56:15 +0100 Subject: [PATCH 098/113] fix compilation error for complexes (#694) --- code/ulab.c | 2 +- code/utils/utils.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/code/ulab.c b/code/ulab.c index b9999671..12f37027 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.6.0 +#define ULAB_VERSION 6.6.1 #define xstr(s) str(s) #define str(s) #s diff --git a/code/utils/utils.c b/code/utils/utils.c index 8477f5de..17e6ca0a 100644 --- a/code/utils/utils.c +++ b/code/utils/utils.c @@ -286,7 +286,7 @@ mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw uint8_t *array = (uint8_t *)in->array; - #if ULAB_FFT_IS_NUMPY_COMPATIBLE // ULAB_SUPPORTS_COMPLEX is automatically true + #if ULAB_FFT_IS_NUMPY_COMPATIBLE & ULAB_SUPPORTS_COMPLEX if(in->dtype == NDARRAY_COMPLEX) { uint8_t sz = 2 * sizeof(mp_float_t); for(size_t i = 0; i < len; i++) { diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 11e4a648..c857f96d 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sun, 24 Nov 2024 + +version 6.6.1 + + fix compilation error, for complexes + Wed, 9 Oct 2024 version 6.6.0 From 73ed8cc11f57c5c7605d97eaf5303687a2d21106 Mon Sep 17 00:00:00 2001 From: Harald Milz <149242962+h-milz@users.noreply.github.com> Date: Sun, 15 Dec 2024 18:49:08 +0100 Subject: [PATCH 099/113] Add scipy integration (#699) * adding scipy integrate, initial check-in * compile unix double-precision, select integrations algos * bumping ulab version number to 6.7.0 * adding documentation * documentation fix * documentation fix * documentation fix * rewritten in some places * complex number error handling * added test cases * resolved importing scipy.integrate * resolved importing scipy.integrate #2 * build integrate only when we have MICROPY_FLOAT_IMPL_DOUBLE * reverting commit a4c0c0b * re-pushing failed commit * Revert "re-pushing failed commit" This reverts commit a10e89fe14ce17306bb35e629509c01b1b6f8094. * improve tests using math.isclose() * enabled fp32 builds * removed conditional includes * adapted to new function names, corrected importing * function names similar to in CPython scipy.integrate, some minor corrections * major rewrite representing the name changes, mapping to CPython scipy.integrate, more background info --- code/micropython.mk | 1 + code/scipy/integrate/integrate.c | 701 +++++++++++++++++++++++++++++++ code/scipy/integrate/integrate.h | 34 ++ code/scipy/scipy.c | 6 +- code/ulab.c | 2 +- code/ulab.h | 22 + docs/scipy-integrate.ipynb | 510 ++++++++++++++++++++++ tests/2d/scipy/integrate.py | 28 ++ tests/2d/scipy/integrate.py.exp | 4 + 9 files changed, 1306 insertions(+), 2 deletions(-) create mode 100644 code/scipy/integrate/integrate.c create mode 100644 code/scipy/integrate/integrate.h create mode 100644 docs/scipy-integrate.ipynb create mode 100644 tests/2d/scipy/integrate.py create mode 100644 tests/2d/scipy/integrate.py.exp diff --git a/code/micropython.mk b/code/micropython.mk index 4aa6f615..e835d87b 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -2,6 +2,7 @@ USERMODULES_DIR := $(USERMOD_DIR) # Add all C files to SRC_USERMOD. +SRC_USERMOD += $(USERMODULES_DIR)/scipy/integrate/integrate.c SRC_USERMOD += $(USERMODULES_DIR)/scipy/linalg/linalg.c SRC_USERMOD += $(USERMODULES_DIR)/scipy/optimize/optimize.c SRC_USERMOD += $(USERMODULES_DIR)/scipy/signal/signal.c diff --git a/code/scipy/integrate/integrate.c b/code/scipy/integrate/integrate.c new file mode 100644 index 00000000..80def711 --- /dev/null +++ b/code/scipy/integrate/integrate.c @@ -0,0 +1,701 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Harald Milz + * + * References: + * - Dr. Robert van Engelen, Improving the mp_float_t Exponential Quadrature Tanh-Sinh, Sinh-Sinh and Exp-Sinh Formulas, + * 2021, https://www.genivia.com/qthsh.html + * - Borwein, Bailey & Girgensohn, "Experimentation in Mathematics - Computational Paths to Discovery", A K Peters, + * 2003, pages 312-313 + * - Joren Vanherck, Bart Sorée, Wim Magnus, Tanh-sinh quadrature for single and multiple integration using + * floating-point arithmetic, 2020, https://arxiv.org/abs/2007.15057 + * - Tanh-Sinh quadrature, Wikipedia, https://en.wikipedia.org/wiki/Tanh-sinh_quadrature + * - Romberg's method, Wikipedia, https://en.wikipedia.org/wiki/Romberg%27s_method + * - Adaptive Simpson's method, Wikipedia, https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method + * - Gauss–Kronrod quadrature formula, Wikipedia, https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula + * + * This module provides four integration methods, and thus deviates from scipy.integrate a bit. + * As for the pros and cons of the different methods please consult the literature above. + * The code was ported to Micropython from Dr. Engelen's paper and used with his written kind permission + * - quad - Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature + * - romberg - Romberg quadrature + * - simpson - Adaptive Simpson quadrature + * - quadgk - Adaptive Gauss-Kronrod (G10,K21) quadrature + */ + +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "py/misc.h" +#include "py/objtuple.h" + +#include "../../ndarray.h" +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "integrate.h" + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-14), 0x283424dcUL, 0x3e901b2b29a4692bULL); +#define MACHEPS MICROPY_FLOAT_CONST(1e-17) +#else +ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-8), 0x358637cfUL, 0x3e7010c6f7d42d18ULL); +#define MACHEPS MICROPY_FLOAT_CONST(1e-8) +#endif + +#define ZERO MICROPY_FLOAT_CONST(0.0) +#define POINT_TWO_FIVE MICROPY_FLOAT_CONST(0.25) +#define ONE MICROPY_FLOAT_CONST(1.0) +#define TWO MICROPY_FLOAT_CONST(2.0) +#define FOUR MICROPY_FLOAT_CONST(4.0) +#define SIX MICROPY_FLOAT_CONST(6.0) +#define TEN MICROPY_FLOAT_CONST(10.0) +#define FIFTEEN MICROPY_FLOAT_CONST(15.0) +#define EPS_5 MICROPY_FLOAT_CONST(1e-5) + + +static mp_float_t integrate_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) { + // Helper function for calculating the value of f(x, a, b, c, ...), + // where f is defined in python. Takes a float, returns a float. + // The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams + fargs[0] = mp_obj_new_float(x); + return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs)); +} + +// sign helper function +int sign(mp_float_t x) { + if (x >= ZERO) + return 1; + else + return -1; +} + + +#if ULAB_INTEGRATE_HAS_TANHSINH +// Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature +// https://www.genivia.com/qthsh.html + +// return optimized Exp-Sinh integral split point d +mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t eps, mp_float_t d) { + const mp_obj_type_t *type = mp_obj_get_type(fun); + mp_obj_t fargs[1]; + mp_float_t h2 = integrate_python_call(type, fun, a + d/2, fargs, 0) - integrate_python_call(type, fun, (a + d*2)*4, fargs, 0); + int i = 1, j = 32; // j=32 is optimal to find r + if (isfinite(h2) && MICROPY_FLOAT_C_FUN(fabs)(h2) > EPS_5) { // if |h2| > 2^-16 + mp_float_t r, fl, fr, h, s = 0, lfl, lfr, lr = 2; + do { // find max j such that fl and fr are finite + j /= 2; + r = 1 << (i + j); + fl = integrate_python_call(type, fun, a + d/r, fargs, 0); + fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0); + h = fl - fr; + } while (j > 1 && !isfinite(h)); + if (j > 1 && isfinite(h) && sign(h) != sign(h2)) { + lfl = fl; // last fl=f(a+d/r) + lfr = fr; // last fr=f(a+d*r)*r*r + do { // bisect in 4 iterations + j /= 2; + r = 1 << (i + j); + fl = integrate_python_call(type, fun, a + d/r, fargs, 0); + fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0); + h = fl - fr; + if (isfinite(h)) { + s += MICROPY_FLOAT_C_FUN(fabs)(h); // sum |h| to remove noisy cases + if (sign(h) == sign(h2)) { + i += j; // search right half + } + else { // search left half + lfl = fl; // record last fl=f(a+d/r) + lfr = fr; // record last fl=f(a+d*r)*r*r + lr = r; // record last r + } + } + } while (j > 1); + if (s > eps) { // if sum of |h| > eps + h = lfl - lfr; // use last fl and fr before the sign change + r = lr; // use last r before the sign change + if (h != ZERO) // if last diff != 0, back up r by one step + r /= TWO; + if (MICROPY_FLOAT_C_FUN(fabs)(lfl) < MICROPY_FLOAT_C_FUN(fabs)(lfr)) + d /= r; // move d closer to the finite endpoint + else + d *= r; // move d closer to the infinite endpoint + } + } + } + return d; +} + + +// integrate function f, range a..b, max levels n, error tolerance eps +mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps, mp_float_t *e) { + const mp_obj_type_t *type = mp_obj_get_type(fun); + mp_obj_t fargs[1]; + const mp_float_t tol = TEN*eps; + mp_float_t c = ZERO, d = ONE, s, sign = ONE, v, h = TWO; + int k = 0, mode = 0; // Tanh-Sinh = 0, Exp-Sinh = 1, Sinh-Sinh = 2 + if (b < a) { // swap bounds + v = b; + b = a; + a = v; + sign = -1; + } + if (isfinite(a) && isfinite(b)) { + c = (a+b)/TWO; + d = (b-a)/TWO; + v = c; + } + else if (isfinite(a)) { + mode = 1; // Exp-Sinh + d = exp_sinh_opt_d(fun, a, eps, d); + c = a; + v = a+d; + } + else if (isfinite(b)) { + mode = 1; // Exp-Sinh + // d = -d; + d = exp_sinh_opt_d(fun, b, eps, -d); + sign = -sign; + c = b; + v = b+d; + } + else { + mode = 2; // Sinh-Sinh + v = ZERO; + } + s = integrate_python_call(type, fun, v, fargs, 0); + do { + mp_float_t p = ZERO, q, fp = ZERO, fm = ZERO, t, eh; + h /= TWO; + t = eh = MICROPY_FLOAT_C_FUN(exp)(h); + if (k > ZERO) + eh *= eh; + if (mode == 0) { // Tanh-Sinh + do { + mp_float_t u = MICROPY_FLOAT_C_FUN(exp)(ONE / t - t); // = exp(-2*sinh(j*h)) = 1/exp(sinh(j*h))^2 + mp_float_t r = TWO * u / (ONE + u); // = 1 - tanh(sinh(j*h)) + mp_float_t w = (t + ONE / t) * r / (ONE + u); // = cosh(j*h)/cosh(sinh(j*h))^2 + mp_float_t x = d*r; + if (a+x > a) { // if too close to a then reuse previous fp + mp_float_t y = integrate_python_call(type, fun, a+x, fargs, 0); + if (isfinite(y)) + fp = y; // if f(x) is finite, add to local sum + } + if (b-x < b) { // if too close to a then reuse previous fp + mp_float_t y = integrate_python_call(type, fun, b-x, fargs, 0); + if (isfinite(y)) + fm = y; // if f(x) is finite, add to local sum + } + q = w*(fp+fm); + p += q; + t *= eh; + } while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p)); + } + else { + t /= TWO; + do { + mp_float_t r = MICROPY_FLOAT_C_FUN(exp)(t - POINT_TWO_FIVE / t); // = exp(sinh(j*h)) + mp_float_t x, y, w = r; + q = ZERO; + if (mode == 1) { // Exp-Sinh + x = c + d/r; + if (x == c) // if x hit the finite endpoint then break + break; + y = integrate_python_call(type, fun, x, fargs, 0); + if (isfinite(y)) // if f(x) is finite, add to local sum + q += y/w; + } + else { // Sinh-Sinh + r = (r - ONE / r) / TWO; // = sinh(sinh(j*h)) + w = (w + ONE / w) / TWO; // = cosh(sinh(j*h)) + x = c - d*r; + y = integrate_python_call(type, fun, x, fargs, 0); + if (isfinite(y)) // if f(x) is finite, add to local sum + q += y*w; + } + x = c + d*r; + y = integrate_python_call(type, fun, x, fargs, 0); + if (isfinite(y)) // if f(x) is finite, add to local sum + q += y*w; + q *= t + POINT_TWO_FIVE / t; // q *= cosh(j*h) + p += q; + t *= eh; + } while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p)); + } + v = s-p; + s += p; + ++k; + } while (MICROPY_FLOAT_C_FUN(fabs)(v) > tol*MICROPY_FLOAT_C_FUN(fabs)(s) && k <= n); + // return the error estimate by reference + *e = MICROPY_FLOAT_C_FUN(fabs)(v)/(MICROPY_FLOAT_C_FUN(fabs)(s)+eps); + return sign*d*s*h; // result with estimated relative error e +} + +//| def tanhsinh( +//| fun: Callable[[float], float], +//| a: float, +//| b: float, +//| *, +//| levels: int = 6 +//| eps: float = etolerance +//| ) -> float: +//| """ +//| :param callable f: The function to integrate +//| :param float a: The lower integration limit +//| :param float b: The upper integration limit +//| :param float levels: The number of levels to perform (6..7 is optimal) +//| :param float eps: The error tolerance value +//| +//| Find a quadrature of the function ``f(x)`` on the interval +//| (``a``..``b``) using an optimized double exponential. The result is accurate to within +//| ``eps`` unless more than ``levels`` levels are required.""" +//| + + +static mp_obj_t integrate_tanhsinh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_levels, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 6} }, + { MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable")); + } + + // iterate over args 1, 2, and 4 + // arg 3 will be handled by MP_ARG_INT above. + for (int i=1; i<=4; i*=2) { + type = mp_obj_get_type(args[i].u_obj); + if (type != &mp_type_float && type != &mp_type_int) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj)); + } + } + mp_float_t a = mp_obj_get_float(args[1].u_obj); + mp_float_t b = mp_obj_get_float(args[2].u_obj); + uint16_t n = (uint16_t)args[3].u_int; + if (n < 1) { + mp_raise_ValueError(MP_ERROR_TEXT("levels needs to be a positive integer")); + } + mp_float_t eps = mp_obj_get_float(args[4].u_obj); + + mp_obj_t res[2]; + mp_float_t e; + res[0] = mp_obj_new_float(tanhsinh(fun, a, b, n, eps, &e)); + res[1] = mp_obj_new_float(e); + return mp_obj_new_tuple(2, res); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(integrate_tanhsinh_obj, 2, integrate_tanhsinh); +#endif /* ULAB_INTEGRATE_HAS_TANHSINH */ + +#if ULAB_INTEGRATE_HAS_ROMBERG +// Romberg quadrature +// This function is deprecated as of SciPy 1.12.0 and will be removed in SciPy 1.15.0. Please use scipy.integrate.quad instead. +// https://en.wikipedia.org/wiki/Romberg%27s_method, https://www.genivia.com/qthsh.html, +// https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html (which is different +// insofar as the latter expects an array of function values). + +mp_float_t qromb(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps) { + const mp_obj_type_t *type = mp_obj_get_type(fun); + mp_obj_t fargs[1]; + mp_float_t R1[n], R2[n]; + mp_float_t *Ro = &R1[0], *Ru = &R2[0]; + mp_float_t h = b-a; + uint16_t i, j; + Ro[0] = (integrate_python_call(type, fun, a, fargs, 0) + integrate_python_call(type, fun, b, fargs, 0)) * h/2; + for (i = 1; i < n; ++i) { + unsigned long long k = 1UL << i; + unsigned long long s = 1; + mp_float_t sum = ZERO; + mp_float_t *Rt; + h /= TWO; + for (j = 1; j < k; j += 2) + sum += integrate_python_call(type, fun, a+j*h, fargs, 0); + Ru[0] = h*sum + Ro[0] / TWO; + for (j = 1; j <= i; ++j) { + s <<= 2; + Ru[j] = (s*Ru[j-1] - Ro[j-1])/(s-1); + } + if (i > 2 && MICROPY_FLOAT_C_FUN(fabs)(Ro[i-1]-Ru[i]) <= eps*MICROPY_FLOAT_C_FUN(fabs)(Ru[i])+eps) + return Ru[i]; + Rt = Ro; + Ro = Ru; + Ru = Rt; + } + return Ro[n-1]; +} + +//| def romberg( +//| fun: Callable[[float], float], +//| a: float, +//| b: float, +//| *, +//| steps: int = 100 +//| eps: float = etolerance +//| ) -> float: +//| """ +//| :param callable f: The function to integrate +//| :param float a: The lower integration limit +//| :param float b: The upper integration limit +//| :param float steps: The number of equidistant steps +//| :param float eps: The tolerance value +//| +//| Find a quadrature of the function ``f(x)`` on the interval +//| (``a``..``b``) using the Romberg method. The result is accurate to within +//| ``eps`` unless more than ``steps`` steps are required.""" +//| ... +//| + +static mp_obj_t integrate_romberg(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, + { MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable")); + } + + // iterate over args 1, 2, and 4 + // arg 3 will be handled by MP_ARG_INT above. + for (int i=1; i<=4; i*=2) { + type = mp_obj_get_type(args[i].u_obj); + if (type != &mp_type_float && type != &mp_type_int) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj)); + } + } + mp_float_t a = mp_obj_get_float(args[1].u_obj); + mp_float_t b = mp_obj_get_float(args[2].u_obj); + uint16_t steps = (uint16_t)args[3].u_int; + if (steps < 1) { + mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer")); + } + mp_float_t eps = mp_obj_get_float(args[4].u_obj); + + return mp_obj_new_float(qromb(fun, a, b, steps, eps)); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(integrate_romberg_obj, 2, integrate_romberg); +#endif /* ULAB_INTEGRATE_HAS_ROMBERG */ + +#if ULAB_INTEGRATE_HAS_SIMPSON +// Adaptive Simpson quadrature +// https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method, https://www.genivia.com/qthsh.html + +mp_float_t as(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, mp_float_t fa, mp_float_t fm, + mp_float_t fb, mp_float_t v, mp_float_t eps, int n, mp_float_t t) { + const mp_obj_type_t *type = mp_obj_get_type(fun); + mp_obj_t fargs[1]; + mp_float_t h = (b-a) / TWO; + mp_float_t f1 = integrate_python_call(type, fun, a + h / TWO, fargs, 0); + mp_float_t f2 = integrate_python_call(type, fun, b - h / TWO, fargs, 0); + mp_float_t sl = h*(fa + FOUR * f1 + fm) / SIX; + mp_float_t sr = h*(fm + FOUR * f2 + fb) / SIX; + mp_float_t s = sl+sr; + mp_float_t d = (s-v) / FIFTEEN; + mp_float_t m = a+h; + if (n <= 0 || MICROPY_FLOAT_C_FUN(fabs)(d) < eps) + return t + s + d; // note: fabs(d) can be used as error estimate + eps /= TWO; + --n; + t = as(fun, a, m, fa, f1, fm, sl, eps, n, t); + return as(fun, m, b, fm, f2, fb, sr, eps, n, t); +} + +mp_float_t qasi(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t eps) { + const mp_obj_type_t *type = mp_obj_get_type(fun); + mp_obj_t fargs[1]; + mp_float_t fa = integrate_python_call(type, fun, a, fargs, 0); + mp_float_t fm = integrate_python_call(type, fun, (a+b)/2, fargs, 0); + mp_float_t fb = integrate_python_call(type, fun, b, fargs, 0); + mp_float_t v = (fa + FOUR * fm + fb) * (b-a) / SIX; + return as(fun, a, b, fa, fm, fb, v, eps, n, 0); +} + +//| def simpson( +//| fun: Callable[[float], float], +//| a: float, +//| b: float, +//| *, +//| steps: int = 100 +//| eps: float = etolerance +//| ) -> float: +//| """ +//| :param callable f: The function to integrate +//| :param float a: The lower integration limit +//| :param float b: The upper integration limit +//| :param float steps: The number of equidistant steps +//| :param float eps: The tolerance value +//| +//| Find a quadrature of the function ``f(x)`` on the interval +//| (``a``..``b``) using the Adaptive Simpson's method. The result is accurate to within +//| ``eps`` unless more than ``steps`` steps are required.""" +//| ... +//| + +static mp_obj_t integrate_simpson(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, + { MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function")); + } + + // iterate over args 1, 2, and 4 + // arg 3 will be handled by MP_ARG_INT above. + for (int i=1; i<=4; i*=2) { + type = mp_obj_get_type(args[i].u_obj); + if (type != &mp_type_float && type != &mp_type_int) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj)); + } + } + mp_float_t a = mp_obj_get_float(args[1].u_obj); + mp_float_t b = mp_obj_get_float(args[2].u_obj); + uint16_t steps = (uint16_t)args[3].u_int; + if (steps < 1) { + mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer")); + } + mp_float_t eps = mp_obj_get_float(args[4].u_obj); + + return mp_obj_new_float(qasi(fun, a, b, steps, eps)); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(integrate_simpson_obj, 2, integrate_simpson); +#endif /* ULAB_INTEGRATE_HAS_SIMPSON */ + +#if ULAB_INTEGRATE_HAS_QUAD +// Adaptive Gauss-Kronrod (G10,K21) quadrature +// https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula, https://www.genivia.com/qthsh.html + +mp_float_t gk(mp_float_t (*fun)(mp_float_t), mp_float_t c, mp_float_t d, mp_float_t *err) { +// abscissas and weights pre-calculated with Legendre Stieltjes polynomials + static const mp_float_t abscissas[21] = { + MICROPY_FLOAT_CONST(0.00000000000000000e+00), + MICROPY_FLOAT_CONST(7.65265211334973338e-02), + MICROPY_FLOAT_CONST(1.52605465240922676e-01), + MICROPY_FLOAT_CONST(2.27785851141645078e-01), + MICROPY_FLOAT_CONST(3.01627868114913004e-01), + MICROPY_FLOAT_CONST(3.73706088715419561e-01), + MICROPY_FLOAT_CONST(4.43593175238725103e-01), + MICROPY_FLOAT_CONST(5.10867001950827098e-01), + MICROPY_FLOAT_CONST(5.75140446819710315e-01), + MICROPY_FLOAT_CONST(6.36053680726515025e-01), + MICROPY_FLOAT_CONST(6.93237656334751385e-01), + MICROPY_FLOAT_CONST(7.46331906460150793e-01), + MICROPY_FLOAT_CONST(7.95041428837551198e-01), + MICROPY_FLOAT_CONST(8.39116971822218823e-01), + MICROPY_FLOAT_CONST(8.78276811252281976e-01), + MICROPY_FLOAT_CONST(9.12234428251325906e-01), + MICROPY_FLOAT_CONST(9.40822633831754754e-01), + MICROPY_FLOAT_CONST(9.63971927277913791e-01), + MICROPY_FLOAT_CONST(9.81507877450250259e-01), + MICROPY_FLOAT_CONST(9.93128599185094925e-01), + MICROPY_FLOAT_CONST(9.98859031588277664e-01), + }; + static const mp_float_t weights[21] = { + MICROPY_FLOAT_CONST(7.66007119179996564e-02), + MICROPY_FLOAT_CONST(7.63778676720807367e-02), + MICROPY_FLOAT_CONST(7.57044976845566747e-02), + MICROPY_FLOAT_CONST(7.45828754004991890e-02), + MICROPY_FLOAT_CONST(7.30306903327866675e-02), + MICROPY_FLOAT_CONST(7.10544235534440683e-02), + MICROPY_FLOAT_CONST(6.86486729285216193e-02), + MICROPY_FLOAT_CONST(6.58345971336184221e-02), + MICROPY_FLOAT_CONST(6.26532375547811680e-02), + MICROPY_FLOAT_CONST(5.91114008806395724e-02), + MICROPY_FLOAT_CONST(5.51951053482859947e-02), + MICROPY_FLOAT_CONST(5.09445739237286919e-02), + MICROPY_FLOAT_CONST(4.64348218674976747e-02), + MICROPY_FLOAT_CONST(4.16688733279736863e-02), + MICROPY_FLOAT_CONST(3.66001697582007980e-02), + MICROPY_FLOAT_CONST(3.12873067770327990e-02), + MICROPY_FLOAT_CONST(2.58821336049511588e-02), + MICROPY_FLOAT_CONST(2.03883734612665236e-02), + MICROPY_FLOAT_CONST(1.46261692569712530e-02), + MICROPY_FLOAT_CONST(8.60026985564294220e-03), + MICROPY_FLOAT_CONST(3.07358371852053150e-03), + }; + static const mp_float_t gauss_weights[10] = { + MICROPY_FLOAT_CONST(1.52753387130725851e-01), + MICROPY_FLOAT_CONST(1.49172986472603747e-01), + MICROPY_FLOAT_CONST(1.42096109318382051e-01), + MICROPY_FLOAT_CONST(1.31688638449176627e-01), + MICROPY_FLOAT_CONST(1.18194531961518417e-01), + MICROPY_FLOAT_CONST(1.01930119817240435e-01), + MICROPY_FLOAT_CONST(8.32767415767047487e-02), + MICROPY_FLOAT_CONST(6.26720483341090636e-02), + MICROPY_FLOAT_CONST(4.06014298003869413e-02), + MICROPY_FLOAT_CONST(1.76140071391521183e-02), + }; + const mp_obj_type_t *type = mp_obj_get_type(fun); + mp_obj_t fargs[1]; + mp_float_t p = ZERO; // kronrod quadrature sum + mp_float_t q = ZERO; // gauss quadrature sum + mp_float_t fp, fm; + mp_float_t e; + int i; + fp = integrate_python_call(type, fun, c, fargs, 0); + p = fp * weights[0]; + for (i = 1; i < 21; i += 2) { + fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0); + fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0); + p += (fp + fm) * weights[i]; + q += (fp + fm) * gauss_weights[i/2]; + } + for (i = 2; i < 21; i += 2) { + fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0); + fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0); + p += (fp + fm) * weights[i]; + } + *err = MICROPY_FLOAT_C_FUN(fabs)(p - q); + e = MICROPY_FLOAT_C_FUN(fabs)(2 * p * MACHEPS); // optional, to take 1e-17 MachEps prec. into account + if (*err < e) + *err = e; + return p; +} + +mp_float_t qakro(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t tol, mp_float_t eps, mp_float_t *err) { + mp_float_t c = (a+b) / TWO; + mp_float_t d = (b-a) / TWO; + mp_float_t e; + mp_float_t r = gk(fun, c, d, &e); + mp_float_t s = d*r; + mp_float_t t = MICROPY_FLOAT_C_FUN(fabs)(s*tol); + if (tol == ZERO) + tol = t; + if (n > 0 && t < e && tol < e) { + s = qakro(fun, a, c, n-1, t / TWO, eps, err); + s += qakro(fun, c, b, n-1, t / TWO, eps, &e); + *err += e; + return s; + } + *err = e; + return s; +} + + +//| def quad( +//| fun: Callable[[float], float], +//| a: float, +//| b: float, +//| *, +//| order: int = 5 +//| eps: float = etolerance +//| ) -> float: +//| """ +//| :param callable f: The function to integrate +//| :param float a: The lower integration limit +//| :param float b: The upper integration limit +//| :param float order: Order of quadrature integration. Default is 5. +//| :param float eps: The tolerance value +//| +//| Find a quadrature of the function ``f(x)`` on the interval +//| (``a``..``b``) using the Adaptive Gauss-Kronrod method. The result is accurate to within +//| ``eps`` unless a higher order than ``order`` is required.""" +//| ... +//| + +static mp_obj_t integrate_quad(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_order, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5} }, + { MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) { + mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable")); + } + + // iterate over args 1, 2, and 4 + // arg 3 will be handled by MP_ARG_INT above. + for (int i=1; i<=4; i*=2) { + type = mp_obj_get_type(args[i].u_obj); + if (type != &mp_type_float && type != &mp_type_int) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj)); + } + } + mp_float_t a = mp_obj_get_float(args[1].u_obj); + mp_float_t b = mp_obj_get_float(args[2].u_obj); + uint16_t order = (uint16_t)args[3].u_int; + if (order < 1) { + mp_raise_ValueError(MP_ERROR_TEXT("order needs to be a positive integer")); + } + mp_float_t eps = mp_obj_get_float(args[4].u_obj); + + mp_obj_t res[2]; + mp_float_t e; + res[0] = mp_obj_new_float(qakro(fun, a, b, order, 0, eps, &e)); + res[1] = mp_obj_new_float(e); + return mp_obj_new_tuple(2, res); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(integrate_quad_obj, 2, integrate_quad); +#endif /* ULAB_INTEGRATE_HAS_QUAD */ + +static const mp_rom_map_elem_t ulab_scipy_integrate_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_integrate) }, +#if ULAB_INTEGRATE_HAS_TANHSINH + { MP_ROM_QSTR(MP_QSTR_tanhsinh), MP_ROM_PTR(&integrate_tanhsinh_obj) }, +#endif +#if ULAB_INTEGRATE_HAS_ROMBERG + { MP_ROM_QSTR(MP_QSTR_romberg), MP_ROM_PTR(&integrate_romberg_obj) }, +#endif +#if ULAB_INTEGRATE_HAS_SIMPSON + { MP_ROM_QSTR(MP_QSTR_simpson), MP_ROM_PTR(&integrate_simpson_obj) }, +#endif +#if ULAB_INTEGRATE_HAS_QUAD + { MP_ROM_QSTR(MP_QSTR_quad), MP_ROM_PTR(&integrate_quad_obj) }, +#endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_integrate_globals, ulab_scipy_integrate_globals_table); + +const mp_obj_module_t ulab_scipy_integrate_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_integrate_globals, +}; +#if CIRCUITPY_ULAB +MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_integrate, ulab_scipy_integrate_module); +#endif + diff --git a/code/scipy/integrate/integrate.h b/code/scipy/integrate/integrate.h new file mode 100644 index 00000000..ebfac2ea --- /dev/null +++ b/code/scipy/integrate/integrate.h @@ -0,0 +1,34 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Harald Milz + * +*/ + +#ifndef _SCIPY_INTEGRATE_ +#define _SCIPY_INTEGRATE_ + +#include "../../ulab_tools.h" + +extern const mp_obj_module_t ulab_scipy_integrate_module; + +#if ULAB_INTEGRATE_HAS_TANHSINH +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_tanhsinh_obj); +#endif +#if ULAB_INTEGRATE_HAS_ROMBERG +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_romberg_obj); +#endif +#if ULAB_INTEGRATE_HAS_SIMPSON +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_simpson_obj); +#endif +#if ULAB_INTEGRATE_HAS_QUAD +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_quad_obj); +#endif + +#endif /* _SCIPY_INTEGRATE_ */ + diff --git a/code/scipy/scipy.c b/code/scipy/scipy.c index 6895127b..e4ad306c 100644 --- a/code/scipy/scipy.c +++ b/code/scipy/scipy.c @@ -1,4 +1,3 @@ - /* * This file is part of the micropython-ulab project, * @@ -20,6 +19,8 @@ #include "signal/signal.h" #include "special/special.h" #include "linalg/linalg.h" +#include "integrate/integrate.h" + #if ULAB_HAS_SCIPY @@ -28,6 +29,9 @@ static const mp_rom_map_elem_t ulab_scipy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_scipy) }, + #if ULAB_SCIPY_HAS_INTEGRATE_MODULE + { MP_ROM_QSTR(MP_QSTR_integrate), MP_ROM_PTR(&ulab_scipy_integrate_module) }, + #endif #if ULAB_SCIPY_HAS_LINALG_MODULE { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) }, #endif diff --git a/code/ulab.c b/code/ulab.c index 12f37027..be148c45 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.6.1 +#define ULAB_VERSION 6.7.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 78b8a1ba..3eb30131 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -398,6 +398,28 @@ #define ULAB_NUMPY_HAS_WHERE (1) #endif +// the integrate module; functions of the integrate module still have +// to be defined separately +#ifndef ULAB_SCIPY_HAS_INTEGRATE_MODULE +#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1) +#endif + +#ifndef ULAB_INTEGRATE_HAS_TANHSINH +#define ULAB_INTEGRATE_HAS_TANHSINH (1) +#endif + +#ifndef ULAB_INTEGRATE_HAS_ROMBERG +#define ULAB_INTEGRATE_HAS_ROMBERG (1) +#endif + +#ifndef ULAB_INTEGRATE_HAS_SIMPSON +#define ULAB_INTEGRATE_HAS_SIMPSON (1) +#endif + +#ifndef ULAB_INTEGRATE_HAS_QUAD +#define ULAB_INTEGRATE_HAS_QUAD (1) +#endif + // the linalg module; functions of the linalg module still have // to be defined separately #ifndef ULAB_NUMPY_HAS_LINALG_MODULE diff --git a/docs/scipy-integrate.ipynb b/docs/scipy-integrate.ipynb new file mode 100644 index 00000000..23220231 --- /dev/null +++ b/docs/scipy-integrate.ipynb @@ -0,0 +1,510 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2021-01-12T16:11:12.111639Z", + "start_time": "2021-01-12T16:11:11.914041Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + } + ], + "source": [ + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Notebook magic" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-29T20:50:20.813162Z", + "start_time": "2022-01-29T20:50:20.794562Z" + } + }, + "outputs": [], + "source": [ + "from IPython.core.magic import Magics, magics_class, line_cell_magic\n", + "from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n", + "from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n", + "import subprocess\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-29T20:50:21.613220Z", + "start_time": "2022-01-29T20:50:21.557819Z" + } + }, + "outputs": [], + "source": [ + "@magics_class\n", + "class PyboardMagic(Magics):\n", + " @cell_magic\n", + " @magic_arguments()\n", + " @argument('-skip')\n", + " @argument('-unix')\n", + " @argument('-pyboard')\n", + " @argument('-file')\n", + " @argument('-data')\n", + " @argument('-time')\n", + " @argument('-memory')\n", + " def micropython(self, line='', cell=None):\n", + " args = parse_argstring(self.micropython, line)\n", + " if args.skip: # doesn't care about the cell's content\n", + " print('skipped execution')\n", + " return None # do not parse the rest\n", + " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", + " with open('/dev/shm/micropython.py', 'w') as fout:\n", + " fout.write(cell)\n", + " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", + " print(proc.stdout.read().decode(\"utf-8\"))\n", + " print(proc.stderr.read().decode(\"utf-8\"))\n", + " return None\n", + " if args.file: # can be used to copy the cell content onto the pyboard's flash\n", + " spaces = \" \"\n", + " try:\n", + " with open(args.file, 'w') as fout:\n", + " fout.write(cell.replace('\\t', spaces))\n", + " printf('written cell to {}'.format(args.file))\n", + " except:\n", + " print('Failed to write to disc!')\n", + " return None # do not parse the rest\n", + " if args.data: # can be used to load data from the pyboard directly into kernel space\n", + " message = pyb.exec(cell)\n", + " if len(message) == 0:\n", + " print('pyboard >>>')\n", + " else:\n", + " print(message.decode('utf-8'))\n", + " # register new variable in user namespace\n", + " self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n", + " \n", + " if args.time: # measures the time of executions\n", + " pyb.exec('import utime')\n", + " message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n", + " \"\\nprint('execution time: {:d} us'.format(delta))\")\n", + " print(message.decode('utf-8'))\n", + " \n", + " if args.memory: # prints out memory information \n", + " message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n", + " print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n", + " message = pyb.exec(cell)\n", + " print(\">>> \", message.decode('utf-8'))\n", + " message = pyb.exec('print(mem_info())')\n", + " print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n", + "\n", + " if args.pyboard:\n", + " message = pyb.exec(cell)\n", + " print(message.decode('utf-8'))\n", + "\n", + "ip = get_ipython()\n", + "ip.register_magics(PyboardMagic)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## pyboard" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:35.126401Z", + "start_time": "2020-05-07T07:35:35.105824Z" + } + }, + "outputs": [], + "source": [ + "import pyboard\n", + "pyb = pyboard.Pyboard('/dev/ttyACM0')\n", + "pyb.enter_raw_repl()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-19T19:11:18.145548Z", + "start_time": "2020-05-19T19:11:18.137468Z" + } + }, + "outputs": [], + "source": [ + "pyb.exit_raw_repl()\n", + "pyb.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:38.725924Z", + "start_time": "2020-05-07T07:35:38.645488Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "%%micropython -pyboard 1\n", + "\n", + "import utime\n", + "import ulab as np\n", + "\n", + "def timeit(n=1000):\n", + " def wrapper(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " run_times = np.zeros(n, dtype=np.uint16)\n", + " for i in range(n):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n", + " print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n", + " print('\\tbest: %d us'%np.min(run_times))\n", + " print('\\tworst: %d us'%np.max(run_times))\n", + " print('\\taverage: %d us'%np.mean(run_times))\n", + " print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n", + " return result\n", + " return new_func\n", + " return wrapper\n", + "\n", + "def timeit(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n", + " return result\n", + " return new_func" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__END_OF_DEFS__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# scipy.integrate\n", + "\n", + "This module provides a simplified subset of CPython's `scipy.integrate` module. The algorithms were not ported from CPython's `scipy.integrate` for the sake of resource usage, but derived from a paper found in https://www.genivia.com/qthsh.html. There are four numerical integration algorithms:\n", + "\n", + "1. [scipy.integrate.quad](#quad)\n", + "2. [scipy.integrate.romberg](#romberg)\n", + "3. [scipy.integrate.simpson](#simpson)\n", + "4. [scipy.integrate.tanhsinh](#tanhsinh)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "Numerical integration works best with float64 math enabled. If you require float64 math, be sure to set `MICROPY_OBJ_REPR_A` and `MICROPY_FLOAT_IMPL_DOUBLE`. This being said, the modules work equally well using float32, albeit with reduced precision. The required error tolerance can be specified for each of the function calls using the \"eps=\" option, defaulting to the compiled in `etolerance` value (1e-14 for fp64, 1e-8 for fp32).\n", + "\n", + "The submodule can be enabled by setting `ULAB_SCIPY_HAS_INTEGRATE_MODULE` in `code/ulab.h`. As for the individual integration algorithms, you can select which to include by setting one or more of `ULAB_INTEGRATE_HAS_QUAD`, `ULAB_INTEGRATE_HAS_ROMBERG`, `ULAB_INTEGRATE_HAS_SIMPSON`, and `ULAB_INTEGRATE_HAS_TANHSINH`.\n", + "\n", + "Also note that these algorithms do not support complex numbers, although it is certainly possible to implement complex integration in MicroPython on top of this module, e.g. as in https://stackoverflow.com/questions/5965583/use-scipy-integrate-quad-to-integrate-complex-numbers. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## quad\n", + "\n", + "`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html \n", + "\n", + "In CPython `scipy.integrate`, `quad` is a wrapper implementing many algorithms based on the Fortran QUADPACK package. Gauss-Kronrod is just one of them, and it is useful for most general-purpose tasks. This particular function implements an Adaptive Gauss-Kronrod (G10,K21) quadrature algorithm. The Gauss–Kronrod quadrature formula is a variant of Gaussian quadrature, in which the evaluation points are chosen so that an accurate approximation can be computed by re-using the information produced by the computation of a less accurate approximation (https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula). \n", + "\n", + "The function takes three to five arguments: \n", + "\n", + "* f, a callable,\n", + "* a and b, the lower and upper integration limit, \n", + "* order=, the order of integration (default 5),\n", + "* eps=, the error tolerance (default etolerance) \n", + "\n", + "The function returns the result and the error estimate as a tuple of floats. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2020-06-19T20:24:10.529668Z", + "start_time": "2020-06-19T20:24:10.520389Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UsageError: Cell magic `%%micropython` not found.\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import scipy\n", + "\n", + "f = lambda x: x**2 + 2*x + 1\n", + "result = scipy.integrate.quad(f, 0, 5, order=5, eps=1e-10)\n", + "print (f\"result = {result}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## romberg\n", + "\n", + "`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html \n", + "\n", + "This function implements the Romberg quadrature algorithm. Romberg's method is a Newton–Cotes formula – it evaluates the integrand at equally spaced points. The integrand must have continuous derivatives, though fairly good results may be obtained if only a few derivatives exist. If it is possible to evaluate the integrand at unequally spaced points, then other methods such as Gaussian quadrature and Clenshaw–Curtis quadrature are generally more accurate (https://en.wikipedia.org/wiki/Romberg%27s_method). \n", + "\n", + "Please note: This function is deprecated as of SciPy 1.12.0 and will be removed in SciPy 1.15.0. Please use `scipy.integrate.quad` instead. \n", + "\n", + "The function takes three to five arguments: \n", + "\n", + "* f, a callable,\n", + "* a and b, the lower and upper integration limit, \n", + "* steps=, the number of steps taken to calculate (default 100),\n", + "* eps=, the error tolerance (default etolerance) \n", + "\n", + "The function returns the result as a float.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UsageError: Cell magic `%%micropython` not found.\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import scipy\n", + "\n", + "f = lambda x: x**2 + 2*x + 1\n", + "result = scipy.integrate.romberg(f, 0, 5)\n", + "print (f\"result = {result}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## simpson\n", + "\n", + "`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.simpson.html \n", + "\n", + "This function is different from CPython's `simpson` method in that it does not take an array of function values but determines the optimal spacing of samples itself. Adaptive Simpson's method, also called adaptive Simpson's rule, is a method of numerical integration proposed by G.F. Kuncir in 1962. It is probably the first recursive adaptive algorithm for numerical integration to appear in print, although more modern adaptive methods based on Gauss–Kronrod quadrature and Clenshaw–Curtis quadrature are now generally preferred (https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method). \n", + "\n", + "The function takes three to five arguments: \n", + "\n", + "* f, a callable,\n", + "* a and b, the lower and upper integration limit, \n", + "* steps=, the number of steps taken to calculate (default 100),\n", + "* eps=, the error tolerance (default etolerance) \n", + "\n", + "The function returns the result as a float." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UsageError: Cell magic `%%micropython` not found.\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import scipy\n", + "\n", + "f = lambda x: x**2 + 2*x + 1\n", + "result = scipy.integrate.simpson(f, 0, 5)\n", + "print (f\"result = {result}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## tanhsinh\n", + "\n", + "`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html \n", + "\n", + "In CPython `scipy.integrate`, `tanhsinh` is written in Python (https://github.com/scipy/scipy/blob/main/scipy/integrate/_tanhsinh.py). It is used in cases where Newton-Cotes, Gauss-Kronrod, and other formulae do not work due to properties of the integrand or the integration limits. (In SciPy v1.14.1, it is not a public function but it has been marked as public in SciPy v1.15.0rc1). \n", + "\n", + "This particular function implements an optimized Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature algorithm. It is especially applied where singularities or infinite derivatives exist at one or both endpoints. The method uses hyperbolic functions in a change of variables to transform an integral on the interval x ∈ (−1, 1) to an integral on the entire real line t ∈ (−∞, ∞), the two integrals having the same value. After this transformation, the integrand decays with a double exponential rate, and thus, this method is also known as the double exponential (DE) formula (https://en.wikipedia.org/wiki/Tanh-sinh_quadrature). \n", + "\n", + "As opposed to the three algorithms mentioned before, it also supports integrals with infinite limits like the Gaussian integral (https://en.wikipedia.org/wiki/Gaussian_integral), as shown below. \n", + "\n", + "The function takes three to five arguments: \n", + "\n", + "* f, a callable,\n", + "* a and b, the lower and upper integration limit, \n", + "* levels=, the number of loops taken to calculate (default 6),\n", + "* eps=, the error tolerance (default: etolerance)\n", + "\n", + "The function returns the result and the error estimate as a tuple of floats.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "UsageError: Cell magic `%%micropython` not found.\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import scipy, numpy as np\n", + "from math import *\n", + "f = lambda x: exp(- x**2)\n", + "result = scipy.integrate.tanhsinh(f, -np.inf, np.inf)\n", + "print (f\"result = {result}\")\n", + "exact = sqrt(pi) # which is the exact value\n", + "print (f\"exact value = {exact}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "382.797px" + }, + "toc_section_display": true, + "toc_window_display": true + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tests/2d/scipy/integrate.py b/tests/2d/scipy/integrate.py new file mode 100644 index 00000000..1d0edb7b --- /dev/null +++ b/tests/2d/scipy/integrate.py @@ -0,0 +1,28 @@ +import sys +from math import * + +try: + from ulab import scipy +except ImportError: + import scipy + +f = lambda x: x * sin(x) * exp(x) +a=1 +b=2 + +(res, err) = scipy.integrate.tanhsinh(f, a, b) +if isclose (res, 7.11263821415851) and isclose (err, 5.438231077315757e-14): + print (res, err) + +res = scipy.integrate.romberg(f, a, b) +if isclose (res, 7.112638214158507): + print (res) + +res = scipy.integrate.simpson(f, a, b) +if isclose (res, 7.112638214158494): + print (res) + +(res, err) = scipy.integrate.quad(f, a, b) +if isclose (res, 7.112638214158507) and isclose (err, 7.686723611780195e-14): + print (res, err) + diff --git a/tests/2d/scipy/integrate.py.exp b/tests/2d/scipy/integrate.py.exp new file mode 100644 index 00000000..10426e6c --- /dev/null +++ b/tests/2d/scipy/integrate.py.exp @@ -0,0 +1,4 @@ +7.11263821415851 5.438231077315757e-14 +7.112638214158507 +7.112638214158494 +7.112638214158507 7.686723611780195e-14 From 903506ca9ae5acc0a2323438953041d61b1512cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 15 Jan 2025 10:29:27 +0100 Subject: [PATCH 100/113] Add keepdims keyword argument (#701) * add function to deal with keepdims=True * preliminary keepdims fix * fux keepdims code * remove out-commented code --- code/numpy/numerical.c | 33 +++++++++------- code/ulab.c | 2 +- code/ulab_tools.c | 76 +++++++++++++++++++++++++------------ code/ulab_tools.h | 2 + docs/ulab-change-log.md | 12 ++++++ tests/2d/numpy/sum.py | 23 +++++++++++ tests/2d/numpy/sum.py.exp | 80 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 38 deletions(-) create mode 100644 tests/2d/numpy/sum.py create mode 100644 tests/2d/numpy/sum.py.exp diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 0961e3c0..79808f39 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -274,7 +274,7 @@ static mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, si } } -static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype, size_t ddof) { +static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, mp_obj_t keepdims, uint8_t optype, size_t ddof) { COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); @@ -372,7 +372,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t mp_float_t norm = (mp_float_t)_shape_strides.shape[0]; // re-wind the array here farray = (mp_float_t *)results->array; - for(size_t i=0; i < results->len; i++) { + for(size_t i = 0; i < results->len; i++) { *farray++ *= norm; } } @@ -380,7 +380,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t bool isStd = optype == NUMERICAL_STD ? 1 : 0; results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT); farray = (mp_float_t *)results->array; - // we can return the 0 array here, if the degrees of freedom is larger than the length of the axis + // we can return the 0 array here, if the degrees of freedom are larger than the length of the axis if((optype == NUMERICAL_STD) && (_shape_strides.shape[0] <= ddof)) { return MP_OBJ_FROM_PTR(results); } @@ -397,11 +397,9 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, div, isStd); } } - if(results->ndim == 0) { // return a scalar here - return mp_binary_get_val_array(results->dtype, results->array, 0); - } - return MP_OBJ_FROM_PTR(results); + return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides); } + // we should never get to this point return mp_const_none; } #endif @@ -441,7 +439,7 @@ static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) { } } -static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) { +static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t keepdims, mp_obj_t axis, uint8_t optype) { // TODO: treat the flattened array if(ndarray->len == 0) { mp_raise_ValueError(MP_ERROR_TEXT("attempt to get (arg)min/(arg)max of empty sequence")); @@ -521,7 +519,9 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS); numerical_reduce_axes(ndarray, ax, shape, strides); - uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax; + shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); + + uint8_t index = _shape_strides.axis; ndarray_obj_t *results = NULL; @@ -550,8 +550,9 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t if(results->len == 1) { return mp_binary_get_val_array(results->dtype, results->array, 0); } - return MP_OBJ_FROM_PTR(results); + return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides); } + // we should never get to this point return mp_const_none; } #endif @@ -560,6 +561,7 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_keepdims, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -567,6 +569,8 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m mp_obj_t oin = args[0].u_obj; mp_obj_t axis = args[1].u_obj; + mp_obj_t keepdims = args[2].u_obj; + if((axis != mp_const_none) && (!mp_obj_is_int(axis))) { mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer")); } @@ -598,11 +602,11 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m case NUMERICAL_ARGMIN: case NUMERICAL_ARGMAX: COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) - return numerical_argmin_argmax_ndarray(ndarray, axis, optype); + return numerical_argmin_argmax_ndarray(ndarray, keepdims, axis, optype); case NUMERICAL_SUM: case NUMERICAL_MEAN: COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) - return numerical_sum_mean_std_ndarray(ndarray, axis, optype, 0); + return numerical_sum_mean_std_ndarray(ndarray, axis, keepdims, optype, 0); default: mp_raise_NotImplementedError(MP_ERROR_TEXT("operation is not implemented on ndarrays")); } @@ -1385,6 +1389,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } } , { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_keepdims, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1393,6 +1398,8 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_obj_t oin = args[0].u_obj; mp_obj_t axis = args[1].u_obj; size_t ddof = args[2].u_int; + mp_obj_t keepdims = args[2].u_obj; + if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) { // this seems to pass with False, and True... mp_raise_ValueError(MP_ERROR_TEXT("axis must be None, or an integer")); @@ -1401,7 +1408,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof); } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); - return numerical_sum_mean_std_ndarray(ndarray, axis, NUMERICAL_STD, ddof); + return numerical_sum_mean_std_ndarray(ndarray, axis, keepdims, NUMERICAL_STD, ddof); } else { mp_raise_TypeError(MP_ERROR_TEXT("input must be tuple, list, range, or ndarray")); } diff --git a/code/ulab.c b/code/ulab.c index be148c45..8c5cc956 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.0 +#define ULAB_VERSION 6.7.1 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 05ed1ede..92f1b62b 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -162,6 +162,15 @@ void *ndarray_set_float_function(uint8_t dtype) { } #endif /* NDARRAY_BINARY_USES_FUN_POINTER */ +int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) { + int8_t ax = mp_obj_get_int(axis); + if(ax < 0) ax += ndim; + if((ax < 0) || (ax > ndim - 1)) { + mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds")); + } + return ax; +} + shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { // TODO: replace numerical_reduce_axes with this function, wherever applicable // This function should be used, whenever a tensor is contracted; @@ -172,38 +181,36 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { } shape_strides _shape_strides; - size_t *shape = m_new(size_t, ULAB_MAX_DIMS + 1); - _shape_strides.shape = shape; - int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS + 1); - _shape_strides.strides = strides; - _shape_strides.increment = 0; // this is the contracted dimension (won't be overwritten for axis == None) _shape_strides.ndim = 0; - memcpy(_shape_strides.shape, ndarray->shape, sizeof(size_t) * ULAB_MAX_DIMS); - memcpy(_shape_strides.strides, ndarray->strides, sizeof(int32_t) * ULAB_MAX_DIMS); - if(axis == mp_const_none) { + _shape_strides.shape = ndarray->shape; + _shape_strides.strides = ndarray->strides; return _shape_strides; } - uint8_t index = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten) + size_t *shape = m_new(size_t, ULAB_MAX_DIMS + 1); + _shape_strides.shape = shape; + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS + 1); + _shape_strides.strides = strides; + + memcpy(_shape_strides.shape, ndarray->shape, sizeof(size_t) * ULAB_MAX_DIMS); + memcpy(_shape_strides.strides, ndarray->strides, sizeof(int32_t) * ULAB_MAX_DIMS); + + _shape_strides.axis = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten) if(axis != mp_const_none) { // i.e., axis is an integer - int8_t ax = mp_obj_get_int(axis); - if(ax < 0) ax += ndarray->ndim; - if((ax < 0) || (ax > ndarray->ndim - 1)) { - mp_raise_ValueError(MP_ERROR_TEXT("index out of range")); - } - index = ULAB_MAX_DIMS - ndarray->ndim + ax; + int8_t ax = tools_get_axis(axis, ndarray->ndim); + _shape_strides.axis = ULAB_MAX_DIMS - ndarray->ndim + ax; _shape_strides.ndim = ndarray->ndim - 1; } // move the value stored at index to the leftmost position, and align everything else to the right - _shape_strides.shape[0] = ndarray->shape[index]; - _shape_strides.strides[0] = ndarray->strides[index]; - for(uint8_t i = 0; i < index; i++) { + _shape_strides.shape[0] = ndarray->shape[_shape_strides.axis]; + _shape_strides.strides[0] = ndarray->strides[_shape_strides.axis]; + for(uint8_t i = 0; i < _shape_strides.axis; i++) { // entries to the right of index must be shifted by one position to the left _shape_strides.shape[i + 1] = ndarray->shape[i]; _shape_strides.strides[i + 1] = ndarray->strides[i]; @@ -213,16 +220,37 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { _shape_strides.increment = 1; } + if(_shape_strides.ndim == 0) { + _shape_strides.ndim = 1; + _shape_strides.shape[ULAB_MAX_DIMS - 1] = 1; + _shape_strides.strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize; + } + return _shape_strides; } -int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) { - int8_t ax = mp_obj_get_int(axis); - if(ax < 0) ax += ndim; - if((ax < 0) || (ax > ndim - 1)) { - mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds")); +mp_obj_t ulab_tools_restore_dims(ndarray_obj_t *ndarray, ndarray_obj_t *results, mp_obj_t keepdims, shape_strides _shape_strides) { + // restores the contracted dimension, if keepdims is True + if((ndarray->ndim == 1) && (keepdims != mp_const_true)) { + // since the original array has already been contracted and + // we don't want to keep the dimensions here, we have to return a scalar + return mp_binary_get_val_array(results->dtype, results->array, 0); } - return ax; + + if(keepdims == mp_const_true) { + results->ndim += 1; + for(int8_t i = 0; i < ULAB_MAX_DIMS; i++) { + results->shape[i] = ndarray->shape[i]; + } + results->shape[_shape_strides.axis] = 1; + + results->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize; + for(uint8_t i = ULAB_MAX_DIMS; i > 1; i--) { + results->strides[i - 2] = results->strides[i - 1] * results->shape[i - 1]; + } + } + + return MP_OBJ_FROM_PTR(results); } #if ULAB_MAX_DIMS > 1 diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 62170fb8..dd7cdff6 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -17,6 +17,7 @@ typedef struct _shape_strides_t { uint8_t increment; + uint8_t axis; uint8_t ndim; size_t *shape; int32_t *strides; @@ -34,6 +35,7 @@ void *ndarray_set_float_function(uint8_t ); shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); int8_t tools_get_axis(mp_obj_t , uint8_t ); +mp_obj_t ulab_tools_restore_dims(ndarray_obj_t * , ndarray_obj_t * , mp_obj_t , shape_strides ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); uint8_t ulab_binary_get_size(uint8_t ); diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index c857f96d..07a38142 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,15 @@ +Mon, 30 Dec 2024 + +version 6.7.1 + + add keepdims keyword argument to numerical functions + +Sun, 15 Dec 2024 + +version 6.7.0 + + add scipy.integrate module + Sun, 24 Nov 2024 version 6.6.1 diff --git a/tests/2d/numpy/sum.py b/tests/2d/numpy/sum.py new file mode 100644 index 00000000..dc3043e2 --- /dev/null +++ b/tests/2d/numpy/sum.py @@ -0,0 +1,23 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +for dtype in (np.uint8, np.int8, np.uint16, np.int8, np.float): + a = np.array(range(12), dtype=dtype) + b = a.reshape((3, 4)) + + print(a) + print(b) + print() + + print(np.sum(a)) + print(np.sum(a, axis=0)) + print(np.sum(a, axis=0, keepdims=True)) + + print() + print(np.sum(b)) + print(np.sum(b, axis=0)) + print(np.sum(b, axis=1)) + print(np.sum(b, axis=0, keepdims=True)) + print(np.sum(b, axis=1, keepdims=True)) \ No newline at end of file diff --git a/tests/2d/numpy/sum.py.exp b/tests/2d/numpy/sum.py.exp new file mode 100644 index 00000000..89bb41e8 --- /dev/null +++ b/tests/2d/numpy/sum.py.exp @@ -0,0 +1,80 @@ +array([0, 1, 2, ..., 9, 10, 11], dtype=uint8) +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=uint8) + +66 +66 +array([66], dtype=uint8) + +66 +array([12, 15, 18, 21], dtype=uint8) +array([6, 22, 38], dtype=uint8) +array([[12, 15, 18, 21]], dtype=uint8) +array([[6], + [22], + [38]], dtype=uint8) +array([0, 1, 2, ..., 9, 10, 11], dtype=int8) +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=int8) + +66 +66 +array([66], dtype=int8) + +66 +array([12, 15, 18, 21], dtype=int8) +array([6, 22, 38], dtype=int8) +array([[12, 15, 18, 21]], dtype=int8) +array([[6], + [22], + [38]], dtype=int8) +array([0, 1, 2, ..., 9, 10, 11], dtype=uint16) +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=uint16) + +66 +66 +array([66], dtype=uint16) + +66 +array([12, 15, 18, 21], dtype=uint16) +array([6, 22, 38], dtype=uint16) +array([[12, 15, 18, 21]], dtype=uint16) +array([[6], + [22], + [38]], dtype=uint16) +array([0, 1, 2, ..., 9, 10, 11], dtype=int8) +array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]], dtype=int8) + +66 +66 +array([66], dtype=int8) + +66 +array([12, 15, 18, 21], dtype=int8) +array([6, 22, 38], dtype=int8) +array([[12, 15, 18, 21]], dtype=int8) +array([[6], + [22], + [38]], dtype=int8) +array([0.0, 1.0, 2.0, ..., 9.0, 10.0, 11.0], dtype=float64) +array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0]], dtype=float64) + +66.0 +66.0 +array([66.0], dtype=float64) + +66.0 +array([12.0, 15.0, 18.0, 21.0], dtype=float64) +array([6.0, 22.0, 38.0], dtype=float64) +array([[12.0, 15.0, 18.0, 21.0]], dtype=float64) +array([[6.0], + [22.0], + [38.0]], dtype=float64) From be15d626324350fcb4efe3da15ebf717cda5b0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 19 Jan 2025 22:51:02 +0100 Subject: [PATCH 101/113] fix keepdims for std, update docs (#704) --- code/numpy/numerical.c | 5 +- code/numpy/numerical.h | 130 -------------- code/ulab.c | 2 +- docs/manual/source/conf.py | 5 +- docs/manual/source/index.rst | 1 + docs/manual/source/scipy-integrate.rst | 220 +++++++++++++++++++++++ docs/ulab-change-log.md | 6 + docs/ulab-convert.ipynb | 21 +-- docs/ulab-numerical.ipynb | 234 +++++++++++++------------ 9 files changed, 366 insertions(+), 258 deletions(-) create mode 100644 docs/manual/source/scipy-integrate.rst diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 79808f39..76f8c225 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -603,8 +603,9 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m case NUMERICAL_ARGMAX: COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) return numerical_argmin_argmax_ndarray(ndarray, keepdims, axis, optype); - case NUMERICAL_SUM: case NUMERICAL_MEAN: + case NUMERICAL_STD: + case NUMERICAL_SUM: COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) return numerical_sum_mean_std_ndarray(ndarray, axis, keepdims, optype, 0); default: @@ -1398,7 +1399,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_obj_t oin = args[0].u_obj; mp_obj_t axis = args[1].u_obj; size_t ddof = args[2].u_int; - mp_obj_t keepdims = args[2].u_obj; + mp_obj_t keepdims = args[3].u_obj; if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) { // this seems to pass with False, and True... diff --git a/code/numpy/numerical.h b/code/numpy/numerical.h index 186c817b..163b6ea5 100644 --- a/code/numpy/numerical.h +++ b/code/numpy/numerical.h @@ -57,35 +57,9 @@ (rarray) += (results)->itemsize;\ }) -// The mean could be calculated by simply dividing the sum by -// the number of elements, but that method is numerically unstable -#define RUN_MEAN1(type, array, rarray, ss)\ -({\ - mp_float_t M = 0.0;\ - for(size_t i=0; i < (ss).shape[0]; i++) {\ - mp_float_t value = (mp_float_t)(*(type *)(array));\ - M = M + (value - M) / (mp_float_t)(i+1);\ - (array) += (ss).strides[0];\ - }\ - *(rarray)++ = M;\ -}) - // Instead of the straightforward implementation of the definition, // we take the numerically stable Welford algorithm here // https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/ -#define RUN_STD1(type, array, rarray, ss, div)\ -({\ - mp_float_t M = 0.0, m = 0.0, S = 0.0;\ - for(size_t i=0; i < (ss).shape[0]; i++) {\ - mp_float_t value = (mp_float_t)(*(type *)(array));\ - m = M + (value - M) / (mp_float_t)(i+1);\ - S = S + (value - M) * (value - m);\ - M = m;\ - (array) += (ss).strides[0];\ - }\ - *(rarray)++ = MICROPY_FLOAT_C_FUN(sqrt)(S / (div));\ -}) - #define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\ ({\ mp_float_t M = 0.0, m = 0.0, S = 0.0;\ @@ -193,14 +167,6 @@ RUN_SUM1(type, (array), (results), (rarray), (ss));\ } while(0) -#define RUN_MEAN(type, array, rarray, ss) do {\ - RUN_MEAN1(type, (array), (rarray), (ss));\ -} while(0) - -#define RUN_STD(type, array, rarray, ss, div) do {\ - RUN_STD1(type, (array), (results), (rarray), (ss), (div));\ -} while(0) - #define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\ } while(0) @@ -234,26 +200,6 @@ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ } while(0) -#define RUN_MEAN(type, array, rarray, ss) do {\ - size_t l = 0;\ - do {\ - RUN_MEAN1(type, (array), (rarray), (ss));\ - (array) -= (ss).strides[0] * (ss).shape[0];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ -} while(0) - -#define RUN_STD(type, array, rarray, ss, div) do {\ - size_t l = 0;\ - do {\ - RUN_STD1(type, (array), (rarray), (ss), (div));\ - (array) -= (ss).strides[0] * (ss).shape[0];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ -} while(0) - #define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ size_t l = 0;\ do {\ @@ -325,38 +271,6 @@ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ } while(0) -#define RUN_MEAN(type, array, rarray, ss) do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - RUN_MEAN1(type, (array), (rarray), (ss));\ - (array) -= (ss).strides[0] * (ss).shape[0];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ - (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ -} while(0) - -#define RUN_STD(type, array, rarray, ss, div) do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - RUN_STD1(type, (array), (rarray), (ss), (div));\ - (array) -= (ss).strides[0] * (ss).shape[0];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ - (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ -} while(0) - #define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ size_t k = 0;\ do {\ @@ -467,50 +381,6 @@ } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ } while(0) -#define RUN_MEAN(type, array, rarray, ss) do {\ - size_t j = 0;\ - do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - RUN_MEAN1(type, (array), (rarray), (ss));\ - (array) -= (ss).strides[0] * (ss).shape[0];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ - (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ - (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 3];\ - j++;\ - } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ -} while(0) - -#define RUN_STD(type, array, rarray, ss, div) do {\ - size_t j = 0;\ - do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - RUN_STD1(type, (array), (rarray), (ss), (div));\ - (array) -= (ss).strides[0] * (ss).shape[0];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ - (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ - k++;\ - } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ - (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\ - (array) += (ss).strides[ULAB_MAX_DIMS - 3];\ - j++;\ - } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ -} while(0) - #define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ size_t j = 0;\ do {\ diff --git a/code/ulab.c b/code/ulab.c index 8c5cc956..f3bba0f4 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.1 +#define ULAB_VERSION 6.7.2 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index c4a27e26..8db23fbc 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -23,11 +23,12 @@ # -- Project information ----------------------------------------------------- project = 'The ulab book' -copyright = '2019-2024, Zoltán Vörös and contributors' +copyright = '2019-2025, Zoltán Vörös and contributors' author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '6.6.0' +release = '6.7.2' + # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/index.rst b/docs/manual/source/index.rst index 40fbc00d..b4633f57 100644 --- a/docs/manual/source/index.rst +++ b/docs/manual/source/index.rst @@ -23,6 +23,7 @@ Welcome to the ulab book! numpy-fft numpy-linalg numpy-random + scipy-integrate scipy-linalg scipy-optimize scipy-signal diff --git a/docs/manual/source/scipy-integrate.rst b/docs/manual/source/scipy-integrate.rst new file mode 100644 index 00000000..781e9171 --- /dev/null +++ b/docs/manual/source/scipy-integrate.rst @@ -0,0 +1,220 @@ + +scipy.integrate +=============== + +This module provides a simplified subset of CPython’s +``scipy.integrate`` module. The algorithms were not ported from +CPython’s ``scipy.integrate`` for the sake of resource usage, but +derived from a paper found in https://www.genivia.com/qthsh.html. There +are four numerical integration algorithms: + +1. `scipy.integrate.quad <#quad>`__ +2. `scipy.integrate.romberg <#romberg>`__ +3. `scipy.integrate.simpson <#simpson>`__ +4. `scipy.integrate.tanhsinh <#tanhsinh>`__ + +Introduction +------------ + +Numerical integration works best with float64 math enabled. If you +require float64 math, be sure to set ``MICROPY_OBJ_REPR_A`` and +``MICROPY_FLOAT_IMPL_DOUBLE``. This being said, the modules work equally +well using float32, albeit with reduced precision. The required error +tolerance can be specified for each of the function calls using the +“eps=” option, defaulting to the compiled in ``etolerance`` value (1e-14 +for fp64, 1e-8 for fp32). + +The submodule can be enabled by setting +``ULAB_SCIPY_HAS_INTEGRATE_MODULE`` in ``code/ulab.h``. As for the +individual integration algorithms, you can select which to include by +setting one or more of ``ULAB_INTEGRATE_HAS_QUAD``, +``ULAB_INTEGRATE_HAS_ROMBERG``, ``ULAB_INTEGRATE_HAS_SIMPSON``, and +``ULAB_INTEGRATE_HAS_TANHSINH``. + +Also note that these algorithms do not support complex numbers, although +it is certainly possible to implement complex integration in MicroPython +on top of this module, e.g. as in +https://stackoverflow.com/questions/5965583/use-scipy-integrate-quad-to-integrate-complex-numbers. + +quad +---- + +``scipy``: +https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html + +In CPython ``scipy.integrate``, ``quad`` is a wrapper implementing many +algorithms based on the Fortran QUADPACK package. Gauss-Kronrod is just +one of them, and it is useful for most general-purpose tasks. This +particular function implements an Adaptive Gauss-Kronrod (G10,K21) +quadrature algorithm. The Gauss–Kronrod quadrature formula is a variant +of Gaussian quadrature, in which the evaluation points are chosen so +that an accurate approximation can be computed by re-using the +information produced by the computation of a less accurate approximation +(https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula). + +The function takes three to five arguments: + +- f, a callable, +- a and b, the lower and upper integration limit, +- order=, the order of integration (default 5), +- eps=, the error tolerance (default etolerance) + +The function returns the result and the error estimate as a tuple of +floats. + +.. code:: + + # code to be run in micropython + + from ulab import scipy + + f = lambda x: x**2 + 2*x + 1 + result = scipy.integrate.quad(f, 0, 5, order=5, eps=1e-10) + print (f"result = {result}") + +.. parsed-literal:: + + UsageError: Cell magic `%%micropython` not found. + + +romberg +------- + +``scipy``: +https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html + +This function implements the Romberg quadrature algorithm. Romberg’s +method is a Newton–Cotes formula – it evaluates the integrand at equally +spaced points. The integrand must have continuous derivatives, though +fairly good results may be obtained if only a few derivatives exist. If +it is possible to evaluate the integrand at unequally spaced points, +then other methods such as Gaussian quadrature and Clenshaw–Curtis +quadrature are generally more accurate +(https://en.wikipedia.org/wiki/Romberg%27s_method). + +Please note: This function is deprecated as of SciPy 1.12.0 and will be +removed in SciPy 1.15.0. Please use ``scipy.integrate.quad`` instead. + +The function takes three to five arguments: + +- f, a callable, +- a and b, the lower and upper integration limit, +- steps=, the number of steps taken to calculate (default 100), +- eps=, the error tolerance (default etolerance) + +The function returns the result as a float. + +.. code:: + + # code to be run in micropython + + from ulab import scipy + + f = lambda x: x**2 + 2*x + 1 + result = scipy.integrate.romberg(f, 0, 5) + print (f"result = {result}") + +.. parsed-literal:: + + UsageError: Cell magic `%%micropython` not found. + + +simpson +------- + +``scipy``: +https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.simpson.html + +This function is different from CPython’s ``simpson`` method in that it +does not take an array of function values but determines the optimal +spacing of samples itself. Adaptive Simpson’s method, also called +adaptive Simpson’s rule, is a method of numerical integration proposed +by G.F. Kuncir in 1962. It is probably the first recursive adaptive +algorithm for numerical integration to appear in print, although more +modern adaptive methods based on Gauss–Kronrod quadrature and +Clenshaw–Curtis quadrature are now generally preferred +(https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method). + +The function takes three to five arguments: + +- f, a callable, +- a and b, the lower and upper integration limit, +- steps=, the number of steps taken to calculate (default 100), +- eps=, the error tolerance (default etolerance) + +The function returns the result as a float. + +.. code:: + + # code to be run in micropython + + from ulab import scipy + + f = lambda x: x**2 + 2*x + 1 + result = scipy.integrate.simpson(f, 0, 5) + print (f"result = {result}") + +.. parsed-literal:: + + UsageError: Cell magic `%%micropython` not found. + + +tanhsinh +-------- + +``scipy``: +https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html + +In CPython ``scipy.integrate``, ``tanhsinh`` is written in Python +(https://github.com/scipy/scipy/blob/main/scipy/integrate/\_tanhsinh.py). +It is used in cases where Newton-Cotes, Gauss-Kronrod, and other +formulae do not work due to properties of the integrand or the +integration limits. (In SciPy v1.14.1, it is not a public function but +it has been marked as public in SciPy v1.15.0rc1). + +This particular function implements an optimized Tanh-Sinh, Sinh-Sinh +and Exp-Sinh quadrature algorithm. It is especially applied where +singularities or infinite derivatives exist at one or both endpoints. +The method uses hyperbolic functions in a change of variables to +transform an integral on the interval x ∈ (−1, 1) to an integral on the +entire real line t ∈ (−∞, ∞), the two integrals having the same value. +After this transformation, the integrand decays with a double +exponential rate, and thus, this method is also known as the double +exponential (DE) formula +(https://en.wikipedia.org/wiki/Tanh-sinh_quadrature). + +As opposed to the three algorithms mentioned before, it also supports +integrals with infinite limits like the Gaussian integral +(https://en.wikipedia.org/wiki/Gaussian_integral), as shown below. + +The function takes three to five arguments: + +- f, a callable, +- a and b, the lower and upper integration limit, +- levels=, the number of loops taken to calculate (default 6), +- eps=, the error tolerance (default: etolerance) + +The function returns the result and the error estimate as a tuple of +floats. + +.. code:: + + # code to be run in micropython + + from ulab import scipy, numpy as np + from math import * + f = lambda x: exp(- x**2) + result = scipy.integrate.tanhsinh(f, -np.inf, np.inf) + print (f"result = {result}") + exact = sqrt(pi) # which is the exact value + print (f"exact value = {exact}") + +.. parsed-literal:: + + UsageError: Cell magic `%%micropython` not found. + + +.. code:: + + # code to be run in CPython + diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 07a38142..d79b7827 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sun, 19 Jan 2025 + +version 6.7.2 + + fix keepdims for std, remove redundant macros from numerical.h, update documentation + Mon, 30 Dec 2024 version 6.7.1 diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index 4ce30e5a..c81e0fd3 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -57,11 +57,11 @@ "# -- Project information -----------------------------------------------------\n", "\n", "project = 'The ulab book'\n", - "copyright = '2019-2024, Zoltán Vörös and contributors'\n", + "copyright = '2019-2025, Zoltán Vörös and contributors'\n", "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '6.6.0'\n", + "release = '6.7.2'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -191,6 +191,7 @@ " numpy-fft\n", " numpy-linalg\n", " numpy-random\n", + " scipy-integrate\n", " scipy-linalg\n", " scipy-optimize\n", " scipy-signal\n", @@ -216,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:21.647179Z", @@ -257,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:42.024028Z", @@ -294,6 +295,10 @@ "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", " _, nbc = validator.normalize(nbc)\n", "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", + " _, nbc = validator.normalize(nbc)\n", + "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", " _, nbc = validator.normalize(nbc)\n" ] } @@ -306,6 +311,7 @@ " 'numpy-fft',\n", " 'numpy-linalg',\n", " 'numpy-random',\n", + " 'scipy-integrate',\n", " 'scipy-linalg',\n", " 'scipy-optimize',\n", " 'scipy-signal',\n", @@ -470,7 +476,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.8.5 ('base')", + "display_name": "base", "language": "python", "name": "python3" }, @@ -532,11 +538,6 @@ "_Feature" ], "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10" - } } }, "nbformat": 4, diff --git a/docs/ulab-numerical.ipynb b/docs/ulab-numerical.ipynb index be549545..d8a70d89 100644 --- a/docs/ulab-numerical.ipynb +++ b/docs/ulab-numerical.ipynb @@ -14,6 +14,7 @@ "name": "stdout", "output_type": "stream", "text": [ + "%pylab is deprecated, use %matplotlib inline and import the required libraries.\n", "Populating the interactive namespace from numpy and matplotlib\n" ] } @@ -31,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:16:29.118001Z", @@ -49,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:16:37.453883Z", @@ -77,7 +78,7 @@ " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", " with open('/dev/shm/micropython.py', 'w') as fout:\n", " fout.write(cell)\n", - " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " print(proc.stdout.read().decode(\"utf-8\"))\n", " print(proc.stderr.read().decode(\"utf-8\"))\n", @@ -182,7 +183,7 @@ "%%micropython -pyboard 1\n", "\n", "import utime\n", - "import ulab as np\n", + "from ulab import numpy as np\n", "\n", "def timeit(n=1000):\n", " def wrapper(f, *args, **kwargs):\n", @@ -251,7 +252,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2020-10-17T21:26:22.507996Z", @@ -263,11 +264,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "array([1.0, 2.0, 3.0], dtype=float)\n", - "array([], dtype=float)\n", + "array([1.0, 2.0, 3.0], dtype=float64)\n", + "array([], dtype=float64)\n", "[] 0\n", - "array([1.0, 2.0, 3.0], dtype=float)\n", - "array([], dtype=float)\n", + "array([1.0, 2.0, 3.0], dtype=float64)\n", + "array([], dtype=float64)\n", "\n", "\n" ] @@ -276,7 +277,7 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", + "from ulab import numpy as np\n", "\n", "a = np.array([1, 2, 3])\n", "print(a)\n", @@ -294,13 +295,12 @@ "print(a)\n", "\n", "b = np.ones(0) + 1\n", - "print(b)\n", - "# print('b', b.shape())" + "print(b)" ] }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2020-10-17T21:54:49.123748Z", @@ -312,7 +312,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "0, 1, -3array([], dtype=float)\n", + "array([], dtype=float64)\n", "\n", "\n" ] @@ -321,38 +321,12 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", + "from ulab import numpy as np\n", + "\n", "a = np.array([1, 2, 3])\n", "print(a[0:1:-3])" ] }, - { - "cell_type": "code", - "execution_count": 127, - "metadata": { - "ExecuteTime": { - "end_time": "2020-10-17T21:57:01.482277Z", - "start_time": "2020-10-17T21:57:01.477362Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[0]" - ] - }, - "execution_count": 127, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "l = list(range(13))\n", - "\n", - "l[0:10:113]" - ] - }, { "cell_type": "code", "execution_count": 81, @@ -382,7 +356,7 @@ }, { "cell_type": "code", - "execution_count": 375, + "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2019-10-18T13:08:28.113525Z", @@ -394,16 +368,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float)\n", + "a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float64)\n", "min of a: 0.0\n", "argmin of a: 2\n", "\n", "b:\n", " array([[1.0, 2.0, 0.0],\n", - "\t [1.0, 10.0, -1.0]], dtype=float)\n", + " [1.0, 10.0, -1.0]], dtype=float64)\n", "min of b (flattened): -1.0\n", - "min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float)\n", - "min of b (axis=1): array([0.0, -1.0], dtype=float)\n", + "min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float64)\n", + "min of b (axis=1): array([0.0, -1.0], dtype=float64)\n", "\n", "\n" ] @@ -412,19 +386,18 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([1, 2, 0, 1, 10])\n", "print('a:', a)\n", - "print('min of a:', numerical.min(a))\n", - "print('argmin of a:', numerical.argmin(a))\n", + "print('min of a:', np.min(a))\n", + "print('argmin of a:', np.argmin(a))\n", "\n", "b = np.array([[1, 2, 0], [1, 10, -1]])\n", "print('\\nb:\\n', b)\n", - "print('min of b (flattened):', numerical.min(b))\n", - "print('min of b (axis=0):', numerical.min(b, axis=0))\n", - "print('min of b (axis=1):', numerical.min(b, axis=1))" + "print('min of b (flattened):', np.min(b))\n", + "print('min of b (axis=0):', np.min(b, axis=0))\n", + "print('min of b (axis=1):', np.min(b, axis=1))" ] }, { @@ -439,12 +412,14 @@ "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.mean.html\n", "\n", - "These three functions follow the same pattern: if the axis keyword is not specified, it assumes the default value of `None`, and returns the result of the computation for the flattened array. Otherwise, the calculation is along the given axis." + "These three functions follow the same pattern: if the `axis` keyword is not specified, they assume the default value of `None`, and return the result of the computation for the flattened array. Otherwise, the calculation is along the given axis. \n", + "\n", + "If the `axis` keyword argument is a number (this can also be negative to signify counting from the rightmost axis) the functions contract the arrays, i.e., the results will have one axis fewer than the input array. The only exception to this rule is when the `keepdims` keyword argument is supplied with a value `True`, in which case, the results will have the same number of axis as the input, but the axis specified in `axis` will have a length of 1. This is useful in cases, when the output is to be broadcast with the input in subsequent computations." ] }, { "cell_type": "code", - "execution_count": 527, + "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2019-10-20T06:51:58.845076Z", @@ -458,29 +433,73 @@ "text": [ "a: \n", " array([[1.0, 2.0, 3.0],\n", - "\t [4.0, 5.0, 6.0],\n", - "\t [7.0, 8.0, 9.0]], dtype=float)\n", + " [4.0, 5.0, 6.0],\n", + " [7.0, 8.0, 9.0]], dtype=float64)\n", "sum, flat array: 45.0\n", - "mean, horizontal: array([2.0, 5.0, 8.0], dtype=float)\n", - "std, vertical: array([2.44949, 2.44949, 2.44949], dtype=float)\n", + "mean, horizontal: array([2.0, 5.0, 8.0], dtype=float64)\n", + "std, vertical: array([2.449489742783178, 2.449489742783178, 2.449489742783178], dtype=float64)\n", + "\n", "\n" ] } ], "source": [ - "%%micropython -pyboard 1\n", + "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n", "print('a: \\n', a)\n", "\n", - "print('sum, flat array: ', numerical.sum(a))\n", + "print('sum, flat array: ', np.sum(a))\n", + "\n", + "print('mean, horizontal: ', np.mean(a, axis=1))\n", "\n", - "print('mean, horizontal: ', numerical.mean(a, axis=1))\n", + "print('std, vertical: ', np.std(a, axis=0))" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: \n", + " array([[1.0, 2.0, 3.0],\n", + " [4.0, 5.0, 6.0],\n", + " [7.0, 8.0, 9.0]], dtype=float64)\n", + "\n", + "std, along 0th axis:\n", + " array([2.449489742783178, 2.449489742783178, 2.449489742783178], dtype=float64)\n", + "\n", + "a: \n", + " array([[1.0, 2.0, 3.0],\n", + " [4.0, 5.0, 6.0],\n", + " [7.0, 8.0, 9.0]], dtype=float64)\n", + "\n", + "std, along 1st axis, keeping dimensions:\n", + " array([[0.8164965809277261],\n", + " [0.8164965809277261],\n", + " [0.8164965809277261]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n", + "print('a: \\n', a)\n", + "print('\\nstd, along 0th axis:\\n', np.std(a, axis=0))\n", "\n", - "print('std, vertical: ', numerical.std(a, axis=0))" + "print('\\na: \\n', a)\n", + "print('\\nstd, along 1st axis, keeping dimensions:\\n', np.std(a, axis=1, keepdims=True))" ] }, { @@ -519,13 +538,12 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([1, 2, 3, 4, 5, 6, 7, 8])\n", "print(\"a:\\t\\t\\t\", a)\n", "\n", - "numerical.roll(a, 2)\n", + "np.roll(a, 2)\n", "print(\"a rolled to the left:\\t\", a)\n", "\n", "# this should be the original vector\n", @@ -581,19 +599,18 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])\n", "print(\"a:\\n\", a)\n", "\n", - "numerical.roll(a, 2)\n", + "np.roll(a, 2)\n", "print(\"\\na rolled to the left:\\n\", a)\n", "\n", - "numerical.roll(a, -1, axis=1)\n", + "np.roll(a, -1, axis=1)\n", "print(\"\\na rolled up:\\n\", a)\n", "\n", - "numerical.roll(a, 1, axis=None)\n", + "np.roll(a, 1, axis=None)\n", "print(\"\\na rolled with None:\\n\", a)" ] }, @@ -649,9 +666,7 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", - "from ulab import vector\n", + "from ulab import numpy as np\n", "\n", "def dummy_adc():\n", " # dummy adc function, so that the results are reproducible\n", @@ -659,8 +674,8 @@ " \n", "n = 10\n", "# These are the normalised weights; the last entry is the most dominant\n", - "weight = vector.exp([1, 2, 3, 4, 5])\n", - "weight = weight/numerical.sum(weight)\n", + "weight = np.exp([1, 2, 3, 4, 5])\n", + "weight = weight/np.sum(weight)\n", "\n", "print(weight)\n", "# initial array of samples\n", @@ -669,10 +684,10 @@ "for i in range(n):\n", " # a new datum is inserted on the right hand side. This simply overwrites whatever was in the last slot\n", " samples[-1] = dummy_adc()\n", - " print(numerical.mean(samples[-5:]*weight))\n", + " print(np.mean(samples[-5:]*weight))\n", " print(samples[-5:])\n", " # the data are shifted by one position to the left\n", - " numerical.roll(samples, 1)" + " numerical.np(samples, 1)" ] }, { @@ -725,17 +740,16 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([1, 2, 3, 4, 5])\n", "print(\"a: \\t\", a)\n", "print(\"a flipped:\\t\", np.flip(a))\n", "\n", "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.uint8)\n", - "print(\"\\na flipped horizontally\\n\", numerical.flip(a, axis=1))\n", - "print(\"\\na flipped vertically\\n\", numerical.flip(a, axis=0))\n", - "print(\"\\na flipped horizontally+vertically\\n\", numerical.flip(a))" + "print(\"\\na flipped horizontally\\n\", np.flip(a, axis=1))\n", + "print(\"\\na flipped vertically\\n\", np.flip(a, axis=0))\n", + "print(\"\\na flipped horizontally+vertically\\n\", np.flip(a))" ] }, { @@ -800,19 +814,18 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array(range(9), dtype=np.uint8)\n", "print('a:\\n', a)\n", "\n", - "print('\\nfirst derivative:\\n', numerical.diff(a, n=1))\n", - "print('\\nsecond derivative:\\n', numerical.diff(a, n=2))\n", + "print('\\nfirst derivative:\\n', np.diff(a, n=1))\n", + "print('\\nsecond derivative:\\n', np.diff(a, n=2))\n", "\n", "c = np.array([[1, 2, 3, 4], [4, 3, 2, 1], [1, 4, 9, 16], [0, 0, 0, 0]])\n", "print('\\nc:\\n', c)\n", - "print('\\nfirst derivative, first axis:\\n', numerical.diff(c, axis=0))\n", - "print('\\nfirst derivative, second axis:\\n', numerical.diff(c, axis=1))" + "print('\\nfirst derivative, first axis:\\n', np.diff(c, axis=0))\n", + "print('\\nfirst derivative, second axis:\\n', np.diff(c, axis=1))" ] }, { @@ -858,7 +871,7 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", + "from ulab import numpy as np\n", "\n", "a = np.array(range(12), dtype=np.int8).reshape((3, 4))\n", "print('a:\\n', a)\n", @@ -925,18 +938,17 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n", "print('\\na:\\n', a)\n", - "b = numerical.sort(a, axis=0)\n", + "b = np.sort(a, axis=0)\n", "print('\\na sorted along vertical axis:\\n', b)\n", "\n", - "c = numerical.sort(a, axis=1)\n", + "c = np.sort(a, axis=1)\n", "print('\\na sorted along horizontal axis:\\n', c)\n", "\n", - "c = numerical.sort(a, axis=None)\n", + "c = np.sort(a, axis=None)\n", "print('\\nflattened a sorted:\\n', c)" ] }, @@ -955,15 +967,13 @@ "source": [ "%%micropython -pyboard 1\n", "\n", - "import ulab as np\n", - "from ulab import vector\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "@timeit\n", "def sort_time(array):\n", - " return numerical.sort(array)\n", + " return np.sort(array)\n", "\n", - "b = vector.sin(np.linspace(0, 6.28, num=1000))\n", + "b = np.sin(np.linspace(0, 6.28, num=1000))\n", "print('b: ', b)\n", "sort_time(b)\n", "print('\\nb sorted:\\n', b)" @@ -1025,18 +1035,17 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n", "print('\\na:\\n', a)\n", - "b = numerical.argsort(a, axis=0)\n", + "b = np.argsort(a, axis=0)\n", "print('\\na sorted along vertical axis:\\n', b)\n", "\n", - "c = numerical.argsort(a, axis=1)\n", + "c = np.argsort(a, axis=1)\n", "print('\\na sorted along horizontal axis:\\n', c)\n", "\n", - "c = numerical.argsort(a, axis=None)\n", + "c = np.argsort(a, axis=None)\n", "print('\\nflattened a sorted:\\n', c)" ] }, @@ -1078,12 +1087,11 @@ "source": [ "%%micropython -unix 1\n", "\n", - "import ulab as np\n", - "from ulab import numerical\n", + "from ulab import numpy as np\n", "\n", "a = np.array([0, 5, 1, 3, 2, 4], dtype=np.uint8)\n", "print('\\na:\\n', a)\n", - "b = numerical.argsort(a, axis=1)\n", + "b = np.argsort(a, axis=1)\n", "print('\\nsorting indices:\\n', b)\n", "print('\\nthe original array:\\n', a)" ] @@ -1091,7 +1099,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "base", "language": "python", "name": "python3" }, @@ -1105,7 +1113,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.11.7" }, "toc": { "base_numbering": 1, From 20f7259a471fe180ad64279a6970eb991537cffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 26 Jan 2025 21:04:20 +0100 Subject: [PATCH 102/113] ix keepdims for min, max, argmin, argmax (#707) --- code/numpy/numerical.c | 4 - code/ulab.c | 2 +- docs/manual/source/conf.py | 2 +- docs/ulab-change-log.md | 6 ++ docs/ulab-convert.ipynb | 6 +- docs/ulab-numerical.ipynb | 145 ++++++++++--------------------------- 6 files changed, 50 insertions(+), 115 deletions(-) diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 76f8c225..9a3ee322 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -546,10 +546,6 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t } m_del(int32_t, strides, ULAB_MAX_DIMS); - - if(results->len == 1) { - return mp_binary_get_val_array(results->dtype, results->array, 0); - } return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides); } // we should never get to this point diff --git a/code/ulab.c b/code/ulab.c index f3bba0f4..f80bf620 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.2 +#define ULAB_VERSION 6.7.3 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 8db23fbc..c525ee24 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,7 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '6.7.2' +release = '6.7.3' # -- General configuration --------------------------------------------------- diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index d79b7827..11fd65e6 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sun, 26 Jan 2025 + +version 6.7.3 + + fix keepdims for min, max, argmin, argmax + Sun, 19 Jan 2025 version 6.7.2 diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index c81e0fd3..1c7a8430 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '6.7.2'\n", + "release = '6.7.3'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -217,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:21.647179Z", @@ -258,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:42.024028Z", diff --git a/docs/ulab-numerical.ipynb b/docs/ulab-numerical.ipynb index d8a70d89..7be30092 100644 --- a/docs/ulab-numerical.ipynb +++ b/docs/ulab-numerical.ipynb @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:16:37.453883Z", @@ -245,115 +245,11 @@ "\n", "**WARNING:** Difference to `numpy`: the `out` keyword argument is not implemented.\n", "\n", - "These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined.\n", + "These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined. The functions also accept the `keepdims=True` or `keepdims=False` keyword argument. The latter case is the default, while the former keeps the dimensions (the number of axes) of the supplied array. \n", "\n", "`argmin/argmax` return the position (index) of the minimum/maximum in the sequence." ] }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2020-10-17T21:26:22.507996Z", - "start_time": "2020-10-17T21:26:22.492543Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "array([1.0, 2.0, 3.0], dtype=float64)\n", - "array([], dtype=float64)\n", - "[] 0\n", - "array([1.0, 2.0, 3.0], dtype=float64)\n", - "array([], dtype=float64)\n", - "\n", - "\n" - ] - } - ], - "source": [ - "%%micropython -unix 1\n", - "\n", - "from ulab import numpy as np\n", - "\n", - "a = np.array([1, 2, 3])\n", - "print(a)\n", - "print(a[-1:-1:-3])\n", - "try:\n", - " sa = list(a[-1:-1:-3])\n", - " la = len(sa)\n", - "except IndexError as e:\n", - " sa = str(e)\n", - " la = -1\n", - " \n", - "print(sa, la)\n", - "\n", - "a[-1:-1:-3] = np.ones(0)\n", - "print(a)\n", - "\n", - "b = np.ones(0) + 1\n", - "print(b)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2020-10-17T21:54:49.123748Z", - "start_time": "2020-10-17T21:54:49.093819Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "array([], dtype=float64)\n", - "\n", - "\n" - ] - } - ], - "source": [ - "%%micropython -unix 1\n", - "\n", - "from ulab import numpy as np\n", - "\n", - "a = np.array([1, 2, 3])\n", - "print(a[0:1:-3])" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "metadata": { - "ExecuteTime": { - "end_time": "2020-10-17T20:59:58.285134Z", - "start_time": "2020-10-17T20:59:58.263605Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(0,)" - ] - }, - "execution_count": 81, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([1, 2, 3])\n", - "np.ones(0, dtype=uint8) / np.zeros(0, dtype=uint16)\n", - "np.ones(0).shape" - ] - }, { "cell_type": "code", "execution_count": 10, @@ -400,6 +296,43 @@ "print('min of b (axis=1):', np.min(b, axis=1))" ] }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: array([[0.0, 1.0, 2.0, 3.0],\n", + " [4.0, 5.0, 6.0, 7.0],\n", + " [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n", + "\n", + "min of a (axis=1):\n", + " array([[0.0],\n", + " [4.0],\n", + " [8.0]], dtype=float64)\n", + "\n", + "min of a (axis=0):\n", + " array([[0.0, 1.0, 2.0, 3.0]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(12)).reshape((3, 4))\n", + "\n", + "print('a:', a)\n", + "print('\\nmin of a (axis=1):\\n', np.min(a, axis=1, keepdims=True))\n", + "print('\\nmin of a (axis=0):\\n', np.min(a, axis=0, keepdims=True))" + ] + }, { "cell_type": "markdown", "metadata": {}, From 66daa8960a574cf5357b3b59722e5b5588c451c7 Mon Sep 17 00:00:00 2001 From: Brian Whitman Date: Sun, 16 Feb 2025 10:36:33 -0800 Subject: [PATCH 103/113] Adding tulipcc to ulab's users (#711) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5c91d671..063c26d1 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ of the user manual. 1. `MaixPy` https://github.com/sipeed/MaixPy 1. `OpenMV` https://github.com/openmv/openmv 1. `pimoroni-pico` https://github.com/pimoroni/pimoroni-pico +1. `Tulip Creative Computer` https://github.com/shorepine/tulipcc ## Compiling From 825ec2b143ebd8d3d3707bac2af0fe1ae6cb401a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 16 Mar 2025 20:37:00 +0100 Subject: [PATCH 104/113] re-name integration constants (#714) --- code/scipy/integrate/integrate.c | 102 +++++++++++++++---------------- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++ 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/code/scipy/integrate/integrate.c b/code/scipy/integrate/integrate.c index 80def711..17b5371f 100644 --- a/code/scipy/integrate/integrate.c +++ b/code/scipy/integrate/integrate.c @@ -41,21 +41,21 @@ #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-14), 0x283424dcUL, 0x3e901b2b29a4692bULL); -#define MACHEPS MICROPY_FLOAT_CONST(1e-17) +#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-17) #else ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-8), 0x358637cfUL, 0x3e7010c6f7d42d18ULL); -#define MACHEPS MICROPY_FLOAT_CONST(1e-8) +#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-8) #endif -#define ZERO MICROPY_FLOAT_CONST(0.0) -#define POINT_TWO_FIVE MICROPY_FLOAT_CONST(0.25) -#define ONE MICROPY_FLOAT_CONST(1.0) -#define TWO MICROPY_FLOAT_CONST(2.0) -#define FOUR MICROPY_FLOAT_CONST(4.0) -#define SIX MICROPY_FLOAT_CONST(6.0) -#define TEN MICROPY_FLOAT_CONST(10.0) -#define FIFTEEN MICROPY_FLOAT_CONST(15.0) -#define EPS_5 MICROPY_FLOAT_CONST(1e-5) +#define ULAB_ZERO MICROPY_FLOAT_CONST(0.0) +#define ULAB_POINT_TWO_FIVE MICROPY_FLOAT_CONST(0.25) +#define ULAB_ONE MICROPY_FLOAT_CONST(1.0) +#define ULAB_TWO MICROPY_FLOAT_CONST(2.0) +#define ULAB_FOUR MICROPY_FLOAT_CONST(4.0) +#define ULAB_SIX MICROPY_FLOAT_CONST(6.0) +#define ULAB_TEN MICROPY_FLOAT_CONST(10.0) +#define ULAB_FIFTEEN MICROPY_FLOAT_CONST(15.0) +#define ULAB_EPSILON_5 MICROPY_FLOAT_CONST(1e-5) static mp_float_t integrate_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) { @@ -68,7 +68,7 @@ static mp_float_t integrate_python_call(const mp_obj_type_t *type, mp_obj_t fun, // sign helper function int sign(mp_float_t x) { - if (x >= ZERO) + if (x >= ULAB_ZERO) return 1; else return -1; @@ -85,7 +85,7 @@ mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_ mp_obj_t fargs[1]; mp_float_t h2 = integrate_python_call(type, fun, a + d/2, fargs, 0) - integrate_python_call(type, fun, (a + d*2)*4, fargs, 0); int i = 1, j = 32; // j=32 is optimal to find r - if (isfinite(h2) && MICROPY_FLOAT_C_FUN(fabs)(h2) > EPS_5) { // if |h2| > 2^-16 + if (isfinite(h2) && MICROPY_FLOAT_C_FUN(fabs)(h2) > ULAB_EPSILON_5) { // if |h2| > 2^-16 mp_float_t r, fl, fr, h, s = 0, lfl, lfr, lr = 2; do { // find max j such that fl and fr are finite j /= 2; @@ -118,8 +118,8 @@ mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_ if (s > eps) { // if sum of |h| > eps h = lfl - lfr; // use last fl and fr before the sign change r = lr; // use last r before the sign change - if (h != ZERO) // if last diff != 0, back up r by one step - r /= TWO; + if (h != ULAB_ZERO) // if last diff != 0, back up r by one step + r /= ULAB_TWO; if (MICROPY_FLOAT_C_FUN(fabs)(lfl) < MICROPY_FLOAT_C_FUN(fabs)(lfr)) d /= r; // move d closer to the finite endpoint else @@ -135,8 +135,8 @@ mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_ mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps, mp_float_t *e) { const mp_obj_type_t *type = mp_obj_get_type(fun); mp_obj_t fargs[1]; - const mp_float_t tol = TEN*eps; - mp_float_t c = ZERO, d = ONE, s, sign = ONE, v, h = TWO; + const mp_float_t tol = ULAB_TEN * eps; + mp_float_t c = ULAB_ZERO, d = ULAB_ONE, s, sign = ULAB_ONE, v, h = ULAB_TWO; int k = 0, mode = 0; // Tanh-Sinh = 0, Exp-Sinh = 1, Sinh-Sinh = 2 if (b < a) { // swap bounds v = b; @@ -145,8 +145,8 @@ mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, u sign = -1; } if (isfinite(a) && isfinite(b)) { - c = (a+b)/TWO; - d = (b-a)/TWO; + c = (a+b) / ULAB_TWO; + d = (b-a) / ULAB_TWO; v = c; } else if (isfinite(a)) { @@ -165,20 +165,20 @@ mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, u } else { mode = 2; // Sinh-Sinh - v = ZERO; + v = ULAB_ZERO; } s = integrate_python_call(type, fun, v, fargs, 0); do { - mp_float_t p = ZERO, q, fp = ZERO, fm = ZERO, t, eh; - h /= TWO; + mp_float_t p = ULAB_ZERO, q, fp = ULAB_ZERO, fm = ULAB_ZERO, t, eh; + h /= ULAB_TWO; t = eh = MICROPY_FLOAT_C_FUN(exp)(h); - if (k > ZERO) + if (k > ULAB_ZERO) eh *= eh; if (mode == 0) { // Tanh-Sinh do { - mp_float_t u = MICROPY_FLOAT_C_FUN(exp)(ONE / t - t); // = exp(-2*sinh(j*h)) = 1/exp(sinh(j*h))^2 - mp_float_t r = TWO * u / (ONE + u); // = 1 - tanh(sinh(j*h)) - mp_float_t w = (t + ONE / t) * r / (ONE + u); // = cosh(j*h)/cosh(sinh(j*h))^2 + mp_float_t u = MICROPY_FLOAT_C_FUN(exp)(ULAB_ONE / t - t); // = exp(-2*sinh(j*h)) = 1/exp(sinh(j*h))^2 + mp_float_t r = ULAB_TWO * u / (ULAB_ONE + u); // = 1 - tanh(sinh(j*h)) + mp_float_t w = (t + ULAB_ONE / t) * r / (ULAB_ONE + u); // = cosh(j*h)/cosh(sinh(j*h))^2 mp_float_t x = d*r; if (a+x > a) { // if too close to a then reuse previous fp mp_float_t y = integrate_python_call(type, fun, a+x, fargs, 0); @@ -196,11 +196,11 @@ mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, u } while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p)); } else { - t /= TWO; + t /= ULAB_TWO; do { - mp_float_t r = MICROPY_FLOAT_C_FUN(exp)(t - POINT_TWO_FIVE / t); // = exp(sinh(j*h)) + mp_float_t r = MICROPY_FLOAT_C_FUN(exp)(t - ULAB_POINT_TWO_FIVE / t); // = exp(sinh(j*h)) mp_float_t x, y, w = r; - q = ZERO; + q = ULAB_ZERO; if (mode == 1) { // Exp-Sinh x = c + d/r; if (x == c) // if x hit the finite endpoint then break @@ -210,8 +210,8 @@ mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, u q += y/w; } else { // Sinh-Sinh - r = (r - ONE / r) / TWO; // = sinh(sinh(j*h)) - w = (w + ONE / w) / TWO; // = cosh(sinh(j*h)) + r = (r - ULAB_ONE / r) / ULAB_TWO; // = sinh(sinh(j*h)) + w = (w + ULAB_ONE / w) / ULAB_TWO; // = cosh(sinh(j*h)) x = c - d*r; y = integrate_python_call(type, fun, x, fargs, 0); if (isfinite(y)) // if f(x) is finite, add to local sum @@ -221,7 +221,7 @@ mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, u y = integrate_python_call(type, fun, x, fargs, 0); if (isfinite(y)) // if f(x) is finite, add to local sum q += y*w; - q *= t + POINT_TWO_FIVE / t; // q *= cosh(j*h) + q *= t + ULAB_POINT_TWO_FIVE / t; // q *= cosh(j*h) p += q; t *= eh; } while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p)); @@ -319,12 +319,12 @@ mp_float_t qromb(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint for (i = 1; i < n; ++i) { unsigned long long k = 1UL << i; unsigned long long s = 1; - mp_float_t sum = ZERO; + mp_float_t sum = ULAB_ZERO; mp_float_t *Rt; - h /= TWO; + h /= ULAB_TWO; for (j = 1; j < k; j += 2) sum += integrate_python_call(type, fun, a+j*h, fargs, 0); - Ru[0] = h*sum + Ro[0] / TWO; + Ru[0] = h*sum + Ro[0] / ULAB_TWO; for (j = 1; j <= i; ++j) { s <<= 2; Ru[j] = (s*Ru[j-1] - Ro[j-1])/(s-1); @@ -408,17 +408,17 @@ mp_float_t as(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, mp_floa mp_float_t fb, mp_float_t v, mp_float_t eps, int n, mp_float_t t) { const mp_obj_type_t *type = mp_obj_get_type(fun); mp_obj_t fargs[1]; - mp_float_t h = (b-a) / TWO; - mp_float_t f1 = integrate_python_call(type, fun, a + h / TWO, fargs, 0); - mp_float_t f2 = integrate_python_call(type, fun, b - h / TWO, fargs, 0); - mp_float_t sl = h*(fa + FOUR * f1 + fm) / SIX; - mp_float_t sr = h*(fm + FOUR * f2 + fb) / SIX; + mp_float_t h = (b-a) / ULAB_TWO; + mp_float_t f1 = integrate_python_call(type, fun, a + h / ULAB_TWO, fargs, 0); + mp_float_t f2 = integrate_python_call(type, fun, b - h / ULAB_TWO, fargs, 0); + mp_float_t sl = h*(fa + ULAB_FOUR * f1 + fm) / ULAB_SIX; + mp_float_t sr = h*(fm + ULAB_FOUR * f2 + fb) / ULAB_SIX; mp_float_t s = sl+sr; - mp_float_t d = (s-v) / FIFTEEN; + mp_float_t d = (s-v) / ULAB_FIFTEEN; mp_float_t m = a+h; if (n <= 0 || MICROPY_FLOAT_C_FUN(fabs)(d) < eps) return t + s + d; // note: fabs(d) can be used as error estimate - eps /= TWO; + eps /= ULAB_TWO; --n; t = as(fun, a, m, fa, f1, fm, sl, eps, n, t); return as(fun, m, b, fm, f2, fb, sr, eps, n, t); @@ -430,7 +430,7 @@ mp_float_t qasi(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n mp_float_t fa = integrate_python_call(type, fun, a, fargs, 0); mp_float_t fm = integrate_python_call(type, fun, (a+b)/2, fargs, 0); mp_float_t fb = integrate_python_call(type, fun, b, fargs, 0); - mp_float_t v = (fa + FOUR * fm + fb) * (b-a) / SIX; + mp_float_t v = (fa + ULAB_FOUR * fm + fb) * (b-a) / ULAB_SIX; return as(fun, a, b, fa, fm, fb, v, eps, n, 0); } @@ -562,8 +562,8 @@ mp_float_t gk(mp_float_t (*fun)(mp_float_t), mp_float_t c, mp_float_t d, mp_floa }; const mp_obj_type_t *type = mp_obj_get_type(fun); mp_obj_t fargs[1]; - mp_float_t p = ZERO; // kronrod quadrature sum - mp_float_t q = ZERO; // gauss quadrature sum + mp_float_t p = ULAB_ZERO; // kronrod quadrature sum + mp_float_t q = ULAB_ZERO; // gauss quadrature sum mp_float_t fp, fm; mp_float_t e; int i; @@ -581,24 +581,24 @@ mp_float_t gk(mp_float_t (*fun)(mp_float_t), mp_float_t c, mp_float_t d, mp_floa p += (fp + fm) * weights[i]; } *err = MICROPY_FLOAT_C_FUN(fabs)(p - q); - e = MICROPY_FLOAT_C_FUN(fabs)(2 * p * MACHEPS); // optional, to take 1e-17 MachEps prec. into account + e = MICROPY_FLOAT_C_FUN(fabs)(2 * p * ULAB_MACHEPS); // optional, to take 1e-17 MachEps prec. into account if (*err < e) *err = e; return p; } mp_float_t qakro(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t tol, mp_float_t eps, mp_float_t *err) { - mp_float_t c = (a+b) / TWO; - mp_float_t d = (b-a) / TWO; + mp_float_t c = (a+b) / ULAB_TWO; + mp_float_t d = (b-a) / ULAB_TWO; mp_float_t e; mp_float_t r = gk(fun, c, d, &e); mp_float_t s = d*r; mp_float_t t = MICROPY_FLOAT_C_FUN(fabs)(s*tol); - if (tol == ZERO) + if (tol == ULAB_ZERO) tol = t; if (n > 0 && t < e && tol < e) { - s = qakro(fun, a, c, n-1, t / TWO, eps, err); - s += qakro(fun, c, b, n-1, t / TWO, eps, &e); + s = qakro(fun, a, c, n-1, t / ULAB_TWO, eps, err); + s += qakro(fun, c, b, n-1, t / ULAB_TWO, eps, &e); *err += e; return s; } diff --git a/code/ulab.c b/code/ulab.c index f80bf620..6858f41d 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.3 +#define ULAB_VERSION 6.7.4 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 11fd65e6..fb6c5aa1 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Sun, 16 Mar 2025 + +version 6.7.4 + + re-name integration constants to avoid name clash with EPS ports + Sun, 26 Jan 2025 version 6.7.3 From 88ef8935406f5a297dde66d74f2edd5d19bd6e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 29 May 2025 19:16:43 +0200 Subject: [PATCH 105/113] Random fix (#718) * fix typo and shape in radnom module * add random test files * fix expected file --- code/ndarray.c | 3 +++ code/numpy/random/random.c | 33 ++++++++------------------------- docs/ulab-change-log.md | 5 +++++ tests/2d/numpy/random.py | 10 ++++++++++ tests/2d/numpy/random.py.exp | 3 +++ 5 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 tests/2d/numpy/random.py create mode 100644 tests/2d/numpy/random.py.exp diff --git a/code/ndarray.c b/code/ndarray.c index 26ced6fc..96750735 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -563,6 +563,9 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dtype) { // creates a dense array from a tuple // the function should work in the general n-dimensional case + if(_shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); for(size_t i = 0; i < _shape->len; i++) { shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[_shape->len - 1 - i]); diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 165f11b5..b660f084 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -57,7 +57,7 @@ const mp_obj_type_t random_generator_type = { void random_generator_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; random_generator_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(MP_PYTHON_PRINTER, "Gnerator() at 0x%p", self); + mp_printf(MP_PYTHON_PRINTER, "Generator() at 0x%p", self); } mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -149,12 +149,9 @@ static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t ndarray = ndarray_new_linear_array((size_t)mp_obj_get_int(size), NDARRAY_FLOAT); } else if(mp_obj_is_type(size, &mp_type_tuple)) { mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); - if(_shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); - } ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); } else { // input type not supported - mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers")); } } else { // return single value @@ -221,27 +218,16 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_obj_t out = args[2].u_obj; ndarray_obj_t *ndarray = NULL; - size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - uint8_t ndim = 1; + size_t *shape = m_new0(size_t, ULAB_MAX_DIMS); if(size != mp_const_none) { if(mp_obj_is_int(size)) { shape[ULAB_MAX_DIMS - 1] = (size_t)mp_obj_get_int(size); } else if(mp_obj_is_type(size, &mp_type_tuple)) { mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); - if(_shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); - } - ndim = _shape->len; - for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { - if(i >= ndim) { - shape[ULAB_MAX_DIMS - 1 - i] = 0; - } else { - shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]); - } - } + ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); } else { // input type not supported - mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers")); } } @@ -267,7 +253,8 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t } } else { // out == None if(size != mp_const_none) { - ndarray = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); } else { // return single value mp_float_t value; @@ -336,13 +323,9 @@ static mp_obj_t random_uniform(size_t n_args, const mp_obj_t *pos_args, mp_map_t return mp_obj_new_float(value); } else if(mp_obj_is_type(size, &mp_type_tuple)) { mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); - // TODO: this could be reduced, if the inspection was in the ndarray_new_ndarray_from_tuple function - if(_shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); - } ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); } else { // input type not supported - mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers")); } mp_float_t *array = (mp_float_t *)ndarray->array; diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index fb6c5aa1..2a06dda0 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,8 @@ +Thu, 29 May 2025 + +version 6.7.5 + + fix typo and shape in radnom module Sun, 16 Mar 2025 version 6.7.4 diff --git a/tests/2d/numpy/random.py b/tests/2d/numpy/random.py new file mode 100644 index 00000000..f1d67c9f --- /dev/null +++ b/tests/2d/numpy/random.py @@ -0,0 +1,10 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +rng = np.random.Generator(1234) + +for generator in (rng.normal, rng.random, rng.uniform): + random_array = generator(size=(1, 2)) + print("array shape:", random_array.shape) \ No newline at end of file diff --git a/tests/2d/numpy/random.py.exp b/tests/2d/numpy/random.py.exp new file mode 100644 index 00000000..65d7ebaf --- /dev/null +++ b/tests/2d/numpy/random.py.exp @@ -0,0 +1,3 @@ +array shape: (1, 2) +array shape: (1, 2) +array shape: (1, 2) From 1398a8606f6fa78b2e772595b39b8667f6158b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 6 Jun 2025 20:58:31 +0200 Subject: [PATCH 106/113] loadtxt can deal with multi-line comments (#720) * loadtxt can deal with multi-line comments * multiline headers/footers are treated correctly * add macro to traverse arrays --- code/ndarray.c | 232 +++++++---------------------- code/ndarray.h | 85 +++++++++++ code/numpy/compare.c | 50 +------ code/numpy/io/io.c | 103 ++++++------- code/numpy/poly.c | 39 +---- code/numpy/vector.c | 322 ++++++---------------------------------- code/ulab.c | 2 +- docs/ulab-change-log.md | 7 + 8 files changed, 245 insertions(+), 595 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 96750735..04827706 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -586,43 +586,10 @@ void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target, uint8_t sh } #endif - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - memcpy(tarray, sarray, target->itemsize); - tarray += target->itemsize; - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif + ITERATOR_HEAD(); + memcpy(tarray, sarray, target->itemsize); + tarray += target->itemsize; + ITERATOR_TAIL(source, sarray); } ndarray_obj_t *ndarray_new_view(ndarray_obj_t *source, uint8_t ndim, size_t *shape, int32_t *strides, int32_t offset) { @@ -676,69 +643,36 @@ ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *source, uint8_t dty uint8_t complex_size = 2 * sizeof(mp_float_t); #endif - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { + ITERATOR_HEAD() + mp_obj_t item; + #if ULAB_SUPPORTS_COMPLEX + if(source->dtype == NDARRAY_COMPLEX) { + if(dtype != NDARRAY_COMPLEX) { + mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex type")); + } else { + memcpy(array, sarray, complex_size); + } + } else { #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_obj_t item; - #if ULAB_SUPPORTS_COMPLEX - if(source->dtype == NDARRAY_COMPLEX) { - if(dtype != NDARRAY_COMPLEX) { - mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex type")); - } else { - memcpy(array, sarray, complex_size); - } - } else { - #endif - if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) { - // floats must be treated separately, because they can't directly be converted to integer types - mp_float_t f = ndarray_get_float_value(sarray, source->dtype); - item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(f)); - } else { - item = mp_binary_get_val_array(source->dtype, sarray, 0); - } - #if ULAB_SUPPORTS_COMPLEX - if(dtype == NDARRAY_COMPLEX) { - ndarray_set_value(NDARRAY_FLOAT, array, 0, item); - } else { - ndarray_set_value(dtype, array, 0, item); - } - } - #else - ndarray_set_value(dtype, array, 0, item); - #endif - array += ndarray->itemsize; - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); + if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) { + // floats must be treated separately, because they can't directly be converted to integer types + mp_float_t f = ndarray_get_float_value(sarray, source->dtype); + item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(f)); + } else { + item = mp_binary_get_val_array(source->dtype, sarray, 0); + } + #if ULAB_SUPPORTS_COMPLEX + if(dtype == NDARRAY_COMPLEX) { + ndarray_set_value(NDARRAY_FLOAT, array, 0, item); + } else { + ndarray_set_value(dtype, array, 0, item); + } + } + #else + ndarray_set_value(dtype, array, 0, item); #endif - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif + array += ndarray->itemsize; + ITERATOR_TAIL(source, sarray); return ndarray; } @@ -765,54 +699,21 @@ mp_obj_t ndarray_byteswap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return MP_OBJ_FROM_PTR(ndarray); } else { uint8_t *array = (uint8_t *)ndarray->array; - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - if(self->dtype == NDARRAY_FLOAT) { - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - SWAP(uint8_t, array[0], array[3]); - SWAP(uint8_t, array[1], array[2]); - #else - SWAP(uint8_t, array[0], array[7]); - SWAP(uint8_t, array[1], array[6]); - SWAP(uint8_t, array[2], array[5]); - SWAP(uint8_t, array[3], array[4]); - #endif - } else { - SWAP(uint8_t, array[0], array[1]); - } - array += ndarray->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; - array += ndarray->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]); + ITERATOR_HEAD(); + if(self->dtype == NDARRAY_FLOAT) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + SWAP(uint8_t, array[0], array[3]); + SWAP(uint8_t, array[1], array[2]); + #else + SWAP(uint8_t, array[0], array[7]); + SWAP(uint8_t, array[1], array[6]); + SWAP(uint8_t, array[2], array[5]); + SWAP(uint8_t, array[3], array[4]); #endif - #if ULAB_MAX_DIMS > 2 - array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; - array += ndarray->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; - array += ndarray->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]); - #endif + } else { + SWAP(uint8_t, array[0], array[1]); + } + ITERATOR_TAIL(ndarray, array); } return MP_OBJ_FROM_PTR(ndarray); } @@ -1441,43 +1342,10 @@ mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a uint8_t *array = (uint8_t *)ndarray->array; if(memcmp(order, "C", 1) == 0) { // C-type ordering - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - memcpy(array, sarray, self->itemsize); - array += ndarray->strides[ULAB_MAX_DIMS - 1]; - sarray += self->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < self->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= self->strides[ULAB_MAX_DIMS - 1] * self->shape[ULAB_MAX_DIMS-1]; - sarray += self->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < self->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - sarray -= self->strides[ULAB_MAX_DIMS - 2] * self->shape[ULAB_MAX_DIMS-2]; - sarray += self->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < self->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - sarray -= self->strides[ULAB_MAX_DIMS - 3] * self->shape[ULAB_MAX_DIMS-3]; - sarray += self->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < self->shape[ULAB_MAX_DIMS - 4]); - #endif + ITERATOR_HEAD(); + memcpy(array, sarray, self->itemsize); + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + ITERATOR_TAIL(self, sarray); } else { // 'F', Fortran-type ordering #if ULAB_MAX_DIMS > 3 size_t i = 0; diff --git a/code/ndarray.h b/code/ndarray.h index 3e82b385..d027134e 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -709,4 +709,89 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); #endif /* ULAB_MAX_DIMS == 4 */ #endif /* ULAB_HAS_FUNCTION_ITERATOR */ + +// iterator macro for traversing arrays over all dimensions +#if ULAB_MAX_DIMS == 1 +#define ITERATOR_HEAD()\ + size_t _l_ = 0;\ + do { + +#define ITERATOR_TAIL(_source_, _source_array_)\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ + } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]); + +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define ITERATOR_HEAD()\ + size_t _k_ = 0;\ + do {\ + size_t _l_ = 0;\ + do { + +#define ITERATOR_TAIL(_source_, _source_array_)\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ + } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\ + _k_++;\ + } while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]); +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define ITERATOR_HEAD()\ + size_t _j_ = 0;\ + do {\ + size_t _k_ = 0;\ + do {\ + size_t _l_ = 0;\ + do { + +#define ITERATOR_TAIL(_source_, _source_array_)\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ + } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\ + _k_++;\ + } while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (_source_)->shape[ULAB_MAX_DIMS - 3]); + +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define ITERATOR_HEAD()\ + size_t _i_ = 0;\ + do {\ + size_t _j_ = 0;\ + do {\ + size_t _k_ = 0;\ + do {\ + size_t _l_ = 0;\ + do { + +#define ITERATOR_TAIL(_source_, _source_array_)\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ + } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\ + _k_++;\ + } while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\ + _j_++;\ + } while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 3] * (_source_)->shape[ULAB_MAX_DIMS - 3];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 4];\ + _i_++;\ + } while(_i_ < (_source_)->shape[ULAB_MAX_DIMS - 4]); +#endif /* ULAB_MAX_DIMS == 4 */ + + #endif diff --git a/code/numpy/compare.c b/code/numpy/compare.c index b2762e41..5845a9dd 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -260,48 +260,14 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { } uint8_t *xarray = (uint8_t *)x->array; - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t value = *(mp_float_t *)xarray; - if(isnan(value)) { - *rarray++ = 0; - } else { - *rarray++ = isinf(value) ? mask : 1 - mask; - } - xarray += x->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < x->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - xarray -= x->strides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1]; - xarray += x->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < x->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - xarray -= x->strides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2]; - xarray += x->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < x->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - xarray -= x->strides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3]; - xarray += x->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < x->shape[ULAB_MAX_DIMS - 4]); - #endif - + ITERATOR_HEAD(); + mp_float_t value = *(mp_float_t *)xarray; + if(isnan(value)) { + *rarray++ = 0; + } else { + *rarray++ = isinf(value) ? mask : 1 - mask; + } + ITERATOR_TAIL(x, xarray); return MP_OBJ_FROM_PTR(results); } else { mp_raise_TypeError(MP_ERROR_TEXT("wrong input type")); diff --git a/code/numpy/io/io.c b/code/numpy/io/io.c index 95e62adc..6e72ff8a 100644 --- a/code/numpy/io/io.c +++ b/code/numpy/io/io.c @@ -338,7 +338,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw buffer[read] = '\0'; offset = buffer; while(*offset != '\0') { - if(*offset == comment_char) { + while(*offset == comment_char) { // clear the line till the end, or the buffer's end while((*offset != '\0')) { offset++; @@ -425,7 +425,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw offset = buffer; while(*offset != '\0') { - if(*offset == comment_char) { + while(*offset == comment_char) { // clear the line till the end, or the buffer's end while((*offset != '\0')) { offset++; @@ -619,48 +619,14 @@ static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) { uint8_t *array = (uint8_t *)ndarray->array; - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - memcpy(buffer+offset, array, sz); - offset += sz; - if(offset == ULAB_IO_BUFFER_SIZE) { - stream_p->write(stream, buffer, offset, &error); - offset = 0; - } - array += ndarray->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; - array += ndarray->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; - array += ndarray->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; - array += ndarray->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]); - #endif - + ITERATOR_HEAD(); + memcpy(buffer+offset, array, sz); + offset += sz; + if(offset == ULAB_IO_BUFFER_SIZE) { + stream_p->write(stream, buffer, offset, &error); + offset = 0; + } + ITERATOR_TAIL(ndarray, array); stream_p->write(stream, buffer, offset, &error); stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); @@ -751,16 +717,32 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE); int error; + size_t len_comment; + char *comments; + + if(mp_obj_is_str(args[5].u_obj)) { + const char *_comments = mp_obj_str_get_data(args[5].u_obj, &len_comment); + comments = (char *)_comments; + } else { + len_comment = 2; + comments = m_new(char, len_comment); + comments[0] = '#'; + comments[1] = ' '; + } + if(mp_obj_is_str(args[3].u_obj)) { size_t _len; - if(mp_obj_is_str(args[5].u_obj)) { - const char *comments = mp_obj_str_get_data(args[5].u_obj, &_len); - stream_p->write(stream, comments, _len, &error); - } else { - stream_p->write(stream, "# ", 2, &error); - } const char *header = mp_obj_str_get_data(args[3].u_obj, &_len); - stream_p->write(stream, header, _len, &error); + + stream_p->write(stream, comments, len_comment, &error); + + // We can't write the header in the single chunk, for it might contain line breaks + for(size_t i = 0; i < _len; header++, i++) { + stream_p->write(stream, header, 1, &error); + if((*header == '\n') && (i < _len)) { + stream_p->write(stream, comments, len_comment, &error); + } + } stream_p->write(stream, "\n", 1, &error); } @@ -799,16 +781,19 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]); #endif - if(mp_obj_is_str(args[4].u_obj)) { + if(mp_obj_is_str(args[4].u_obj)) { // footer string size_t _len; - if(mp_obj_is_str(args[5].u_obj)) { - const char *comments = mp_obj_str_get_data(args[5].u_obj, &_len); - stream_p->write(stream, comments, _len, &error); - } else { - stream_p->write(stream, "# ", 2, &error); - } const char *footer = mp_obj_str_get_data(args[4].u_obj, &_len); - stream_p->write(stream, footer, _len, &error); + + stream_p->write(stream, comments, len_comment, &error); + + // We can't write the header in the single chunk, for it might contain line breaks + for(size_t i = 0; i < _len; footer++, i++) { + stream_p->write(stream, footer, 1, &error); + if((*footer == '\n') && (i < _len)) { + stream_p->write(stream, comments, len_comment, &error); + } + } stream_p->write(stream, "\n", 1, &error); } diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 8b8e1435..91fc01f1 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -197,42 +197,9 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { // TODO: these loops are really nothing, but the re-impplementation of // ITERATE_VECTOR from vectorise.c. We could pass a function pointer here - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - *array++ = poly_eval(func(sarray), p, plen); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif + ITERATOR_HEAD(); + *array++ = poly_eval(func(sarray), p, plen); + ITERATOR_TAIL(source, sarray); } else { // o_x had better be a one-dimensional standard iterable ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT); diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 95e2e207..d17bd850 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -91,43 +91,10 @@ static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, m mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t value = func(sarray); - *tarray++ = f(value); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif /* ULAB_MAX_DIMS > 1 */ - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif /* ULAB_MAX_DIMS > 2 */ - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif /* ULAB_MAX_DIMS > 3 */ + ITERATOR_HEAD(); + mp_float_t value = func(sarray); + *tarray++ = f(value); + ITERATOR_TAIL(source, sarray); #else if(source->dtype == NDARRAY_UINT8) { ITERATE_VECTOR(uint8_t, target, tarray, tstrides, source, sarray); @@ -171,43 +138,10 @@ static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t) mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t value = func(sarray); - *array++ = f(value); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif /* ULAB_MAX_DIMS > 1 */ - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif /* ULAB_MAX_DIMS > 2 */ - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif /* ULAB_MAX_DIMS > 3 */ + ITERATOR_HEAD(); + mp_float_t value = func(sarray); + *array++ = f(value); + ITERATOR_TAIL(source, sarray); #else if(source->dtype == NDARRAY_UINT8) { ITERATE_VECTOR(uint8_t, array, source, sarray); @@ -327,43 +261,11 @@ mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t f = func(sarray); - *narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul; - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif + ITERATOR_HEAD(); + mp_float_t f = func(sarray); + *narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul; + ITERATOR_TAIL(source, sarray); + return MP_OBJ_FROM_PTR(ndarray); } @@ -631,46 +533,13 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { mp_float_t *array = (mp_float_t *)ndarray->array; uint8_t itemsize = sizeof(mp_float_t); - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t real = *(mp_float_t *)sarray; - mp_float_t imag = *(mp_float_t *)(sarray + itemsize); - mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); - *array++ = exp_real * MICROPY_FLOAT_C_FUN(cos)(imag); - *array++ = exp_real * MICROPY_FLOAT_C_FUN(sin)(imag); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif /* ULAB_MAX_DIMS > 1 */ - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif /* ULAB_MAX_DIMS > 2 */ - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif /* ULAB_MAX_DIMS > 3 */ + ITERATOR_HEAD(); + mp_float_t real = *(mp_float_t *)sarray; + mp_float_t imag = *(mp_float_t *)(sarray + itemsize); + mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); + *array++ = exp_real * MICROPY_FLOAT_C_FUN(cos)(imag); + *array++ = exp_real * MICROPY_FLOAT_C_FUN(sin)(imag); + ITERATOR_TAIL(source, sarray); return MP_OBJ_FROM_PTR(ndarray); } } @@ -921,97 +790,33 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) mp_float_t *array = (mp_float_t *)ndarray->array; uint8_t itemsize = sizeof(mp_float_t); - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t real = *(mp_float_t *)sarray; - mp_float_t imag = *(mp_float_t *)(sarray + itemsize); - mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag); - sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs); - mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real); - *array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta); - *array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif /* ULAB_MAX_DIMS > 1 */ - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif /* ULAB_MAX_DIMS > 2 */ - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif /* ULAB_MAX_DIMS > 3 */ + ITERATOR_HEAD(); + mp_float_t real = *(mp_float_t *)sarray; + mp_float_t imag = *(mp_float_t *)(sarray + itemsize); + mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag); + sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs); + mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real); + *array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta); + *array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta); + ITERATOR_TAIL(source, sarray); + return MP_OBJ_FROM_PTR(ndarray); } else if(source->dtype == NDARRAY_FLOAT) { uint8_t *sarray = (uint8_t *)source->array; ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX); mp_float_t *array = (mp_float_t *)ndarray->array; - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - mp_float_t value = *(mp_float_t *)sarray; - if(value >= MICROPY_FLOAT_CONST(0.0)) { - *array++ = MICROPY_FLOAT_C_FUN(sqrt)(value); - array++; - } else { - array++; - *array++ = MICROPY_FLOAT_C_FUN(sqrt)(-value); - } - sarray += source->strides[ULAB_MAX_DIMS - 1]; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif /* ULAB_MAX_DIMS > 1 */ - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif /* ULAB_MAX_DIMS > 2 */ - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif /* ULAB_MAX_DIMS > 3 */ + ITERATOR_HEAD(); + mp_float_t value = *(mp_float_t *)sarray; + if(value >= MICROPY_FLOAT_CONST(0.0)) { + *array++ = MICROPY_FLOAT_C_FUN(sqrt)(value); + array++; + } else { + array++; + *array++ = MICROPY_FLOAT_C_FUN(sqrt)(-value); + } + ITERATOR_TAIL(source, sarray); + return MP_OBJ_FROM_PTR(ndarray); } else { mp_raise_TypeError(MP_ERROR_TEXT("input dtype must be float or complex")); @@ -1071,45 +876,12 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, uint8_t *sarray = (uint8_t *)source->array; uint8_t *narray = (uint8_t *)ndarray->array; - #if ULAB_MAX_DIMS > 3 - size_t i = 0; - do { - #endif - #if ULAB_MAX_DIMS > 2 - size_t j = 0; - do { - #endif - #if ULAB_MAX_DIMS > 1 - size_t k = 0; - do { - #endif - size_t l = 0; - do { - avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0); - fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue); - ndarray_set_value(self->otypes, narray, 0, fvalue); - sarray += source->strides[ULAB_MAX_DIMS - 1]; - narray += ndarray->itemsize; - l++; - } while(l < source->shape[ULAB_MAX_DIMS - 1]); - #if ULAB_MAX_DIMS > 1 - sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS - 1]; - sarray += source->strides[ULAB_MAX_DIMS - 2]; - k++; - } while(k < source->shape[ULAB_MAX_DIMS - 2]); - #endif /* ULAB_MAX_DIMS > 1 */ - #if ULAB_MAX_DIMS > 2 - sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS - 2]; - sarray += source->strides[ULAB_MAX_DIMS - 3]; - j++; - } while(j < source->shape[ULAB_MAX_DIMS - 3]); - #endif /* ULAB_MAX_DIMS > 2 */ - #if ULAB_MAX_DIMS > 3 - sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS - 3]; - sarray += source->strides[ULAB_MAX_DIMS - 4]; - i++; - } while(i < source->shape[ULAB_MAX_DIMS - 4]); - #endif /* ULAB_MAX_DIMS > 3 */ + ITERATOR_HEAD(); + avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0); + fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue); + ndarray_set_value(self->otypes, narray, 0, fvalue); + narray += ndarray->itemsize; + ITERATOR_TAIL(source, sarray); return MP_OBJ_FROM_PTR(ndarray); } else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) || diff --git a/code/ulab.c b/code/ulab.c index 6858f41d..bd420ae8 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.4 +#define ULAB_VERSION 6.7.6 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 2a06dda0..b96b51db 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,8 +1,15 @@ Thu, 29 May 2025 +version 6.7.6 + + loadtxt can deal with multi-line comments + +Thu, 29 May 2025 + version 6.7.5 fix typo and shape in radnom module + Sun, 16 Mar 2025 version 6.7.4 From 844f6e5e1e5338b46516c5c798709500da3adf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 6 Jun 2025 21:29:41 +0200 Subject: [PATCH 107/113] fix type inference * fix type inference * make checks inclusive for negative values of integers * move to newer version of ubuntu * fix iterator macros * fix iterator macros --- .github/workflows/build.yml | 4 ++-- code/ndarray.c | 4 ++-- code/ndarray.h | 26 +++++++++++++------------- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c0477efb..2ca9beba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: os: - - ubuntu-20.04 + - ubuntu-24.04 - macOS-latest dims: [1, 2, 3, 4] runs-on: ${{ matrix.os }} @@ -61,7 +61,7 @@ jobs: strategy: matrix: os: - - ubuntu-20.04 + - ubuntu-24.04 - macOS-latest dims: [1, 2, 3, 4] runs-on: ${{ matrix.os }} diff --git a/code/ndarray.c b/code/ndarray.c index 04827706..72a82b2d 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1482,7 +1482,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { if(mp_obj_is_int(obj)) { int32_t ivalue = mp_obj_get_int(obj); - if((ivalue < -32767) || (ivalue > 32767)) { + if((ivalue < -32768) || (ivalue > 65535)) { // the integer value clearly does not fit the ulab integer types, so move on to float ndarray = ndarray_new_linear_array(1, NDARRAY_FLOAT); mp_float_t *array = (mp_float_t *)ndarray->array; @@ -1490,7 +1490,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { } else { uint8_t dtype; if(ivalue < 0) { - if(ivalue > -128) { + if(ivalue >= -128) { dtype = NDARRAY_INT8; } else { dtype = NDARRAY_INT16; diff --git a/code/ndarray.h b/code/ndarray.h index d027134e..6e24b24a 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -731,8 +731,8 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); do { #define ITERATOR_TAIL(_source_, _source_array_)\ - (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ - _l_++;\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\ (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\ (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\ @@ -750,17 +750,17 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); do { #define ITERATOR_TAIL(_source_, _source_array_)\ - (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ - _l_++;\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\ (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\ (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\ _k_++;\ } while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\ - (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\ - (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\ - j++;\ - } while(j < (_source_)->shape[ULAB_MAX_DIMS - 3]); + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\ + _j_++;\ + } while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]); #endif /* ULAB_MAX_DIMS == 3 */ @@ -776,16 +776,16 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); do { #define ITERATOR_TAIL(_source_, _source_array_)\ - (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ - _l_++;\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\ + _l_++;\ } while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\ (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\ (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\ _k_++;\ } while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\ - (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\ - (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\ - _j_++;\ + (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\ + (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\ + _j_++;\ } while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);\ (_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 3] * (_source_)->shape[ULAB_MAX_DIMS - 3];\ (_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 4];\ diff --git a/code/ulab.c b/code/ulab.c index bd420ae8..34222e58 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.6 +#define ULAB_VERSION 6.7.7 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index b96b51db..3f9bda9f 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Fri, 06 Jun 2025 + +version 6.7.7 + + fix ndarray type inference for micropython objects + Thu, 29 May 2025 version 6.7.6 From 66e9eb3ed3115b0371a9ec7499b3ce6c2878e24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 6 Jun 2025 21:46:21 +0200 Subject: [PATCH 108/113] add ndim property (#725) --- code/ndarray.c | 7 +++++++ code/ndarray.h | 4 ++++ code/ndarray_properties.c | 7 ++++++- code/ndarray_properties.h | 6 +++++- code/ulab.c | 2 +- code/ulab.h | 4 ++++ 6 files changed, 27 insertions(+), 3 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 72a82b2d..489be2a8 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1398,6 +1398,13 @@ mp_obj_t ndarray_itemsize(mp_obj_t self_in) { } #endif +#if NDARRAY_HAS_NDIM +mp_obj_t ndarray_ndim(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->ndim); +} +#endif + #if NDARRAY_HAS_SHAPE mp_obj_t ndarray_shape(mp_obj_t self_in) { ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); diff --git a/code/ndarray.h b/code/ndarray.h index 6e24b24a..8e81773d 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -232,6 +232,10 @@ mp_obj_t ndarray_dtype(mp_obj_t ); mp_obj_t ndarray_itemsize(mp_obj_t ); #endif +#if NDARRAY_HAS_NDIM +mp_obj_t ndarray_ndim(mp_obj_t ); +#endif + #if NDARRAY_HAS_SIZE mp_obj_t ndarray_size(mp_obj_t ); #endif diff --git a/code/ndarray_properties.c b/code/ndarray_properties.c index aa297158..6c048bda 100644 --- a/code/ndarray_properties.c +++ b/code/ndarray_properties.c @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Zoltán Vörös + * Copyright (c) 2021-2025 Zoltán Vörös * */ @@ -42,6 +42,11 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = ndarray_itemsize(self_in); break; #endif + #if NDARRAY_HAS_NDIM + case MP_QSTR_ndim: + dest[0] = ndarray_ndim(self_in); + break; + #endif #if NDARRAY_HAS_SHAPE case MP_QSTR_shape: dest[0] = ndarray_shape(self_in); diff --git a/code/ndarray_properties.h b/code/ndarray_properties.h index 3e0b9a40..aa2e15ad 100644 --- a/code/ndarray_properties.h +++ b/code/ndarray_properties.h @@ -7,7 +7,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Jeff Epler for Adafruit Industries - * 2020-2021 Zoltán Vörös + * 2020-2025 Zoltán Vörös */ #ifndef _NDARRAY_PROPERTIES_ @@ -36,6 +36,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_n MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize); #endif +#if NDARRAY_HAS_NDIM +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_ndim_obj, ndarray_ndim); +#endif + #if NDARRAY_HAS_SHAPE MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape); #endif diff --git a/code/ulab.c b/code/ulab.c index 34222e58..fb42f6a8 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.7.7 +#define ULAB_VERSION 6.8.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 3eb30131..38d46903 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -245,6 +245,10 @@ #define NDARRAY_HAS_ITEMSIZE (1) #endif +#ifndef NDARRAY_HAS_NDIM +#define NDARRAY_HAS_NDIM (1) +#endif + #ifndef NDARRAY_HAS_RESHAPE #define NDARRAY_HAS_RESHAPE (1) #endif From 8eb8eaf5a19f5ed3a2e2193ba6e727d7518458a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 6 Jun 2025 21:47:50 +0200 Subject: [PATCH 109/113] update changelog; will push directly into master --- docs/ulab-change-log.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 3f9bda9f..48f0adea 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,5 +1,11 @@ Fri, 06 Jun 2025 +version 6.8.0 + + expose ndim property + +Fri, 06 Jun 2025 + version 6.7.7 fix ndarray type inference for micropython objects From a0999aba795ea63420790da3e8a39f9fb9cce4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 4 Aug 2025 22:55:02 +0200 Subject: [PATCH 110/113] add modulo operator (#733) * add modulo operator * fix modulo loops * add in-place modulo operator * update readme --- README.md | 2 +- code/ndarray.c | 12 +++ code/ndarray_operators.c | 122 +++++++++++++++++++++++++++ code/ndarray_operators.h | 175 +++++++++++++++++++++++++++++++++++++++ code/ulab.c | 2 +- code/ulab.h | 8 ++ docs/ulab-ndarray.ipynb | 2 +- 7 files changed, 320 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 063c26d1..72300f32 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ detected and handled. ## ndarray methods `ulab` implements `numpy`'s `ndarray` with the `==`, `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `/`, `*`, `**`, -`+=`, `-=`, `*=`, `/=`, `**=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that +`%`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that operate element-wise. Type-aware `ndarray`s can be initialised from any `micropython` iterable, lists of iterables via the `array` constructor, or by means of the `arange`, `concatenate`, `diag`, `eye`, `frombuffer`, `full`, `linspace`, `logspace`, `ones`, or `zeros` functions. diff --git a/code/ndarray.c b/code/ndarray.c index 489be2a8..b297b3f1 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1648,6 +1648,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { return ndarray_inplace_ams(lhs, rhs, rstrides, op); break; #endif + #if NDARRAY_HAS_INPLACE_MODULO + case MP_BINARY_OP_INPLACE_MODULO: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); + return ndarray_inplace_modulo(lhs, rhs, rstrides); + break; + #endif #if NDARRAY_HAS_INPLACE_MULTIPLY case MP_BINARY_OP_INPLACE_MULTIPLY: COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); @@ -1703,6 +1709,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { return ndarray_binary_add(lhs, rhs, ndim, shape, lstrides, rstrides); break; #endif + #if NDARRAY_HAS_BINARY_OP_MODULO + case MP_BINARY_OP_MODULO: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); + return ndarray_binary_modulo(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif #if NDARRAY_HAS_BINARY_OP_MULTIPLY case MP_BINARY_OP_MULTIPLY: return ndarray_binary_multiply(lhs, rhs, ndim, shape, lstrides, rstrides); diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index e8de4d48..878fbc00 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -248,6 +248,105 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } #endif /* NDARRAY_HAS_BINARY_OP_ADD */ +#if NDARRAY_HAS_BINARY_OP_MODULO +mp_obj_t ndarray_binary_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_MODULO */ + #if NDARRAY_HAS_BINARY_OP_MULTIPLY mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { @@ -1074,6 +1173,29 @@ mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rs } #endif /* NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT */ + +#if NDARRAY_HAS_INPLACE_MODULO +mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { + if((lhs->dtype != NDARRAY_FLOAT) && (rhs->dtype == NDARRAY_FLOAT)) { + mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type")); + } + if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + INLINE_MODULO_FLOAT_LOOP(lhs, uint8_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT8) { + INLINE_MODULO_FLOAT_LOOP(lhs, int8_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + INLINE_MODULO_FLOAT_LOOP(lhs, uint16_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + INLINE_MODULO_FLOAT_LOOP(lhs, int16_t, larray, rarray, rstrides); + } else { + INLINE_MODULO_FLOAT_LOOP(lhs, mp_float_t, larray, rarray, rstrides); + } + } + return MP_OBJ_FROM_PTR(lhs); +} +#endif /* NDARRAY_HAS_INPLACE_MODULO */ + #if NDARRAY_HAS_INPLACE_TRUE_DIVIDE mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { diff --git a/code/ndarray_operators.h b/code/ndarray_operators.h index f0f3c89d..5f3b5d9c 100644 --- a/code/ndarray_operators.h +++ b/code/ndarray_operators.h @@ -12,6 +12,7 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); mp_obj_t ndarray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_modulo(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); @@ -21,6 +22,7 @@ mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t ); +mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *, ndarray_obj_t *, int32_t *); mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *); mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *); @@ -537,3 +539,176 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *); } while(0) #endif /* ULAB_MAX_DIMS == 4 */ + +#define MODULO_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\ +({\ + size_t l = 0;\ + do {\ + *(array)++ = MICROPY_FLOAT_C_FUN(fmod)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + +#if ULAB_MAX_DIMS == 1 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t l = 0;\ + do {\ + MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 4 */ + + +#define INPLACE_MODULO_FLOAT1(results, type_right, larray, rarray, rstrides)\ +({\ + size_t l = 0;\ + do {\ + *((mp_float_t *)larray) = MICROPY_FLOAT_C_FUN(fmod)(*((mp_float_t *)(larray)), *((type_right *)(rarray)));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + + +#if ULAB_MAX_DIMS == 1 +#define INPLACE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + + +#if ULAB_MAX_DIMS == 2 +#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + size_t l = 0;\ + do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 4 */ \ No newline at end of file diff --git a/code/ulab.c b/code/ulab.c index fb42f6a8..c1f32c0a 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.8.0 +#define ULAB_VERSION 6.9.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 38d46903..597cdef5 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -117,6 +117,10 @@ #define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1) #endif +#ifndef NDARRAY_HAS_BINARY_OP_MODULO +#define NDARRAY_HAS_BINARY_OP_MODULO (1) +#endif + #ifndef NDARRAY_HAS_BINARY_OP_MORE #define NDARRAY_HAS_BINARY_OP_MORE (1) #endif @@ -161,6 +165,10 @@ #define NDARRAY_HAS_INPLACE_ADD (1) #endif +#ifndef NDARRAY_HAS_INPLACE_MODULO +#define NDARRAY_HAS_INPLACE_MODU (1) +#endif + #ifndef NDARRAY_HAS_INPLACE_MULTIPLY #define NDARRAY_HAS_INPLACE_MULTIPLY (1) #endif diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index 8d67ed9b..c7518c26 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -2599,7 +2599,7 @@ "source": [ "# Binary operators\n", "\n", - "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n", + "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `%`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n", "\n", "Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", "A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n", From 068da5fc96e0118da3cff75bfcbcbb44139d61b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 5 Aug 2025 20:40:40 +0200 Subject: [PATCH 111/113] Modulo (#734) * add modulo operator * fix module loops * add in-place modulo operator * update readme * add test files, update documentation --- docs/manual/source/conf.py | 2 +- docs/manual/source/ulab-ndarray.rst | 12 ++-- docs/ulab-convert.ipynb | 8 +-- tests/2d/numpy/modulo.py | 26 +++++++ tests/2d/numpy/modulo.py.exp | 105 ++++++++++++++++++++++++++++ 5 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 tests/2d/numpy/modulo.py create mode 100644 tests/2d/numpy/modulo.py.exp diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index c525ee24..c10faeb2 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,7 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '6.7.3' +release = '6.9.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index 04036338..46288a26 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -1814,12 +1814,12 @@ array. Binary operators ================ -``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``<``, -``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, ``/=``, -``**=`` binary operators, as well as the ``AND``, ``OR``, ``XOR`` -bit-wise operators that work element-wise. Note that the bit-wise -operators will raise an exception, if either of the operands is of -``float`` or ``complex`` type. +``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``%``, +``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, +``/=``, ``**=``, ``%=`` binary operators, as well as the ``AND``, +``OR``, ``XOR`` bit-wise operators that work element-wise. Note that the +bit-wise operators will raise an exception, if either of the operands is +of ``float`` or ``complex`` type. Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index 1c7a8430..53f28bcb 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:15.118699Z", @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '6.7.3'\n", + "release = '6.9.0'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -217,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:21.647179Z", @@ -258,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:42.024028Z", diff --git a/tests/2d/numpy/modulo.py b/tests/2d/numpy/modulo.py new file mode 100644 index 00000000..b5171bae --- /dev/null +++ b/tests/2d/numpy/modulo.py @@ -0,0 +1,26 @@ +try: + from ulab import numpy as np +except: + import numpy as np + + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +for dtype1 in dtypes: + x1 = np.array(range(6), dtype=dtype1).reshape((2, 3)) + for dtype2 in dtypes: + x2 = np.array(range(1, 4), dtype=dtype2) + print(x1 % x2) + +print() +print('=' * 30) +print('inplace modulo') +print('=' * 30) +print() + +for dtype1 in dtypes: + x1 = np.array(range(6), dtype=dtype1).reshape((2, 3)) + for dtype2 in dtypes: + x2 = np.array(range(1, 4), dtype=dtype2) + x1 %= x2 + print(x1) diff --git a/tests/2d/numpy/modulo.py.exp b/tests/2d/numpy/modulo.py.exp new file mode 100644 index 00000000..73aa4633 --- /dev/null +++ b/tests/2d/numpy/modulo.py.exp @@ -0,0 +1,105 @@ +array([[0, 1, 2], + [0, 0, 2]], dtype=uint8) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0, 1, 2], + [0, 0, 2]], dtype=uint16) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0, 1, 2], + [0, 0, 2]], dtype=int8) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 0, 1], + [0, 2, 0]], dtype=uint8) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 1, 2], + [0, 0, 2]], dtype=uint16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) + +============================== +inplace modulo +============================== + +array([[0, 1, 2], + [0, 0, 2]], dtype=uint8) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0, 0, 1], + [0, 2, 0]], dtype=uint8) +array([[0, 0, 1], + [0, 0, 0]], dtype=int16) +array([[0.0, 0.0, 1.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 1.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 1.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0, 1, 2], + [0, 0, 2]], dtype=int16) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) +array([[0.0, 1.0, 2.0], + [0.0, 0.0, 2.0]], dtype=float64) From 11eefeaeb5318966e2604acb2daed1f6951a219a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 3 Sep 2025 20:27:26 +0200 Subject: [PATCH 112/113] Bincount (#738) * working skeletoon of bincount * integer arrays can be binned * add keyword handling * add tests, fix small glitches in code * add documentation * correct changelog --- code/numpy/compare.c | 132 ++++++++++++++++++++++++++++++++- code/numpy/compare.h | 3 +- code/numpy/numpy.c | 3 + code/ulab.c | 2 +- code/ulab.h | 6 +- docs/numpy-functions.ipynb | 70 +++++++++++++++-- docs/ulab-change-log.md | 6 ++ tests/2d/numpy/bincount.py | 82 ++++++++++++++++++++ tests/2d/numpy/bincount.py.exp | 41 ++++++++++ 9 files changed, 335 insertions(+), 10 deletions(-) create mode 100644 tests/2d/numpy/bincount.py create mode 100644 tests/2d/numpy/bincount.py.exp diff --git a/code/numpy/compare.c b/code/numpy/compare.c index 5845a9dd..6406cc44 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020-2021 Zoltán Vörös + * Copyright (c) 2020-2025 Zoltán Vörös * 2020 Jeff Epler for Adafruit Industries */ @@ -23,6 +23,136 @@ #include "carray/carray_tools.h" #include "compare.h" +#ifdef ULAB_NUMPY_HAS_BINCOUNT +mp_obj_t compare_bincount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } , + { MP_QSTR_weights, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_minlength, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray")); + } + ndarray_obj_t *input = MP_OBJ_TO_PTR(args[0].u_obj); + + #if ULAB_MAX_DIMS > 1 + // no need to check anything, if the maximum number of dimensions is 1 + if(input->ndim != 1) { + mp_raise_ValueError(MP_ERROR_TEXT("object too deep for desired array")); + } + #endif + if((input->dtype != NDARRAY_UINT8) && (input->dtype != NDARRAY_UINT16)) { + mp_raise_TypeError(MP_ERROR_TEXT("cannot cast array data from dtype")); + } + + // first find the maximum of the array, and figure out how long the result should be + size_t length = 0; + int32_t stride = input->strides[ULAB_MAX_DIMS - 1]; + if(input->dtype == NDARRAY_UINT8) { + uint8_t *iarray = (uint8_t *)input->array; + for(size_t i = 0; i < input->len; i++) { + if(*iarray > length) { + length = *iarray; + } + iarray += stride; + } + } else if(input->dtype == NDARRAY_UINT16) { + stride /= 2; + uint16_t *iarray = (uint16_t *)input->array; + for(size_t i = 0; i < input->len; i++) { + if(*iarray > length) { + length = *iarray; + } + iarray += stride; + } + } + length += 1; + + if(args[2].u_obj != mp_const_none) { + int32_t minlength = mp_obj_get_int(args[2].u_obj); + if(minlength < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("minlength must not be negative")); + } + if((size_t)minlength > length) { + length = minlength; + } + } else { + if(input->len == 0) { + length = 0; + } + } + + ndarray_obj_t *result = NULL; + ndarray_obj_t *weights = NULL; + + if(args[1].u_obj == mp_const_none) { + result = ndarray_new_linear_array(length, NDARRAY_UINT16); + } else { + if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray")); + } + weights = MP_OBJ_TO_PTR(args[1].u_obj); + if(weights->len < input->len) { + mp_raise_ValueError(MP_ERROR_TEXT("the weights and list don't have the same length")); + } + #if ULAB_SUPPORTS_COMPLEX + if(weights->dtype == NDARRAY_COMPLEX) { + mp_raise_TypeError(MP_ERROR_TEXT("cannot cast weigths to float")); + } + #endif /* ULAB_SUPPORTS_COMPLEX */ + + result = ndarray_new_linear_array(length, NDARRAY_FLOAT); + } + + // now we can do the binning + if(result->dtype == NDARRAY_UINT16) { + uint16_t *rarray = (uint16_t *)result->array; + if(input->dtype == NDARRAY_UINT8) { + uint8_t *iarray = (uint8_t *)input->array; + for(size_t i = 0; i < input->len; i++) { + rarray[*iarray] += 1; + iarray += stride; + } + } else if(input->dtype == NDARRAY_UINT16) { + uint16_t *iarray = (uint16_t *)input->array; + for(size_t i = 0; i < input->len; i++) { + rarray[*iarray] += 1; + iarray += stride; + } + } + } else { + mp_float_t *rarray = (mp_float_t *)result->array; + + mp_float_t (*get_weights)(void *) = ndarray_get_float_function(weights->dtype); + uint8_t *warray = (uint8_t *)weights->array; + + if(input->dtype == NDARRAY_UINT8) { + uint8_t *iarray = (uint8_t *)input->array; + for(size_t i = 0; i < input->len; i++) { + rarray[*iarray] += get_weights(warray); + iarray += stride; + warray += weights->strides[ULAB_MAX_DIMS - 1]; + } + } else if(input->dtype == NDARRAY_UINT16) { + uint16_t *iarray = (uint16_t *)input->array; + for(size_t i = 0; i < input->len; i++) { + rarray[*iarray] += get_weights(warray); + iarray += stride; + warray += weights->strides[ULAB_MAX_DIMS - 1]; + } + } + } + + return MP_OBJ_FROM_PTR(result); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(compare_bincount_obj, 1, compare_bincount); +#endif /* ULAB_NUMPY_HAS_BINCOUNT */ + static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) { ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0); ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0); diff --git a/code/numpy/compare.h b/code/numpy/compare.h index de3d7e65..4169a648 100644 --- a/code/numpy/compare.h +++ b/code/numpy/compare.h @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020-2021 Zoltán Vörös + * Copyright (c) 2020-2025 Zoltán Vörös */ #ifndef _COMPARE_ @@ -23,6 +23,7 @@ enum COMPARE_FUNCTION_TYPE { COMPARE_CLIP, }; +MP_DECLARE_CONST_FUN_OBJ_KW(compare_bincount_obj); MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj); MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj); MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index eafd7728..765f5d14 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -169,6 +169,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_ZEROS { MP_ROM_QSTR(MP_QSTR_zeros), MP_ROM_PTR(&create_zeros_obj) }, #endif + #if ULAB_NUMPY_HAS_BINCOUNT + { MP_ROM_QSTR(MP_QSTR_bincount), MP_ROM_PTR(&compare_bincount_obj) }, + #endif #if ULAB_NUMPY_HAS_CLIP { MP_ROM_QSTR(MP_QSTR_clip), MP_ROM_PTR(&compare_clip_obj) }, #endif diff --git a/code/ulab.c b/code/ulab.c index c1f32c0a..8a0b50ca 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.9.0 +#define ULAB_VERSION 6.10.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 597cdef5..ca234433 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -374,6 +374,10 @@ #endif // functions that compare arrays +#ifndef ULAB_NUMPY_HAS_BINCOUNT +#define ULAB_NUMPY_HAS_BINCOUNT (1) +#endif + #ifndef ULAB_NUMPY_HAS_CLIP #define ULAB_NUMPY_HAS_CLIP (1) #endif @@ -413,7 +417,7 @@ // the integrate module; functions of the integrate module still have // to be defined separately #ifndef ULAB_SCIPY_HAS_INTEGRATE_MODULE -#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1) +#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1) #endif #ifndef ULAB_INTEGRATE_HAS_TANHSINH diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index d13278cf..0c617bb1 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -238,6 +238,7 @@ "1. [numpy.argmin](#argmin)\n", "1. [numpy.argsort](#argsort)\n", "1. [numpy.asarray*](#asarray)\n", + "1. [numpy.bincount](#bincount)\n", "1. [numpy.bitwise_and](#bitwise_and)\n", "1. [numpy.bitwise_or](#bitwise_and)\n", "1. [numpy.bitwise_xor](#bitwise_and)\n", @@ -612,6 +613,68 @@ "print('a == c: {}'.format(a is c))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## bincount\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bincount.htm\n", + "\n", + "This method counts number of occurrences of each value in array of non-negative integers. A method accepts a single positional, and two keyword arguments, `weights`, and `minlength`. The positional arguments is assumed to be a one-dimensional array of unsigned integers that are either 8 or 16 bits wide. For other types, a `TypeError` is raised.\n", + "\n", + "The `weights` keyword argument can be supplied to apply weigths to the counts. In this case, the return value is a `float` array, otherwise, it is a `uint16`. The `weights` are assumed to be a one-dimensional `ndarray`, otherwise, a `TypeError` is raised. \n", + "\n", + "`minlength` can be supplied to give a lower bound to the length of the `ndarray` returned. In this case, the unused slots are filled with zeros." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n", + "bincount(a): array([1, 1, 1, 1, 1, 1, 1, 1], dtype=uint16)\n", + "\n", + "a: array([0, 0, 1, 1, 1, 2, 2, 2, 2], dtype=uint8)\n", + "bincount(a): array([2, 3, 4], dtype=uint16)\n", + "\n", + "a: array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n", + "w: array([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8], dtype=float64)\n", + "bincount(a, weights=w): array([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8], dtype=float64)\n", + "\n", + "a: array([0, 1, 2, 3], dtype=uint8)\n", + "bincount(a, minlength=8): array([1, 1, 1, 1, 0, 0, 0, 0], dtype=uint16)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(8), dtype=np.uint8)\n", + "\n", + "print(f'a: {a}\\nbincount(a): {np.bincount(a)}')\n", + "\n", + "a = np.array([0, 0, 1, 1, 1, 2, 2, 2, 2], dtype=np.uint8)\n", + "print(f'\\na: {a}\\nbincount(a): {np.bincount(a)}')\n", + "\n", + "a = np.array(range(8), dtype=np.uint8)\n", + "w = np.array([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8])\n", + "\n", + "print(f'\\na: {a}\\nw: {w}\\nbincount(a, weights=w): {np.bincount(a, weights=w)}')\n", + "\n", + "a = np.array(range(4), dtype=np.uint8)\n", + "print(f'\\na: {a}\\nbincount(a, minlength=8): {np.bincount(a, minlength=8)}')\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2944,7 +3007,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.8.5 ('base')", + "display_name": "base", "language": "python", "name": "python3" }, @@ -3006,11 +3069,6 @@ "_Feature" ], "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10" - } } }, "nbformat": 4, diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index 48f0adea..6b5995b8 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Wed, 3 Sep 2025 + +version 6.10.0 + + add bincount + Fri, 06 Jun 2025 version 6.8.0 diff --git a/tests/2d/numpy/bincount.py b/tests/2d/numpy/bincount.py new file mode 100644 index 00000000..681e3d18 --- /dev/null +++ b/tests/2d/numpy/bincount.py @@ -0,0 +1,82 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +for dtype in (np.uint8, np.uint16): + a = np.array([0, 1, 1, 3, 3, 3], dtype=dtype) + print(np.bincount(a)) + +for dtype in (np.uint8, np.uint16): + a = np.array([0, 2, 2, 4], dtype=dtype) + print(np.bincount(a, minlength=3)) + +for dtype in (np.uint8, np.uint16): + a = np.array([0, 2, 2, 4], dtype=dtype) + print(np.bincount(a, minlength=8)) + +for dtype in (np.uint8, np.uint16): + a = np.array([], dtype=dtype) + print(np.bincount(a)) + print(np.bincount(a, minlength=8)) + +for dtype in (np.uint8, np.uint16): + a = np.array([0, 1, 1, 3], dtype=dtype) + w = np.array([0.5, 1.0, 2.5, 0.25]) + print(np.where(abs(np.bincount(a, weights=w) - np.array([0.5, 2.0, 0.0, 0.25])) < 0.001, 1, 0)) + + w = np.array([1, 2, 3, 4], dtype=np.uint8) + print(np.bincount(a, weights=w)) + +for dtype in (np.uint8, np.uint16): + a = np.array([1, 1], dtype=dtype) + w = np.array([0.5, 1.5]) + print(np.bincount(a, weights=w, minlength=4)) + +for dtype in (np.uint8, np.uint16): + a = np.array([2, 2, 2, 3], dtype=dtype) + for wtype in (np.uint8, np.uint16, np.int8, np.int16, np.float): + w = np.array([1, 2, 3, 4], dtype=wtype) + print(np.bincount(a, weights=w)) + +for dtype in (np.int8, np.int16, np.float): + a = np.array([2, 2, 2, 3], dtype=dtype) + try: + np.bincount(a) + except Exception as e: + print(e) + +for dtype in (np.uint8, np.int8, np.uint16, np.int16, np.float): + a = np.array(range(4), dtype=dtype).reshape((2, 2)) + try: + np.bincount(a) + except Exception as e: + print(e) + +for dtype in (np.uint8, np.uint16): + a = np.array([1, 2, 3], dtype=dtype) + w = np.array([1, 2]) + try: + np.bincount(a, weights=w) + except Exception as e: + print(e) + +for dtype in (np.uint8, np.uint16): + a = np.array([1, 2, 3], dtype=dtype) + try: + np.bincount(a, minlength=-1) + except Exception as e: + print(e) + +for dtype in (np.uint8, np.uint16): + a = np.array([1, 2, 3], dtype=dtype) + w = np.array([1j, 2j, 3j], dtype=np.complex) + try: + np.bincount(a, weights=w) + except Exception as e: + print(e) + + +a = np.array([0, 1000], dtype=np.uint16) +y = np.bincount(a) +print(y[0], y[1000], len(y)) \ No newline at end of file diff --git a/tests/2d/numpy/bincount.py.exp b/tests/2d/numpy/bincount.py.exp new file mode 100644 index 00000000..e7871c62 --- /dev/null +++ b/tests/2d/numpy/bincount.py.exp @@ -0,0 +1,41 @@ +array([1, 2, 0, 3], dtype=uint16) +array([1, 2, 0, 3], dtype=uint16) +array([1, 0, 2, 0, 1], dtype=uint16) +array([1, 0, 2, 0, 1], dtype=uint16) +array([1, 0, 2, 0, 1, 0, 0, 0], dtype=uint16) +array([1, 0, 2, 0, 1, 0, 0, 0], dtype=uint16) +array([], dtype=uint16) +array([0, 0, 0, 0, 0, 0, 0, 0], dtype=uint16) +array([], dtype=uint16) +array([0, 0, 0, 0, 0, 0, 0, 0], dtype=uint16) +array([1, 0, 1, 1], dtype=uint8) +array([1.0, 5.0, 0.0, 4.0], dtype=float64) +array([1, 0, 1, 1], dtype=uint8) +array([1.0, 5.0, 0.0, 4.0], dtype=float64) +array([0.0, 2.0, 0.0, 0.0], dtype=float64) +array([0.0, 2.0, 0.0, 0.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +array([0.0, 0.0, 6.0, 4.0], dtype=float64) +cannot cast array data from dtype +cannot cast array data from dtype +cannot cast array data from dtype +object too deep for desired array +object too deep for desired array +object too deep for desired array +object too deep for desired array +object too deep for desired array +the weights and list don't have the same length +the weights and list don't have the same length +minlength must not be negative +minlength must not be negative +cannot cast weigths to float +cannot cast weigths to float +1 1 1001 From a8b25eff3eea742b949012e512de7c69886e7794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 8 Oct 2025 23:17:30 +0200 Subject: [PATCH 113/113] implement axes keyword argument in transpose (#735) * implement axis keyword of transpose * fix keyword typo * add 2D transpose tests * update documentation * clean up expected values of test scripts * update missed test script --- code/ndarray.c | 98 +++++++++- code/ndarray.h | 9 +- code/ndarray_properties.c | 2 +- code/ulab.c | 2 +- docs/manual/source/conf.py | 2 +- docs/manual/source/ulab-ndarray.rst | 3 + docs/ulab-convert.ipynb | 36 +--- docs/ulab-ndarray.ipynb | 14 +- tests/1d/numpy/universal_functions.py | 206 +++++++++------------- tests/1d/numpy/universal_functions.py.exp | 93 ++++++++-- tests/2d/numpy/cholesky.py | 29 ++- tests/2d/numpy/cholesky.py.exp | 38 +++- tests/2d/numpy/logspace.py | 16 +- tests/2d/numpy/logspace.py.exp | 4 +- tests/2d/numpy/transpose.py | 58 ++++++ tests/2d/numpy/transpose.py.exp | 78 ++++++++ tests/2d/scipy/integrate.py | 42 ++--- tests/2d/scipy/integrate.py.exp | 15 +- 18 files changed, 512 insertions(+), 233 deletions(-) create mode 100644 tests/2d/numpy/transpose.py create mode 100644 tests/2d/numpy/transpose.py.exp diff --git a/code/ndarray.c b/code/ndarray.c index b297b3f1..361d664d 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1874,28 +1874,110 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { #endif /* NDARRAY_HAS_UNARY_OPS */ #if NDARRAY_HAS_TRANSPOSE -mp_obj_t ndarray_transpose(mp_obj_t self_in) { - #if ULAB_MAX_DIMS == 1 - return self_in; - #endif - // TODO: check, what happens to the offset here, if we have a view +// We have to implement the T property separately, for the property can't take keyword arguments + +#if ULAB_MAX_DIMS == 1 +// isolating the one-dimensional case saves space, because the transpose is sort of meaningless +mp_obj_t ndarray_T(mp_obj_t self_in) { + return self_in; +} +#else +mp_obj_t ndarray_T(mp_obj_t self_in) { + // without argument, simply return a view with axes in reverse order ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); if(self->ndim == 1) { return self_in; } size_t *shape = m_new(size_t, self->ndim); int32_t *strides = m_new(int32_t, self->ndim); - for(uint8_t i=0; i < self->ndim; i++) { + for(uint8_t i = 0; i < self->ndim; i++) { shape[ULAB_MAX_DIMS - 1 - i] = self->shape[ULAB_MAX_DIMS - self->ndim + i]; strides[ULAB_MAX_DIMS - 1 - i] = self->strides[ULAB_MAX_DIMS - self->ndim + i]; } - // TODO: I am not sure ndarray_new_view is OK here... - // should be deep copy... ndarray_obj_t *ndarray = ndarray_new_view(self, self->ndim, shape, strides, 0); return MP_OBJ_FROM_PTR(ndarray); } +#endif /* ULAB_MAX_DIMS == 1 */ + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_T_obj, ndarray_T); + +# if ULAB_MAX_DIMS == 1 +// again, nothing to do, if there is only one dimension, though, the arguments might still have to be parsed... +mp_obj_t ndarray_transpose(mp_obj_t self_in) { + return self_in; +} MP_DEFINE_CONST_FUN_OBJ_1(ndarray_transpose_obj, ndarray_transpose); +#else +mp_obj_t ndarray_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_axes, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ndarray_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + + if(self->ndim == 1) { + return args[0].u_obj; + } + + size_t *shape = m_new(size_t, self->ndim); + int32_t *strides = m_new(int32_t, self->ndim); + uint8_t *order = m_new(uint8_t, self->ndim); + + mp_obj_t axes = args[1].u_obj; + + if(axes == mp_const_none) { + // simply swap the order of the axes + for(uint8_t i = 0; i < self->ndim; i++) { + order[i] = self->ndim - 1 - i; + } + } else { + if(!mp_obj_is_type(axes, &mp_type_tuple)) { + mp_raise_TypeError(MP_ERROR_TEXT("keyword argument must be tuple of integers")); + } + // start with the straight array, and then swap only those specified in the argument + for(uint8_t i = 0; i < self->ndim; i++) { + order[i] = i; + } + + mp_obj_tuple_t *axes_tuple = MP_OBJ_TO_PTR(axes); + + if(axes_tuple->len > self->ndim) { + mp_raise_ValueError(MP_ERROR_TEXT("too many axes specified")); + } + + for(uint8_t i = 0; i < axes_tuple->len; i++) { + int32_t ax = mp_obj_get_int(axes_tuple->items[i]); + if((ax >= self->ndim) || (ax < 0)) { + mp_raise_ValueError(MP_ERROR_TEXT("axis index out of bounds")); + } else { + order[i] = (uint8_t)ax; + // TODO: check that no two identical numbers appear in the tuple + for(uint8_t j = 0; j < i; j++) { + if(order[i] == order[j]) { + mp_raise_ValueError(MP_ERROR_TEXT("repeated indices")); + } + } + } + } + } + + uint8_t axis_offset = ULAB_MAX_DIMS - self->ndim; + for(uint8_t i = 0; i < self->ndim; i++) { + shape[axis_offset + i] = self->shape[axis_offset + order[i]]; + strides[axis_offset + i] = self->strides[axis_offset + order[i]]; + } + + ndarray_obj_t *ndarray = ndarray_new_view(self, self->ndim, shape, strides, 0); + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_transpose_obj, 1, ndarray_transpose); +#endif /* ULAB_MAX_DIMS == 1 */ #endif /* NDARRAY_HAS_TRANSPOSE */ #if ULAB_MAX_DIMS > 1 diff --git a/code/ndarray.h b/code/ndarray.h index 8e81773d..af72b1ad 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -265,9 +265,16 @@ MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tolist_obj); #endif #if NDARRAY_HAS_TRANSPOSE +mp_obj_t ndarray_T(mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_1(ndarray_T_obj); +#if ULAB_MAX_DIMS == 1 mp_obj_t ndarray_transpose(mp_obj_t ); MP_DECLARE_CONST_FUN_OBJ_1(ndarray_transpose_obj); -#endif +#else +mp_obj_t ndarray_transpose(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_transpose_obj); +#endif /* ULAB_MAX_DIMS == 1 */ +#endif /* NDARRAY_HAS_TRANSPOSE */ #if ULAB_NUMPY_HAS_NDINFO mp_obj_t ndarray_info(mp_obj_t ); diff --git a/code/ndarray_properties.c b/code/ndarray_properties.c index 6c048bda..8200445a 100644 --- a/code/ndarray_properties.c +++ b/code/ndarray_properties.c @@ -64,7 +64,7 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { #endif #if NDARRAY_HAS_TRANSPOSE case MP_QSTR_T: - dest[0] = ndarray_transpose(self_in); + dest[0] = ndarray_T(self_in); break; #endif #if ULAB_SUPPORTS_COMPLEX diff --git a/code/ulab.c b/code/ulab.c index 8a0b50ca..205ddac5 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.10.0 +#define ULAB_VERSION 6.11.0 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index c10faeb2..3feeef88 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,7 @@ author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '6.9.0' +release = '6.11.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index 46288a26..dde9eab0 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -1564,6 +1564,9 @@ dimensions is larger than 1. +The method also accepts the ``axes`` keyword argument, if permutation of +the returned axes is required. + The transpose of the array can also be gotten through the ``T`` property: diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index 53f28bcb..1cf66f52 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:15.118699Z", @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '6.9.0'\n", + "release = '6.11.0'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -217,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:21.647179Z", @@ -258,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2022-02-09T06:27:42.024028Z", @@ -270,34 +270,6 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", - "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", - " _, nbc = validator.normalize(nbc)\n", "/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n", " _, nbc = validator.normalize(nbc)\n" ] diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index c7518c26..eca54340 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -2237,6 +2237,13 @@ "print('shape of a:', a.shape)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The method also accepts the `axes` keyword argument, if permutation of the returned axes is required." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -3731,11 +3738,8 @@ } ], "metadata": { - "interpreter": { - "hash": "ce9a02f9f7db620716422019cafa4bc1786ca85daa298b819f6da075e7993842" - }, "kernelspec": { - "display_name": "Python 3", + "display_name": "base", "language": "python", "name": "python3" }, @@ -3749,7 +3753,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.11.7" }, "toc": { "base_numbering": 1, diff --git a/tests/1d/numpy/universal_functions.py b/tests/1d/numpy/universal_functions.py index c41cf948..a72d73d1 100644 --- a/tests/1d/numpy/universal_functions.py +++ b/tests/1d/numpy/universal_functions.py @@ -7,142 +7,94 @@ import numpy as np import scipy as spy -result = (np.sin(np.pi/2)) -ref_result = 1.0 -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +def test_function(func, reference, expression): + result = expression + print(f'testing {func}') + print(math.isclose(result, reference, rel_tol=1E-6, abs_tol=1E-6)) + print() -result = (np.cos(np.pi/2)) -ref_result = 0.0 -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +def test_function_array(func, reference, expression): + print(f'testing {func}') + results = [] + for i in range(len(references)): + results.append(math.isclose(references[i], expression[i], rel_tol=1E-6, abs_tol=1E-6)) + print(results) + print() -result = (np.tan(np.pi/2)) -ref_result = 1.633123935319537e+16 -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) -result = (np.sinh(np.pi/2)) -ref_result = 2.3012989023072947 -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('sin', 1.0, np.sin(np.pi/2)) -result = (np.cosh(np.pi/2)) -ref_result = 2.5091784786580567 -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('cos', 0.0, (np.cos(np.pi/2))) -result = (np.tanh(np.pi/2)) -ref_result = 0.9171523356672744 -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('tan', 1.633123935319537e+16, np.tan(np.pi/2)) -ref_result = np.pi/2 -result = (np.asin(np.sin(np.pi/2))) -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('sinh', 2.3012989023072947, np.sinh(np.pi/2)) -result = (np.acos(np.cos(np.pi/2))) -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('cosh:', 2.5091784786580567, np.cosh(np.pi/2)) -result = (np.atan(np.tan(np.pi/2))) -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('tanh', 0.9171523356672744, np.tanh(np.pi/2)) -result = (np.cosh(np.acosh(np.pi/2))) -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) +test_function('asin', np.pi/2, np.asin(np.sin(np.pi/2))) -result = (np.sinh(np.asinh(np.pi/2))) -print(math.isclose(result, ref_result, rel_tol=1E-6, abs_tol=1E-6)) - -print(np.degrees(np.pi)) -print(np.radians(np.degrees(np.pi))) -print(np.floor(np.pi)) -print(np.ceil(np.pi)) -print(np.sqrt(np.pi)) -print(np.exp(1)) -print(np.log(np.exp(1))) - -print(np.log2(2**1)) - -print(np.log10(10**1)) -print(math.isclose(np.exp(1) - np.expm1(1), 1)) - -x = np.array([-1, +1, +1, -1]) -y = np.array([-1, -1, +1, +1]) -result = (np.arctan2(y, x) * 180 / np.pi) -ref_result = np.array([-135.0, -45.0, 45.0, 135.0], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) +test_function('acos', np.pi/2, np.acos(np.cos(np.pi/2))) + +test_function('atan', np.pi/2, np.atan(np.tan(np.pi/2))) + +test_function('acosh', np.pi/2, np.cosh(np.acosh(np.pi/2))) + +test_function('asinh', np.pi/2, np.sinh(np.asinh(np.pi/2))) + +test_function('degrees', 180.0, np.degrees(np.pi)) + +test_function('radians', np.pi, np.radians(180.0)) + +test_function('floor', 3.0, np.floor(np.pi)) + +test_function('ceil', 4.0, np.ceil(np.pi)) + +test_function('sqrt', 1.7724538509055159, np.sqrt(np.pi)) + +test_function('exp', 2.718281828459045, np.exp(1)) + +test_function('log', 1.0, np.log(2.718281828459045)) + +test_function('log2', 1.0, np.log2(2)) + +test_function('log10', 1.0, np.log10(10.0)) + +test_function('expm1', 1.0, np.exp(1) - np.expm1(1)) + +x = [-1, +1, +1, -1] +y = [-1, -1, +1, +1] +reference = [-135.0, -45.0, 45.0, 135.0] +for i in range(4): + test_function('arctan2', reference[i], np.arctan2(y[i], x[i]) * 180 / np.pi) x = np.linspace(-2*np.pi, 2*np.pi, 5) -result = np.sin(x) -ref_result = np.array([2.4492936e-16, -1.2246468e-16, 0.0000000e+00, 1.2246468e-16, -2.4492936e-16], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = np.cos(x) -ref_result = np.array([1., -1., 1., -1., 1.], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = np.tan(x) -ref_result = np.array([2.4492936e-16, 1.2246468e-16, 0.0000000e+00, -1.2246468e-16, -2.4492936e-16], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = np.sinh(x) -ref_result = np.array([-267.74489404, -11.54873936, 0., 11.54873936, 267.74489404], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = np.cosh(x) -ref_result = np.array([267.74676148, 11.59195328, 1.0, 11.59195328, 267.74676148], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = np.tanh(x) -ref_result = np.array([-0.9999930253396107, -0.99627207622075, 0.0, 0.99627207622075, 0.9999930253396107], dtype=np.float) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = np.sinc(x) -ref_result = np.array([0.03935584386392389, -0.04359862862918773, 1.0, -0.04359862862918773, 0.03935584386392389]) -cmp_result = [] -for i in range(len(x)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = (spy.special.erf(np.linspace(-3, 3, num=5))) -ref_result = np.array([-0.9999779095030014, -0.9661051464753108, 0.0, 0.9661051464753108, 0.9999779095030014], dtype=np.float) -cmp_result = [] -for i in range(len(ref_result)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = (spy.special.erfc(np.linspace(-3, 3, num=5))) -ref_result = np.array([1.99997791e+00, 1.96610515e+00, 1.00000000e+00, 3.38948535e-02, 2.20904970e-05], dtype=np.float) -cmp_result = [] -for i in range(len(ref_result)): - cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-6, abs_tol=1E-6)) -print(cmp_result) - -result = (spy.special.gamma(np.array([0, 0.5, 1, 5]))) -ref_result = np.array([1.77245385, 1.0, 24.0]) -cmp_result = [] -cmp_result.append(math.isinf(result[0])) -for i in range(len(ref_result)): - cmp_result.append(math.isclose(result[i+1], ref_result[i], rel_tol=1E-9, abs_tol=1E-9)) -print(cmp_result) - -result = (spy.special.gammaln([0, -1, -2, -3, -4])) -cmp_result = [] -for i in range(len(ref_result)): - cmp_result.append(math.isinf(result[i])) -print(cmp_result) + +references = [2.4492936e-16, -1.2246468e-16, 0.0000000e+00, 1.2246468e-16, -2.4492936e-16] +test_function_array('sin', references, np.sin(x)) + +references = [1., -1., 1., -1., 1.] +test_function_array('cos', references, np.cos(x)) + +references = [0.0, 0.0, 0.0, 0.0, 0.0] +test_function_array('tan', references, np.tan(x)) + +references = [-267.74489404, -11.54873936, 0., 11.54873936, 267.74489404] +test_function_array('sinh', references, np.sinh(x)) + +references = [267.74676148, 11.59195328, 1.0, 11.59195328, 267.74676148] +test_function_array('cosh', references, np.cosh(x)) + +references = [-0.9999930253396107, -0.99627207622075, 0.0, 0.99627207622075, 0.9999930253396107] +test_function_array('tanh', references, np.tanh(x)) + +references = [0.03935584386392389, -0.04359862862918773, 1.0, -0.04359862862918773, 0.03935584386392389] +test_function_array('sinc', references, np.sinc(x)) + +references = [-0.9999779095030014, -0.9661051464753108, 0.0, 0.9661051464753108, 0.9999779095030014] +test_function_array('erf', references, spy.special.erf(np.linspace(-3, 3, num=5))) + +references = [1.99997791e+00, 1.96610515e+00, 1.00000000e+00, 3.38948535e-02, 2.20904970e-05] +test_function_array('erfc', references, spy.special.erfc(np.linspace(-3, 3, num=5))) \ No newline at end of file diff --git a/tests/1d/numpy/universal_functions.py.exp b/tests/1d/numpy/universal_functions.py.exp index 919c94a4..e6efd3ea 100644 --- a/tests/1d/numpy/universal_functions.py.exp +++ b/tests/1d/numpy/universal_functions.py.exp @@ -1,33 +1,102 @@ +testing sin True + +testing cos True + +testing tan True + +testing sinh True + +testing cosh: True + +testing tanh True + +testing asin True + +testing acos True + +testing atan True + +testing acosh True + +testing asinh True -180.0 -3.141592653589793 -3.0 -4.0 -1.772453850905516 -2.718281828459045 -1.0 -1.0 -1.0 + +testing degrees True -[True, True, True, True] + +testing radians +True + +testing floor +True + +testing ceil +True + +testing sqrt +True + +testing exp +True + +testing log +True + +testing log2 +True + +testing log10 +True + +testing expm1 +True + +testing arctan2 +True + +testing arctan2 +True + +testing arctan2 +True + +testing arctan2 +True + +testing sin [True, True, True, True, True] + +testing cos [True, True, True, True, True] + +testing tan [True, True, True, True, True] + +testing sinh [True, True, True, True, True] + +testing cosh [True, True, True, True, True] + +testing tanh [True, True, True, True, True] + +testing sinc [True, True, True, True, True] + +testing erf [True, True, True, True, True] + +testing erfc [True, True, True, True, True] -[True, True, True, True] -[True, True, True] + diff --git a/tests/2d/numpy/cholesky.py b/tests/2d/numpy/cholesky.py index beab3c1d..368005c8 100644 --- a/tests/2d/numpy/cholesky.py +++ b/tests/2d/numpy/cholesky.py @@ -1,14 +1,27 @@ from ulab import numpy as np +import math + a = np.array([[1, 2], [2, 5]]) -print(np.linalg.cholesky(a)) +reference = np.array([[1.0, 0.0], [2.0, 1.0]]) +result = np.linalg.cholesky(a) +for i in range(2): + for j in range(2): + print(f'({i}, {j}): {math.isclose(result[i][j], reference[i][j], rel_tol=1E-6, abs_tol=1E-6)}') -b = a = np.array([[25, 15, -5], [15, 18, 0], [-5, 0, 11]]) -print(np.linalg.cholesky(b)) +b = np.array([[25, 15, -5], [15, 18, 0], [-5, 0, 11]]) +reference = np.array([[5.0, 0.0, 0.0], [3.0, 3.0, 0.0], [-1.0, 1.0, 3.0]]) +result = np.linalg.cholesky(b) +for i in range(3): + for j in range(3): + print(f'({i}, {j}): {math.isclose(result[i][j], reference[i][j], rel_tol=1E-6, abs_tol=1E-6)}') c = np.array([[18, 22, 54, 42], [22, 70, 86, 62], [54, 86, 174, 134], [42, 62, 134, 106]]) -print(np.linalg.cholesky(c)) - - - - +reference = np.array([[4.242640687119285, 0.0, 0.0, 0.0], + [5.185449728701349, 6.565905201197403, 0.0, 0.0], + [12.727922061357857, 3.0460384954008553, 1.6497422479090682, 0.0], + [9.899494936611665, 1.6245538642137891, 1.8497110052313865, 1.3926212476455826]]) +result = np.linalg.cholesky(c) +for i in range(4): + for j in range(4): + print(f'({i}, {j}): {math.isclose(result[i][j], reference[i][j], rel_tol=1E-6, abs_tol=1E-6)}') diff --git a/tests/2d/numpy/cholesky.py.exp b/tests/2d/numpy/cholesky.py.exp index a8e88ef6..aa2dc3e5 100644 --- a/tests/2d/numpy/cholesky.py.exp +++ b/tests/2d/numpy/cholesky.py.exp @@ -1,9 +1,29 @@ -array([[1.0, 0.0], - [2.0, 1.0]], dtype=float64) -array([[5.0, 0.0, 0.0], - [3.0, 3.0, 0.0], - [-1.0, 1.0, 3.0]], dtype=float64) -array([[4.242640687119285, 0.0, 0.0, 0.0], - [5.185449728701349, 6.565905201197403, 0.0, 0.0], - [12.72792206135786, 3.046038495400855, 1.649742247909068, 0.0], - [9.899494936611665, 1.624553864213789, 1.849711005231386, 1.392621247645583]], dtype=float64) +(0, 0): True +(0, 1): True +(1, 0): True +(1, 1): True +(0, 0): True +(0, 1): True +(0, 2): True +(1, 0): True +(1, 1): True +(1, 2): True +(2, 0): True +(2, 1): True +(2, 2): True +(0, 0): True +(0, 1): True +(0, 2): True +(0, 3): True +(1, 0): True +(1, 1): True +(1, 2): True +(1, 3): True +(2, 0): True +(2, 1): True +(2, 2): True +(2, 3): True +(3, 0): True +(3, 1): True +(3, 2): True +(3, 3): True diff --git a/tests/2d/numpy/logspace.py b/tests/2d/numpy/logspace.py index e6f2047b..7fb1ef26 100644 --- a/tests/2d/numpy/logspace.py +++ b/tests/2d/numpy/logspace.py @@ -3,8 +3,20 @@ except: import numpy as np -dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) +import math + +dtypes = (np.uint8, np.int8, np.uint16, np.int16) for dtype in dtypes: print(np.logspace(0, 10, num=5, endpoint=False, dtype=dtype)) - print(np.logspace(0, 10, num=5, endpoint=True, dtype=dtype)) \ No newline at end of file + print(np.logspace(0, 10, num=5, endpoint=True, dtype=dtype)) + + +reference = [1.0, 100.0, 10000.0, 1000000.0, 100000000.0] +result = np.logspace(0, 10, num=5, endpoint=False, dtype=np.float) +print([math.isclose(result[i], reference[i], rel_tol=1E-6, abs_tol=1E-6) for i in range(len(reference))]) + + +reference = [1.0, 316.22776601683796, 100000.00000000001, 31622776.6016838, 10000000000.000002] +result = np.logspace(0, 10, num=5, endpoint=True, dtype=np.float) +print([math.isclose(result[i], reference[i], rel_tol=1E-6, abs_tol=1E-6) for i in range(len(reference))]) diff --git a/tests/2d/numpy/logspace.py.exp b/tests/2d/numpy/logspace.py.exp index 1a09cef4..5e91bbe8 100644 --- a/tests/2d/numpy/logspace.py.exp +++ b/tests/2d/numpy/logspace.py.exp @@ -6,5 +6,5 @@ array([1, 100, 10000, 16960, 57600], dtype=uint16) array([1, 316, 34464, 34424, 0], dtype=uint16) array([1, 100, 10000, 16960, -7936], dtype=int16) array([1, 316, -31072, -31112, 0], dtype=int16) -array([1.0, 100.0, 10000.0, 1000000.0, 100000000.0], dtype=float64) -array([1.0, 316.227766016838, 100000.0, 31622776.6016838, 10000000000.0], dtype=float64) +[True, True, True, True, True] +[True, True, True, True, True] diff --git a/tests/2d/numpy/transpose.py b/tests/2d/numpy/transpose.py new file mode 100644 index 00000000..9ffbfb31 --- /dev/null +++ b/tests/2d/numpy/transpose.py @@ -0,0 +1,58 @@ +try: + from ulab import numpy as np +except ImportError: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +print("Basic 2D transpose") +a = np.array(range(6)).reshape((2, 3)) +print("Original array:") +print(a) +print("Transposed (no axes):") +print(a.transpose()) +print() + +print("2D transpose with axes=(1, 0)") +a = np.array(range(6)).reshape((2, 3)) +print("Original array:") +print(a) +print("Transposed with axes=(1, 0):") +print(a.transpose(axes=(1, 0))) +print() + +print("2D transpose with axes=(0, 1)") +a = np.array(range(6)).reshape((2, 3)) +print("Original array:") +print(a) +print("Transposed with axes=(0, 1):") +print(a.transpose(axes=(0, 1))) +print() + +print("1D array transpose") +a = np.array(range(5)) +print("Original array:") +print(a) +print("Transposed array:") +print(a.transpose()) +print() + +print("Transpose of different dtypes") +for dtype in dtypes: + a = np.array(range(4), dtype=dtype).reshape((2, 2)) + print('Original array:') + print(a) + print('Transposed array:') + print(a.transpose()) +print() + +print(".T property vs transpose() method") +a = np.array(range(6)).reshape((2, 3)) +print("Original array:") +print(a) +print("Using .T property:") +print(a.T) +print("Using .transpose():") +print(a.transpose()) +print("Both should be equal:", np.all(a.T == a.transpose())) +print() \ No newline at end of file diff --git a/tests/2d/numpy/transpose.py.exp b/tests/2d/numpy/transpose.py.exp new file mode 100644 index 00000000..87627cb2 --- /dev/null +++ b/tests/2d/numpy/transpose.py.exp @@ -0,0 +1,78 @@ +Basic 2D transpose +Original array: +array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0]], dtype=float64) +Transposed (no axes): +array([[0.0, 3.0], + [1.0, 4.0], + [2.0, 5.0]], dtype=float64) + +2D transpose with axes=(1, 0) +Original array: +array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0]], dtype=float64) +Transposed with axes=(1, 0): +array([[0.0, 3.0], + [1.0, 4.0], + [2.0, 5.0]], dtype=float64) + +2D transpose with axes=(0, 1) +Original array: +array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0]], dtype=float64) +Transposed with axes=(0, 1): +array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0]], dtype=float64) + +1D array transpose +Original array: +array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64) +Transposed array: +array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64) + +Transpose of different dtypes +Original array: +array([[0, 1], + [2, 3]], dtype=uint8) +Transposed array: +array([[0, 2], + [1, 3]], dtype=uint8) +Original array: +array([[0, 1], + [2, 3]], dtype=int8) +Transposed array: +array([[0, 2], + [1, 3]], dtype=int8) +Original array: +array([[0, 1], + [2, 3]], dtype=uint16) +Transposed array: +array([[0, 2], + [1, 3]], dtype=uint16) +Original array: +array([[0, 1], + [2, 3]], dtype=int16) +Transposed array: +array([[0, 2], + [1, 3]], dtype=int16) +Original array: +array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) +Transposed array: +array([[0.0, 2.0], + [1.0, 3.0]], dtype=float64) + +.T property vs transpose() method +Original array: +array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0]], dtype=float64) +Using .T property: +array([[0.0, 3.0], + [1.0, 4.0], + [2.0, 5.0]], dtype=float64) +Using .transpose(): +array([[0.0, 3.0], + [1.0, 4.0], + [2.0, 5.0]], dtype=float64) +Both should be equal: True + diff --git a/tests/2d/scipy/integrate.py b/tests/2d/scipy/integrate.py index 1d0edb7b..2c176c46 100644 --- a/tests/2d/scipy/integrate.py +++ b/tests/2d/scipy/integrate.py @@ -1,28 +1,30 @@ -import sys -from math import * - try: - from ulab import scipy + from ulab import scipy as spy + from ulab import numpy as np except ImportError: import scipy -f = lambda x: x * sin(x) * exp(x) -a=1 -b=2 +import math + +f = lambda x: x * np.sin(x) * np.exp(x) +a = 1 +b = 2 -(res, err) = scipy.integrate.tanhsinh(f, a, b) -if isclose (res, 7.11263821415851) and isclose (err, 5.438231077315757e-14): - print (res, err) - -res = scipy.integrate.romberg(f, a, b) -if isclose (res, 7.112638214158507): - print (res) +(res, err) = spy.integrate.tanhsinh(f, a, b) +print('testing tanhsinh') +print(math.isclose(res, 7.11263821415851, rel_tol=1E-6, abs_tol=1E-6)) +print() -res = scipy.integrate.simpson(f, a, b) -if isclose (res, 7.112638214158494): - print (res) +print('testing romberg') +res = spy.integrate.romberg(f, a, b) +print(math.isclose(res, 7.112638214158507, rel_tol=1E-6, abs_tol=1E-6)) +print() -(res, err) = scipy.integrate.quad(f, a, b) -if isclose (res, 7.112638214158507) and isclose (err, 7.686723611780195e-14): - print (res, err) +print('testing simpson') +res = spy.integrate.simpson(f, a, b) +print(math.isclose(res, 7.112638214158507, rel_tol=1E-6, abs_tol=1E-6)) +print() +print('testing quad') +(res, err) = spy.integrate.quad(f, a, b) +print(math.isclose(res, 7.112638214158507, rel_tol=1E-6, abs_tol=1E-6)) diff --git a/tests/2d/scipy/integrate.py.exp b/tests/2d/scipy/integrate.py.exp index 10426e6c..d8c790c7 100644 --- a/tests/2d/scipy/integrate.py.exp +++ b/tests/2d/scipy/integrate.py.exp @@ -1,4 +1,11 @@ -7.11263821415851 5.438231077315757e-14 -7.112638214158507 -7.112638214158494 -7.112638214158507 7.686723611780195e-14 +testing tanhsinh +True + +testing romberg +True + +testing simpson +True + +testing quad +True