From 3d00480443c0e311330a3f2fb4bfb9fa16935849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 20 Nov 2021 11:10:06 +0100 Subject: [PATCH 01/58] add complex utilities --- code/micropython.mk | 2 + code/numpy/carray/carray.c | 65 ++++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 18 +++++++++ code/numpy/carray/carray_tools.c | 28 ++++++++++++++ code/numpy/carray/carray_tools.h | 25 ++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 code/numpy/carray/carray.c create mode 100644 code/numpy/carray/carray.h create mode 100644 code/numpy/carray/carray_tools.c create mode 100644 code/numpy/carray/carray_tools.h diff --git a/code/micropython.mk b/code/micropython.mk index 03d3fd40..64ab6152 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -13,6 +13,8 @@ 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/compare.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c SRC_USERMOD += $(USERMODULES_DIR)/ulab_create.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft_tools.c diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c new file mode 100644 index 00000000..cc3c7342 --- /dev/null +++ b/code/numpy/carray/carray.c @@ -0,0 +1,65 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#include +#include +#include +#include "py/obj.h" +#include "py/objint.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/misc.h" + +#include "../../ulab.h" +#include "../../ndarray.h" + +#if ULAB_SUPPORTS_COMPLEX + +STATIC mp_obj_t carray_real(mp_obj_t _source) { + if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->dtype != NDARRAY_COMPLEX) { + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + ndarray_copy_array(source, target, 0); + return MP_OBJ_FROM_PTR(target); + } else { // the input is most definitely a complex array + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + ndarray_copy_array(source, target, 0); + return MP_OBJ_FROM_PTR(target); + } + } else { + mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); + +STATIC mp_obj_t carray_imag(mp_obj_t _source) { + if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + return MP_OBJ_FROM_PTR(target); + } else { // the input is most definitely a complex array + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + ndarray_copy_array(source, target, source->itemsize / 2); + return MP_OBJ_FROM_PTR(target); + } + } else { + mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); + +#endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h new file mode 100644 index 00000000..91362c08 --- /dev/null +++ b/code/numpy/carray/carray.h @@ -0,0 +1,18 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#ifndef _CARRAY_ +#define _CARRAY_ + +MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); +MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); + +#endif diff --git a/code/numpy/carray/carray_tools.c b/code/numpy/carray/carray_tools.c new file mode 100644 index 00000000..c6fb8de2 --- /dev/null +++ b/code/numpy/carray/carray_tools.c @@ -0,0 +1,28 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#include +#include +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "py/misc.h" + +#include "../../ulab.h" +#include "../../ndarray.h" + +#if ULAB_SUPPORTS_COMPLEX + +void raise_complex_NotImplementedError(void) { + mp_raise_NotImplementedError(translate("not implemented for complex dtype")); +} + +#endif diff --git a/code/numpy/carray/carray_tools.h b/code/numpy/carray/carray_tools.h new file mode 100644 index 00000000..3b28e1c4 --- /dev/null +++ b/code/numpy/carray/carray_tools.h @@ -0,0 +1,25 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#ifndef _CARRAY_TOOLS_ +#define _CARRAY_TOOLS_ + +void raise_complex_NotImplementedError(void); + +#if ULAB_SUPPORTS_COMPLEX + #define NOT_IMPLEMENTED_FOR_COMPLEX() raise_complex_NotImplementedError(); + #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) if((dtype) == NDARRAY_COMPLEX) raise_complex_NotImplementedError(); +#else + #define NOT_IMPLEMENTED_FOR_COMPLEX() // do nothing + #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) // do nothing +#endif + +#endif From 92b351380485ad2a6dce9393161e4f6c72049ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 18:40:12 +0100 Subject: [PATCH 02/58] ulab can now be compiled with complex support --- code/ndarray.c | 119 ++++++++++++++++++++++++++++++------- code/ndarray.h | 5 +- code/numpy/approx.c | 6 ++ code/numpy/compare.c | 11 ++++ code/numpy/fft/fft.c | 2 + code/numpy/fft/fft_tools.c | 3 + code/numpy/filter.c | 3 + code/numpy/linalg/linalg.c | 6 ++ code/numpy/numerical.c | 12 +++- code/numpy/numpy.c | 9 ++- code/numpy/poly.c | 18 ++++++ code/numpy/stats.c | 2 + code/numpy/transform.c | 4 ++ code/numpy/vector.c | 7 +++ code/scipy/signal/signal.c | 7 +++ code/ulab.h | 4 ++ code/ulab_create.c | 7 ++- code/ulab_tools.c | 10 ++++ code/ulab_tools.h | 2 + 19 files changed, 210 insertions(+), 27 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 836d5494..5286290f 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -171,7 +171,11 @@ void ndarray_rewind_array(uint8_t ndim, uint8_t *array, size_t *shape, int32_t * static int32_t *strides_from_shape(size_t *shape, uint8_t dtype) { // returns a strides array that corresponds to a dense array with the prescribed shape int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_complex_size(dtype); + #else + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + #endif for(uint8_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * shape[i-1]; } @@ -231,7 +235,13 @@ void ndarray_dtype_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin mp_print_str(print, "uint16')"); } else if(self->dtype == NDARRAY_INT16) { mp_print_str(print, "int16')"); - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if(self->dtype == NDARRAY_COMPLEX) { + mp_print_str(print, "complex')"); + } + #endif + else { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT mp_print_str(print, "float32')"); #else @@ -280,7 +290,13 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t _dtype = NDARRAY_INT16; } else if(memcmp(_dtype_, "float", 5) == 0) { _dtype = NDARRAY_FLOAT; - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if(memcmp(_dtype_, "complex", 7) == 0) { + _dtype = NDARRAY_COMPLEX; + } + #endif + else { mp_raise_TypeError(translate("data type not understood")); } } @@ -308,7 +324,11 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) { GET_STR_DATA_LEN(self_in, _dtype, len); if((len != 1) || ((*_dtype != NDARRAY_BOOL) && (*_dtype != NDARRAY_UINT8) && (*_dtype != NDARRAY_INT8) && (*_dtype != NDARRAY_UINT16) - && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT))) { + && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT) + #if ULAB_SUPPORTS_COMPLEX + && (*_dtype != NDARRAY_COMPLEX) + #endif + )) { mp_raise_TypeError(translate("data type not understood")); } dtype = *_dtype; @@ -361,32 +381,55 @@ mp_obj_t ndarray_get_item(ndarray_obj_t *ndarray, void *array) { } } +static void ndarray_print_element(const mp_print_t *print, ndarray_obj_t *ndarray, uint8_t *array) { + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + // real part first + mp_float_t fvalue = *(mp_float_t *)array; + mp_obj_print_helper(print, mp_obj_new_float(fvalue), PRINT_REPR); + // imaginary part + array += ndarray->itemsize / 2; + fvalue = *(mp_float_t *)array; + if(fvalue >= MICROPY_FLOAT_CONST(0.0) || isnan(fvalue)) { + mp_print_str(print, "+"); + } + array += ndarray->itemsize / 2; + mp_obj_print_helper(print, mp_obj_new_float(fvalue), PRINT_REPR); + mp_print_str(print, "j"); + } else { + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + } + #else + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + #endif +} + static void ndarray_print_row(const mp_print_t *print, ndarray_obj_t * ndarray, uint8_t *array, size_t stride, size_t n) { if(n == 0) { return; } mp_print_str(print, "["); if((n <= ndarray_print_threshold) || (n <= 2*ndarray_print_edgeitems)) { // if the array is short, print everything - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); array += stride; for(size_t i=1; i < n; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } } else { mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); array += stride; for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } mp_printf(print, ", ..., "); - array += stride * (n - 2 * ndarray_print_edgeitems); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + array += stride * (n - 2 * ndarray_print_edgeitems); + ndarray_print_element(print, ndarray, array); array += stride; for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } } mp_print_str(print, "]"); @@ -494,7 +537,19 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t } } else { while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + #if ULAB_SUPPORTS_COMPLEX + mp_float_t real; + mp_float_t imag; + if(dtype == NDARRAY_COMPLEX) { + mp_obj_get_complex(item, &real, &imag); + ndarray_set_value(NDARRAY_FLOAT, ndarray->array, (*idx)++, mp_obj_new_float(real)); + ndarray_set_value(NDARRAY_FLOAT, ndarray->array, (*idx)++, mp_obj_new_float(imag)); + } else { + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + } + #else + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + #endif } } } @@ -518,7 +573,11 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; ndarray->ndim = ndim; ndarray->len = ndim == 0 ? 0 : 1; - ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + ndarray->itemsize = mp_binary_get_complex_size(dtype); + #else + ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + #endif int32_t *_strides; if(strides == NULL) { _strides = strides_from_shape(shape, ndarray->dtype); @@ -546,7 +605,11 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt // 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] = dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_complex_size(dtype); + #else + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_size('@', dtype, NULL); + #endif for(size_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * MAX(1, shape[i-1]); } @@ -567,13 +630,18 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt return ndarray_new_dense_ndarray(_shape->len, shape, dtype); } -void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { +void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target, uint8_t shift) { // TODO: if the array is dense, the content could be copied in a single pass // copies the content of source->array into a new dense void pointer // it is assumed that the dtypes in source and target are the same // Since the target is a new array, it is supposed to be dense uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; + #if ULAB_SUPPORTS_COMPLEX + if(source->dtype == NDARRAY_COMPLEX) { + sarray += shift; + } + #endif #if ULAB_MAX_DIMS > 3 size_t i = 0; @@ -589,7 +657,7 @@ void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { #endif size_t l = 0; do { - memcpy(tarray, sarray, source->itemsize); + memcpy(tarray, sarray, target->itemsize); tarray += target->itemsize; sarray += source->strides[ULAB_MAX_DIMS - 1]; l++; @@ -648,7 +716,7 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { dtype = NDARRAY_BOOLEAN; } ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype); - ndarray_copy_array(source, ndarray); + ndarray_copy_array(source, ndarray, 0); return ndarray; } @@ -1479,12 +1547,13 @@ mp_obj_t ndarray_itemsize(mp_obj_t self_in) { #if NDARRAY_HAS_SHAPE mp_obj_t ndarray_shape(mp_obj_t self_in) { ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t *items = m_new(mp_obj_t, self->ndim); - for(uint8_t i=0; i < self->ndim; i++) { - items[self->ndim - i - 1] = mp_obj_new_int(self->shape[ULAB_MAX_DIMS - i - 1]); + uint8_t nitems = MAX(1, self->ndim); + mp_obj_t *items = m_new(mp_obj_t, nitems); + for(uint8_t i = 0; i < nitems; i++) { + items[nitems - i - 1] = mp_obj_new_int(self->shape[ULAB_MAX_DIMS - i - 1]); } - mp_obj_t tuple = mp_obj_new_tuple(self->ndim, items); - m_del(mp_obj_t, items, self->ndim); + mp_obj_t tuple = mp_obj_new_tuple(nitems, items); + m_del(mp_obj_t, items, nitems); return tuple; } #endif @@ -1804,7 +1873,11 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + uint8_t itemsize = mp_binary_get_complex_size(self->dtype); + #else + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #endif for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); @@ -1913,7 +1986,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { mp_raise_ValueError(translate("cannot assign new shape")); } ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); - ndarray_copy_array(source, ndarray); + ndarray_copy_array(source, ndarray, 0); } return MP_OBJ_FROM_PTR(ndarray); } diff --git a/code/ndarray.h b/code/ndarray.h index 04abd965..dc827a68 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -77,6 +77,9 @@ enum NDARRAY_TYPE { NDARRAY_INT8 = 'b', NDARRAY_UINT16 = 'H', NDARRAY_INT16 = 'h', + #if ULAB_SUPPORTS_COMPLEX + NDARRAY_COMPLEX = 'c', + #endif NDARRAY_FLOAT = FLOAT_TYPECODE, }; @@ -138,7 +141,7 @@ 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 *); ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *); -void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *); +void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *, uint8_t ); MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj); mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 6ed5d7c2..85cdbf78 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -19,6 +19,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "approx.h" //| """Numerical approximation methods""" @@ -60,6 +61,9 @@ STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0); ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) + 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")); } @@ -157,6 +161,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype) ndarray_obj_t *x; mp_float_t mean = MICROPY_FLOAT_CONST(0.0); if(y->len < 2) { @@ -174,6 +179,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * if(args[1].u_obj != mp_const_none) { 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")); } diff --git a/code/numpy/compare.c b/code/numpy/compare.c index b9154569..5a820725 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -20,11 +20,17 @@ #include "../ulab.h" #include "../ndarray_operators.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "compare.h" 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); + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + NOT_IMPLEMENTED_FOR_COMPLEX() + } + #endif uint8_t ndim = 0; size_t *shape = m_new(size_t, ULAB_MAX_DIMS); int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); @@ -197,6 +203,7 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { } } else if(mp_obj_is_type(_x, &ulab_ndarray_type)) { ndarray_obj_t *x = MP_OBJ_TO_PTR(_x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL); // At this point, results is all False uint8_t *rarray = (uint8_t *)results->array; @@ -313,6 +320,10 @@ mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0); ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype) + int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS); diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 6f6534f4..3b3f66a5 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -20,6 +20,7 @@ #include "py/obj.h" #include "py/objarray.h" +#include "../carray/carray_tools.h" #include "fft.h" //| """Frequency-domain functions""" @@ -60,6 +61,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft); //| 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); } else { diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 6dd2ca47..a08f2244 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -13,6 +13,7 @@ #include "../../ndarray.h" #include "../../ulab_tools.h" +#include "../carray/carray_tools.h" #include "fft_tools.h" #ifndef MP_PI @@ -95,6 +96,7 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i 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")); } #endif @@ -122,6 +124,7 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im); #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")); } #endif diff --git a/code/numpy/filter.c b/code/numpy/filter.c index bf2d16cd..fff122ef 100644 --- a/code/numpy/filter.c +++ b/code/numpy/filter.c @@ -21,6 +21,7 @@ #include "../ulab.h" #include "../scipy/signal/signal.h" +#include "carray/carray_tools.h" #include "filter.h" #if ULAB_NUMPY_HAS_CONVOLVE @@ -40,6 +41,8 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) // deal with linear arrays only #if ULAB_MAX_DIMS > 1 if((a->ndim != 1) || (c->ndim != 1)) { diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 596280fe..d9343fb8 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -22,6 +22,7 @@ #include "../../ulab.h" #include "../../ulab_tools.h" +#include "../carray/carray_tools.h" #include "linalg.h" #if ULAB_NUMPY_HAS_LINALG_MODULE @@ -44,6 +45,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT); mp_float_t *Larray = (mp_float_t *)L->array; @@ -110,6 +112,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky); static mp_obj_t linalg_det(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; mp_float_t *tmp = m_new(mp_float_t, N * N); @@ -182,6 +185,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det); static mp_obj_t linalg_eig(mp_obj_t oin) { ndarray_obj_t *in = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(in->dtype) uint8_t *iarray = (uint8_t *)in->array; size_t S = in->shape[ULAB_MAX_DIMS - 1]; mp_float_t *array = m_new(mp_float_t, S*S); @@ -243,6 +247,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig); //| static mp_obj_t linalg_inv(mp_obj_t o_in) { ndarray_obj_t *ndarray = tools_object_is_square(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT); @@ -305,6 +310,7 @@ static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1))); } else if(mp_obj_is_type(x, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; // always get a float, so that we don't have to resolve the dtype later mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 34a35a3d..dab1f682 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -22,6 +22,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "./carray/carray_tools.h" #include "numerical.h" enum NUMERICAL_FUNCTION_TYPE { @@ -97,6 +98,7 @@ static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { bool anytype = optype == NUMERICAL_ALL ? 1 : 0; if(mp_obj_is_type(oin, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; if(ndarray->len == 0) { // return immediately with empty arrays if(optype == NUMERICAL_ALL) { @@ -229,6 +231,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) { + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); @@ -550,9 +553,11 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m case NUMERICAL_MAX: case NUMERICAL_ARGMIN: case NUMERICAL_ARGMAX: + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) return numerical_argmin_argmax_ndarray(ndarray, axis, optype); case NUMERICAL_SUM: case NUMERICAL_MEAN: + 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")); @@ -575,6 +580,7 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla } else { ndarray = ndarray_copy_view(MP_OBJ_TO_PTR(oin)); } + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) int8_t ax = 0; if(axis == mp_const_none) { @@ -677,6 +683,7 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } 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")); @@ -780,6 +787,8 @@ static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) { } 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")); } @@ -868,6 +877,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) int8_t ax = args[2].u_int; if(ax < 0) ax += ndarray->ndim; @@ -951,7 +961,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); if(args[1].u_obj == mp_const_none) { // flip the flattened array results = ndarray_new_linear_array(ndarray->len, ndarray->dtype); - ndarray_copy_array(ndarray, results); + ndarray_copy_array(ndarray, results, 0); uint8_t *rarray = (uint8_t *)results->array; rarray += (results->len - 1) * results->itemsize; results->array = rarray; diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index a6559ff8..9e9c3fd1 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -19,6 +19,7 @@ #include "numpy.h" #include "../ulab_create.h" #include "approx.h" +#include "carray/carray.h" #include "compare.h" #include "fft/fft.h" #include "filter.h" @@ -125,6 +126,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) }, { MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) }, { MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) }, + #if ULAB_SUPPORTS_COMPLEX + { MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_INT(NDARRAY_COMPLEX) }, + #endif // modules of numpy #if ULAB_NUMPY_HAS_FFT_MODULE { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) }, @@ -350,7 +354,10 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_VECTORIZE { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vectorise_vectorize_obj }, #endif - + #if ULAB_SUPPORTS_COMPLEX + { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + #endif }; static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table); diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 124d3bc8..0e3476eb 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -19,6 +19,7 @@ #include "../ulab.h" #include "linalg/linalg_tools.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "poly.h" #if ULAB_NUMPY_HAS_POLYFIT @@ -27,6 +28,12 @@ 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")); } + #if ULAB_SUPPORTS_COMPLEX + if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) + } + #endif size_t lenx = 0, leny = 0; uint8_t deg = 0; mp_float_t *x, *XT, *y, *prod; @@ -142,6 +149,17 @@ 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 ULAB_SUPPORTS_COMPLEX + ndarray_obj_t *input; + if(MP_OBJ_IS_TYPE(o_p, &ulab_ndarray_type)) { + input = MP_OBJ_TO_PTR(o_p); + COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) + } + if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) { + input = MP_OBJ_TO_PTR(o_x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) + } + #endif // p had better be a one-dimensional standard iterable uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p)); mp_float_t *p = m_new(mp_float_t, plen); diff --git a/code/numpy/stats.c b/code/numpy/stats.c index a63964fe..2d348893 100644 --- a/code/numpy/stats.c +++ b/code/numpy/stats.c @@ -21,6 +21,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "stats.h" #if ULAB_MAX_DIMS > 1 @@ -36,6 +37,7 @@ static mp_obj_t stats_trace(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) mp_float_t trace = 0.0; for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) { int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]); diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 2c2d2dbd..026f9e57 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -18,6 +18,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "transform.h" #if ULAB_MAX_DIMS > 1 @@ -39,6 +40,9 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { } ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1); ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2); + COMPLEX_DTYPE_NOT_IMPLEMENTED(m1->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(m2->dtype) + uint8_t *array1 = (uint8_t *)m1->array; uint8_t *array2 = (uint8_t *)m2->array; diff --git a/code/numpy/vector.c b/code/numpy/vector.c index ceba2559..e33acd59 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -22,6 +22,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "vector.h" //| """Element-by-element functions @@ -39,6 +40,7 @@ static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float ndarray_obj_t *ndarray = NULL; if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype) uint8_t *sarray = (uint8_t *)source->array; ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); mp_float_t *array = (mp_float_t *)ndarray->array; @@ -169,6 +171,7 @@ mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ int8_t n = args[1].u_int; 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) 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; @@ -238,7 +241,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan); mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) { ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype) + ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_y->dtype) uint8_t ndim = 0; size_t *shape = m_new(size_t, ULAB_MAX_DIMS); @@ -544,6 +550,7 @@ static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_ar mp_obj_t fvalue; 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); diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index cc559b59..6116039f 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -18,6 +18,7 @@ #include "../../ulab.h" #include "../../ndarray.h" +#include "../../numpy/carray/carray_tools.h" #include "../../numpy/fft/fft_tools.h" #if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM @@ -68,6 +69,12 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar 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")); } + #if ULAB_SUPPORTS_COMPLEX + if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) + } + #endif size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj)); ndarray_obj_t *y = ndarray_new_linear_array(lenx, NDARRAY_FLOAT); mp_float_t *yarray = (mp_float_t *)y->array; diff --git a/code/ulab.h b/code/ulab.h index 248047c8..c0a5b45f 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -31,6 +31,10 @@ #include ULAB_CONFIG_FILE #endif +// Adds support for complex ndarrays +#ifndef ULAB_SUPPORTS_COMPLEX +#define ULAB_SUPPORTS_COMPLEX (1) +#endif // Determines, whether scipy is defined in ulab. The sub-modules and functions // of scipy have to be defined separately diff --git a/code/ulab_create.c b/code/ulab_create.c index a93ec741..efee18ef 100644 --- a/code/ulab_create.c +++ b/code/ulab_create.c @@ -19,6 +19,7 @@ #include "ulab.h" #include "ulab_create.h" +#include "ulab_tools.h" #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) { @@ -530,7 +531,11 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { size_t sz = 1; if(dtype != NDARRAY_BOOL) { // mp_binary_get_size doesn't work with Booleans - sz = mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + sz = mp_binary_get_complex_size(dtype); + #else + sz = mp_binary_get_size('@', dtype, NULL); + #endif } if(bufinfo.len < offset) { mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length")); diff --git a/code/ulab_tools.c b/code/ulab_tools.c index acd3d8a5..7c5311c4 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -231,3 +231,13 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { return ndarray; } #endif + +#if ULAB_SUPPORTS_COMPLEX +uint8_t mp_binary_get_complex_size(uint8_t dtype) { + if(dtype == NDARRAY_COMPLEX) { + return 2 * (uint8_t)mp_binary_get_size('@', NDARRAY_FLOAT, NULL); + } else { + return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + } +} +#endif \ No newline at end of file diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 378e4f0c..7d849fc0 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -34,4 +34,6 @@ void *ndarray_set_float_function(uint8_t ); shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); + +uint8_t mp_binary_get_complex_size(uint8_t ); #endif From 58b0db5171990db44cafb7ef0f68f2aea158fa3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 19:57:34 +0100 Subject: [PATCH 03/58] add missing newline character --- code/ulab_tools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 7c5311c4..6656e2b4 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -240,4 +240,4 @@ uint8_t mp_binary_get_complex_size(uint8_t dtype) { return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); } } -#endif \ No newline at end of file +#endif From fc8a9bde8535ed62f4ef249b43d8ea4436fcafee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:03:11 +0100 Subject: [PATCH 04/58] fix uppercase mp_obj_is_type macro calls --- code/numpy/carray/carray.c | 4 ++-- code/numpy/poly.c | 4 ++-- code/scipy/signal/signal.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index cc3c7342..1600afbe 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -24,7 +24,7 @@ #if ULAB_SUPPORTS_COMPLEX STATIC mp_obj_t carray_real(mp_obj_t _source) { - if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); @@ -44,7 +44,7 @@ STATIC mp_obj_t carray_real(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); STATIC mp_obj_t carray_imag(mp_obj_t _source) { - if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 0e3476eb..e320983b 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -151,11 +151,11 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { } #if ULAB_SUPPORTS_COMPLEX ndarray_obj_t *input; - if(MP_OBJ_IS_TYPE(o_p, &ulab_ndarray_type)) { + if(mp_obj_is_type(o_p, &ulab_ndarray_type)) { input = MP_OBJ_TO_PTR(o_p); COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) } - if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) { + if(mp_obj_is_type(o_x, &ulab_ndarray_type)) { input = MP_OBJ_TO_PTR(o_x); COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) } diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 6116039f..7ab471a3 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -70,7 +70,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(translate("sosfilt requires iterable arguments")); } #if ULAB_SUPPORTS_COMPLEX - if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { + if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From ebcf600b4bc4e6ec61ac38c94121e93fafa10de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:05:45 +0100 Subject: [PATCH 05/58] fix macro call in poly.c --- code/numpy/poly.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/poly.c b/code/numpy/poly.c index e320983b..97ee5c75 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -29,7 +29,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(translate("input data must be an iterable")); } #if ULAB_SUPPORTS_COMPLEX - if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) { + if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From 551221977797cec49038f607998b2e3f3f798e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 20 Nov 2021 11:10:06 +0100 Subject: [PATCH 06/58] add complex utilities --- code/micropython.mk | 2 + code/numpy/carray/carray.c | 65 ++++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 18 +++++++++ code/numpy/carray/carray_tools.c | 28 ++++++++++++++ code/numpy/carray/carray_tools.h | 25 ++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 code/numpy/carray/carray.c create mode 100644 code/numpy/carray/carray.h create mode 100644 code/numpy/carray/carray_tools.c create mode 100644 code/numpy/carray/carray_tools.h diff --git a/code/micropython.mk b/code/micropython.mk index 03d3fd40..64ab6152 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -13,6 +13,8 @@ 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/compare.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c SRC_USERMOD += $(USERMODULES_DIR)/ulab_create.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft_tools.c diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c new file mode 100644 index 00000000..cc3c7342 --- /dev/null +++ b/code/numpy/carray/carray.c @@ -0,0 +1,65 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#include +#include +#include +#include "py/obj.h" +#include "py/objint.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/misc.h" + +#include "../../ulab.h" +#include "../../ndarray.h" + +#if ULAB_SUPPORTS_COMPLEX + +STATIC mp_obj_t carray_real(mp_obj_t _source) { + if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->dtype != NDARRAY_COMPLEX) { + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + ndarray_copy_array(source, target, 0); + return MP_OBJ_FROM_PTR(target); + } else { // the input is most definitely a complex array + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + ndarray_copy_array(source, target, 0); + return MP_OBJ_FROM_PTR(target); + } + } else { + mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); + +STATIC mp_obj_t carray_imag(mp_obj_t _source) { + if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + return MP_OBJ_FROM_PTR(target); + } else { // the input is most definitely a complex array + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + ndarray_copy_array(source, target, source->itemsize / 2); + return MP_OBJ_FROM_PTR(target); + } + } else { + mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); + +#endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h new file mode 100644 index 00000000..91362c08 --- /dev/null +++ b/code/numpy/carray/carray.h @@ -0,0 +1,18 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#ifndef _CARRAY_ +#define _CARRAY_ + +MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); +MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); + +#endif diff --git a/code/numpy/carray/carray_tools.c b/code/numpy/carray/carray_tools.c new file mode 100644 index 00000000..c6fb8de2 --- /dev/null +++ b/code/numpy/carray/carray_tools.c @@ -0,0 +1,28 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#include +#include +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "py/misc.h" + +#include "../../ulab.h" +#include "../../ndarray.h" + +#if ULAB_SUPPORTS_COMPLEX + +void raise_complex_NotImplementedError(void) { + mp_raise_NotImplementedError(translate("not implemented for complex dtype")); +} + +#endif diff --git a/code/numpy/carray/carray_tools.h b/code/numpy/carray/carray_tools.h new file mode 100644 index 00000000..3b28e1c4 --- /dev/null +++ b/code/numpy/carray/carray_tools.h @@ -0,0 +1,25 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#ifndef _CARRAY_TOOLS_ +#define _CARRAY_TOOLS_ + +void raise_complex_NotImplementedError(void); + +#if ULAB_SUPPORTS_COMPLEX + #define NOT_IMPLEMENTED_FOR_COMPLEX() raise_complex_NotImplementedError(); + #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) if((dtype) == NDARRAY_COMPLEX) raise_complex_NotImplementedError(); +#else + #define NOT_IMPLEMENTED_FOR_COMPLEX() // do nothing + #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) // do nothing +#endif + +#endif From ee3181a1d9c695d02a4554a6f7c11cc8f3003795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 18:40:12 +0100 Subject: [PATCH 07/58] ulab can now be compiled with complex support --- code/ndarray.c | 108 ++++++++++++++++++++++++++++++------- code/ndarray.h | 5 +- code/numpy/approx.c | 6 +++ code/numpy/compare.c | 11 ++++ code/numpy/fft/fft.c | 2 + code/numpy/fft/fft_tools.c | 3 ++ code/numpy/filter.c | 3 ++ code/numpy/linalg/linalg.c | 6 +++ code/numpy/numerical.c | 12 ++++- code/numpy/numpy.c | 9 +++- code/numpy/poly.c | 18 +++++++ code/numpy/stats.c | 2 + code/numpy/transform.c | 4 ++ code/numpy/vector.c | 7 +++ code/scipy/signal/signal.c | 7 +++ code/ulab.h | 4 ++ code/ulab_create.c | 7 ++- code/ulab_tools.c | 10 ++++ code/ulab_tools.h | 2 + 19 files changed, 204 insertions(+), 22 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 45e19c26..5286290f 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -171,7 +171,11 @@ void ndarray_rewind_array(uint8_t ndim, uint8_t *array, size_t *shape, int32_t * static int32_t *strides_from_shape(size_t *shape, uint8_t dtype) { // returns a strides array that corresponds to a dense array with the prescribed shape int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_complex_size(dtype); + #else + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + #endif for(uint8_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * shape[i-1]; } @@ -231,7 +235,13 @@ void ndarray_dtype_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin mp_print_str(print, "uint16')"); } else if(self->dtype == NDARRAY_INT16) { mp_print_str(print, "int16')"); - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if(self->dtype == NDARRAY_COMPLEX) { + mp_print_str(print, "complex')"); + } + #endif + else { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT mp_print_str(print, "float32')"); #else @@ -280,7 +290,13 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t _dtype = NDARRAY_INT16; } else if(memcmp(_dtype_, "float", 5) == 0) { _dtype = NDARRAY_FLOAT; - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if(memcmp(_dtype_, "complex", 7) == 0) { + _dtype = NDARRAY_COMPLEX; + } + #endif + else { mp_raise_TypeError(translate("data type not understood")); } } @@ -308,7 +324,11 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) { GET_STR_DATA_LEN(self_in, _dtype, len); if((len != 1) || ((*_dtype != NDARRAY_BOOL) && (*_dtype != NDARRAY_UINT8) && (*_dtype != NDARRAY_INT8) && (*_dtype != NDARRAY_UINT16) - && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT))) { + && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT) + #if ULAB_SUPPORTS_COMPLEX + && (*_dtype != NDARRAY_COMPLEX) + #endif + )) { mp_raise_TypeError(translate("data type not understood")); } dtype = *_dtype; @@ -361,32 +381,55 @@ mp_obj_t ndarray_get_item(ndarray_obj_t *ndarray, void *array) { } } +static void ndarray_print_element(const mp_print_t *print, ndarray_obj_t *ndarray, uint8_t *array) { + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + // real part first + mp_float_t fvalue = *(mp_float_t *)array; + mp_obj_print_helper(print, mp_obj_new_float(fvalue), PRINT_REPR); + // imaginary part + array += ndarray->itemsize / 2; + fvalue = *(mp_float_t *)array; + if(fvalue >= MICROPY_FLOAT_CONST(0.0) || isnan(fvalue)) { + mp_print_str(print, "+"); + } + array += ndarray->itemsize / 2; + mp_obj_print_helper(print, mp_obj_new_float(fvalue), PRINT_REPR); + mp_print_str(print, "j"); + } else { + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + } + #else + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + #endif +} + static void ndarray_print_row(const mp_print_t *print, ndarray_obj_t * ndarray, uint8_t *array, size_t stride, size_t n) { if(n == 0) { return; } mp_print_str(print, "["); if((n <= ndarray_print_threshold) || (n <= 2*ndarray_print_edgeitems)) { // if the array is short, print everything - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); array += stride; for(size_t i=1; i < n; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } } else { mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); array += stride; for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } mp_printf(print, ", ..., "); - array += stride * (n - 2 * ndarray_print_edgeitems); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + array += stride * (n - 2 * ndarray_print_edgeitems); + ndarray_print_element(print, ndarray, array); array += stride; for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } } mp_print_str(print, "]"); @@ -494,7 +537,19 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t } } else { while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + #if ULAB_SUPPORTS_COMPLEX + mp_float_t real; + mp_float_t imag; + if(dtype == NDARRAY_COMPLEX) { + mp_obj_get_complex(item, &real, &imag); + ndarray_set_value(NDARRAY_FLOAT, ndarray->array, (*idx)++, mp_obj_new_float(real)); + ndarray_set_value(NDARRAY_FLOAT, ndarray->array, (*idx)++, mp_obj_new_float(imag)); + } else { + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + } + #else + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + #endif } } } @@ -518,7 +573,11 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; ndarray->ndim = ndim; ndarray->len = ndim == 0 ? 0 : 1; - ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + ndarray->itemsize = mp_binary_get_complex_size(dtype); + #else + ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + #endif int32_t *_strides; if(strides == NULL) { _strides = strides_from_shape(shape, ndarray->dtype); @@ -546,7 +605,11 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt // 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] = dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_complex_size(dtype); + #else + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_size('@', dtype, NULL); + #endif for(size_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * MAX(1, shape[i-1]); } @@ -567,13 +630,18 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt return ndarray_new_dense_ndarray(_shape->len, shape, dtype); } -void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { +void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target, uint8_t shift) { // TODO: if the array is dense, the content could be copied in a single pass // copies the content of source->array into a new dense void pointer // it is assumed that the dtypes in source and target are the same // Since the target is a new array, it is supposed to be dense uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; + #if ULAB_SUPPORTS_COMPLEX + if(source->dtype == NDARRAY_COMPLEX) { + sarray += shift; + } + #endif #if ULAB_MAX_DIMS > 3 size_t i = 0; @@ -589,7 +657,7 @@ void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { #endif size_t l = 0; do { - memcpy(tarray, sarray, source->itemsize); + memcpy(tarray, sarray, target->itemsize); tarray += target->itemsize; sarray += source->strides[ULAB_MAX_DIMS - 1]; l++; @@ -648,7 +716,7 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { dtype = NDARRAY_BOOLEAN; } ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype); - ndarray_copy_array(source, ndarray); + ndarray_copy_array(source, ndarray, 0); return ndarray; } @@ -1805,7 +1873,11 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + uint8_t itemsize = mp_binary_get_complex_size(self->dtype); + #else + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #endif for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); @@ -1914,7 +1986,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { mp_raise_ValueError(translate("cannot assign new shape")); } ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); - ndarray_copy_array(source, ndarray); + ndarray_copy_array(source, ndarray, 0); } return MP_OBJ_FROM_PTR(ndarray); } diff --git a/code/ndarray.h b/code/ndarray.h index 04abd965..dc827a68 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -77,6 +77,9 @@ enum NDARRAY_TYPE { NDARRAY_INT8 = 'b', NDARRAY_UINT16 = 'H', NDARRAY_INT16 = 'h', + #if ULAB_SUPPORTS_COMPLEX + NDARRAY_COMPLEX = 'c', + #endif NDARRAY_FLOAT = FLOAT_TYPECODE, }; @@ -138,7 +141,7 @@ 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 *); ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *); -void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *); +void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *, uint8_t ); MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj); mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 6ed5d7c2..85cdbf78 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -19,6 +19,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "approx.h" //| """Numerical approximation methods""" @@ -60,6 +61,9 @@ STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0); ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) + 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")); } @@ -157,6 +161,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype) ndarray_obj_t *x; mp_float_t mean = MICROPY_FLOAT_CONST(0.0); if(y->len < 2) { @@ -174,6 +179,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * if(args[1].u_obj != mp_const_none) { 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")); } diff --git a/code/numpy/compare.c b/code/numpy/compare.c index b9154569..5a820725 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -20,11 +20,17 @@ #include "../ulab.h" #include "../ndarray_operators.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "compare.h" 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); + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + NOT_IMPLEMENTED_FOR_COMPLEX() + } + #endif uint8_t ndim = 0; size_t *shape = m_new(size_t, ULAB_MAX_DIMS); int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); @@ -197,6 +203,7 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { } } else if(mp_obj_is_type(_x, &ulab_ndarray_type)) { ndarray_obj_t *x = MP_OBJ_TO_PTR(_x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL); // At this point, results is all False uint8_t *rarray = (uint8_t *)results->array; @@ -313,6 +320,10 @@ mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0); ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype) + int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS); diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 6f6534f4..3b3f66a5 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -20,6 +20,7 @@ #include "py/obj.h" #include "py/objarray.h" +#include "../carray/carray_tools.h" #include "fft.h" //| """Frequency-domain functions""" @@ -60,6 +61,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft); //| 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); } else { diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 6dd2ca47..a08f2244 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -13,6 +13,7 @@ #include "../../ndarray.h" #include "../../ulab_tools.h" +#include "../carray/carray_tools.h" #include "fft_tools.h" #ifndef MP_PI @@ -95,6 +96,7 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i 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")); } #endif @@ -122,6 +124,7 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im); #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")); } #endif diff --git a/code/numpy/filter.c b/code/numpy/filter.c index bf2d16cd..fff122ef 100644 --- a/code/numpy/filter.c +++ b/code/numpy/filter.c @@ -21,6 +21,7 @@ #include "../ulab.h" #include "../scipy/signal/signal.h" +#include "carray/carray_tools.h" #include "filter.h" #if ULAB_NUMPY_HAS_CONVOLVE @@ -40,6 +41,8 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) // deal with linear arrays only #if ULAB_MAX_DIMS > 1 if((a->ndim != 1) || (c->ndim != 1)) { diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 596280fe..d9343fb8 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -22,6 +22,7 @@ #include "../../ulab.h" #include "../../ulab_tools.h" +#include "../carray/carray_tools.h" #include "linalg.h" #if ULAB_NUMPY_HAS_LINALG_MODULE @@ -44,6 +45,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT); mp_float_t *Larray = (mp_float_t *)L->array; @@ -110,6 +112,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky); static mp_obj_t linalg_det(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; mp_float_t *tmp = m_new(mp_float_t, N * N); @@ -182,6 +185,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det); static mp_obj_t linalg_eig(mp_obj_t oin) { ndarray_obj_t *in = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(in->dtype) uint8_t *iarray = (uint8_t *)in->array; size_t S = in->shape[ULAB_MAX_DIMS - 1]; mp_float_t *array = m_new(mp_float_t, S*S); @@ -243,6 +247,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig); //| static mp_obj_t linalg_inv(mp_obj_t o_in) { ndarray_obj_t *ndarray = tools_object_is_square(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT); @@ -305,6 +310,7 @@ static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1))); } else if(mp_obj_is_type(x, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; // always get a float, so that we don't have to resolve the dtype later mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 9dae02f5..51507f78 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -22,6 +22,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "./carray/carray_tools.h" #include "numerical.h" enum NUMERICAL_FUNCTION_TYPE { @@ -97,6 +98,7 @@ static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { bool anytype = optype == NUMERICAL_ALL ? 1 : 0; if(mp_obj_is_type(oin, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; if(ndarray->len == 0) { // return immediately with empty arrays if(optype == NUMERICAL_ALL) { @@ -229,6 +231,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) { + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); @@ -550,9 +553,11 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m case NUMERICAL_MAX: case NUMERICAL_ARGMIN: case NUMERICAL_ARGMAX: + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) return numerical_argmin_argmax_ndarray(ndarray, axis, optype); case NUMERICAL_SUM: case NUMERICAL_MEAN: + 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")); @@ -575,6 +580,7 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla } else { ndarray = ndarray_copy_view(MP_OBJ_TO_PTR(oin)); } + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) int8_t ax = 0; if(axis == mp_const_none) { @@ -677,6 +683,7 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } 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")); @@ -780,6 +787,8 @@ static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) { } 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")); } @@ -868,6 +877,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) int8_t ax = args[2].u_int; if(ax < 0) ax += ndarray->ndim; @@ -951,7 +961,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); if(args[1].u_obj == mp_const_none) { // flip the flattened array results = ndarray_new_linear_array(ndarray->len, ndarray->dtype); - ndarray_copy_array(ndarray, results); + ndarray_copy_array(ndarray, results, 0); uint8_t *rarray = (uint8_t *)results->array; rarray += (results->len - 1) * results->itemsize; results->array = rarray; diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index a6559ff8..9e9c3fd1 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -19,6 +19,7 @@ #include "numpy.h" #include "../ulab_create.h" #include "approx.h" +#include "carray/carray.h" #include "compare.h" #include "fft/fft.h" #include "filter.h" @@ -125,6 +126,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) }, { MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) }, { MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) }, + #if ULAB_SUPPORTS_COMPLEX + { MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_INT(NDARRAY_COMPLEX) }, + #endif // modules of numpy #if ULAB_NUMPY_HAS_FFT_MODULE { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) }, @@ -350,7 +354,10 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_VECTORIZE { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vectorise_vectorize_obj }, #endif - + #if ULAB_SUPPORTS_COMPLEX + { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + #endif }; static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table); diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 124d3bc8..0e3476eb 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -19,6 +19,7 @@ #include "../ulab.h" #include "linalg/linalg_tools.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "poly.h" #if ULAB_NUMPY_HAS_POLYFIT @@ -27,6 +28,12 @@ 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")); } + #if ULAB_SUPPORTS_COMPLEX + if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) + } + #endif size_t lenx = 0, leny = 0; uint8_t deg = 0; mp_float_t *x, *XT, *y, *prod; @@ -142,6 +149,17 @@ 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 ULAB_SUPPORTS_COMPLEX + ndarray_obj_t *input; + if(MP_OBJ_IS_TYPE(o_p, &ulab_ndarray_type)) { + input = MP_OBJ_TO_PTR(o_p); + COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) + } + if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) { + input = MP_OBJ_TO_PTR(o_x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) + } + #endif // p had better be a one-dimensional standard iterable uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p)); mp_float_t *p = m_new(mp_float_t, plen); diff --git a/code/numpy/stats.c b/code/numpy/stats.c index a63964fe..2d348893 100644 --- a/code/numpy/stats.c +++ b/code/numpy/stats.c @@ -21,6 +21,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "stats.h" #if ULAB_MAX_DIMS > 1 @@ -36,6 +37,7 @@ static mp_obj_t stats_trace(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) mp_float_t trace = 0.0; for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) { int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]); diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 2c2d2dbd..026f9e57 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -18,6 +18,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "transform.h" #if ULAB_MAX_DIMS > 1 @@ -39,6 +40,9 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { } ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1); ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2); + COMPLEX_DTYPE_NOT_IMPLEMENTED(m1->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(m2->dtype) + uint8_t *array1 = (uint8_t *)m1->array; uint8_t *array2 = (uint8_t *)m2->array; diff --git a/code/numpy/vector.c b/code/numpy/vector.c index ceba2559..e33acd59 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -22,6 +22,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "vector.h" //| """Element-by-element functions @@ -39,6 +40,7 @@ static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float ndarray_obj_t *ndarray = NULL; if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype) uint8_t *sarray = (uint8_t *)source->array; ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); mp_float_t *array = (mp_float_t *)ndarray->array; @@ -169,6 +171,7 @@ mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ int8_t n = args[1].u_int; 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) 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; @@ -238,7 +241,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan); mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) { ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype) + ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_y->dtype) uint8_t ndim = 0; size_t *shape = m_new(size_t, ULAB_MAX_DIMS); @@ -544,6 +550,7 @@ static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_ar mp_obj_t fvalue; 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); diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index cc559b59..6116039f 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -18,6 +18,7 @@ #include "../../ulab.h" #include "../../ndarray.h" +#include "../../numpy/carray/carray_tools.h" #include "../../numpy/fft/fft_tools.h" #if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM @@ -68,6 +69,12 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar 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")); } + #if ULAB_SUPPORTS_COMPLEX + if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) + } + #endif size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj)); ndarray_obj_t *y = ndarray_new_linear_array(lenx, NDARRAY_FLOAT); mp_float_t *yarray = (mp_float_t *)y->array; diff --git a/code/ulab.h b/code/ulab.h index 248047c8..c0a5b45f 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -31,6 +31,10 @@ #include ULAB_CONFIG_FILE #endif +// Adds support for complex ndarrays +#ifndef ULAB_SUPPORTS_COMPLEX +#define ULAB_SUPPORTS_COMPLEX (1) +#endif // Determines, whether scipy is defined in ulab. The sub-modules and functions // of scipy have to be defined separately diff --git a/code/ulab_create.c b/code/ulab_create.c index a93ec741..efee18ef 100644 --- a/code/ulab_create.c +++ b/code/ulab_create.c @@ -19,6 +19,7 @@ #include "ulab.h" #include "ulab_create.h" +#include "ulab_tools.h" #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) { @@ -530,7 +531,11 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { size_t sz = 1; if(dtype != NDARRAY_BOOL) { // mp_binary_get_size doesn't work with Booleans - sz = mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + sz = mp_binary_get_complex_size(dtype); + #else + sz = mp_binary_get_size('@', dtype, NULL); + #endif } if(bufinfo.len < offset) { mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length")); diff --git a/code/ulab_tools.c b/code/ulab_tools.c index acd3d8a5..7c5311c4 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -231,3 +231,13 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { return ndarray; } #endif + +#if ULAB_SUPPORTS_COMPLEX +uint8_t mp_binary_get_complex_size(uint8_t dtype) { + if(dtype == NDARRAY_COMPLEX) { + return 2 * (uint8_t)mp_binary_get_size('@', NDARRAY_FLOAT, NULL); + } else { + return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + } +} +#endif \ No newline at end of file diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 378e4f0c..7d849fc0 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -34,4 +34,6 @@ void *ndarray_set_float_function(uint8_t ); shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); + +uint8_t mp_binary_get_complex_size(uint8_t ); #endif From c603aff3a03689300d1d0b2d73f622af814f44aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 19:57:34 +0100 Subject: [PATCH 08/58] add missing newline character --- code/ulab_tools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 7c5311c4..6656e2b4 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -240,4 +240,4 @@ uint8_t mp_binary_get_complex_size(uint8_t dtype) { return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); } } -#endif \ No newline at end of file +#endif From 6b85ca08e3ed4370445acf58e40324a100ec09ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:03:11 +0100 Subject: [PATCH 09/58] fix uppercase mp_obj_is_type macro calls --- code/numpy/carray/carray.c | 4 ++-- code/numpy/poly.c | 4 ++-- code/scipy/signal/signal.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index cc3c7342..1600afbe 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -24,7 +24,7 @@ #if ULAB_SUPPORTS_COMPLEX STATIC mp_obj_t carray_real(mp_obj_t _source) { - if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); @@ -44,7 +44,7 @@ STATIC mp_obj_t carray_real(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); STATIC mp_obj_t carray_imag(mp_obj_t _source) { - if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 0e3476eb..e320983b 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -151,11 +151,11 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { } #if ULAB_SUPPORTS_COMPLEX ndarray_obj_t *input; - if(MP_OBJ_IS_TYPE(o_p, &ulab_ndarray_type)) { + if(mp_obj_is_type(o_p, &ulab_ndarray_type)) { input = MP_OBJ_TO_PTR(o_p); COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) } - if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) { + if(mp_obj_is_type(o_x, &ulab_ndarray_type)) { input = MP_OBJ_TO_PTR(o_x); COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) } diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 6116039f..7ab471a3 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -70,7 +70,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(translate("sosfilt requires iterable arguments")); } #if ULAB_SUPPORTS_COMPLEX - if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { + if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From cd7c1de441c3a0b1ee2fe5d1c733bc9b59edda29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:05:45 +0100 Subject: [PATCH 10/58] fix macro call in poly.c --- code/numpy/poly.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/poly.c b/code/numpy/poly.c index e320983b..97ee5c75 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -29,7 +29,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(translate("input data must be an iterable")); } #if ULAB_SUPPORTS_COMPLEX - if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) { + if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From b9eea417bd52436368303ec57142e6f20e24b136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 1 Dec 2021 07:49:29 +0100 Subject: [PATCH 11/58] implement unary operators for complex arrays --- code/ndarray.c | 59 ++++++++++++++++++++++++-------------- code/numpy/carray/carray.c | 47 ++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 2 ++ code/numpy/numerical.c | 2 +- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 5286290f..77ebde5a 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -25,6 +25,7 @@ #include "ulab_tools.h" #include "ndarray.h" #include "ndarray_operators.h" +#include "numpy/carray/carray.h" mp_uint_t ndarray_print_threshold = NDARRAY_PRINT_THRESHOLD; mp_uint_t ndarray_print_edgeitems = NDARRAY_PRINT_EDGEITEMS; @@ -1841,30 +1842,44 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { switch (op) { #if NDARRAY_HAS_UNARY_OP_ABS case MP_UNARY_OP_ABS: - ndarray = ndarray_copy_view(self); - // if Boolean, NDARRAY_UINT8, or NDARRAY_UINT16, there is nothing to do - if(self->dtype == NDARRAY_INT8) { - int8_t *array = (int8_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) { - if(*array < 0) *array = -(*array); - } - } else if(self->dtype == NDARRAY_INT16) { - int16_t *array = (int16_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) { - if(*array < 0) *array = -(*array); - } + #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 = carray_abs(self, target); } else { - mp_float_t *array = (mp_float_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) { - if(*array < 0) *array = -(*array); + #endif + ndarray = ndarray_copy_view(self); + // if Boolean, NDARRAY_UINT8, or NDARRAY_UINT16, there is nothing to do + if(self->dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } else if(self->dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } else { + mp_float_t *array = (mp_float_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } } + #if ULAB_SUPPORTS_COMPLEX } + #endif return MP_OBJ_FROM_PTR(ndarray); break; #endif #if NDARRAY_HAS_UNARY_OP_INVERT case MP_UNARY_OP_INVERT: + #if ULAB_SUPPORTS_COMPLEX + if(self->dtype == NDARRAY_FLOAT || self->dtype == NDARRAY_COMPLEX) { + #else if(self->dtype == NDARRAY_FLOAT) { + #endif mp_raise_ValueError(translate("operation is not supported for given type")); } // we can invert the content byte by byte, no need to distinguish between different dtypes @@ -1873,11 +1888,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - #if ULAB_SUPPORTS_COMPLEX - uint8_t itemsize = mp_binary_get_complex_size(self->dtype); - #else - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); - #endif + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); @@ -1905,7 +1916,13 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { for(size_t i=0; i < self->len; i++, array++) *array = -(*array); } else { mp_float_t *array = (mp_float_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + size_t len = self->len; + #if ULAB_SUPPORTS_COMPLEX + if(self->dtype == NDARRAY_COMPLEX) { + len *= 2; + } + #endif + for(size_t i=0; i < len; i++, array++) *array = -(*array); } return MP_OBJ_FROM_PTR(ndarray); break; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 1600afbe..60dede8a 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -62,4 +62,51 @@ STATIC mp_obj_t carray_imag(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); +mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { + // calculates the absolute value of a complex array and returns a dense array + uint8_t *sarray = (uint8_t *)source->array; + mp_float_t *tarray = (mp_float_t *)target->array; + uint8_t itemsize = mp_binary_get_size('@', NDARRAY_FLOAT, NULL); + + #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 rvalue = *(mp_float_t *)sarray; + mp_float_t ivalue = *(mp_float_t *)(sarray + itemsize); + *tarray++ = MICROPY_FLOAT_C_FUN(sqrt)(rvalue * rvalue + ivalue * ivalue); + 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 + return MP_OBJ_FROM_PTR(target); +} + #endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 91362c08..113fe3ad 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -15,4 +15,6 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); +mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); + #endif diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 51507f78..46267d26 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -242,7 +242,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t return mp_obj_new_float(MICROPY_FLOAT_CONST(0.0)); } mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); - mp_float_t M =MICROPY_FLOAT_CONST(0.0); + mp_float_t M = MICROPY_FLOAT_CONST(0.0); mp_float_t m = MICROPY_FLOAT_CONST(0.0); mp_float_t S = MICROPY_FLOAT_CONST(0.0); mp_float_t s = MICROPY_FLOAT_CONST(0.0); From a1a391b90d5aac639a05337a987ccc4c15ff88ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 1 Dec 2021 21:37:05 +0100 Subject: [PATCH 12/58] implement complex exponential --- code/ndarray.c | 23 +++++++++++------ code/numpy/vector.c | 63 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 77ebde5a..6472b5a7 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -503,21 +503,28 @@ void ndarray_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t ki ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-4], "]"); #endif } + mp_print_str(print, ", dtype="); if(self->boolean) { - mp_print_str(print, ", dtype=bool)"); + mp_print_str(print, "bool)"); } else if(self->dtype == NDARRAY_UINT8) { - mp_print_str(print, ", dtype=uint8)"); + mp_print_str(print, "uint8)"); } else if(self->dtype == NDARRAY_INT8) { - mp_print_str(print, ", dtype=int8)"); + mp_print_str(print, "int8)"); } else if(self->dtype == NDARRAY_UINT16) { - mp_print_str(print, ", dtype=uint16)"); + mp_print_str(print, "uint16)"); } else if(self->dtype == NDARRAY_INT16) { - mp_print_str(print, ", dtype=int16)"); - } else { + mp_print_str(print, "int16)"); + } + #if ULAB_SUPPORTS_COMPLEX + else if(self->dtype == NDARRAY_COMPLEX) { + mp_print_str(print, "complex)"); + } + #endif /* ULAB_SUPPORTS_COMPLEX */ + else { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - mp_print_str(print, ", dtype=float32)"); + mp_print_str(print, "float32)"); #else - mp_print_str(print, ", dtype=float64)"); + mp_print_str(print, "float64)"); #endif } } diff --git a/code/numpy/vector.c b/code/numpy/vector.c index e33acd59..d2998c3d 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -401,7 +401,68 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc); //| ... //| -MATH_FUN_1(exp, exp); +static mp_obj_t vectorise_exp(mp_obj_t o_in) { + #if ULAB_SUPPORTS_COMPLEX + if(mp_obj_is_type(o_in, &mp_type_complex)) { + mp_float_t real, imag; + mp_obj_get_complex(o_in, &real, &imag); + mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); + 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(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); + 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 */ + return MP_OBJ_FROM_PTR(ndarray); + } + } + #endif + return vectorise_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp)); +} + MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp); #endif From add4f41b2f6203a9410529d3d5d7efdd6f7db40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 2 Dec 2021 21:21:38 +0100 Subject: [PATCH 13/58] implement iterator for complex arrays --- code/ndarray.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 6472b5a7..b05b8370 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -372,6 +372,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) { @@ -945,6 +953,11 @@ STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, dtype); uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; + + #if ULAB_SUPPORTS_COMPLEX + uint8_t complex_size = 2 * sizeof(mp_float_t); + #endif + #if ULAB_MAX_DIMS > 3 size_t i = 0; do { @@ -960,14 +973,26 @@ STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t l = 0; do { mp_obj_t item; - 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(floor)(f)); + #if ULAB_SUPPORTS_COMPLEX + if(source->dtype == NDARRAY_COMPLEX) { + if(dtype != NDARRAY_COMPLEX) { + mp_raise_TypeError(translate("cannot convert complex type")); + } else { + memcpy(tarray, sarray, complex_size); + } } else { - item = mp_binary_get_val_array(source->dtype, sarray, 0); + #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(floor)(f)); + } else { + item = mp_binary_get_val_array(source->dtype, sarray, 0); + } + ndarray_set_value(dtype, tarray, 0, item); + #if ULAB_SUPPORTS_COMPLEX } - ndarray_set_value(dtype, tarray, 0, item); + #endif tarray += target->itemsize; sarray += source->strides[ULAB_MAX_DIMS - 1]; l++; From 322145d3ace8942b38cba10bf14fed33443fd494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 2 Dec 2021 21:42:14 +0100 Subject: [PATCH 14/58] simplify binary_get_size function --- code/ndarray.c | 21 ++++----------------- code/ulab_create.c | 10 ++-------- code/ulab_tools.c | 11 +++++------ code/ulab_tools.h | 2 +- 4 files changed, 12 insertions(+), 32 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index b05b8370..eedc1fd7 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -172,11 +172,7 @@ void ndarray_rewind_array(uint8_t ndim, uint8_t *array, size_t *shape, int32_t * static int32_t *strides_from_shape(size_t *shape, uint8_t dtype) { // returns a strides array that corresponds to a dense array with the prescribed shape int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - #if ULAB_SUPPORTS_COMPLEX - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_complex_size(dtype); - #else - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); - #endif + strides[ULAB_MAX_DIMS-1] = (int32_t)ulab_binary_get_size(dtype); for(uint8_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * shape[i-1]; } @@ -544,7 +540,6 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t uint8_t *array = (uint8_t *)ndarray->array; array += *idx; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - // TODO: this might be wrong here: we have to check for the trueness of item if(mp_obj_is_true(item)) { *array = 1; } @@ -553,7 +548,7 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t } } else { while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - #if ULAB_SUPPORTS_COMPLEX + #if ULAB_SUPPORTS_COMPLEX mp_float_t real; mp_float_t imag; if(dtype == NDARRAY_COMPLEX) { @@ -589,11 +584,7 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; ndarray->ndim = ndim; ndarray->len = ndim == 0 ? 0 : 1; - #if ULAB_SUPPORTS_COMPLEX - ndarray->itemsize = mp_binary_get_complex_size(dtype); - #else - ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); - #endif + ndarray->itemsize = ulab_binary_get_size(dtype); int32_t *_strides; if(strides == NULL) { _strides = strides_from_shape(shape, ndarray->dtype); @@ -621,11 +612,7 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt // 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); - #if ULAB_SUPPORTS_COMPLEX - strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_complex_size(dtype); - #else - strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_size('@', dtype, NULL); - #endif + 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]); } diff --git a/code/ulab_create.c b/code/ulab_create.c index efee18ef..2dc1412b 100644 --- a/code/ulab_create.c +++ b/code/ulab_create.c @@ -529,14 +529,8 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw mp_buffer_info_t bufinfo; if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { - size_t sz = 1; - if(dtype != NDARRAY_BOOL) { // mp_binary_get_size doesn't work with Booleans - #if ULAB_SUPPORTS_COMPLEX - sz = mp_binary_get_complex_size(dtype); - #else - sz = mp_binary_get_size('@', dtype, NULL); - #endif - } + 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")); } diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 6656e2b4..d0537230 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -232,12 +232,11 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { } #endif -#if ULAB_SUPPORTS_COMPLEX -uint8_t mp_binary_get_complex_size(uint8_t dtype) { +uint8_t ulab_binary_get_size(uint8_t dtype) { + #if ULAB_SUPPORTS_COMPLEX if(dtype == NDARRAY_COMPLEX) { - return 2 * (uint8_t)mp_binary_get_size('@', NDARRAY_FLOAT, NULL); - } else { - return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + return 2 * (uint8_t)sizeof(mp_float_t); } + #endif + return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); } -#endif diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 7d849fc0..216dbaba 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -35,5 +35,5 @@ void *ndarray_set_float_function(uint8_t ); shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); -uint8_t mp_binary_get_complex_size(uint8_t ); +uint8_t ulab_binary_get_size(uint8_t ); #endif From 9a2449c0c6ea667caca0683b896eb1abe8a54314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 3 Dec 2021 00:06:20 +0100 Subject: [PATCH 15/58] implement slicing for complex dtype --- code/ndarray.c | 160 ++++++++++++++++++++++++++++++++++++++----------- code/ndarray.h | 6 +- 2 files changed, 127 insertions(+), 39 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index eedc1fd7..dd565f57 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1168,9 +1168,70 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { } uint8_t *rarray = (uint8_t *)values->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; + 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(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]); + #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]); + #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; + } + #endif } if(view->dtype == NDARRAY_UINT8) { @@ -1217,21 +1278,21 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { } 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); + 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); + ASSIGNMENT_LOOP(view, int16_t, mp_float_t, lstrides, rarray, rstrides); } - } else { // the dtype must be an mp_float_t now + } 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); + 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); + 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); + 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); + ASSIGNMENT_LOOP(view, mp_float_t, mp_float_t, lstrides, rarray, rstrides); } } } @@ -1269,16 +1330,16 @@ static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarra // assigns values to a Boolean-indexed array // first we have to find out how many trues there are uint8_t *iarray = (uint8_t *)index->array; + size_t istride = index->strides[ULAB_MAX_DIMS - 1]; size_t count = 0; for(size_t i=0; i < index->len; i++) { count += *iarray; - iarray += index->strides[ULAB_MAX_DIMS - 1]; + iarray += istride; } // re-wind the index array iarray = index->array; uint8_t *varray = (uint8_t *)values->array; size_t vstride; - size_t istride = index->strides[ULAB_MAX_DIMS - 1]; if(count == values->len) { // there are as many values as true indices @@ -1287,65 +1348,92 @@ static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarra // there is a single value vstride = 0; } + + #if ULAB_SUPPORTS_COMPLEX + if(values->dtype == NDARRAY_COMPLEX) { + if(ndarray->dtype != NDARRAY_COMPLEX) { + mp_raise_TypeError(translate("cannot convert complex to dtype")); + } else { + uint8_t *array = (uint8_t *)ndarray->array; + for(size_t i = 0; i < ndarray->len; i++) { + if(*iarray) { + memcpy(array, varray, ndarray->itemsize); + varray += vstride; + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + iarray += istride; + } while(0); + return MP_OBJ_FROM_PTR(ndarray); + } + } + #endif + + int32_t lstrides = ndarray->strides[ULAB_MAX_DIMS - 1] / ndarray->itemsize; + if(ndarray->dtype == NDARRAY_UINT8) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else if(ndarray->dtype == NDARRAY_INT8) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else if(ndarray->dtype == NDARRAY_UINT16) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else if(ndarray->dtype == NDARRAY_INT16) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else { + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + lstrides *= 2; + } + #endif if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } return MP_OBJ_FROM_PTR(ndarray); @@ -1360,7 +1448,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra if(values == NULL) { // return value(s) return ndarray_from_boolean_index(ndarray, nindex); } else { // assign value(s) - ndarray_assign_from_boolean_index(ndarray, index, values); + ndarray_assign_from_boolean_index(ndarray, nindex, values); } } if(mp_obj_is_type(index, &mp_type_tuple) || mp_obj_is_int(index) || mp_obj_is_type(index, &mp_type_slice)) { @@ -1379,7 +1467,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra if(values == NULL) { // return value(s) // if the view has been reduced to nothing, return a single value if(view->ndim == 0) { - return mp_binary_get_val_array(view->dtype, view->array, 0); + return ndarray_get_item(view, view->array); } else { return MP_OBJ_FROM_PTR(view); } diff --git a/code/ndarray.h b/code/ndarray.h index dc827a68..0b4be867 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -204,15 +204,15 @@ mp_int_t ndarray_get_buffer(mp_obj_t , mp_buffer_info_t *, mp_uint_t ); ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); -#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, iarray, istride, varray, vstride)\ +#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, lstrides, iarray, istride, varray, vstride)\ type_left *array = (type_left *)(ndarray)->array;\ for(size_t i=0; i < (ndarray)->len; i++) {\ if(*(iarray)) {\ *array = (type_left)(*((type_right *)(varray)));\ + (varray) += (vstride);\ }\ - array += (ndarray)->strides[ULAB_MAX_DIMS - 1] / (ndarray)->itemsize;\ + array += (lstrides);\ (iarray) += (istride);\ - (varray) += (vstride);\ } while(0) #if ULAB_HAS_FUNCTION_ITERATOR From 1013daa90250d6824e5aa8d4da3794055dfce7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 20 Nov 2021 11:10:06 +0100 Subject: [PATCH 16/58] add complex utilities --- code/micropython.mk | 2 + code/numpy/carray/carray.c | 65 ++++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 18 +++++++++ code/numpy/carray/carray_tools.c | 28 ++++++++++++++ code/numpy/carray/carray_tools.h | 25 ++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 code/numpy/carray/carray.c create mode 100644 code/numpy/carray/carray.h create mode 100644 code/numpy/carray/carray_tools.c create mode 100644 code/numpy/carray/carray_tools.h diff --git a/code/micropython.mk b/code/micropython.mk index 03d3fd40..64ab6152 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -13,6 +13,8 @@ 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/compare.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c SRC_USERMOD += $(USERMODULES_DIR)/ulab_create.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft_tools.c diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c new file mode 100644 index 00000000..cc3c7342 --- /dev/null +++ b/code/numpy/carray/carray.c @@ -0,0 +1,65 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#include +#include +#include +#include "py/obj.h" +#include "py/objint.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/misc.h" + +#include "../../ulab.h" +#include "../../ndarray.h" + +#if ULAB_SUPPORTS_COMPLEX + +STATIC mp_obj_t carray_real(mp_obj_t _source) { + if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->dtype != NDARRAY_COMPLEX) { + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + ndarray_copy_array(source, target, 0); + return MP_OBJ_FROM_PTR(target); + } else { // the input is most definitely a complex array + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + ndarray_copy_array(source, target, 0); + return MP_OBJ_FROM_PTR(target); + } + } else { + mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); + +STATIC mp_obj_t carray_imag(mp_obj_t _source) { + if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + return MP_OBJ_FROM_PTR(target); + } else { // the input is most definitely a complex array + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + ndarray_copy_array(source, target, source->itemsize / 2); + return MP_OBJ_FROM_PTR(target); + } + } else { + mp_raise_NotImplementedError(translate("function is implemented for ndarrays only")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); + +#endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h new file mode 100644 index 00000000..91362c08 --- /dev/null +++ b/code/numpy/carray/carray.h @@ -0,0 +1,18 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#ifndef _CARRAY_ +#define _CARRAY_ + +MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); +MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); + +#endif diff --git a/code/numpy/carray/carray_tools.c b/code/numpy/carray/carray_tools.c new file mode 100644 index 00000000..c6fb8de2 --- /dev/null +++ b/code/numpy/carray/carray_tools.c @@ -0,0 +1,28 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#include +#include +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "py/misc.h" + +#include "../../ulab.h" +#include "../../ndarray.h" + +#if ULAB_SUPPORTS_COMPLEX + +void raise_complex_NotImplementedError(void) { + mp_raise_NotImplementedError(translate("not implemented for complex dtype")); +} + +#endif diff --git a/code/numpy/carray/carray_tools.h b/code/numpy/carray/carray_tools.h new file mode 100644 index 00000000..3b28e1c4 --- /dev/null +++ b/code/numpy/carray/carray_tools.h @@ -0,0 +1,25 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös +*/ + +#ifndef _CARRAY_TOOLS_ +#define _CARRAY_TOOLS_ + +void raise_complex_NotImplementedError(void); + +#if ULAB_SUPPORTS_COMPLEX + #define NOT_IMPLEMENTED_FOR_COMPLEX() raise_complex_NotImplementedError(); + #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) if((dtype) == NDARRAY_COMPLEX) raise_complex_NotImplementedError(); +#else + #define NOT_IMPLEMENTED_FOR_COMPLEX() // do nothing + #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) // do nothing +#endif + +#endif From 911fdb45878d8542bc79faa7f5a078562a0cab39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 18:40:12 +0100 Subject: [PATCH 17/58] ulab can now be compiled with complex support --- code/ndarray.c | 108 ++++++++++++++++++++++++++++++------- code/ndarray.h | 5 +- code/numpy/approx.c | 6 +++ code/numpy/compare.c | 11 ++++ code/numpy/fft/fft.c | 2 + code/numpy/fft/fft_tools.c | 3 ++ code/numpy/filter.c | 3 ++ code/numpy/linalg/linalg.c | 6 +++ code/numpy/numerical.c | 12 ++++- code/numpy/numpy.c | 9 +++- code/numpy/poly.c | 18 +++++++ code/numpy/stats.c | 2 + code/numpy/transform.c | 4 ++ code/numpy/vector.c | 7 +++ code/scipy/signal/signal.c | 7 +++ code/ulab.h | 4 ++ code/ulab_create.c | 7 ++- code/ulab_tools.c | 10 ++++ code/ulab_tools.h | 2 + 19 files changed, 204 insertions(+), 22 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 45e19c26..5286290f 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -171,7 +171,11 @@ void ndarray_rewind_array(uint8_t ndim, uint8_t *array, size_t *shape, int32_t * static int32_t *strides_from_shape(size_t *shape, uint8_t dtype) { // returns a strides array that corresponds to a dense array with the prescribed shape int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_complex_size(dtype); + #else + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + #endif for(uint8_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * shape[i-1]; } @@ -231,7 +235,13 @@ void ndarray_dtype_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin mp_print_str(print, "uint16')"); } else if(self->dtype == NDARRAY_INT16) { mp_print_str(print, "int16')"); - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if(self->dtype == NDARRAY_COMPLEX) { + mp_print_str(print, "complex')"); + } + #endif + else { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT mp_print_str(print, "float32')"); #else @@ -280,7 +290,13 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t _dtype = NDARRAY_INT16; } else if(memcmp(_dtype_, "float", 5) == 0) { _dtype = NDARRAY_FLOAT; - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if(memcmp(_dtype_, "complex", 7) == 0) { + _dtype = NDARRAY_COMPLEX; + } + #endif + else { mp_raise_TypeError(translate("data type not understood")); } } @@ -308,7 +324,11 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) { GET_STR_DATA_LEN(self_in, _dtype, len); if((len != 1) || ((*_dtype != NDARRAY_BOOL) && (*_dtype != NDARRAY_UINT8) && (*_dtype != NDARRAY_INT8) && (*_dtype != NDARRAY_UINT16) - && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT))) { + && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT) + #if ULAB_SUPPORTS_COMPLEX + && (*_dtype != NDARRAY_COMPLEX) + #endif + )) { mp_raise_TypeError(translate("data type not understood")); } dtype = *_dtype; @@ -361,32 +381,55 @@ mp_obj_t ndarray_get_item(ndarray_obj_t *ndarray, void *array) { } } +static void ndarray_print_element(const mp_print_t *print, ndarray_obj_t *ndarray, uint8_t *array) { + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + // real part first + mp_float_t fvalue = *(mp_float_t *)array; + mp_obj_print_helper(print, mp_obj_new_float(fvalue), PRINT_REPR); + // imaginary part + array += ndarray->itemsize / 2; + fvalue = *(mp_float_t *)array; + if(fvalue >= MICROPY_FLOAT_CONST(0.0) || isnan(fvalue)) { + mp_print_str(print, "+"); + } + array += ndarray->itemsize / 2; + mp_obj_print_helper(print, mp_obj_new_float(fvalue), PRINT_REPR); + mp_print_str(print, "j"); + } else { + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + } + #else + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + #endif +} + static void ndarray_print_row(const mp_print_t *print, ndarray_obj_t * ndarray, uint8_t *array, size_t stride, size_t n) { if(n == 0) { return; } mp_print_str(print, "["); if((n <= ndarray_print_threshold) || (n <= 2*ndarray_print_edgeitems)) { // if the array is short, print everything - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); array += stride; for(size_t i=1; i < n; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } } else { mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); array += stride; for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } mp_printf(print, ", ..., "); - array += stride * (n - 2 * ndarray_print_edgeitems); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + array += stride * (n - 2 * ndarray_print_edgeitems); + ndarray_print_element(print, ndarray, array); array += stride; for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { mp_print_str(print, ", "); - mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + ndarray_print_element(print, ndarray, array); } } mp_print_str(print, "]"); @@ -494,7 +537,19 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t } } else { while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + #if ULAB_SUPPORTS_COMPLEX + mp_float_t real; + mp_float_t imag; + if(dtype == NDARRAY_COMPLEX) { + mp_obj_get_complex(item, &real, &imag); + ndarray_set_value(NDARRAY_FLOAT, ndarray->array, (*idx)++, mp_obj_new_float(real)); + ndarray_set_value(NDARRAY_FLOAT, ndarray->array, (*idx)++, mp_obj_new_float(imag)); + } else { + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + } + #else + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + #endif } } } @@ -518,7 +573,11 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; ndarray->ndim = ndim; ndarray->len = ndim == 0 ? 0 : 1; - ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + ndarray->itemsize = mp_binary_get_complex_size(dtype); + #else + ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + #endif int32_t *_strides; if(strides == NULL) { _strides = strides_from_shape(shape, ndarray->dtype); @@ -546,7 +605,11 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt // 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] = dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_complex_size(dtype); + #else + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_size('@', dtype, NULL); + #endif for(size_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * MAX(1, shape[i-1]); } @@ -567,13 +630,18 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt return ndarray_new_dense_ndarray(_shape->len, shape, dtype); } -void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { +void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target, uint8_t shift) { // TODO: if the array is dense, the content could be copied in a single pass // copies the content of source->array into a new dense void pointer // it is assumed that the dtypes in source and target are the same // Since the target is a new array, it is supposed to be dense uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; + #if ULAB_SUPPORTS_COMPLEX + if(source->dtype == NDARRAY_COMPLEX) { + sarray += shift; + } + #endif #if ULAB_MAX_DIMS > 3 size_t i = 0; @@ -589,7 +657,7 @@ void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { #endif size_t l = 0; do { - memcpy(tarray, sarray, source->itemsize); + memcpy(tarray, sarray, target->itemsize); tarray += target->itemsize; sarray += source->strides[ULAB_MAX_DIMS - 1]; l++; @@ -648,7 +716,7 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { dtype = NDARRAY_BOOLEAN; } ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype); - ndarray_copy_array(source, ndarray); + ndarray_copy_array(source, ndarray, 0); return ndarray; } @@ -1805,7 +1873,11 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + uint8_t itemsize = mp_binary_get_complex_size(self->dtype); + #else + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #endif for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); @@ -1914,7 +1986,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { mp_raise_ValueError(translate("cannot assign new shape")); } ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); - ndarray_copy_array(source, ndarray); + ndarray_copy_array(source, ndarray, 0); } return MP_OBJ_FROM_PTR(ndarray); } diff --git a/code/ndarray.h b/code/ndarray.h index 04abd965..dc827a68 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -77,6 +77,9 @@ enum NDARRAY_TYPE { NDARRAY_INT8 = 'b', NDARRAY_UINT16 = 'H', NDARRAY_INT16 = 'h', + #if ULAB_SUPPORTS_COMPLEX + NDARRAY_COMPLEX = 'c', + #endif NDARRAY_FLOAT = FLOAT_TYPECODE, }; @@ -138,7 +141,7 @@ 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 *); ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *); -void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *); +void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *, uint8_t ); MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj); mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); diff --git a/code/numpy/approx.c b/code/numpy/approx.c index 6ed5d7c2..85cdbf78 100644 --- a/code/numpy/approx.c +++ b/code/numpy/approx.c @@ -19,6 +19,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "approx.h" //| """Numerical approximation methods""" @@ -60,6 +61,9 @@ STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0); ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) + 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")); } @@ -157,6 +161,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype) ndarray_obj_t *x; mp_float_t mean = MICROPY_FLOAT_CONST(0.0); if(y->len < 2) { @@ -174,6 +179,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t * if(args[1].u_obj != mp_const_none) { 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")); } diff --git a/code/numpy/compare.c b/code/numpy/compare.c index b9154569..5a820725 100644 --- a/code/numpy/compare.c +++ b/code/numpy/compare.c @@ -20,11 +20,17 @@ #include "../ulab.h" #include "../ndarray_operators.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "compare.h" 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); + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + NOT_IMPLEMENTED_FOR_COMPLEX() + } + #endif uint8_t ndim = 0; size_t *shape = m_new(size_t, ULAB_MAX_DIMS); int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); @@ -197,6 +203,7 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { } } else if(mp_obj_is_type(_x, &ulab_ndarray_type)) { ndarray_obj_t *x = MP_OBJ_TO_PTR(_x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL); // At this point, results is all False uint8_t *rarray = (uint8_t *)results->array; @@ -313,6 +320,10 @@ mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0); ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype) + int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS); int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS); diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 6f6534f4..3b3f66a5 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -20,6 +20,7 @@ #include "py/obj.h" #include "py/objarray.h" +#include "../carray/carray_tools.h" #include "fft.h" //| """Frequency-domain functions""" @@ -60,6 +61,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft); //| 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); } else { diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index 6dd2ca47..a08f2244 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -13,6 +13,7 @@ #include "../../ndarray.h" #include "../../ulab_tools.h" +#include "../carray/carray_tools.h" #include "fft_tools.h" #ifndef MP_PI @@ -95,6 +96,7 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i 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")); } #endif @@ -122,6 +124,7 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im); #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")); } #endif diff --git a/code/numpy/filter.c b/code/numpy/filter.c index bf2d16cd..fff122ef 100644 --- a/code/numpy/filter.c +++ b/code/numpy/filter.c @@ -21,6 +21,7 @@ #include "../ulab.h" #include "../scipy/signal/signal.h" +#include "carray/carray_tools.h" #include "filter.h" #if ULAB_NUMPY_HAS_CONVOLVE @@ -40,6 +41,8 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) // deal with linear arrays only #if ULAB_MAX_DIMS > 1 if((a->ndim != 1) || (c->ndim != 1)) { diff --git a/code/numpy/linalg/linalg.c b/code/numpy/linalg/linalg.c index 596280fe..d9343fb8 100644 --- a/code/numpy/linalg/linalg.c +++ b/code/numpy/linalg/linalg.c @@ -22,6 +22,7 @@ #include "../../ulab.h" #include "../../ulab_tools.h" +#include "../carray/carray_tools.h" #include "linalg.h" #if ULAB_NUMPY_HAS_LINALG_MODULE @@ -44,6 +45,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT); mp_float_t *Larray = (mp_float_t *)L->array; @@ -110,6 +112,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky); static mp_obj_t linalg_det(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; mp_float_t *tmp = m_new(mp_float_t, N * N); @@ -182,6 +185,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det); static mp_obj_t linalg_eig(mp_obj_t oin) { ndarray_obj_t *in = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(in->dtype) uint8_t *iarray = (uint8_t *)in->array; size_t S = in->shape[ULAB_MAX_DIMS - 1]; mp_float_t *array = m_new(mp_float_t, S*S); @@ -243,6 +247,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig); //| static mp_obj_t linalg_inv(mp_obj_t o_in) { ndarray_obj_t *ndarray = tools_object_is_square(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT); @@ -305,6 +310,7 @@ static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1))); } else if(mp_obj_is_type(x, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; // always get a float, so that we don't have to resolve the dtype later mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 750bc794..ebd53284 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -22,6 +22,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "./carray/carray_tools.h" #include "numerical.h" enum NUMERICAL_FUNCTION_TYPE { @@ -97,6 +98,7 @@ static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { bool anytype = optype == NUMERICAL_ALL ? 1 : 0; if(mp_obj_is_type(oin, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; if(ndarray->len == 0) { // return immediately with empty arrays if(optype == NUMERICAL_ALL) { @@ -237,6 +239,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) { + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); @@ -558,9 +561,11 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m case NUMERICAL_MAX: case NUMERICAL_ARGMIN: case NUMERICAL_ARGMAX: + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) return numerical_argmin_argmax_ndarray(ndarray, axis, optype); case NUMERICAL_SUM: case NUMERICAL_MEAN: + 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")); @@ -583,6 +588,7 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla } else { ndarray = ndarray_copy_view(MP_OBJ_TO_PTR(oin)); } + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) int8_t ax = 0; if(axis == mp_const_none) { @@ -685,6 +691,7 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw } 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")); @@ -788,6 +795,8 @@ static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) { } 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")); } @@ -876,6 +885,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) int8_t ax = args[2].u_int; if(ax < 0) ax += ndarray->ndim; @@ -959,7 +969,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); if(args[1].u_obj == mp_const_none) { // flip the flattened array results = ndarray_new_linear_array(ndarray->len, ndarray->dtype); - ndarray_copy_array(ndarray, results); + ndarray_copy_array(ndarray, results, 0); uint8_t *rarray = (uint8_t *)results->array; rarray += (results->len - 1) * results->itemsize; results->array = rarray; diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index a6559ff8..9e9c3fd1 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -19,6 +19,7 @@ #include "numpy.h" #include "../ulab_create.h" #include "approx.h" +#include "carray/carray.h" #include "compare.h" #include "fft/fft.h" #include "filter.h" @@ -125,6 +126,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) }, { MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) }, { MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) }, + #if ULAB_SUPPORTS_COMPLEX + { MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_INT(NDARRAY_COMPLEX) }, + #endif // modules of numpy #if ULAB_NUMPY_HAS_FFT_MODULE { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) }, @@ -350,7 +354,10 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_VECTORIZE { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vectorise_vectorize_obj }, #endif - + #if ULAB_SUPPORTS_COMPLEX + { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + #endif }; static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table); diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 124d3bc8..0e3476eb 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -19,6 +19,7 @@ #include "../ulab.h" #include "linalg/linalg_tools.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "poly.h" #if ULAB_NUMPY_HAS_POLYFIT @@ -27,6 +28,12 @@ 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")); } + #if ULAB_SUPPORTS_COMPLEX + if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) + } + #endif size_t lenx = 0, leny = 0; uint8_t deg = 0; mp_float_t *x, *XT, *y, *prod; @@ -142,6 +149,17 @@ 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 ULAB_SUPPORTS_COMPLEX + ndarray_obj_t *input; + if(MP_OBJ_IS_TYPE(o_p, &ulab_ndarray_type)) { + input = MP_OBJ_TO_PTR(o_p); + COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) + } + if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) { + input = MP_OBJ_TO_PTR(o_x); + COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) + } + #endif // p had better be a one-dimensional standard iterable uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p)); mp_float_t *p = m_new(mp_float_t, plen); diff --git a/code/numpy/stats.c b/code/numpy/stats.c index a63964fe..2d348893 100644 --- a/code/numpy/stats.c +++ b/code/numpy/stats.c @@ -21,6 +21,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "stats.h" #if ULAB_MAX_DIMS > 1 @@ -36,6 +37,7 @@ static mp_obj_t stats_trace(mp_obj_t oin) { ndarray_obj_t *ndarray = tools_object_is_square(oin); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) mp_float_t trace = 0.0; for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) { int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]); diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 2c2d2dbd..026f9e57 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -18,6 +18,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "transform.h" #if ULAB_MAX_DIMS > 1 @@ -39,6 +40,9 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { } ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1); ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2); + COMPLEX_DTYPE_NOT_IMPLEMENTED(m1->dtype) + COMPLEX_DTYPE_NOT_IMPLEMENTED(m2->dtype) + uint8_t *array1 = (uint8_t *)m1->array; uint8_t *array2 = (uint8_t *)m2->array; diff --git a/code/numpy/vector.c b/code/numpy/vector.c index ceba2559..e33acd59 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -22,6 +22,7 @@ #include "../ulab.h" #include "../ulab_tools.h" +#include "carray/carray_tools.h" #include "vector.h" //| """Element-by-element functions @@ -39,6 +40,7 @@ static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float ndarray_obj_t *ndarray = NULL; if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); + COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype) uint8_t *sarray = (uint8_t *)source->array; ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); mp_float_t *array = (mp_float_t *)ndarray->array; @@ -169,6 +171,7 @@ mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ int8_t n = args[1].u_int; 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) 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; @@ -238,7 +241,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan); mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) { ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype) + ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_y->dtype) uint8_t ndim = 0; size_t *shape = m_new(size_t, ULAB_MAX_DIMS); @@ -544,6 +550,7 @@ static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_ar mp_obj_t fvalue; 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); diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index cc559b59..6116039f 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -18,6 +18,7 @@ #include "../../ulab.h" #include "../../ndarray.h" +#include "../../numpy/carray/carray_tools.h" #include "../../numpy/fft/fft_tools.h" #if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM @@ -68,6 +69,12 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar 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")); } + #if ULAB_SUPPORTS_COMPLEX + if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); + COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) + } + #endif size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj)); ndarray_obj_t *y = ndarray_new_linear_array(lenx, NDARRAY_FLOAT); mp_float_t *yarray = (mp_float_t *)y->array; diff --git a/code/ulab.h b/code/ulab.h index 248047c8..c0a5b45f 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -31,6 +31,10 @@ #include ULAB_CONFIG_FILE #endif +// Adds support for complex ndarrays +#ifndef ULAB_SUPPORTS_COMPLEX +#define ULAB_SUPPORTS_COMPLEX (1) +#endif // Determines, whether scipy is defined in ulab. The sub-modules and functions // of scipy have to be defined separately diff --git a/code/ulab_create.c b/code/ulab_create.c index a93ec741..efee18ef 100644 --- a/code/ulab_create.c +++ b/code/ulab_create.c @@ -19,6 +19,7 @@ #include "ulab.h" #include "ulab_create.h" +#include "ulab_tools.h" #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) { @@ -530,7 +531,11 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { size_t sz = 1; if(dtype != NDARRAY_BOOL) { // mp_binary_get_size doesn't work with Booleans - sz = mp_binary_get_size('@', dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + sz = mp_binary_get_complex_size(dtype); + #else + sz = mp_binary_get_size('@', dtype, NULL); + #endif } if(bufinfo.len < offset) { mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length")); diff --git a/code/ulab_tools.c b/code/ulab_tools.c index acd3d8a5..7c5311c4 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -231,3 +231,13 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { return ndarray; } #endif + +#if ULAB_SUPPORTS_COMPLEX +uint8_t mp_binary_get_complex_size(uint8_t dtype) { + if(dtype == NDARRAY_COMPLEX) { + return 2 * (uint8_t)mp_binary_get_size('@', NDARRAY_FLOAT, NULL); + } else { + return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + } +} +#endif \ No newline at end of file diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 378e4f0c..7d849fc0 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -34,4 +34,6 @@ void *ndarray_set_float_function(uint8_t ); shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); + +uint8_t mp_binary_get_complex_size(uint8_t ); #endif From 8db5ee897ec398d9a5a62b3e4097605942b7f235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 19:57:34 +0100 Subject: [PATCH 18/58] add missing newline character --- code/ulab_tools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 7c5311c4..6656e2b4 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -240,4 +240,4 @@ uint8_t mp_binary_get_complex_size(uint8_t dtype) { return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); } } -#endif \ No newline at end of file +#endif From 08313e3f02c95fab6f02a644547b01ec57e6063b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:03:11 +0100 Subject: [PATCH 19/58] fix uppercase mp_obj_is_type macro calls --- code/numpy/carray/carray.c | 4 ++-- code/numpy/poly.c | 4 ++-- code/scipy/signal/signal.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index cc3c7342..1600afbe 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -24,7 +24,7 @@ #if ULAB_SUPPORTS_COMPLEX STATIC mp_obj_t carray_real(mp_obj_t _source) { - if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); @@ -44,7 +44,7 @@ STATIC mp_obj_t carray_real(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); STATIC mp_obj_t carray_imag(mp_obj_t _source) { - if(MP_OBJ_IS_TYPE(_source, &ulab_ndarray_type)) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); diff --git a/code/numpy/poly.c b/code/numpy/poly.c index 0e3476eb..e320983b 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -151,11 +151,11 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { } #if ULAB_SUPPORTS_COMPLEX ndarray_obj_t *input; - if(MP_OBJ_IS_TYPE(o_p, &ulab_ndarray_type)) { + if(mp_obj_is_type(o_p, &ulab_ndarray_type)) { input = MP_OBJ_TO_PTR(o_p); COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) } - if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) { + if(mp_obj_is_type(o_x, &ulab_ndarray_type)) { input = MP_OBJ_TO_PTR(o_x); COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype) } diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 6116039f..7ab471a3 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -70,7 +70,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(translate("sosfilt requires iterable arguments")); } #if ULAB_SUPPORTS_COMPLEX - if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { + if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From 8a6741b3508180c1a8ab24ee3aee235aff2ec46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:05:45 +0100 Subject: [PATCH 20/58] fix macro call in poly.c --- code/numpy/poly.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/poly.c b/code/numpy/poly.c index e320983b..97ee5c75 100644 --- a/code/numpy/poly.c +++ b/code/numpy/poly.c @@ -29,7 +29,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(translate("input data must be an iterable")); } #if ULAB_SUPPORTS_COMPLEX - if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) { + if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From ef8989ecff7b232bfa706f42917b739812634479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 1 Dec 2021 07:49:29 +0100 Subject: [PATCH 21/58] implement unary operators for complex arrays --- code/ndarray.c | 59 ++++++++++++++++++++++++-------------- code/numpy/carray/carray.c | 47 ++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 2 ++ code/numpy/numerical.c | 2 +- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 5286290f..77ebde5a 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -25,6 +25,7 @@ #include "ulab_tools.h" #include "ndarray.h" #include "ndarray_operators.h" +#include "numpy/carray/carray.h" mp_uint_t ndarray_print_threshold = NDARRAY_PRINT_THRESHOLD; mp_uint_t ndarray_print_edgeitems = NDARRAY_PRINT_EDGEITEMS; @@ -1841,30 +1842,44 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { switch (op) { #if NDARRAY_HAS_UNARY_OP_ABS case MP_UNARY_OP_ABS: - ndarray = ndarray_copy_view(self); - // if Boolean, NDARRAY_UINT8, or NDARRAY_UINT16, there is nothing to do - if(self->dtype == NDARRAY_INT8) { - int8_t *array = (int8_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) { - if(*array < 0) *array = -(*array); - } - } else if(self->dtype == NDARRAY_INT16) { - int16_t *array = (int16_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) { - if(*array < 0) *array = -(*array); - } + #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 = carray_abs(self, target); } else { - mp_float_t *array = (mp_float_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) { - if(*array < 0) *array = -(*array); + #endif + ndarray = ndarray_copy_view(self); + // if Boolean, NDARRAY_UINT8, or NDARRAY_UINT16, there is nothing to do + if(self->dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } else if(self->dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } else { + mp_float_t *array = (mp_float_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } } + #if ULAB_SUPPORTS_COMPLEX } + #endif return MP_OBJ_FROM_PTR(ndarray); break; #endif #if NDARRAY_HAS_UNARY_OP_INVERT case MP_UNARY_OP_INVERT: + #if ULAB_SUPPORTS_COMPLEX + if(self->dtype == NDARRAY_FLOAT || self->dtype == NDARRAY_COMPLEX) { + #else if(self->dtype == NDARRAY_FLOAT) { + #endif mp_raise_ValueError(translate("operation is not supported for given type")); } // we can invert the content byte by byte, no need to distinguish between different dtypes @@ -1873,11 +1888,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - #if ULAB_SUPPORTS_COMPLEX - uint8_t itemsize = mp_binary_get_complex_size(self->dtype); - #else - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); - #endif + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); @@ -1905,7 +1916,13 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { for(size_t i=0; i < self->len; i++, array++) *array = -(*array); } else { mp_float_t *array = (mp_float_t *)ndarray->array; - for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + size_t len = self->len; + #if ULAB_SUPPORTS_COMPLEX + if(self->dtype == NDARRAY_COMPLEX) { + len *= 2; + } + #endif + for(size_t i=0; i < len; i++, array++) *array = -(*array); } return MP_OBJ_FROM_PTR(ndarray); break; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 1600afbe..60dede8a 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -62,4 +62,51 @@ STATIC mp_obj_t carray_imag(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); +mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { + // calculates the absolute value of a complex array and returns a dense array + uint8_t *sarray = (uint8_t *)source->array; + mp_float_t *tarray = (mp_float_t *)target->array; + uint8_t itemsize = mp_binary_get_size('@', NDARRAY_FLOAT, NULL); + + #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 rvalue = *(mp_float_t *)sarray; + mp_float_t ivalue = *(mp_float_t *)(sarray + itemsize); + *tarray++ = MICROPY_FLOAT_C_FUN(sqrt)(rvalue * rvalue + ivalue * ivalue); + 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 + return MP_OBJ_FROM_PTR(target); +} + #endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 91362c08..113fe3ad 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -15,4 +15,6 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); +mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); + #endif diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index ebd53284..8b944818 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -250,7 +250,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t return mp_obj_new_float(MICROPY_FLOAT_CONST(0.0)); } mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); - mp_float_t M =MICROPY_FLOAT_CONST(0.0); + mp_float_t M = MICROPY_FLOAT_CONST(0.0); mp_float_t m = MICROPY_FLOAT_CONST(0.0); mp_float_t S = MICROPY_FLOAT_CONST(0.0); mp_float_t s = MICROPY_FLOAT_CONST(0.0); From 316f84174de7addbb597233e54dc8236c3e33179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 18:40:12 +0100 Subject: [PATCH 22/58] rebasing with any/all fix --- code/ndarray.c | 6 +++++- code/scipy/signal/signal.c | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index 77ebde5a..0197a91a 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1888,7 +1888,11 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #if ULAB_SUPPORTS_COMPLEX + uint8_t itemsize = mp_binary_get_complex_size(self->dtype); + #else + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + #endif for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 7ab471a3..7a85aa0e 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -70,7 +70,11 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(translate("sosfilt requires iterable arguments")); } #if ULAB_SUPPORTS_COMPLEX +<<<<<<< HEAD if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { +======= + if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { +>>>>>>> 92b3513 (ulab can now be compiled with complex support) ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From d0b1d40cf14ff1d19c2051dce941492909bab801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 23 Nov 2021 20:03:11 +0100 Subject: [PATCH 23/58] rebasing on master with any/all fix --- code/scipy/signal/signal.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 7a85aa0e..7ab471a3 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -70,11 +70,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_TypeError(translate("sosfilt requires iterable arguments")); } #if ULAB_SUPPORTS_COMPLEX -<<<<<<< HEAD if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { -======= - if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) { ->>>>>>> 92b3513 (ulab can now be compiled with complex support) ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) } From 3b3639a56012581bc52d9a9b35783a5096b351cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 1 Dec 2021 21:37:05 +0100 Subject: [PATCH 24/58] implement complex exponential --- code/ndarray.c | 23 +++++++++++------ code/numpy/vector.c | 63 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 0197a91a..33c055c4 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -503,21 +503,28 @@ void ndarray_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t ki ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-4], "]"); #endif } + mp_print_str(print, ", dtype="); if(self->boolean) { - mp_print_str(print, ", dtype=bool)"); + mp_print_str(print, "bool)"); } else if(self->dtype == NDARRAY_UINT8) { - mp_print_str(print, ", dtype=uint8)"); + mp_print_str(print, "uint8)"); } else if(self->dtype == NDARRAY_INT8) { - mp_print_str(print, ", dtype=int8)"); + mp_print_str(print, "int8)"); } else if(self->dtype == NDARRAY_UINT16) { - mp_print_str(print, ", dtype=uint16)"); + mp_print_str(print, "uint16)"); } else if(self->dtype == NDARRAY_INT16) { - mp_print_str(print, ", dtype=int16)"); - } else { + mp_print_str(print, "int16)"); + } + #if ULAB_SUPPORTS_COMPLEX + else if(self->dtype == NDARRAY_COMPLEX) { + mp_print_str(print, "complex)"); + } + #endif /* ULAB_SUPPORTS_COMPLEX */ + else { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - mp_print_str(print, ", dtype=float32)"); + mp_print_str(print, "float32)"); #else - mp_print_str(print, ", dtype=float64)"); + mp_print_str(print, "float64)"); #endif } } diff --git a/code/numpy/vector.c b/code/numpy/vector.c index e33acd59..d2998c3d 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -401,7 +401,68 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc); //| ... //| -MATH_FUN_1(exp, exp); +static mp_obj_t vectorise_exp(mp_obj_t o_in) { + #if ULAB_SUPPORTS_COMPLEX + if(mp_obj_is_type(o_in, &mp_type_complex)) { + mp_float_t real, imag; + mp_obj_get_complex(o_in, &real, &imag); + mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); + 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(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); + 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 */ + return MP_OBJ_FROM_PTR(ndarray); + } + } + #endif + return vectorise_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp)); +} + MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp); #endif From 93409e759a3f9ade3aa57afdf2163c97745f3aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 2 Dec 2021 21:21:38 +0100 Subject: [PATCH 25/58] implement iterator for complex arrays --- code/ndarray.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 33c055c4..61be7ecf 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -372,6 +372,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) { @@ -945,6 +953,11 @@ STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, dtype); uint8_t *sarray = (uint8_t *)source->array; uint8_t *tarray = (uint8_t *)target->array; + + #if ULAB_SUPPORTS_COMPLEX + uint8_t complex_size = 2 * sizeof(mp_float_t); + #endif + #if ULAB_MAX_DIMS > 3 size_t i = 0; do { @@ -960,14 +973,26 @@ STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t l = 0; do { mp_obj_t item; - 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(floor)(f)); + #if ULAB_SUPPORTS_COMPLEX + if(source->dtype == NDARRAY_COMPLEX) { + if(dtype != NDARRAY_COMPLEX) { + mp_raise_TypeError(translate("cannot convert complex type")); + } else { + memcpy(tarray, sarray, complex_size); + } } else { - item = mp_binary_get_val_array(source->dtype, sarray, 0); + #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(floor)(f)); + } else { + item = mp_binary_get_val_array(source->dtype, sarray, 0); + } + ndarray_set_value(dtype, tarray, 0, item); + #if ULAB_SUPPORTS_COMPLEX } - ndarray_set_value(dtype, tarray, 0, item); + #endif tarray += target->itemsize; sarray += source->strides[ULAB_MAX_DIMS - 1]; l++; From 78455afa5d7c76003dd9fb70a76cddf058e45166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 2 Dec 2021 21:42:14 +0100 Subject: [PATCH 26/58] simplify binary_get_size function --- code/ndarray.c | 21 ++++----------------- code/ulab_create.c | 10 ++-------- code/ulab_tools.c | 11 +++++------ code/ulab_tools.h | 2 +- 4 files changed, 12 insertions(+), 32 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 61be7ecf..52f1f4ea 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -172,11 +172,7 @@ void ndarray_rewind_array(uint8_t ndim, uint8_t *array, size_t *shape, int32_t * static int32_t *strides_from_shape(size_t *shape, uint8_t dtype) { // returns a strides array that corresponds to a dense array with the prescribed shape int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); - #if ULAB_SUPPORTS_COMPLEX - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_complex_size(dtype); - #else - strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); - #endif + strides[ULAB_MAX_DIMS-1] = (int32_t)ulab_binary_get_size(dtype); for(uint8_t i=ULAB_MAX_DIMS; i > 1; i--) { strides[i-2] = strides[i-1] * shape[i-1]; } @@ -544,7 +540,6 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t uint8_t *array = (uint8_t *)ndarray->array; array += *idx; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - // TODO: this might be wrong here: we have to check for the trueness of item if(mp_obj_is_true(item)) { *array = 1; } @@ -553,7 +548,7 @@ void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t } } else { while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - #if ULAB_SUPPORTS_COMPLEX + #if ULAB_SUPPORTS_COMPLEX mp_float_t real; mp_float_t imag; if(dtype == NDARRAY_COMPLEX) { @@ -589,11 +584,7 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; ndarray->ndim = ndim; ndarray->len = ndim == 0 ? 0 : 1; - #if ULAB_SUPPORTS_COMPLEX - ndarray->itemsize = mp_binary_get_complex_size(dtype); - #else - ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); - #endif + ndarray->itemsize = ulab_binary_get_size(dtype); int32_t *_strides; if(strides == NULL) { _strides = strides_from_shape(shape, ndarray->dtype); @@ -621,11 +612,7 @@ ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dt // 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); - #if ULAB_SUPPORTS_COMPLEX - strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_complex_size(dtype); - #else - strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : (int32_t)mp_binary_get_size('@', dtype, NULL); - #endif + 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]); } diff --git a/code/ulab_create.c b/code/ulab_create.c index efee18ef..2dc1412b 100644 --- a/code/ulab_create.c +++ b/code/ulab_create.c @@ -529,14 +529,8 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw mp_buffer_info_t bufinfo; if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { - size_t sz = 1; - if(dtype != NDARRAY_BOOL) { // mp_binary_get_size doesn't work with Booleans - #if ULAB_SUPPORTS_COMPLEX - sz = mp_binary_get_complex_size(dtype); - #else - sz = mp_binary_get_size('@', dtype, NULL); - #endif - } + 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")); } diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 6656e2b4..d0537230 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -232,12 +232,11 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { } #endif -#if ULAB_SUPPORTS_COMPLEX -uint8_t mp_binary_get_complex_size(uint8_t dtype) { +uint8_t ulab_binary_get_size(uint8_t dtype) { + #if ULAB_SUPPORTS_COMPLEX if(dtype == NDARRAY_COMPLEX) { - return 2 * (uint8_t)mp_binary_get_size('@', NDARRAY_FLOAT, NULL); - } else { - return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + return 2 * (uint8_t)sizeof(mp_float_t); } + #endif + return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); } -#endif diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 7d849fc0..216dbaba 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -35,5 +35,5 @@ void *ndarray_set_float_function(uint8_t ); shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); -uint8_t mp_binary_get_complex_size(uint8_t ); +uint8_t ulab_binary_get_size(uint8_t ); #endif From ce26a0d76b0d697a466074fe0dfa99cec0354065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 3 Dec 2021 00:06:20 +0100 Subject: [PATCH 27/58] implement slicing for complex dtype --- code/ndarray.c | 160 ++++++++++++++++++++++++++++++++++++++----------- code/ndarray.h | 6 +- 2 files changed, 127 insertions(+), 39 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 52f1f4ea..0659204e 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1168,9 +1168,70 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { } uint8_t *rarray = (uint8_t *)values->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; + 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(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]); + #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]); + #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; + } + #endif } if(view->dtype == NDARRAY_UINT8) { @@ -1217,21 +1278,21 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { } 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); + 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); + ASSIGNMENT_LOOP(view, int16_t, mp_float_t, lstrides, rarray, rstrides); } - } else { // the dtype must be an mp_float_t now + } 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); + 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); + 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); + 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); + ASSIGNMENT_LOOP(view, mp_float_t, mp_float_t, lstrides, rarray, rstrides); } } } @@ -1269,16 +1330,16 @@ static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarra // assigns values to a Boolean-indexed array // first we have to find out how many trues there are uint8_t *iarray = (uint8_t *)index->array; + size_t istride = index->strides[ULAB_MAX_DIMS - 1]; size_t count = 0; for(size_t i=0; i < index->len; i++) { count += *iarray; - iarray += index->strides[ULAB_MAX_DIMS - 1]; + iarray += istride; } // re-wind the index array iarray = index->array; uint8_t *varray = (uint8_t *)values->array; size_t vstride; - size_t istride = index->strides[ULAB_MAX_DIMS - 1]; if(count == values->len) { // there are as many values as true indices @@ -1287,65 +1348,92 @@ static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarra // there is a single value vstride = 0; } + + #if ULAB_SUPPORTS_COMPLEX + if(values->dtype == NDARRAY_COMPLEX) { + if(ndarray->dtype != NDARRAY_COMPLEX) { + mp_raise_TypeError(translate("cannot convert complex to dtype")); + } else { + uint8_t *array = (uint8_t *)ndarray->array; + for(size_t i = 0; i < ndarray->len; i++) { + if(*iarray) { + memcpy(array, varray, ndarray->itemsize); + varray += vstride; + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + iarray += istride; + } while(0); + return MP_OBJ_FROM_PTR(ndarray); + } + } + #endif + + int32_t lstrides = ndarray->strides[ULAB_MAX_DIMS - 1] / ndarray->itemsize; + if(ndarray->dtype == NDARRAY_UINT8) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(uint8_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else if(ndarray->dtype == NDARRAY_INT8) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(int8_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int8_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else if(ndarray->dtype == NDARRAY_UINT16) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(uint16_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else if(ndarray->dtype == NDARRAY_INT16) { if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(int16_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(int16_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } else { + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + lstrides *= 2; + } + #endif if(values->dtype == NDARRAY_UINT8) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT8) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int8_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int8_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_UINT16) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_INT16) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int16_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int16_t, ndarray, lstrides, iarray, istride, varray, vstride); } else if(values->dtype == NDARRAY_FLOAT) { - BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, mp_float_t, ndarray, lstrides, iarray, istride, varray, vstride); } } return MP_OBJ_FROM_PTR(ndarray); @@ -1360,7 +1448,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra if(values == NULL) { // return value(s) return ndarray_from_boolean_index(ndarray, nindex); } else { // assign value(s) - ndarray_assign_from_boolean_index(ndarray, index, values); + ndarray_assign_from_boolean_index(ndarray, nindex, values); } } if(mp_obj_is_type(index, &mp_type_tuple) || mp_obj_is_int(index) || mp_obj_is_type(index, &mp_type_slice)) { @@ -1379,7 +1467,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra if(values == NULL) { // return value(s) // if the view has been reduced to nothing, return a single value if(view->ndim == 0) { - return mp_binary_get_val_array(view->dtype, view->array, 0); + return ndarray_get_item(view, view->array); } else { return MP_OBJ_FROM_PTR(view); } diff --git a/code/ndarray.h b/code/ndarray.h index dc827a68..0b4be867 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -204,15 +204,15 @@ mp_int_t ndarray_get_buffer(mp_obj_t , mp_buffer_info_t *, mp_uint_t ); ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); -#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, iarray, istride, varray, vstride)\ +#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, lstrides, iarray, istride, varray, vstride)\ type_left *array = (type_left *)(ndarray)->array;\ for(size_t i=0; i < (ndarray)->len; i++) {\ if(*(iarray)) {\ *array = (type_left)(*((type_right *)(varray)));\ + (varray) += (vstride);\ }\ - array += (ndarray)->strides[ULAB_MAX_DIMS - 1] / (ndarray)->itemsize;\ + array += (lstrides);\ (iarray) += (istride);\ - (varray) += (vstride);\ } while(0) #if ULAB_HAS_FUNCTION_ITERATOR From 235329c3fb91dc8af4deca1cd040689aa00cd540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 3 Dec 2021 09:11:41 +0100 Subject: [PATCH 28/58] fix itemsize glitch --- code/ndarray.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 0659204e..0aa346f5 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1995,11 +1995,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { if(ndarray->boolean) { for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; } else { - #if ULAB_SUPPORTS_COMPLEX - uint8_t itemsize = mp_binary_get_complex_size(self->dtype); - #else - uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); - #endif + uint8_t itemsize = ulab_binary_get_size(self->dtype); for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; } return MP_OBJ_FROM_PTR(ndarray); From 3557e16cd193fd7c43c1c2fec66079dbd361a820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 7 Dec 2021 19:55:15 +0100 Subject: [PATCH 29/58] implement sqrt for complex arrays --- code/numpy/numpy.c | 50 ++++---- code/numpy/vector.c | 236 +++++++++++++++++++++++++++-------- code/numpy/vector.h | 71 ++++++----- code/scipy/special/special.c | 8 +- docs/ulab-programming.ipynb | 2 +- 5 files changed, 255 insertions(+), 112 deletions(-) diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 9e9c3fd1..f69150e7 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -280,79 +280,79 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #endif // functions of the vector sub-module #if ULAB_NUMPY_HAS_ACOS - { MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vectorise_acos_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vector_acos_obj }, #endif #if ULAB_NUMPY_HAS_ACOSH - { MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vectorise_acosh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vector_acosh_obj }, #endif #if ULAB_NUMPY_HAS_ARCTAN2 - { MP_OBJ_NEW_QSTR(MP_QSTR_arctan2), (mp_obj_t)&vectorise_arctan2_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_arctan2), (mp_obj_t)&vector_arctan2_obj }, #endif #if ULAB_NUMPY_HAS_AROUND - { MP_OBJ_NEW_QSTR(MP_QSTR_around), (mp_obj_t)&vectorise_around_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_around), (mp_obj_t)&vector_around_obj }, #endif #if ULAB_NUMPY_HAS_ASIN - { MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vectorise_asin_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vector_asin_obj }, #endif #if ULAB_NUMPY_HAS_ASINH - { MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vectorise_asinh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vector_asinh_obj }, #endif #if ULAB_NUMPY_HAS_ATAN - { MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vectorise_atan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vector_atan_obj }, #endif #if ULAB_NUMPY_HAS_ATANH - { MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vectorise_atanh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vector_atanh_obj }, #endif #if ULAB_NUMPY_HAS_CEIL - { MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vectorise_ceil_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vector_ceil_obj }, #endif #if ULAB_NUMPY_HAS_COS - { MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vectorise_cos_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vector_cos_obj }, #endif #if ULAB_NUMPY_HAS_COSH - { MP_OBJ_NEW_QSTR(MP_QSTR_cosh), (mp_obj_t)&vectorise_cosh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_cosh), (mp_obj_t)&vector_cosh_obj }, #endif #if ULAB_NUMPY_HAS_DEGREES - { MP_OBJ_NEW_QSTR(MP_QSTR_degrees), (mp_obj_t)&vectorise_degrees_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_degrees), (mp_obj_t)&vector_degrees_obj }, #endif #if ULAB_NUMPY_HAS_EXP - { MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vectorise_exp_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vector_exp_obj }, #endif #if ULAB_NUMPY_HAS_EXPM1 - { MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vectorise_expm1_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vector_expm1_obj }, #endif #if ULAB_NUMPY_HAS_FLOOR - { MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vectorise_floor_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vector_floor_obj }, #endif #if ULAB_NUMPY_HAS_LOG - { MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vectorise_log_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vector_log_obj }, #endif #if ULAB_NUMPY_HAS_LOG10 - { MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vectorise_log10_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vector_log10_obj }, #endif #if ULAB_NUMPY_HAS_LOG2 - { MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vectorise_log2_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vector_log2_obj }, #endif #if ULAB_NUMPY_HAS_RADIANS - { MP_OBJ_NEW_QSTR(MP_QSTR_radians), (mp_obj_t)&vectorise_radians_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_radians), (mp_obj_t)&vector_radians_obj }, #endif #if ULAB_NUMPY_HAS_SIN - { MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vectorise_sin_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vector_sin_obj }, #endif #if ULAB_NUMPY_HAS_SINH - { MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vectorise_sinh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vector_sinh_obj }, #endif #if ULAB_NUMPY_HAS_SQRT - { MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vectorise_sqrt_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vector_sqrt_obj }, #endif #if ULAB_NUMPY_HAS_TAN - { MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vectorise_tan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vector_tan_obj }, #endif #if ULAB_NUMPY_HAS_TANH - { MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vectorise_tanh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vector_tanh_obj }, #endif #if ULAB_NUMPY_HAS_VECTORIZE - { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vectorise_vectorize_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vector_vectorize_obj }, #endif #if ULAB_SUPPORTS_COMPLEX { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, diff --git a/code/numpy/vector.c b/code/numpy/vector.c index d2998c3d..292c164a 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -32,7 +32,7 @@ //| much more efficient than expressing the same operation as a Python loop.""" //| -static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) { +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)) { return mp_obj_new_float(f(mp_obj_get_float(o_in))); @@ -101,10 +101,10 @@ static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float #endif /* ULAB_VECTORISE_USES_FUN_POINTER */ } else { ndarray = ndarray_from_mp_obj(o_in, 0); - mp_float_t *array = (mp_float_t *)ndarray->array; + mp_float_t *narray = (mp_float_t *)ndarray->array; for(size_t i = 0; i < ndarray->len; i++) { - *array = f(*array); - array++; + *narray = f(*narray); + narray++; } } return MP_OBJ_FROM_PTR(ndarray); @@ -117,7 +117,7 @@ static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float //| MATH_FUN_1(acos, acos); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos); +MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos); #endif #if ULAB_NUMPY_HAS_ACOSH @@ -127,7 +127,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos); //| MATH_FUN_1(acosh, acosh); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh); +MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh); #endif #if ULAB_NUMPY_HAS_ASIN @@ -137,7 +137,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh); //| MATH_FUN_1(asin, asin); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin); +MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin); #endif #if ULAB_NUMPY_HAS_ASINH @@ -147,7 +147,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin); //| MATH_FUN_1(asinh, asinh); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh); +MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh); #endif #if ULAB_NUMPY_HAS_AROUND @@ -157,7 +157,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh); //| ... //| -mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +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_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } } @@ -218,7 +218,7 @@ mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return MP_OBJ_FROM_PTR(ndarray); } -MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_around_obj, 1, vectorise_around); +MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around); #endif #if ULAB_NUMPY_HAS_ATAN @@ -229,7 +229,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_around_obj, 1, vectorise_around); //| MATH_FUN_1(atan, atan); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan); +MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan); #endif #if ULAB_NUMPY_HAS_ARCTAN2 @@ -239,7 +239,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan); //| ... //| -mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) { +mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) { ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype) @@ -315,7 +315,7 @@ mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) { return MP_OBJ_FROM_PTR(results); } -MP_DEFINE_CONST_FUN_OBJ_2(vectorise_arctan2_obj, vectorise_arctan2); +MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2); #endif /* ULAB_VECTORISE_HAS_ARCTAN2 */ #if ULAB_NUMPY_HAS_ATANH @@ -325,7 +325,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(vectorise_arctan2_obj, vectorise_arctan2); //| MATH_FUN_1(atanh, atanh); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh); +MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh); #endif #if ULAB_NUMPY_HAS_CEIL @@ -335,7 +335,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh); //| MATH_FUN_1(ceil, ceil); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil); +MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil); #endif #if ULAB_NUMPY_HAS_COS @@ -345,7 +345,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil); //| MATH_FUN_1(cos, cos); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos); +MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos); #endif #if ULAB_NUMPY_HAS_COSH @@ -355,7 +355,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos); //| MATH_FUN_1(cosh, cosh); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cosh_obj, vectorise_cosh); +MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh); #endif #if ULAB_NUMPY_HAS_DEGREES @@ -364,15 +364,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cosh_obj, vectorise_cosh); //| ... //| -static mp_float_t vectorise_degrees_(mp_float_t value) { +static mp_float_t vector_degrees_(mp_float_t value) { return value * MICROPY_FLOAT_CONST(180.0) / MP_PI; } -static mp_obj_t vectorise_degrees(mp_obj_t x_obj) { - return vectorise_generic_vector(x_obj, vectorise_degrees_); +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(vectorise_degrees_obj, vectorise_degrees); +MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees); #endif #if ULAB_SCIPY_SPECIAL_HAS_ERF @@ -382,7 +382,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_degrees_obj, vectorise_degrees); //| MATH_FUN_1(erf, erf); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf); +MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf); #endif #if ULAB_SCIPY_SPECIAL_HAS_ERFC @@ -392,7 +392,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf); //| MATH_FUN_1(erfc, erfc); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc); +MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc); #endif #if ULAB_NUMPY_HAS_EXP @@ -401,7 +401,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc); //| ... //| -static mp_obj_t vectorise_exp(mp_obj_t o_in) { +static mp_obj_t vector_exp(mp_obj_t o_in) { #if ULAB_SUPPORTS_COMPLEX if(mp_obj_is_type(o_in, &mp_type_complex)) { mp_float_t real, imag; @@ -460,10 +460,10 @@ static mp_obj_t vectorise_exp(mp_obj_t o_in) { } } #endif - return vectorise_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp)); + return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp)); } -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp); +MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp); #endif #if ULAB_NUMPY_HAS_EXPM1 @@ -473,7 +473,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp); //| MATH_FUN_1(expm1, expm1); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1); +MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1); #endif #if ULAB_NUMPY_HAS_FLOOR @@ -483,7 +483,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1); //| MATH_FUN_1(floor, floor); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor); +MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor); #endif #if ULAB_SCIPY_SPECIAL_HAS_GAMMA @@ -493,7 +493,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor); //| MATH_FUN_1(gamma, tgamma); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma); +MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma); #endif #if ULAB_SCIPY_SPECIAL_HAS_GAMMALN @@ -503,7 +503,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma); //| MATH_FUN_1(lgamma, lgamma); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma); +MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma); #endif #if ULAB_NUMPY_HAS_LOG @@ -513,7 +513,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma); //| MATH_FUN_1(log, log); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log); +MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log); #endif #if ULAB_NUMPY_HAS_LOG10 @@ -523,7 +523,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log); //| MATH_FUN_1(log10, log10); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10); +MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10); #endif #if ULAB_NUMPY_HAS_LOG2 @@ -533,7 +533,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10); //| MATH_FUN_1(log2, log2); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2); +MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2); #endif #if ULAB_NUMPY_HAS_RADIANS @@ -542,15 +542,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2); //| ... //| -static mp_float_t vectorise_radians_(mp_float_t value) { +static mp_float_t vector_radians_(mp_float_t value) { return value * MP_PI / MICROPY_FLOAT_CONST(180.0); } -static mp_obj_t vectorise_radians(mp_obj_t x_obj) { - return vectorise_generic_vector(x_obj, vectorise_radians_); +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(vectorise_radians_obj, vectorise_radians); +MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians); #endif #if ULAB_NUMPY_HAS_SIN @@ -560,7 +560,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_radians_obj, vectorise_radians); //| MATH_FUN_1(sin, sin); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin); +MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin); #endif #if ULAB_NUMPY_HAS_SINH @@ -570,18 +570,156 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin); //| MATH_FUN_1(sinh, sinh); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sinh_obj, vectorise_sinh); +MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh); #endif + #if ULAB_NUMPY_HAS_SQRT //| def sqrt(a: _ArrayLike) -> ulab.numpy.ndarray: //| """Computes the square root""" //| ... //| +#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_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)]; + 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((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); + mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); + 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((source->dtype == NDARRAY_COMPLEX) && (dtype == NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("can't convert complex to float")); + } + + if(dtype == NDARRAY_COMPLEX) { + 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); + 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 */ + 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 */ + return MP_OBJ_FROM_PTR(ndarray); + } else { + mp_raise_TypeError(translate("input dtype must be float or complex")); + } + } + } + return vector_generic_vector(o_in, 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(vectorise_sqrt_obj, vectorise_sqrt); -#endif +MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt); +#endif /* ULAB_SUPPORTS_COMPLEX */ + +#endif /* ULAB_NUMPY_HAS_SQRT */ #if ULAB_NUMPY_HAS_TAN //| def tan(a: _ArrayLike) -> ulab.numpy.ndarray: @@ -590,7 +728,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sqrt_obj, vectorise_sqrt); //| MATH_FUN_1(tan, tan); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan); +MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan); #endif #if ULAB_NUMPY_HAS_TANH @@ -599,11 +737,11 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan); //| ... MATH_FUN_1(tanh, tanh); -MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tanh_obj, vectorise_tanh); +MP_DEFINE_CONST_FUN_OBJ_1(vector_tanh_obj, vector_tanh); #endif #if ULAB_NUMPY_HAS_VECTORIZE -static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +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) { (void) n_args; (void) n_kw; vectorized_function_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -643,12 +781,12 @@ static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_ar return mp_const_none; } -const mp_obj_type_t vectorise_function_type = { +const mp_obj_type_t vector_function_type = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EXTENDED, .name = MP_QSTR_, MP_TYPE_EXTENDED_FIELDS( - .call = vectorise_vectorized_function_call, + .call = vector_vectorized_function_call, ) }; @@ -666,7 +804,7 @@ const mp_obj_type_t vectorise_function_type = { //| ... //| -static mp_obj_t vectorise_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +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} } @@ -693,12 +831,12 @@ static mp_obj_t vectorise_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_ mp_raise_ValueError(translate("wrong output type")); } vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t); - function->base.type = &vectorise_function_type; + function->base.type = &vector_function_type; function->otypes = otypes; function->fun = args[0].u_obj; function->type = type; return MP_OBJ_FROM_PTR(function); } -MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_vectorize_obj, 1, vectorise_vectorize); +MP_DEFINE_CONST_FUN_OBJ_KW(vector_vectorize_obj, 1, vector_vectorize); #endif diff --git a/code/numpy/vector.h b/code/numpy/vector.h index dbd0b33e..ea38b0fd 100644 --- a/code/numpy/vector.h +++ b/code/numpy/vector.h @@ -15,35 +15,39 @@ #include "../ulab.h" #include "../ndarray.h" -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_acos_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_acosh_obj); -MP_DECLARE_CONST_FUN_OBJ_2(vectorise_arctan2_obj); -MP_DECLARE_CONST_FUN_OBJ_KW(vectorise_around_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_asin_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_asinh_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_atan_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_atanh_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_ceil_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_cos_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_cosh_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_degrees_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_erf_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_erfc_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_exp_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_expm1_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_floor_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_gamma_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_lgamma_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_log_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_log10_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_log2_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_radians_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_sin_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_sinh_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_sqrt_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_tan_obj); -MP_DECLARE_CONST_FUN_OBJ_1(vectorise_tanh_obj); -MP_DECLARE_CONST_FUN_OBJ_KW(vectorise_vectorize_obj); +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); +MP_DECLARE_CONST_FUN_OBJ_1(vector_atanh_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_ceil_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_cos_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_cosh_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_degrees_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_erf_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_erfc_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_exp_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_expm1_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_floor_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_gamma_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_lgamma_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vector_log_obj); +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_sinh_obj); +#if ULAB_SUPPORTS_COMPLEX +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 { mp_obj_base_t base; @@ -53,12 +57,13 @@ typedef struct _vectorized_function_obj_t { } vectorized_function_obj_t; #if ULAB_HAS_FUNCTION_ITERATOR -#define ITERATE_VECTOR(type, array, source, sarray)\ +#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) = f(*((type *)(sarray)));\ + (array) += (shift);\ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ }\ ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\ @@ -149,8 +154,8 @@ typedef struct _vectorized_function_obj_t { #endif /* ULAB_HAS_FUNCTION_ITERATOR */ #define MATH_FUN_1(py_name, c_name) \ - static mp_obj_t vectorise_ ## py_name(mp_obj_t x_obj) { \ - return vectorise_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(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 /* _VECTOR_ */ diff --git a/code/scipy/special/special.c b/code/scipy/special/special.c index 82b53247..79d9b77f 100644 --- a/code/scipy/special/special.c +++ b/code/scipy/special/special.c @@ -21,16 +21,16 @@ 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) }, #if ULAB_SCIPY_SPECIAL_HAS_ERF - { MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vectorise_erf_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vector_erf_obj }, #endif #if ULAB_SCIPY_SPECIAL_HAS_ERFC - { MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vectorise_erfc_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vector_erfc_obj }, #endif #if ULAB_SCIPY_SPECIAL_HAS_GAMMA - { MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vectorise_gamma_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vector_gamma_obj }, #endif #if ULAB_SCIPY_SPECIAL_HAS_GAMMALN - { MP_OBJ_NEW_QSTR(MP_QSTR_gammaln), (mp_obj_t)&vectorise_lgamma_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_gammaln), (mp_obj_t)&vector_lgamma_obj }, #endif }; diff --git a/docs/ulab-programming.ipynb b/docs/ulab-programming.ipynb index 32f12271..6eabf6d5 100644 --- a/docs/ulab-programming.ipynb +++ b/docs/ulab-programming.ipynb @@ -275,7 +275,7 @@ "A good example of how the function would be called can be found in [vector.c](https://github.com/v923z/micropython-ulab/blob/master/code/numpy/vector/vector.c), in the `vector_arctan2` function:\n", "\n", "```c\n", - "mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) {\n", + "mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) {\n", " ...\n", " uint8_t ndim = 0;\n", " size_t *shape = m_new(size_t, ULAB_MAX_DIMS);\n", From 864ab31766155a416844017dae0975a637c45def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 15 Dec 2021 06:52:42 +0100 Subject: [PATCH 30/58] implement binary add for complex arrays --- code/ndarray_operators.c | 37 ++++++++++++++ code/numpy/carray/carray.c | 17 +++++++ code/numpy/carray/carray.h | 101 +++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index 465140b6..9b412e2b 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -17,6 +17,7 @@ #include "ndarray_operators.h" #include "ulab.h" #include "ulab_tools.h" +#include "numpy/carray/carray.h" /* This file contains the actual implementations of the various @@ -161,6 +162,42 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); + mp_float_t *resarray = (mp_float_t *)results->array; + + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + uint8_t *lo = larray, *ro = rarray; + int32_t *left_strides = lstrides; + int32_t *right_strides = rstrides; + uint8_t rdtype = rhs->dtype; + + // align the complex array to the left + if(rhs->dtype == NDARRAY_COMPLEX) { + lo = (uint8_t *)rhs->array; + ro = (uint8_t *)lhs->array; + rdtype = lhs->dtype; + left_strides = rstrides; + right_strides = lstrides; + } + + larray = lo; + rarray = ro; + carray_binary_add(results, resarray, larray, rarray, left_strides, right_strides, rdtype == NDARRAY_COMPLEX ? NDARRAY_FLOAT : rdtype); + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + larray = lo + sizeof(mp_float_t); + rarray = ro + sizeof(mp_float_t); + resarray = (mp_float_t *)results->array; + resarray++; + carray_binary_add(results, resarray, larray, rarray, left_strides, right_strides, NDARRAY_FLOAT); + } + return MP_OBJ_FROM_PTR(results); + } + #endif + ndarray_obj_t *results = NULL; uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 60dede8a..62c5d6d3 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -20,6 +20,7 @@ #include "../../ulab.h" #include "../../ndarray.h" +#include "carray.h" #if ULAB_SUPPORTS_COMPLEX @@ -109,4 +110,20 @@ mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { return MP_OBJ_FROM_PTR(target); } +void carray_binary_add(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray, + int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) { + + if(rdtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, +); + } else if(rdtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, +); + } else if(rdtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, +); + } else if(rdtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, +); + } else if(rdtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, +); + } +} + #endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 113fe3ad..dc86fa5e 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -16,5 +16,106 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); +void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int32_t *, int32_t *, uint8_t); + +#if ULAB_MAX_DIMS == 1 +#define BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t l = 0;\ + do {\ + *(resarray) = *((mp_float_t *)(larray)++ OPERATOR *((type_right *)(rarray)++;\ + (resarray) += 2;\ + (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 BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ + (resarray) += 2;\ + (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 BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ + (resarray) += 2;\ + (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 BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ + (resarray) += 2;\ + (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 From cc06735cffc5069c212ff8472b040367e64ab426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 15 Dec 2021 08:02:26 +0100 Subject: [PATCH 31/58] fix binary add for 1D --- code/numpy/carray/carray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index dc86fa5e..5c28928f 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -22,7 +22,7 @@ void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int3 #define BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t l = 0;\ do {\ - *(resarray) = *((mp_float_t *)(larray)++ OPERATOR *((type_right *)(rarray)++;\ + *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ (resarray) += 2;\ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ From 8a156fe5055ceff84951cdbc93e6c5022f0fd0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 15 Dec 2021 18:52:07 +0100 Subject: [PATCH 32/58] implement multiplication for complex arrays --- code/ndarray_operators.c | 100 +++++++++++++++++++++++++++++++++++++ code/numpy/carray/carray.c | 16 ++++++ code/numpy/carray/carray.h | 85 +++++++++---------------------- 3 files changed, 139 insertions(+), 62 deletions(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index 9b412e2b..5137320a 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -185,8 +185,11 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, larray = lo; rarray = ro; + + // real part carray_binary_add(results, resarray, larray, rarray, left_strides, right_strides, rdtype == NDARRAY_COMPLEX ? NDARRAY_FLOAT : rdtype); + // imaginary part if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { larray = lo + sizeof(mp_float_t); rarray = ro + sizeof(mp_float_t); @@ -275,6 +278,103 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, 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) { + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); + mp_float_t *resarray = (mp_float_t *)results->array; + + int32_t *left_strides = lstrides; + int32_t *right_strides = rstrides; + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + mp_float_t *larray = (mp_float_t *)lhs->array; + mp_float_t *rarray = (mp_float_t *)rhs->array; + uint8_t sz = sizeof(mp_float_t); + // re-scale the strides, so that we can work with floats + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + left_strides[i] /= sz; + right_strides[i] /= sz; + } + + #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 { + // real part + *resarray++ = larray[0] * rarray[0] - larray[1] * rarray[1]; + // imaginary part + *resarray++ = larray[0] * rarray[1] + larray[1] * rarray[0]; + 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 + 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 > 1 */ + #if 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 > 2 */ + #if 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 > 3 */ + } else { + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + uint8_t *lo = larray, *ro = rarray; + int32_t *left_strides = lstrides; + int32_t *right_strides = rstrides; + uint8_t rdtype = rhs->dtype; + + // align the complex array to the left + if(rhs->dtype == NDARRAY_COMPLEX) { + lo = (uint8_t *)rhs->array; + ro = (uint8_t *)lhs->array; + rdtype = lhs->dtype; + left_strides = rstrides; + right_strides = lstrides; + } + + larray = lo; + rarray = ro; + // real part + carray_binary_multiply(results, resarray, larray, rarray, left_strides, right_strides, rdtype); + + larray = lo + sizeof(mp_float_t); + rarray = ro; + resarray = (mp_float_t *)results->array; + resarray++; + // imaginary part + carray_binary_multiply(results, resarray, larray, rarray, left_strides, right_strides, rdtype); + } + return MP_OBJ_FROM_PTR(results); + } + #endif + ndarray_obj_t *results = NULL; uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 62c5d6d3..9f3c2db2 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -126,4 +126,20 @@ void carray_binary_add(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *la } } +void carray_binary_multiply(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray, + int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) { + + if(rdtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, *); + } else if(rdtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, *); + } else if(rdtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, *); + } else if(rdtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, *); + } else if(rdtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, *); + } +} + #endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 5c28928f..5cec6049 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -17,9 +17,9 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int32_t *, int32_t *, uint8_t); +void carray_binary_multiply(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int32_t *, int32_t *, uint8_t); -#if ULAB_MAX_DIMS == 1 -#define BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ +#define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t l = 0;\ do {\ *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ @@ -29,20 +29,10 @@ void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int3 l++;\ } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ -#endif /* ULAB_MAX_DIMS == 1 */ - -#if ULAB_MAX_DIMS == 2 -#define BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ +#define BINARY_LOOP_COMPLEX2(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t k = 0;\ do {\ - size_t l = 0;\ - do {\ - *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ - (resarray) += 2;\ - (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ - (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ - l++;\ - } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + BINARY_LOOP_COMPLEX1((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\ (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];\ @@ -50,28 +40,10 @@ void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int3 k++;\ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ -#endif /* ULAB_MAX_DIMS == 2 */ - -#if ULAB_MAX_DIMS == 3 -#define BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ +#define BINARY_LOOP_COMPLEX3(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t j = 0;\ do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ - (resarray) += 2;\ - (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]);\ + BINARY_LOOP_COMPLEX2((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\ (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];\ @@ -79,36 +51,10 @@ void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int3 j++;\ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ -#endif /* ULAB_MAX_DIMS == 3 */ - -#if ULAB_MAX_DIMS == 4 -#define BINARY_LOOP_COMPLEX(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ +#define BINARY_LOOP_COMPLEX4(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t i = 0;\ do {\ - size_t j = 0;\ - do {\ - size_t k = 0;\ - do {\ - size_t l = 0;\ - do {\ - *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\ - (resarray) += 2;\ - (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]);\ + BINARY_LOOP_COMPLEX3((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\ (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];\ @@ -116,6 +62,21 @@ void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int3 i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#if ULAB_MAX_DIMS == 1 +#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1 +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2 +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3 +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4 #endif /* ULAB_MAX_DIMS == 4 */ #endif From 76218f23864fcc749c20283304c1c45f832bfbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 15 Dec 2021 18:54:14 +0100 Subject: [PATCH 33/58] fix 3D loop --- code/ndarray_operators.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index 5137320a..c4736202 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -332,7 +332,7 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, 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]); + } while(j < results->shape[ULAB_MAX_DIMS - 3]); #endif /* ULAB_MAX_DIMS > 2 */ #if ULAB_MAX_DIMS > 3 larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3]; From 13cebfecccbe6ea10c4b86a6ff54a087dbeec8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 28 Dec 2021 20:42:00 +0100 Subject: [PATCH 34/58] add complex subtraction, fix addition --- code/ndarray_operators.c | 137 ++------------- code/numpy/carray/carray.c | 333 ++++++++++++++++++++++++++++++++++++- code/numpy/carray/carray.h | 51 +++++- code/ulab_tools.c | 10 ++ code/ulab_tools.h | 5 + 5 files changed, 405 insertions(+), 131 deletions(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index c4736202..449d37ea 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -25,7 +25,8 @@ These are the upcasting rules of the binary operators - - if one of the operarands is a float, the result is always float + - if complex is supported, and if one of the operarands is a complex, the result is always complex + - if both operarands are real one of them is a float, then the result is also a float - operation on identical types preserves type uint8 + int8 => int16 @@ -164,40 +165,7 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, #if ULAB_SUPPORTS_COMPLEX if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { - ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); - mp_float_t *resarray = (mp_float_t *)results->array; - - uint8_t *larray = (uint8_t *)lhs->array; - uint8_t *rarray = (uint8_t *)rhs->array; - uint8_t *lo = larray, *ro = rarray; - int32_t *left_strides = lstrides; - int32_t *right_strides = rstrides; - uint8_t rdtype = rhs->dtype; - - // align the complex array to the left - if(rhs->dtype == NDARRAY_COMPLEX) { - lo = (uint8_t *)rhs->array; - ro = (uint8_t *)lhs->array; - rdtype = lhs->dtype; - left_strides = rstrides; - right_strides = lstrides; - } - - larray = lo; - rarray = ro; - - // real part - carray_binary_add(results, resarray, larray, rarray, left_strides, right_strides, rdtype == NDARRAY_COMPLEX ? NDARRAY_FLOAT : rdtype); - - // imaginary part - if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { - larray = lo + sizeof(mp_float_t); - rarray = ro + sizeof(mp_float_t); - resarray = (mp_float_t *)results->array; - resarray++; - carray_binary_add(results, resarray, larray, rarray, left_strides, right_strides, NDARRAY_FLOAT); - } - return MP_OBJ_FROM_PTR(results); + return carray_binary_add(lhs, rhs, ndim, shape, lstrides, rstrides); } #endif @@ -280,98 +248,7 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, #if ULAB_SUPPORTS_COMPLEX if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { - ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); - mp_float_t *resarray = (mp_float_t *)results->array; - - int32_t *left_strides = lstrides; - int32_t *right_strides = rstrides; - - if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { - mp_float_t *larray = (mp_float_t *)lhs->array; - mp_float_t *rarray = (mp_float_t *)rhs->array; - uint8_t sz = sizeof(mp_float_t); - // re-scale the strides, so that we can work with floats - for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { - left_strides[i] /= sz; - right_strides[i] /= sz; - } - - #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 { - // real part - *resarray++ = larray[0] * rarray[0] - larray[1] * rarray[1]; - // imaginary part - *resarray++ = larray[0] * rarray[1] + larray[1] * rarray[0]; - 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 - 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 > 1 */ - #if 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 > 2 */ - #if 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 > 3 */ - } else { - uint8_t *larray = (uint8_t *)lhs->array; - uint8_t *rarray = (uint8_t *)rhs->array; - uint8_t *lo = larray, *ro = rarray; - int32_t *left_strides = lstrides; - int32_t *right_strides = rstrides; - uint8_t rdtype = rhs->dtype; - - // align the complex array to the left - if(rhs->dtype == NDARRAY_COMPLEX) { - lo = (uint8_t *)rhs->array; - ro = (uint8_t *)lhs->array; - rdtype = lhs->dtype; - left_strides = rstrides; - right_strides = lstrides; - } - - larray = lo; - rarray = ro; - // real part - carray_binary_multiply(results, resarray, larray, rarray, left_strides, right_strides, rdtype); - - larray = lo + sizeof(mp_float_t); - rarray = ro; - resarray = (mp_float_t *)results->array; - resarray++; - // imaginary part - carray_binary_multiply(results, resarray, larray, rarray, left_strides, right_strides, rdtype); - } - return MP_OBJ_FROM_PTR(results); + return carray_binary_multiply(lhs, rhs, ndim, shape, lstrides, rstrides); } #endif @@ -597,6 +474,12 @@ mp_obj_t ndarray_binary_more(ndarray_obj_t *lhs, ndarray_obj_t *rhs, mp_obj_t ndarray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + return carray_binary_subtract(lhs, rhs, ndim, shape, lstrides, rstrides); + } + #endif + ndarray_obj_t *results = NULL; uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 9f3c2db2..806764f3 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -20,6 +20,7 @@ #include "../../ulab.h" #include "../../ndarray.h" +#include "../../ulab_tools.h" #include "carray.h" #if ULAB_SUPPORTS_COMPLEX @@ -110,7 +111,51 @@ mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { return MP_OBJ_FROM_PTR(target); } -void carray_binary_add(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray, +static void carray_copy_part(uint8_t *tarray, uint8_t *sarray, size_t *shape, int32_t *strides) { + // copies the real or imaginary part of an array + // into the respective part of a dense complex array + uint8_t sz = 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 { + memcpy(tarray, sarray, sz); + tarray += 2 * sz; + sarray += strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS-1]; + sarray += strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < shape[ULAB_MAX_DIMS - 2]); + #endif /* ULAB_MAX_DIMS > 1 */ + #if ULAB_MAX_DIMS > 2 + sarray -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2]; + sarray += strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < shape[ULAB_MAX_DIMS - 3]); + #endif /* ULAB_MAX_DIMS > 2 */ + #if ULAB_MAX_DIMS > 3 + sarray -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS-3]; + sarray += strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < shape[ULAB_MAX_DIMS - 4]); + #endif /* ULAB_MAX_DIMS > 3 */ +} + +static void carray_binary_add_(ndarray_obj_t *results, mp_float_t *resarray, mp_float_t *larray, uint8_t *rarray, int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) { if(rdtype == NDARRAY_UINT8) { @@ -126,7 +171,96 @@ void carray_binary_add(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *la } } -void carray_binary_multiply(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray, +mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); + mp_float_t *resarray = (mp_float_t *)results->array; + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + mp_float_t *larray = (mp_float_t *)lhs->array; + mp_float_t *rarray = (mp_float_t *)rhs->array; + + ulab_rescale_float_strides(lstrides); + ulab_rescale_float_strides(rstrides); + + #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 { + // real part + *resarray++ = larray[0] + rarray[0]; + // imaginary part + *resarray++ = larray[1] + rarray[1]; + 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 + 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 > 1 */ + #if 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 > 2 */ + #if 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 > 3 */ + } else { // only one of the operands is complex + mp_float_t *larray; + uint8_t *rarray; + + // align the complex array to the left + if(lhs->dtype == NDARRAY_COMPLEX) { + larray = (mp_float_t *)lhs->array; + rarray = (uint8_t *)rhs->array; + carray_binary_add_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype); + } else { + larray = (mp_float_t *)rhs->array; + rarray = (uint8_t *)lhs->array; + carray_binary_add_(results, resarray, larray, rarray, rstrides, lstrides, lhs->dtype); + } + // simply copy the imaginary part + uint8_t *tarray = (uint8_t *)results->array; + tarray += sizeof(mp_float_t); + + if(lhs->dtype == NDARRAY_COMPLEX) { + rarray = (uint8_t *)lhs->array; + rstrides = lstrides; + } else { + rarray = (uint8_t *)rhs->array; + } + rarray += sizeof(mp_float_t); + carray_copy_part(tarray, rarray, results->shape, rstrides); + } + return MP_OBJ_FROM_PTR(results); +} + +static void carray_binary_multiply_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray, int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) { if(rdtype == NDARRAY_UINT8) { @@ -142,4 +276,199 @@ void carray_binary_multiply(ndarray_obj_t *results, mp_float_t *resarray, uint8_ } } +mp_obj_t carray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); + mp_float_t *resarray = (mp_float_t *)results->array; + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + mp_float_t *larray = (mp_float_t *)lhs->array; + mp_float_t *rarray = (mp_float_t *)rhs->array; + + ulab_rescale_float_strides(lstrides); + ulab_rescale_float_strides(rstrides); + + #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 { + // real part + *resarray++ = larray[0] * rarray[0] - larray[1] * rarray[1]; + // imaginary part + *resarray++ = larray[0] * rarray[1] + larray[1] * rarray[0]; + 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 + 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 > 1 */ + #if 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 > 2 */ + #if 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 > 3 */ + } else { // only one of the operands is complex + + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + uint8_t *lo = larray, *ro = rarray; + int32_t *left_strides = lstrides; + int32_t *right_strides = rstrides; + uint8_t rdtype = rhs->dtype; + + // align the complex array to the left + if(rhs->dtype == NDARRAY_COMPLEX) { + lo = (uint8_t *)rhs->array; + ro = (uint8_t *)lhs->array; + rdtype = lhs->dtype; + left_strides = rstrides; + right_strides = lstrides; + } + + larray = lo; + rarray = ro; + // real part + carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype); + + larray = lo + sizeof(mp_float_t); + rarray = ro; + resarray = (mp_float_t *)results->array; + resarray++; + // imaginary part + carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype); + } + return MP_OBJ_FROM_PTR(results); +} + + +mp_obj_t carray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); + mp_float_t *resarray = (mp_float_t *)results->array; + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + mp_float_t *larray = (mp_float_t *)lhs->array; + mp_float_t *rarray = (mp_float_t *)rhs->array; + + ulab_rescale_float_strides(lstrides); + ulab_rescale_float_strides(rstrides); + + #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 { + // real part + *resarray++ = larray[0] - rarray[0]; + // imaginary part + *resarray++ = larray[1] - rarray[1]; + 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 + 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 > 1 */ + #if 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 > 2 */ + #if 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 > 3 */ + } else { + uint8_t *larray = (uint8_t *)lhs->array; + if(lhs->dtype == NDARRAY_COMPLEX) { + uint8_t *rarray = (uint8_t *)rhs->array; + if(rhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, -); + } + // copy the imaginary part + uint8_t *tarray = (uint8_t *)results->array; + tarray += sizeof(mp_float_t); + + larray = (uint8_t *)lhs->array; + larray += sizeof(mp_float_t); + + carray_copy_part(tarray, larray, results->shape, lstrides); + } else if(rhs->dtype == NDARRAY_COMPLEX) { + mp_float_t *rarray = (mp_float_t *)rhs->array; + ulab_rescale_float_strides(rstrides); + + if(lhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint8_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int8_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint16_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int16_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides); + } + } + } + + return MP_OBJ_FROM_PTR(results); +} #endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 5cec6049..f62bf9a0 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -16,8 +16,9 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); -void carray_binary_add(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int32_t *, int32_t *, uint8_t); -void carray_binary_multiply(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, int32_t *, int32_t *, uint8_t); +mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); #define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t l = 0;\ @@ -62,21 +63,67 @@ void carray_binary_multiply(ndarray_obj_t *, mp_float_t *, uint8_t *, uint8_t *, i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t l = 0;\ + do {\ + *(resarray)++ = *((type_left *)(larray)) - (rarray)[0];\ + *(resarray)++ = -(rarray)[1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t k = 0;\ + do {\ + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1((results), (resarray), type_left, (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];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t j = 0;\ + do {\ + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ + (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]);\ + +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t i = 0;\ + do {\ + BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ + (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]);\ #if ULAB_MAX_DIMS == 1 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1 +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1 #endif /* ULAB_MAX_DIMS == 1 */ #if ULAB_MAX_DIMS == 2 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2 +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2 #endif /* ULAB_MAX_DIMS == 2 */ #if ULAB_MAX_DIMS == 3 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3 +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3 #endif /* ULAB_MAX_DIMS == 3 */ #if ULAB_MAX_DIMS == 4 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4 +#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4 #endif /* ULAB_MAX_DIMS == 4 */ #endif diff --git a/code/ulab_tools.c b/code/ulab_tools.c index d0537230..5bf9b07d 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -240,3 +240,13 @@ uint8_t ulab_binary_get_size(uint8_t dtype) { #endif return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); } + +#if ULAB_SUPPORTS_COMPLEX +void ulab_rescale_float_strides(int32_t *strides) { + // re-scale the strides, so that we can work with floats, when iterating + uint8_t sz = sizeof(mp_float_t); + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + strides[i] /= sz; + } +} +#endif \ No newline at end of file diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 216dbaba..beab3382 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -36,4 +36,9 @@ shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); uint8_t ulab_binary_get_size(uint8_t ); + +#if ULAB_SUPPORTS_COMPLEX +void ulab_rescale_float_strides(int32_t *); +#endif + #endif From 5b7395d6df18ec44db2463e3ce64733fae626de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 28 Dec 2021 21:51:32 +0100 Subject: [PATCH 35/58] implement complex divide --- code/ndarray_operators.c | 6 ++ code/numpy/carray/carray.c | 113 ++++++++++++++++++++++++++++++++++++- code/numpy/carray/carray.h | 52 +++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index 449d37ea..f3b87613 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -579,6 +579,12 @@ mp_obj_t ndarray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs, mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + #if ULAB_SUPPORTS_COMPLEX + if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) { + return carray_binary_divide(lhs, rhs, ndim, shape, lstrides, rstrides); + } + #endif + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); uint8_t *larray = (uint8_t *)lhs->array; uint8_t *rarray = (uint8_t *)rhs->array; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 806764f3..b5bcec3b 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -368,7 +368,6 @@ mp_obj_t carray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, return MP_OBJ_FROM_PTR(results); } - mp_obj_t carray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { @@ -471,4 +470,116 @@ mp_obj_t carray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs, return MP_OBJ_FROM_PTR(results); } + +static void carray_binary_left_divide_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray, + int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) { + + if(rdtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, /); + } else if(rdtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, /); + } else if(rdtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, /); + } else if(rdtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, /); + } else if(rdtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, /); + } +} + +mp_obj_t carray_binary_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 = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX); + mp_float_t *resarray = (mp_float_t *)results->array; + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + mp_float_t *larray = (mp_float_t *)lhs->array; + mp_float_t *rarray = (mp_float_t *)rhs->array; + + ulab_rescale_float_strides(lstrides); + ulab_rescale_float_strides(rstrides); + + #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 { + // (a + bi) / (c + di) = + // (ac + bd) / (c^2 + d^2) + i (bc - ad) / (c^2 + d^2) + // denominator + mp_float_t denom = rarray[0] * rarray[0] + rarray[1] * rarray[1]; + + // real part + *resarray++ = (larray[0] * rarray[0] + larray[1] * rarray[1]) / denom; + // imaginary part + *resarray++ = (larray[1] * rarray[0] - larray[0] * rarray[1]) / denom; + 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 + 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 > 1 */ + #if 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 > 2 */ + #if 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 > 3 */ + } else { + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + if(lhs->dtype == NDARRAY_COMPLEX) { + // real part + carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype); + // imaginary part + resarray = (mp_float_t *)results->array; + resarray++; + larray = (uint8_t *)lhs->array; + larray += sizeof(mp_float_t); + rarray = (uint8_t *)rhs->array; + carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype); + } else { + if(lhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint8_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int8_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint16_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int16_t, larray, lstrides, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides); + } + } + } + + return MP_OBJ_FROM_PTR(results); +} + #endif diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index f62bf9a0..df504d3b 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -19,6 +19,7 @@ mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); #define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t l = 0;\ @@ -106,24 +107,75 @@ mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t l = 0;\ + do {\ + mp_float_t *c = (mp_float_t *)(rarray);\ + mp_float_t denom = c[0] * c[0] + c[1] * c[1];\ + mp_float_t a = *((type_left *)(larray)) / denom;\ + *(resarray)++ = a * c[0];\ + *(resarray)++ = -a * c[1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t k = 0;\ + do {\ + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1((results), (resarray), type_left, (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];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t j = 0;\ + do {\ + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ + (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]);\ + +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ + size_t i = 0;\ + do {\ + BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ + (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]);\ + #if ULAB_MAX_DIMS == 1 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1 +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1 #endif /* ULAB_MAX_DIMS == 1 */ #if ULAB_MAX_DIMS == 2 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2 +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2 #endif /* ULAB_MAX_DIMS == 2 */ #if ULAB_MAX_DIMS == 3 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3 +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3 #endif /* ULAB_MAX_DIMS == 3 */ #if ULAB_MAX_DIMS == 4 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4 +#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4 #endif /* ULAB_MAX_DIMS == 4 */ #endif From 14b5c13d50f4c3ceebf69985cfc7f3dbbf0938ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 28 Dec 2021 21:55:00 +0100 Subject: [PATCH 36/58] add complex flag to version string --- code/ulab.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/ulab.c b/code/ulab.c index 2b9ebd73..dd2cdc2e 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,10 +33,15 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 3.3.8 +#define ULAB_VERSION 4.0.0 #define xstr(s) str(s) #define str(s) #s + +#if ULAB_SUPPORTS_COMPLEX +#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D-c) +#else #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); From dbd570a9fbb96f8152e05fff2427482737230bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 28 Dec 2021 22:18:37 +0100 Subject: [PATCH 37/58] implement complex scalar binary operations --- code/ndarray.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/code/ndarray.c b/code/ndarray.c index 0aa346f5..146a91b0 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1747,7 +1747,15 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { array[0] = mp_obj_get_float(obj); } else if(mp_obj_is_type(obj, &ulab_ndarray_type)){ return obj; - } else { + } + #if ULAB_SUPPORTS_COMPLEX + else if (mp_obj_is_type(obj, &mp_type_complex)) { + ndarray = ndarray_new_linear_array(1, NDARRAY_COMPLEX); + mp_float_t *array = (mp_float_t *)ndarray->array; + mp_obj_get_complex(obj, &array[0], &array[1]); + } + #endif + else { // assume that the input is an iterable (raises an exception, if it is not the case) ndarray = ndarray_from_iterable(obj, NDARRAY_FLOAT); } From ee197ef03f105b81e84303881c9b1ce04a2c8aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 28 Dec 2021 22:30:17 +0100 Subject: [PATCH 38/58] bring in a99e0b9 in an attempt to fix the docs error --- code/numpy/numerical.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 8b944818..08e5153b 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -49,11 +49,8 @@ enum NUMERICAL_FUNCTION_TYPE { //| _DType = int //| """`ulab.numpy.int8`, `ulab.numpy.uint8`, `ulab.numpy.int16`, `ulab.numpy.uint16`, `ulab.numpy.float` or `ulab.numpy.bool`""" //| -//| _float = float -//| """Type alias of the bulitin float""" -//| -//| _bool = bool -//| """Type alias of the bulitin bool""" +//| from builtins import float as _float +//| from builtins import bool as _bool //| //| int8: _DType //| """Type code for signed integers in the range -128 .. 127 inclusive, like the 'b' typecode of `array.array`""" From 7b17796c91d6462ae806f518acfa2b264f97f3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 30 Dec 2021 08:10:54 +0100 Subject: [PATCH 39/58] fix complex sqrt --- code/numpy/vector.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/numpy/vector.c b/code/numpy/vector.c index 292c164a..97ab66d2 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -599,8 +599,10 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) if(mp_obj_is_type(o_in, &mp_type_complex)) { mp_float_t real, imag; mp_obj_get_complex(o_in, &real, &imag); - mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); - return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag)); + 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); + return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta)); } 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)) { From 4d7e2799963f5546fb9d8800b9ded8cc5db9509f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 30 Dec 2021 08:54:52 +0100 Subject: [PATCH 40/58] fix circuitpython documentation --- code/micropython.mk | 2 +- code/{ulab_create.c => numpy/create.c} | 160 ++++++++++++++++++++++++- code/{ulab_create.h => numpy/create.h} | 4 +- code/numpy/numpy.c | 2 +- code/ulab.c | 2 +- 5 files changed, 162 insertions(+), 8 deletions(-) rename code/{ulab_create.c => numpy/create.c} (80%) rename code/{ulab_create.h => numpy/create.h} (97%) diff --git a/code/micropython.mk b/code/micropython.mk index 64ab6152..d16b177a 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -15,7 +15,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/numpy/approx.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 -SRC_USERMOD += $(USERMODULES_DIR)/ulab_create.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/create.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft_tools.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/filter.c diff --git a/code/ulab_create.c b/code/numpy/create.c similarity index 80% rename from code/ulab_create.c rename to code/numpy/create.c index 2dc1412b..80e3864c 100644 --- a/code/ulab_create.c +++ b/code/numpy/create.c @@ -17,9 +17,9 @@ #include "py/obj.h" #include "py/runtime.h" -#include "ulab.h" -#include "ulab_create.h" -#include "ulab_tools.h" +#include "../ulab.h" +#include "create.h" +#include "../ulab_tools.h" #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) { @@ -90,6 +90,24 @@ static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, #endif #if ULAB_NUMPY_HAS_ARANGE +//| @overload +//| def arange(stop: _float, step: _float = 1, *, dtype: _DType = ulab.float) -> ulab.ndarray: ... +//| @overload +//| def arange(start: _float, stop: _float, step: _float = 1, *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: start +//| First value in the array, optional, defaults to 0 +//| .. param: stop +//| Final value in the array +//| .. param: step +//| Difference between consecutive elements, optional, defaults to 1.0 +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new 1-D array with elements ranging from ``start`` to ``stop``, with step size ``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 } }, @@ -140,6 +158,17 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_arange_obj, 1, create_arange); #endif #if ULAB_NUMPY_HAS_CONCATENATE +//| def concatenate(arrays: Tuple[ulab.ndarray], *, axis: int = 0) -> ulab.ndarray: +//| """ +//| .. param: arrays +//| tuple of ndarrays +//| .. param: axis +//| axis along which the arrays will be joined +//| +//| Join a sequence of arrays along an existing axis.""" +//| ... +//| + 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 } }, @@ -255,6 +284,17 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_concatenate_obj, 1, create_concatenate); #if ULAB_MAX_DIMS > 1 #if ULAB_NUMPY_HAS_DIAG +//| def diag(a: ulab.ndarray, *, k: int = 0) -> ulab.ndarray: +//| """ +//| .. param: a +//| an ndarray +//| .. param: k +//| Offset of the diagonal from the main diagonal. Can be positive or negative. +//| +//| Return specified diagonals.""" +//| ... +//| + 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 } }, @@ -316,7 +356,28 @@ mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) MP_DEFINE_CONST_FUN_OBJ_KW(create_diag_obj, 1, create_diag); #endif /* ULAB_NUMPY_HAS_DIAG */ +#if ULAB_NUMPY_HAS_EMPTY +// This function is bound in numpy.c to numpy.zeros(), and is simply an alias for that + +//| def empty(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 0. An alias for numpy.zeros.""" +//| ... +//| +#endif + #if ULAB_NUMPY_HAS_EYE +//| def eye(size: int, *, M: Optional[int] = None, k: int = 0, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """Return a new square array of size, with the diagonal elements set to 1 +//| and the other elements set to 0. If k is given, the diagonal is shifted by the specified amount.""" +//| ... +//| + 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 } }, @@ -363,6 +424,19 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_eye_obj, 1, create_eye); #endif /* ULAB_MAX_DIMS > 1 */ #if ULAB_NUMPY_HAS_FULL +//| def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[_float, _bool], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of integers (for tensors of higher rank) +//| .. param: fill_value +//| scalar, the value with which the array is filled +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 0.""" +//| ... +//| + mp_obj_t create_full(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 } }, @@ -383,6 +457,35 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_full_obj, 0, create_full); #if ULAB_NUMPY_HAS_LINSPACE +//| def linspace( +//| start: _float, +//| stop: _float, +//| *, +//| dtype: _DType = ulab.float, +//| num: int = 50, +//| endpoint: _bool = True, +//| retstep: _bool = False +//| ) -> ulab.ndarray: +//| """ +//| .. param: start +//| First value in the array +//| .. param: stop +//| Final value in the array +//| .. param int: num +//| Count of values in the array. +//| .. param: dtype +//| Type of values in the array +//| .. param bool: endpoint +//| Whether the ``stop`` value is included. Note that even when +//| endpoint=True, the exact ``stop`` value may not be included due to the +//| inaccuracy of floating point arithmetic. +//| .. param bool: retstep, +//| If True, return (`samples`, `step`), where `step` is the spacing between samples. +//| +//| Return a new 1-D array with ``num`` elements ranging from ``start`` to ``stop`` linearly.""" +//| ... +//| + 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 } }, @@ -420,6 +523,37 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace); #endif #if ULAB_NUMPY_HAS_LOGSPACE +//| def logspace( +//| start: _float, +//| stop: _float, +//| *, +//| dtype: _DType = ulab.float, +//| num: int = 50, +//| endpoint: _bool = True, +//| base: _float = 10.0 +//| ) -> ulab.ndarray: +//| """ +//| .. param: start +//| First value in the array +//| .. param: stop +//| Final value in the array +//| .. param int: num +//| Count of values in the array. Defaults to 50. +//| .. param: base +//| The base of the log space. The step size between the elements in +//| ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform. Defaults to 10.0. +//| .. param: dtype +//| Type of values in the array +//| .. param bool: endpoint +//| Whether the ``stop`` value is included. Note that even when +//| endpoint=True, the exact ``stop`` value may not be included due to the +//| inaccuracy of floating point arithmetic. Defaults to True. +//| +//| Return a new 1-D array with ``num`` evenly spaced elements on a log scale. +//| The sequence starts at ``base ** start``, and ends with ``base ** stop``.""" +//| ... +//| + const mp_obj_float_t create_float_const_ten = {{&mp_type_float}, MICROPY_FLOAT_CONST(10.0)}; mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -476,6 +610,16 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_logspace_obj, 2, create_logspace); #endif #if ULAB_NUMPY_HAS_ONES +//| def ones(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 1.""" +//| ... +//| mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { @@ -495,6 +639,16 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones); #endif #if ULAB_NUMPY_HAS_ZEROS +//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 0.""" +//| ... +//| mp_obj_t create_zeros(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/ulab_create.h b/code/numpy/create.h similarity index 97% rename from code/ulab_create.h rename to code/numpy/create.h index 9aefc0b2..cba2eba8 100644 --- a/code/ulab_create.h +++ b/code/numpy/create.h @@ -12,8 +12,8 @@ #ifndef _CREATE_ #define _CREATE_ -#include "ulab.h" -#include "ndarray.h" +#include "../ulab.h" +#include "../ndarray.h" #if ULAB_NUMPY_HAS_ARANGE mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index f69150e7..86f73eb8 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -17,10 +17,10 @@ #include "py/runtime.h" #include "numpy.h" -#include "../ulab_create.h" #include "approx.h" #include "carray/carray.h" #include "compare.h" +#include "create.h" #include "fft/fft.h" #include "filter.h" #include "linalg/linalg.h" diff --git a/code/ulab.c b/code/ulab.c index dd2cdc2e..3b825335 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -20,9 +20,9 @@ #include "py/objarray.h" #include "ulab.h" -#include "ulab_create.h" #include "ndarray.h" #include "ndarray_properties.h" +#include "numpy/create.h" #include "numpy/ndarray/ndarray_iter.h" #include "numpy/numpy.h" From e01e547b6240a8af6a9ae2d1455e3af3b8cc27b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 30 Dec 2021 09:09:41 +0100 Subject: [PATCH 41/58] fix dtype in stubs --- code/numpy/create.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/code/numpy/create.c b/code/numpy/create.c index 80e3864c..32519bc4 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -91,9 +91,9 @@ static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, #if ULAB_NUMPY_HAS_ARANGE //| @overload -//| def arange(stop: _float, step: _float = 1, *, dtype: _DType = ulab.float) -> ulab.ndarray: ... +//| def arange(stop: _float, step: _float = 1, *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: ... //| @overload -//| def arange(start: _float, stop: _float, step: _float = 1, *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| def arange(start: _float, stop: _float, step: _float = 1, *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """ //| .. param: start //| First value in the array, optional, defaults to 0 @@ -158,7 +158,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_arange_obj, 1, create_arange); #endif #if ULAB_NUMPY_HAS_CONCATENATE -//| def concatenate(arrays: Tuple[ulab.ndarray], *, axis: int = 0) -> ulab.ndarray: +//| def concatenate(arrays: Tuple[ulab.numpy.ndarray], *, axis: int = 0) -> ulab.numpy.ndarray: //| """ //| .. param: arrays //| tuple of ndarrays @@ -284,7 +284,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_concatenate_obj, 1, create_concatenate); #if ULAB_MAX_DIMS > 1 #if ULAB_NUMPY_HAS_DIAG -//| def diag(a: ulab.ndarray, *, k: int = 0) -> ulab.ndarray: +//| def diag(a: ulab.numpy.ndarray, *, k: int = 0) -> ulab.numpy.ndarray: //| """ //| .. param: a //| an ndarray @@ -359,7 +359,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_diag_obj, 1, create_diag); #if ULAB_NUMPY_HAS_EMPTY // This function is bound in numpy.c to numpy.zeros(), and is simply an alias for that -//| def empty(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| def empty(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """ //| .. param: shape //| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) @@ -372,7 +372,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_diag_obj, 1, create_diag); #endif #if ULAB_NUMPY_HAS_EYE -//| def eye(size: int, *, M: Optional[int] = None, k: int = 0, dtype: _DType = ulab.float) -> ulab.ndarray: +//| def eye(size: int, *, M: Optional[int] = None, k: int = 0, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """Return a new square array of size, with the diagonal elements set to 1 //| and the other elements set to 0. If k is given, the diagonal is shifted by the specified amount.""" //| ... @@ -424,7 +424,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_eye_obj, 1, create_eye); #endif /* ULAB_MAX_DIMS > 1 */ #if ULAB_NUMPY_HAS_FULL -//| def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[_float, _bool], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[_float, _bool], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """ //| .. param: shape //| Shape of the array, either an integer (for a 1-D array) or a tuple of integers (for tensors of higher rank) @@ -461,11 +461,11 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_full_obj, 0, create_full); //| start: _float, //| stop: _float, //| *, -//| dtype: _DType = ulab.float, +//| dtype: _DType = ulab.numpy.float, //| num: int = 50, //| endpoint: _bool = True, //| retstep: _bool = False -//| ) -> ulab.ndarray: +//| ) -> ulab.numpy.ndarray: //| """ //| .. param: start //| First value in the array @@ -527,11 +527,11 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace); //| start: _float, //| stop: _float, //| *, -//| dtype: _DType = ulab.float, +//| dtype: _DType = ulab.numpy.float, //| num: int = 50, //| endpoint: _bool = True, //| base: _float = 10.0 -//| ) -> ulab.ndarray: +//| ) -> ulab.numpy.ndarray: //| """ //| .. param: start //| First value in the array @@ -610,7 +610,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_logspace_obj, 2, create_logspace); #endif #if ULAB_NUMPY_HAS_ONES -//| def ones(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| def ones(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """ //| .. param: shape //| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) @@ -639,7 +639,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones); #endif #if ULAB_NUMPY_HAS_ZEROS -//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: //| """ //| .. param: shape //| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) From 9eebed2d3a13f725f5c87b844b3b351302896728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 30 Dec 2021 11:32:07 +0100 Subject: [PATCH 42/58] add basic complex tests --- test-common.sh | 2 +- tests/1d/complex/complex_exp.py | 17 + tests/1d/complex/complex_exp.py.exp | 42 ++ tests/1d/complex/complex_sqrt.py | 18 + tests/1d/complex/complex_sqrt.py.exp | 42 ++ tests/1d/complex/imag_real.py | 19 + tests/1d/complex/imag_real.py.exp | 14 + tests/2d/complex/complex_exp.py | 24 + tests/2d/complex/complex_exp.py.exp | 98 +++++ tests/2d/complex/complex_sqrt.py | 25 ++ tests/2d/complex/complex_sqrt.py.exp | 98 +++++ tests/2d/complex/imag_real.py | 28 ++ tests/2d/complex/imag_real.py.exp | 146 +++++++ tests/3d/complex/complex_exp.py | 24 + tests/3d/complex/complex_exp.py.exp | 115 +++++ tests/3d/complex/complex_sqrt.py | 26 ++ tests/3d/complex/complex_sqrt.py.exp | 151 +++++++ tests/3d/complex/imag_real.py | 28 ++ tests/3d/complex/imag_real.py.exp | 309 +++++++++++++ tests/4d/complex/complex_exp.py | 26 ++ tests/4d/complex/complex_exp.py.exp | 142 ++++++ tests/4d/complex/complex_sqrt.py | 27 ++ tests/4d/complex/complex_sqrt.py.exp | 250 +++++++++++ tests/4d/complex/imag_real.py | 29 ++ tests/4d/complex/imag_real.py.exp | 625 +++++++++++++++++++++++++++ 25 files changed, 2324 insertions(+), 1 deletion(-) create mode 100644 tests/1d/complex/complex_exp.py create mode 100644 tests/1d/complex/complex_exp.py.exp create mode 100644 tests/1d/complex/complex_sqrt.py create mode 100644 tests/1d/complex/complex_sqrt.py.exp create mode 100644 tests/1d/complex/imag_real.py create mode 100644 tests/1d/complex/imag_real.py.exp create mode 100644 tests/2d/complex/complex_exp.py create mode 100644 tests/2d/complex/complex_exp.py.exp create mode 100644 tests/2d/complex/complex_sqrt.py create mode 100644 tests/2d/complex/complex_sqrt.py.exp create mode 100644 tests/2d/complex/imag_real.py create mode 100644 tests/2d/complex/imag_real.py.exp create mode 100644 tests/3d/complex/complex_exp.py create mode 100644 tests/3d/complex/complex_exp.py.exp create mode 100644 tests/3d/complex/complex_sqrt.py create mode 100644 tests/3d/complex/complex_sqrt.py.exp create mode 100644 tests/3d/complex/imag_real.py create mode 100644 tests/3d/complex/imag_real.py.exp create mode 100644 tests/4d/complex/complex_exp.py create mode 100644 tests/4d/complex/complex_exp.py.exp create mode 100644 tests/4d/complex/complex_sqrt.py create mode 100644 tests/4d/complex/complex_sqrt.py.exp create mode 100644 tests/4d/complex/imag_real.py create mode 100644 tests/4d/complex/imag_real.py.exp diff --git a/test-common.sh b/test-common.sh index 265bb834..d4e4d1e7 100644 --- a/test-common.sh +++ b/test-common.sh @@ -4,7 +4,7 @@ dims="$1" micropython="$2" for level1 in $(printf "%dd " $(seq 1 ${dims})) do - for level2 in numpy scipy utils; do + for level2 in numpy scipy utils complex; do rm -f *.exp if ! env MICROPY_MICROPYTHON="$micropython" ./run-tests -d tests/"$level1"/"$level2"; then for exp in *.exp; do diff --git a/tests/1d/complex/complex_exp.py b/tests/1d/complex/complex_exp.py new file mode 100644 index 00000000..979b5b8e --- /dev/null +++ b/tests/1d/complex/complex_exp.py @@ -0,0 +1,17 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + print('\narray:\n', a) + print('\nexponential:\n', np.exp(a)) + +b = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +print('\narray:\n', b) +print('\nexponential:\n', np.exp(b)) \ No newline at end of file diff --git a/tests/1d/complex/complex_exp.py.exp b/tests/1d/complex/complex_exp.py.exp new file mode 100644 index 00000000..fb34d531 --- /dev/null +++ b/tests/1d/complex/complex_exp.py.exp @@ -0,0 +1,42 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=uint16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 2.718281828459045+0.0j, 7.38905609893065+0.0j, 20.08553692318767+0.0j], dtype=complex) + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], dtype=complex) diff --git a/tests/1d/complex/complex_sqrt.py b/tests/1d/complex/complex_sqrt.py new file mode 100644 index 00000000..aa709aef --- /dev/null +++ b/tests/1d/complex/complex_sqrt.py @@ -0,0 +1,18 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + outtype = np.float if dtype is not np.complex else np.complex + print('\narray:\n', a) + print('\nsquare root:\n', np.sqrt(a, dtype=outtype)) + +b = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +print('\narray:\n', b) +print('\nsquare root:\n', np.sqrt(b, dtype=np.complex)) \ No newline at end of file diff --git a/tests/1d/complex/complex_sqrt.py.exp b/tests/1d/complex/complex_sqrt.py.exp new file mode 100644 index 00000000..30459fcf --- /dev/null +++ b/tests/1d/complex/complex_sqrt.py.exp @@ -0,0 +1,42 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int8) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=uint16) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int16) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +square root: + array([0.0+0.0j, 1.0+0.0j, 1.414213562373095+0.0j, 1.732050807568877+0.0j], dtype=complex) + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +square root: + array([0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], dtype=complex) diff --git a/tests/1d/complex/imag_real.py b/tests/1d/complex/imag_real.py new file mode 100644 index 00000000..e05783b6 --- /dev/null +++ b/tests/1d/complex/imag_real.py @@ -0,0 +1,19 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(5), dtype=dtype) + print('real part: ', np.real(a)) + print('imaginary part: ', np.imag(a)) + + +b = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +print('real part: ', np.real(b)) +print('imaginary part: ', np.imag(b)) + diff --git a/tests/1d/complex/imag_real.py.exp b/tests/1d/complex/imag_real.py.exp new file mode 100644 index 00000000..977fb4ac --- /dev/null +++ b/tests/1d/complex/imag_real.py.exp @@ -0,0 +1,14 @@ +real part: array([0, 1, 2, 3, 4], dtype=uint8) +imaginary part: array([0, 0, 0, 0, 0], dtype=uint8) +real part: array([0, 1, 2, 3, 4], dtype=int8) +imaginary part: array([0, 0, 0, 0, 0], dtype=int8) +real part: array([0, 1, 2, 3, 4], dtype=uint16) +imaginary part: array([0, 0, 0, 0, 0], dtype=uint16) +real part: array([0, 1, 2, 3, 4], dtype=int16) +imaginary part: array([0, 0, 0, 0, 0], dtype=int16) +real part: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64) +imaginary part: array([0.0, 0.0, 0.0, 0.0, 0.0], dtype=float64) +real part: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64) +imaginary part: array([0.0, 0.0, 0.0, 0.0, 0.0], dtype=float64) +real part: array([0.0, 0.0, 2.0, 3.0], dtype=float64) +imaginary part: array([0.0, 1.0, 2.0, -3.0], dtype=float64) diff --git a/tests/2d/complex/complex_exp.py b/tests/2d/complex/complex_exp.py new file mode 100644 index 00000000..90b3adf7 --- /dev/null +++ b/tests/2d/complex/complex_exp.py @@ -0,0 +1,24 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + b = a.reshape((2, 2)) + print('\narray:\n', a) + print('\nexponential:\n', np.exp(a)) + print('\narray:\n', b) + print('\nexponential:\n', np.exp(b)) + +b = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +print('\narray:\n', b) +print('\nexponential:\n', np.exp(b)) + +b = np.array([[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], dtype=np.complex) +print('\narray:\n', b) +print('\nexponential:\n', np.exp(b)) \ No newline at end of file diff --git a/tests/2d/complex/complex_exp.py.exp b/tests/2d/complex/complex_exp.py.exp new file mode 100644 index 00000000..3f7c0af7 --- /dev/null +++ b/tests/2d/complex/complex_exp.py.exp @@ -0,0 +1,98 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint8) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int8) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=uint16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint16) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int16) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 2.718281828459045+0.0j, 7.38905609893065+0.0j, 20.08553692318767+0.0j], dtype=complex) + +array: + array([[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], dtype=complex) + +exponential: + array([[1.0+0.0j, 2.718281828459045+0.0j], + [7.38905609893065+0.0j, 20.08553692318767+0.0j]], dtype=complex) + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], dtype=complex) + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +exponential: + array([[1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], + [1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], dtype=complex) diff --git a/tests/2d/complex/complex_sqrt.py b/tests/2d/complex/complex_sqrt.py new file mode 100644 index 00000000..5baebb5f --- /dev/null +++ b/tests/2d/complex/complex_sqrt.py @@ -0,0 +1,25 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + b = a.reshape((2, 2)) + outtype = np.float if dtype is not np.complex else np.complex + print('\narray:\n', a) + print('\nsquare root:\n', np.sqrt(a, dtype=outtype)) + print('\narray:\n', b) + print('\nsquare root:\n', np.sqrt(b, dtype=outtype)) + +b = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +print('\narray:\n', b) +print('\nsquare root:\n', np.sqrt(b, dtype=np.complex)) + +b = np.array([[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], dtype=np.complex) +print('\narray:\n', b) +print('\nsquare root:\n', np.sqrt(b, dtype=np.complex)) diff --git a/tests/2d/complex/complex_sqrt.py.exp b/tests/2d/complex/complex_sqrt.py.exp new file mode 100644 index 00000000..30cb5e5c --- /dev/null +++ b/tests/2d/complex/complex_sqrt.py.exp @@ -0,0 +1,98 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint8) + +square root: + array([[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int8) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int8) + +square root: + array([[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=uint16) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint16) + +square root: + array([[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int16) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int16) + +square root: + array([[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877], dtype=float64) + +array: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +square root: + array([[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +square root: + array([0.0+0.0j, 1.0+0.0j, 1.414213562373095+0.0j, 1.732050807568877+0.0j], dtype=complex) + +array: + array([[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], dtype=complex) + +square root: + array([[0.0+0.0j, 1.0+0.0j], + [1.414213562373095+0.0j, 1.732050807568877+0.0j]], dtype=complex) + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +square root: + array([0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], dtype=complex) + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +square root: + array([[0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], + [0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], dtype=complex) diff --git a/tests/2d/complex/imag_real.py b/tests/2d/complex/imag_real.py new file mode 100644 index 00000000..536d7297 --- /dev/null +++ b/tests/2d/complex/imag_real.py @@ -0,0 +1,28 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + b = a.reshape((2, 2)) + print('\narray:\n', a) + print('\nreal part:\n', np.real(a)) + print('\nimaginary part:\n', np.imag(a)) + print('\narray:\n', b) + print('\nreal part:\n', np.real(b)) + print('\nimaginary part:\n', np.imag(b), '\n') + + +b = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +print('\nreal part:\n', np.real(b)) +print('\nimaginary part:\n', np.imag(b)) + +b = np.array([[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], dtype=np.complex) +print('\nreal part:\n', np.real(b)) +print('\nimaginary part:\n', np.imag(b)) + diff --git a/tests/2d/complex/imag_real.py.exp b/tests/2d/complex/imag_real.py.exp new file mode 100644 index 00000000..3df1561d --- /dev/null +++ b/tests/2d/complex/imag_real.py.exp @@ -0,0 +1,146 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +real part: + array([0, 1, 2, 3], dtype=uint8) + +imaginary part: + array([0, 0, 0, 0], dtype=uint8) + +array: + array([[0, 1], + [2, 3]], dtype=uint8) + +real part: + array([[0, 1], + [2, 3]], dtype=uint8) + +imaginary part: + array([[0, 0], + [0, 0]], dtype=uint8) + + +array: + array([0, 1, 2, 3], dtype=int8) + +real part: + array([0, 1, 2, 3], dtype=int8) + +imaginary part: + array([0, 0, 0, 0], dtype=int8) + +array: + array([[0, 1], + [2, 3]], dtype=int8) + +real part: + array([[0, 1], + [2, 3]], dtype=int8) + +imaginary part: + array([[0, 0], + [0, 0]], dtype=int8) + + +array: + array([0, 1, 2, 3], dtype=uint16) + +real part: + array([0, 1, 2, 3], dtype=uint16) + +imaginary part: + array([0, 0, 0, 0], dtype=uint16) + +array: + array([[0, 1], + [2, 3]], dtype=uint16) + +real part: + array([[0, 1], + [2, 3]], dtype=uint16) + +imaginary part: + array([[0, 0], + [0, 0]], dtype=uint16) + + +array: + array([0, 1, 2, 3], dtype=int16) + +real part: + array([0, 1, 2, 3], dtype=int16) + +imaginary part: + array([0, 0, 0, 0], dtype=int16) + +array: + array([[0, 1], + [2, 3]], dtype=int16) + +real part: + array([[0, 1], + [2, 3]], dtype=int16) + +imaginary part: + array([[0, 0], + [0, 0]], dtype=int16) + + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +real part: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +imaginary part: + array([0.0, 0.0, 0.0, 0.0], dtype=float64) + +array: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +real part: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +imaginary part: + array([[0.0, 0.0], + [0.0, 0.0]], dtype=float64) + + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +real part: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +imaginary part: + array([0.0, 0.0, 0.0, 0.0], dtype=float64) + +array: + array([[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], dtype=complex) + +real part: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +imaginary part: + array([[0.0, 0.0], + [0.0, 0.0]], dtype=float64) + + +real part: + array([0.0, 0.0, 2.0, 3.0], dtype=float64) + +imaginary part: + array([0.0, 1.0, 2.0, -3.0], dtype=float64) + +real part: + array([[0.0, 0.0, 2.0, 3.0], + [0.0, 0.0, 2.0, 3.0]], dtype=float64) + +imaginary part: + array([[0.0, 1.0, 2.0, -3.0], + [0.0, 1.0, 2.0, -3.0]], dtype=float64) diff --git a/tests/3d/complex/complex_exp.py b/tests/3d/complex/complex_exp.py new file mode 100644 index 00000000..ef36e226 --- /dev/null +++ b/tests/3d/complex/complex_exp.py @@ -0,0 +1,24 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + b = a.reshape((2, 2)) + print('\narray:\n', a) + print('\nexponential:\n', np.exp(a)) + print('\narray:\n', b) + print('\nexponential:\n', np.exp(b)) + +a = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +b = np.array([[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], dtype=np.complex) +c = np.array([[[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], [[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]]], dtype=np.complex) + +for m in (a, b, c): + print('\n\narray:\n', m) + print('\nexponential:\n', np.exp(m)) diff --git a/tests/3d/complex/complex_exp.py.exp b/tests/3d/complex/complex_exp.py.exp new file mode 100644 index 00000000..0ebc9c87 --- /dev/null +++ b/tests/3d/complex/complex_exp.py.exp @@ -0,0 +1,115 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint8) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int8) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=uint16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint16) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int16) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 2.718281828459045+0.0j, 7.38905609893065+0.0j, 20.08553692318767+0.0j], dtype=complex) + +array: + array([[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], dtype=complex) + +exponential: + array([[1.0+0.0j, 2.718281828459045+0.0j], + [7.38905609893065+0.0j, 20.08553692318767+0.0j]], dtype=complex) + + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], dtype=complex) + + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +exponential: + array([[1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], + [1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], dtype=complex) + + +array: + array([[[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]]], dtype=complex) + +exponential: + array([[[1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], + [1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], + + [[1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], + [1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]]], dtype=complex) diff --git a/tests/3d/complex/complex_sqrt.py b/tests/3d/complex/complex_sqrt.py new file mode 100644 index 00000000..4bc9def0 --- /dev/null +++ b/tests/3d/complex/complex_sqrt.py @@ -0,0 +1,26 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(8), dtype=dtype) + b = a.reshape((2, 2, 2)) + outtype = np.float if dtype is not np.complex else np.complex + print('\narray:\n', a) + print('\nsquare root:\n', np.sqrt(a, dtype=outtype)) + print('\narray:\n', b) + print('\nsquare root:\n', np.sqrt(b, dtype=outtype)) + + +a = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +b = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 4)) +c = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 2, 2)) + +for m in (a, b, c): + print('\n\narray:\n', m) + print('\nsquare root:\n', np.sqrt(m, dtype=np.complex)) diff --git a/tests/3d/complex/complex_sqrt.py.exp b/tests/3d/complex/complex_sqrt.py.exp new file mode 100644 index 00000000..1744cc7e --- /dev/null +++ b/tests/3d/complex/complex_sqrt.py.exp @@ -0,0 +1,151 @@ + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877, 2.0, 2.23606797749979, 2.449489742783178, 2.645751311064591], dtype=float64) + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=uint8) + +square root: + array([[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], dtype=float64) + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int8) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877, 2.0, 2.23606797749979, 2.449489742783178, 2.645751311064591], dtype=float64) + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=int8) + +square root: + array([[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], dtype=float64) + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint16) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877, 2.0, 2.23606797749979, 2.449489742783178, 2.645751311064591], dtype=float64) + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=uint16) + +square root: + array([[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], dtype=float64) + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int16) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877, 2.0, 2.23606797749979, 2.449489742783178, 2.645751311064591], dtype=float64) + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=int16) + +square root: + array([[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], dtype=float64) + +square root: + array([0.0, 1.0, 1.414213562373095, 1.732050807568877, 2.0, 2.23606797749979, 2.449489742783178, 2.645751311064591], dtype=float64) + +array: + array([[[0.0, 1.0], + [2.0, 3.0]], + + [[4.0, 5.0], + [6.0, 7.0]]], dtype=float64) + +square root: + array([[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j, 6.0+0.0j, 7.0+0.0j], dtype=complex) + +square root: + array([0.0+0.0j, 1.0+0.0j, 1.414213562373095+0.0j, 1.732050807568877+0.0j, 2.0+0.0j, 2.23606797749979+0.0j, 2.449489742783178+0.0j, 2.645751311064591+0.0j], dtype=complex) + +array: + array([[[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], + + [[4.0+0.0j, 5.0+0.0j], + [6.0+0.0j, 7.0+0.0j]]], dtype=complex) + +square root: + array([[[0.0+0.0j, 1.0+0.0j], + [1.414213562373095+0.0j, 1.732050807568877+0.0j]], + + [[2.0+0.0j, 2.23606797749979+0.0j], + [2.449489742783178+0.0j, 2.645751311064591+0.0j]]], dtype=complex) + + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +square root: + array([0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], dtype=complex) + + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +square root: + array([[0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], + [0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], dtype=complex) + + +array: + array([[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], dtype=complex) + +square root: + array([[[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], + + [[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]]], dtype=complex) diff --git a/tests/3d/complex/imag_real.py b/tests/3d/complex/imag_real.py new file mode 100644 index 00000000..1e12a8df --- /dev/null +++ b/tests/3d/complex/imag_real.py @@ -0,0 +1,28 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(8), dtype=dtype) + print('\narray:\n', a) + print('\nreal part:\n', np.real(a)) + print('\nimaginary part:\n', np.imag(a)) + for m in (a.reshape((2, 4)), a.reshape((2, 2, 2))): + print('\narray:\n', m) + print('\nreal part:\n', np.real(m)) + print('\nimaginary part:\n', np.imag(m), '\n') + + +a = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +b = np.array([[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], dtype=np.complex) +c = np.array([[[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]], [[0, 1j, 2+2j, 3-3j], [0, 1j, 2+2j, 3-3j]]], dtype=np.complex) + +for m in (a, b, c): + print('\n\narray:\n', m) + print('\nreal part:\n', np.real(m)) + print('\nimaginary part:\n', np.imag(m)) diff --git a/tests/3d/complex/imag_real.py.exp b/tests/3d/complex/imag_real.py.exp new file mode 100644 index 00000000..3eaf9d0f --- /dev/null +++ b/tests/3d/complex/imag_real.py.exp @@ -0,0 +1,309 @@ + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8) + +real part: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8) + +imaginary part: + array([0, 0, 0, 0, 0, 0, 0, 0], dtype=uint8) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=uint8) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=uint8) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0]], dtype=uint8) + + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=uint8) + +real part: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=uint8) + +imaginary part: + array([[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], dtype=uint8) + + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int8) + +real part: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int8) + +imaginary part: + array([0, 0, 0, 0, 0, 0, 0, 0], dtype=int8) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=int8) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=int8) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=int8) + +real part: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=int8) + +imaginary part: + array([[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], dtype=int8) + + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint16) + +real part: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint16) + +imaginary part: + array([0, 0, 0, 0, 0, 0, 0, 0], dtype=uint16) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=uint16) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=uint16) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0]], dtype=uint16) + + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=uint16) + +real part: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=uint16) + +imaginary part: + array([[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], dtype=uint16) + + +array: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int16) + +real part: + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int16) + +imaginary part: + array([0, 0, 0, 0, 0, 0, 0, 0], dtype=int16) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=int16) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7]], dtype=int16) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int16) + + +array: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=int16) + +real part: + array([[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], dtype=int16) + +imaginary part: + array([[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], dtype=int16) + + +array: + array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], dtype=float64) + +real part: + array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], dtype=float64) + +imaginary part: + array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], dtype=float64) + +array: + array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0]], dtype=float64) + +real part: + array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0]], dtype=float64) + +imaginary part: + array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) + + +array: + array([[[0.0, 1.0], + [2.0, 3.0]], + + [[4.0, 5.0], + [6.0, 7.0]]], dtype=float64) + +real part: + array([[[0.0, 1.0], + [2.0, 3.0]], + + [[4.0, 5.0], + [6.0, 7.0]]], dtype=float64) + +imaginary part: + array([[[0.0, 0.0], + [0.0, 0.0]], + + [[0.0, 0.0], + [0.0, 0.0]]], dtype=float64) + + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j, 6.0+0.0j, 7.0+0.0j], dtype=complex) + +real part: + array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], dtype=float64) + +imaginary part: + array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], dtype=float64) + +array: + array([[0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], + [4.0+0.0j, 5.0+0.0j, 6.0+0.0j, 7.0+0.0j]], dtype=complex) + +real part: + array([[0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0]], dtype=float64) + +imaginary part: + array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) + + +array: + array([[[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], + + [[4.0+0.0j, 5.0+0.0j], + [6.0+0.0j, 7.0+0.0j]]], dtype=complex) + +real part: + array([[[0.0, 1.0], + [2.0, 3.0]], + + [[4.0, 5.0], + [6.0, 7.0]]], dtype=float64) + +imaginary part: + array([[[0.0, 0.0], + [0.0, 0.0]], + + [[0.0, 0.0], + [0.0, 0.0]]], dtype=float64) + + + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +real part: + array([0.0, 0.0, 2.0, 3.0], dtype=float64) + +imaginary part: + array([0.0, 1.0, 2.0, -3.0], dtype=float64) + + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +real part: + array([[0.0, 0.0, 2.0, 3.0], + [0.0, 0.0, 2.0, 3.0]], dtype=float64) + +imaginary part: + array([[0.0, 1.0, 2.0, -3.0], + [0.0, 1.0, 2.0, -3.0]], dtype=float64) + + +array: + array([[[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]]], dtype=complex) + +real part: + array([[[0.0, 0.0, 2.0, 3.0], + [0.0, 0.0, 2.0, 3.0]], + + [[0.0, 0.0, 2.0, 3.0], + [0.0, 0.0, 2.0, 3.0]]], dtype=float64) + +imaginary part: + array([[[0.0, 1.0, 2.0, -3.0], + [0.0, 1.0, 2.0, -3.0]], + + [[0.0, 1.0, 2.0, -3.0], + [0.0, 1.0, 2.0, -3.0]]], dtype=float64) diff --git a/tests/4d/complex/complex_exp.py b/tests/4d/complex/complex_exp.py new file mode 100644 index 00000000..63ed8732 --- /dev/null +++ b/tests/4d/complex/complex_exp.py @@ -0,0 +1,26 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(4), dtype=dtype) + b = a.reshape((2, 2)) + print('\narray:\n', a) + print('\nexponential:\n', np.exp(a)) + print('\narray:\n', b) + print('\nexponential:\n', np.exp(b)) + + +a = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +b = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 4)) +c = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 2, 2)) +d = np.array([0, 1j, 2+2j, 3-3j] * 4, dtype=np.complex).reshape((2, 2, 2, 2)) + +for m in (a, b, c, d): + print('\n\narray:\n', m) + print('\nexponential:\n', np.exp(m)) diff --git a/tests/4d/complex/complex_exp.py.exp b/tests/4d/complex/complex_exp.py.exp new file mode 100644 index 00000000..ebf135e5 --- /dev/null +++ b/tests/4d/complex/complex_exp.py.exp @@ -0,0 +1,142 @@ + +array: + array([0, 1, 2, 3], dtype=uint8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint8) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int8) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int8) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=uint16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=uint16) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0, 1, 2, 3], dtype=int16) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0, 1], + [2, 3]], dtype=int16) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0.0, 1.0, 2.0, 3.0], dtype=float64) + +exponential: + array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + +array: + array([[0.0, 1.0], + [2.0, 3.0]], dtype=float64) + +exponential: + array([[1.0, 2.718281828459045], + [7.38905609893065, 20.08553692318767]], dtype=float64) + +array: + array([0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 2.718281828459045+0.0j, 7.38905609893065+0.0j, 20.08553692318767+0.0j], dtype=complex) + +array: + array([[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], dtype=complex) + +exponential: + array([[1.0+0.0j, 2.718281828459045+0.0j], + [7.38905609893065+0.0j, 20.08553692318767+0.0j]], dtype=complex) + + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +exponential: + array([1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], dtype=complex) + + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +exponential: + array([[1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j], + [1.0+0.0j, 0.5403023058681398+0.8414709848078965j, -3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], dtype=complex) + + +array: + array([[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], dtype=complex) + +exponential: + array([[[1.0+0.0j, 0.5403023058681398+0.8414709848078965j], + [-3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], + + [[1.0+0.0j, 0.5403023058681398+0.8414709848078965j], + [-3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]]], dtype=complex) + + +array: + array([[[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], + + [[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]]], dtype=complex) + +exponential: + array([[[[1.0+0.0j, 0.5403023058681398+0.8414709848078965j], + [-3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], + + [[1.0+0.0j, 0.5403023058681398+0.8414709848078965j], + [-3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]]], + + [[[1.0+0.0j, 0.5403023058681398+0.8414709848078965j], + [-3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]], + + [[1.0+0.0j, 0.5403023058681398+0.8414709848078965j], + [-3.074932320639359+6.71884969742825j, -19.88453084414699-2.834471132487004j]]]], dtype=complex) diff --git a/tests/4d/complex/complex_sqrt.py b/tests/4d/complex/complex_sqrt.py new file mode 100644 index 00000000..052a07d7 --- /dev/null +++ b/tests/4d/complex/complex_sqrt.py @@ -0,0 +1,27 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(16), dtype=dtype) + b = a.reshape((2, 2, 2, 2)) + outtype = np.float if dtype is not np.complex else np.complex + print('\narray:\n', a) + print('\nsquare root:\n', np.sqrt(a, dtype=outtype)) + print('\narray:\n', b) + print('\nsquare root:\n', np.sqrt(b, dtype=outtype)) + + +a = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +b = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 4)) +c = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 2, 2)) +d = np.array([0, 1j, 2+2j, 3-3j] * 4, dtype=np.complex).reshape((2, 2, 2, 2)) + +for m in (a, b, c, d): + print('\n\narray:\n', m) + print('\nsquare root:\n', np.sqrt(m, dtype=np.complex)) diff --git a/tests/4d/complex/complex_sqrt.py.exp b/tests/4d/complex/complex_sqrt.py.exp new file mode 100644 index 00000000..44f87f05 --- /dev/null +++ b/tests/4d/complex/complex_sqrt.py.exp @@ -0,0 +1,250 @@ + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=uint8) + +square root: + array([0.0, 1.0, 1.414213562373095, ..., 3.605551275463989, 3.741657386773941, 3.872983346207417], dtype=float64) + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=uint8) + +square root: + array([[[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], + + [[[2.82842712474619, 3.0], + [3.16227766016838, 3.3166247903554]], + + [[3.464101615137754, 3.605551275463989], + [3.741657386773941, 3.872983346207417]]]], dtype=float64) + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=int8) + +square root: + array([0.0, 1.0, 1.414213562373095, ..., 3.605551275463989, 3.741657386773941, 3.872983346207417], dtype=float64) + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=int8) + +square root: + array([[[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], + + [[[2.82842712474619, 3.0], + [3.16227766016838, 3.3166247903554]], + + [[3.464101615137754, 3.605551275463989], + [3.741657386773941, 3.872983346207417]]]], dtype=float64) + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=uint16) + +square root: + array([0.0, 1.0, 1.414213562373095, ..., 3.605551275463989, 3.741657386773941, 3.872983346207417], dtype=float64) + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=uint16) + +square root: + array([[[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], + + [[[2.82842712474619, 3.0], + [3.16227766016838, 3.3166247903554]], + + [[3.464101615137754, 3.605551275463989], + [3.741657386773941, 3.872983346207417]]]], dtype=float64) + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=int16) + +square root: + array([0.0, 1.0, 1.414213562373095, ..., 3.605551275463989, 3.741657386773941, 3.872983346207417], dtype=float64) + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=int16) + +square root: + array([[[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], + + [[[2.82842712474619, 3.0], + [3.16227766016838, 3.3166247903554]], + + [[3.464101615137754, 3.605551275463989], + [3.741657386773941, 3.872983346207417]]]], dtype=float64) + +array: + array([0.0, 1.0, 2.0, ..., 13.0, 14.0, 15.0], dtype=float64) + +square root: + array([0.0, 1.0, 1.414213562373095, ..., 3.605551275463989, 3.741657386773941, 3.872983346207417], dtype=float64) + +array: + 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) + +square root: + array([[[[0.0, 1.0], + [1.414213562373095, 1.732050807568877]], + + [[2.0, 2.23606797749979], + [2.449489742783178, 2.645751311064591]]], + + [[[2.82842712474619, 3.0], + [3.16227766016838, 3.3166247903554]], + + [[3.464101615137754, 3.605551275463989], + [3.741657386773941, 3.872983346207417]]]], dtype=float64) + +array: + array([0j, 1.0+0.0j, 2.0+0.0j, ..., 13.0+0.0j, 14.0+0.0j, 15.0+0.0j], dtype=complex) + +square root: + array([0j, 1.0+0.0j, 1.414213562373095+0.0j, ..., 3.605551275463989+0.0j, 3.741657386773941+0.0j, 3.872983346207417+0.0j], dtype=complex) + +array: + array([[[[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], + + [[4.0+0.0j, 5.0+0.0j], + [6.0+0.0j, 7.0+0.0j]]], + + [[[8.0+0.0j, 9.0+0.0j], + [10.0+0.0j, 11.0+0.0j]], + + [[12.0+0.0j, 13.0+0.0j], + [14.0+0.0j, 15.0+0.0j]]]], dtype=complex) + +square root: + array([[[[0.0+0.0j, 1.0+0.0j], + [1.414213562373095+0.0j, 1.732050807568877+0.0j]], + + [[2.0+0.0j, 2.23606797749979+0.0j], + [2.449489742783178+0.0j, 2.645751311064591+0.0j]]], + + [[[2.82842712474619+0.0j, 3.0+0.0j], + [3.16227766016838+0.0j, 3.3166247903554+0.0j]], + + [[3.464101615137754+0.0j, 3.605551275463989+0.0j], + [3.741657386773941+0.0j, 3.872983346207417+0.0j]]]], dtype=complex) + + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +square root: + array([0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], dtype=complex) + + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +square root: + array([[0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j], + [0.0+0.0j, 0.7071067811865476+0.7071067811865475j, 1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], dtype=complex) + + +array: + array([[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], dtype=complex) + +square root: + array([[[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], + + [[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]]], dtype=complex) + + +array: + array([[[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], + + [[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]]], dtype=complex) + +square root: + array([[[[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], + + [[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]]], + + [[[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]], + + [[0.0+0.0j, 0.7071067811865476+0.7071067811865475j], + [1.553773974030037+0.6435942529055827j, 1.902976705995016-0.7882387605032136j]]]], dtype=complex) diff --git a/tests/4d/complex/imag_real.py b/tests/4d/complex/imag_real.py new file mode 100644 index 00000000..63b9da5e --- /dev/null +++ b/tests/4d/complex/imag_real.py @@ -0,0 +1,29 @@ +# this test is meaningful only, when the firmware supports complex arrays + +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + a = np.array(range(16), dtype=dtype) + print('\narray:\n', a) + print('\nreal part:\n', np.real(a)) + print('\nimaginary part:\n', np.imag(a)) + for m in (a.reshape((4, 4)), a.reshape((2, 2, 4)), a.reshape((2, 2, 2, 2))): + print('\narray:\n', m) + print('\nreal part:\n', np.real(m)) + print('\nimaginary part:\n', np.imag(m), '\n') + + +a = np.array([0, 1j, 2+2j, 3-3j], dtype=np.complex) +b = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 4)) +c = np.array([0, 1j, 2+2j, 3-3j] * 2, dtype=np.complex).reshape((2, 2, 2)) +d = np.array([0, 1j, 2+2j, 3-3j] * 4, dtype=np.complex).reshape((2, 2, 2, 2)) + +for m in (a, b, c, d): + print('\n\narray:\n', m) + print('\nreal part:\n', np.real(m)) + print('\nimaginary part:\n', np.imag(m)) \ No newline at end of file diff --git a/tests/4d/complex/imag_real.py.exp b/tests/4d/complex/imag_real.py.exp new file mode 100644 index 00000000..95c9ab2f --- /dev/null +++ b/tests/4d/complex/imag_real.py.exp @@ -0,0 +1,625 @@ + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=uint8) + +real part: + array([0, 1, 2, ..., 13, 14, 15], dtype=uint8) + +imaginary part: + array([0, 0, 0, ..., 0, 0, 0], dtype=uint8) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=uint8) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=uint8) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=uint8) + + +array: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=uint8) + +real part: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=uint8) + +imaginary part: + array([[[0, 0, 0, 0], + [0, 0, 0, 0]], + + [[0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=uint8) + + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=uint8) + +real part: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=uint8) + +imaginary part: + array([[[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], + + [[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]]], dtype=uint8) + + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=int8) + +real part: + array([0, 1, 2, ..., 13, 14, 15], dtype=int8) + +imaginary part: + array([0, 0, 0, ..., 0, 0, 0], dtype=int8) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=int8) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=int8) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + +array: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=int8) + +real part: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=int8) + +imaginary part: + array([[[0, 0, 0, 0], + [0, 0, 0, 0]], + + [[0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=int8) + + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=int8) + +real part: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=int8) + +imaginary part: + array([[[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], + + [[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]]], dtype=int8) + + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=uint16) + +real part: + array([0, 1, 2, ..., 13, 14, 15], dtype=uint16) + +imaginary part: + array([0, 0, 0, ..., 0, 0, 0], dtype=uint16) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=uint16) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=uint16) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=uint16) + + +array: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=uint16) + +real part: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=uint16) + +imaginary part: + array([[[0, 0, 0, 0], + [0, 0, 0, 0]], + + [[0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=uint16) + + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=uint16) + +real part: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=uint16) + +imaginary part: + array([[[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], + + [[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]]], dtype=uint16) + + +array: + array([0, 1, 2, ..., 13, 14, 15], dtype=int16) + +real part: + array([0, 1, 2, ..., 13, 14, 15], dtype=int16) + +imaginary part: + array([0, 0, 0, ..., 0, 0, 0], dtype=int16) + +array: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=int16) + +real part: + array([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], dtype=int16) + +imaginary part: + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int16) + + +array: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=int16) + +real part: + array([[[0, 1, 2, 3], + [4, 5, 6, 7]], + + [[8, 9, 10, 11], + [12, 13, 14, 15]]], dtype=int16) + +imaginary part: + array([[[0, 0, 0, 0], + [0, 0, 0, 0]], + + [[0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=int16) + + +array: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=int16) + +real part: + array([[[[0, 1], + [2, 3]], + + [[4, 5], + [6, 7]]], + + [[[8, 9], + [10, 11]], + + [[12, 13], + [14, 15]]]], dtype=int16) + +imaginary part: + array([[[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]], + + [[[0, 0], + [0, 0]], + + [[0, 0], + [0, 0]]]], dtype=int16) + + +array: + array([0.0, 1.0, 2.0, ..., 13.0, 14.0, 15.0], dtype=float64) + +real part: + array([0.0, 1.0, 2.0, ..., 13.0, 14.0, 15.0], dtype=float64) + +imaginary part: + array([0.0, 0.0, 0.0, ..., 0.0, 0.0, 0.0], dtype=float64) + +array: + 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) + +real part: + 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) + +imaginary part: + array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) + + +array: + 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) + +real part: + 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) + +imaginary part: + array([[[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], + + [[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]]], dtype=float64) + + +array: + 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) + +real part: + 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) + +imaginary part: + array([[[[0.0, 0.0], + [0.0, 0.0]], + + [[0.0, 0.0], + [0.0, 0.0]]], + + [[[0.0, 0.0], + [0.0, 0.0]], + + [[0.0, 0.0], + [0.0, 0.0]]]], dtype=float64) + + +array: + array([0j, 1.0+0.0j, 2.0+0.0j, ..., 13.0+0.0j, 14.0+0.0j, 15.0+0.0j], dtype=complex) + +real part: + array([0.0, 1.0, 2.0, ..., 13.0, 14.0, 15.0], dtype=float64) + +imaginary part: + array([0.0, 0.0, 0.0, ..., 0.0, 0.0, 0.0], dtype=float64) + +array: + array([[0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], + [4.0+0.0j, 5.0+0.0j, 6.0+0.0j, 7.0+0.0j], + [8.0+0.0j, 9.0+0.0j, 10.0+0.0j, 11.0+0.0j], + [12.0+0.0j, 13.0+0.0j, 14.0+0.0j, 15.0+0.0j]], dtype=complex) + +real part: + 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) + +imaginary part: + array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) + + +array: + array([[[0.0+0.0j, 1.0+0.0j, 2.0+0.0j, 3.0+0.0j], + [4.0+0.0j, 5.0+0.0j, 6.0+0.0j, 7.0+0.0j]], + + [[8.0+0.0j, 9.0+0.0j, 10.0+0.0j, 11.0+0.0j], + [12.0+0.0j, 13.0+0.0j, 14.0+0.0j, 15.0+0.0j]]], dtype=complex) + +real part: + 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) + +imaginary part: + array([[[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], + + [[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]]], dtype=float64) + + +array: + array([[[[0.0+0.0j, 1.0+0.0j], + [2.0+0.0j, 3.0+0.0j]], + + [[4.0+0.0j, 5.0+0.0j], + [6.0+0.0j, 7.0+0.0j]]], + + [[[8.0+0.0j, 9.0+0.0j], + [10.0+0.0j, 11.0+0.0j]], + + [[12.0+0.0j, 13.0+0.0j], + [14.0+0.0j, 15.0+0.0j]]]], dtype=complex) + +real part: + 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) + +imaginary part: + array([[[[0.0, 0.0], + [0.0, 0.0]], + + [[0.0, 0.0], + [0.0, 0.0]]], + + [[[0.0, 0.0], + [0.0, 0.0]], + + [[0.0, 0.0], + [0.0, 0.0]]]], dtype=float64) + + + +array: + array([0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], dtype=complex) + +real part: + array([0.0, 0.0, 2.0, 3.0], dtype=float64) + +imaginary part: + array([0.0, 1.0, 2.0, -3.0], dtype=float64) + + +array: + array([[0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j], + [0.0+0.0j, 0.0+1.0j, 2.0+2.0j, 3.0-3.0j]], dtype=complex) + +real part: + array([[0.0, 0.0, 2.0, 3.0], + [0.0, 0.0, 2.0, 3.0]], dtype=float64) + +imaginary part: + array([[0.0, 1.0, 2.0, -3.0], + [0.0, 1.0, 2.0, -3.0]], dtype=float64) + + +array: + array([[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], dtype=complex) + +real part: + array([[[0.0, 0.0], + [2.0, 3.0]], + + [[0.0, 0.0], + [2.0, 3.0]]], dtype=float64) + +imaginary part: + array([[[0.0, 1.0], + [2.0, -3.0]], + + [[0.0, 1.0], + [2.0, -3.0]]], dtype=float64) + + +array: + array([[[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]], + + [[[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]], + + [[0.0+0.0j, 0.0+1.0j], + [2.0+2.0j, 3.0-3.0j]]]], dtype=complex) + +real part: + array([[[[0.0, 0.0], + [2.0, 3.0]], + + [[0.0, 0.0], + [2.0, 3.0]]], + + [[[0.0, 0.0], + [2.0, 3.0]], + + [[0.0, 0.0], + [2.0, 3.0]]]], dtype=float64) + +imaginary part: + array([[[[0.0, 1.0], + [2.0, -3.0]], + + [[0.0, 1.0], + [2.0, -3.0]]], + + [[[0.0, 1.0], + [2.0, -3.0]], + + [[0.0, 1.0], + [2.0, -3.0]]]], dtype=float64) From 94d6b13f8d02a4337f4d452b8841fd6b82140272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 31 Dec 2021 23:11:59 +0100 Subject: [PATCH 43/58] make fft optionally numpy-compatible, when complex is supported --- code/numpy/fft/fft.c | 16 +++++ code/numpy/fft/fft.h | 6 ++ code/numpy/fft/fft_tools.c | 133 +++++++++++++++++++++++++++++++++++-- code/numpy/fft/fft_tools.h | 5 ++ code/scipy/signal/signal.c | 9 +++ code/ulab.h | 9 +++ 6 files changed, 171 insertions(+), 7 deletions(-) diff --git a/code/numpy/fft/fft.c b/code/numpy/fft/fft.c index 3b3f66a5..8b73bddd 100644 --- a/code/numpy/fft/fft.c +++ b/code/numpy/fft/fft.c @@ -40,6 +40,13 @@ //| rather than separately returning its real and imaginary parts.""" //| ... //| +#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); +} + +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); @@ -49,6 +56,7 @@ static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft); +#endif //| def ifft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]: //| """ @@ -60,6 +68,13 @@ 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); +} + +MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft); +#else static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) { NOT_IMPLEMENTED_FOR_COMPLEX() if(n_args == 2) { @@ -70,6 +85,7 @@ 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[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fft) }, diff --git a/code/numpy/fft/fft.h b/code/numpy/fft/fft.h index 66acafe1..7a166984 100644 --- a/code/numpy/fft/fft.h +++ b/code/numpy/fft/fft.h @@ -19,6 +19,12 @@ extern 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); +MP_DECLARE_CONST_FUN_OBJ_3(fft_ifft_obj); +#else MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj); #endif + +#endif diff --git a/code/numpy/fft/fft_tools.c b/code/numpy/fft/fft_tools.c index a08f2244..8a55927e 100644 --- a/code/numpy/fft/fft_tools.c +++ b/code/numpy/fft/fft_tools.c @@ -9,6 +9,7 @@ */ #include +#include #include "py/runtime.h" #include "../../ndarray.h" @@ -23,7 +24,8 @@ #define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536) #endif -/* +/* Kernel implementation for the case, when ulab has no complex support + * The following function takes two arrays, namely, the real and imaginary * parts of a complex array, and calculates the Fourier transform in place. * @@ -32,6 +34,128 @@ * and can be used independent of ulab. */ +#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE +/* Kernel implementation for the complex case. Data are contained in data as + + data[0], data[1], data[2], data[3], .... , data[2n - 2], data[2n-1] + real[0], imag[0], real[1], imag[1], .... , real[n-1], imag[n-1] + + In general + real[i] = data[2i] + imag[i] = data[2i+1] + +*/ +void fft_kernel_complex(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; + + j = 0; + for(size_t i = 0; i < n; i++) { + if (j > i) { + SWAP(mp_float_t, data[2*i], data[2*j]); + SWAP(mp_float_t, data[2*i+1], data[2*j+1]); + } + m = n >> 1; + while (j >= m && m > 0) { + j -= m; + m >>= 1; + } + j += m; + } + + mmax = 1; + while (n > mmax) { + istep = mmax << 1; + theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep; + wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta); + wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp; + wpi = MICROPY_FLOAT_C_FUN(sin)(theta); + wr = MICROPY_FLOAT_CONST(1.0); + wi = MICROPY_FLOAT_CONST(0.0); + for(m = 0; m < mmax; m++) { + for(size_t i = m; i < n; i += istep) { + j = i + mmax; + tempr = wr * data[2*j] - wi * data[2*j+1]; + tempi = wr * data[2*j+1] + wi * data[2*j]; + data[2*j] = data[2*i] - tempr; + data[2*j+1] = data[2*i+1] - tempi; + data[2*i] += tempr; + data[2*i+1] += tempi; + } + wtemp = wr; + wr = wr*wpr - wi*wpi + wr; + wi = wi*wpr + wtemp*wpi + wi; + } + mmax = istep; + } +} + +/* + * 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. + */ +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")); + } + 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")); + } + #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")); + } + + ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_COMPLEX); + mp_float_t *data = (mp_float_t *)out->array; + uint8_t *array = (uint8_t *)in->array; + + 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); + 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++) { + // real part; the imaginary part is 0, no need to assign + *data = func(array); + data += 2; + array += in->strides[ULAB_MAX_DIMS - 1]; + } + } + 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); + } + } else { // inverse transform + fft_kernel_complex(data, len, -1); + // TODO: numpy accepts the norm keyword argument + for(size_t i = 0; i < len; i++) { + *data++ /= len; + } + } + return MP_OBJ_FROM_PTR(out); +} +#else /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */ void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) { size_t j, m, mmax, istep; mp_float_t tempr, tempi; @@ -78,12 +202,6 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, 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. - */ - 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")); @@ -166,3 +284,4 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i 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 d3b856d0..9444232f 100644 --- a/code/numpy/fft/fft_tools.h +++ b/code/numpy/fft/fft_tools.h @@ -17,7 +17,12 @@ enum FFT_TYPE { 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 ); +#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 ); +#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */ #endif /* _FFT_TOOLS_ */ diff --git a/code/scipy/signal/signal.c b/code/scipy/signal/signal.c index 7ab471a3..7a0963ac 100644 --- a/code/scipy/signal/signal.c +++ b/code/scipy/signal/signal.c @@ -34,14 +34,23 @@ //| mp_obj_t signal_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); + #else if(n_args == 2) { return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_SPECTROGRAM); } else { return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_SPECTROGRAM); } + #endif } +#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj, 1, 1, signal_spectrogram); +#else MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj, 1, 2, signal_spectrogram); +#endif + #endif /* ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM */ #if ULAB_SCIPY_SIGNAL_HAS_SOSFILT diff --git a/code/ulab.h b/code/ulab.h index c0a5b45f..d99f5645 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -389,6 +389,15 @@ #define ULAB_NUMPY_HAS_FFT_MODULE (1) #endif +// by setting this constant to 1, the FFT routine will behave in a +// numpy-compatible way, i.e., it will output a complex array +// this setting has no effect, if ULAB_SUPPORTS_COMPLEX is 0 +// 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) +#endif + #ifndef ULAB_FFT_HAS_FFT #define ULAB_FFT_HAS_FFT (1) #endif From 7e13fab9115213a22a261e6a5f6cfaae1b3f2f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 31 Dec 2021 23:39:58 +0100 Subject: [PATCH 44/58] support ones, zeros, and full for complex dtype --- code/ndarray.c | 18 ++++++++++++++++++ code/ndarray.h | 2 ++ code/numpy/create.c | 8 ++++++++ 3 files changed, 28 insertions(+) diff --git a/code/ndarray.c b/code/ndarray.c index 146a91b0..05ebdddd 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -47,6 +47,19 @@ mp_uint_t ndarray_print_edgeitems = NDARRAY_PRINT_EDGEITEMS; //| https://docs.scipy.org/doc/numpy/index.html""" //| +void ndarray_set_complex_value(void *p, size_t index, mp_obj_t value) { + mp_float_t real, imag; + if(mp_obj_is_type(value, &mp_type_complex)) { + mp_obj_get_complex(value, &real, &imag); + ((mp_float_t *)p)[2 * index] = real; + ((mp_float_t *)p)[2 * index + 1] = imag; + } else { + real = mp_obj_get_float(value); + ((mp_float_t *)p)[2 * index] = real; + ((mp_float_t *)p)[2 * index + 1] = MICROPY_FLOAT_CONST(0.0); + } +} + #ifdef CIRCUITPY void ndarray_set_value(char typecode, void *p, size_t index, mp_obj_t val_in) { switch (typecode) { @@ -65,6 +78,11 @@ void ndarray_set_value(char typecode, void *p, size_t index, mp_obj_t val_in) { 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 diff --git a/code/ndarray.h b/code/ndarray.h index 0b4be867..a2315754 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -63,6 +63,8 @@ typedef struct _mp_obj_slice_t { void ndarray_set_value(char , void *, size_t , mp_obj_t ); #endif +void ndarray_set_complex_value(void *, size_t , mp_obj_t ); + #define NDARRAY_NUMERIC 0 #define NDARRAY_BOOLEAN 1 diff --git a/code/numpy/create.c b/code/numpy/create.c index 32519bc4..33d9236a 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -56,7 +56,15 @@ static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t } } for(size_t i=0; i < ndarray->len; i++) { + #if ULAB_SUPPORTS_COMPLEX + if(dtype == NDARRAY_COMPLEX) { + ndarray_set_complex_value(ndarray->array, i, value); + } else { + ndarray_set_value(dtype, ndarray->array, i, value); + } + #else ndarray_set_value(dtype, ndarray->array, i, value); + #endif } } // if zeros calls the function, we don't have to do anything From 4855baa8cc91a19e12b1f8a490c4b942e9d139c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 1 Jan 2022 00:33:28 +0100 Subject: [PATCH 45/58] add conjugate function --- code/numpy/carray/carray.c | 34 ++++++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 1 + code/numpy/numpy.c | 3 +++ code/ulab.h | 5 +++++ 4 files changed, 43 insertions(+) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index b5bcec3b..86389d27 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -64,6 +64,40 @@ STATIC mp_obj_t carray_imag(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); +#if ULAB_NUMPY_HAS_CONJUGATE +mp_obj_t carray_conjugate(mp_obj_t _source) { + if(mp_obj_is_type(_source, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype); + ndarray_copy_array(source, ndarray, 0); + if(source->dtype == NDARRAY_COMPLEX) { + mp_float_t *array = (mp_float_t *)ndarray->array; + array++; + for(size_t i = 0; i < ndarray->len; i++) { + *array *= MICROPY_FLOAT_CONST(-1.0); + array += 2; + } + } + return MP_OBJ_FROM_PTR(ndarray); + } else { + if(mp_obj_is_type(_source, &mp_type_complex)) { + mp_float_t real, imag; + mp_obj_get_complex(_source, &real, &imag); + imag = imag * MICROPY_FLOAT_CONST(-1.0); + return mp_obj_new_complex(real, imag); + } 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")); + } + } + // this should never happen + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate); +#endif + mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { // calculates the absolute value of a complex array and returns a dense array uint8_t *sarray = (uint8_t *)source->array; diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index df504d3b..15d170a5 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -14,6 +14,7 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); +MP_DECLARE_CONST_FUN_OBJ_1(carray_conjugate_obj); mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 86f73eb8..6a54f40c 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -146,6 +146,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_ARANGE { MP_ROM_QSTR(MP_QSTR_arange), (mp_obj_t)&create_arange_obj }, #endif + #if ULAB_NUMPY_HAS_CONJUGATE + { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj }, + #endif #if ULAB_NUMPY_HAS_CONCATENATE { MP_ROM_QSTR(MP_QSTR_concatenate), (mp_obj_t)&create_concatenate_obj }, #endif diff --git a/code/ulab.h b/code/ulab.h index d99f5645..34c3bcd7 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -422,6 +422,11 @@ #define ULAB_NUMPY_HAS_ARGSORT (1) #endif +// conjugate is defined only, if ULAB_SUPPORTS_COMPLEX is set to 1 +#ifndef ULAB_NUMPY_HAS_CONJUGATE +#define ULAB_NUMPY_HAS_CONJUGATE (1) +#endif + #ifndef ULAB_NUMPY_HAS_CONVOLVE #define ULAB_NUMPY_HAS_CONVOLVE (1) #endif From c11dac322dd27fbfeef647bfabc7df7d8f796823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 1 Jan 2022 09:20:00 +0100 Subject: [PATCH 46/58] fix array initialisation from complex array --- code/ndarray.c | 149 ++++++++++++++++++++----------------- code/ndarray.h | 1 + code/numpy/carray/carray.c | 2 +- code/numpy/carray/carray.h | 1 + 4 files changed, 83 insertions(+), 70 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index 05ebdddd..a995e46f 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -741,6 +741,85 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { return ndarray; } +ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *source, uint8_t dtype) { + // creates a copy, similar to ndarray_copy_view, but it also converts the dtype, if necessary + if(dtype == source->dtype) { + return ndarray_copy_view(source); + } + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, dtype); + uint8_t *sarray = (uint8_t *)source->array; + uint8_t *array = (uint8_t *)ndarray->array; + + #if ULAB_SUPPORTS_COMPLEX + 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 { + #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(translate("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(floor)(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]); + #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 + return MP_OBJ_FROM_PTR(ndarray); +} + #if NDARRAY_HAS_BYTESWAP mp_obj_t ndarray_byteswap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // changes the endiannes of an array @@ -952,75 +1031,7 @@ STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]); - if(dtype == source->dtype) { - return ndarray_copy_view(source); - } - ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, dtype); - uint8_t *sarray = (uint8_t *)source->array; - uint8_t *tarray = (uint8_t *)target->array; - - #if ULAB_SUPPORTS_COMPLEX - 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 { - #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(translate("cannot convert complex type")); - } else { - memcpy(tarray, 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(floor)(f)); - } else { - item = mp_binary_get_val_array(source->dtype, sarray, 0); - } - ndarray_set_value(dtype, tarray, 0, item); - #if ULAB_SUPPORTS_COMPLEX - } - #endif - 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 - return MP_OBJ_FROM_PTR(target); + return MP_OBJ_FROM_PTR(ndarray_copy_view_convert_type(source, dtype)); } else { // assume that the input is an iterable return MP_OBJ_FROM_PTR(ndarray_from_iterable(args[0], dtype)); diff --git a/code/ndarray.h b/code/ndarray.h index a2315754..3b63dd87 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -143,6 +143,7 @@ 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 *); ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *); +ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *, uint8_t ); void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *, uint8_t ); MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj); diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 86389d27..220e6e5f 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Zoltán Vörös + * Copyright (c) 2021-2022 Zoltán Vörös */ #include diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 15d170a5..393b209c 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -15,6 +15,7 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_conjugate_obj); +MP_DECLARE_CONST_FUN_OBJ_1(carray_sort_complex_obj); mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); From 2b578ae333a03b675e9b0f9b6557f7c12bd91ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 1 Jan 2022 18:16:04 +0100 Subject: [PATCH 47/58] add tolist method to ndarray --- code/ndarray.c | 26 ++++++++++++++++++++++++++ code/ndarray.h | 5 +++++ code/ulab.c | 3 +++ code/ulab.h | 4 ++++ 4 files changed, 38 insertions(+) diff --git a/code/ndarray.c b/code/ndarray.c index a995e46f..8960fb66 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1730,6 +1730,32 @@ mp_obj_t ndarray_tobytes(mp_obj_t self_in) { MP_DEFINE_CONST_FUN_OBJ_1(ndarray_tobytes_obj, ndarray_tobytes); #endif +#if NDARRAY_HAS_TOLIST +static mp_obj_t ndarray_recursive_list(ndarray_obj_t *self, uint8_t *array, uint8_t dim) { + int32_t stride = self->strides[ULAB_MAX_DIMS - dim]; + size_t len = self->shape[ULAB_MAX_DIMS - dim]; + + mp_obj_list_t *list = MP_OBJ_TO_PTR(mp_obj_new_list(len, NULL)); + for(size_t i = 0; i < len; i++) { + if(dim == 1) { + list->items[i] = ndarray_get_item(self, array); + } else { + list->items[i] = ndarray_recursive_list(self, array, dim-1); + } + array += stride; + } + return MP_OBJ_FROM_PTR(list); +} + +mp_obj_t ndarray_tolist(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t *array = (uint8_t *)self->array; + return ndarray_recursive_list(self, array, self->ndim); +} + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_tolist_obj, ndarray_tolist); +#endif + // Binary operations ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { // creates an ndarray from a micropython int or float diff --git a/code/ndarray.h b/code/ndarray.h index 3b63dd87..4478f942 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -191,6 +191,11 @@ mp_obj_t ndarray_tobytes(mp_obj_t ); MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tobytes_obj); #endif +#if NDARRAY_HAS_TOBYTES +mp_obj_t ndarray_tolist(mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tolist_obj); +#endif + #if NDARRAY_HAS_TRANSPOSE mp_obj_t ndarray_transpose(mp_obj_t ); MP_DECLARE_CONST_FUN_OBJ_1(ndarray_transpose_obj); diff --git a/code/ulab.c b/code/ulab.c index 3b825335..dd2c9947 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -67,6 +67,9 @@ STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { #if NDARRAY_HAS_TOBYTES { MP_ROM_QSTR(MP_QSTR_tobytes), MP_ROM_PTR(&ndarray_tobytes_obj) }, #endif + #if NDARRAY_HAS_TOLIST + { MP_ROM_QSTR(MP_QSTR_tolist), MP_ROM_PTR(&ndarray_tolist_obj) }, + #endif #if NDARRAY_HAS_SORT { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) }, #endif diff --git a/code/ulab.h b/code/ulab.h index 34c3bcd7..c37d4f19 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -232,6 +232,10 @@ #define NDARRAY_HAS_TOBYTES (1) #endif +#ifndef NDARRAY_HAS_TOLIST +#define NDARRAY_HAS_TOLIST (1) +#endif + #ifndef NDARRAY_HAS_TRANSPOSE #define NDARRAY_HAS_TRANSPOSE (1) #endif From cb3f4b3898b2cef43d6373843484cfa4b9b8110b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 2 Jan 2022 12:46:12 +0100 Subject: [PATCH 48/58] add compress function --- code/numpy/carray/carray.c | 65 ++++++++++++++++ code/numpy/carray/carray.h | 2 +- code/numpy/carray/carray_tools.c | 2 +- code/numpy/carray/carray_tools.h | 2 +- code/numpy/numerical.c | 39 +++------- code/numpy/numpy.c | 5 +- code/numpy/transform.c | 130 +++++++++++++++++++++++++++++++ code/numpy/transform.h | 1 + code/ulab.h | 6 +- code/ulab_tools.c | 10 ++- code/ulab_tools.h | 3 +- 11 files changed, 229 insertions(+), 36 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 220e6e5f..c0bd8144 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -98,6 +98,71 @@ mp_obj_t carray_conjugate(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate); #endif +#if ULAB_NUMPY_HAS_SORT_COMPLEX +//| def sort_complex(a: ArrayLike) -> ulab.numpy.ndarray: ... +static void carray_sort_complex_(mp_float_t *array, size_t len) { + // helper function to sort by every second element of a complex array + // array is assumed to be floating vector containing the real and imaginary parts at + // alternating positions as + // array[0] = real[0] + // array[1] = imag[0] + // array[2] = real[1] + // array[3] = imag[1] + // + // if array is supposed to be sorted by the imaginary parts, + + mp_float_t tmp; + size_t c, q = len, p, r = len >> 1; + for (;;) { + if (r > 0) { + tmp = array[2 * (--r)]; + } else { + q--; + if(q == 0) { + break; + } + tmp = array[2 * q]; + array[2 * q] = array[0]; + } + p = r; + c = r + r + 1; + while (c < q) { + if((c + 1 < q) && (array[2 * (c+1)]) > array[2 * c])) { + c++; + } + if(array[2 * c] > tmp) { + array[2 * p] = array[2 * c]; + p = c; + c = p + p + 1; + } else { + break; + } + } + array[2 * p] = tmp; + } +}) +} + +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")); + } + ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); + if(source->ndim != 1) { + mp_raise_TypeError(translate("input must be a 1D ndarray")); + } + + if(source->dtype != NDARRAY_COMPLEX) { + return carray_sort_complex_(_source, mp_const_none, 0); + } else { + + } + +} + +MP_DEFINE_CONST_FUN_OBJ_1(carray_sort_complex_obj, carray_sort_complex); +#endif + mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { // calculates the absolute value of a complex array and returns a dense array uint8_t *sarray = (uint8_t *)source->array; diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 393b209c..0fa8ae6b 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Zoltán Vörös + * Copyright (c) 2021-2022 Zoltán Vörös */ #ifndef _CARRAY_ diff --git a/code/numpy/carray/carray_tools.c b/code/numpy/carray/carray_tools.c index c6fb8de2..7b623d34 100644 --- a/code/numpy/carray/carray_tools.c +++ b/code/numpy/carray/carray_tools.c @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Zoltán Vörös + * Copyright (c) 2022 Zoltán Vörös */ #include diff --git a/code/numpy/carray/carray_tools.h b/code/numpy/carray/carray_tools.h index 3b28e1c4..3ac79b5f 100644 --- a/code/numpy/carray/carray_tools.h +++ b/code/numpy/carray/carray_tools.h @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Zoltán Vörös + * Copyright (c) 2022 Zoltán Vörös */ #ifndef _CARRAY_TOOLS_ diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 08e5153b..5b8a1b50 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -475,11 +475,7 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t } } } else { - 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("axis is out of bounds")); - } + 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); @@ -600,11 +596,7 @@ static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inpla ndarray->ndim = 1; #endif } else { - 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")); - } + ax = tools_get_axis(axis, ndarray->ndim); } size_t *shape = m_new(size_t, ULAB_MAX_DIMS); @@ -700,11 +692,8 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw mp_raise_ValueError(translate("axis too long")); } } - int8_t ax = mp_obj_get_int(args[1].u_obj); - if(ax < 0) ax += ndarray->ndim; - if((ax < 0) || (ax > ndarray->ndim - 1)) { - mp_raise_ValueError(translate("index out of range")); - } + 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); @@ -972,11 +961,8 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar results->array = rarray; results->strides[ULAB_MAX_DIMS - 1] = -results->strides[ULAB_MAX_DIMS - 1]; } else if(mp_obj_is_int(args[1].u_obj)){ - int8_t ax = mp_obj_get_int(args[1].u_obj); - if(ax < 0) ax += ndarray->ndim; - if((ax < 0) || (ax > ndarray->ndim - 1)) { - mp_raise_ValueError(translate("index out of range")); - } + int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim); + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; int32_t offset = (ndarray->shape[ax] - 1) * ndarray->strides[ax]; results = ndarray_new_view(ndarray, ndarray->ndim, ndarray->shape, ndarray->strides, offset); @@ -1054,10 +1040,8 @@ mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ } return mp_obj_new_float(median); } else { - int8_t ax = mp_obj_get_int(args[1].u_obj); - if(ax < 0) ax += ndarray->ndim; - // here we can save the exception, because if the axis is out of range, - // then numerical_sort_helper has already taken care of the issue + 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); @@ -1210,11 +1194,8 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]); #endif } else if(mp_obj_is_int(args[2].u_obj)){ - int8_t ax = mp_obj_get_int(args[2].u_obj); - if(ax < 0) ax += ndarray->ndim; - if((ax < 0) || (ax > ndarray->ndim - 1)) { - mp_raise_ValueError(translate("index out of range")); - } + 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); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 6a54f40c..88a5c394 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -8,7 +8,7 @@ * * Copyright (c) 2020 Jeff Epler for Adafruit Industries * 2020 Scott Shawcroft for Adafruit Industries - * 2020-2021 Zoltán Vörös + * 2020-2022 Zoltán Vörös * 2020 Taku Fukada */ @@ -146,6 +146,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_ARANGE { MP_ROM_QSTR(MP_QSTR_arange), (mp_obj_t)&create_arange_obj }, #endif + #if ULAB_NUMPY_HAS_COMPRESS + { MP_ROM_QSTR(MP_QSTR_compress), (mp_obj_t)&transform_compress_obj }, + #endif #if ULAB_NUMPY_HAS_CONJUGATE { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj }, #endif diff --git a/code/numpy/transform.c b/code/numpy/transform.c index 026f9e57..f0e3e701 100644 --- a/code/numpy/transform.c +++ b/code/numpy/transform.c @@ -21,6 +21,136 @@ #include "carray/carray_tools.h" #include "transform.h" +#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_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 condition = args[0].u_obj; + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj); + uint8_t *array = (uint8_t *)ndarray->array; + mp_obj_t axis = args[2].u_obj; + + size_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(condition)); + int8_t ax, shift_ax; + + if(axis != mp_const_none) { + ax = tools_get_axis(axis, ndarray->ndim); + shift_ax = ULAB_MAX_DIMS - ndarray->ndim + ax; + } + + 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")); + } + + size_t true_count = 0; + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(condition, &iter_buf); + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if(mp_obj_is_true(item)) { + true_count++; + } + } + + iterable = mp_getiter(condition, &iter_buf); + + ndarray_obj_t *result = NULL; + uint8_t *rarray = NULL; + + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memcpy(shape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t)); + + size_t *rshape = m_new(size_t, ULAB_MAX_DIMS); + memcpy(rshape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t)); + + 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); + + if(axis == mp_const_none) { + result = ndarray_new_linear_array(true_count, ndarray->dtype); + rarray = (uint8_t *)result->array; + memset(rstrides, 0, ndarray->ndim * sizeof(int32_t)); + rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize; + rshape[ULAB_MAX_DIMS - 1] = 0; + } else { + rshape[shift_ax] = true_count; + + result = ndarray_new_dense_ndarray(ndarray->ndim, rshape, ndarray->dtype); + rarray = (uint8_t *)result->array; + + SWAP(size_t, shape[shift_ax], shape[ULAB_MAX_DIMS - 1]); + SWAP(size_t, rshape[shift_ax], rshape[ULAB_MAX_DIMS - 1]); + SWAP(int32_t, strides[shift_ax], strides[ULAB_MAX_DIMS - 1]); + + memcpy(rstrides, result->strides, ULAB_MAX_DIMS * sizeof(int32_t)); + SWAP(int32_t, rstrides[shift_ax], rstrides[ULAB_MAX_DIMS - 1]); + } + + #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; + if(axis != mp_const_none) { + iterable = mp_getiter(condition, &iter_buf); + } + do { + item = mp_iternext(iterable); + if(mp_obj_is_true(item)) { + memcpy(rarray, array, ndarray->itemsize); + rarray += rstrides[ULAB_MAX_DIMS - 1]; + } + array += strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1]; + array += strides[ULAB_MAX_DIMS - 2]; + rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS - 1]; + rarray += rstrides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2]; + array += strides[ULAB_MAX_DIMS - 3]; + rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2]; + rarray += rstrides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < shape[ULAB_MAX_DIMS - 3]); + #endif + #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]; + i++; + } while(i < shape[ULAB_MAX_DIMS - 4]); + #endif + + return result; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(transform_compress_obj, 2, transform_compress); +#endif /* ULAB_NUMPY_HAS_COMPRESS */ + #if ULAB_MAX_DIMS > 1 #if ULAB_NUMPY_HAS_DOT //| def dot(m1: ulab.numpy.ndarray, m2: ulab.numpy.ndarray) -> Union[ulab.numpy.ndarray, _float]: diff --git a/code/numpy/transform.h b/code/numpy/transform.h index f4a09b8e..039dceae 100644 --- a/code/numpy/transform.h +++ b/code/numpy/transform.h @@ -23,6 +23,7 @@ #include "../ulab_tools.h" #include "transform.h" +MP_DECLARE_CONST_FUN_OBJ_KW(transform_compress_obj); MP_DECLARE_CONST_FUN_OBJ_2(transform_dot_obj); #endif diff --git a/code/ulab.h b/code/ulab.h index c37d4f19..64e4cbe2 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019-2021 Zoltán Vörös + * Copyright (c) 2019-2022 Zoltán Vörös */ #ifndef __ULAB__ @@ -426,6 +426,10 @@ #define ULAB_NUMPY_HAS_ARGSORT (1) #endif +#ifndef ULAB_NUMPY_HAS_COMPRESS +#define ULAB_NUMPY_HAS_COMPRESS (1) +#endif + // conjugate is defined only, if ULAB_SUPPORTS_COMPLEX is set to 1 #ifndef ULAB_NUMPY_HAS_CONJUGATE #define ULAB_NUMPY_HAS_CONJUGATE (1) diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 5bf9b07d..7fb63634 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -5,7 +5,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020-2021 Zoltán Vörös + * Copyright (c) 2020-2022 Zoltán Vörös */ @@ -216,6 +216,14 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { 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(translate("axis is out of bounds")); + } + return ax; +} #if ULAB_MAX_DIMS > 1 ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { diff --git a/code/ulab_tools.h b/code/ulab_tools.h index beab3382..2898ef1f 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -5,7 +5,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020-2021 Zoltán Vörös + * Copyright (c) 2020-2022 Zoltán Vörös */ #ifndef _TOOLS_ @@ -33,6 +33,7 @@ uint8_t ndarray_upcast_dtype(uint8_t , uint8_t ); 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 ); ndarray_obj_t *tools_object_is_square(mp_obj_t ); uint8_t ulab_binary_get_size(uint8_t ); From d39549e3ac78b90b72dd56b8bd32e0eabf539679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 2 Jan 2022 17:34:38 +0100 Subject: [PATCH 49/58] add sort_complex function --- code/numpy/carray/carray.c | 45 ++++++++++++++++++++------------------ code/numpy/numpy.c | 13 ++++++----- code/ulab.h | 10 +++++++-- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index c0bd8144..11f4a566 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -99,48 +99,53 @@ MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate); #endif #if ULAB_NUMPY_HAS_SORT_COMPLEX -//| def sort_complex(a: ArrayLike) -> ulab.numpy.ndarray: ... static void carray_sort_complex_(mp_float_t *array, size_t len) { - // helper function to sort by every second element of a complex array - // array is assumed to be floating vector containing the real and imaginary parts at - // alternating positions as + // array is assumed to be a floating vector containing the real and imaginary parts + // of a complex array at alternating positions as // array[0] = real[0] // array[1] = imag[0] // array[2] = real[1] // array[3] = imag[1] - // - // if array is supposed to be sorted by the imaginary parts, - mp_float_t tmp; + mp_float_t real, imag; size_t c, q = len, p, r = len >> 1; for (;;) { if (r > 0) { - tmp = array[2 * (--r)]; + r--; + real = array[2 * r]; + imag = array[2 * r + 1]; } else { q--; if(q == 0) { break; } - tmp = array[2 * q]; + real = array[2 * q]; + imag = array[2 * q + 1]; array[2 * q] = array[0]; + array[2 * q + 1] = array[1]; } p = r; c = r + r + 1; while (c < q) { - if((c + 1 < q) && (array[2 * (c+1)]) > array[2 * c])) { - c++; + if(c + 1 < q) { + if((array[2 * (c+1)] > array[2 * c]) || + ((array[2 * (c+1)] == array[2 * c]) && (array[2 * (c+1) + 1] > array[2 * c + 1]))) { + c++; + } } - if(array[2 * c] > tmp) { - array[2 * p] = array[2 * c]; + if((array[2 * c] > real) || + ((array[2 * c] == real) && (array[2 * c + 1] == imag))) { + array[2 * p] = array[2 * c]; // real part + array[2 * p + 1] = array[2 * c + 1]; // imag part p = c; c = p + p + 1; } else { break; } } - array[2 * p] = tmp; + array[2 * p] = real; + array[2 * p + 1] = imag; } -}) } mp_obj_t carray_sort_complex(mp_obj_t _source) { @@ -152,12 +157,10 @@ mp_obj_t carray_sort_complex(mp_obj_t _source) { mp_raise_TypeError(translate("input must be a 1D ndarray")); } - if(source->dtype != NDARRAY_COMPLEX) { - return carray_sort_complex_(_source, mp_const_none, 0); - } else { - - } - + 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); + return MP_OBJ_FROM_PTR(ndarray); } MP_DEFINE_CONST_FUN_OBJ_1(carray_sort_complex_obj, carray_sort_complex); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index 88a5c394..bbf498e6 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -149,9 +149,6 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_COMPRESS { MP_ROM_QSTR(MP_QSTR_compress), (mp_obj_t)&transform_compress_obj }, #endif - #if ULAB_NUMPY_HAS_CONJUGATE - { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj }, - #endif #if ULAB_NUMPY_HAS_CONCATENATE { MP_ROM_QSTR(MP_QSTR_concatenate), (mp_obj_t)&create_concatenate_obj }, #endif @@ -361,8 +358,14 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vector_vectorize_obj }, #endif #if ULAB_SUPPORTS_COMPLEX - { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + #if ULAB_NUMPY_HAS_CONJUGATE + { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj }, + #endif + #if ULAB_NUMPY_HAS_SORT_COMPLEX + { MP_ROM_QSTR(MP_QSTR_sort_complex), (mp_obj_t)&carray_sort_complex_obj }, + #endif #endif }; diff --git a/code/ulab.h b/code/ulab.h index 64e4cbe2..9504934e 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -21,6 +21,7 @@ // - whether the python syntax is numpy-like, or modular // - whether arrays can be sliced and iterated over // - which binary/unary operators are supported +// - whether ulab can deal with complex numbers // // A considerable amount of flash space can be saved by removing (setting // the corresponding constants to 0) the unnecessary functions and features. @@ -393,9 +394,9 @@ #define ULAB_NUMPY_HAS_FFT_MODULE (1) #endif -// by setting this constant to 1, the FFT routine will behave in a +// By setting this constant to 1, the FFT routine will behave in a // numpy-compatible way, i.e., it will output a complex array -// this setting has no effect, if ULAB_SUPPORTS_COMPLEX is 0 +// This setting has no effect, if ULAB_SUPPORTS_COMPLEX is 0 // 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 @@ -487,6 +488,11 @@ #define ULAB_NUMPY_HAS_SORT (1) #endif +// This function can be included only, when ULAB_SUPPORTS_COMPLEX is set to 1 +#ifndef ULAB_NUMPY_HAS_SORT_COMPLEX +#define ULAB_NUMPY_HAS_SORT_COMPLEX (1) +#endif + #ifndef ULAB_NUMPY_HAS_STD #define ULAB_NUMPY_HAS_STD (1) #endif From 6a7d20dd581983c22d4bf9e1f725137824301cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 2 Jan 2022 19:14:12 +0100 Subject: [PATCH 50/58] add real/imag properties to ndarray --- code/ndarray_properties.c | 15 +++++++++++++++ code/numpy/carray/carray.c | 4 ++-- code/numpy/carray/carray.h | 4 ++++ code/numpy/numpy.c | 4 ++++ code/ulab.h | 29 +++++++++++++++++++---------- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/code/ndarray_properties.c b/code/ndarray_properties.c index 4a93fb82..5464b31d 100644 --- a/code/ndarray_properties.c +++ b/code/ndarray_properties.c @@ -20,6 +20,9 @@ #include "ulab.h" #include "ndarray.h" #include "numpy/ndarray/ndarray_iter.h" +#if ULAB_SUPPORTS_COMPLEX +#include "numpy/carray/carray.h" +#endif #ifndef CIRCUITPY @@ -82,6 +85,18 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = ndarray_transpose(self_in); break; #endif + #if ULAB_SUPPORTS_COMPLEX + #if ULAB_NUMPY_HAS_IMAG + case MP_QSTR_imag: + dest[0] = carray_imag(self_in); + break; + #endif + #if ULAB_NUMPY_HAS_IMAG + case MP_QSTR_real: + dest[0] = carray_real(self_in); + break; + #endif + #endif /* ULAB_SUPPORTS_COMPLEX */ default: call_local_method(self_in, attr, dest); break; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 11f4a566..4e7d84df 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -25,7 +25,7 @@ #if ULAB_SUPPORTS_COMPLEX -STATIC mp_obj_t carray_real(mp_obj_t _source) { +mp_obj_t carray_real(mp_obj_t _source) { if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { @@ -45,7 +45,7 @@ STATIC mp_obj_t carray_real(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); -STATIC mp_obj_t carray_imag(mp_obj_t _source) { +mp_obj_t carray_imag(mp_obj_t _source) { if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index 0fa8ae6b..fec6e4cb 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -17,6 +17,10 @@ MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_conjugate_obj); MP_DECLARE_CONST_FUN_OBJ_1(carray_sort_complex_obj); + +mp_obj_t carray_imag(mp_obj_t ); +mp_obj_t carray_real(mp_obj_t ); + mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *); mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index bbf498e6..d674db66 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -358,8 +358,12 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&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 }, + #endif + #if ULAB_NUMPY_HAS_IMAG { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj }, + #endif #if ULAB_NUMPY_HAS_CONJUGATE { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj }, #endif diff --git a/code/ulab.h b/code/ulab.h index 9504934e..5c468b65 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -431,11 +431,6 @@ #define ULAB_NUMPY_HAS_COMPRESS (1) #endif -// conjugate is defined only, if ULAB_SUPPORTS_COMPLEX is set to 1 -#ifndef ULAB_NUMPY_HAS_CONJUGATE -#define ULAB_NUMPY_HAS_CONJUGATE (1) -#endif - #ifndef ULAB_NUMPY_HAS_CONVOLVE #define ULAB_NUMPY_HAS_CONVOLVE (1) #endif @@ -488,11 +483,6 @@ #define ULAB_NUMPY_HAS_SORT (1) #endif -// This function can be included only, when ULAB_SUPPORTS_COMPLEX is set to 1 -#ifndef ULAB_NUMPY_HAS_SORT_COMPLEX -#define ULAB_NUMPY_HAS_SORT_COMPLEX (1) -#endif - #ifndef ULAB_NUMPY_HAS_STD #define ULAB_NUMPY_HAS_STD (1) #endif @@ -611,6 +601,25 @@ #define ULAB_NUMPY_HAS_VECTORIZE (1) #endif +// Complex functions. The implementations are compiled into +// the firmware, only if ULAB_SUPPORTS_COMPLEX is set to 1 +#ifndef ULAB_NUMPY_HAS_CONJUGATE +#define ULAB_NUMPY_HAS_CONJUGATE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_IMAG +#define ULAB_NUMPY_HAS_IMAG (1) +#endif + +#ifndef ULAB_NUMPY_HAS_REAL +#define ULAB_NUMPY_HAS_REAL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_SORT_COMPLEX +#define ULAB_NUMPY_HAS_SORT_COMPLEX (1) +#endif + +// scipy modules #ifndef ULAB_SCIPY_HAS_LINALG_MODULE #define ULAB_SCIPY_HAS_LINALG_MODULE (1) #endif From 8efdec785e0f652dc505bf7fa72f344602e32e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 5 Jan 2022 20:46:03 +0100 Subject: [PATCH 51/58] extend convolve for the complex case --- code/numpy/filter.c | 67 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/code/numpy/filter.c b/code/numpy/filter.c index fff122ef..057cd6dc 100644 --- a/code/numpy/filter.c +++ b/code/numpy/filter.c @@ -41,8 +41,6 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj); - COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype) - COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype) // deal with linear arrays only #if ULAB_MAX_DIMS > 1 if((a->ndim != 1) || (c->ndim != 1)) { @@ -56,30 +54,77 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a } int len = len_a + len_c - 1; // convolve mode "full" - ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_FLOAT); - mp_float_t *outptr = (mp_float_t *)out->array; + int32_t off = len_c - 1; + uint8_t dtype = NDARRAY_FLOAT; + + #if ULAB_SUPPORTS_COMPLEX + if((a->dtype == NDARRAY_COMPLEX) || (c->dtype == NDARRAY_COMPLEX)) { + dtype = NDARRAY_COMPLEX; + } + #endif + ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype); + mp_float_t *array = (mp_float_t *)ndarray->array; + uint8_t *aarray = (uint8_t *)a->array; uint8_t *carray = (uint8_t *)c->array; - int32_t off = len_c - 1; int32_t as = a->strides[ULAB_MAX_DIMS - 1] / a->itemsize; int32_t cs = c->strides[ULAB_MAX_DIMS - 1] / c->itemsize; - for(int32_t k=-off; k < len-off; k++) { - mp_float_t accum = (mp_float_t)0.0; + + #if ULAB_SUPPORTS_COMPLEX + if(dtype == NDARRAY_COMPLEX) { + mp_float_t a_real, a_imag; + mp_float_t c_real, c_imag = MICROPY_FLOAT_CONST(0.0); + for(int32_t k = -off; k < len-off; k++) { + mp_float_t accum_real = MICROPY_FLOAT_CONST(0.0); + mp_float_t accum_imag = MICROPY_FLOAT_CONST(0.0); + + int32_t top_n = MIN(len_c, len_a - k); + int32_t bot_n = MAX(-k, 0); + + for(int32_t n = bot_n; n < top_n; n++) { + int32_t idx_c = (len_c - n - 1) * cs; + int32_t idx_a = (n + k) * as; + if(a->dtype != NDARRAY_COMPLEX) { + a_real = ndarray_get_float_index(aarray, a->dtype, idx_a); + a_imag = MICROPY_FLOAT_CONST(0.0); + } else { + a_real = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a); + a_imag = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a + 1); + } + + if(c->dtype != NDARRAY_COMPLEX) { + c_real = ndarray_get_float_index(carray, c->dtype, idx_c); + c_imag = MICROPY_FLOAT_CONST(0.0); + } else { + c_real = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c); + c_imag = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c + 1); + } + accum_real += a_real * c_real - a_imag * c_imag; + accum_imag += a_real * c_imag + a_imag * c_real; + } + *array++ = accum_real; + *array++ = accum_imag; + } + return MP_OBJ_FROM_PTR(ndarray); + } + #endif + + for(int32_t k = -off; k < len-off; k++) { + mp_float_t accum = MICROPY_FLOAT_CONST(0.0); int32_t top_n = MIN(len_c, len_a - k); int32_t bot_n = MAX(-k, 0); - for(int32_t n=bot_n; n < top_n; n++) { + for(int32_t n = bot_n; n < top_n; n++) { int32_t idx_c = (len_c - n - 1) * cs; int32_t idx_a = (n + k) * as; mp_float_t ai = ndarray_get_float_index(aarray, a->dtype, idx_a); mp_float_t ci = ndarray_get_float_index(carray, c->dtype, idx_c); accum += ai * ci; } - *outptr++ = accum; + *array++ = accum; } - - return out; + return MP_OBJ_FROM_PTR(ndarray); } MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve); From d1b3d40b475a01f59eaaecea5d538d3e78cdf9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 6 Jan 2022 09:22:45 +0100 Subject: [PATCH 52/58] add complex support to all/any --- code/numpy/numerical.c | 79 +++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/code/numpy/numerical.c b/code/numpy/numerical.c index 5b8a1b50..d6983c0a 100644 --- a/code/numpy/numerical.c +++ b/code/numpy/numerical.c @@ -95,7 +95,6 @@ static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { bool anytype = optype == NUMERICAL_ALL ? 1 : 0; if(mp_obj_is_type(oin, &ulab_ndarray_type)) { ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); - COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype) uint8_t *array = (uint8_t *)ndarray->array; if(ndarray->len == 0) { // return immediately with empty arrays if(optype == NUMERICAL_ALL) { @@ -132,33 +131,71 @@ static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { size_t l = 0; if(axis == mp_const_none) { do { - mp_float_t value = func(array); - if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) { - // optype = NUMERICAL_ANY - return mp_const_true; - } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) { - // optype == NUMERICAL_ALL - return mp_const_false; + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + mp_float_t real = *((mp_float_t *)array); + mp_float_t imag = *((mp_float_t *)(array + sizeof(mp_float_t))); + if(((real != MICROPY_FLOAT_CONST(0.0)) | (imag != MICROPY_FLOAT_CONST(0.0))) & !anytype) { + // optype = NUMERICAL_ANY + return mp_const_true; + } else if(((real == MICROPY_FLOAT_CONST(0.0)) & (imag == MICROPY_FLOAT_CONST(0.0))) & anytype) { + // optype == NUMERICAL_ALL + return mp_const_false; + } + } else { + #endif + mp_float_t value = func(array); + if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) { + // optype = NUMERICAL_ANY + return mp_const_true; + } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) { + // optype == NUMERICAL_ALL + return mp_const_false; + } + #if ULAB_SUPPORTS_COMPLEX } + #endif array += _shape_strides.strides[0]; l++; } while(l < _shape_strides.shape[0]); } else { // a scalar axis keyword was supplied do { - mp_float_t value = func(array); - if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) { - // optype == NUMERICAL_ANY - *rarray = 1; - // since we are breaking out of the loop, move the pointer forward - array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); - break; - } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) { - // optype == NUMERICAL_ALL - *rarray = 0; - // since we are breaking out of the loop, move the pointer forward - array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); - break; + #if ULAB_SUPPORTS_COMPLEX + if(ndarray->dtype == NDARRAY_COMPLEX) { + mp_float_t real = *((mp_float_t *)array); + mp_float_t imag = *((mp_float_t *)(array + sizeof(mp_float_t))); + if(((real != MICROPY_FLOAT_CONST(0.0)) | (imag != MICROPY_FLOAT_CONST(0.0))) & !anytype) { + // optype = NUMERICAL_ANY + *rarray = 1; + // since we are breaking out of the loop, move the pointer forward + array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); + break; + } else if(((real == MICROPY_FLOAT_CONST(0.0)) & (imag == MICROPY_FLOAT_CONST(0.0))) & anytype) { + // optype == NUMERICAL_ALL + *rarray = 0; + // since we are breaking out of the loop, move the pointer forward + array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); + break; + } + } else { + #endif + mp_float_t value = func(array); + if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) { + // optype == NUMERICAL_ANY + *rarray = 1; + // since we are breaking out of the loop, move the pointer forward + array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); + break; + } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) { + // optype == NUMERICAL_ALL + *rarray = 0; + // since we are breaking out of the loop, move the pointer forward + array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); + break; + } + #if ULAB_SUPPORTS_COMPLEX } + #endif array += _shape_strides.strides[0]; l++; } while(l < _shape_strides.shape[0]); From ad9e1e99504c6db9303820168c2086b296bebb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 6 Jan 2022 19:55:34 +0100 Subject: [PATCH 53/58] fix linspace error, extend to complex dtype, factor out tests --- README.md | 15 ++- code/ndarray.c | 4 +- code/numpy/create.c | 88 ++++++++++++--- code/numpy/create.h | 7 +- tests/2d/numpy/arange.py | 11 ++ tests/2d/numpy/arange.py.exp | 15 +++ tests/2d/numpy/concatenate.py | 18 ++++ tests/2d/numpy/concatenate.py.exp | 20 ++++ tests/2d/numpy/eye.py | 30 ++++++ tests/2d/numpy/eye.py.exp | 78 ++++++++++++++ tests/2d/numpy/full.py | 9 ++ tests/2d/numpy/full.py.exp | 10 ++ tests/2d/numpy/initialisation.py | 102 +----------------- tests/2d/numpy/initialisation.py.exp | 156 --------------------------- tests/2d/numpy/linspace.py | 10 ++ tests/2d/numpy/linspace.py.exp | 10 ++ tests/2d/numpy/logspace.py | 10 ++ tests/2d/numpy/logspace.py.exp | 10 ++ tests/2d/numpy/ones.py | 13 +++ tests/2d/numpy/ones.py.exp | 39 +++++++ tests/2d/numpy/zeros.py | 13 +++ tests/2d/numpy/zeros.py.exp | 39 +++++++ 22 files changed, 427 insertions(+), 280 deletions(-) create mode 100644 tests/2d/numpy/arange.py create mode 100644 tests/2d/numpy/arange.py.exp create mode 100644 tests/2d/numpy/concatenate.py create mode 100644 tests/2d/numpy/concatenate.py.exp create mode 100644 tests/2d/numpy/eye.py create mode 100644 tests/2d/numpy/eye.py.exp create mode 100644 tests/2d/numpy/full.py create mode 100644 tests/2d/numpy/full.py.exp create mode 100644 tests/2d/numpy/linspace.py create mode 100644 tests/2d/numpy/linspace.py.exp create mode 100644 tests/2d/numpy/logspace.py create mode 100644 tests/2d/numpy/logspace.py.exp create mode 100644 tests/2d/numpy/ones.py create mode 100644 tests/2d/numpy/ones.py.exp create mode 100644 tests/2d/numpy/zeros.py create mode 100644 tests/2d/numpy/zeros.py.exp diff --git a/README.md b/README.md index c9c07388..0346d897 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,12 @@ [![Documentation Status](https://readthedocs.org/projects/micropython-ulab-robert/badge/?version=latest)](https://micropython-ulab-robert.readthedocs.io/en/latest/?badge=latest) `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 for numerical data of one to four +The module is written in C, defines compact containers (`ndarray`s) for numerical data of one to four dimensions, and is fast. The library is a software-only standard `micropython` user module, -i.e., it has no hardware dependencies, and can be compiled for any platform. -The `float` implementation of `micropython` (`float`, or `double`) is automatically detected. +i.e., it has no hardware dependencies, and can be compiled for any platform. 8-, and 16-bit signed +and unsigned integer `dtypes`, as well as `float`, and, optionally, ` complex` are supported. +The `float` implementation of `micropython` (32-bit `float`, or 64-bit `double`) is automatically +detected and handled. 1. [Supported functions and methods](#supported-functions-and-methods) 1. [ndarray methods](#ndarray-methods) @@ -38,7 +40,8 @@ iterables via the `array` constructor, or by means of the `arange`, `concatenate `frombuffer`, `full`, `linspace`, `logspace`, `ones`, or `zeros` functions. `ndarray`s can be sliced, and iterated on, and have a number of their own methods, and properties, such as `flatten()`, `itemsize`, `reshape()`, -`shape`, `size`, `strides`, `tobytes()`, and `transpose()` and `T`. +`shape`, `size`, `strides`, `tobytes()`, `tolist()`, and `transpose()` and `T`. If the firmware is compiled with `complex` support, +the `imag`, and `real` properties are automatically included. ## `numpy` and `scipy` functions @@ -46,7 +49,9 @@ In addition, `ulab` includes [universal functions](https://micropython-ulab.read ## `ulab` utilities -The [`utils`](https://micropython-ulab.readthedocs.io/en/latest/ulab-utils.html) module contains functions for interfacing with peripheral devices supporting the buffer protocol. +The [`utils`](https://micropython-ulab.readthedocs.io/en/latest/ulab-utils.html) module contains functions for +interfacing with peripheral devices supporting the buffer protocol. These functions do not have an obvious +`numpy` equivalent, but share a similar programming interface, and allow direct data input to and output from ## `user` module diff --git a/code/ndarray.c b/code/ndarray.c index 8960fb66..e5ece2de 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -6,7 +6,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019-2021 Zoltán Vörös + * Copyright (c) 2019-2022 Zoltán Vörös * 2020 Jeff Epler for Adafruit Industries * 2020 Taku Fukada */ @@ -1804,7 +1804,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { return obj; } #if ULAB_SUPPORTS_COMPLEX - else if (mp_obj_is_type(obj, &mp_type_complex)) { + else if(mp_obj_is_type(obj, &mp_type_complex)) { ndarray = ndarray_new_linear_array(1, NDARRAY_COMPLEX); mp_float_t *array = (mp_float_t *)ndarray->array; mp_obj_get_complex(obj, &array[0], &array[1]); diff --git a/code/numpy/create.c b/code/numpy/create.c index 33d9236a..5777070c 100644 --- a/code/numpy/create.c +++ b/code/numpy/create.c @@ -73,7 +73,7 @@ static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t #endif #if ULAB_NUMPY_HAS_ARANGE | ULAB_NUMPY_HAS_LINSPACE -static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, size_t len, uint8_t dtype) { +static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, mp_float_t stop, size_t len, uint8_t dtype) { mp_float_t value = start; ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype); @@ -83,15 +83,15 @@ static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, *array++ = value == MICROPY_FLOAT_CONST(0.0) ? 0 : 1; } } else if(dtype == NDARRAY_UINT8) { - ARANGE_LOOP(uint8_t, ndarray, len, step); + ARANGE_LOOP(uint8_t, ndarray, len, step, stop); } else if(dtype == NDARRAY_INT8) { - ARANGE_LOOP(int8_t, ndarray, len, step); + ARANGE_LOOP(int8_t, ndarray, len, step, stop); } else if(dtype == NDARRAY_UINT16) { - ARANGE_LOOP(uint16_t, ndarray, len, step); + ARANGE_LOOP(uint16_t, ndarray, len, step, stop); } else if(dtype == NDARRAY_INT16) { - ARANGE_LOOP(int16_t, ndarray, len, step); + ARANGE_LOOP(int16_t, ndarray, len, step, stop); } else { - ARANGE_LOOP(mp_float_t, ndarray, len, step); + ARANGE_LOOP(mp_float_t, ndarray, len, step, stop); } return ndarray; } @@ -129,14 +129,14 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg uint8_t dtype = NDARRAY_FLOAT; mp_float_t start, stop, step; if(n_args == 1) { - start = 0.0; + start = MICROPY_FLOAT_CONST(0.0); stop = mp_obj_get_float(args[0].u_obj); - step = 1.0; + step = MICROPY_FLOAT_CONST(1.0); if(mp_obj_is_int(args[0].u_obj)) dtype = NDARRAY_INT16; } else if(n_args == 2) { start = mp_obj_get_float(args[0].u_obj); stop = mp_obj_get_float(args[1].u_obj); - step = 1.0; + step = MICROPY_FLOAT_CONST(1.0); if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj)) dtype = NDARRAY_INT16; } else if(n_args == 3) { start = mp_obj_get_float(args[0].u_obj); @@ -156,8 +156,9 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg 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)); - ndarray = create_linspace_arange(start, step, len, dtype); + size_t len = (size_t)(MICROPY_FLOAT_C_FUN(ceil)((stop - start) / step)); + stop = start + (len - 1) * step; + ndarray = create_linspace_arange(start, step, stop, len, dtype); } return MP_OBJ_FROM_PTR(ndarray); } @@ -511,18 +512,71 @@ mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a mp_raise_ValueError(translate("number of points must be at least 2")); } size_t len = (size_t)args[2].u_int; - mp_float_t start, step; - start = mp_obj_get_float(args[0].u_obj); - uint8_t typecode = args[5].u_int; - if(args[3].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj)-start)/(len-1); - else step = (mp_obj_get_float(args[1].u_obj)-start)/len; - ndarray_obj_t *ndarray = create_linspace_arange(start, step, len, typecode); + mp_float_t start, step, stop; + + ndarray_obj_t *ndarray = NULL; + + #if ULAB_SUPPORTS_COMPLEX + mp_float_t step_real, step_imag; + bool complex_out = false; + + if(mp_obj_is_type(args[0].u_obj, &mp_type_complex) || mp_obj_is_type(args[1].u_obj, &mp_type_complex)) { + complex_out = true; + ndarray = ndarray_new_linear_array(len, NDARRAY_COMPLEX); + mp_float_t *array = (mp_float_t *)ndarray->array; + mp_float_t start_real, start_imag; + mp_float_t stop_real, stop_imag; + + mp_obj_get_complex(args[0].u_obj, &start_real, &start_imag); + mp_obj_get_complex(args[1].u_obj, &stop_real, &stop_imag); + if(args[3].u_obj == mp_const_true) { + step_real = (stop_real - start_real) / (len - 1); + step_imag = (stop_imag - start_imag) / (len - 1); + } else { + step_real = (stop_real - start_real) / len; + step_imag = (stop_imag - start_imag) / len; + } + + for(size_t i = 0; i < len; i++) { + *array++ = start_real; + *array++ = start_imag; + start_real += step_real; + start_imag += step_imag; + } + } else { + #endif + start = mp_obj_get_float(args[0].u_obj); + stop = mp_obj_get_float(args[1].u_obj); + + uint8_t typecode = args[5].u_int; + + if(args[3].u_obj == mp_const_true) { + step = (stop - start) / (len - 1); + } else { + step = (stop - start) / len; + stop = start + step * (len - 1); + } + + ndarray = create_linspace_arange(start, step, stop, len, typecode); + #if ULAB_SUPPORTS_COMPLEX + } + #endif + if(args[4].u_obj == mp_const_false) { return MP_OBJ_FROM_PTR(ndarray); } else { mp_obj_t tuple[2]; tuple[0] = ndarray; + #if ULAB_SUPPORTS_COMPLEX + if(complex_out) { + tuple[1] = mp_obj_new_complex(step_real, step_imag); + } else { + tuple[1] = mp_obj_new_float(step); + } + #else /* ULAB_SUPPORTS_COMPLEX */ tuple[1] = mp_obj_new_float(step); + #endif + return mp_obj_new_tuple(2, tuple); } } diff --git a/code/numpy/create.h b/code/numpy/create.h index cba2eba8..18f636cc 100644 --- a/code/numpy/create.h +++ b/code/numpy/create.h @@ -67,12 +67,13 @@ mp_obj_t create_frombuffer(size_t , const mp_obj_t *, mp_map_t *); MP_DECLARE_CONST_FUN_OBJ_KW(create_frombuffer_obj); #endif -#define ARANGE_LOOP(type_, ndarray, len, step) \ +#define ARANGE_LOOP(type_, ndarray, len, step, stop) \ ({\ type_ *array = (type_ *)(ndarray)->array;\ - for (size_t i = 0; i < (len); i++, (value) += (step)) {\ - *array++ = (type_)value;\ + for (size_t i = 0; i < (len) - 1; i++, (value) += (step)) {\ + *array++ = (type_)(value);\ }\ + *array = (type_)(stop);\ }) #endif diff --git a/tests/2d/numpy/arange.py b/tests/2d/numpy/arange.py new file mode 100644 index 00000000..91d21fe5 --- /dev/null +++ b/tests/2d/numpy/arange.py @@ -0,0 +1,11 @@ +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(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 diff --git a/tests/2d/numpy/arange.py.exp b/tests/2d/numpy/arange.py.exp new file mode 100644 index 00000000..894e0085 --- /dev/null +++ b/tests/2d/numpy/arange.py.exp @@ -0,0 +1,15 @@ +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([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([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([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([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) diff --git a/tests/2d/numpy/concatenate.py b/tests/2d/numpy/concatenate.py new file mode 100644 index 00000000..bcae97a0 --- /dev/null +++ b/tests/2d/numpy/concatenate.py @@ -0,0 +1,18 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +a = np.array([1,2,3], dtype=np.float) +b = np.array([4,5,6], dtype=np.float) + +print(np.concatenate((a,b))) +print(np.concatenate((a,b), axis=0)) + +a = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.float) +b = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.float) + +print(np.concatenate((a,b), axis=0)) +print(np.concatenate((a,b), axis=1)) +print(np.concatenate((b,a), axis=0)) +print(np.concatenate((b,a), axis=1)) diff --git a/tests/2d/numpy/concatenate.py.exp b/tests/2d/numpy/concatenate.py.exp new file mode 100644 index 00000000..4310f352 --- /dev/null +++ b/tests/2d/numpy/concatenate.py.exp @@ -0,0 +1,20 @@ +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], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0]], dtype=float64) +array([[1.0, 2.0, 3.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 4.0, 5.0, 6.0], + [7.0, 8.0, 9.0, 7.0, 8.0, 9.0]], dtype=float64) +array([[1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0]], dtype=float64) +array([[1.0, 2.0, 3.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 4.0, 5.0, 6.0], + [7.0, 8.0, 9.0, 7.0, 8.0, 9.0]], dtype=float64) diff --git a/tests/2d/numpy/eye.py b/tests/2d/numpy/eye.py new file mode 100644 index 00000000..630eed4e --- /dev/null +++ b/tests/2d/numpy/eye.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(np.ones(3)) +print(np.ones((3,3))) + +print(np.eye(3)) +print(np.eye(3, M=4)) +print(np.eye(3, M=4, k=0)) +print(np.eye(3, M=4, k=-1)) +print(np.eye(3, M=4, k=-2)) +print(np.eye(3, M=4, k=-3)) +print(np.eye(3, M=4, k=1)) +print(np.eye(3, M=4, k=2)) +print(np.eye(3, M=4, k=3)) +print(np.eye(4, M=4)) +print(np.eye(4, M=3, k=0)) +print(np.eye(4, M=3, k=-1)) +print(np.eye(4, M=3, k=-2)) +print(np.eye(4, M=3, k=-3)) +print(np.eye(4, M=3, k=1)) +print(np.eye(4, M=3, k=2)) +print(np.eye(4, M=3, k=3)) + +for dtype in dtypes: + print(np.eye(3, dtype=dtype)) \ No newline at end of file diff --git a/tests/2d/numpy/eye.py.exp b/tests/2d/numpy/eye.py.exp new file mode 100644 index 00000000..2591d428 --- /dev/null +++ b/tests/2d/numpy/eye.py.exp @@ -0,0 +1,78 @@ +array([1.0, 1.0, 1.0], dtype=float64) +array([[1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.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.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0]], dtype=float64) +array([[1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0]], dtype=float64) +array([[0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], dtype=float64) +array([[1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0]], dtype=float64) +array([[1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0]], dtype=float64) +array([[0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 1.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 1.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]], dtype=uint8) +array([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]], dtype=int8) +array([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]], dtype=uint16) +array([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]], dtype=int16) +array([[1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0]], dtype=float64) diff --git a/tests/2d/numpy/full.py b/tests/2d/numpy/full.py new file mode 100644 index 00000000..474f5185 --- /dev/null +++ b/tests/2d/numpy/full.py @@ -0,0 +1,9 @@ +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(np.full((2, 4), 3, dtype=dtype)) \ No newline at end of file diff --git a/tests/2d/numpy/full.py.exp b/tests/2d/numpy/full.py.exp new file mode 100644 index 00000000..0bf90a4d --- /dev/null +++ b/tests/2d/numpy/full.py.exp @@ -0,0 +1,10 @@ +array([[3, 3, 3, 3], + [3, 3, 3, 3]], dtype=uint8) +array([[3, 3, 3, 3], + [3, 3, 3, 3]], dtype=int8) +array([[3, 3, 3, 3], + [3, 3, 3, 3]], dtype=uint16) +array([[3, 3, 3, 3], + [3, 3, 3, 3]], dtype=int16) +array([[3.0, 3.0, 3.0, 3.0], + [3.0, 3.0, 3.0, 3.0]], dtype=float64) diff --git a/tests/2d/numpy/initialisation.py b/tests/2d/numpy/initialisation.py index cb0af1a5..6136d513 100644 --- a/tests/2d/numpy/initialisation.py +++ b/tests/2d/numpy/initialisation.py @@ -3,100 +3,8 @@ except ImportError: import numpy as np -print("Array creation using ARRAY:") -print(np.array(np.array(range(5), dtype=np.uint8), dtype=np.uint8)) -print(np.array(np.array(range(5), dtype=np.uint8), dtype=np.int8)) -print(np.array(np.array(range(5), dtype=np.uint8), dtype=np.uint16)) -print(np.array(np.array(range(5), dtype=np.uint8), dtype=np.int16)) -print(np.array(np.array(range(5), dtype=np.uint8), dtype=np.float)) -print(np.array(np.array(range(5), dtype=np.int8), dtype=np.uint8)) -print(np.array(np.array(range(5), dtype=np.int8), dtype=np.int8)) -print(np.array(np.array(range(5), dtype=np.int8), dtype=np.uint16)) -print(np.array(np.array(range(5), dtype=np.int8), dtype=np.int16)) -print(np.array(np.array(range(5), dtype=np.int8), dtype=np.float)) -print(np.array(np.array(range(5), dtype=np.uint16), dtype=np.uint8)) -print(np.array(np.array(range(5), dtype=np.uint16), dtype=np.int8)) -print(np.array(np.array(range(5), dtype=np.uint16), dtype=np.uint16)) -print(np.array(np.array(range(5), dtype=np.uint16), dtype=np.int16)) -print(np.array(np.array(range(5), dtype=np.uint16), dtype=np.float)) -print(np.array(np.array(range(5), dtype=np.int16), dtype=np.uint8)) -print(np.array(np.array(range(5), dtype=np.int16), dtype=np.int8)) -print(np.array(np.array(range(5), dtype=np.int16), dtype=np.uint16)) -print(np.array(np.array(range(5), dtype=np.int16), dtype=np.int16)) -print(np.array(np.array(range(5), dtype=np.int16), dtype=np.float)) -print(np.array(np.array(range(5), dtype=np.float), dtype=np.uint8)) -print(np.array(np.array(range(5), dtype=np.float), dtype=np.int8)) -print(np.array(np.array(range(5), dtype=np.float), dtype=np.uint16)) -print(np.array(np.array(range(5), dtype=np.float), dtype=np.int16)) -print(np.array(np.array(range(5), dtype=np.float), dtype=np.float)) -print("Array creation using ARANGE:") -print(np.arange(10)) -print(np.arange(2, 10)) -print(np.arange(2, 10, 3)) -print(np.arange(2, 10, 3, dtype=np.float)) -print("Array concatenation:") -a = np.array([1,2,3], dtype=np.float) -b = np.array([4,5,6], dtype=np.float) -print(np.concatenate((a,b))) -print(np.concatenate((a,b), axis=0)) -a = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.float) -b = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.float) -print(np.concatenate((a,b), axis=0)) -print(np.concatenate((a,b), axis=1)) -print(np.concatenate((b,a), axis=0)) -print(np.concatenate((b,a), axis=1)) -print("Identity array creation:") -print(np.eye(3)) -print(np.eye(3, M=4)) -print(np.eye(3, M=4, k=0)) -print(np.eye(3, M=4, k=-1)) -print(np.eye(3, M=4, k=-2)) -print(np.eye(3, M=4, k=-3)) -print(np.eye(3, M=4, k=1)) -print(np.eye(3, M=4, k=2)) -print(np.eye(3, M=4, k=3)) -print(np.eye(4, M=4)) -print(np.eye(4, M=3, k=0)) -print(np.eye(4, M=3, k=-1)) -print(np.eye(4, M=3, k=-2)) -print(np.eye(4, M=3, k=-3)) -print(np.eye(4, M=3, k=1)) -print(np.eye(4, M=3, k=2)) -print(np.eye(4, M=3, k=3)) -print("Array creation using FULL:") -print(np.full((2, 4), 3, dtype=np.float)) -print(np.full((2, 4), 3, dtype=np.uint8)) -print("Array creation using LINSPACE:") -print(np.linspace(0, 10, num=5)) -print(np.linspace(0, 10, num=5, endpoint=False)) -print(np.linspace(0, 10, num=5, endpoint=True)) -print(np.linspace(0, 10, num=5, endpoint=False, dtype=np.uint8)) -print(np.linspace(0, 10, num=5, endpoint=False, dtype=np.uint16)) -print(np.linspace(0, 10, num=5, endpoint=False, dtype=np.int8)) -print(np.linspace(0, 10, num=5, endpoint=False, dtype=np.int16)) -print("Array creation using LOGSPACE:") -print(np.logspace(0, 10, num=5)) -print(np.logspace(0, 10, num=5, endpoint=False)) -print(np.logspace(0, 10, num=5, endpoint=True)) -print(np.logspace(0, 10, num=5, endpoint=False, dtype=np.uint8)) -print(np.logspace(0, 10, num=5, endpoint=False, dtype=np.uint16)) -print(np.logspace(0, 10, num=5, endpoint=False, dtype=np.int8)) -print(np.logspace(0, 10, num=5, endpoint=False, dtype=np.int16)) -print("Array creation using ZEROS:") -print(np.zeros(3)) -print(np.zeros((3,3))) -print(np.zeros((3,3), dtype=np.uint8)) -print(np.zeros((3,3), dtype=np.uint16)) -print(np.zeros((3,3), dtype=np.int8)) -print(np.zeros((3,3), dtype=np.int16)) -print(np.zeros((4,3), dtype=np.float)) -print(np.zeros((3,4), dtype=np.float)) -print("Array creation using ONES:") -print(np.ones(3)) -print(np.ones((3,3))) -print(np.ones((3,3), dtype=np.uint8)) -print(np.ones((3,3), dtype=np.uint16)) -print(np.ones((3,3), dtype=np.int8)) -print(np.ones((3,3), dtype=np.int16)) -print(np.ones((4,3), dtype=np.float)) -print(np.ones((3,4), dtype=np.float)) +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +for dtype1 in dtypes: + for dtype2 in dtypes: + print(np.array(np.array(range(5), dtype=dtype1), dtype=dtype2)) \ No newline at end of file diff --git a/tests/2d/numpy/initialisation.py.exp b/tests/2d/numpy/initialisation.py.exp index c04548d2..09312c46 100644 --- a/tests/2d/numpy/initialisation.py.exp +++ b/tests/2d/numpy/initialisation.py.exp @@ -1,4 +1,3 @@ -Array creation using ARRAY: array([0, 1, 2, 3, 4], dtype=uint8) array([0, 1, 2, 3, 4], dtype=int8) array([0, 1, 2, 3, 4], dtype=uint16) @@ -24,158 +23,3 @@ array([0, 1, 2, 3, 4], dtype=int8) array([0, 1, 2, 3, 4], dtype=uint16) array([0, 1, 2, 3, 4], dtype=int16) array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64) -Array creation using ARANGE: -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([2.0, 5.0, 8.0], dtype=float64) -Array concatenation: -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], - [4.0, 5.0, 6.0], - [7.0, 8.0, 9.0], - [1.0, 2.0, 3.0], - [4.0, 5.0, 6.0], - [7.0, 8.0, 9.0]], dtype=float64) -array([[1.0, 2.0, 3.0, 1.0, 2.0, 3.0], - [4.0, 5.0, 6.0, 4.0, 5.0, 6.0], - [7.0, 8.0, 9.0, 7.0, 8.0, 9.0]], dtype=float64) -array([[1.0, 2.0, 3.0], - [4.0, 5.0, 6.0], - [7.0, 8.0, 9.0], - [1.0, 2.0, 3.0], - [4.0, 5.0, 6.0], - [7.0, 8.0, 9.0]], dtype=float64) -array([[1.0, 2.0, 3.0, 1.0, 2.0, 3.0], - [4.0, 5.0, 6.0, 4.0, 5.0, 6.0], - [7.0, 8.0, 9.0, 7.0, 8.0, 9.0]], dtype=float64) -Identity array creation: -array([[1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0]], dtype=float64) -array([[1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0]], dtype=float64) -array([[1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0, 0.0], - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - [1.0, 0.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0]], dtype=float64) -array([[0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - [0.0, 0.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0, 1.0], - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0]], dtype=float64) -array([[1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0]], dtype=float64) -array([[1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0], - [0.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0]], dtype=float64) -array([[0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 1.0, 0.0], - [0.0, 0.0, 1.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 1.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0]], dtype=float64) -Array creation using FULL: -array([[3.0, 3.0, 3.0, 3.0], - [3.0, 3.0, 3.0, 3.0]], dtype=float64) -array([[3, 3, 3, 3], - [3, 3, 3, 3]], dtype=uint8) -Array creation using LINSPACE: -array([0.0, 2.5, 5.0, 7.5, 10.0], dtype=float64) -array([0.0, 2.0, 4.0, 6.0, 8.0], dtype=float64) -array([0.0, 2.5, 5.0, 7.5, 10.0], dtype=float64) -array([0, 2, 4, 6, 8], dtype=uint8) -array([0, 2, 4, 6, 8], dtype=uint16) -array([0, 2, 4, 6, 8], dtype=int8) -array([0, 2, 4, 6, 8], dtype=int16) -Array creation using LOGSPACE: -array([1.0, 316.227766016838, 100000.0, 31622776.6016838, 10000000000.0], dtype=float64) -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) -array([1, 100, 16, 64, 0], dtype=uint8) -array([1, 100, 10000, 16960, 57600], dtype=uint16) -array([1, 100, 16, 64, 0], dtype=int8) -array([1, 100, 10000, 16960, -7936], dtype=int16) -Array creation using ZEROS: -array([0.0, 0.0, 0.0], dtype=float64) -array([[0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0]], dtype=float64) -array([[0, 0, 0], - [0, 0, 0], - [0, 0, 0]], dtype=uint8) -array([[0, 0, 0], - [0, 0, 0], - [0, 0, 0]], dtype=uint16) -array([[0, 0, 0], - [0, 0, 0], - [0, 0, 0]], dtype=int8) -array([[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.0, 0.0, 0.0]], dtype=float64) -array([[0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0]], dtype=float64) -Array creation using ONES: -array([1.0, 1.0, 1.0], dtype=float64) -array([[1.0, 1.0, 1.0], - [1.0, 1.0, 1.0], - [1.0, 1.0, 1.0]], dtype=float64) -array([[1, 1, 1], - [1, 1, 1], - [1, 1, 1]], dtype=uint8) -array([[1, 1, 1], - [1, 1, 1], - [1, 1, 1]], dtype=uint16) -array([[1, 1, 1], - [1, 1, 1], - [1, 1, 1]], dtype=int8) -array([[1, 1, 1], - [1, 1, 1], - [1, 1, 1]], dtype=int16) -array([[1.0, 1.0, 1.0], - [1.0, 1.0, 1.0], - [1.0, 1.0, 1.0], - [1.0, 1.0, 1.0]], dtype=float64) -array([[1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0]], dtype=float64) diff --git a/tests/2d/numpy/linspace.py b/tests/2d/numpy/linspace.py new file mode 100644 index 00000000..c97199a2 --- /dev/null +++ b/tests/2d/numpy/linspace.py @@ -0,0 +1,10 @@ +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(np.linspace(0, 10, num=5, dtype=dtype)) + print(np.linspace(0, 10, num=5, endpoint=True, dtype=dtype)) diff --git a/tests/2d/numpy/linspace.py.exp b/tests/2d/numpy/linspace.py.exp new file mode 100644 index 00000000..2b95990a --- /dev/null +++ b/tests/2d/numpy/linspace.py.exp @@ -0,0 +1,10 @@ +array([0, 2, 5, 7, 10], dtype=uint8) +array([0, 2, 5, 7, 10], dtype=uint8) +array([0, 2, 5, 7, 10], dtype=int8) +array([0, 2, 5, 7, 10], dtype=int8) +array([0, 2, 5, 7, 10], dtype=uint16) +array([0, 2, 5, 7, 10], dtype=uint16) +array([0, 2, 5, 7, 10], dtype=int16) +array([0, 2, 5, 7, 10], dtype=int16) +array([0.0, 2.5, 5.0, 7.5, 10.0], dtype=float64) +array([0.0, 2.5, 5.0, 7.5, 10.0], dtype=float64) diff --git a/tests/2d/numpy/logspace.py b/tests/2d/numpy/logspace.py new file mode 100644 index 00000000..e6f2047b --- /dev/null +++ b/tests/2d/numpy/logspace.py @@ -0,0 +1,10 @@ +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(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 diff --git a/tests/2d/numpy/logspace.py.exp b/tests/2d/numpy/logspace.py.exp new file mode 100644 index 00000000..1a09cef4 --- /dev/null +++ b/tests/2d/numpy/logspace.py.exp @@ -0,0 +1,10 @@ +array([1, 100, 16, 64, 0], dtype=uint8) +array([1, 60, 160, 120, 0], dtype=uint8) +array([1, 100, 16, 64, 0], dtype=int8) +array([1, 60, -96, 120, 0], dtype=int8) +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) diff --git a/tests/2d/numpy/ones.py b/tests/2d/numpy/ones.py new file mode 100644 index 00000000..f0aee868 --- /dev/null +++ b/tests/2d/numpy/ones.py @@ -0,0 +1,13 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +print(np.ones(3)) +print(np.ones((3,3))) + +for dtype in dtypes: + print(np.ones((3,3), dtype=dtype)) + print(np.ones((4,2), dtype=dtype)) diff --git a/tests/2d/numpy/ones.py.exp b/tests/2d/numpy/ones.py.exp new file mode 100644 index 00000000..9e66f3cc --- /dev/null +++ b/tests/2d/numpy/ones.py.exp @@ -0,0 +1,39 @@ +array([1.0, 1.0, 1.0], dtype=float64) +array([[1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0]], dtype=float64) +array([[1, 1, 1], + [1, 1, 1], + [1, 1, 1]], dtype=uint8) +array([[1, 1], + [1, 1], + [1, 1], + [1, 1]], dtype=uint8) +array([[1, 1, 1], + [1, 1, 1], + [1, 1, 1]], dtype=int8) +array([[1, 1], + [1, 1], + [1, 1], + [1, 1]], dtype=int8) +array([[1, 1, 1], + [1, 1, 1], + [1, 1, 1]], dtype=uint16) +array([[1, 1], + [1, 1], + [1, 1], + [1, 1]], dtype=uint16) +array([[1, 1, 1], + [1, 1, 1], + [1, 1, 1]], dtype=int16) +array([[1, 1], + [1, 1], + [1, 1], + [1, 1]], dtype=int16) +array([[1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0]], dtype=float64) +array([[1.0, 1.0], + [1.0, 1.0], + [1.0, 1.0], + [1.0, 1.0]], dtype=float64) diff --git a/tests/2d/numpy/zeros.py b/tests/2d/numpy/zeros.py new file mode 100644 index 00000000..af9bd0f4 --- /dev/null +++ b/tests/2d/numpy/zeros.py @@ -0,0 +1,13 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float) + +print(np.zeros(3)) +print(np.zeros((3,3))) + +for dtype in dtypes: + print(np.zeros((3,3), dtype=dtype)) + print(np.zeros((4,2), dtype=dtype)) diff --git a/tests/2d/numpy/zeros.py.exp b/tests/2d/numpy/zeros.py.exp new file mode 100644 index 00000000..ec61a9ca --- /dev/null +++ b/tests/2d/numpy/zeros.py.exp @@ -0,0 +1,39 @@ +array([0.0, 0.0, 0.0], dtype=float64) +array([[0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0]], dtype=float64) +array([[0, 0, 0], + [0, 0, 0], + [0, 0, 0]], dtype=uint8) +array([[0, 0], + [0, 0], + [0, 0], + [0, 0]], dtype=uint8) +array([[0, 0, 0], + [0, 0, 0], + [0, 0, 0]], dtype=int8) +array([[0, 0], + [0, 0], + [0, 0], + [0, 0]], dtype=int8) +array([[0, 0, 0], + [0, 0, 0], + [0, 0, 0]], dtype=uint16) +array([[0, 0], + [0, 0], + [0, 0], + [0, 0]], dtype=uint16) +array([[0, 0, 0], + [0, 0, 0], + [0, 0, 0]], dtype=int16) +array([[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]], dtype=float64) +array([[0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0]], dtype=float64) From ecaa355bab4980848aef219264813422fb12cf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 7 Jan 2022 12:47:42 +0100 Subject: [PATCH 54/58] fix sort_complex, add tests to sort_complex, conjugate --- code/numpy/carray/carray.c | 93 ++++++++++++++++++++-------- code/numpy/carray/carray.h | 32 +++++----- tests/2d/complex/conjugate.py | 12 ++++ tests/2d/complex/conjugate.py.exp | 7 +++ tests/2d/complex/sort_complex.py | 26 ++++++++ tests/2d/complex/sort_complex.py.exp | 12 ++++ 6 files changed, 140 insertions(+), 42 deletions(-) create mode 100644 tests/2d/complex/conjugate.py create mode 100644 tests/2d/complex/conjugate.py.exp create mode 100644 tests/2d/complex/sort_complex.py create mode 100644 tests/2d/complex/sort_complex.py.exp diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index 4e7d84df..b6bf4c44 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -25,6 +25,15 @@ #if ULAB_SUPPORTS_COMPLEX +//| import ulab.numpy + +//| def real(val): +//| """ +//| Return the real part of the complex argument, which can be +//| either an ndarray, or a scalar.""" +//| ... +//| + mp_obj_t carray_real(mp_obj_t _source) { if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); @@ -45,6 +54,13 @@ mp_obj_t carray_real(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real); +//| def imag(val): +//| """ +//| Return the imaginary part of the complex argument, which can be +//| either an ndarray, or a scalar.""" +//| ... +//| + mp_obj_t carray_imag(mp_obj_t _source) { if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); @@ -65,6 +81,13 @@ mp_obj_t carray_imag(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag); #if ULAB_NUMPY_HAS_CONJUGATE + +//| def conjugate(val): +//| """ +//| Return the conjugate of the complex argument, which can be +//| either an ndarray, or a scalar.""" +//| ... +//| mp_obj_t carray_conjugate(mp_obj_t _source) { if(mp_obj_is_type(_source, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(_source); @@ -99,6 +122,16 @@ MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate); #endif #if ULAB_NUMPY_HAS_SORT_COMPLEX +//| def sort_complex(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray: +//| """ +//| .. param: a +//| a one-dimensional ndarray +//| +//| Sort a complex array using the real part first, then the imaginary part. +//| Always returns a sorted complex array, even if the input was real.""" +//| ... +//| + static void carray_sort_complex_(mp_float_t *array, size_t len) { // array is assumed to be a floating vector containing the real and imaginary parts // of a complex array at alternating positions as @@ -134,7 +167,7 @@ static void carray_sort_complex_(mp_float_t *array, size_t len) { } } if((array[2 * c] > real) || - ((array[2 * c] == real) && (array[2 * c + 1] == imag))) { + ((array[2 * c] == real) && (array[2 * c + 1] > imag))) { array[2 * p] = array[2 * c]; // real part array[2 * p + 1] = array[2 * c + 1]; // imag part p = c; @@ -166,6 +199,15 @@ mp_obj_t carray_sort_complex(mp_obj_t _source) { MP_DEFINE_CONST_FUN_OBJ_1(carray_sort_complex_obj, carray_sort_complex); #endif +//| def abs(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray: +//| """ +//| .. param: a +//| a one-dimensional ndarray +//| +//| Return the absolute value of complex ndarray.""" +//| ... +//| + mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) { // calculates the absolute value of a complex array and returns a dense array uint8_t *sarray = (uint8_t *)source->array; @@ -257,22 +299,6 @@ static void carray_copy_part(uint8_t *tarray, uint8_t *sarray, size_t *shape, in #endif /* ULAB_MAX_DIMS > 3 */ } -static void carray_binary_add_(ndarray_obj_t *results, mp_float_t *resarray, mp_float_t *larray, uint8_t *rarray, - int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) { - - if(rdtype == NDARRAY_UINT8) { - BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, +); - } else if(rdtype == NDARRAY_INT8) { - BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, +); - } else if(rdtype == NDARRAY_UINT16) { - BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, +); - } else if(rdtype == NDARRAY_INT16) { - BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, +); - } else if(rdtype == NDARRAY_FLOAT) { - BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, +); - } -} - mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { @@ -333,19 +359,34 @@ mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } while(i < results->shape[ULAB_MAX_DIMS - 4]); #endif /* ULAB_MAX_DIMS > 3 */ } else { // only one of the operands is complex - mp_float_t *larray; - uint8_t *rarray; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; // align the complex array to the left - if(lhs->dtype == NDARRAY_COMPLEX) { - larray = (mp_float_t *)lhs->array; - rarray = (uint8_t *)rhs->array; - carray_binary_add_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype); - } else { - larray = (mp_float_t *)rhs->array; + uint8_t rdtype = rhs->dtype; + int32_t *lstrides_ = lstrides; + int32_t *rstrides_ = rstrides; + + if(rhs->dtype == NDARRAY_COMPLEX) { + larray = (uint8_t *)rhs->array; rarray = (uint8_t *)lhs->array; - carray_binary_add_(results, resarray, larray, rarray, rstrides, lstrides, lhs->dtype); + lstrides_ = rstrides; + rstrides_ = lstrides; + rdtype = lhs->dtype; } + + if(rdtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides_, rarray, rstrides_, +); + } else if(rdtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides_, rarray, rstrides_, +); + } else if(rdtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides_, rarray, rstrides_, +); + } else if(rdtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides_, rarray, rstrides_, +); + } else if(rdtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides_, rarray, rstrides_, +); + } + // simply copy the imaginary part uint8_t *tarray = (uint8_t *)results->array; tarray += sizeof(mp_float_t); diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index fec6e4cb..aa300496 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -41,9 +41,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t k = 0;\ do {\ BINARY_LOOP_COMPLEX1((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (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 - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ k++;\ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ @@ -52,9 +52,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t j = 0;\ do {\ BINARY_LOOP_COMPLEX2((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (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 - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ j++;\ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ @@ -63,9 +63,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t i = 0;\ do {\ BINARY_LOOP_COMPLEX3((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (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 - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ @@ -95,9 +95,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t j = 0;\ do {\ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (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 - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ j++;\ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ @@ -106,9 +106,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t i = 0;\ do {\ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (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 - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ @@ -131,9 +131,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t k = 0;\ do {\ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (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 - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ k++;\ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ @@ -142,9 +142,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t j = 0;\ do {\ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (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 - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ j++;\ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ @@ -153,9 +153,9 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t size_t i = 0;\ do {\ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\ - (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (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 - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ diff --git a/tests/2d/complex/conjugate.py b/tests/2d/complex/conjugate.py new file mode 100644 index 00000000..eafaf574 --- /dev/null +++ b/tests/2d/complex/conjugate.py @@ -0,0 +1,12 @@ +try: + from ulab import numpy as np +except: + import numpy as np + +dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float, np.complex) + +for dtype in dtypes: + print(np.conjugate(np.array(range(5), dtype=dtype))) + +a = np.array([1, 2+2j, 3-3j, 4j], dtype=np.complex) +print(np.conjugate(a)) \ No newline at end of file diff --git a/tests/2d/complex/conjugate.py.exp b/tests/2d/complex/conjugate.py.exp new file mode 100644 index 00000000..4f9a8bb4 --- /dev/null +++ b/tests/2d/complex/conjugate.py.exp @@ -0,0 +1,7 @@ +array([0, 1, 2, 3, 4], dtype=uint8) +array([0, 1, 2, 3, 4], dtype=int8) +array([0, 1, 2, 3, 4], dtype=uint16) +array([0, 1, 2, 3, 4], dtype=int16) +array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64) +array([0.0+-0.0j, 1.0+-0.0j, 2.0+-0.0j, 3.0+-0.0j, 4.0+-0.0j], dtype=complex) +array([1.0+-0.0j, 2.0-2.0j, 3.0+3.0j, 0.0-4.0j], dtype=complex) diff --git a/tests/2d/complex/sort_complex.py b/tests/2d/complex/sort_complex.py new file mode 100644 index 00000000..1ac1edc1 --- /dev/null +++ b/tests/2d/complex/sort_complex.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, np.complex) + +for dtype in dtypes: + print(np.sort_complex(np.array(range(5, 0, -1), dtype=dtype))) + +print() +n = 6 +a = np.array(range(n, 0, -1)) * 1j +b = np.array([1] * n) +print(np.sort_complex(a + b)) + +a = np.array(range(n)) * 1j +b = np.array([1] * n) +print(np.sort_complex(a + b)) + +print() +a = np.array([0, -3j, 1+2j, 1-2j, 2j], dtype=np.complex) +print(np.sort_complex(a)) + +a = np.array([0, 3j, 1-2j, 1+2j, -2j], dtype=np.complex) +print(np.sort_complex(a)) diff --git a/tests/2d/complex/sort_complex.py.exp b/tests/2d/complex/sort_complex.py.exp new file mode 100644 index 00000000..9026e4ae --- /dev/null +++ b/tests/2d/complex/sort_complex.py.exp @@ -0,0 +1,12 @@ +array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], 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([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], 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([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], 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([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) + +array([-0.0-3.0j, 0.0+0.0j, 0.0+2.0j, 1.0-2.0j, 1.0+2.0j], dtype=complex) +array([-0.0-2.0j, 0.0+0.0j, 0.0+3.0j, 1.0-2.0j, 1.0+2.0j], dtype=complex) From 969afdec7f12868a5d4ce5c168273a0c8baf9e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 7 Jan 2022 14:05:38 +0100 Subject: [PATCH 55/58] implement ==, and != for complex dtypes --- code/ndarray.c | 13 +++- code/ndarray_operators.c | 6 ++ code/numpy/carray/carray.c | 98 +++++++++++++++++++++++++++++++ code/numpy/carray/carray.h | 52 +++++++++++++++- tests/2d/complex/binary_op.py | 26 ++++++++ tests/2d/complex/binary_op.py.exp | 21 +++++++ 6 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 tests/2d/complex/binary_op.py create mode 100644 tests/2d/complex/binary_op.py.exp diff --git a/code/ndarray.c b/code/ndarray.c index e5ece2de..f8caa672 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -26,6 +26,7 @@ #include "ndarray.h" #include "ndarray_operators.h" #include "numpy/carray/carray.h" +#include "numpy/carray/carray_tools.h" mp_uint_t ndarray_print_threshold = NDARRAY_PRINT_THRESHOLD; mp_uint_t ndarray_print_edgeitems = NDARRAY_PRINT_EDGEITEMS; @@ -1901,7 +1902,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { case MP_BINARY_OP_EQUAL: case MP_BINARY_OP_NOT_EQUAL: nd = ndarray_new_linear_array(0, NDARRAY_UINT8); - nd->boolean = true; + nd->boolean = 1; return MP_OBJ_FROM_PTR(nd); default: @@ -1914,26 +1915,31 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { // first the in-place operators #if NDARRAY_HAS_INPLACE_ADD case MP_BINARY_OP_INPLACE_ADD: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_inplace_ams(lhs, rhs, rstrides, op); break; #endif #if NDARRAY_HAS_INPLACE_MULTIPLY case MP_BINARY_OP_INPLACE_MULTIPLY: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_inplace_ams(lhs, rhs, rstrides, op); break; #endif #if NDARRAY_HAS_INPLACE_POWER case MP_BINARY_OP_INPLACE_POWER: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_inplace_power(lhs, rhs, rstrides); break; #endif #if NDARRAY_HAS_INPLACE_SUBTRACT case MP_BINARY_OP_INPLACE_SUBTRACT: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_inplace_ams(lhs, rhs, rstrides, op); break; #endif #if NDARRAY_HAS_INPLACE_TRUE_DIVIDE case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_inplace_divide(lhs, rhs, rstrides); break; #endif @@ -1941,12 +1947,14 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { #if NDARRAY_HAS_BINARY_OP_LESS case MP_BINARY_OP_LESS: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); // here we simply swap the operands return ndarray_binary_more(rhs, lhs, ndim, shape, rstrides, lstrides, MP_BINARY_OP_MORE); break; #endif #if NDARRAY_HAS_BINARY_OP_LESS_EQUAL case MP_BINARY_OP_LESS_EQUAL: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); // here we simply swap the operands return ndarray_binary_more(rhs, lhs, ndim, shape, rstrides, lstrides, MP_BINARY_OP_MORE_EQUAL); break; @@ -1973,11 +1981,13 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { #endif #if NDARRAY_HAS_BINARY_OP_MORE case MP_BINARY_OP_MORE: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_binary_more(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_MORE); break; #endif #if NDARRAY_HAS_BINARY_OP_MORE_EQUAL case MP_BINARY_OP_MORE_EQUAL: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_binary_more(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_MORE_EQUAL); break; #endif @@ -1993,6 +2003,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { #endif #if NDARRAY_HAS_BINARY_OP_POWER case MP_BINARY_OP_POWER: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); return ndarray_binary_power(lhs, rhs, ndim, shape, lstrides, rstrides); break; #endif diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index f3b87613..de1042cc 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -41,6 +41,12 @@ mp_obj_t ndarray_binary_equality(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)) { + return carray_binary_equal_not_equal(lhs, rhs, ndim, shape, lstrides, rstrides, op); + } + #endif + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); results->boolean = 1; uint8_t *array = (uint8_t *)results->array; diff --git a/code/numpy/carray/carray.c b/code/numpy/carray/carray.c index b6bf4c44..a5f8a2b1 100644 --- a/code/numpy/carray/carray.c +++ b/code/numpy/carray/carray.c @@ -299,6 +299,104 @@ static void carray_copy_part(uint8_t *tarray, uint8_t *sarray, size_t *shape, in #endif /* ULAB_MAX_DIMS > 3 */ } +mp_obj_t carray_binary_equal_not_equal(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) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + results->boolean = 1; + uint8_t *array = (uint8_t *)results->array; + + if(op == MP_BINARY_OP_NOT_EQUAL) { + memset(array, 1, results->len); + } + + if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) { + mp_float_t *larray = (mp_float_t *)lhs->array; + mp_float_t *rarray = (mp_float_t *)rhs->array; + + ulab_rescale_float_strides(lstrides); + ulab_rescale_float_strides(rstrides); + + #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((larray[0] == rarray[0]) && (larray[1] == rarray[1])) { + *array ^= 0x01; + } + array++; + 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 + 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 > 1 */ + #if 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 > 2 */ + #if 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 > 3 */ + } else { // only one of the operands is complex + mp_float_t *larray = (mp_float_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + // align the complex array to the left + uint8_t rdtype = rhs->dtype; + int32_t *lstrides_ = lstrides; + int32_t *rstrides_ = rstrides; + + if(rhs->dtype == NDARRAY_COMPLEX) { + larray = (mp_float_t *)rhs->array; + rarray = (uint8_t *)lhs->array; + lstrides_ = rstrides; + rstrides_ = lstrides; + rdtype = lhs->dtype; + } + + ulab_rescale_float_strides(lstrides_); + + if(rdtype == NDARRAY_UINT8) { + BINARY_LOOP_COMPLEX_EQUAL(results, array, uint8_t, larray, lstrides_, rarray, rstrides_); + } else if(rdtype == NDARRAY_INT8) { + BINARY_LOOP_COMPLEX_EQUAL(results, array, int8_t, larray, lstrides_, rarray, rstrides_); + } else if(rdtype == NDARRAY_UINT16) { + BINARY_LOOP_COMPLEX_EQUAL(results, array, uint16_t, larray, lstrides_, rarray, rstrides_); + } else if(rdtype == NDARRAY_INT16) { + BINARY_LOOP_COMPLEX_EQUAL(results, array, int16_t, larray, lstrides_, rarray, rstrides_); + } else if(rdtype == NDARRAY_FLOAT) { + BINARY_LOOP_COMPLEX_EQUAL(results, array, mp_float_t, larray, lstrides_, rarray, rstrides_); + } + } + return MP_OBJ_FROM_PTR(results); +} + mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { diff --git a/code/numpy/carray/carray.h b/code/numpy/carray/carray.h index aa300496..8ca5de2d 100644 --- a/code/numpy/carray/carray.h +++ b/code/numpy/carray/carray.h @@ -26,6 +26,7 @@ mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); #define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ size_t l = 0;\ @@ -113,7 +114,6 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ - #define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\ size_t l = 0;\ do {\ @@ -160,28 +160,78 @@ mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t i++;\ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#define BINARY_LOOP_COMPLEX_EQUAL1(results, array, type_right, larray, lstrides, rarray, rstrides)\ + size_t l = 0;\ + do {\ + if((*(larray) == *((type_right *)(rarray))) && ((larray)[1] == MICROPY_FLOAT_CONST(0.0))) {\ + *(array) ^= 0x01;\ + }\ + (array)++;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#define BINARY_LOOP_COMPLEX_EQUAL2(results, array, type_right, larray, lstrides, rarray, rstrides)\ + size_t k = 0;\ + do {\ + BINARY_LOOP_COMPLEX_EQUAL1((results), (array), 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];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#define BINARY_LOOP_COMPLEX_EQUAL3(results, array, type_right, larray, lstrides, rarray, rstrides)\ + size_t j = 0;\ + do {\ + BINARY_LOOP_COMPLEX_EQUAL2((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\ + (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]);\ + +#define BINARY_LOOP_COMPLEX_EQUAL4(results, array, type_right, larray, lstrides, rarray, rstrides)\ + size_t i = 0;\ + do {\ + BINARY_LOOP_COMPLEX_EQUAL3((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\ + (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]);\ + #if ULAB_MAX_DIMS == 1 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1 #define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1 +#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL1 #endif /* ULAB_MAX_DIMS == 1 */ #if ULAB_MAX_DIMS == 2 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2 #define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2 +#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL2 #endif /* ULAB_MAX_DIMS == 2 */ #if ULAB_MAX_DIMS == 3 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3 #define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3 +#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL3 #endif /* ULAB_MAX_DIMS == 3 */ #if ULAB_MAX_DIMS == 4 #define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4 #define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4 #define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4 +#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL4 #endif /* ULAB_MAX_DIMS == 4 */ #endif diff --git a/tests/2d/complex/binary_op.py b/tests/2d/complex/binary_op.py new file mode 100644 index 00000000..36efa76f --- /dev/null +++ b/tests/2d/complex/binary_op.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) + +n = 5 +a = np.array(range(n), dtype=np.complex) +c = np.array(range(n), dtype=np.complex) + +print(a == c) +print(a != c) +print() + +c = np.array(range(n), dtype=np.complex) * 1j +print(a == c) +print(a != c) +print() + +for dtype in dtypes: + b = np.array(range(n), dtype=dtype) + print(b == a) + print(b != a) + print() + diff --git a/tests/2d/complex/binary_op.py.exp b/tests/2d/complex/binary_op.py.exp new file mode 100644 index 00000000..ef92f161 --- /dev/null +++ b/tests/2d/complex/binary_op.py.exp @@ -0,0 +1,21 @@ +array([True, True, True, True, True], dtype=bool) +array([False, False, False, False, False], dtype=bool) + +array([True, False, False, False, False], dtype=bool) +array([False, True, True, True, True], dtype=bool) + +array([True, True, True, True, True], dtype=bool) +array([False, False, False, False, False], dtype=bool) + +array([True, True, True, True, True], dtype=bool) +array([False, False, False, False, False], dtype=bool) + +array([True, True, True, True, True], dtype=bool) +array([False, False, False, False, False], dtype=bool) + +array([True, True, True, True, True], dtype=bool) +array([False, False, False, False, False], dtype=bool) + +array([True, True, True, True, True], dtype=bool) +array([False, False, False, False, False], dtype=bool) + From 6609afd006ce8012477621f5e9af89be491c21ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 7 Jan 2022 20:43:14 +0100 Subject: [PATCH 56/58] update documentation --- README.md | 3 +- docs/manual/source/conf.py | 4 +- docs/manual/source/numpy-fft.rst | 36 +++ docs/manual/source/numpy-functions.rst | 223 +++++++++++++--- docs/manual/source/numpy-universal.rst | 85 ++++++- docs/manual/source/scipy-signal.rst | 4 +- docs/manual/source/ulab-intro.rst | 65 +++-- docs/manual/source/ulab-ndarray.rst | 175 +++++++++++-- docs/numpy-fft.ipynb | 40 ++- docs/numpy-functions.ipynb | 250 ++++++++++++++++-- docs/numpy-universal.ipynb | 112 +++++++- docs/scipy-signal.ipynb | 16 +- docs/ulab-convert.ipynb | 165 ++++++------ docs/ulab-intro.ipynb | 71 +++++- docs/ulab-ndarray.ipynb | 338 ++++++++++++++++++++----- docs/ulab-numerical.ipynb | 14 +- 16 files changed, 1324 insertions(+), 277 deletions(-) diff --git a/README.md b/README.md index 0346d897..885e635b 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,8 @@ In addition, `ulab` includes [universal functions](https://micropython-ulab.read The [`utils`](https://micropython-ulab.readthedocs.io/en/latest/ulab-utils.html) module contains functions for interfacing with peripheral devices supporting the buffer protocol. These functions do not have an obvious -`numpy` equivalent, but share a similar programming interface, and allow direct data input to and output from +`numpy` equivalent, but share a similar programming interface, and allow direct data input-output between +numerical arrays and hardware components. ## `user` module diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 75129213..5c7b7dce 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-2021, Zoltán Vörös and contributors' +copyright = '2019-2022, Zoltán Vörös and contributors' author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '3.3.4' +release = '4.0.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/numpy-fft.rst b/docs/manual/source/numpy-fft.rst index aff6d038..7da9b60e 100644 --- a/docs/manual/source/numpy-fft.rst +++ b/docs/manual/source/numpy-fft.rst @@ -74,6 +74,35 @@ parts of the transform separately. +ulab with complex support +~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the ``ULAB_SUPPORTS_COMPLEX``, and ``ULAB_FFT_IS_NUMPY_COMPATIBLE`` +pre-processor constants are set to 1 in +`ulab.h `__ +as + +.. code:: c + + // Adds support for complex ndarrays + #ifndef ULAB_SUPPORTS_COMPLEX + #define ULAB_SUPPORTS_COMPLEX (1) + #endif + +.. code:: c + + #ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE + #define ULAB_FFT_IS_NUMPY_COMPATIBLE (1) + #endif + +then the FFT routine will behave in a ``numpy``-compatible way: the +single input array can either be real, in which case the imaginary part +is assumed to be zero, or complex. The output is also complex. + +While ``numpy``-compatibility might be a desired feature, it has one +side effect, namely, the FFT routine consumes approx. 50% more RAM. The +reason for this lies in the implementation details. + ifft ---- @@ -115,6 +144,13 @@ Note that unlike in ``numpy``, the length of the array on which the Fourier transform is carried out must be a power of 2. If this is not the case, the function raises a ``ValueError`` exception. +ulab with complex support +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``fft.ifft`` function can also be made ``numpy``-compatible by +setting the ``ULAB_SUPPORTS_COMPLEX``, and +``ULAB_FFT_IS_NUMPY_COMPATIBLE`` pre-processor constants to 1. + Computation and storage costs ----------------------------- diff --git a/docs/manual/source/numpy-functions.rst b/docs/manual/source/numpy-functions.rst index dcab4130..de0987f3 100644 --- a/docs/manual/source/numpy-functions.rst +++ b/docs/manual/source/numpy-functions.rst @@ -3,38 +3,43 @@ Numpy functions =============== This section of the manual discusses those functions that were adapted -from ``numpy``. +from ``numpy``. Starred functions accept complex arrays as arguments, if +the firmware was compiled with complex support. -1. `numpy.all <#all>`__ -2. `numpy.any <#any>`__ +1. `numpy.all\* <#all>`__ +2. `numpy.any\* <#any>`__ 3. `numpy.argmax <#argmax>`__ 4. `numpy.argmin <#argmin>`__ 5. `numpy.argsort <#argsort>`__ 6. `numpy.clip <#clip>`__ -7. `numpy.convolve <#convolve>`__ -8. `numpy.diff <#diff>`__ -9. `numpy.dot <#dot>`__ -10. `numpy.equal <#equal>`__ -11. `numpy.flip <#flip>`__ -12. `numpy.interp <#interp>`__ -13. `numpy.isfinite <#isfinite>`__ -14. `numpy.isinf <#isinf>`__ -15. `numpy.max <#max>`__ -16. `numpy.maximum <#maximum>`__ -17. `numpy.mean <#mean>`__ -18. `numpy.median <#median>`__ -19. `numpy.min <#min>`__ -20. `numpy.minimum <#minimum>`__ -21. `numpy.not_equal <#equal>`__ -22. `numpy.polyfit <#polyfit>`__ -23. `numpy.polyval <#polyval>`__ -24. `numpy.roll <#roll>`__ -25. `numpy.sort <#sort>`__ -26. `numpy.std <#std>`__ -27. `numpy.sum <#sum>`__ -28. `numpy.trace <#trace>`__ -29. `numpy.trapz <#trapz>`__ -30. `numpy.where <#where>`__ +7. `numpy.conjugate\* <#conjugate>`__ +8. `numpy.convolve\* <#convolve>`__ +9. `numpy.diff <#diff>`__ +10. `numpy.dot <#dot>`__ +11. `numpy.equal <#equal>`__ +12. `numpy.flip\* <#flip>`__ +13. `numpy.imag\* <#imag>`__ +14. `numpy.interp <#interp>`__ +15. `numpy.isfinite <#isfinite>`__ +16. `numpy.isinf <#isinf>`__ +17. `numpy.max <#max>`__ +18. `numpy.maximum <#maximum>`__ +19. `numpy.mean <#mean>`__ +20. `numpy.median <#median>`__ +21. `numpy.min <#min>`__ +22. `numpy.minimum <#minimum>`__ +23. `numpy.not_equal <#equal>`__ +24. `numpy.polyfit <#polyfit>`__ +25. `numpy.polyval <#polyval>`__ +26. `numpy.real\* <#real>`__ +27. `numpy.roll <#roll>`__ +28. `numpy.sort <#sort>`__ +29. `numpy.sort_complex\* <#sort_complex>`__ +30. `numpy.std <#std>`__ +31. `numpy.sum <#sum>`__ +32. `numpy.trace <#trace>`__ +33. `numpy.trapz <#trapz>`__ +34. `numpy.where <#where>`__ all --- @@ -51,6 +56,9 @@ Elements of an array evaluate to ``True``, if they are not equal to zero, or the Boolean ``False``. The return value if a Boolean ``ndarray``. +If the firmware was compiled with complex support, the function can +accept complex arrays. + .. code:: # code to be run in micropython @@ -105,6 +113,9 @@ Elements of an array evaluate to ``True``, if they are not equal to zero, or the Boolean ``False``. The return value if a Boolean ``ndarray``. +If the firmware was compiled with complex support, the function can +accept complex arrays. + .. code:: # code to be run in micropython @@ -295,6 +306,43 @@ the output is upcast as in `Binary operators <#Binary-operators>`__. +conjugate +--------- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.conjugate.html + +If the firmware was compiled with complex support, the function +calculates the complex conjugate of the input array. If the input array +is of real ``dtype``, then the output is simply a copy, preserving the +``dtype``. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, 2, 3, 4], dtype=np.uint8) + b = np.array([1+1j, 2-2j, 3+3j, 4-4j], dtype=np.complex) + + print('a:\t\t', a) + print('conjugate(a):\t', np.conjugate(a)) + print() + print('b:\t\t', b) + print('conjugate(b):\t', np.conjugate(b)) + +.. parsed-literal:: + + a: array([1, 2, 3, 4], dtype=uint8) + conjugate(a): array([1, 2, 3, 4], dtype=uint8) + + b: array([1.0+1.0j, 2.0-2.0j, 3.0+3.0j, 4.0-4.0j], dtype=complex) + conjugate(b): array([1.0-1.0j, 2.0+2.0j, 3.0-3.0j, 4.0+4.0j], dtype=complex) + + + + convolve -------- @@ -307,6 +355,9 @@ Only the ``full`` mode is supported, and the ``mode`` named parameter is not accepted. Note that all other modes can be had by slicing a ``full`` result. +If the firmware was compiled with complex support, the function can +accept complex arrays. + .. code:: # code to be run in micropython @@ -544,6 +595,9 @@ along the given axis. If the keyword argument is ``None``, the matrix’ entries are flipped along all axes. ``flip`` returns a new copy of the array. +If the firmware was compiled with complex support, the function can +accept complex arrays. + .. code:: # code to be run in micropython @@ -580,6 +634,41 @@ array. +imag +---- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.imag.html + +The ``imag`` function returns the imaginary part of an array, or scalar. +It cannot accept a generic iterable as its argument. The function is +defined only, if the firmware was compiled with complex support. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, 2, 3], dtype=np.uint16) + print("a:\t\t", a) + print("imag(a):\t", np.imag(a)) + + b = np.array([1, 2+1j, 3-1j], dtype=np.complex) + print("\nb:\t\t", b) + print("imag(b):\t", np.imag(b)) + +.. parsed-literal:: + + a: array([1, 2, 3], dtype=uint16) + imag(a): array([0, 0, 0], dtype=uint16) + + b: array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex) + imag(b): array([0.0, 1.0, -1.0], dtype=float64) + + + + interp ------ @@ -1059,6 +1148,41 @@ iterables returning scalars. +real +---- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.real.html + +The ``real`` function returns the real part of an array, or scalar. It +cannot accept a generic iterable as its argument. The function is +defined only, if the firmware was compiled with complex support. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, 2, 3], dtype=np.uint16) + print("a:\t\t", a) + print("real(a):\t", np.real(a)) + + b = np.array([1, 2+1j, 3-1j], dtype=np.complex) + print("\nb:\t\t", b) + print("real(b):\t", np.real(b)) + +.. parsed-literal:: + + a: array([1, 2, 3], dtype=uint16) + real(a): array([1, 2, 3], dtype=uint16) + + b: array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex) + real(b): array([1.0, 2.0, 3.0], dtype=float64) + + + + roll ---- @@ -1237,18 +1361,53 @@ spaced numbers between 0, and two pi, and sort them: # code to be run in micropython - import ulab as np - from ulab import vector - from ulab import numerical + from ulab import numpy as np @timeit def sort_time(array): - return numerical.sort(array) + return nup.sort(array) - b = vector.sin(np.linspace(0, 6.28, num=1000)) + b = np.sin(np.linspace(0, 6.28, num=1000)) print('b: ', b) sort_time(b) print('\nb sorted:\n', b) +sort_complex +------------ + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.sort_complex.html + +If the firmware was compiled with complex support, the functions sorts +the input array first according to its real part, and then the imaginary +part. The input must be a one-dimensional array. The output is always of +``dtype`` complex, even if the input was real integer. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([5, 4, 3, 2, 1], dtype=np.int16) + print('a:\t\t\t', a) + print('sort_complex(a):\t', np.sort_complex(a)) + print() + + b = np.array([5, 4+3j, 4-2j, 0, 1j], dtype=np.complex) + print('b:\t\t\t', b) + print('sort_complex(b):\t', np.sort_complex(b)) + +.. parsed-literal:: + + a: array([5, 4, 3, 2, 1], dtype=int16) + sort_complex(a): array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex) + + b: array([5.0+0.0j, 4.0+3.0j, 4.0-2.0j, 0.0+0.0j, 0.0+1.0j], dtype=complex) + sort_complex(b): array([0.0+0.0j, 0.0+1.0j, 4.0-2.0j, 4.0+3.0j, 5.0+0.0j], dtype=complex) + + + + std --- diff --git a/docs/manual/source/numpy-universal.rst b/docs/manual/source/numpy-universal.rst index f0a5477d..b9b7f9f1 100644 --- a/docs/manual/source/numpy-universal.rst +++ b/docs/manual/source/numpy-universal.rst @@ -6,19 +6,25 @@ Standard mathematical functions can be calculated on any scalar, scalar-valued iterable (ranges, lists, tuples containing numbers), and on ``ndarray``\ s without having to change the call signature. In all cases the functions return a new ``ndarray`` of typecode ``float`` -(since these functions usually generate float values, anyway). The -functions execute faster with ``ndarray`` arguments than with iterables, -because the values of the input vector can be extracted faster. +(since these functions usually generate float values, anyway). The only +exceptions to this rule are the ``exp``, and ``sqrt`` functions, which, +if ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in +`ulab.h `__, +can return complex arrays, depending on the argument. All functions +execute faster with ``ndarray`` arguments than with iterables, because +the values of the input vector can be extracted faster. -At present, the following functions are supported: +At present, the following functions are supported (starred functions can +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``. +``exp*``, ``expm1``, ``floor``, ``log``, ``log10``, ``log2``, +``radians``, ``sin``, ``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. +the exponential of a matrix cannot be calculated in this way, only the +exponential of the matrix entries. .. code:: @@ -199,6 +205,71 @@ returns an ndarray of type ``mp_float_t``. +exp +--- + +If ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in +`ulab.h `__, +the exponential function can also take complex arrays as its argument, +in which case the return value is also complex. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, 2, 3]) + print('a:\t\t', a) + print('exp(a):\t\t', np.exp(a)) + print() + + b = np.array([1+1j, 2+2j, 3+3j], dtype=np.complex) + print('b:\t\t', b) + print('exp(b):\t\t', np.exp(b)) + +.. parsed-literal:: + + a: array([1.0, 2.0, 3.0], dtype=float64) + exp(a): array([2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64) + + b: array([1.0+1.0j, 2.0+2.0j, 3.0+3.0j], dtype=complex) + exp(b): array([1.468693939915885+2.287355287178842j, -3.074932320639359+6.71884969742825j, -19.88453084414699+2.834471132487004j], dtype=complex) + + + + +sqrt +---- + +If ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in +`ulab.h `__, +the exponential function can also take complex arrays as its argument, +in which case the return value is also complex. If the input is real, +but the results might be complex, the user is supposed to specify the +output ``dtype`` in the function call. Otherwise, the square roots of +negative numbers will result in ``NaN``. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, -1]) + print('a:\t\t', a) + print('sqrt(a):\t\t', np.sqrt(a)) + print('sqrt(a):\t\t', np.sqrt(a, dtype=np.complex)) + +.. parsed-literal:: + + a: array([1.0, -1.0], dtype=float64) + sqrt(a): array([1.0, nan], dtype=float64) + sqrt(a): array([1.0+0.0j, 0.0+1.0j], dtype=complex) + + + + Vectorising generic python functions ------------------------------------ diff --git a/docs/manual/source/scipy-signal.rst b/docs/manual/source/scipy-signal.rst index 6865a0a6..b3bcd52a 100644 --- a/docs/manual/source/scipy-signal.rst +++ b/docs/manual/source/scipy-signal.rst @@ -77,7 +77,9 @@ 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. 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``. +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. .. code:: diff --git a/docs/manual/source/ulab-intro.rst b/docs/manual/source/ulab-intro.rst index c4ccb069..81019e33 100644 --- a/docs/manual/source/ulab-intro.rst +++ b/docs/manual/source/ulab-intro.rst @@ -93,14 +93,15 @@ The main points of ``ulab`` are - fast Fourier transforms - filtering of data (convolution and second-order filters) - function minimisation, fitting, and numerical approximation routines +- interfacing between numerical data and peripheral hardware devices ``ulab`` implements close to a hundred functions and array methods. At -the time of writing this manual (for version 2.1.0), the library adds +the time of writing this manual (for version 4.0.0), the library adds approximately 120 kB of extra compiled code to the ``micropython`` -(pyboard.v.11) firmware. However, if you are tight with flash space, you -can easily shave tens of kB off the firmware. In fact, if only a small -sub-set of functions are needed, you can get away with less than 10 kB -of flash space. See the section on `customising +(pyboard.v.1.17) firmware. However, if you are tight with flash space, +you can easily shave tens of kB off the firmware. In fact, if only a +small sub-set of functions are needed, you can get away with less than +10 kB of flash space. See the section on `customising ulab <#Customising-the-firmware>`__. Resources and legal matters @@ -136,13 +137,13 @@ Differences between micropython-ulab and circuitpython-ulab ----------------------------------------------------------- ``ulab`` has originally been developed for ``micropython``, but has -since been integrated into a number of its flavours. Most of these -flavours are simply forks of ``micropython`` itself, with some -additional functionality. One of the notable exceptions is -``circuitpython``, which has slightly diverged at the core level, and -this has some minor consequences. Some of these concern the C -implementation details only, which all have been sorted out with the -generous and enthusiastic support of Jeff Epler from `Adafruit +since been integrated into a number of its flavours. Most of these are +simply forks of ``micropython`` itself, with some additional +functionality. One of the notable exceptions is ``circuitpython``, which +has slightly diverged at the core level, and this has some minor +consequences. Some of these concern the C implementation details only, +which all have been sorted out with the generous and enthusiastic +support of Jeff Epler from `Adafruit Industries `__. There are, however, a couple of instances, where the two environments @@ -176,6 +177,17 @@ fine-tuned. The first couple of lines of the file look like this // A considerable amount of flash space can be saved by removing (setting // the corresponding constants to 0) the unnecessary functions and features. + // Values defined here can be overridden by your own config file as + // make -DULAB_CONFIG_FILE="my_ulab_config.h" + #if defined(ULAB_CONFIG_FILE) + #include ULAB_CONFIG_FILE + #endif + + // Adds support for complex ndarrays + #ifndef ULAB_SUPPORTS_COMPLEX + #define ULAB_SUPPORTS_COMPLEX (1) + #endif + // Determines, whether scipy is defined in ulab. The sub-modules and functions // of scipy have to be defined separately #define ULAB_HAS_SCIPY (1) @@ -240,9 +252,9 @@ everything else, you get away with less than 5 kB extra. Compatibility with numpy ------------------------ -The functions implemented in ``ulab`` are organised in three sub-modules -at the C level, namely, ``numpy``, ``scipy``, and ``user``. This -modularity is elevated to ``python``, meaning that in order to use +The functions implemented in ``ulab`` are organised in four sub-modules +at the C level, namely, ``numpy``, ``scipy``, ``utils``, and ``user``. +This modularity is elevated to ``python``, meaning that in order to use functions that are part of ``numpy``, you have to import ``numpy`` as .. code:: python @@ -463,6 +475,29 @@ from the version string in the following way: +ulab with complex arrays +~~~~~~~~~~~~~~~~~~~~~~~~ + +If the firmware supports complex arrays, ``-c`` is appended to the +version string as can be seen below. + +.. code:: + + # code to be run in micropython + + import ulab + + version = ulab.__version__ + + print('version string: ', version) + +.. parsed-literal:: + + version string: 4.0.0-2D-c + + + + Finding out what your firmware supports --------------------------------------- diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index b39235a7..a37cef7c 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -117,7 +117,9 @@ types can be mixed in the initialisation function. If the ``dtype`` keyword with the possible ``uint8/int8/uint16/int16/float`` values is supplied, the new ``ndarray`` will have that type, otherwise, it assumes ``float`` as -default. +default. In addition, if ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in +`ulab.h `__, +the ``dtype`` can also take on the value of ``complex``. .. code:: @@ -251,18 +253,20 @@ 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 +the firmware was compiled with complex support. 1. `numpy.arange <#arange>`__ 2. `numpy.concatenate <#concatenate>`__ -3. `numpy.diag <#diag>`__ -4. `numpy.empty <#empty>`__ -5. `numpy.eye <#eye>`__ +3. `numpy.diag\* <#diag>`__ +4. `numpy.empty\* <#empty>`__ +5. `numpy.eye\* <#eye>`__ 6. `numpy.frombuffer <#frombuffer>`__ -7. `numpy.full <#full>`__ -8. `numpy.linspace <#linspace>`__ +7. `numpy.full\* <#full>`__ +8. `numpy.linspace\* <#linspace>`__ 9. `numpy.logspace <#logspace>`__ -10. `numpy.ones <#ones>`__ -11. `numpy.zeros <#zeros>`__ +10. `numpy.ones\* <#ones>`__ +11. `numpy.zeros\* <#zeros>`__ arange ~~~~~~ @@ -392,6 +396,9 @@ 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. +The ``diag`` function can accept a complex array, if the firmware was +compiled with complex support. + .. code:: # code to be run in micropython @@ -443,6 +450,9 @@ https://numpy.org/doc/stable/reference/generated/numpy.empty.html ``empty`` is simply an alias for ``zeros``, i.e., as opposed to ``numpy``, the entries of the tensor will be initialised to zero. +The ``empty`` function can accept complex as the value of the dtype, if +the firmware was compiled with complex support. + eye ~~~ @@ -461,6 +471,9 @@ is supplied, then we get a square matrix, otherwise one with ``M`` rows, and ``N`` columns), and ``k`` is the shift of the ones (the main diagonal corresponds to ``k=0``). Here are a couple of examples. +The ``eye`` function can accept ``complex`` as the value of the +``dtype``, if the firmware was compiled with complex support. + With a single argument ^^^^^^^^^^^^^^^^^^^^^^ @@ -579,6 +592,9 @@ all equal to the second positional argument. The first argument is a tuple describing the shape of the tensor. The ``dtype`` keyword argument with a default value of ``float`` can also be supplied. +The ``full`` function can accept a complex scalar, or ``complex`` as the +value of ``dtype``, if the firmware was compiled with complex support. + .. code:: # code to be run in micropython @@ -621,6 +637,11 @@ be supplied to force type conversion of the output. The default is not necessarily evenly spaced. This is not an error, rather a consequence of rounding. (This is also the ``numpy`` behaviour.) +The ``linspace`` function can accept ``complex`` as the value of the +``dtype``, if the firmware was compiled with complex support. The output +``dtype`` is automatically complex, if either of the endpoints is a +complex scalar. + .. code:: # code to be run in micropython @@ -721,6 +742,9 @@ calling one of the ``ones``, or ``zeros`` functions. ``ones`` and where shape is either an integer, or a tuple specifying the shape. +The ``ones/zeros`` functions can accept complex as the value of the +dtype, if the firmware was compiled with complex support. + .. code:: # code to be run in micropython @@ -859,20 +883,26 @@ 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 operators, properties return an object that describe some feature of the -array, while the methods return a new array-like object. +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. 1. `.byteswap <#.byteswap>`__ 2. `.copy <#.copy>`__ 3. `.dtype <#.dtype>`__ 4. `.flat <#.flat>`__ 5. `.flatten <#.flatten>`__ -6. `.itemsize <#.itemsize>`__ -7. `.reshape <#.reshape>`__ -8. `.shape <#.shape>`__ -9. `.size <#.size>`__ -10. `.T <#.transpose>`__ -11. `.transpose <#.transpose>`__ -12. `.sort <#.sort>`__ +6. `.imag\* <#.imag>`__ +7. `.itemsize <#.itemsize>`__ +8. `.real\* <#.real>`__ +9. `.reshape <#.reshape>`__ +10. `.shape <#.shape>`__ +11. `.size <#.size>`__ +12. `.T <#.transpose>`__ +13. `.tobytes <#.tobytes>`__ +14. `.tolist <#.tolist>`__ +15. `.transpose <#.transpose>`__ +16. `.sort <#.sort>`__ .byteswap ~~~~~~~~~ @@ -1111,6 +1141,43 @@ 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 + +The ``.imag`` property is defined only, if the firmware was compiled +with complex support, and returns a copy with the imaginary part of an +array. If the array is real, then the output is straight zeros with the +``dtype`` of the input. If the input is complex, the output ``dtype`` is +always ``float``, irrespective of the values. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, 2, 3], dtype=np.uint16) + print("a:\t", a) + print("a.imag:\t", a.imag) + + b = np.array([1, 2+1j, 3-1j], dtype=np.complex) + print("\nb:\t", b) + print("b.imag:\t", b.imag) + +.. parsed-literal:: + + a: array([1, 2, 3], dtype=uint16) + a.imag: array([0, 0, 0], dtype=uint16) + + b: array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex) + b.imag: array([0.0, 1.0, -1.0], dtype=float64) + + + + .itemsize ~~~~~~~~~ @@ -1128,11 +1195,11 @@ the array. a = np.array([1, 2, 3], dtype=np.int8) print("a:\n", a) - print("itemsize of a:", a.itemsize + print("itemsize of a:", a.itemsize) b= np.array([[1, 2], [3, 4]], dtype=np.float) print("\nb:\n", b) - print("itemsize of b:", b.itemsize + print("itemsize of b:", b.itemsize) .. parsed-literal:: @@ -1148,6 +1215,40 @@ the array. +.real +~~~~~ + +numpy: +https://numpy.org/doc/stable/reference/generated/numpy.ndarray.real.html + +The ``.real`` property is defined only, if the firmware was compiled +with complex support, and returns a copy with the real part of an array. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array([1, 2, 3], dtype=np.uint16) + print("a:\t", a) + print("a.real:\t", a.real) + + b = np.array([1, 2+1j, 3-1j], dtype=np.complex) + print("\nb:\t", b) + print("b.real:\t", b.real) + +.. parsed-literal:: + + a: array([1, 2, 3], dtype=uint16) + a.real: array([1, 2, 3], dtype=uint16) + + b: array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex) + b.real: array([1.0, 2.0, 3.0], dtype=float64) + + + + .reshape ~~~~~~~~ @@ -1342,6 +1443,44 @@ not dense (i.e., it has already been sliced). +.tolist +~~~~~~~ + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tolist.html + +The ``.tolist`` method can be used for converting the numerical array +into a (nested) ``python`` lists. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array(range(4), dtype=np.uint8) + print('a: ', a) + b = a.tolist() + print('b: ', b) + + c = a.reshape((2, 2)) + print('='*20) + print('c: ', c) + d = c.tolist() + print('d: ', d) + +.. parsed-literal:: + + a: array([0, 1, 2, 3], dtype=uint8) + b: [0, 1, 2, 3] + ==================== + c: array([[0, 1], + [2, 3]], dtype=uint8) + d: [[0, 1], [2, 3]] + + + + .transpose ~~~~~~~~~~ diff --git a/docs/numpy-fft.ipynb b/docs/numpy-fft.ipynb index eccb86e0..803c9239 100644 --- a/docs/numpy-fft.ipynb +++ b/docs/numpy-fft.ipynb @@ -31,11 +31,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2020-08-03T18:32:45.342280Z", - "start_time": "2020-08-03T18:32:45.338442Z" + "end_time": "2022-01-07T18:24:48.499467Z", + "start_time": "2022-01-07T18:24:48.488004Z" } }, "outputs": [], @@ -315,6 +315,31 @@ "print('\\nimaginary part:\\t', d)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ulab with complex support\n", + "\n", + "If the `ULAB_SUPPORTS_COMPLEX`, and `ULAB_FFT_IS_NUMPY_COMPATIBLE` pre-processor constants are set to 1 in [ulab.h](https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h) as \n", + "\n", + "```c\n", + "// Adds support for complex ndarrays\n", + "#ifndef ULAB_SUPPORTS_COMPLEX\n", + "#define ULAB_SUPPORTS_COMPLEX (1)\n", + "#endif\n", + "```\n", + "\n", + "```c\n", + "#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE\n", + "#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)\n", + "#endif\n", + "```\n", + "then the FFT routine will behave in a `numpy`-compatible way: the single input array can either be real, in which case the imaginary part is assumed to be zero, or complex. The output is also complex. \n", + "\n", + "While `numpy`-compatibility might be a desired feature, it has one side effect, namely, the FFT routine consumes approx. 50% more RAM. The reason for this lies in the implementation details." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -373,6 +398,15 @@ "Note that unlike in `numpy`, the length of the array on which the Fourier transform is carried out must be a power of 2. If this is not the case, the function raises a `ValueError` exception." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ulab with complex support\n", + "\n", + "The `fft.ifft` function can also be made `numpy`-compatible by setting the `ULAB_SUPPORTS_COMPLEX`, and `ULAB_FFT_IS_NUMPY_COMPATIBLE` pre-processor constants to 1." + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index 6e73a3ad..f7274252 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -31,11 +31,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2021-03-23T16:11:29.096141Z", - "start_time": "2021-03-23T16:11:29.090178Z" + "end_time": "2022-01-07T19:26:30.392923Z", + "start_time": "2022-01-07T19:26:30.384485Z" } }, "outputs": [], @@ -49,11 +49,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2021-03-23T16:11:29.622773Z", - "start_time": "2021-03-23T16:11:29.604457Z" + "end_time": "2022-01-07T19:26:37.811055Z", + "start_time": "2022-01-07T19:26:37.788500Z" } }, "outputs": [], @@ -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\", \"/dev/shm/micropython.py\"], \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", @@ -230,19 +230,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This section of the manual discusses those functions that were adapted from `numpy`.\n", + "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", "\n", - "1. [numpy.all](#all)\n", - "1. [numpy.any](#any)\n", + "1. [numpy.all*](#all)\n", + "1. [numpy.any*](#any)\n", "1. [numpy.argmax](#argmax)\n", "1. [numpy.argmin](#argmin)\n", "1. [numpy.argsort](#argsort)\n", "1. [numpy.clip](#clip)\n", - "1. [numpy.convolve](#convolve)\n", + "1. [numpy.conjugate*](#conjugate)\n", + "1. [numpy.convolve*](#convolve)\n", "1. [numpy.diff](#diff)\n", "1. [numpy.dot](#dot)\n", "1. [numpy.equal](#equal)\n", - "1. [numpy.flip](#flip)\n", + "1. [numpy.flip*](#flip)\n", + "1. [numpy.imag*](#imag)\n", "1. [numpy.interp](#interp)\n", "1. [numpy.isfinite](#isfinite)\n", "1. [numpy.isinf](#isinf)\n", @@ -255,8 +257,10 @@ "1. [numpy.not_equal](#equal)\n", "1. [numpy.polyfit](#polyfit)\n", "1. [numpy.polyval](#polyval)\n", + "1. [numpy.real*](#real)\n", "1. [numpy.roll](#roll)\n", "1. [numpy.sort](#sort)\n", + "1. [numpy.sort_complex*](#sort_complex)\n", "1. [numpy.std](#std)\n", "1. [numpy.sum](#sum)\n", "1. [numpy.trace](#trace)\n", @@ -274,7 +278,9 @@ "\n", "The function takes one positional, and one keyword argument, the `axis`, with a default value of `None`, and tests, whether *all* array elements along the given axis evaluate to `True`. If the keyword argument is `None`, the flattened array is inspected. \n", "\n", - "Elements of an array evaluate to `True`, if they are not equal to zero, or the Boolean `False`. The return value if a Boolean `ndarray`." + "Elements of an array evaluate to `True`, if they are not equal to zero, or the Boolean `False`. The return value if a Boolean `ndarray`.\n", + "\n", + "If the firmware was compiled with complex support, the function can accept complex arrays." ] }, { @@ -339,7 +345,9 @@ "\n", "The function takes one positional, and one keyword argument, the `axis`, with a default value of `None`, and tests, whether *any* array element along the given axis evaluates to `True`. If the keyword argument is `None`, the flattened array is inspected. \n", "\n", - "Elements of an array evaluate to `True`, if they are not equal to zero, or the Boolean `False`. The return value if a Boolean `ndarray`." + "Elements of an array evaluate to `True`, if they are not equal to zero, or the Boolean `False`. The return value if a Boolean `ndarray`.\n", + "\n", + "If the firmware was compiled with complex support, the function can accept complex arrays." ] }, { @@ -584,6 +592,56 @@ "print('clipped:\\t', np.clip(a, b, 7))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## conjugate\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.conjugate.html\n", + "\n", + "If the firmware was compiled with complex support, the function calculates the complex conjugate of the input array. If the input array is of real `dtype`, then the output is simply a copy, preserving the `dtype`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:30:53.394539Z", + "start_time": "2022-01-07T19:30:53.374737Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t\t array([1, 2, 3, 4], dtype=uint8)\n", + "conjugate(a):\t array([1, 2, 3, 4], dtype=uint8)\n", + "\n", + "b:\t\t array([1.0+1.0j, 2.0-2.0j, 3.0+3.0j, 4.0-4.0j], dtype=complex)\n", + "conjugate(b):\t array([1.0-1.0j, 2.0+2.0j, 3.0-3.0j, 4.0+4.0j], dtype=complex)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array([1, 2, 3, 4], dtype=np.uint8)\n", + "b = np.array([1+1j, 2-2j, 3+3j, 4-4j], dtype=np.complex)\n", + "\n", + "print('a:\\t\\t', a)\n", + "print('conjugate(a):\\t', np.conjugate(a))\n", + "print()\n", + "print('b:\\t\\t', b)\n", + "print('conjugate(b):\\t', np.conjugate(b))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -594,7 +652,9 @@ "\n", "Returns the discrete, linear convolution of two one-dimensional arrays.\n", "\n", - "Only the ``full`` mode is supported, and the ``mode`` named parameter is not accepted. Note that all other modes can be had by slicing a ``full`` result." + "Only the ``full`` mode is supported, and the ``mode`` named parameter is not accepted. Note that all other modes can be had by slicing a ``full`` result.\n", + "\n", + "If the firmware was compiled with complex support, the function can accept complex arrays." ] }, { @@ -876,7 +936,9 @@ "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html\n", "\n", - "The `flip` function takes one positional, an `ndarray`, and one keyword argument, `axis = None`, and reverses the order of elements along the given axis. If the keyword argument is `None`, the matrix' entries are flipped along all axes. `flip` returns a new copy of the array." + "The `flip` function takes one positional, an `ndarray`, and one keyword argument, `axis = None`, and reverses the order of elements along the given axis. If the keyword argument is `None`, the matrix' entries are flipped along all axes. `flip` returns a new copy of the array.\n", + "\n", + "If the firmware was compiled with complex support, the function can accept complex arrays." ] }, { @@ -928,6 +990,55 @@ "print(\"\\na flipped horizontally+vertically\\n\", np.flip(a))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## imag\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.imag.html\n", + "\n", + "The `imag` function returns the imaginary part of an array, or scalar. It cannot accept a generic iterable as its argument. The function is defined only, if the firmware was compiled with complex support." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:26:42.901258Z", + "start_time": "2022-01-07T19:26:42.880755Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t\t array([1, 2, 3], dtype=uint16)\n", + "imag(a):\t array([0, 0, 0], dtype=uint16)\n", + "\n", + "b:\t\t array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex)\n", + "imag(b):\t array([0.0, 1.0, -1.0], 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], dtype=np.uint16)\n", + "print(\"a:\\t\\t\", a)\n", + "print(\"imag(a):\\t\", np.imag(a))\n", + "\n", + "b = np.array([1, 2+1j, 3-1j], dtype=np.complex)\n", + "print(\"\\nb:\\t\\t\", b)\n", + "print(\"imag(b):\\t\", np.imag(b))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1535,6 +1646,55 @@ "print('value of p(a): ', np.polyval(p, a))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## real\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.real.html\n", + "\n", + "The `real` function returns the real part of an array, or scalar. It cannot accept a generic iterable as its argument. The function is defined only, if the firmware was compiled with complex support." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:27:22.141930Z", + "start_time": "2022-01-07T19:27:22.122577Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t\t array([1, 2, 3], dtype=uint16)\n", + "real(a):\t array([1, 2, 3], dtype=uint16)\n", + "\n", + "b:\t\t array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex)\n", + "real(b):\t array([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([1, 2, 3], dtype=np.uint16)\n", + "print(\"a:\\t\\t\", a)\n", + "print(\"real(a):\\t\", np.real(a))\n", + "\n", + "b = np.array([1, 2+1j, 3-1j], dtype=np.complex)\n", + "print(\"\\nb:\\t\\t\", b)\n", + "print(\"real(b):\\t\", np.real(b))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1749,20 +1909,68 @@ "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 nup.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)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## sort_complex\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.sort_complex.html\n", + "\n", + "If the firmware was compiled with complex support, the functions sorts the input array first according to its real part, and then the imaginary part. The input must be a one-dimensional array. The output is always of `dtype` complex, even if the input was real integer." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:36:15.750029Z", + "start_time": "2022-01-07T19:36:15.732210Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t\t\t array([5, 4, 3, 2, 1], dtype=int16)\n", + "sort_complex(a):\t array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex)\n", + "\n", + "b:\t\t\t array([5.0+0.0j, 4.0+3.0j, 4.0-2.0j, 0.0+0.0j, 0.0+1.0j], dtype=complex)\n", + "sort_complex(b):\t array([0.0+0.0j, 0.0+1.0j, 4.0-2.0j, 4.0+3.0j, 5.0+0.0j], dtype=complex)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array([5, 4, 3, 2, 1], dtype=np.int16)\n", + "print('a:\\t\\t\\t', a)\n", + "print('sort_complex(a):\\t', np.sort_complex(a))\n", + "print()\n", + "\n", + "b = np.array([5, 4+3j, 4-2j, 0, 1j], dtype=np.complex)\n", + "print('b:\\t\\t\\t', b)\n", + "print('sort_complex(b):\\t', np.sort_complex(b))" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/numpy-universal.ipynb b/docs/numpy-universal.ipynb index 70160b2f..8934fa6e 100644 --- a/docs/numpy-universal.ipynb +++ b/docs/numpy-universal.ipynb @@ -31,11 +31,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2021-01-13T18:55:01.909310Z", - "start_time": "2021-01-13T18:55:01.903634Z" + "end_time": "2022-01-07T19:10:30.696795Z", + "start_time": "2022-01-07T19:10:30.690003Z" } }, "outputs": [], @@ -49,11 +49,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2021-01-13T18:55:02.434518Z", - "start_time": "2021-01-13T18:55:02.382296Z" + "end_time": "2022-01-07T19:10:30.785887Z", + "start_time": "2022-01-07T19:10:30.710912Z" } }, "outputs": [], @@ -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\", \"/dev/shm/micropython.py\"], \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", @@ -225,13 +225,13 @@ "source": [ "# Universal functions\n", "\n", - "Standard mathematical functions can be calculated on any scalar, scalar-valued iterable (ranges, lists, tuples containing numbers), and on `ndarray`s without having to change the call signature. In all cases the functions return a new `ndarray` of typecode `float` (since these functions usually generate float values, anyway). The functions execute faster with `ndarray` arguments than with iterables, because the values of the input vector can be extracted faster. \n", + "Standard mathematical functions can be calculated on any scalar, scalar-valued iterable (ranges, lists, tuples containing numbers), and on `ndarray`s without having to change the call signature. In all cases the functions return a new `ndarray` of typecode `float` (since these functions usually generate float values, anyway). The only exceptions to this rule are the `exp`, and `sqrt` functions, which, if `ULAB_SUPPORTS_COMPLEX` is set to 1 in [ulab.h](https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h), can return complex arrays, depending on the argument. All functions execute faster with `ndarray` arguments than with iterables, because the values of the input vector can be extracted faster. \n", "\n", - "At present, the following functions are supported:\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`, `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." + "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." ] }, { @@ -459,6 +459,96 @@ "print('\\ndecimals = -1\\t', np.around(a, decimals=-1))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## exp\n", + "\n", + "If `ULAB_SUPPORTS_COMPLEX` is set to 1 in [ulab.h](https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h), the exponential function can also take complex arrays as its argument, in which case the return value is also complex." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:41:51.865779Z", + "start_time": "2022-01-07T18:41:51.843897Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t\t array([1.0, 2.0, 3.0], dtype=float64)\n", + "exp(a):\t\t array([2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64)\n", + "\n", + "b:\t\t array([1.0+1.0j, 2.0+2.0j, 3.0+3.0j], dtype=complex)\n", + "exp(b):\t\t array([1.468693939915885+2.287355287178842j, -3.074932320639359+6.71884969742825j, -19.88453084414699+2.834471132487004j], dtype=complex)\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:\\t\\t', a)\n", + "print('exp(a):\\t\\t', np.exp(a))\n", + "print()\n", + "\n", + "b = np.array([1+1j, 2+2j, 3+3j], dtype=np.complex)\n", + "print('b:\\t\\t', b)\n", + "print('exp(b):\\t\\t', np.exp(b))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## sqrt\n", + "\n", + "If `ULAB_SUPPORTS_COMPLEX` is set to 1 in [ulab.h](https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h), the exponential function can also take complex arrays as its argument, in which case the return value is also complex. If the input is real, but the results might be complex, the user is supposed to specify the output `dtype` in the function call. Otherwise, the square roots of negative numbers will result in `NaN`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:45:26.554520Z", + "start_time": "2022-01-07T18:45:26.543552Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t\t array([1.0, -1.0], dtype=float64)\n", + "sqrt(a):\t\t array([1.0, nan], dtype=float64)\n", + "sqrt(a):\t\t array([1.0+0.0j, 0.0+1.0j], dtype=complex)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array([1, -1])\n", + "print('a:\\t\\t', a)\n", + "print('sqrt(a):\\t\\t', np.sqrt(a))\n", + "print('sqrt(a):\\t\\t', np.sqrt(a, dtype=np.complex))" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/scipy-signal.ipynb b/docs/scipy-signal.ipynb index 0c9146bf..c4d5f85f 100644 --- a/docs/scipy-signal.ipynb +++ b/docs/scipy-signal.ipynb @@ -31,11 +31,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2021-01-12T16:11:13.416714Z", - "start_time": "2021-01-12T16:11:13.404067Z" + "end_time": "2022-01-07T19:20:19.755153Z", + "start_time": "2022-01-07T19:20:19.745524Z" } }, "outputs": [], @@ -49,11 +49,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2021-01-12T16:11:13.920842Z", - "start_time": "2021-01-12T16:11:13.863737Z" + "end_time": "2022-01-07T19:20:27.595871Z", + "start_time": "2022-01-07T19:20:27.565514Z" } }, "outputs": [], @@ -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\", \"/dev/shm/micropython.py\"], \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", @@ -322,7 +322,7 @@ "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. 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`." + "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. 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." ] }, { diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index d6e9c583..c85594f9 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -2,19 +2,34 @@ "cells": [ { "cell_type": "markdown", - "source": [ - "# conf.py" - ], "metadata": { "ExecuteTime": { "end_time": "2020-05-01T09:27:13.438054Z", "start_time": "2020-05-01T09:27:13.191491Z" } - } + }, + "source": [ + "# conf.py" + ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:24:12.745063Z", + "start_time": "2022-01-07T18:24:12.733067Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting manual/source/conf.py\n" + ] + } + ], "source": [ "%%writefile manual/source/conf.py\n", "# Configuration file for the Sphinx documentation builder.\n", @@ -42,11 +57,11 @@ "# -- Project information -----------------------------------------------------\n", "\n", "project = 'The ulab book'\n", - "copyright = '2019-2021, Zoltán Vörös and contributors'\n", + "copyright = '2019-2022, 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 = '3.3.4'\n", + "release = '4.0.0'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -129,26 +144,26 @@ " html_theme_path = ['.']\n", "else:\n", " html_theme_path = ['.']" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2021-05-09T06:06:28.491158Z", + "start_time": "2021-05-09T06:06:28.477127Z" + } + }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "Overwriting manual/source/conf.py\n" + "Overwriting manual/source/index.rst\n" ] } ], - "metadata": { - "ExecuteTime": { - "end_time": "2021-05-09T06:05:50.855683Z", - "start_time": "2021-05-09T06:05:50.838482Z" - } - } - }, - { - "cell_type": "code", - "execution_count": 2, "source": [ "%%writefile manual/source/index.rst\n", "\n", @@ -189,33 +204,25 @@ "* :ref:`genindex`\n", "* :ref:`modindex`\n", "* :ref:`search`" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Overwriting manual/source/index.rst\n" - ] - } - ], - "metadata": { - "ExecuteTime": { - "end_time": "2021-05-09T06:06:28.491158Z", - "start_time": "2021-05-09T06:06:28.477127Z" - } - } + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "# Notebook conversion" - ], - "metadata": {} + ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:24:27.671415Z", + "start_time": "2022-01-07T18:24:24.933205Z" + } + }, + "outputs": [], "source": [ "import nbformat as nb\n", "import nbformat.v4.nbbase as nb4\n", @@ -245,18 +252,18 @@ " # it's a bit odd, but even an emtpy notebook is converted into a \"None\" string\n", " rst = rst.lstrip('None')\n", " fout.write(rst)" - ], - "outputs": [], - "metadata": { - "ExecuteTime": { - "end_time": "2021-05-09T06:06:35.839960Z", - "start_time": "2021-05-09T06:06:33.112686Z" - } - } + ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:41:24.175364Z", + "start_time": "2022-01-07T19:41:15.326494Z" + } + }, + "outputs": [], "source": [ "files = ['ulab-intro',\n", " 'ulab-ndarray',\n", @@ -274,25 +281,33 @@ "\n", "for file in files:\n", " convert_notebook(file)" - ], - "outputs": [], - "metadata": { - "ExecuteTime": { - "end_time": "2021-05-09T06:07:00.143083Z", - "start_time": "2021-05-09T06:06:56.719659Z" - } - } + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Template" - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2020-10-30T19:04:50.295563Z", + "start_time": "2020-10-30T19:04:50.227535Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting ./templates/manual.tpl\n" + ] + } + ], "source": [ "%%writefile ./templates/manual.tpl\n", "\n", @@ -408,35 +423,24 @@ "{% block unknowncell scoped %}\n", "unknown type {{cell.type}}\n", "{% endblock unknowncell %}\n" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Overwriting ./templates/manual.tpl\n" - ] - } - ], - "metadata": { - "ExecuteTime": { - "end_time": "2020-10-30T19:04:50.295563Z", - "start_time": "2020-10-30T19:04:50.227535Z" - } - } + ] }, { "cell_type": "code", "execution_count": null, - "source": [], + "metadata": {}, "outputs": [], - "metadata": {} + "source": [] } ], "metadata": { + "interpreter": { + "hash": "ce9a02f9f7db620716422019cafa4bc1786ca85daa298b819f6da075e7993842" + }, "kernelspec": { - "name": "python3", - "display_name": "Python 3.8.5 64-bit ('base': conda)" + "display_name": "Python 3", + "language": "python", + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -496,11 +500,8 @@ "_Feature" ], "window_display": false - }, - "interpreter": { - "hash": "ce9a02f9f7db620716422019cafa4bc1786ca85daa298b819f6da075e7993842" } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/docs/ulab-intro.ipynb b/docs/ulab-intro.ipynb index c85bea68..67d6b608 100644 --- a/docs/ulab-intro.ipynb +++ b/docs/ulab-intro.ipynb @@ -38,11 +38,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2021-01-08T12:07:56.746059Z", - "start_time": "2021-01-08T12:07:56.737187Z" + "end_time": "2022-01-07T18:13:14.590799Z", + "start_time": "2022-01-07T18:13:14.585845Z" } }, "outputs": [], @@ -56,11 +56,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": { "ExecuteTime": { - "end_time": "2021-01-08T12:08:00.405800Z", - "start_time": "2021-01-08T12:08:00.382869Z" + "end_time": "2022-01-07T18:20:56.550047Z", + "start_time": "2022-01-07T18:20:56.527475Z" } }, "outputs": [], @@ -84,7 +84,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\", \"/dev/shm/micropython.py\"], \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", @@ -276,8 +276,9 @@ "- fast Fourier transforms\n", "- filtering of data (convolution and second-order filters)\n", "- function minimisation, fitting, and numerical approximation routines\n", + "- interfacing between numerical data and peripheral hardware devices\n", "\n", - "`ulab` implements close to a hundred functions and array methods. At the time of writing this manual (for version 2.1.0), the library adds approximately 120 kB of extra compiled code to the `micropython` (pyboard.v.11) firmware. However, if you are tight with flash space, you can easily shave tens of kB off the firmware. In fact, if only a small sub-set of functions are needed, you can get away with less than 10 kB of flash space. See the section on [customising ulab](#Customising-the-firmware).\n", + "`ulab` implements close to a hundred functions and array methods. At the time of writing this manual (for version 4.0.0), the library adds approximately 120 kB of extra compiled code to the `micropython` (pyboard.v.1.17) firmware. However, if you are tight with flash space, you can easily shave tens of kB off the firmware. In fact, if only a small sub-set of functions are needed, you can get away with less than 10 kB of flash space. See the section on [customising ulab](#Customising-the-firmware).\n", "\n", "## Resources and legal matters\n", "\n", @@ -295,7 +296,7 @@ "\n", "## Differences between micropython-ulab and circuitpython-ulab\n", "\n", - "`ulab` has originally been developed for `micropython`, but has since been integrated into a number of its flavours. Most of these flavours are simply forks of `micropython` itself, with some additional functionality. One of the notable exceptions is `circuitpython`, which has slightly diverged at the core level, and this has some minor consequences. Some of these concern the C implementation details only, which all have been sorted out with the generous and enthusiastic support of Jeff Epler from [Adafruit Industries](http://www.adafruit.com).\n", + "`ulab` has originally been developed for `micropython`, but has since been integrated into a number of its flavours. Most of these are simply forks of `micropython` itself, with some additional functionality. One of the notable exceptions is `circuitpython`, which has slightly diverged at the core level, and this has some minor consequences. Some of these concern the C implementation details only, which all have been sorted out with the generous and enthusiastic support of Jeff Epler from [Adafruit Industries](http://www.adafruit.com).\n", "\n", "There are, however, a couple of instances, where the two environments differ at the python level in how the class properties can be accessed. We will point out the differences and possible workarounds at the relevant places in this document." ] @@ -323,6 +324,17 @@ "// A considerable amount of flash space can be saved by removing (setting\n", "// the corresponding constants to 0) the unnecessary functions and features.\n", "\n", + "// Values defined here can be overridden by your own config file as\n", + "// make -DULAB_CONFIG_FILE=\"my_ulab_config.h\"\n", + "#if defined(ULAB_CONFIG_FILE)\n", + "#include ULAB_CONFIG_FILE\n", + "#endif\n", + "\n", + "// Adds support for complex ndarrays\n", + "#ifndef ULAB_SUPPORTS_COMPLEX\n", + "#define ULAB_SUPPORTS_COMPLEX (1)\n", + "#endif\n", + "\n", "// Determines, whether scipy is defined in ulab. The sub-modules and functions\n", "// of scipy have to be defined separately\n", "#define ULAB_HAS_SCIPY (1)\n", @@ -382,7 +394,7 @@ "\n", "## Compatibility with numpy\n", "\n", - "The functions implemented in `ulab` are organised in three sub-modules at the C level, namely, `numpy`, `scipy`, and `user`. This modularity is elevated to `python`, meaning that in order to use functions that are part of `numpy`, you have to import `numpy` as\n", + "The functions implemented in `ulab` are organised in four sub-modules at the C level, namely, `numpy`, `scipy`, `utils`, and `user`. This modularity is elevated to `python`, meaning that in order to use functions that are part of `numpy`, you have to import `numpy` as\n", "\n", "```python\n", "from ulab import numpy as np\n", @@ -607,6 +619,45 @@ "print('numerical value of dimensions: ', version_num)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ulab with complex arrays\n", + "\n", + "If the firmware supports complex arrays, `-c` is appended to the version string as can be seen below." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:21:04.079894Z", + "start_time": "2022-01-07T18:21:04.058855Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "version string: 4.0.0-2D-c\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "import ulab\n", + "\n", + "version = ulab.__version__\n", + "\n", + "print('version string: ', version)" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index 77b30cbf..7524e35f 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -34,8 +34,8 @@ "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2021-02-15T15:55:00.261542Z", - "start_time": "2021-02-15T15:55:00.255639Z" + "end_time": "2022-01-07T18:46:22.666663Z", + "start_time": "2022-01-07T18:46:22.663583Z" } }, "outputs": [], @@ -52,8 +52,8 @@ "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2021-02-15T15:55:02.228409Z", - "start_time": "2021-02-15T15:55:02.207636Z" + "end_time": "2022-01-07T18:46:29.198681Z", + "start_time": "2022-01-07T18:46:29.177654Z" } }, "outputs": [], @@ -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\", \"/dev/shm/micropython.py\"], \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", @@ -312,7 +312,7 @@ "\n", "If the iterable is one-dimensional, i.e., one whose elements are numbers, then a row vector will be created and returned. If the iterable is two-dimensional, i.e., one whose elements are again iterables, a matrix will be created. If the lengths of the iterables are not consistent, a `ValueError` will be raised. Iterables of different types can be mixed in the initialisation function. \n", "\n", - "If the `dtype` keyword with the possible `uint8/int8/uint16/int16/float` values is supplied, the new `ndarray` will have that type, otherwise, it assumes `float` as default. " + "If the `dtype` keyword with the possible `uint8/int8/uint16/int16/float` values is supplied, the new `ndarray` will have that type, otherwise, it assumes `float` as default. In addition, if `ULAB_SUPPORTS_COMPLEX` is set to 1 in [ulab.h](https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h), the `dtype` can also take on the value of `complex`." ] }, { @@ -507,19 +507,19 @@ "source": [ "## Array initialisation functions\n", "\n", - "There are nine functions that can be used for initialising an array.\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", "1. [numpy.arange](#arange)\n", "1. [numpy.concatenate](#concatenate)\n", - "1. [numpy.diag](#diag)\n", - "1. [numpy.empty](#empty)\n", - "1. [numpy.eye](#eye)\n", + "1. [numpy.diag*](#diag)\n", + "1. [numpy.empty*](#empty)\n", + "1. [numpy.eye*](#eye)\n", "1. [numpy.frombuffer](#frombuffer)\n", - "1. [numpy.full](#full)\n", - "1. [numpy.linspace](#linspace)\n", + "1. [numpy.full*](#full)\n", + "1. [numpy.linspace*](#linspace)\n", "1. [numpy.logspace](#logspace)\n", - "1. [numpy.ones](#ones)\n", - "1. [numpy.zeros](#zeros)" + "1. [numpy.ones*](#ones)\n", + "1. [numpy.zeros*](#zeros)" ] }, { @@ -675,6 +675,8 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ "## diag\n", "\n", @@ -684,10 +686,10 @@ "\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", "\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" - ], - "cell_type": "markdown", - "metadata": {} + "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", + "\n", + "The `diag` function can accept a complex array, if the firmware was compiled with complex support." + ] }, { "cell_type": "code", @@ -695,10 +697,15 @@ "metadata": {}, "outputs": [ { - "output_type": "stream", "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\n\n" + "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", + "\n", + "\n" ] } ], @@ -717,10 +724,17 @@ "metadata": {}, "outputs": [ { - "output_type": "stream", "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\ndiagonal of a: array([0.0, 5.0, 10.0, 15.0], dtype=float64)\n\n\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],\n", + " [12.0, 13.0, 14.0, 15.0]], dtype=float64)\n", + "\n", + "diagonal of a: array([0.0, 5.0, 10.0, 15.0], dtype=float64)\n", + "\n", + "\n" ] } ], @@ -736,15 +750,17 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ "## empty\n", "\n", "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.empty.html\n", "\n", - "`empty` is simply an alias for `zeros`, i.e., as opposed to `numpy`, the entries of the tensor will be initialised to zero." - ], - "cell_type": "markdown", - "metadata": {} + "`empty` is simply an alias for `zeros`, i.e., as opposed to `numpy`, the entries of the tensor will be initialised to zero. \n", + "\n", + "The `empty` function can accept complex as the value of the dtype, if the firmware was compiled with complex support." + ] }, { "cell_type": "markdown", @@ -759,7 +775,9 @@ "```python\n", "eye(N, M, k=0, dtype=float)\n", "```\n", - "where `N` (`M`) specify the dimensions of the matrix (if only `N` is supplied, then we get a square matrix, otherwise one with `M` rows, and `N` columns), and `k` is the shift of the ones (the main diagonal corresponds to `k=0`). Here are a couple of examples." + "where `N` (`M`) specify the dimensions of the matrix (if only `N` is supplied, then we get a square matrix, otherwise one with `M` rows, and `N` columns), and `k` is the shift of the ones (the main diagonal corresponds to `k=0`). Here are a couple of examples.\n", + "\n", + "The `eye` function can accept `complex` as the value of the `dtype`, if the firmware was compiled with complex support." ] }, { @@ -932,7 +950,9 @@ "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.full.html\n", "\n", - "The function returns an array of arbitrary dimension, whose elements are all equal to the second positional argument. The first argument is a tuple describing the shape of the tensor. The `dtype` keyword argument with a default value of `float` can also be supplied." + "The function returns an array of arbitrary dimension, whose elements are all equal to the second positional argument. The first argument is a tuple describing the shape of the tensor. The `dtype` keyword argument with a default value of `float` can also be supplied.\n", + "\n", + "The `full` function can accept a complex scalar, or `complex` as the value of `dtype`, if the firmware was compiled with complex support." ] }, { @@ -982,7 +1002,9 @@ "\n", "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html\n", "\n", - "This function returns an array, whose elements are uniformly spaced between the `start`, and `stop` points. The number of intervals is determined by the `num` keyword argument, whose default value is 50. With the `endpoint` keyword argument (defaults to `True`) one can include `stop` in the sequence. In addition, the `dtype` keyword can be supplied to force type conversion of the output. The default is `float`. Note that, when `dtype` is of integer type, the sequence is not necessarily evenly spaced. This is not an error, rather a consequence of rounding. (This is also the `numpy` behaviour.)" + "This function returns an array, whose elements are uniformly spaced between the `start`, and `stop` points. The number of intervals is determined by the `num` keyword argument, whose default value is 50. With the `endpoint` keyword argument (defaults to `True`) one can include `stop` in the sequence. In addition, the `dtype` keyword can be supplied to force type conversion of the output. The default is `float`. Note that, when `dtype` is of integer type, the sequence is not necessarily evenly spaced. This is not an error, rather a consequence of rounding. (This is also the `numpy` behaviour.)\n", + "\n", + "The `linspace` function can accept `complex` as the value of the `dtype`, if the firmware was compiled with complex support. The output `dtype` is automatically complex, if either of the endpoints is a complex scalar." ] }, { @@ -1098,7 +1120,9 @@ "ones(shape, dtype=float)\n", "zeros(shape, dtype=float)\n", "```\n", - "where shape is either an integer, or a tuple specifying the shape." + "where shape is either an integer, or a tuple specifying the shape.\n", + "\n", + "The `ones/zeros` functions can accept complex as the value of the dtype, if the firmware was compiled with complex support." ] }, { @@ -1314,18 +1338,22 @@ "source": [ "## 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. \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", "1. [.byteswap](#.byteswap)\n", "1. [.copy](#.copy)\n", "1. [.dtype](#.dtype)\n", "1. [.flat](#.flat)\n", "1. [.flatten](#.flatten)\n", + "1. [.imag*](#.imag)\n", "1. [.itemsize](#.itemsize)\n", + "1. [.real*](#.real)\n", "1. [.reshape](#.reshape)\n", "1. [.shape](#.shape)\n", "1. [.size](#.size)\n", "1. [.T](#.transpose)\n", + "1. [.tobytes](#.tobytes)\n", + "1. [.tolist](#.tolist)\n", "1. [.transpose](#.transpose)\n", "1. [.sort](#.sort)" ] @@ -1525,15 +1553,15 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ "### .flat\n", "\n", "numpy: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flat.htm\n", "\n", "`.flat` returns the array's flat iterator. For one-dimensional objects the flat iterator is equivalent to the standart iterator, while for higher dimensional tensors, it amounts to first flattening the array, and then iterating over it. Note, however, that the flat iterator does not consume RAM beyond what is required for holding the position of the iterator itself, while flattening produces a new copy." - ], - "cell_type": "markdown", - "metadata": {} + ] }, { "cell_type": "code", @@ -1541,10 +1569,28 @@ "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "1\n2\n3\n4\na:\n array([[1, 2, 3, 4],\n [5, 6, 7, 8]], dtype=int8)\narray([1, 2, 3, 4], dtype=int8)\narray([5, 6, 7, 8], dtype=int8)\n1\n2\n3\n4\n5\n6\n7\n8\n\n\n" + "1\n", + "2\n", + "3\n", + "4\n", + "a:\n", + " array([[1, 2, 3, 4],\n", + " [5, 6, 7, 8]], dtype=int8)\n", + "array([1, 2, 3, 4], dtype=int8)\n", + "array([5, 6, 7, 8], dtype=int8)\n", + "1\n", + "2\n", + "3\n", + "4\n", + "5\n", + "6\n", + "7\n", + "8\n", + "\n", + "\n" ] } ], @@ -1620,6 +1666,55 @@ "print(\"b flattened (F): \\t\", b.flatten(order='F'))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### .imag\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.imag.html\n", + "\n", + "The `.imag` property is defined only, if the firmware was compiled with complex support, and returns a copy with the imaginary part of an array. If the array is real, then the output is straight zeros with the `dtype` of the input. If the input is complex, the output `dtype` is always `float`, irrespective of the values." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:07:26.171208Z", + "start_time": "2022-01-07T19:07:26.152633Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t array([1, 2, 3], dtype=uint16)\n", + "a.imag:\t array([0, 0, 0], dtype=uint16)\n", + "\n", + "b:\t array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex)\n", + "b.imag:\t array([0.0, 1.0, -1.0], 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], dtype=np.uint16)\n", + "print(\"a:\\t\", a)\n", + "print(\"a.imag:\\t\", a.imag)\n", + "\n", + "b = np.array([1, 2+1j, 3-1j], dtype=np.complex)\n", + "print(\"\\nb:\\t\", b)\n", + "print(\"b.imag:\\t\", b.imag)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1665,11 +1760,60 @@ "\n", "a = np.array([1, 2, 3], dtype=np.int8)\n", "print(\"a:\\n\", a)\n", - "print(\"itemsize of a:\", a.itemsize\n", + "print(\"itemsize of a:\", a.itemsize)\n", "\n", "b= np.array([[1, 2], [3, 4]], dtype=np.float)\n", "print(\"\\nb:\\n\", b)\n", - "print(\"itemsize of b:\", b.itemsize" + "print(\"itemsize of b:\", b.itemsize)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### .real\n", + "\n", + "numpy: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.real.html\n", + "\n", + "The `.real` property is defined only, if the firmware was compiled with complex support, and returns a copy with the real part of an array." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:10:01.870921Z", + "start_time": "2022-01-07T19:10:01.860071Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\t array([1, 2, 3], dtype=uint16)\n", + "a.real:\t array([1, 2, 3], dtype=uint16)\n", + "\n", + "b:\t array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex)\n", + "b.real:\t array([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([1, 2, 3], dtype=np.uint16)\n", + "print(\"a:\\t\", a)\n", + "print(\"a.real:\\t\", a.real)\n", + "\n", + "b = np.array([1, 2+1j, 3-1j], dtype=np.complex)\n", + "print(\"\\nb:\\t\", b)\n", + "print(\"b.real:\\t\", b.real)" ] }, { @@ -1782,11 +1926,11 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ "By assigning a tuple to the `.shape` property, the array can be `reshape`d:" - ], - "cell_type": "markdown", - "metadata": {} + ] }, { "cell_type": "code", @@ -1794,10 +1938,18 @@ "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "a:\n array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64)\n\na:\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\n" + "a:\n", + " array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], 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", + "\n" ] } ], @@ -1866,13 +2018,13 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ ".T\n", "\n", "The `.T` property of the `ndarray` is equivalent to [.transpose](#.transpose)." - ], - "cell_type": "markdown", - "metadata": {} + ] }, { "cell_type": "markdown", @@ -1929,6 +2081,59 @@ "print('a: ', a)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### .tolist\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tolist.html\n", + "\n", + "The `.tolist` method can be used for converting the numerical array into a (nested) `python` lists." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:01:28.671234Z", + "start_time": "2022-01-07T19:01:28.568786Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: array([0, 1, 2, 3], dtype=uint8)\n", + "b: [0, 1, 2, 3]\n", + "====================\n", + "c: array([[0, 1],\n", + " [2, 3]], dtype=uint8)\n", + "d: [[0, 1], [2, 3]]\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(4), dtype=np.uint8)\n", + "print('a: ', a)\n", + "b = a.tolist()\n", + "print('b: ', b)\n", + "\n", + "c = a.reshape((2, 2))\n", + "print('='*20)\n", + "print('c: ', c)\n", + "d = c.tolist()\n", + "print('d: ', d)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1985,11 +2190,11 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ "The transpose of the array can also be gotten through the `T` property:" - ], - "cell_type": "markdown", - "metadata": {} + ] }, { "cell_type": "code", @@ -1997,10 +2202,20 @@ "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "a:\n array([[1, 2, 3],\n [4, 5, 6],\n [7, 8, 9]], dtype=uint8)\n\ntranspose of a:\n array([[1, 4, 7],\n [2, 5, 8],\n [3, 6, 9]], dtype=uint8)\n\n\n" + "a:\n", + " array([[1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]], dtype=uint8)\n", + "\n", + "transpose of a:\n", + " array([[1, 4, 7],\n", + " [2, 5, 8],\n", + " [3, 6, 9]], dtype=uint8)\n", + "\n", + "\n" ] } ], @@ -2389,10 +2604,14 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "\nTraceback (most recent call last):\n File \"/dev/shm/micropython.py\", line 5, in \nTypeError: unsupported types for __lt__: 'int', 'ndarray'\n\n" + "\n", + "Traceback (most recent call last):\n", + " File \"/dev/shm/micropython.py\", line 5, in \n", + "TypeError: unsupported types for __lt__: 'int', 'ndarray'\n", + "\n" ] } ], @@ -3462,9 +3681,13 @@ } ], "metadata": { + "interpreter": { + "hash": "ce9a02f9f7db620716422019cafa4bc1786ca85daa298b819f6da075e7993842" + }, "kernelspec": { - "name": "python3", - "display_name": "Python 3.8.5 64-bit ('base': conda)" + "display_name": "Python 3", + "language": "python", + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -3524,11 +3747,8 @@ "_Feature" ], "window_display": false - }, - "interpreter": { - "hash": "ce9a02f9f7db620716422019cafa4bc1786ca85daa298b819f6da075e7993842" } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/docs/ulab-numerical.ipynb b/docs/ulab-numerical.ipynb index 66e5b059..be549545 100644 --- a/docs/ulab-numerical.ipynb +++ b/docs/ulab-numerical.ipynb @@ -31,11 +31,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2020-11-03T19:50:51.340719Z", - "start_time": "2020-11-03T19:50:51.330015Z" + "end_time": "2022-01-07T19:16:29.118001Z", + "start_time": "2022-01-07T19:16:29.114692Z" } }, "outputs": [], @@ -52,8 +52,8 @@ "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2020-11-03T19:50:52.899529Z", - "start_time": "2020-11-03T19:50:52.837604Z" + "end_time": "2022-01-07T19:16:37.453883Z", + "start_time": "2022-01-07T19:16:37.422478Z" } }, "outputs": [], @@ -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\", \"/dev/shm/micropython.py\"], \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", @@ -225,7 +225,7 @@ "source": [ "# Numerical\n", "\n", - "Function in the `numerical` sub-module can be called by importing the sub-module first. " + "Function in this section can be used for calculating statistical properties, or manipulating the arrangement of array elements." ] }, { From 665c32dbb43912fa149404420ef654e1c59b12da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 7 Jan 2022 20:53:31 +0100 Subject: [PATCH 57/58] add documentation for compress --- README.md | 2 + docs/manual/source/numpy-functions.rst | 92 ++++++++++++++++++-------- docs/numpy-functions.ipynb | 62 +++++++++++++++-- docs/ulab-convert.ipynb | 6 +- 4 files changed, 125 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 885e635b..7112cadc 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ Documentation can be found on [readthedocs](https://readthedocs.org/) under as well as at [circuitpython-ulab](https://circuitpython.readthedocs.io/en/latest/shared-bindings/ulab/__init__.html). A number of practical examples are listed in Jeff Epler's excellent [circuitpython-ulab](https://learn.adafruit.com/ulab-crunch-numbers-fast-with-circuitpython/overview) overview. +The [tricks](https://micropython-ulab.readthedocs.io/en/latest/ulab-tricks.html) chapter of the user manual discusses +methods by which RAM and speed can be leveraged in particular numerical problems. # Benchmarks diff --git a/docs/manual/source/numpy-functions.rst b/docs/manual/source/numpy-functions.rst index de0987f3..206d641a 100644 --- a/docs/manual/source/numpy-functions.rst +++ b/docs/manual/source/numpy-functions.rst @@ -12,34 +12,35 @@ the firmware was compiled with complex support. 4. `numpy.argmin <#argmin>`__ 5. `numpy.argsort <#argsort>`__ 6. `numpy.clip <#clip>`__ -7. `numpy.conjugate\* <#conjugate>`__ -8. `numpy.convolve\* <#convolve>`__ -9. `numpy.diff <#diff>`__ -10. `numpy.dot <#dot>`__ -11. `numpy.equal <#equal>`__ -12. `numpy.flip\* <#flip>`__ -13. `numpy.imag\* <#imag>`__ -14. `numpy.interp <#interp>`__ -15. `numpy.isfinite <#isfinite>`__ -16. `numpy.isinf <#isinf>`__ -17. `numpy.max <#max>`__ -18. `numpy.maximum <#maximum>`__ -19. `numpy.mean <#mean>`__ -20. `numpy.median <#median>`__ -21. `numpy.min <#min>`__ -22. `numpy.minimum <#minimum>`__ -23. `numpy.not_equal <#equal>`__ -24. `numpy.polyfit <#polyfit>`__ -25. `numpy.polyval <#polyval>`__ -26. `numpy.real\* <#real>`__ -27. `numpy.roll <#roll>`__ -28. `numpy.sort <#sort>`__ -29. `numpy.sort_complex\* <#sort_complex>`__ -30. `numpy.std <#std>`__ -31. `numpy.sum <#sum>`__ -32. `numpy.trace <#trace>`__ -33. `numpy.trapz <#trapz>`__ -34. `numpy.where <#where>`__ +7. `numpy.compress\* <#compress>`__ +8. `numpy.conjugate\* <#conjugate>`__ +9. `numpy.convolve\* <#convolve>`__ +10. `numpy.diff <#diff>`__ +11. `numpy.dot <#dot>`__ +12. `numpy.equal <#equal>`__ +13. `numpy.flip\* <#flip>`__ +14. `numpy.imag\* <#imag>`__ +15. `numpy.interp <#interp>`__ +16. `numpy.isfinite <#isfinite>`__ +17. `numpy.isinf <#isinf>`__ +18. `numpy.max <#max>`__ +19. `numpy.maximum <#maximum>`__ +20. `numpy.mean <#mean>`__ +21. `numpy.median <#median>`__ +22. `numpy.min <#min>`__ +23. `numpy.minimum <#minimum>`__ +24. `numpy.not_equal <#equal>`__ +25. `numpy.polyfit <#polyfit>`__ +26. `numpy.polyval <#polyval>`__ +27. `numpy.real\* <#real>`__ +28. `numpy.roll <#roll>`__ +29. `numpy.sort <#sort>`__ +30. `numpy.sort_complex\* <#sort_complex>`__ +31. `numpy.std <#std>`__ +32. `numpy.sum <#sum>`__ +33. `numpy.trace <#trace>`__ +34. `numpy.trapz <#trapz>`__ +35. `numpy.where <#where>`__ all --- @@ -306,6 +307,41 @@ the output is upcast as in `Binary operators <#Binary-operators>`__. +compress +-------- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.compress.html + +The function returns selected slices of an array along given axis. If +the axis keyword is ``None``, the flattened array is used. + +If the firmware was compiled with complex support, the function can +accept complex arguments. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array(range(6)).reshape((2, 3)) + + print('a:\n', a) + print('\ncompress(a):\n', np.compress([0, 1], a, axis=0)) + +.. parsed-literal:: + + a: + array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0]], dtype=float64) + + compress(a): + array([[3.0, 4.0, 5.0]], dtype=float64) + + + + conjugate --------- diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index f7274252..f115a418 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -31,11 +31,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2022-01-07T19:26:30.392923Z", - "start_time": "2022-01-07T19:26:30.384485Z" + "end_time": "2022-01-07T19:45:28.079350Z", + "start_time": "2022-01-07T19:45:28.073911Z" } }, "outputs": [], @@ -49,11 +49,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2022-01-07T19:26:37.811055Z", - "start_time": "2022-01-07T19:26:37.788500Z" + "end_time": "2022-01-07T19:45:28.654136Z", + "start_time": "2022-01-07T19:45:28.634610Z" } }, "outputs": [], @@ -238,6 +238,7 @@ "1. [numpy.argmin](#argmin)\n", "1. [numpy.argsort](#argsort)\n", "1. [numpy.clip](#clip)\n", + "1. [numpy.compress*](#compress)\n", "1. [numpy.conjugate*](#conjugate)\n", "1. [numpy.convolve*](#convolve)\n", "1. [numpy.diff](#diff)\n", @@ -592,6 +593,55 @@ "print('clipped:\\t', np.clip(a, b, 7))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## compress\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.compress.html\n", + "\n", + "The function returns selected slices of an array along given axis. If the axis keyword is `None`, the flattened array is used.\n", + "\n", + "If the firmware was compiled with complex support, the function can accept complex arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T19:51:44.994323Z", + "start_time": "2022-01-07T19:51:44.978185Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\n", + " array([[0.0, 1.0, 2.0],\n", + " [3.0, 4.0, 5.0]], dtype=float64)\n", + "\n", + "compress(a):\n", + " array([[3.0, 4.0, 5.0]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(6)).reshape((2, 3))\n", + "\n", + "print('a:\\n', a)\n", + "print('\\ncompress(a):\\n', np.compress([0, 1], a, axis=0))" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index c85594f9..70c6fa4e 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -256,11 +256,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": { "ExecuteTime": { - "end_time": "2022-01-07T19:41:24.175364Z", - "start_time": "2022-01-07T19:41:15.326494Z" + "end_time": "2022-01-07T19:52:29.910335Z", + "start_time": "2022-01-07T19:52:28.432391Z" } }, "outputs": [], From 6552cfdc380ee16aab0a742c24ff9350cb292f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 7 Jan 2022 21:19:06 +0100 Subject: [PATCH 58/58] remove misleading comment from ulab.h --- code/ulab.h | 1 - 1 file changed, 1 deletion(-) diff --git a/code/ulab.h b/code/ulab.h index 5c468b65..924f4c7c 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -18,7 +18,6 @@ // // - how many dimensions ulab can handle // - which functions are included in the compiled firmware -// - whether the python syntax is numpy-like, or modular // - whether arrays can be sliced and iterated over // - which binary/unary operators are supported // - whether ulab can deal with complex numbers