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] 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 e915f12..ad957ce 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 6e54b10..ffa7a44 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 c1a45e9..eafd772 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 6658269..b999967 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 36462e5..78b8a1b 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 b57f1e6..05ed1ed 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 3e6b81e..62170fb 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 6bbff15..c4a27e2 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,8 +27,7 @@ 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 = '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 a2a3e41..252f5a5 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 2ef884a..d13278c 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 4600c7c..11e4a64 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 ef5e24f..4ce30e5 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 0000000..3bf5ab8 --- /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 0000000..081ec1b --- /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)