Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f10e24147 | ||
|
|
3648d64fbe | ||
|
|
55a4a256d1 | ||
|
|
81f564f689 | ||
|
|
70e364ae4b | ||
|
|
c3b541f043 | ||
|
|
a765c35dc1 | ||
|
|
a9fec6461f | ||
|
|
9e2b1c22dd |
68 changed files with 2957 additions and 3949 deletions
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
|
|
@ -16,11 +16,10 @@ on:
|
|||
|
||||
jobs:
|
||||
micropython:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-24.04
|
||||
- ubuntu-20.04
|
||||
- macOS-latest
|
||||
dims: [1, 2, 3, 4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
|
@ -29,10 +28,10 @@ jobs:
|
|||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.12"
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
|
|
@ -45,10 +44,10 @@ jobs:
|
|||
gcc --version
|
||||
python3 --version
|
||||
- name: Checkout ulab
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Checkout micropython repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: micropython/micropython
|
||||
path: micropython
|
||||
|
|
@ -57,11 +56,10 @@ jobs:
|
|||
run: ./build.sh ${{ matrix.dims }}
|
||||
|
||||
circuitpython:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-24.04
|
||||
- ubuntu-20.04
|
||||
- macOS-latest
|
||||
dims: [1, 2, 3, 4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
|
@ -70,10 +68,10 @@ jobs:
|
|||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.12"
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Versions
|
||||
run: |
|
||||
|
|
@ -81,7 +79,7 @@ jobs:
|
|||
python3 --version
|
||||
|
||||
- name: Checkout ulab
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# ulab
|
||||
|
||||
[](https://micropython-ulab.readthedocs.io/en/latest/index.html)
|
||||
[](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 (`ndarray`s) for numerical data of one to four
|
||||
|
|
@ -36,7 +36,7 @@ detected and handled.
|
|||
## ndarray methods
|
||||
|
||||
`ulab` implements `numpy`'s `ndarray` with the `==`, `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `/`, `*`, `**`,
|
||||
`%`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that
|
||||
`+=`, `-=`, `*=`, `/=`, `**=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that
|
||||
operate element-wise. Type-aware `ndarray`s can be initialised from any `micropython` iterable, lists of
|
||||
iterables via the `array` constructor, or by means of the `arange`, `concatenate`, `diag`, `eye`,
|
||||
`frombuffer`, `full`, `linspace`, `logspace`, `ones`, or `zeros` functions.
|
||||
|
|
@ -47,7 +47,7 @@ the `imag`, and `real` properties are automatically included.
|
|||
|
||||
## `numpy` and `scipy` functions
|
||||
|
||||
In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`numpy.random`](https://micropython-ulab.readthedocs.io/en/latest/numpy-random.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest).
|
||||
In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest).
|
||||
|
||||
## `ulab` utilities
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ of the user manual.
|
|||
1. `MaixPy` https://github.com/sipeed/MaixPy
|
||||
1. `OpenMV` https://github.com/openmv/openmv
|
||||
1. `pimoroni-pico` https://github.com/pimoroni/pimoroni-pico
|
||||
1. `Tulip Creative Computer` https://github.com/shorepine/tulipcc
|
||||
3. `pycom` https://pycom.io/
|
||||
|
||||
## Compiling
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )"
|
|||
rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab
|
||||
dims=${1-2}
|
||||
make -C circuitpython/mpy-cross -j$NPROC
|
||||
make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims
|
||||
sed -e '/MICROPY_PY_UHASHLIB/s/1/0/' < circuitpython/ports/unix/mpconfigport.h > circuitpython/ports/unix/mpconfigport_ulab.h
|
||||
make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-DMP_CONFIGFILE=\"<mpconfigport_ulab.h>\" -Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims
|
||||
|
||||
# bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
USERMODULES_DIR := $(USERMOD_DIR)
|
||||
|
||||
# Add all C files to SRC_USERMOD.
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/integrate/integrate.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/linalg/linalg.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/optimize/optimize.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/signal/signal.c
|
||||
|
|
@ -32,6 +31,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c
|
|||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c
|
||||
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numpy.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/pid/pid.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/scipy.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/user/user.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/utils/utils.c
|
||||
|
|
|
|||
311
code/ndarray.c
311
code/ndarray.c
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2024 Zoltán Vörös
|
||||
* Copyright (c) 2019-2022 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
|
@ -509,9 +509,8 @@ static size_t multiply_size(size_t a, size_t b) {
|
|||
return result;
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype, uint8_t *buffer) {
|
||||
ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype) {
|
||||
// Creates the base ndarray with shape, and initialises the values to straight 0s
|
||||
// optionally, values can be supplied via the last argument
|
||||
ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t);
|
||||
ndarray->base.type = &ulab_ndarray_type;
|
||||
ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype;
|
||||
|
|
@ -537,13 +536,9 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides
|
|||
|
||||
// if the length is 0, still allocate a single item, so that contractions can be handled
|
||||
size_t len = multiply_size(ndarray->itemsize, MAX(1, ndarray->len));
|
||||
uint8_t *array;
|
||||
array = buffer;
|
||||
if(array == NULL) {
|
||||
// this should set all elements to 0, irrespective of the of the dtype (all bits are zero)
|
||||
// we could, perhaps, leave this step out, and initialise the array only, when needed
|
||||
array = m_new0(byte, len);
|
||||
}
|
||||
uint8_t *array = m_new0(byte, len);
|
||||
// this should set all elements to 0, irrespective of the of the dtype (all bits are zero)
|
||||
// we could, perhaps, leave this step out, and initialise the array only, when needed
|
||||
ndarray->array = array;
|
||||
ndarray->origin = array;
|
||||
return ndarray;
|
||||
|
|
@ -552,23 +547,24 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides
|
|||
ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dtype) {
|
||||
// creates a dense array, i.e., one, where the strides are derived directly from the shapes
|
||||
// the function should work in the general n-dimensional case
|
||||
// int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
// strides[ULAB_MAX_DIMS - 1] = (int32_t)ulab_binary_get_size(dtype);
|
||||
// for(size_t i = ULAB_MAX_DIMS; i > 1; i--) {
|
||||
// strides[i-2] = strides[i-1] * MAX(1, shape[i-1]);
|
||||
// }
|
||||
return ndarray_new_ndarray(ndim, shape, NULL, dtype, NULL);
|
||||
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
strides[ULAB_MAX_DIMS-1] = (int32_t)ulab_binary_get_size(dtype);
|
||||
for(size_t i=ULAB_MAX_DIMS; i > 1; i--) {
|
||||
strides[i-2] = strides[i-1] * MAX(1, shape[i-1]);
|
||||
}
|
||||
return ndarray_new_ndarray(ndim, shape, strides, dtype);
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dtype) {
|
||||
// creates a dense array from a tuple
|
||||
// the function should work in the general n-dimensional case
|
||||
if(_shape->len > ULAB_MAX_DIMS) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS)));
|
||||
}
|
||||
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
|
||||
for(size_t i = 0; i < _shape->len; i++) {
|
||||
shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[_shape->len - 1 - i]);
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
for(size_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
if(i >= _shape->len) {
|
||||
shape[ULAB_MAX_DIMS - 1 - i] = 0;
|
||||
} else {
|
||||
shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]);
|
||||
}
|
||||
}
|
||||
return ndarray_new_dense_ndarray(_shape->len, shape, dtype);
|
||||
}
|
||||
|
|
@ -586,10 +582,43 @@ void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target, uint8_t sh
|
|||
}
|
||||
#endif
|
||||
|
||||
ITERATOR_HEAD();
|
||||
memcpy(tarray, sarray, target->itemsize);
|
||||
tarray += target->itemsize;
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
memcpy(tarray, sarray, target->itemsize);
|
||||
tarray += target->itemsize;
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray_new_view(ndarray_obj_t *source, uint8_t ndim, size_t *shape, int32_t *strides, int32_t offset) {
|
||||
|
|
@ -625,7 +654,7 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) {
|
|||
if(source->boolean) {
|
||||
dtype = NDARRAY_BOOL;
|
||||
}
|
||||
ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype, NULL);
|
||||
ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype);
|
||||
ndarray_copy_array(source, ndarray, 0);
|
||||
return ndarray;
|
||||
}
|
||||
|
|
@ -643,36 +672,69 @@ ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *source, uint8_t dty
|
|||
uint8_t complex_size = 2 * sizeof(mp_float_t);
|
||||
#endif
|
||||
|
||||
ITERATOR_HEAD()
|
||||
mp_obj_t item;
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(source->dtype == NDARRAY_COMPLEX) {
|
||||
if(dtype != NDARRAY_COMPLEX) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex type"));
|
||||
} else {
|
||||
memcpy(array, sarray, complex_size);
|
||||
}
|
||||
} else {
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) {
|
||||
// floats must be treated separately, because they can't directly be converted to integer types
|
||||
mp_float_t f = ndarray_get_float_value(sarray, source->dtype);
|
||||
item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(f));
|
||||
} else {
|
||||
item = mp_binary_get_val_array(source->dtype, sarray, 0);
|
||||
}
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(dtype == NDARRAY_COMPLEX) {
|
||||
ndarray_set_value(NDARRAY_FLOAT, array, 0, item);
|
||||
} else {
|
||||
ndarray_set_value(dtype, array, 0, item);
|
||||
}
|
||||
}
|
||||
#else
|
||||
ndarray_set_value(dtype, array, 0, item);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_obj_t item;
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(source->dtype == NDARRAY_COMPLEX) {
|
||||
if(dtype != NDARRAY_COMPLEX) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex type"));
|
||||
} else {
|
||||
memcpy(array, sarray, complex_size);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) {
|
||||
// floats must be treated separately, because they can't directly be converted to integer types
|
||||
mp_float_t f = ndarray_get_float_value(sarray, source->dtype);
|
||||
item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(f));
|
||||
} else {
|
||||
item = mp_binary_get_val_array(source->dtype, sarray, 0);
|
||||
}
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(dtype == NDARRAY_COMPLEX) {
|
||||
ndarray_set_value(NDARRAY_FLOAT, array, 0, item);
|
||||
} else {
|
||||
ndarray_set_value(dtype, array, 0, item);
|
||||
}
|
||||
}
|
||||
#else
|
||||
ndarray_set_value(dtype, array, 0, item);
|
||||
#endif
|
||||
array += ndarray->itemsize;
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
array += ndarray->itemsize;
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#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 ndarray;
|
||||
}
|
||||
|
||||
|
|
@ -699,21 +761,54 @@ mp_obj_t ndarray_byteswap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
|
|||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
ITERATOR_HEAD();
|
||||
if(self->dtype == NDARRAY_FLOAT) {
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
SWAP(uint8_t, array[0], array[3]);
|
||||
SWAP(uint8_t, array[1], array[2]);
|
||||
#else
|
||||
SWAP(uint8_t, array[0], array[7]);
|
||||
SWAP(uint8_t, array[1], array[6]);
|
||||
SWAP(uint8_t, array[2], array[5]);
|
||||
SWAP(uint8_t, array[3], array[4]);
|
||||
#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
|
||||
} else {
|
||||
SWAP(uint8_t, array[0], array[1]);
|
||||
}
|
||||
ITERATOR_TAIL(ndarray, array);
|
||||
size_t l = 0;
|
||||
do {
|
||||
if(self->dtype == NDARRAY_FLOAT) {
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
SWAP(uint8_t, array[0], array[3]);
|
||||
SWAP(uint8_t, array[1], array[2]);
|
||||
#else
|
||||
SWAP(uint8_t, array[0], array[7]);
|
||||
SWAP(uint8_t, array[1], array[6]);
|
||||
SWAP(uint8_t, array[2], array[5]);
|
||||
SWAP(uint8_t, array[3], array[4]);
|
||||
#endif
|
||||
} else {
|
||||
SWAP(uint8_t, array[0], array[1]);
|
||||
}
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < ndarray->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < ndarray->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
|
@ -826,7 +921,7 @@ ndarray_obj_t *ndarray_from_iterable(mp_obj_t obj, uint8_t dtype) {
|
|||
return ndarray;
|
||||
}
|
||||
|
||||
static uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } },
|
||||
|
|
@ -849,7 +944,7 @@ static uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
return _dtype;
|
||||
}
|
||||
|
||||
static mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
uint8_t dtype = ndarray_init_helper(n_args, args, kw_args);
|
||||
|
||||
if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
|
||||
|
|
@ -1342,10 +1437,43 @@ mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
|
|||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
|
||||
if(memcmp(order, "C", 1) == 0) { // C-type ordering
|
||||
ITERATOR_HEAD();
|
||||
memcpy(array, sarray, self->itemsize);
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
ITERATOR_TAIL(self, sarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
memcpy(array, sarray, self->itemsize);
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
sarray += self->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < self->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= self->strides[ULAB_MAX_DIMS - 1] * self->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += self->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < self->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= self->strides[ULAB_MAX_DIMS - 2] * self->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += self->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < self->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= self->strides[ULAB_MAX_DIMS - 3] * self->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += self->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < self->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
} else { // 'F', Fortran-type ordering
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
|
|
@ -1398,13 +1526,6 @@ mp_obj_t ndarray_itemsize(mp_obj_t self_in) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_NDIM
|
||||
mp_obj_t ndarray_ndim(mp_obj_t self_in) {
|
||||
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return MP_OBJ_NEW_SMALL_INT(self->ndim);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_SHAPE
|
||||
mp_obj_t ndarray_shape(mp_obj_t self_in) {
|
||||
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
|
@ -1489,7 +1610,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) {
|
|||
|
||||
if(mp_obj_is_int(obj)) {
|
||||
int32_t ivalue = mp_obj_get_int(obj);
|
||||
if((ivalue < -32768) || (ivalue > 65535)) {
|
||||
if((ivalue < -32767) || (ivalue > 32767)) {
|
||||
// the integer value clearly does not fit the ulab integer types, so move on to float
|
||||
ndarray = ndarray_new_linear_array(1, NDARRAY_FLOAT);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
|
@ -1497,7 +1618,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) {
|
|||
} else {
|
||||
uint8_t dtype;
|
||||
if(ivalue < 0) {
|
||||
if(ivalue >= -128) {
|
||||
if(ivalue > -128) {
|
||||
dtype = NDARRAY_INT8;
|
||||
} else {
|
||||
dtype = NDARRAY_INT16;
|
||||
|
|
@ -1648,12 +1769,6 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) {
|
|||
return ndarray_inplace_ams(lhs, rhs, rstrides, op);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_INPLACE_MODULO
|
||||
case MP_BINARY_OP_INPLACE_MODULO:
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
|
||||
return ndarray_inplace_modulo(lhs, rhs, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_INPLACE_MULTIPLY
|
||||
case MP_BINARY_OP_INPLACE_MULTIPLY:
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
|
||||
|
|
@ -1709,12 +1824,6 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) {
|
|||
return ndarray_binary_add(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_BINARY_OP_MODULO
|
||||
case MP_BINARY_OP_MODULO:
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
|
||||
return ndarray_binary_modulo(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_BINARY_OP_MULTIPLY
|
||||
case MP_BINARY_OP_MULTIPLY:
|
||||
return ndarray_binary_multiply(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
|
|
@ -1779,7 +1888,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
|
|||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(self->dtype == NDARRAY_COMPLEX) {
|
||||
int32_t *strides = strides_from_shape(self->shape, NDARRAY_FLOAT);
|
||||
ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT, NULL);
|
||||
ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT);
|
||||
ndarray = MP_OBJ_TO_PTR(carray_abs(self, target));
|
||||
} else {
|
||||
#endif
|
||||
|
|
@ -1912,7 +2021,7 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) {
|
|||
mp_obj_t *items = m_new(mp_obj_t, 1);
|
||||
items[0] = _shape;
|
||||
shape = mp_obj_new_tuple(1, items);
|
||||
} else { // at this point it's certain that _shape is a tuple
|
||||
} else {
|
||||
shape = MP_OBJ_TO_PTR(_shape);
|
||||
}
|
||||
|
||||
|
|
@ -1963,7 +2072,11 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) {
|
|||
if(inplace) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("cannot assign new shape"));
|
||||
}
|
||||
ndarray = ndarray_new_dense_ndarray(shape->len, new_shape, source->dtype);
|
||||
if(mp_obj_is_type(_shape, &mp_type_tuple)) {
|
||||
ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype);
|
||||
} else {
|
||||
ndarray = ndarray_new_linear_array(source->len, source->dtype);
|
||||
}
|
||||
ndarray_copy_array(source, ndarray, 0);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ int32_t *ndarray_contract_strides(ndarray_obj_t *, uint8_t );
|
|||
ndarray_obj_t *ndarray_from_iterable(mp_obj_t , uint8_t );
|
||||
ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t , size_t *, uint8_t );
|
||||
ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *, uint8_t );
|
||||
ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t , uint8_t *);
|
||||
ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t );
|
||||
ndarray_obj_t *ndarray_new_linear_array(size_t , uint8_t );
|
||||
ndarray_obj_t *ndarray_new_view(ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t );
|
||||
bool ndarray_is_dense(ndarray_obj_t *);
|
||||
|
|
@ -232,10 +232,6 @@ mp_obj_t ndarray_dtype(mp_obj_t );
|
|||
mp_obj_t ndarray_itemsize(mp_obj_t );
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_NDIM
|
||||
mp_obj_t ndarray_ndim(mp_obj_t );
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_SIZE
|
||||
mp_obj_t ndarray_size(mp_obj_t );
|
||||
#endif
|
||||
|
|
@ -713,89 +709,4 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t );
|
|||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
|
||||
|
||||
|
||||
// iterator macro for traversing arrays over all dimensions
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _k_ = 0;\
|
||||
do {\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
|
||||
_k_++;\
|
||||
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _j_ = 0;\
|
||||
do {\
|
||||
size_t _k_ = 0;\
|
||||
do {\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
|
||||
_k_++;\
|
||||
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
|
||||
_j_++;\
|
||||
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _i_ = 0;\
|
||||
do {\
|
||||
size_t _j_ = 0;\
|
||||
do {\
|
||||
size_t _k_ = 0;\
|
||||
do {\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
|
||||
_k_++;\
|
||||
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
|
||||
_j_++;\
|
||||
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 3] * (_source_)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 4];\
|
||||
_i_++;\
|
||||
} while(_i_ < (_source_)->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -248,105 +248,6 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
|||
}
|
||||
#endif /* NDARRAY_HAS_BINARY_OP_ADD */
|
||||
|
||||
#if NDARRAY_HAS_BINARY_OP_MODULO
|
||||
mp_obj_t ndarray_binary_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
|
||||
ndarray_obj_t *results = NULL;
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
BINARY_LOOP(results, mp_float_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
BINARY_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, %);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_FLOAT) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
#endif /* NDARRAY_HAS_BINARY_OP_MODULO */
|
||||
|
||||
#if NDARRAY_HAS_BINARY_OP_MULTIPLY
|
||||
mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
|
|
@ -1173,29 +1074,6 @@ mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rs
|
|||
}
|
||||
#endif /* NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT */
|
||||
|
||||
|
||||
#if NDARRAY_HAS_INPLACE_MODULO
|
||||
mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) {
|
||||
if((lhs->dtype != NDARRAY_FLOAT) && (rhs->dtype == NDARRAY_FLOAT)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type"));
|
||||
}
|
||||
if(lhs->dtype == NDARRAY_FLOAT) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
INLINE_MODULO_FLOAT_LOOP(lhs, uint8_t, larray, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_UINT8) {
|
||||
INLINE_MODULO_FLOAT_LOOP(lhs, int8_t, larray, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
INLINE_MODULO_FLOAT_LOOP(lhs, uint16_t, larray, rarray, rstrides);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
INLINE_MODULO_FLOAT_LOOP(lhs, int16_t, larray, rarray, rstrides);
|
||||
} else {
|
||||
INLINE_MODULO_FLOAT_LOOP(lhs, mp_float_t, larray, rarray, rstrides);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(lhs);
|
||||
}
|
||||
#endif /* NDARRAY_HAS_INPLACE_MODULO */
|
||||
|
||||
#if NDARRAY_HAS_INPLACE_TRUE_DIVIDE
|
||||
mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) {
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
mp_obj_t ndarray_binary_equality(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
|
||||
mp_obj_t ndarray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_modulo(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
|
||||
mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
|
|
@ -22,7 +21,6 @@ mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size
|
|||
mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
|
||||
mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t );
|
||||
mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
|
||||
|
|
@ -539,176 +537,3 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
|||
} while(0)
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
#define MODULO_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = MICROPY_FLOAT_C_FUN(fmod)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
|
||||
#define INPLACE_MODULO_FLOAT1(results, type_right, larray, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((mp_float_t *)larray) = MICROPY_FLOAT_C_FUN(fmod)(*((mp_float_t *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define INPLACE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021-2025 Zoltán Vörös
|
||||
* Copyright (c) 2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
@ -42,11 +42,6 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|||
dest[0] = ndarray_itemsize(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_NDIM
|
||||
case MP_QSTR_ndim:
|
||||
dest[0] = ndarray_ndim(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_SHAPE
|
||||
case MP_QSTR_shape:
|
||||
dest[0] = ndarray_shape(self_in);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020-2025 Zoltán Vörös
|
||||
* 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _NDARRAY_PROPERTIES_
|
||||
|
|
@ -36,10 +36,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_n
|
|||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_NDIM
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_ndim_obj, ndarray_ndim);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_SHAPE
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL,
|
|||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
|
|
@ -151,7 +151,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp);
|
|||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
|
|
|
|||
|
|
@ -25,11 +25,9 @@
|
|||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
|
||||
//| import builtins
|
||||
//|
|
||||
//| import ulab.numpy
|
||||
|
||||
//| def real(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| def real(val):
|
||||
//| """
|
||||
//| Return the real part of the complex argument, which can be
|
||||
//| either an ndarray, or a scalar."""
|
||||
|
|
@ -56,7 +54,7 @@ mp_obj_t carray_real(mp_obj_t _source) {
|
|||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real);
|
||||
|
||||
//| def imag(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| def imag(val):
|
||||
//| """
|
||||
//| Return the imaginary part of the complex argument, which can be
|
||||
//| either an ndarray, or a scalar."""
|
||||
|
|
@ -84,9 +82,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag);
|
|||
|
||||
#if ULAB_NUMPY_HAS_CONJUGATE
|
||||
|
||||
//| def conjugate(
|
||||
//| val: builtins.complex | ulab.numpy.ndarray
|
||||
//| ) -> builtins.complex | ulab.numpy.ndarray:
|
||||
//| def conjugate(val):
|
||||
//| """
|
||||
//| Return the conjugate of the complex argument, which can be
|
||||
//| either an ndarray, or a scalar."""
|
||||
|
|
|
|||
|
|
@ -140,23 +140,7 @@ static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype)
|
|||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_CLIP
|
||||
//| def clip(
|
||||
//| a: _ScalarOrArrayLike,
|
||||
//| a_min: _ScalarOrArrayLike,
|
||||
//| a_max: _ScalarOrArrayLike,
|
||||
//| ) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Clips (limits) the values in an array.
|
||||
//|
|
||||
//| :param a: Scalar or array containing elements to clip.
|
||||
//| :param a_min: Minimum value, it will be broadcast against ``a``.
|
||||
//| :param a_max: Maximum value, it will be broadcast against ``a``.
|
||||
//| :return:
|
||||
//| A scalar or array with the elements of ``a``, but where
|
||||
//| values < ``a_min`` are replaced with ``a_min``, and those
|
||||
//| > ``a_max`` with ``a_max``.
|
||||
//| """
|
||||
//| ...
|
||||
|
||||
mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) {
|
||||
// Note: this function could be made faster by implementing a single-loop comparison in
|
||||
// RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we
|
||||
|
|
@ -182,18 +166,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip);
|
|||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_EQUAL
|
||||
//| def equal(x: _ScalarOrArrayLike, y: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Returns ``x == y`` element-wise.
|
||||
//|
|
||||
//| :param x, y:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A boolean scalar or array with the element-wise result of ``x == y``.
|
||||
//| """
|
||||
//| ...
|
||||
|
||||
mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) {
|
||||
return compare_equal_helper(x1, x2, COMPARE_EQUAL);
|
||||
}
|
||||
|
|
@ -202,21 +175,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal);
|
|||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_NOTEQUAL
|
||||
//| def not_equal(
|
||||
//| x: _ScalarOrArrayLike,
|
||||
//| y: _ScalarOrArrayLike,
|
||||
//| ) -> Union[_bool, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| Returns ``x != y`` element-wise.
|
||||
//|
|
||||
//| :param x, y:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A boolean scalar or array with the element-wise result of ``x != y``.
|
||||
//| """
|
||||
//| ...
|
||||
|
||||
mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
|
||||
return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL);
|
||||
}
|
||||
|
|
@ -260,14 +219,48 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
|
|||
}
|
||||
uint8_t *xarray = (uint8_t *)x->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = *(mp_float_t *)xarray;
|
||||
if(isnan(value)) {
|
||||
*rarray++ = 0;
|
||||
} else {
|
||||
*rarray++ = isinf(value) ? mask : 1 - mask;
|
||||
}
|
||||
ITERATOR_TAIL(x, xarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t value = *(mp_float_t *)xarray;
|
||||
if(isnan(value)) {
|
||||
*rarray++ = 0;
|
||||
} else {
|
||||
*rarray++ = isinf(value) ? mask : 1 - mask;
|
||||
}
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < x->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
xarray -= x->strides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < x->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
xarray -= x->strides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < x->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
xarray -= x->strides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < x->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
} else {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
|
||||
|
|
@ -277,16 +270,6 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
|
|||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ISFINITE
|
||||
//| def isfinite(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| Tests element-wise for finiteness (i.e., it should not be infinity or a NaN).
|
||||
//|
|
||||
//| :param x: Input scalar or ndarray.
|
||||
//| :return:
|
||||
//| A boolean scalar or array with True where ``x`` is finite, and
|
||||
//| False otherwise.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_isfinite(mp_obj_t _x) {
|
||||
return compare_isinf_isfinite(_x, 0);
|
||||
}
|
||||
|
|
@ -295,16 +278,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite);
|
|||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ISINF
|
||||
//| def isinf(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| Tests element-wise for positive or negative infinity.
|
||||
//|
|
||||
//| :param x: Input scalar or ndarray.
|
||||
//| :return:
|
||||
//| A boolean scalar or array with True where ``x`` is positive or
|
||||
//| negative infinity, and False otherwise.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_isinf(mp_obj_t _x) {
|
||||
return compare_isinf_isfinite(_x, 1);
|
||||
}
|
||||
|
|
@ -313,18 +286,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf);
|
|||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_MAXIMUM
|
||||
//| def maximum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Returns the element-wise maximum.
|
||||
//|
|
||||
//| :param x1, x2:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A scalar or array with the element-wise maximum of ``x1`` and ``x2``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) {
|
||||
// extra round, so that we can return maximum(3, 4) properly
|
||||
mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM);
|
||||
|
|
@ -340,18 +301,6 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum);
|
|||
|
||||
#if ULAB_NUMPY_HAS_MINIMUM
|
||||
|
||||
//| def minimum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Returns the element-wise minimum.
|
||||
//|
|
||||
//| :param x1, x2:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A scalar or array with the element-wise minimum of ``x1`` and ``x2``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) {
|
||||
// extra round, so that we can return minimum(3, 4) properly
|
||||
mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM);
|
||||
|
|
@ -367,17 +316,6 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum);
|
|||
|
||||
#if ULAB_NUMPY_HAS_NONZERO
|
||||
|
||||
//| def nonzero(x: _ScalarOrArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Returns the indices of elements that are non-zero.
|
||||
//|
|
||||
//| :param x:
|
||||
//| Input scalar or array. If ``x`` is a scalar, it is treated
|
||||
//| as a single-element 1-d array.
|
||||
//| :return:
|
||||
//| An array of indices that are non-zero.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_nonzero(mp_obj_t x) {
|
||||
ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
|
||||
// since ndarray_new_linear_array calls m_new0, the content of zero is a single zero
|
||||
|
|
@ -508,27 +446,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_nonzero_obj, compare_nonzero);
|
|||
|
||||
#if ULAB_NUMPY_HAS_WHERE
|
||||
|
||||
//| def where(
|
||||
//| condition: _ScalarOrArrayLike,
|
||||
//| x: _ScalarOrArrayLike,
|
||||
//| y: _ScalarOrArrayLike,
|
||||
//| ) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Returns elements from ``x`` or ``y`` depending on ``condition``.
|
||||
//|
|
||||
//| :param condition:
|
||||
//| Input scalar or array. If an element (or scalar) is truthy,
|
||||
//| the corresponding element from ``x`` is chosen, otherwise
|
||||
//| ``y`` is used. ``condition``, ``x`` and ``y`` must also be
|
||||
//| broadcastable to the same shape (which becomes the output
|
||||
//| shape.)
|
||||
//| :param x, y:
|
||||
//| Input scalar or array.
|
||||
//| :return:
|
||||
//| An array with elements from ``x`` when ``condition`` is
|
||||
//| truthy, and ``y`` elsewhere.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) {
|
||||
// this implementation will work with ndarrays, and scalars only
|
||||
ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2019-2024 Zoltán Vörös
|
||||
* 2019-2021 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
|
|
@ -776,235 +776,6 @@ mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|||
MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_TAKE
|
||||
//| def take(
|
||||
//| a: ulab.numpy.ndarray,
|
||||
//| indices: _ArrayLike,
|
||||
//| axis: Optional[int] = None,
|
||||
//| out: Optional[ulab.numpy.ndarray] = None,
|
||||
//| mode: Optional[str] = None) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| .. param: a
|
||||
//| The source array.
|
||||
//| .. param: indices
|
||||
//| The indices of the values to extract.
|
||||
//| .. param: axis
|
||||
//| The axis over which to select values. By default, the flattened input array is used.
|
||||
//| .. param: out
|
||||
//| If provided, the result will be placed in this array. It should be of the appropriate shape and dtype.
|
||||
//| .. param: mode
|
||||
//| Specifies how out-of-bounds indices will behave.
|
||||
//| - `raise`: raise an error (default)
|
||||
//| - `wrap`: wrap around
|
||||
//| - `clip`: clip to the range
|
||||
//| `clip` mode means that all indices that are too large are replaced by the
|
||||
//| index that addresses the last element along that axis. Note that this disables
|
||||
//| indexing with negative numbers.
|
||||
//|
|
||||
//| Return a new array."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
enum CREATE_TAKE_MODE {
|
||||
CREATE_TAKE_RAISE,
|
||||
CREATE_TAKE_WRAP,
|
||||
CREATE_TAKE_CLIP,
|
||||
};
|
||||
|
||||
mp_obj_t create_take(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input is not an array"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
int8_t axis = 0;
|
||||
int8_t axis_index = 0;
|
||||
int32_t axis_len;
|
||||
uint8_t mode = CREATE_TAKE_RAISE;
|
||||
uint8_t ndim;
|
||||
|
||||
// axis keyword argument
|
||||
if(args[2].u_obj == mp_const_none) {
|
||||
// work with the flattened array
|
||||
axis_len = a->len;
|
||||
ndim = 1;
|
||||
} else { // i.e., axis is an integer
|
||||
// TODO: this pops up at quite a few places, write it as a function
|
||||
axis = mp_obj_get_int(args[2].u_obj);
|
||||
ndim = a->ndim;
|
||||
if(axis < 0) axis += a->ndim;
|
||||
if((axis < 0) || (axis > a->ndim - 1)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("index out of range"));
|
||||
}
|
||||
axis_index = ULAB_MAX_DIMS - a->ndim + axis;
|
||||
axis_len = (int32_t)a->shape[axis_index];
|
||||
}
|
||||
|
||||
size_t _len;
|
||||
// mode keyword argument
|
||||
if(mp_obj_is_str(args[4].u_obj)) {
|
||||
const char *_mode = mp_obj_str_get_data(args[4].u_obj, &_len);
|
||||
if(memcmp(_mode, "raise", 5) == 0) {
|
||||
mode = CREATE_TAKE_RAISE;
|
||||
} else if(memcmp(_mode, "wrap", 4) == 0) {
|
||||
mode = CREATE_TAKE_WRAP;
|
||||
} else if(memcmp(_mode, "clip", 4) == 0) {
|
||||
mode = CREATE_TAKE_CLIP;
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("mode should be raise, wrap or clip"));
|
||||
}
|
||||
}
|
||||
|
||||
size_t indices_len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj));
|
||||
|
||||
size_t *indices = m_new(size_t, indices_len);
|
||||
|
||||
mp_obj_iter_buf_t buf;
|
||||
mp_obj_t item, iterable = mp_getiter(args[1].u_obj, &buf);
|
||||
|
||||
size_t z = 0;
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
int32_t index = mp_obj_get_int(item);
|
||||
if(mode == CREATE_TAKE_RAISE) {
|
||||
if(index < 0) {
|
||||
index += axis_len;
|
||||
}
|
||||
if((index < 0) || (index > axis_len - 1)) {
|
||||
m_del(size_t, indices, indices_len);
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("index out of range"));
|
||||
}
|
||||
} else if(mode == CREATE_TAKE_WRAP) {
|
||||
index %= axis_len;
|
||||
} else { // mode == CREATE_TAKE_CLIP
|
||||
if(index < 0) {
|
||||
m_del(size_t, indices, indices_len);
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("index must not be negative"));
|
||||
}
|
||||
if(index > axis_len - 1) {
|
||||
index = axis_len - 1;
|
||||
}
|
||||
}
|
||||
indices[z++] = (size_t)index;
|
||||
}
|
||||
|
||||
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
|
||||
if(args[2].u_obj == mp_const_none) { // flattened array
|
||||
shape[ULAB_MAX_DIMS - 1] = indices_len;
|
||||
} else {
|
||||
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
shape[i] = a->shape[i];
|
||||
if(i == axis_index) {
|
||||
shape[i] = indices_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ndarray_obj_t *out = NULL;
|
||||
if(args[3].u_obj == mp_const_none) {
|
||||
// no output was supplied
|
||||
out = ndarray_new_dense_ndarray(ndim, shape, a->dtype);
|
||||
} else {
|
||||
// TODO: deal with last argument being false!
|
||||
out = ulab_tools_inspect_out(args[3].u_obj, a->dtype, ndim, shape, true);
|
||||
}
|
||||
|
||||
#if ULAB_MAX_DIMS > 1 // we can save the hassle, if there is only one possible dimension
|
||||
if((args[2].u_obj == mp_const_none) || (a->ndim == 1)) { // flattened array
|
||||
#endif
|
||||
uint8_t *out_array = (uint8_t *)out->array;
|
||||
for(size_t x = 0; x < indices_len; x++) {
|
||||
uint8_t *a_array = (uint8_t *)a->array;
|
||||
size_t remainder = indices[x];
|
||||
uint8_t q = ULAB_MAX_DIMS - 1;
|
||||
do {
|
||||
size_t div = (remainder / a->shape[q]);
|
||||
a_array += remainder * a->strides[q];
|
||||
remainder -= div * a->shape[q];
|
||||
q--;
|
||||
} while(q > ULAB_MAX_DIMS - a->ndim);
|
||||
// NOTE: for floats and complexes, this might be
|
||||
// better with memcpy(out_array, a_array, a->itemsize)
|
||||
for(uint8_t p = 0; p < a->itemsize; p++) {
|
||||
out_array[p] = a_array[p];
|
||||
}
|
||||
out_array += a->itemsize;
|
||||
}
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
} else {
|
||||
// move the axis shape/stride to the leftmost position:
|
||||
SWAP(size_t, a->shape[0], a->shape[axis_index]);
|
||||
SWAP(size_t, out->shape[0], out->shape[axis_index]);
|
||||
SWAP(int32_t, a->strides[0], a->strides[axis_index]);
|
||||
SWAP(int32_t, out->strides[0], out->strides[axis_index]);
|
||||
|
||||
for(size_t x = 0; x < indices_len; x++) {
|
||||
uint8_t *a_array = (uint8_t *)a->array;
|
||||
uint8_t *out_array = (uint8_t *)out->array;
|
||||
a_array += indices[x] * a->strides[0];
|
||||
out_array += x * out->strides[0];
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
// NOTE: for floats and complexes, this might be
|
||||
// better with memcpy(out_array, a_array, a->itemsize)
|
||||
for(uint8_t p = 0; p < a->itemsize; p++) {
|
||||
out_array[p] = a_array[p];
|
||||
}
|
||||
out_array += out->strides[ULAB_MAX_DIMS - 1];
|
||||
a_array += a->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < a->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
out_array -= out->strides[ULAB_MAX_DIMS - 1] * out->shape[ULAB_MAX_DIMS - 1];
|
||||
out_array += out->strides[ULAB_MAX_DIMS - 2];
|
||||
a_array -= a->strides[ULAB_MAX_DIMS - 1] * a->shape[ULAB_MAX_DIMS - 1];
|
||||
a_array += a->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < a->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
out_array -= out->strides[ULAB_MAX_DIMS - 2] * out->shape[ULAB_MAX_DIMS - 2];
|
||||
out_array += out->strides[ULAB_MAX_DIMS - 3];
|
||||
a_array -= a->strides[ULAB_MAX_DIMS - 2] * a->shape[ULAB_MAX_DIMS - 2];
|
||||
a_array += a->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < a->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// revert back to the original order
|
||||
SWAP(size_t, a->shape[0], a->shape[axis_index]);
|
||||
SWAP(size_t, out->shape[0], out->shape[axis_index]);
|
||||
SWAP(int32_t, a->strides[0], a->strides[axis_index]);
|
||||
SWAP(int32_t, out->strides[0], out->strides[axis_index]);
|
||||
}
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
m_del(size_t, indices, indices_len);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(create_take_obj, 2, create_take);
|
||||
#endif /* ULAB_NUMPY_HAS_TAKE */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ZEROS
|
||||
//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
|
|
@ -1067,10 +838,19 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
|
|||
len = count;
|
||||
}
|
||||
}
|
||||
ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t);
|
||||
ndarray->base.type = &ulab_ndarray_type;
|
||||
ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype;
|
||||
ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC;
|
||||
ndarray->ndim = 1;
|
||||
ndarray->len = len;
|
||||
ndarray->itemsize = sz;
|
||||
ndarray->shape[ULAB_MAX_DIMS - 1] = len;
|
||||
ndarray->strides[ULAB_MAX_DIMS - 1] = sz;
|
||||
|
||||
size_t *shape = ndarray_shape_vector(0, 0, 0, len);
|
||||
uint8_t *buffer = bufinfo.buf;
|
||||
return ndarray_new_ndarray(1, shape, NULL, dtype, buffer + offset);
|
||||
ndarray->array = buffer + offset;
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,11 +62,6 @@ mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *);
|
|||
MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_TAKE
|
||||
mp_obj_t create_take(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_take_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ZEROS
|
||||
mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2024 Zoltán Vörös
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
|
@ -43,16 +43,16 @@
|
|||
//|
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
static mp_obj_t fft_fft(mp_obj_t arg) {
|
||||
return fft_fft_ifft(arg, FFT_FFT);
|
||||
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(n_args, args[0], args[1], FFT_FFT);
|
||||
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_FFT);
|
||||
} else {
|
||||
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_FFT);
|
||||
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_FFT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
|
|||
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
static mp_obj_t fft_ifft(mp_obj_t arg) {
|
||||
return fft_fft_ifft(arg, FFT_IFFT);
|
||||
return fft_fft_ifft_spectrogram(arg, FFT_IFFT);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
|
||||
|
|
@ -79,22 +79,22 @@ MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
|
|||
static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
|
||||
NOT_IMPLEMENTED_FOR_COMPLEX()
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft(n_args, args[0], args[1], FFT_IFFT);
|
||||
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_IFFT);
|
||||
} else {
|
||||
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_IFFT);
|
||||
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_IFFT);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_fft_globals_table[] = {
|
||||
STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fft) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&fft_fft_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ifft), MP_ROM_PTR(&fft_ifft_obj) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table);
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_fft_module = {
|
||||
.base = { &mp_type_module },
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2024 Zoltán Vörös
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
imag[i] = data[2i+1]
|
||||
|
||||
*/
|
||||
void fft_kernel(mp_float_t *data, size_t n, int isign) {
|
||||
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;
|
||||
|
|
@ -94,9 +94,9 @@ void fft_kernel(mp_float_t *data, size_t n, int isign) {
|
|||
/*
|
||||
* The following function is a helper interface to the python side.
|
||||
* It has been factored out from fft.c, so that the same argument parsing
|
||||
* routine can be called from utils.spectrogram.
|
||||
* routine can be called from scipy.signal.spectrogram.
|
||||
*/
|
||||
mp_obj_t fft_fft_ifft(mp_obj_t data_in, uint8_t type) {
|
||||
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(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
|
||||
}
|
||||
|
|
@ -134,10 +134,20 @@ mp_obj_t fft_fft_ifft(mp_obj_t data_in, uint8_t type) {
|
|||
}
|
||||
data -= 2 * len;
|
||||
|
||||
if(type == FFT_FFT) {
|
||||
fft_kernel(data, len, 1);
|
||||
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(data, len, -1);
|
||||
fft_kernel_complex(data, len, -1);
|
||||
// TODO: numpy accepts the norm keyword argument
|
||||
for(size_t i = 0; i < 2 * len; i++) {
|
||||
*data++ /= len;
|
||||
|
|
@ -192,7 +202,7 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) {
|
|||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
|
||||
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(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
|
||||
}
|
||||
|
|
@ -248,8 +258,15 @@ mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t t
|
|||
data_im -= len;
|
||||
}
|
||||
|
||||
if(type == FFT_FFT) {
|
||||
if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) {
|
||||
fft_kernel(data_re, data_im, len, 1);
|
||||
if(type == FFT_SPECTROGRAM) {
|
||||
for(size_t i=0; i < len; i++) {
|
||||
*data_re = MICROPY_FLOAT_C_FUN(sqrt)(*data_re * *data_re + *data_im * *data_im);
|
||||
data_re++;
|
||||
data_im++;
|
||||
}
|
||||
}
|
||||
} else { // inverse transform
|
||||
fft_kernel(data_re, data_im, len, -1);
|
||||
// TODO: numpy accepts the norm keyword argument
|
||||
|
|
@ -258,9 +275,13 @@ mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t t
|
|||
*data_im++ /= len;
|
||||
}
|
||||
}
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = MP_OBJ_FROM_PTR(out_re);
|
||||
tuple[1] = MP_OBJ_FROM_PTR(out_im);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
if(type == FFT_SPECTROGRAM) {
|
||||
return MP_OBJ_FROM_PTR(out_re);
|
||||
} else {
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = MP_OBJ_FROM_PTR(out_re);
|
||||
tuple[1] = MP_OBJ_FROM_PTR(out_im);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
}
|
||||
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
|
||||
|
|
|
|||
|
|
@ -14,14 +14,15 @@
|
|||
enum FFT_TYPE {
|
||||
FFT_FFT,
|
||||
FFT_IFFT,
|
||||
FFT_SPECTROGRAM,
|
||||
};
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
void fft_kernel(mp_float_t *, size_t , int );
|
||||
mp_obj_t fft_fft_ifft(mp_obj_t , uint8_t );
|
||||
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(size_t , mp_obj_t , mp_obj_t , uint8_t );
|
||||
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_ */
|
||||
|
|
|
|||
|
|
@ -239,11 +239,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(io_load_obj, io_load);
|
|||
|
||||
#if ULAB_NUMPY_HAS_LOADTXT
|
||||
static void io_assign_value(const char *clipboard, uint8_t len, ndarray_obj_t *ndarray, size_t *idx, uint8_t dtype) {
|
||||
#if MICROPY_PY_BUILTINS_COMPLEX
|
||||
mp_obj_t value = mp_parse_num_decimal(clipboard, len, false, false, NULL);
|
||||
#else
|
||||
mp_obj_t value = mp_parse_num_float(clipboard, len, false, NULL);
|
||||
#endif
|
||||
if(dtype != NDARRAY_FLOAT) {
|
||||
mp_float_t _value = mp_obj_get_float(value);
|
||||
value = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(_value));
|
||||
|
|
@ -338,7 +334,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
|
|||
buffer[read] = '\0';
|
||||
offset = buffer;
|
||||
while(*offset != '\0') {
|
||||
while(*offset == comment_char) {
|
||||
if(*offset == comment_char) {
|
||||
// clear the line till the end, or the buffer's end
|
||||
while((*offset != '\0')) {
|
||||
offset++;
|
||||
|
|
@ -425,7 +421,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
|
|||
offset = buffer;
|
||||
|
||||
while(*offset != '\0') {
|
||||
while(*offset == comment_char) {
|
||||
if(*offset == comment_char) {
|
||||
// clear the line till the end, or the buffer's end
|
||||
while((*offset != '\0')) {
|
||||
offset++;
|
||||
|
|
@ -619,14 +615,48 @@ static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) {
|
|||
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
memcpy(buffer+offset, array, sz);
|
||||
offset += sz;
|
||||
if(offset == ULAB_IO_BUFFER_SIZE) {
|
||||
stream_p->write(stream, buffer, offset, &error);
|
||||
offset = 0;
|
||||
}
|
||||
ITERATOR_TAIL(ndarray, array);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
memcpy(buffer+offset, array, sz);
|
||||
offset += sz;
|
||||
if(offset == ULAB_IO_BUFFER_SIZE) {
|
||||
stream_p->write(stream, buffer, offset, &error);
|
||||
offset = 0;
|
||||
}
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < ndarray->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < ndarray->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
stream_p->write(stream, buffer, offset, &error);
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
|
||||
|
|
@ -717,32 +747,16 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
|
|||
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
|
||||
int error;
|
||||
|
||||
size_t len_comment;
|
||||
char *comments;
|
||||
|
||||
if(mp_obj_is_str(args[5].u_obj)) {
|
||||
const char *_comments = mp_obj_str_get_data(args[5].u_obj, &len_comment);
|
||||
comments = (char *)_comments;
|
||||
} else {
|
||||
len_comment = 2;
|
||||
comments = m_new(char, len_comment);
|
||||
comments[0] = '#';
|
||||
comments[1] = ' ';
|
||||
}
|
||||
|
||||
if(mp_obj_is_str(args[3].u_obj)) {
|
||||
size_t _len;
|
||||
const char *header = mp_obj_str_get_data(args[3].u_obj, &_len);
|
||||
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
|
||||
// We can't write the header in the single chunk, for it might contain line breaks
|
||||
for(size_t i = 0; i < _len; header++, i++) {
|
||||
stream_p->write(stream, header, 1, &error);
|
||||
if((*header == '\n') && (i < _len)) {
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
}
|
||||
if(mp_obj_is_str(args[5].u_obj)) {
|
||||
const char *comments = mp_obj_str_get_data(args[5].u_obj, &_len);
|
||||
stream_p->write(stream, comments, _len, &error);
|
||||
} else {
|
||||
stream_p->write(stream, "# ", 2, &error);
|
||||
}
|
||||
const char *header = mp_obj_str_get_data(args[3].u_obj, &_len);
|
||||
stream_p->write(stream, header, _len, &error);
|
||||
stream_p->write(stream, "\n", 1, &error);
|
||||
}
|
||||
|
||||
|
|
@ -781,19 +795,16 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
|
|||
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
|
||||
if(mp_obj_is_str(args[4].u_obj)) { // footer string
|
||||
if(mp_obj_is_str(args[4].u_obj)) {
|
||||
size_t _len;
|
||||
const char *footer = mp_obj_str_get_data(args[4].u_obj, &_len);
|
||||
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
|
||||
// We can't write the header in the single chunk, for it might contain line breaks
|
||||
for(size_t i = 0; i < _len; footer++, i++) {
|
||||
stream_p->write(stream, footer, 1, &error);
|
||||
if((*footer == '\n') && (i < _len)) {
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
}
|
||||
if(mp_obj_is_str(args[5].u_obj)) {
|
||||
const char *comments = mp_obj_str_get_data(args[5].u_obj, &_len);
|
||||
stream_p->write(stream, comments, _len, &error);
|
||||
} else {
|
||||
stream_p->write(stream, "# ", 2, &error);
|
||||
}
|
||||
const char *footer = mp_obj_str_get_data(args[4].u_obj, &_len);
|
||||
stream_p->write(stream, footer, _len, &error);
|
||||
stream_p->write(stream, "\n", 1, &error);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
|
|||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_qr_obj, 1, linalg_qr);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
|
||||
STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) },
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_LINALG_HAS_CHOLESKY
|
||||
|
|
@ -530,7 +530,7 @@ static const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
|
|||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_linalg_module = {
|
||||
.base = { &mp_type_module },
|
||||
|
|
|
|||
|
|
@ -45,8 +45,6 @@ enum NUMERICAL_FUNCTION_TYPE {
|
|||
//| from typing import Dict
|
||||
//|
|
||||
//| _ArrayLike = Union[ndarray, List[_float], Tuple[_float], range]
|
||||
//| _ScalarOrArrayLike = Union[int, _float, _ArrayLike]
|
||||
//| _ScalarOrNdArray = Union[int, _float, ndarray]
|
||||
//|
|
||||
//| _DType = int
|
||||
//| """`ulab.numpy.int8`, `ulab.numpy.uint8`, `ulab.numpy.int16`, `ulab.numpy.uint16`, `ulab.numpy.float` or `ulab.numpy.bool`"""
|
||||
|
|
@ -274,7 +272,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, mp_obj_t keepdims, uint8_t optype, size_t ddof) {
|
||||
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);
|
||||
|
|
@ -372,7 +370,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
|
|||
mp_float_t norm = (mp_float_t)_shape_strides.shape[0];
|
||||
// re-wind the array here
|
||||
farray = (mp_float_t *)results->array;
|
||||
for(size_t i = 0; i < results->len; i++) {
|
||||
for(size_t i=0; i < results->len; i++) {
|
||||
*farray++ *= norm;
|
||||
}
|
||||
}
|
||||
|
|
@ -380,7 +378,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
|
|||
bool isStd = optype == NUMERICAL_STD ? 1 : 0;
|
||||
results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT);
|
||||
farray = (mp_float_t *)results->array;
|
||||
// we can return the 0 array here, if the degrees of freedom are larger than the length of the axis
|
||||
// we can return the 0 array here, if the degrees of freedom is larger than the length of the axis
|
||||
if((optype == NUMERICAL_STD) && (_shape_strides.shape[0] <= ddof)) {
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
|
@ -397,9 +395,11 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
|
|||
RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, div, isStd);
|
||||
}
|
||||
}
|
||||
return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides);
|
||||
if(results->ndim == 0) { // return a scalar here
|
||||
return mp_binary_get_val_array(results->dtype, results->array, 0);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
// we should never get to this point
|
||||
return mp_const_none;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -439,7 +439,7 @@ static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) {
|
|||
}
|
||||
}
|
||||
|
||||
static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t keepdims, mp_obj_t axis, uint8_t optype) {
|
||||
static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {
|
||||
// TODO: treat the flattened array
|
||||
if(ndarray->len == 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("attempt to get (arg)min/(arg)max of empty sequence"));
|
||||
|
|
@ -519,9 +519,7 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t
|
|||
int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS);
|
||||
|
||||
numerical_reduce_axes(ndarray, ax, shape, strides);
|
||||
shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
|
||||
|
||||
uint8_t index = _shape_strides.axis;
|
||||
uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax;
|
||||
|
||||
ndarray_obj_t *results = NULL;
|
||||
|
||||
|
|
@ -546,9 +544,12 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t
|
|||
}
|
||||
|
||||
m_del(int32_t, strides, ULAB_MAX_DIMS);
|
||||
return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides);
|
||||
|
||||
if(results->len == 1) {
|
||||
return mp_binary_get_val_array(results->dtype, results->array, 0);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
// we should never get to this point
|
||||
return mp_const_none;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -557,7 +558,6 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
|
||||
{ MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_keepdims, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
|
|
@ -565,8 +565,6 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
|
||||
mp_obj_t oin = args[0].u_obj;
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
mp_obj_t keepdims = args[2].u_obj;
|
||||
|
||||
if((axis != mp_const_none) && (!mp_obj_is_int(axis))) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer"));
|
||||
}
|
||||
|
|
@ -598,12 +596,11 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
case NUMERICAL_ARGMIN:
|
||||
case NUMERICAL_ARGMAX:
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
return numerical_argmin_argmax_ndarray(ndarray, keepdims, axis, optype);
|
||||
case NUMERICAL_MEAN:
|
||||
case NUMERICAL_STD:
|
||||
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, keepdims, optype, 0);
|
||||
return numerical_sum_mean_std_ndarray(ndarray, axis, optype, 0);
|
||||
default:
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("operation is not implemented on ndarrays"));
|
||||
}
|
||||
|
|
@ -747,7 +744,7 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
|
|||
numerical_reduce_axes(ndarray, ax, shape, strides);
|
||||
|
||||
// We could return an NDARRAY_UINT8 array, if all lengths are shorter than 256
|
||||
ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16, NULL);
|
||||
ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16);
|
||||
int32_t *istrides = m_new0(int32_t, ULAB_MAX_DIMS);
|
||||
numerical_reduce_axes(indices, ax, shape, istrides);
|
||||
|
||||
|
|
@ -1187,19 +1184,13 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
|
|||
mp_raise_TypeError(MP_ERROR_TEXT("roll argument must be an ndarray"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
uint8_t *array = ndarray->array;
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype);
|
||||
|
||||
int32_t shift = mp_obj_get_int(args[1].u_obj);
|
||||
|
||||
if(shift == 0) {
|
||||
ndarray_copy_array(ndarray, results, 0);
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
int32_t _shift = shift < 0 ? -shift : shift;
|
||||
|
||||
size_t counter;
|
||||
uint8_t *array = ndarray->array;
|
||||
uint8_t *rarray = (uint8_t *)results->array;
|
||||
|
||||
if(args[2].u_obj == mp_const_none) { // roll the flattened array
|
||||
|
|
@ -1386,7 +1377,6 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
|
|||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } } ,
|
||||
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_keepdims, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
|
|
@ -1395,8 +1385,6 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
|
|||
mp_obj_t oin = args[0].u_obj;
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
size_t ddof = args[2].u_int;
|
||||
mp_obj_t keepdims = args[3].u_obj;
|
||||
|
||||
if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
|
||||
// this seems to pass with False, and True...
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("axis must be None, or an integer"));
|
||||
|
|
@ -1405,7 +1393,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
|
|||
return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof);
|
||||
} else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
|
||||
return numerical_sum_mean_std_ndarray(ndarray, axis, keepdims, NUMERICAL_STD, ddof);
|
||||
return numerical_sum_mean_std_ndarray(ndarray, axis, NUMERICAL_STD, ddof);
|
||||
} else {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be tuple, list, range, or ndarray"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,9 +57,35 @@
|
|||
(rarray) += (results)->itemsize;\
|
||||
})
|
||||
|
||||
// The mean could be calculated by simply dividing the sum by
|
||||
// the number of elements, but that method is numerically unstable
|
||||
#define RUN_MEAN1(type, array, rarray, ss)\
|
||||
({\
|
||||
mp_float_t M = 0.0;\
|
||||
for(size_t i=0; i < (ss).shape[0]; i++) {\
|
||||
mp_float_t value = (mp_float_t)(*(type *)(array));\
|
||||
M = M + (value - M) / (mp_float_t)(i+1);\
|
||||
(array) += (ss).strides[0];\
|
||||
}\
|
||||
*(rarray)++ = M;\
|
||||
})
|
||||
|
||||
// Instead of the straightforward implementation of the definition,
|
||||
// we take the numerically stable Welford algorithm here
|
||||
// https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
|
||||
#define RUN_STD1(type, array, rarray, ss, div)\
|
||||
({\
|
||||
mp_float_t M = 0.0, m = 0.0, S = 0.0;\
|
||||
for(size_t i=0; i < (ss).shape[0]; i++) {\
|
||||
mp_float_t value = (mp_float_t)(*(type *)(array));\
|
||||
m = M + (value - M) / (mp_float_t)(i+1);\
|
||||
S = S + (value - M) * (value - m);\
|
||||
M = m;\
|
||||
(array) += (ss).strides[0];\
|
||||
}\
|
||||
*(rarray)++ = MICROPY_FLOAT_C_FUN(sqrt)(S / (div));\
|
||||
})
|
||||
|
||||
#define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\
|
||||
({\
|
||||
mp_float_t M = 0.0, m = 0.0, S = 0.0;\
|
||||
|
|
@ -167,6 +193,14 @@
|
|||
RUN_SUM1(type, (array), (results), (rarray), (ss));\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN(type, array, rarray, ss) do {\
|
||||
RUN_MEAN1(type, (array), (rarray), (ss));\
|
||||
} while(0)
|
||||
|
||||
#define RUN_STD(type, array, rarray, ss, div) do {\
|
||||
RUN_STD1(type, (array), (results), (rarray), (ss), (div));\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
|
||||
} while(0)
|
||||
|
|
@ -200,6 +234,26 @@
|
|||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN(type, array, rarray, ss) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_MEAN1(type, (array), (rarray), (ss));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_STD(type, array, rarray, ss, div) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_STD1(type, (array), (rarray), (ss), (div));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
|
|
@ -271,6 +325,38 @@
|
|||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN(type, array, rarray, ss) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_MEAN1(type, (array), (rarray), (ss));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_STD(type, array, rarray, ss, div) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_STD1(type, (array), (rarray), (ss), (div));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
|
|
@ -381,6 +467,50 @@
|
|||
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN(type, array, rarray, ss) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_MEAN1(type, (array), (rarray), (ss));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_STD(type, array, rarray, ss, div) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_STD1(type, (array), (rarray), (ss), (div));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
|
|
|
|||
|
|
@ -291,9 +291,6 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
|
|||
#if ULAB_NUMPY_HAS_SUM
|
||||
{ MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&numerical_sum_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_TAKE
|
||||
{ MP_ROM_QSTR(MP_QSTR_take), MP_ROM_PTR(&create_take_obj) },
|
||||
#endif
|
||||
// functions of the poly sub-module
|
||||
#if ULAB_NUMPY_HAS_POLYFIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_polyfit), MP_ROM_PTR(&poly_polyfit_obj) },
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
|
|||
ndarray_obj_t *beta = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT);
|
||||
mp_float_t *betav = (mp_float_t *)beta->array;
|
||||
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
|
||||
m_del(mp_float_t, y, leny);
|
||||
m_del(float, y, leny);
|
||||
|
||||
// now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
|
||||
for(uint8_t i=0; i < deg+1; i++) {
|
||||
|
|
@ -197,9 +197,42 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
|||
|
||||
// TODO: these loops are really nothing, but the re-impplementation of
|
||||
// ITERATE_VECTOR from vectorise.c. We could pass a function pointer here
|
||||
ITERATOR_HEAD();
|
||||
*array++ = poly_eval(func(sarray), p, plen);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
*array++ = poly_eval(func(sarray), p, plen);
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
} else {
|
||||
// o_x had better be a one-dimensional standard iterable
|
||||
ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ MP_DEFINE_CONST_OBJ_TYPE(
|
|||
MP_QSTR_generator,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
print, random_generator_print,
|
||||
make_new, random_generator_make_new,
|
||||
make_new, random_generator_make_new,
|
||||
locals_dict, &random_generator_locals_dict
|
||||
);
|
||||
#else
|
||||
|
|
@ -57,7 +57,7 @@ const mp_obj_type_t random_generator_type = {
|
|||
void random_generator_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
random_generator_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(MP_PYTHON_PRINTER, "Generator() at 0x%p", self);
|
||||
mp_printf(MP_PYTHON_PRINTER, "Gnerator() at 0x%p", self);
|
||||
}
|
||||
|
||||
mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
|
|
@ -76,12 +76,11 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz
|
|||
if(args[0] == mp_const_none) {
|
||||
#ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("no default seed"));
|
||||
#else
|
||||
#endif
|
||||
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
|
||||
generator->base.type = &random_generator_type;
|
||||
generator->state = MICROPY_PY_RANDOM_SEED_INIT_FUNC;
|
||||
return MP_OBJ_FROM_PTR(generator);
|
||||
#endif
|
||||
} else if(mp_obj_is_int(args[0])) {
|
||||
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
|
||||
generator->base.type = &random_generator_type;
|
||||
|
|
@ -90,7 +89,7 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz
|
|||
} else if(mp_obj_is_type(args[0], &mp_type_tuple)){
|
||||
mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_obj_t *items = m_new(mp_obj_t, seeds->len);
|
||||
|
||||
|
||||
for(uint8_t i = 0; i < seeds->len; i++) {
|
||||
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
|
||||
generator->base.type = &random_generator_type;
|
||||
|
|
@ -149,9 +148,12 @@ static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
ndarray = ndarray_new_linear_array((size_t)mp_obj_get_int(size), NDARRAY_FLOAT);
|
||||
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
if(_shape->len > ULAB_MAX_DIMS) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS)));
|
||||
}
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
} else { // input type not supported
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers"));
|
||||
}
|
||||
} else {
|
||||
// return single value
|
||||
|
|
@ -173,7 +175,7 @@ static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
// numpy's random supports only dense output arrays, so we can simply
|
||||
// numpy's random supports only dense output arrays, so we can simply
|
||||
// loop through the elements in a linear fashion
|
||||
for(size_t i = 0; i < ndarray->len; i = i + 2) {
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
|
|
@ -218,16 +220,27 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
mp_obj_t out = args[2].u_obj;
|
||||
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
uint8_t ndim = 1;
|
||||
|
||||
if(size != mp_const_none) {
|
||||
if(mp_obj_is_int(size)) {
|
||||
shape[ULAB_MAX_DIMS - 1] = (size_t)mp_obj_get_int(size);
|
||||
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
if(_shape->len > ULAB_MAX_DIMS) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS)));
|
||||
}
|
||||
ndim = _shape->len;
|
||||
for(size_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
if(i >= ndim) {
|
||||
shape[ULAB_MAX_DIMS - 1 - i] = 0;
|
||||
} else {
|
||||
shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]);
|
||||
}
|
||||
}
|
||||
} else { // input type not supported
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +248,7 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
|
||||
}
|
||||
|
||||
|
||||
ndarray = MP_OBJ_TO_PTR(out);
|
||||
|
||||
if(ndarray->dtype != NDARRAY_FLOAT) {
|
||||
|
|
@ -253,8 +266,7 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
}
|
||||
} else { // out == None
|
||||
if(size != mp_const_none) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
ndarray = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
} else {
|
||||
// return single value
|
||||
mp_float_t value;
|
||||
|
|
@ -271,10 +283,10 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
// numpy's random supports only dense output arrays, so we can simply
|
||||
// numpy's random supports only dense output arrays, so we can simply
|
||||
// loop through the elements in a linear fashion
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
*array = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
|
|
@ -323,9 +335,13 @@ static mp_obj_t random_uniform(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
return mp_obj_new_float(value);
|
||||
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
// TODO: this could be reduced, if the inspection was in the ndarray_new_ndarray_from_tuple function
|
||||
if(_shape->len > ULAB_MAX_DIMS) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS)));
|
||||
}
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
} else { // input type not supported
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers"));
|
||||
}
|
||||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
|
@ -359,3 +375,4 @@ const mp_obj_module_t ulab_numpy_random_module = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_random_globals,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -91,10 +91,43 @@ static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, m
|
|||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = func(sarray);
|
||||
*tarray++ = f(value);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t value = func(sarray);
|
||||
*tarray++ = f(value);
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
#else
|
||||
if(source->dtype == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, target, tarray, tstrides, source, sarray);
|
||||
|
|
@ -138,10 +171,43 @@ static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)
|
|||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = func(sarray);
|
||||
*array++ = f(value);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t value = func(sarray);
|
||||
*array++ = f(value);
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
#else
|
||||
if(source->dtype == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, array, source, sarray);
|
||||
|
|
@ -169,7 +235,7 @@ static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)
|
|||
|
||||
|
||||
#if ULAB_NUMPY_HAS_ACOS
|
||||
//| def acos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def acos(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -183,7 +249,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos);
|
|||
#endif /* ULAB_NUMPY_HAS_ACOS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ACOSH
|
||||
//| def acosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def acosh(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse hyperbolic cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -197,7 +263,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh);
|
|||
#endif /* ULAB_NUMPY_HAS_ACOSH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ASIN
|
||||
//| def asin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def asin(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -211,7 +277,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin);
|
|||
#endif /* ULAB_NUMPY_HAS_ASIN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ASINH
|
||||
//| def asinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def asinh(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse hyperbolic sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -225,7 +291,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh);
|
|||
#endif /* ULAB_NUMPY_HAS_ASINH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_AROUND
|
||||
//| def around(a: ulab.numpy.ndarray, *, decimals: int = 0) -> ulab.numpy.ndarray:
|
||||
//| def around(a: _ArrayLike, *, decimals: int = 0) -> ulab.numpy.ndarray:
|
||||
//| """Returns a new float array in which each element is rounded to
|
||||
//| ``decimals`` places."""
|
||||
//| ...
|
||||
|
|
@ -261,11 +327,43 @@ mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
|
|||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t f = func(sarray);
|
||||
*narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul;
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t f = func(sarray);
|
||||
*narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul;
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
|
|
@ -273,7 +371,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around);
|
|||
#endif /* ULAB_NUMPY_HAS_AROUND */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ATAN
|
||||
//| def atan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def atan(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse tangent function; the return values are in the
|
||||
//| range [-pi/2,pi/2]."""
|
||||
//| ...
|
||||
|
|
@ -289,7 +387,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan);
|
|||
|
||||
|
||||
#if ULAB_NUMPY_HAS_ATANH
|
||||
//| def atanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def atanh(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse hyperbolic tangent function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -303,7 +401,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh);
|
|||
#endif /* ULAB_NUMPY_HAS_ATANH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ARCTAN2
|
||||
//| def arctan2(ya: _ScalarOrArrayLike, xa: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def arctan2(ya: _ArrayLike, xa: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the inverse tangent function of y/x; the return values are in
|
||||
//| the range [-pi, pi]."""
|
||||
//| ...
|
||||
|
|
@ -396,7 +494,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2);
|
|||
#endif /* ULAB_VECTORISE_HAS_ARCTAN2 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_CEIL
|
||||
//| def ceil(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def ceil(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Rounds numbers up to the next whole number"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -410,7 +508,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil);
|
|||
#endif /* ULAB_NUMPY_HAS_CEIL */
|
||||
|
||||
#if ULAB_NUMPY_HAS_COS
|
||||
//| def cos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def cos(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -424,7 +522,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos);
|
|||
#endif /* ULAB_NUMPY_HAS_COS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_COSH
|
||||
//| def cosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def cosh(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the hyperbolic cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -438,7 +536,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh);
|
|||
#endif /* ULAB_NUMPY_HAS_COSH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_DEGREES
|
||||
//| def degrees(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def degrees(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Converts angles from radians to degrees"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -461,7 +559,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees);
|
|||
#endif /* ULAB_NUMPY_HAS_DEGREES */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_ERF
|
||||
//| def erf(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def erf(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the error function, which has applications in statistics"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -475,7 +573,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf);
|
|||
#endif /* ULAB_SCIPY_SPECIAL_HAS_ERF */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_ERFC
|
||||
//| def erfc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def erfc(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the complementary error function, which has applications in statistics"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -489,7 +587,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc);
|
|||
#endif /* ULAB_SCIPY_SPECIAL_HAS_ERFC */
|
||||
|
||||
#if ULAB_NUMPY_HAS_EXP
|
||||
//| def exp(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def exp(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the exponent function."""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -533,13 +631,46 @@ static mp_obj_t vector_exp(mp_obj_t o_in) {
|
|||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
uint8_t itemsize = sizeof(mp_float_t);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t real = *(mp_float_t *)sarray;
|
||||
mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
|
||||
mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
|
||||
*array++ = exp_real * MICROPY_FLOAT_C_FUN(cos)(imag);
|
||||
*array++ = exp_real * MICROPY_FLOAT_C_FUN(sin)(imag);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
|
@ -559,7 +690,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp);
|
|||
#endif /* ULAB_NUMPY_HAS_EXP */
|
||||
|
||||
#if ULAB_NUMPY_HAS_EXPM1
|
||||
//| def expm1(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def expm1(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function."""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -573,7 +704,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1);
|
|||
#endif /* ULAB_NUMPY_HAS_EXPM1 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_FLOOR
|
||||
//| def floor(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def floor(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Rounds numbers up to the next whole number"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -587,7 +718,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor);
|
|||
#endif /* ULAB_NUMPY_HAS_FLOOR */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_GAMMA
|
||||
//| def gamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def gamma(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the gamma function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -601,7 +732,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma);
|
|||
#endif /* ULAB_SCIPY_SPECIAL_HAS_GAMMA */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN
|
||||
//| def lgamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def lgamma(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the natural log of the gamma function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -615,7 +746,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma);
|
|||
#endif /* ULAB_SCIPY_SEPCIAL_HAS_GAMMALN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOG
|
||||
//| def log(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def log(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the natural log"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -629,7 +760,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log);
|
|||
#endif /* ULAB_NUMPY_HAS_LOG */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOG10
|
||||
//| def log10(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def log10(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the log base 10"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -643,7 +774,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10);
|
|||
#endif /* ULAB_NUMPY_HAS_LOG10 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOG2
|
||||
//| def log2(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def log2(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the log base 2"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -657,7 +788,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2);
|
|||
#endif /* ULAB_NUMPY_HAS_LOG2 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_RADIANS
|
||||
//| def radians(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def radians(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Converts angles from degrees to radians"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -680,7 +811,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians);
|
|||
#endif /* ULAB_NUMPY_HAS_RADIANS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SIN
|
||||
//| def sin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def sin(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -694,7 +825,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin);
|
|||
#endif /* ULAB_NUMPY_HAS_SIN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SINC
|
||||
//| def sinc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def sinc(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the normalized sinc function"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -721,7 +852,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc);
|
|||
#endif /* ULAB_NUMPY_HAS_SINC */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SINH
|
||||
//| def sinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def sinh(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the hyperbolic sine"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -736,7 +867,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh);
|
|||
|
||||
|
||||
#if ULAB_NUMPY_HAS_SQRT
|
||||
//| def sqrt(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def sqrt(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the square root"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -790,33 +921,97 @@ mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
uint8_t itemsize = sizeof(mp_float_t);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t real = *(mp_float_t *)sarray;
|
||||
mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
|
||||
mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
|
||||
sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
|
||||
mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
|
||||
*array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta);
|
||||
*array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
#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;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = *(mp_float_t *)sarray;
|
||||
if(value >= MICROPY_FLOAT_CONST(0.0)) {
|
||||
*array++ = MICROPY_FLOAT_C_FUN(sqrt)(value);
|
||||
array++;
|
||||
} else {
|
||||
array++;
|
||||
*array++ = MICROPY_FLOAT_C_FUN(sqrt)(-value);
|
||||
}
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
#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(MP_ERROR_TEXT("input dtype must be float or complex"));
|
||||
|
|
@ -835,7 +1030,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt);
|
|||
#endif /* ULAB_NUMPY_HAS_SQRT */
|
||||
|
||||
#if ULAB_NUMPY_HAS_TAN
|
||||
//| def tan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def tan(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the tangent"""
|
||||
//| ...
|
||||
//|
|
||||
|
|
@ -849,7 +1044,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan);
|
|||
#endif /* ULAB_NUMPY_HAS_TAN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_TANH
|
||||
//| def tanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| def tanh(a: _ArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """Computes the hyperbolic tangent"""
|
||||
//| ...
|
||||
|
||||
|
|
@ -876,12 +1071,45 @@ static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args,
|
|||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
uint8_t *narray = (uint8_t *)ndarray->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0);
|
||||
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue);
|
||||
ndarray_set_value(self->otypes, narray, 0, fvalue);
|
||||
narray += ndarray->itemsize;
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0);
|
||||
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue);
|
||||
ndarray_set_value(self->otypes, narray, 0, fvalue);
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
narray += ndarray->itemsize;
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS - 1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS - 2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS - 3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) ||
|
||||
|
|
@ -930,12 +1158,12 @@ const mp_obj_type_t vector_function_type = {
|
|||
//| f: Union[Callable[[int], _float], Callable[[_float], _float]],
|
||||
//| *,
|
||||
//| otypes: Optional[_DType] = None
|
||||
//| ) -> Callable[[_ScalarOrArrayLike], ulab.numpy.ndarray]:
|
||||
//| ) -> Callable[[_ArrayLike], ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| :param callable f: The function to wrap
|
||||
//| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float.
|
||||
//|
|
||||
//| Wrap a Python function ``f`` so that it can be applied to arrays or scalars. A scalar passed to the wrapped function is treated as a single-element 1-D array.
|
||||
//| Wrap a Python function ``f`` so that it can be applied to arrays.
|
||||
//| The callable must return only values of the types specified by ``otypes``, or the result is undefined."""
|
||||
//| ...
|
||||
//|
|
||||
|
|
|
|||
497
code/pid/pid.c
Normal file
497
code/pid/pid.c
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Zoltán Vörös
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objtuple.h"
|
||||
|
||||
|
||||
#include "pid.h"
|
||||
|
||||
#if ULAB_HAS_PID_MODULE
|
||||
|
||||
// methods of the PID buffer object
|
||||
static const mp_rom_map_elem_t ulab_pid_buffer_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_evaluate), MP_ROM_PTR(&pid_buffer_evaluate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pid_buffer_init_obj) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(ulab_pid_buffer_locals_dict, ulab_pid_buffer_locals_dict_table);
|
||||
|
||||
// attributes of the PID buffer object
|
||||
static void pid_buffer_attributes(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
if(dest[0] == MP_OBJ_NULL) {
|
||||
switch(attr) {
|
||||
case MP_QSTR_order:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_ORDER);
|
||||
break;
|
||||
case MP_QSTR_x0:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_X0);
|
||||
break;
|
||||
case MP_QSTR_coeffs:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_COEFFS);
|
||||
break;
|
||||
case MP_QSTR_offset:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_OFFSET);
|
||||
break;
|
||||
case MP_QSTR_bitdepth:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_BITDEPTH);
|
||||
break;
|
||||
case MP_QSTR_mask:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_MASK);
|
||||
break;
|
||||
case MP_QSTR_bytes:
|
||||
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_BYTES);
|
||||
break;
|
||||
default:
|
||||
// forward to locals dict
|
||||
dest[1] = MP_OBJ_SENTINEL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(dest[1]) {
|
||||
switch(attr) {
|
||||
case MP_QSTR_x0:
|
||||
pid_buffer_setter(self_in, dest[1], PID_BUFFER_X0);
|
||||
break;
|
||||
case MP_QSTR_coeffs:
|
||||
pid_buffer_setter(self_in, dest[1], PID_BUFFER_COEFFS);
|
||||
break;
|
||||
case MP_QSTR_offset:
|
||||
pid_buffer_setter(self_in, dest[1], PID_BUFFER_OFFSET);
|
||||
break;
|
||||
case MP_QSTR_bitdepth:
|
||||
pid_buffer_setter(self_in, dest[1], PID_BUFFER_BITDEPTH);
|
||||
break;
|
||||
case MP_QSTR_bytes:
|
||||
pid_buffer_setter(self_in, dest[1], PID_BUFFER_BYTES);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
ulab_pid_buffer_type,
|
||||
MP_QSTR_buffer,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
print, pid_buffer_print,
|
||||
make_new, pid_buffer_make_new,
|
||||
locals_dict, &ulab_pid_buffer_locals_dict,
|
||||
attr, pid_buffer_attributes
|
||||
);
|
||||
|
||||
static pid_buffer_obj_t *pid_buffer_create(void) {
|
||||
pid_buffer_obj_t *buffer = m_new_obj(pid_buffer_obj_t);
|
||||
buffer->base.type = &ulab_pid_buffer_type;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
mp_obj_t pid_buffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void) type;
|
||||
mp_arg_check_num(n_args, n_kw, 0, 2, true);
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
return MP_OBJ_FROM_PTR(pid_buffer_create());
|
||||
}
|
||||
|
||||
void pid_buffer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(MP_PYTHON_PRINTER, "PID buffer object at 0x%p", self);
|
||||
}
|
||||
|
||||
mp_obj_t pid_buffer_getter(mp_obj_t self_in, uint8_t attribute) {
|
||||
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if(attribute == PID_BUFFER_ORDER) {
|
||||
return MP_OBJ_NEW_SMALL_INT(self->series.order);
|
||||
} else if(attribute == PID_BUFFER_X0) {
|
||||
return mp_obj_new_float(self->series.x0);
|
||||
} else if(attribute == PID_BUFFER_COEFFS) {
|
||||
mp_float_t *coeffs = (mp_float_t *)self->series.coeffs;
|
||||
mp_obj_t *items = m_new(mp_obj_t, self->series.order);
|
||||
for(uint8_t i = 0; i < self->series.order; i++) {
|
||||
items[i] = mp_obj_new_float(*coeffs++);
|
||||
}
|
||||
mp_obj_t tuple = mp_obj_new_tuple(self->series.order, items);
|
||||
return tuple;
|
||||
} else if(attribute == PID_BUFFER_OFFSET) {
|
||||
return MP_OBJ_NEW_SMALL_INT(self->converter.offset);
|
||||
} else if(attribute == PID_BUFFER_BITDEPTH) {
|
||||
return MP_OBJ_NEW_SMALL_INT(self->converter.bitdepth);
|
||||
} else if(attribute == PID_BUFFER_MASK) {
|
||||
return MP_OBJ_NEW_SMALL_INT(self->converter.mask);
|
||||
} else if(attribute == PID_BUFFER_BYTES) {
|
||||
return mp_obj_new_bytearray_by_ref(self->converter.len, self->converter.bytes);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t pid_buffer_setter(mp_obj_t self_in, mp_obj_t value, uint8_t attribute) {
|
||||
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if(attribute == PID_BUFFER_X0) {
|
||||
self->series.x0 = mp_obj_get_float(value);
|
||||
} else if(attribute == PID_BUFFER_COEFFS) {
|
||||
mp_float_t *coeffs = (mp_float_t *)self->series.coeffs;
|
||||
m_del(mp_float_t, coeffs, self->series.order);
|
||||
uint8_t order = (uint8_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(value));
|
||||
coeffs = m_new0(mp_float_t, order);
|
||||
self->series.order = order;
|
||||
self->series.coeffs = coeffs;
|
||||
|
||||
mp_obj_iter_buf_t buf;
|
||||
mp_obj_t item, iterable = mp_getiter(value, &buf);
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
*coeffs++ = (mp_float_t)mp_obj_get_float(item);
|
||||
}
|
||||
} else if(attribute == PID_BUFFER_OFFSET) {
|
||||
self->converter.offset = (uint8_t)MP_OBJ_SMALL_INT_VALUE(value);
|
||||
} else if(attribute == PID_BUFFER_BITDEPTH) {
|
||||
uint8_t bitdepth = (uint8_t)MP_OBJ_SMALL_INT_VALUE(value);
|
||||
self->converter.bitdepth = bitdepth;
|
||||
// TODO: we might have to consider the offset here!!!
|
||||
self->converter.mask = (1 << bitdepth) - 1;
|
||||
self->converter.nbytes = (bitdepth + 7) / 8;
|
||||
} else if(attribute == PID_BUFFER_BYTES) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
if(mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) {
|
||||
if(bufinfo.len < (self->converter.offset + self->converter.bitdepth) / 8) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer is shorter than offset plus bitdepth"));
|
||||
}
|
||||
}
|
||||
self->converter.len = bufinfo.len;
|
||||
self->converter.bytes = bufinfo.buf;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_float_t pid_pid_convert(pid_series_t series, mp_float_t x) {
|
||||
// taking the coefficients of the Taylor expansion, returns the
|
||||
// approximate value of a function at the value x
|
||||
mp_float_t dx = x - series.x0;
|
||||
mp_float_t *coeffs = (mp_float_t *)series.coeffs;
|
||||
mp_float_t y = *coeffs++;
|
||||
|
||||
for(uint8_t i = 0; i < series.order - 1; i++) {
|
||||
y *= dx;
|
||||
y += *coeffs++;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
static mp_obj_t pid_buffer_evaluate(mp_obj_t self_in, mp_obj_t x) {
|
||||
// convenience function for manually checking the conversion
|
||||
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(pid_pid_convert(self->series, mp_obj_get_float(x)));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(pid_buffer_evaluate_obj, pid_buffer_evaluate);
|
||||
|
||||
static mp_obj_t pid_buffer_init(mp_obj_t self_in) {
|
||||
// initialise buffer with some default values
|
||||
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
self->series.order = 2;
|
||||
self->series.x0 = MICROPY_FLOAT_CONST(0.0);
|
||||
self->series.coeffs = m_new0(mp_float_t, 2);
|
||||
self->series.coeffs[0] = MICROPY_FLOAT_CONST(1.0);
|
||||
|
||||
self->converter.len = 2;
|
||||
self->converter.bitdepth = 16;
|
||||
self->converter.mask = 0xffff;
|
||||
self->converter.offset = 0;
|
||||
self->converter.bytes = (uint8_t *)&self->converter.mask;
|
||||
self->converter.nbytes = 2;
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(pid_buffer_init_obj, pid_buffer_init);
|
||||
|
||||
|
||||
|
||||
// methods of the PID object
|
||||
static const mp_rom_map_elem_t ulab_pid_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&pid_pid_reset_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_float_step), MP_ROM_PTR(&pid_pid_float_step_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_step), MP_ROM_PTR(&pid_pid_step_obj) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(ulab_pid_locals_dict, ulab_pid_locals_dict_table);
|
||||
|
||||
// attributes of the PID object; the input/output buffers are defer to till later
|
||||
static void pid_pid_attributes(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
switch(attr) {
|
||||
case MP_QSTR_P:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_PARAMETER_P);
|
||||
break;
|
||||
case MP_QSTR_I:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_PARAMETER_I);
|
||||
break;
|
||||
case MP_QSTR_D:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_PARAMETER_D);
|
||||
break;
|
||||
case MP_QSTR_setpoint:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_SETPOINT);
|
||||
break;
|
||||
case MP_QSTR_steps:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_STEPS);
|
||||
break;
|
||||
case MP_QSTR_value:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_VALUE);
|
||||
break;
|
||||
case MP_QSTR_out:
|
||||
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_OUT);
|
||||
break;
|
||||
case MP_QSTR_input:
|
||||
dest[0] = pid_return_buffer(self_in, MP_OBJ_NULL, PID_BUFFER_INPUT);
|
||||
break;
|
||||
case MP_QSTR_output:
|
||||
dest[0] = pid_return_buffer(self_in, MP_OBJ_NULL, PID_BUFFER_OUTPUT);
|
||||
break;
|
||||
default:
|
||||
// forward to locals dict
|
||||
dest[1] = MP_OBJ_SENTINEL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(dest[1]) {
|
||||
switch(attr) {
|
||||
case MP_QSTR_P:
|
||||
pid_pid_parameters(self_in, dest[1], PID_PARAMETER_P);
|
||||
break;
|
||||
case MP_QSTR_I:
|
||||
pid_pid_parameters(self_in, dest[1], PID_PARAMETER_I);
|
||||
break;
|
||||
case MP_QSTR_D:
|
||||
pid_pid_parameters(self_in, dest[1], PID_PARAMETER_D);
|
||||
break;
|
||||
case MP_QSTR_setpoint:
|
||||
pid_pid_parameters(self_in, dest[1], PID_SETPOINT);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
ulab_pid_type,
|
||||
MP_QSTR_PID,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
print, pid_pid_print,
|
||||
make_new, pid_pid_make_new,
|
||||
locals_dict, &ulab_pid_locals_dict,
|
||||
attr, pid_pid_attributes
|
||||
);
|
||||
|
||||
void pid_pid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(MP_PYTHON_PRINTER, "PID object at 0x%p", self);
|
||||
}
|
||||
|
||||
mp_obj_t pid_pid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void) type;
|
||||
mp_arg_check_num(n_args, n_kw, 0, 2, true);
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
|
||||
pid_obj_t *self = m_new_obj(pid_obj_t);
|
||||
self->base.type = &ulab_pid_type;
|
||||
|
||||
self->setpoint = MICROPY_FLOAT_CONST(0.0);
|
||||
self->input = pid_buffer_create();
|
||||
self->output = pid_buffer_create();
|
||||
|
||||
self->P = MICROPY_FLOAT_CONST(0.0);
|
||||
self->I = MICROPY_FLOAT_CONST(0.0);
|
||||
self->D = MICROPY_FLOAT_CONST(0.0);
|
||||
self->integral = MICROPY_FLOAT_CONST(0.0);
|
||||
self->value = MICROPY_FLOAT_CONST(0.0);
|
||||
self->error = MICROPY_FLOAT_CONST(0.0);
|
||||
self->time_us = 0L;
|
||||
self->out = MICROPY_FLOAT_CONST(0.0);
|
||||
self->steps = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
mp_obj_t pid_return_buffer(mp_obj_t self_in, mp_obj_t value, uint8_t designator) {
|
||||
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if(designator == PID_BUFFER_INPUT) {
|
||||
return self->input;
|
||||
} else {
|
||||
return self->output;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t pid_pid_parameters(mp_obj_t self_in, mp_obj_t value, uint8_t attribute) {
|
||||
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_float_t *parameter;
|
||||
|
||||
if(attribute == PID_STEPS) {
|
||||
if(value == MP_OBJ_NULL) {
|
||||
return MP_OBJ_NEW_SMALL_INT(self->steps);
|
||||
} // internal variable, no setter for this
|
||||
} {
|
||||
if(attribute == PID_PARAMETER_P) {
|
||||
parameter = &(self->P);
|
||||
} else if(attribute == PID_PARAMETER_I) {
|
||||
parameter = &(self->I);
|
||||
} else if(attribute == PID_PARAMETER_D) {
|
||||
parameter = &(self->D);
|
||||
} else if(attribute == PID_SETPOINT) {
|
||||
parameter = &(self->setpoint);
|
||||
} else if(attribute == PID_OUT) {
|
||||
parameter = &(self->out);
|
||||
}
|
||||
else {
|
||||
parameter = &(self->value);
|
||||
}
|
||||
|
||||
if(value == MP_OBJ_NULL) {
|
||||
return mp_obj_new_float(*parameter);
|
||||
} else {
|
||||
*parameter = mp_obj_get_float(value);
|
||||
}
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t pid_pid_reset(mp_obj_t _self) {
|
||||
// resets only the PID values but not the parameters
|
||||
pid_obj_t *self = MP_OBJ_TO_PTR(_self);
|
||||
self->integral = MICROPY_FLOAT_CONST(0.0);
|
||||
self->value = MICROPY_FLOAT_CONST(0.0);
|
||||
self->steps = 0;
|
||||
self->out = MICROPY_FLOAT_CONST(0.0);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(pid_pid_reset_obj, pid_pid_reset);
|
||||
|
||||
mp_float_t pid_float_time_ms(pid_obj_t *self) {
|
||||
// calculates the time difference with respect to the last call
|
||||
mp_uint_t end = mp_hal_ticks_us();
|
||||
mp_float_t dt_ms;
|
||||
|
||||
if(end > self->time_us) {
|
||||
dt_ms = 0.001 * (end - self->time_us);
|
||||
} else { // we have a time warp here, so swap
|
||||
dt_ms = 0.001 * (self->time_us - end);
|
||||
}
|
||||
self->time_us = end;
|
||||
return dt_ms;
|
||||
}
|
||||
|
||||
void pid_pid_loop(pid_obj_t *self, mp_float_t value, mp_float_t dt) {
|
||||
// This is the standard PID loop, stripped of everything
|
||||
mp_float_t error = value - self->setpoint;
|
||||
|
||||
mp_float_t diff = (error - self->error) / dt;
|
||||
self->integral += error * dt;
|
||||
self->out = self->P * error + self->I * self->integral + self->D * diff;
|
||||
|
||||
self->error = error;
|
||||
self->value = value;
|
||||
self->steps++;
|
||||
}
|
||||
|
||||
mp_obj_t pid_pid_step(mp_obj_t self_in) {
|
||||
// The complete PID loop, converting the value from the input buffer,
|
||||
// using the value in the PID loop, and then converting the results to the output buffer
|
||||
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
// // convert value in input buffer
|
||||
// // assume for now that all bytes in the buffer are significant bytes (i.e., there are no auxiliary bytes)
|
||||
uint32_t input = 0; // 32 bits should suffice for most ADCs
|
||||
uint8_t *input_bytes = (uint8_t *)&input;
|
||||
uint8_t *buffer_bytes = (uint8_t *)self->input->converter.bytes;
|
||||
input_bytes += 4;
|
||||
buffer_bytes += self->input->converter.len;
|
||||
for(uint8_t i = 0; i < self->input->converter.len; i++) {
|
||||
*(--input_bytes) = *(--buffer_bytes);
|
||||
}
|
||||
mp_float_t x = (mp_float_t)(input & self->input->converter.mask);
|
||||
mp_float_t value = pid_pid_convert(self->input->series, x);
|
||||
|
||||
mp_float_t dt_ms = pid_float_time_ms(self);
|
||||
pid_pid_loop(self, value, dt_ms);
|
||||
|
||||
// convert value in output buffer
|
||||
value = pid_pid_convert(self->output->series, self->out);
|
||||
|
||||
// ensure that output is within limits
|
||||
if(self->output->converter.bitdepth != 0) {
|
||||
value = (value < 0 ? 0 : value);
|
||||
value = (value > (mp_float_t)self->output->converter.mask ? (mp_float_t)self->output->converter.mask : value);
|
||||
}
|
||||
|
||||
uint32_t output = ((uint32_t)value) & self->output->converter.mask;
|
||||
uint8_t *output_bytes = (uint8_t *)&output;
|
||||
buffer_bytes = (uint8_t *)self->output->converter.bytes;
|
||||
output_bytes += 4;
|
||||
buffer_bytes += self->output->converter.len;
|
||||
for(uint8_t i = 0; i < self->output->converter.len; i++) {
|
||||
*(--buffer_bytes) = *(--output_bytes);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(pid_pid_step_obj, pid_pid_step);
|
||||
|
||||
mp_obj_t pid_pid_float_step(size_t n_args, const mp_obj_t *args) {
|
||||
// The simplified PID loop, working with floating points numbers directly
|
||||
pid_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
|
||||
mp_float_t dt = 1.0;
|
||||
if(n_args == 3) {
|
||||
dt = mp_obj_get_float(args[2]);
|
||||
}
|
||||
mp_float_t value = mp_obj_get_float(args[1]);
|
||||
|
||||
pid_pid_loop(self, value, dt);
|
||||
|
||||
return mp_obj_new_float(self->out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pid_pid_float_step_obj, 2, 3, pid_pid_float_step);
|
||||
|
||||
static const mp_rom_map_elem_t ulab_pid_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_PID) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PID), MP_ROM_PTR(&ulab_pid_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_buffer), MP_ROM_PTR(&ulab_pid_buffer_type) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_pid_globals, ulab_pid_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_pid_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_pid_globals,
|
||||
};
|
||||
|
||||
#endif /* ULAB_HAS_PID_MODULE */
|
||||
104
code/pid/pid.h
Normal file
104
code/pid/pid.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _PID_
|
||||
#define _PID_
|
||||
|
||||
#include "../ulab.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_pid_module;
|
||||
extern const mp_obj_type_t ulab_pid_type;
|
||||
|
||||
enum PID_PARAMETER {
|
||||
PID_PARAMETER_P,
|
||||
PID_PARAMETER_I,
|
||||
PID_PARAMETER_D,
|
||||
PID_SETPOINT,
|
||||
PID_STEPS,
|
||||
PID_VALUE,
|
||||
PID_OUT,
|
||||
};
|
||||
|
||||
enum PID_BUFFER {
|
||||
PID_BUFFER_INPUT,
|
||||
PID_BUFFER_OUTPUT,
|
||||
PID_BUFFER_BITDEPTH,
|
||||
PID_BUFFER_BYTES,
|
||||
PID_BUFFER_COEFFS,
|
||||
PID_BUFFER_MASK,
|
||||
PID_BUFFER_OFFSET,
|
||||
PID_BUFFER_ORDER,
|
||||
PID_BUFFER_X0,
|
||||
};
|
||||
|
||||
|
||||
// structure holding the Taylor series representation of
|
||||
// the conversion of physical values to unitless numbers
|
||||
typedef struct _pid_series_t {
|
||||
uint8_t order; // order of the Taylor expansion
|
||||
mp_float_t x0; // the function is expanded around this point
|
||||
mp_float_t *coeffs; // pointer to coefficients of the Taylor series
|
||||
} pid_series_t;
|
||||
|
||||
// structure holding the description and byte data of the input/output buffers
|
||||
typedef struct _pid_converter_t {
|
||||
uint8_t len; // length of the input/output buffer, bytes
|
||||
uint8_t bitdepth; // resolution of ADC/DAC, bits
|
||||
uint32_t mask; // mask to get rid of non-numerical data (calculated)
|
||||
uint8_t offset; // offset of beginning of data relative to beginning of buffer, bytes
|
||||
uint8_t *bytes; // pointer to bytes of the buffer
|
||||
uint8_t nbytes; // number of bytes in value; calculated from bitdepth
|
||||
} pid_converter_t;
|
||||
|
||||
typedef struct _pid_buffer_t {
|
||||
pid_converter_t converter;
|
||||
pid_series_t series;
|
||||
} pid_buffer_t;
|
||||
|
||||
typedef struct _pid_buffer_obj_t {
|
||||
mp_obj_base_t base;
|
||||
pid_converter_t converter;
|
||||
pid_series_t series;
|
||||
} pid_buffer_obj_t;
|
||||
|
||||
typedef struct _pid_obj_t {
|
||||
mp_obj_base_t base;
|
||||
mp_float_t setpoint; // set point of the controller loop
|
||||
pid_buffer_obj_t *input; // the input buffer
|
||||
pid_buffer_obj_t *output; // the output buffer
|
||||
mp_float_t P; // coefficient of the proportional term
|
||||
mp_float_t I; // coefficient of the integral term
|
||||
mp_float_t D; // coefficient of the differential term
|
||||
mp_float_t integral; // the last value of the integral term
|
||||
mp_float_t out; // the output value
|
||||
mp_float_t value; // the last converted value supplied to the loop
|
||||
mp_float_t error; // the last calculated error; the difference between value and set_point
|
||||
mp_uint_t time_us; // absolute system time, us
|
||||
uint64_t steps; // the step method has been called this many times
|
||||
} pid_obj_t;
|
||||
|
||||
mp_obj_t pid_buffer_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
|
||||
void pid_buffer_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
|
||||
mp_obj_t pid_buffer_getter(mp_obj_t , uint8_t );
|
||||
mp_obj_t pid_buffer_setter(mp_obj_t , mp_obj_t , uint8_t );
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(pid_buffer_evaluate_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(pid_buffer_init_obj);
|
||||
|
||||
mp_obj_t pid_pid_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
|
||||
void pid_pid_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
|
||||
mp_obj_t pid_pid_parameters(mp_obj_t , mp_obj_t , uint8_t );
|
||||
mp_obj_t pid_return_buffer(mp_obj_t , mp_obj_t , uint8_t );
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(pid_pid_reset_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(pid_pid_float_step_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(pid_pid_step_obj);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,701 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Harald Milz <hm@seneca.muc.de>
|
||||
*
|
||||
* References:
|
||||
* - Dr. Robert van Engelen, Improving the mp_float_t Exponential Quadrature Tanh-Sinh, Sinh-Sinh and Exp-Sinh Formulas,
|
||||
* 2021, https://www.genivia.com/qthsh.html
|
||||
* - Borwein, Bailey & Girgensohn, "Experimentation in Mathematics - Computational Paths to Discovery", A K Peters,
|
||||
* 2003, pages 312-313
|
||||
* - Joren Vanherck, Bart Sorée, Wim Magnus, Tanh-sinh quadrature for single and multiple integration using
|
||||
* floating-point arithmetic, 2020, https://arxiv.org/abs/2007.15057
|
||||
* - Tanh-Sinh quadrature, Wikipedia, https://en.wikipedia.org/wiki/Tanh-sinh_quadrature
|
||||
* - Romberg's method, Wikipedia, https://en.wikipedia.org/wiki/Romberg%27s_method
|
||||
* - Adaptive Simpson's method, Wikipedia, https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method
|
||||
* - Gauss–Kronrod quadrature formula, Wikipedia, https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula
|
||||
*
|
||||
* This module provides four integration methods, and thus deviates from scipy.integrate a bit.
|
||||
* As for the pros and cons of the different methods please consult the literature above.
|
||||
* The code was ported to Micropython from Dr. Engelen's paper and used with his written kind permission
|
||||
* - quad - Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature
|
||||
* - romberg - Romberg quadrature
|
||||
* - simpson - Adaptive Simpson quadrature
|
||||
* - quadgk - Adaptive Gauss-Kronrod (G10,K21) quadrature
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/objtuple.h"
|
||||
|
||||
#include "../../ndarray.h"
|
||||
#include "../../ulab.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "integrate.h"
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-14), 0x283424dcUL, 0x3e901b2b29a4692bULL);
|
||||
#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-17)
|
||||
#else
|
||||
ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-8), 0x358637cfUL, 0x3e7010c6f7d42d18ULL);
|
||||
#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-8)
|
||||
#endif
|
||||
|
||||
#define ULAB_ZERO MICROPY_FLOAT_CONST(0.0)
|
||||
#define ULAB_POINT_TWO_FIVE MICROPY_FLOAT_CONST(0.25)
|
||||
#define ULAB_ONE MICROPY_FLOAT_CONST(1.0)
|
||||
#define ULAB_TWO MICROPY_FLOAT_CONST(2.0)
|
||||
#define ULAB_FOUR MICROPY_FLOAT_CONST(4.0)
|
||||
#define ULAB_SIX MICROPY_FLOAT_CONST(6.0)
|
||||
#define ULAB_TEN MICROPY_FLOAT_CONST(10.0)
|
||||
#define ULAB_FIFTEEN MICROPY_FLOAT_CONST(15.0)
|
||||
#define ULAB_EPSILON_5 MICROPY_FLOAT_CONST(1e-5)
|
||||
|
||||
|
||||
static mp_float_t integrate_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) {
|
||||
// Helper function for calculating the value of f(x, a, b, c, ...),
|
||||
// where f is defined in python. Takes a float, returns a float.
|
||||
// The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams
|
||||
fargs[0] = mp_obj_new_float(x);
|
||||
return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs));
|
||||
}
|
||||
|
||||
// sign helper function
|
||||
int sign(mp_float_t x) {
|
||||
if (x >= ULAB_ZERO)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_TANHSINH
|
||||
// Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature
|
||||
// https://www.genivia.com/qthsh.html
|
||||
|
||||
// return optimized Exp-Sinh integral split point d
|
||||
mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t eps, mp_float_t d) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t h2 = integrate_python_call(type, fun, a + d/2, fargs, 0) - integrate_python_call(type, fun, (a + d*2)*4, fargs, 0);
|
||||
int i = 1, j = 32; // j=32 is optimal to find r
|
||||
if (isfinite(h2) && MICROPY_FLOAT_C_FUN(fabs)(h2) > ULAB_EPSILON_5) { // if |h2| > 2^-16
|
||||
mp_float_t r, fl, fr, h, s = 0, lfl, lfr, lr = 2;
|
||||
do { // find max j such that fl and fr are finite
|
||||
j /= 2;
|
||||
r = 1 << (i + j);
|
||||
fl = integrate_python_call(type, fun, a + d/r, fargs, 0);
|
||||
fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0);
|
||||
h = fl - fr;
|
||||
} while (j > 1 && !isfinite(h));
|
||||
if (j > 1 && isfinite(h) && sign(h) != sign(h2)) {
|
||||
lfl = fl; // last fl=f(a+d/r)
|
||||
lfr = fr; // last fr=f(a+d*r)*r*r
|
||||
do { // bisect in 4 iterations
|
||||
j /= 2;
|
||||
r = 1 << (i + j);
|
||||
fl = integrate_python_call(type, fun, a + d/r, fargs, 0);
|
||||
fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0);
|
||||
h = fl - fr;
|
||||
if (isfinite(h)) {
|
||||
s += MICROPY_FLOAT_C_FUN(fabs)(h); // sum |h| to remove noisy cases
|
||||
if (sign(h) == sign(h2)) {
|
||||
i += j; // search right half
|
||||
}
|
||||
else { // search left half
|
||||
lfl = fl; // record last fl=f(a+d/r)
|
||||
lfr = fr; // record last fl=f(a+d*r)*r*r
|
||||
lr = r; // record last r
|
||||
}
|
||||
}
|
||||
} while (j > 1);
|
||||
if (s > eps) { // if sum of |h| > eps
|
||||
h = lfl - lfr; // use last fl and fr before the sign change
|
||||
r = lr; // use last r before the sign change
|
||||
if (h != ULAB_ZERO) // if last diff != 0, back up r by one step
|
||||
r /= ULAB_TWO;
|
||||
if (MICROPY_FLOAT_C_FUN(fabs)(lfl) < MICROPY_FLOAT_C_FUN(fabs)(lfr))
|
||||
d /= r; // move d closer to the finite endpoint
|
||||
else
|
||||
d *= r; // move d closer to the infinite endpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// integrate function f, range a..b, max levels n, error tolerance eps
|
||||
mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps, mp_float_t *e) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
const mp_float_t tol = ULAB_TEN * eps;
|
||||
mp_float_t c = ULAB_ZERO, d = ULAB_ONE, s, sign = ULAB_ONE, v, h = ULAB_TWO;
|
||||
int k = 0, mode = 0; // Tanh-Sinh = 0, Exp-Sinh = 1, Sinh-Sinh = 2
|
||||
if (b < a) { // swap bounds
|
||||
v = b;
|
||||
b = a;
|
||||
a = v;
|
||||
sign = -1;
|
||||
}
|
||||
if (isfinite(a) && isfinite(b)) {
|
||||
c = (a+b) / ULAB_TWO;
|
||||
d = (b-a) / ULAB_TWO;
|
||||
v = c;
|
||||
}
|
||||
else if (isfinite(a)) {
|
||||
mode = 1; // Exp-Sinh
|
||||
d = exp_sinh_opt_d(fun, a, eps, d);
|
||||
c = a;
|
||||
v = a+d;
|
||||
}
|
||||
else if (isfinite(b)) {
|
||||
mode = 1; // Exp-Sinh
|
||||
// d = -d;
|
||||
d = exp_sinh_opt_d(fun, b, eps, -d);
|
||||
sign = -sign;
|
||||
c = b;
|
||||
v = b+d;
|
||||
}
|
||||
else {
|
||||
mode = 2; // Sinh-Sinh
|
||||
v = ULAB_ZERO;
|
||||
}
|
||||
s = integrate_python_call(type, fun, v, fargs, 0);
|
||||
do {
|
||||
mp_float_t p = ULAB_ZERO, q, fp = ULAB_ZERO, fm = ULAB_ZERO, t, eh;
|
||||
h /= ULAB_TWO;
|
||||
t = eh = MICROPY_FLOAT_C_FUN(exp)(h);
|
||||
if (k > ULAB_ZERO)
|
||||
eh *= eh;
|
||||
if (mode == 0) { // Tanh-Sinh
|
||||
do {
|
||||
mp_float_t u = MICROPY_FLOAT_C_FUN(exp)(ULAB_ONE / t - t); // = exp(-2*sinh(j*h)) = 1/exp(sinh(j*h))^2
|
||||
mp_float_t r = ULAB_TWO * u / (ULAB_ONE + u); // = 1 - tanh(sinh(j*h))
|
||||
mp_float_t w = (t + ULAB_ONE / t) * r / (ULAB_ONE + u); // = cosh(j*h)/cosh(sinh(j*h))^2
|
||||
mp_float_t x = d*r;
|
||||
if (a+x > a) { // if too close to a then reuse previous fp
|
||||
mp_float_t y = integrate_python_call(type, fun, a+x, fargs, 0);
|
||||
if (isfinite(y))
|
||||
fp = y; // if f(x) is finite, add to local sum
|
||||
}
|
||||
if (b-x < b) { // if too close to a then reuse previous fp
|
||||
mp_float_t y = integrate_python_call(type, fun, b-x, fargs, 0);
|
||||
if (isfinite(y))
|
||||
fm = y; // if f(x) is finite, add to local sum
|
||||
}
|
||||
q = w*(fp+fm);
|
||||
p += q;
|
||||
t *= eh;
|
||||
} while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p));
|
||||
}
|
||||
else {
|
||||
t /= ULAB_TWO;
|
||||
do {
|
||||
mp_float_t r = MICROPY_FLOAT_C_FUN(exp)(t - ULAB_POINT_TWO_FIVE / t); // = exp(sinh(j*h))
|
||||
mp_float_t x, y, w = r;
|
||||
q = ULAB_ZERO;
|
||||
if (mode == 1) { // Exp-Sinh
|
||||
x = c + d/r;
|
||||
if (x == c) // if x hit the finite endpoint then break
|
||||
break;
|
||||
y = integrate_python_call(type, fun, x, fargs, 0);
|
||||
if (isfinite(y)) // if f(x) is finite, add to local sum
|
||||
q += y/w;
|
||||
}
|
||||
else { // Sinh-Sinh
|
||||
r = (r - ULAB_ONE / r) / ULAB_TWO; // = sinh(sinh(j*h))
|
||||
w = (w + ULAB_ONE / w) / ULAB_TWO; // = cosh(sinh(j*h))
|
||||
x = c - d*r;
|
||||
y = integrate_python_call(type, fun, x, fargs, 0);
|
||||
if (isfinite(y)) // if f(x) is finite, add to local sum
|
||||
q += y*w;
|
||||
}
|
||||
x = c + d*r;
|
||||
y = integrate_python_call(type, fun, x, fargs, 0);
|
||||
if (isfinite(y)) // if f(x) is finite, add to local sum
|
||||
q += y*w;
|
||||
q *= t + ULAB_POINT_TWO_FIVE / t; // q *= cosh(j*h)
|
||||
p += q;
|
||||
t *= eh;
|
||||
} while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p));
|
||||
}
|
||||
v = s-p;
|
||||
s += p;
|
||||
++k;
|
||||
} while (MICROPY_FLOAT_C_FUN(fabs)(v) > tol*MICROPY_FLOAT_C_FUN(fabs)(s) && k <= n);
|
||||
// return the error estimate by reference
|
||||
*e = MICROPY_FLOAT_C_FUN(fabs)(v)/(MICROPY_FLOAT_C_FUN(fabs)(s)+eps);
|
||||
return sign*d*s*h; // result with estimated relative error e
|
||||
}
|
||||
|
||||
//| def tanhsinh(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| levels: int = 6
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float levels: The number of levels to perform (6..7 is optimal)
|
||||
//| :param float eps: The error tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using an optimized double exponential. The result is accurate to within
|
||||
//| ``eps`` unless more than ``levels`` levels are required."""
|
||||
//|
|
||||
|
||||
|
||||
static mp_obj_t integrate_tanhsinh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_levels, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 6} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t n = (uint16_t)args[3].u_int;
|
||||
if (n < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("levels needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
mp_obj_t res[2];
|
||||
mp_float_t e;
|
||||
res[0] = mp_obj_new_float(tanhsinh(fun, a, b, n, eps, &e));
|
||||
res[1] = mp_obj_new_float(e);
|
||||
return mp_obj_new_tuple(2, res);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_tanhsinh_obj, 2, integrate_tanhsinh);
|
||||
#endif /* ULAB_INTEGRATE_HAS_TANHSINH */
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_ROMBERG
|
||||
// Romberg quadrature
|
||||
// This function is deprecated as of SciPy 1.12.0 and will be removed in SciPy 1.15.0. Please use scipy.integrate.quad instead.
|
||||
// https://en.wikipedia.org/wiki/Romberg%27s_method, https://www.genivia.com/qthsh.html,
|
||||
// https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html (which is different
|
||||
// insofar as the latter expects an array of function values).
|
||||
|
||||
mp_float_t qromb(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t R1[n], R2[n];
|
||||
mp_float_t *Ro = &R1[0], *Ru = &R2[0];
|
||||
mp_float_t h = b-a;
|
||||
uint16_t i, j;
|
||||
Ro[0] = (integrate_python_call(type, fun, a, fargs, 0) + integrate_python_call(type, fun, b, fargs, 0)) * h/2;
|
||||
for (i = 1; i < n; ++i) {
|
||||
unsigned long long k = 1UL << i;
|
||||
unsigned long long s = 1;
|
||||
mp_float_t sum = ULAB_ZERO;
|
||||
mp_float_t *Rt;
|
||||
h /= ULAB_TWO;
|
||||
for (j = 1; j < k; j += 2)
|
||||
sum += integrate_python_call(type, fun, a+j*h, fargs, 0);
|
||||
Ru[0] = h*sum + Ro[0] / ULAB_TWO;
|
||||
for (j = 1; j <= i; ++j) {
|
||||
s <<= 2;
|
||||
Ru[j] = (s*Ru[j-1] - Ro[j-1])/(s-1);
|
||||
}
|
||||
if (i > 2 && MICROPY_FLOAT_C_FUN(fabs)(Ro[i-1]-Ru[i]) <= eps*MICROPY_FLOAT_C_FUN(fabs)(Ru[i])+eps)
|
||||
return Ru[i];
|
||||
Rt = Ro;
|
||||
Ro = Ru;
|
||||
Ru = Rt;
|
||||
}
|
||||
return Ro[n-1];
|
||||
}
|
||||
|
||||
//| def romberg(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| steps: int = 100
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float steps: The number of equidistant steps
|
||||
//| :param float eps: The tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the Romberg method. The result is accurate to within
|
||||
//| ``eps`` unless more than ``steps`` steps are required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t integrate_romberg(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t steps = (uint16_t)args[3].u_int;
|
||||
if (steps < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
return mp_obj_new_float(qromb(fun, a, b, steps, eps));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_romberg_obj, 2, integrate_romberg);
|
||||
#endif /* ULAB_INTEGRATE_HAS_ROMBERG */
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_SIMPSON
|
||||
// Adaptive Simpson quadrature
|
||||
// https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method, https://www.genivia.com/qthsh.html
|
||||
|
||||
mp_float_t as(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, mp_float_t fa, mp_float_t fm,
|
||||
mp_float_t fb, mp_float_t v, mp_float_t eps, int n, mp_float_t t) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t h = (b-a) / ULAB_TWO;
|
||||
mp_float_t f1 = integrate_python_call(type, fun, a + h / ULAB_TWO, fargs, 0);
|
||||
mp_float_t f2 = integrate_python_call(type, fun, b - h / ULAB_TWO, fargs, 0);
|
||||
mp_float_t sl = h*(fa + ULAB_FOUR * f1 + fm) / ULAB_SIX;
|
||||
mp_float_t sr = h*(fm + ULAB_FOUR * f2 + fb) / ULAB_SIX;
|
||||
mp_float_t s = sl+sr;
|
||||
mp_float_t d = (s-v) / ULAB_FIFTEEN;
|
||||
mp_float_t m = a+h;
|
||||
if (n <= 0 || MICROPY_FLOAT_C_FUN(fabs)(d) < eps)
|
||||
return t + s + d; // note: fabs(d) can be used as error estimate
|
||||
eps /= ULAB_TWO;
|
||||
--n;
|
||||
t = as(fun, a, m, fa, f1, fm, sl, eps, n, t);
|
||||
return as(fun, m, b, fm, f2, fb, sr, eps, n, t);
|
||||
}
|
||||
|
||||
mp_float_t qasi(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t eps) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t fa = integrate_python_call(type, fun, a, fargs, 0);
|
||||
mp_float_t fm = integrate_python_call(type, fun, (a+b)/2, fargs, 0);
|
||||
mp_float_t fb = integrate_python_call(type, fun, b, fargs, 0);
|
||||
mp_float_t v = (fa + ULAB_FOUR * fm + fb) * (b-a) / ULAB_SIX;
|
||||
return as(fun, a, b, fa, fm, fb, v, eps, n, 0);
|
||||
}
|
||||
|
||||
//| def simpson(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| steps: int = 100
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float steps: The number of equidistant steps
|
||||
//| :param float eps: The tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the Adaptive Simpson's method. The result is accurate to within
|
||||
//| ``eps`` unless more than ``steps`` steps are required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t integrate_simpson(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t steps = (uint16_t)args[3].u_int;
|
||||
if (steps < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
return mp_obj_new_float(qasi(fun, a, b, steps, eps));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_simpson_obj, 2, integrate_simpson);
|
||||
#endif /* ULAB_INTEGRATE_HAS_SIMPSON */
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_QUAD
|
||||
// Adaptive Gauss-Kronrod (G10,K21) quadrature
|
||||
// https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula, https://www.genivia.com/qthsh.html
|
||||
|
||||
mp_float_t gk(mp_float_t (*fun)(mp_float_t), mp_float_t c, mp_float_t d, mp_float_t *err) {
|
||||
// abscissas and weights pre-calculated with Legendre Stieltjes polynomials
|
||||
static const mp_float_t abscissas[21] = {
|
||||
MICROPY_FLOAT_CONST(0.00000000000000000e+00),
|
||||
MICROPY_FLOAT_CONST(7.65265211334973338e-02),
|
||||
MICROPY_FLOAT_CONST(1.52605465240922676e-01),
|
||||
MICROPY_FLOAT_CONST(2.27785851141645078e-01),
|
||||
MICROPY_FLOAT_CONST(3.01627868114913004e-01),
|
||||
MICROPY_FLOAT_CONST(3.73706088715419561e-01),
|
||||
MICROPY_FLOAT_CONST(4.43593175238725103e-01),
|
||||
MICROPY_FLOAT_CONST(5.10867001950827098e-01),
|
||||
MICROPY_FLOAT_CONST(5.75140446819710315e-01),
|
||||
MICROPY_FLOAT_CONST(6.36053680726515025e-01),
|
||||
MICROPY_FLOAT_CONST(6.93237656334751385e-01),
|
||||
MICROPY_FLOAT_CONST(7.46331906460150793e-01),
|
||||
MICROPY_FLOAT_CONST(7.95041428837551198e-01),
|
||||
MICROPY_FLOAT_CONST(8.39116971822218823e-01),
|
||||
MICROPY_FLOAT_CONST(8.78276811252281976e-01),
|
||||
MICROPY_FLOAT_CONST(9.12234428251325906e-01),
|
||||
MICROPY_FLOAT_CONST(9.40822633831754754e-01),
|
||||
MICROPY_FLOAT_CONST(9.63971927277913791e-01),
|
||||
MICROPY_FLOAT_CONST(9.81507877450250259e-01),
|
||||
MICROPY_FLOAT_CONST(9.93128599185094925e-01),
|
||||
MICROPY_FLOAT_CONST(9.98859031588277664e-01),
|
||||
};
|
||||
static const mp_float_t weights[21] = {
|
||||
MICROPY_FLOAT_CONST(7.66007119179996564e-02),
|
||||
MICROPY_FLOAT_CONST(7.63778676720807367e-02),
|
||||
MICROPY_FLOAT_CONST(7.57044976845566747e-02),
|
||||
MICROPY_FLOAT_CONST(7.45828754004991890e-02),
|
||||
MICROPY_FLOAT_CONST(7.30306903327866675e-02),
|
||||
MICROPY_FLOAT_CONST(7.10544235534440683e-02),
|
||||
MICROPY_FLOAT_CONST(6.86486729285216193e-02),
|
||||
MICROPY_FLOAT_CONST(6.58345971336184221e-02),
|
||||
MICROPY_FLOAT_CONST(6.26532375547811680e-02),
|
||||
MICROPY_FLOAT_CONST(5.91114008806395724e-02),
|
||||
MICROPY_FLOAT_CONST(5.51951053482859947e-02),
|
||||
MICROPY_FLOAT_CONST(5.09445739237286919e-02),
|
||||
MICROPY_FLOAT_CONST(4.64348218674976747e-02),
|
||||
MICROPY_FLOAT_CONST(4.16688733279736863e-02),
|
||||
MICROPY_FLOAT_CONST(3.66001697582007980e-02),
|
||||
MICROPY_FLOAT_CONST(3.12873067770327990e-02),
|
||||
MICROPY_FLOAT_CONST(2.58821336049511588e-02),
|
||||
MICROPY_FLOAT_CONST(2.03883734612665236e-02),
|
||||
MICROPY_FLOAT_CONST(1.46261692569712530e-02),
|
||||
MICROPY_FLOAT_CONST(8.60026985564294220e-03),
|
||||
MICROPY_FLOAT_CONST(3.07358371852053150e-03),
|
||||
};
|
||||
static const mp_float_t gauss_weights[10] = {
|
||||
MICROPY_FLOAT_CONST(1.52753387130725851e-01),
|
||||
MICROPY_FLOAT_CONST(1.49172986472603747e-01),
|
||||
MICROPY_FLOAT_CONST(1.42096109318382051e-01),
|
||||
MICROPY_FLOAT_CONST(1.31688638449176627e-01),
|
||||
MICROPY_FLOAT_CONST(1.18194531961518417e-01),
|
||||
MICROPY_FLOAT_CONST(1.01930119817240435e-01),
|
||||
MICROPY_FLOAT_CONST(8.32767415767047487e-02),
|
||||
MICROPY_FLOAT_CONST(6.26720483341090636e-02),
|
||||
MICROPY_FLOAT_CONST(4.06014298003869413e-02),
|
||||
MICROPY_FLOAT_CONST(1.76140071391521183e-02),
|
||||
};
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t p = ULAB_ZERO; // kronrod quadrature sum
|
||||
mp_float_t q = ULAB_ZERO; // gauss quadrature sum
|
||||
mp_float_t fp, fm;
|
||||
mp_float_t e;
|
||||
int i;
|
||||
fp = integrate_python_call(type, fun, c, fargs, 0);
|
||||
p = fp * weights[0];
|
||||
for (i = 1; i < 21; i += 2) {
|
||||
fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0);
|
||||
fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0);
|
||||
p += (fp + fm) * weights[i];
|
||||
q += (fp + fm) * gauss_weights[i/2];
|
||||
}
|
||||
for (i = 2; i < 21; i += 2) {
|
||||
fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0);
|
||||
fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0);
|
||||
p += (fp + fm) * weights[i];
|
||||
}
|
||||
*err = MICROPY_FLOAT_C_FUN(fabs)(p - q);
|
||||
e = MICROPY_FLOAT_C_FUN(fabs)(2 * p * ULAB_MACHEPS); // optional, to take 1e-17 MachEps prec. into account
|
||||
if (*err < e)
|
||||
*err = e;
|
||||
return p;
|
||||
}
|
||||
|
||||
mp_float_t qakro(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t tol, mp_float_t eps, mp_float_t *err) {
|
||||
mp_float_t c = (a+b) / ULAB_TWO;
|
||||
mp_float_t d = (b-a) / ULAB_TWO;
|
||||
mp_float_t e;
|
||||
mp_float_t r = gk(fun, c, d, &e);
|
||||
mp_float_t s = d*r;
|
||||
mp_float_t t = MICROPY_FLOAT_C_FUN(fabs)(s*tol);
|
||||
if (tol == ULAB_ZERO)
|
||||
tol = t;
|
||||
if (n > 0 && t < e && tol < e) {
|
||||
s = qakro(fun, a, c, n-1, t / ULAB_TWO, eps, err);
|
||||
s += qakro(fun, c, b, n-1, t / ULAB_TWO, eps, &e);
|
||||
*err += e;
|
||||
return s;
|
||||
}
|
||||
*err = e;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//| def quad(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| order: int = 5
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float order: Order of quadrature integration. Default is 5.
|
||||
//| :param float eps: The tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the Adaptive Gauss-Kronrod method. The result is accurate to within
|
||||
//| ``eps`` unless a higher order than ``order`` is required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t integrate_quad(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_order, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t order = (uint16_t)args[3].u_int;
|
||||
if (order < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("order needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
mp_obj_t res[2];
|
||||
mp_float_t e;
|
||||
res[0] = mp_obj_new_float(qakro(fun, a, b, order, 0, eps, &e));
|
||||
res[1] = mp_obj_new_float(e);
|
||||
return mp_obj_new_tuple(2, res);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_quad_obj, 2, integrate_quad);
|
||||
#endif /* ULAB_INTEGRATE_HAS_QUAD */
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_integrate_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_integrate) },
|
||||
#if ULAB_INTEGRATE_HAS_TANHSINH
|
||||
{ MP_ROM_QSTR(MP_QSTR_tanhsinh), MP_ROM_PTR(&integrate_tanhsinh_obj) },
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_ROMBERG
|
||||
{ MP_ROM_QSTR(MP_QSTR_romberg), MP_ROM_PTR(&integrate_romberg_obj) },
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_SIMPSON
|
||||
{ MP_ROM_QSTR(MP_QSTR_simpson), MP_ROM_PTR(&integrate_simpson_obj) },
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_QUAD
|
||||
{ MP_ROM_QSTR(MP_QSTR_quad), MP_ROM_PTR(&integrate_quad_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_integrate_globals, ulab_scipy_integrate_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_integrate_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_integrate_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_integrate, ulab_scipy_integrate_module);
|
||||
#endif
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Harald Milz <hm@seneca.muc.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_INTEGRATE_
|
||||
#define _SCIPY_INTEGRATE_
|
||||
|
||||
#include "../../ulab_tools.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_integrate_module;
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_TANHSINH
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_tanhsinh_obj);
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_ROMBERG
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_romberg_obj);
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_SIMPSON
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_simpson_obj);
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_QUAD
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_quad_obj);
|
||||
#endif
|
||||
|
||||
#endif /* _SCIPY_INTEGRATE_ */
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun,
|
|||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// Simple bisection routine
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
|
|
@ -125,7 +125,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(optimize_bisect_obj, 3, optimize_bisect);
|
|||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// downhill simplex method in 1D
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
|
|
@ -19,8 +20,6 @@
|
|||
#include "signal/signal.h"
|
||||
#include "special/special.h"
|
||||
#include "linalg/linalg.h"
|
||||
#include "integrate/integrate.h"
|
||||
|
||||
|
||||
#if ULAB_HAS_SCIPY
|
||||
|
||||
|
|
@ -29,9 +28,6 @@
|
|||
|
||||
static const mp_rom_map_elem_t ulab_scipy_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_scipy) },
|
||||
#if ULAB_SCIPY_HAS_INTEGRATE_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_integrate), MP_ROM_PTR(&ulab_scipy_integrate_module) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_HAS_LINALG_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) },
|
||||
#endif
|
||||
|
|
|
|||
20
code/ulab.c
20
code/ulab.c
|
|
@ -26,6 +26,7 @@
|
|||
#include "numpy/ndarray/ndarray_iter.h"
|
||||
|
||||
#include "numpy/numpy.h"
|
||||
#include "pid/pid.h"
|
||||
#include "scipy/scipy.h"
|
||||
// TODO: we should get rid of this; array.sort depends on it
|
||||
#include "numpy/numerical.h"
|
||||
|
|
@ -33,7 +34,7 @@
|
|||
#include "user/user.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#define ULAB_VERSION 6.9.0
|
||||
#define ULAB_VERSION 6.6.0
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
|
|
@ -43,13 +44,13 @@
|
|||
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D)
|
||||
#endif
|
||||
|
||||
static MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING);
|
||||
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING);
|
||||
|
||||
#ifdef ULAB_HASH
|
||||
static MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH));
|
||||
STATIC MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH));
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
|
||||
STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if NDARRAY_HAS_RESHAPE
|
||||
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) },
|
||||
|
|
@ -78,7 +79,7 @@ static const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
|
|||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
|
||||
STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
|
||||
|
||||
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
|
||||
// MicroPython after-b41aaaa (Sept 19 2022).
|
||||
|
|
@ -192,7 +193,7 @@ const mp_obj_type_t ndarray_flatiter_type = {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_globals_table[] = {
|
||||
STATIC const mp_rom_map_elem_t ulab_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) },
|
||||
#ifdef ULAB_HASH
|
||||
|
|
@ -205,7 +206,10 @@ static const mp_rom_map_elem_t ulab_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) },
|
||||
#endif /* NDARRAY_HAS_DTYPE */
|
||||
#endif /* ULAB_HAS_DTYPE_OBJECT */
|
||||
{ MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) },
|
||||
#if ULAB_HAS_PID_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_PID), MP_ROM_PTR(&ulab_pid_module) },
|
||||
#endif
|
||||
#if ULAB_HAS_SCIPY
|
||||
{ MP_ROM_QSTR(MP_QSTR_scipy), MP_ROM_PTR(&ulab_scipy_module) },
|
||||
#endif
|
||||
|
|
@ -217,7 +221,7 @@ static const mp_rom_map_elem_t ulab_globals_table[] = {
|
|||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT (
|
||||
STATIC MP_DEFINE_CONST_DICT (
|
||||
mp_module_ulab_globals,
|
||||
ulab_globals_table
|
||||
);
|
||||
|
|
|
|||
44
code/ulab.h
44
code/ulab.h
|
|
@ -117,10 +117,6 @@
|
|||
#define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_MODULO
|
||||
#define NDARRAY_HAS_BINARY_OP_MODULO (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_MORE
|
||||
#define NDARRAY_HAS_BINARY_OP_MORE (1)
|
||||
#endif
|
||||
|
|
@ -165,10 +161,6 @@
|
|||
#define NDARRAY_HAS_INPLACE_ADD (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_MODULO
|
||||
#define NDARRAY_HAS_INPLACE_MODU (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_MULTIPLY
|
||||
#define NDARRAY_HAS_INPLACE_MULTIPLY (1)
|
||||
#endif
|
||||
|
|
@ -253,10 +245,6 @@
|
|||
#define NDARRAY_HAS_ITEMSIZE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_NDIM
|
||||
#define NDARRAY_HAS_NDIM (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_RESHAPE
|
||||
#define NDARRAY_HAS_RESHAPE (1)
|
||||
#endif
|
||||
|
|
@ -410,28 +398,6 @@
|
|||
#define ULAB_NUMPY_HAS_WHERE (1)
|
||||
#endif
|
||||
|
||||
// the integrate module; functions of the integrate module still have
|
||||
// to be defined separately
|
||||
#ifndef ULAB_SCIPY_HAS_INTEGRATE_MODULE
|
||||
#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_TANHSINH
|
||||
#define ULAB_INTEGRATE_HAS_TANHSINH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_ROMBERG
|
||||
#define ULAB_INTEGRATE_HAS_ROMBERG (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_SIMPSON
|
||||
#define ULAB_INTEGRATE_HAS_SIMPSON (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_QUAD
|
||||
#define ULAB_INTEGRATE_HAS_QUAD (1)
|
||||
#endif
|
||||
|
||||
// the linalg module; functions of the linalg module still have
|
||||
// to be defined separately
|
||||
#ifndef ULAB_NUMPY_HAS_LINALG_MODULE
|
||||
|
|
@ -474,7 +440,7 @@
|
|||
// Note that in this case, the input also must be numpythonic,
|
||||
// i.e., the real an imaginary parts cannot be passed as two arguments
|
||||
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
|
||||
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (0)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_FFT_HAS_FFT
|
||||
|
|
@ -593,10 +559,6 @@
|
|||
#define ULAB_NUMPY_HAS_SUM (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TAKE
|
||||
#define ULAB_NUMPY_HAS_TAKE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TRACE
|
||||
#define ULAB_NUMPY_HAS_TRACE (1)
|
||||
#endif
|
||||
|
|
@ -752,6 +714,10 @@
|
|||
#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1)
|
||||
#endif
|
||||
|
||||
// PID module
|
||||
#ifndef ULAB_HAS_PID_MODULE
|
||||
#define ULAB_HAS_PID_MODULE (1)
|
||||
#endif
|
||||
|
||||
// scipy modules
|
||||
#ifndef ULAB_SCIPY_HAS_LINALG_MODULE
|
||||
|
|
|
|||
|
|
@ -162,15 +162,6 @@ void *ndarray_set_float_function(uint8_t dtype) {
|
|||
}
|
||||
#endif /* NDARRAY_BINARY_USES_FUN_POINTER */
|
||||
|
||||
int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) {
|
||||
int8_t ax = mp_obj_get_int(axis);
|
||||
if(ax < 0) ax += ndim;
|
||||
if((ax < 0) || (ax > ndim - 1)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds"));
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) {
|
||||
// TODO: replace numerical_reduce_axes with this function, wherever applicable
|
||||
// This function should be used, whenever a tensor is contracted;
|
||||
|
|
@ -181,36 +172,38 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) {
|
|||
}
|
||||
shape_strides _shape_strides;
|
||||
|
||||
_shape_strides.increment = 0;
|
||||
// this is the contracted dimension (won't be overwritten for axis == None)
|
||||
_shape_strides.ndim = 0;
|
||||
|
||||
if(axis == mp_const_none) {
|
||||
_shape_strides.shape = ndarray->shape;
|
||||
_shape_strides.strides = ndarray->strides;
|
||||
return _shape_strides;
|
||||
}
|
||||
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS + 1);
|
||||
_shape_strides.shape = shape;
|
||||
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS + 1);
|
||||
_shape_strides.strides = strides;
|
||||
|
||||
_shape_strides.increment = 0;
|
||||
// this is the contracted dimension (won't be overwritten for axis == None)
|
||||
_shape_strides.ndim = 0;
|
||||
|
||||
memcpy(_shape_strides.shape, ndarray->shape, sizeof(size_t) * ULAB_MAX_DIMS);
|
||||
memcpy(_shape_strides.strides, ndarray->strides, sizeof(int32_t) * ULAB_MAX_DIMS);
|
||||
|
||||
_shape_strides.axis = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten)
|
||||
if(axis == mp_const_none) {
|
||||
return _shape_strides;
|
||||
}
|
||||
|
||||
uint8_t index = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten)
|
||||
|
||||
if(axis != mp_const_none) { // i.e., axis is an integer
|
||||
int8_t ax = tools_get_axis(axis, ndarray->ndim);
|
||||
_shape_strides.axis = ULAB_MAX_DIMS - ndarray->ndim + ax;
|
||||
int8_t ax = mp_obj_get_int(axis);
|
||||
if(ax < 0) ax += ndarray->ndim;
|
||||
if((ax < 0) || (ax > ndarray->ndim - 1)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("index out of range"));
|
||||
}
|
||||
index = ULAB_MAX_DIMS - ndarray->ndim + ax;
|
||||
_shape_strides.ndim = ndarray->ndim - 1;
|
||||
}
|
||||
|
||||
// move the value stored at index to the leftmost position, and align everything else to the right
|
||||
_shape_strides.shape[0] = ndarray->shape[_shape_strides.axis];
|
||||
_shape_strides.strides[0] = ndarray->strides[_shape_strides.axis];
|
||||
for(uint8_t i = 0; i < _shape_strides.axis; i++) {
|
||||
_shape_strides.shape[0] = ndarray->shape[index];
|
||||
_shape_strides.strides[0] = ndarray->strides[index];
|
||||
for(uint8_t i = 0; i < index; i++) {
|
||||
// entries to the right of index must be shifted by one position to the left
|
||||
_shape_strides.shape[i + 1] = ndarray->shape[i];
|
||||
_shape_strides.strides[i + 1] = ndarray->strides[i];
|
||||
|
|
@ -220,37 +213,16 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) {
|
|||
_shape_strides.increment = 1;
|
||||
}
|
||||
|
||||
if(_shape_strides.ndim == 0) {
|
||||
_shape_strides.ndim = 1;
|
||||
_shape_strides.shape[ULAB_MAX_DIMS - 1] = 1;
|
||||
_shape_strides.strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
|
||||
}
|
||||
|
||||
return _shape_strides;
|
||||
}
|
||||
|
||||
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t *ndarray, ndarray_obj_t *results, mp_obj_t keepdims, shape_strides _shape_strides) {
|
||||
// restores the contracted dimension, if keepdims is True
|
||||
if((ndarray->ndim == 1) && (keepdims != mp_const_true)) {
|
||||
// since the original array has already been contracted and
|
||||
// we don't want to keep the dimensions here, we have to return a scalar
|
||||
return mp_binary_get_val_array(results->dtype, results->array, 0);
|
||||
int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) {
|
||||
int8_t ax = mp_obj_get_int(axis);
|
||||
if(ax < 0) ax += ndim;
|
||||
if((ax < 0) || (ax > ndim - 1)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds"));
|
||||
}
|
||||
|
||||
if(keepdims == mp_const_true) {
|
||||
results->ndim += 1;
|
||||
for(int8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
results->shape[i] = ndarray->shape[i];
|
||||
}
|
||||
results->shape[_shape_strides.axis] = 1;
|
||||
|
||||
results->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
|
||||
for(uint8_t i = ULAB_MAX_DIMS; i > 1; i--) {
|
||||
results->strides[i - 2] = results->strides[i - 1] * results->shape[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
return ax;
|
||||
}
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
|
|
@ -302,31 +274,3 @@ bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t out, uint8_t dtype, uint8_t ndim, size_t *shape, bool dense_only) {
|
||||
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out);
|
||||
|
||||
if(ndarray->dtype != dtype) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dtype"));
|
||||
}
|
||||
|
||||
if(ndarray->ndim != ndim) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dimension"));
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
if(ndarray->shape[i] != shape[i]) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong shape"));
|
||||
}
|
||||
}
|
||||
|
||||
if(dense_only) {
|
||||
if(!ndarray_is_dense(ndarray)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous"));
|
||||
}
|
||||
}
|
||||
return ndarray;
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
typedef struct _shape_strides_t {
|
||||
uint8_t increment;
|
||||
uint8_t axis;
|
||||
uint8_t ndim;
|
||||
size_t *shape;
|
||||
int32_t *strides;
|
||||
|
|
@ -35,7 +34,6 @@ void *ndarray_set_float_function(uint8_t );
|
|||
|
||||
shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t );
|
||||
int8_t tools_get_axis(mp_obj_t , uint8_t );
|
||||
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t * , ndarray_obj_t * , mp_obj_t , shape_strides );
|
||||
ndarray_obj_t *tools_object_is_square(mp_obj_t );
|
||||
|
||||
uint8_t ulab_binary_get_size(uint8_t );
|
||||
|
|
@ -46,6 +44,7 @@ void ulab_rescale_float_strides(int32_t *);
|
|||
|
||||
bool ulab_tools_mp_obj_is_scalar(mp_obj_t );
|
||||
|
||||
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t , uint8_t , uint8_t , size_t *, bool );
|
||||
|
||||
#if ULAB_NUMPY_HAS_RANDOM_MODULE
|
||||
ndarray_obj_t *ulab_tools_create_out(mp_obj_tuple_t , mp_obj_t , uint8_t , bool );
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2024 Zoltán Vörös
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
#include "py/misc.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "../ulab_tools.h"
|
||||
#include "../numpy/fft/fft_tools.h"
|
||||
|
||||
#if ULAB_HAS_UTILS_MODULE
|
||||
|
|
@ -204,180 +203,23 @@ MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_bu
|
|||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE }} ,
|
||||
#if !ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
#endif
|
||||
{ MP_QSTR_scratchpad, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_log, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(in->ndim != 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t len = in->len;
|
||||
// Check if input is of length of power of 2
|
||||
if((len & (len-1)) != 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *out = NULL;
|
||||
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
mp_obj_t scratchpad_object = args[1].u_obj;
|
||||
mp_obj_t out_object = args[2].u_obj;
|
||||
mp_obj_t log_object = args[3].u_obj;
|
||||
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *args) {
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
return fft_fft_ifft_spectrogram(args[0], FFT_SPECTROGRAM);
|
||||
#else
|
||||
mp_obj_t scratchpad_object = args[2].u_obj;
|
||||
mp_obj_t out_object = args[3].u_obj;
|
||||
mp_obj_t log_object = args[4].u_obj;
|
||||
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(out_object != mp_const_none) {
|
||||
if(!mp_obj_is_type(out_object, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out must be an ndarray"));
|
||||
}
|
||||
|
||||
out = MP_OBJ_TO_PTR(out_object);
|
||||
if((out->dtype != NDARRAY_FLOAT) || (out->ndim != 1)){
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out array must be a 1D array of float type"));
|
||||
}
|
||||
if(len != out->len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input and out arrays must have same length"));
|
||||
}
|
||||
} else {
|
||||
out = ndarray_new_linear_array(len, NDARRAY_FLOAT);
|
||||
}
|
||||
|
||||
ndarray_obj_t *scratchpad = NULL;
|
||||
mp_float_t *tmp = NULL;
|
||||
|
||||
if(scratchpad_object != mp_const_none) {
|
||||
if(!mp_obj_is_type(scratchpad_object, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("scratchpad must be an ndarray"));
|
||||
}
|
||||
|
||||
scratchpad = MP_OBJ_TO_PTR(scratchpad_object);
|
||||
if(!ndarray_is_dense(scratchpad) || (scratchpad->ndim != 1) || (scratchpad->dtype != NDARRAY_FLOAT)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be a 1D dense float array"));
|
||||
}
|
||||
if(scratchpad->len != 2 * len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be twice as long as input"));
|
||||
}
|
||||
|
||||
tmp = (mp_float_t *)scratchpad->array;
|
||||
} else {
|
||||
tmp = m_new0(mp_float_t, 2 * len);
|
||||
}
|
||||
|
||||
uint8_t *array = (uint8_t *)in->array;
|
||||
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE & ULAB_SUPPORTS_COMPLEX
|
||||
if(in->dtype == NDARRAY_COMPLEX) {
|
||||
uint8_t sz = 2 * sizeof(mp_float_t);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
memcpy(tmp, array, sz);
|
||||
tmp += 2;
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
} else {
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*tmp++ = func(array); // real part
|
||||
*tmp++ = 0; // imaginary part, clear
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
}
|
||||
|
||||
tmp -= 2 * len;
|
||||
fft_kernel(tmp, len, 1);
|
||||
#else // we might have two real input vectors
|
||||
|
||||
ndarray_obj_t *in2 = NULL;
|
||||
|
||||
if(n_args == 2) {
|
||||
if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
|
||||
}
|
||||
in2 = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(in2->ndim != 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
|
||||
}
|
||||
#endif
|
||||
if(len != in2->len) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input arrays are not compatible"));
|
||||
}
|
||||
}
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
|
||||
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*tmp++ = func(array); // real part; imageinary will be cleared later
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
if(n_args == 2) {
|
||||
mp_float_t (*func2)(void *) = ndarray_get_float_function(in2->dtype);
|
||||
array = (uint8_t *)in2->array;
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*tmp++ = func2(array);
|
||||
array += in2->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
tmp -= len;
|
||||
} else {
|
||||
// if there is only one input argument, clear the imaginary part
|
||||
memset(tmp, 0, len * sizeof(mp_float_t));
|
||||
}
|
||||
|
||||
tmp -= len;
|
||||
|
||||
fft_kernel(tmp, tmp + len, len, 1);
|
||||
#endif /* ULAB_FFT_IS_NUMPY_COMPATIBLE */
|
||||
|
||||
mp_float_t *spectrum = (mp_float_t *)out->array;
|
||||
uint8_t spectrum_sz = out->strides[ULAB_MAX_DIMS - 1] / sizeof(mp_float_t);
|
||||
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
*spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + 1) * *(tmp + 1));
|
||||
tmp += 2;
|
||||
#else
|
||||
*spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + len) * *(tmp + len));
|
||||
tmp++;
|
||||
#endif
|
||||
if(log_object == mp_const_true) {
|
||||
*spectrum = MICROPY_FLOAT_C_FUN(log)(*spectrum);
|
||||
}
|
||||
spectrum += spectrum_sz;
|
||||
}
|
||||
|
||||
if(scratchpad_object == mp_const_none) {
|
||||
tmp -= len;
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
tmp -= len;
|
||||
#endif
|
||||
m_del(mp_float_t, tmp, 2 * len);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(utils_spectrogram_obj, 1, utils_spectrogram);
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 1, utils_spectrogram);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 2, utils_spectrogram);
|
||||
#endif
|
||||
|
||||
#endif /* ULAB_UTILS_HAS_SPECTROGRAM */
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ from sphinx import addnodes
|
|||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'The ulab book'
|
||||
copyright = '2019-2025, Zoltán Vörös and contributors'
|
||||
copyright = '2019-2024, Zoltán Vörös and contributors'
|
||||
author = 'Zoltán Vörös'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '6.9.0'
|
||||
release = '6.5.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ Welcome to the ulab book!
|
|||
numpy-fft
|
||||
numpy-linalg
|
||||
numpy-random
|
||||
scipy-integrate
|
||||
scipy-linalg
|
||||
scipy-optimize
|
||||
scipy-signal
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ Numpy functions
|
|||
===============
|
||||
|
||||
This section of the manual discusses those functions that were adapted
|
||||
from ``numpy``. Functions with an asterisk accept complex arrays as
|
||||
arguments, if the firmware was compiled with complex support.
|
||||
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>`__
|
||||
|
|
@ -51,10 +51,9 @@ arguments, if the firmware was compiled with complex support.
|
|||
43. `numpy.sort_complex\* <#sort_complex>`__
|
||||
44. `numpy.std <#std>`__
|
||||
45. `numpy.sum <#sum>`__
|
||||
46. `numpy.take\* <#take>`__
|
||||
47. `numpy.trace <#trace>`__
|
||||
48. `numpy.trapz <#trapz>`__
|
||||
49. `numpy.where <#where>`__
|
||||
46. `numpy.trace <#trace>`__
|
||||
47. `numpy.trapz <#trapz>`__
|
||||
48. `numpy.where <#where>`__
|
||||
|
||||
all
|
||||
---
|
||||
|
|
@ -1986,66 +1985,6 @@ array. Otherwise, the calculation is along the given axis.
|
|||
|
||||
|
||||
|
||||
take
|
||||
----
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/generated/numpy.take.html
|
||||
|
||||
The ``take`` method takes elements from an array along an axis. The
|
||||
function accepts two positional arguments, the array, and the indices,
|
||||
which is either a ``python`` iterable, or a one-dimensional ``ndarray``,
|
||||
as well as three keyword arguments, the ``axis``, which can be ``None``,
|
||||
or an integer, ``out``, which can be ``None``, or an ``ndarray`` with
|
||||
the proper dimensions, and ``mode``, which can be one of the strings
|
||||
``raise``, ``wrap``, or ``clip``. This last argument determines how
|
||||
out-of-bounds indices will be treated. The default value is ``raise``,
|
||||
which raises an exception. ``wrap`` takes the indices modulo the length
|
||||
of the ``axis``, while ``clip`` pegs the values at the 0, and the length
|
||||
of the ``axis``. If ``axis`` is ``None``, then ``take`` operates on the
|
||||
flattened array.
|
||||
|
||||
The function can be regarded as a method of advanced slicing: as opposed
|
||||
to standard slicing, where the indices are distributed uniformly and in
|
||||
either increasing or decreasing order, ``take`` can take indices in an
|
||||
arbitrary order.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array(range(12)).reshape((3, 4))
|
||||
print('\na:', a)
|
||||
|
||||
print('\nslices taken along first axis')
|
||||
print(np.take(a, (0, 2, 2, 1), axis=0))
|
||||
|
||||
print('\nslices taken along second axis')
|
||||
print(np.take(a, (0, 2, 2, 1), axis=1))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|
||||
a: array([[0.0, 1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0, 7.0],
|
||||
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
|
||||
|
||||
slices taken along first axis
|
||||
array([[0.0, 1.0, 2.0, 3.0],
|
||||
[8.0, 9.0, 10.0, 11.0],
|
||||
[8.0, 9.0, 10.0, 11.0],
|
||||
[4.0, 5.0, 6.0, 7.0]], dtype=float64)
|
||||
|
||||
slices taken along second axis
|
||||
array([[0.0, 2.0, 2.0, 1.0],
|
||||
[2.0, 3.0, 4.0, 5.0],
|
||||
[6.0, 7.0, 8.0, 9.0]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
trace
|
||||
-----
|
||||
|
||||
|
|
|
|||
|
|
@ -1,220 +0,0 @@
|
|||
|
||||
scipy.integrate
|
||||
===============
|
||||
|
||||
This module provides a simplified subset of CPython’s
|
||||
``scipy.integrate`` module. The algorithms were not ported from
|
||||
CPython’s ``scipy.integrate`` for the sake of resource usage, but
|
||||
derived from a paper found in https://www.genivia.com/qthsh.html. There
|
||||
are four numerical integration algorithms:
|
||||
|
||||
1. `scipy.integrate.quad <#quad>`__
|
||||
2. `scipy.integrate.romberg <#romberg>`__
|
||||
3. `scipy.integrate.simpson <#simpson>`__
|
||||
4. `scipy.integrate.tanhsinh <#tanhsinh>`__
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Numerical integration works best with float64 math enabled. If you
|
||||
require float64 math, be sure to set ``MICROPY_OBJ_REPR_A`` and
|
||||
``MICROPY_FLOAT_IMPL_DOUBLE``. This being said, the modules work equally
|
||||
well using float32, albeit with reduced precision. The required error
|
||||
tolerance can be specified for each of the function calls using the
|
||||
“eps=” option, defaulting to the compiled in ``etolerance`` value (1e-14
|
||||
for fp64, 1e-8 for fp32).
|
||||
|
||||
The submodule can be enabled by setting
|
||||
``ULAB_SCIPY_HAS_INTEGRATE_MODULE`` in ``code/ulab.h``. As for the
|
||||
individual integration algorithms, you can select which to include by
|
||||
setting one or more of ``ULAB_INTEGRATE_HAS_QUAD``,
|
||||
``ULAB_INTEGRATE_HAS_ROMBERG``, ``ULAB_INTEGRATE_HAS_SIMPSON``, and
|
||||
``ULAB_INTEGRATE_HAS_TANHSINH``.
|
||||
|
||||
Also note that these algorithms do not support complex numbers, although
|
||||
it is certainly possible to implement complex integration in MicroPython
|
||||
on top of this module, e.g. as in
|
||||
https://stackoverflow.com/questions/5965583/use-scipy-integrate-quad-to-integrate-complex-numbers.
|
||||
|
||||
quad
|
||||
----
|
||||
|
||||
``scipy``:
|
||||
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html
|
||||
|
||||
In CPython ``scipy.integrate``, ``quad`` is a wrapper implementing many
|
||||
algorithms based on the Fortran QUADPACK package. Gauss-Kronrod is just
|
||||
one of them, and it is useful for most general-purpose tasks. This
|
||||
particular function implements an Adaptive Gauss-Kronrod (G10,K21)
|
||||
quadrature algorithm. The Gauss–Kronrod quadrature formula is a variant
|
||||
of Gaussian quadrature, in which the evaluation points are chosen so
|
||||
that an accurate approximation can be computed by re-using the
|
||||
information produced by the computation of a less accurate approximation
|
||||
(https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula).
|
||||
|
||||
The function takes three to five arguments:
|
||||
|
||||
- f, a callable,
|
||||
- a and b, the lower and upper integration limit,
|
||||
- order=, the order of integration (default 5),
|
||||
- eps=, the error tolerance (default etolerance)
|
||||
|
||||
The function returns the result and the error estimate as a tuple of
|
||||
floats.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import scipy
|
||||
|
||||
f = lambda x: x**2 + 2*x + 1
|
||||
result = scipy.integrate.quad(f, 0, 5, order=5, eps=1e-10)
|
||||
print (f"result = {result}")
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
UsageError: Cell magic `%%micropython` not found.
|
||||
|
||||
|
||||
romberg
|
||||
-------
|
||||
|
||||
``scipy``:
|
||||
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html
|
||||
|
||||
This function implements the Romberg quadrature algorithm. Romberg’s
|
||||
method is a Newton–Cotes formula – it evaluates the integrand at equally
|
||||
spaced points. The integrand must have continuous derivatives, though
|
||||
fairly good results may be obtained if only a few derivatives exist. If
|
||||
it is possible to evaluate the integrand at unequally spaced points,
|
||||
then other methods such as Gaussian quadrature and Clenshaw–Curtis
|
||||
quadrature are generally more accurate
|
||||
(https://en.wikipedia.org/wiki/Romberg%27s_method).
|
||||
|
||||
Please note: This function is deprecated as of SciPy 1.12.0 and will be
|
||||
removed in SciPy 1.15.0. Please use ``scipy.integrate.quad`` instead.
|
||||
|
||||
The function takes three to five arguments:
|
||||
|
||||
- f, a callable,
|
||||
- a and b, the lower and upper integration limit,
|
||||
- steps=, the number of steps taken to calculate (default 100),
|
||||
- eps=, the error tolerance (default etolerance)
|
||||
|
||||
The function returns the result as a float.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import scipy
|
||||
|
||||
f = lambda x: x**2 + 2*x + 1
|
||||
result = scipy.integrate.romberg(f, 0, 5)
|
||||
print (f"result = {result}")
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
UsageError: Cell magic `%%micropython` not found.
|
||||
|
||||
|
||||
simpson
|
||||
-------
|
||||
|
||||
``scipy``:
|
||||
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.simpson.html
|
||||
|
||||
This function is different from CPython’s ``simpson`` method in that it
|
||||
does not take an array of function values but determines the optimal
|
||||
spacing of samples itself. Adaptive Simpson’s method, also called
|
||||
adaptive Simpson’s rule, is a method of numerical integration proposed
|
||||
by G.F. Kuncir in 1962. It is probably the first recursive adaptive
|
||||
algorithm for numerical integration to appear in print, although more
|
||||
modern adaptive methods based on Gauss–Kronrod quadrature and
|
||||
Clenshaw–Curtis quadrature are now generally preferred
|
||||
(https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method).
|
||||
|
||||
The function takes three to five arguments:
|
||||
|
||||
- f, a callable,
|
||||
- a and b, the lower and upper integration limit,
|
||||
- steps=, the number of steps taken to calculate (default 100),
|
||||
- eps=, the error tolerance (default etolerance)
|
||||
|
||||
The function returns the result as a float.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import scipy
|
||||
|
||||
f = lambda x: x**2 + 2*x + 1
|
||||
result = scipy.integrate.simpson(f, 0, 5)
|
||||
print (f"result = {result}")
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
UsageError: Cell magic `%%micropython` not found.
|
||||
|
||||
|
||||
tanhsinh
|
||||
--------
|
||||
|
||||
``scipy``:
|
||||
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html
|
||||
|
||||
In CPython ``scipy.integrate``, ``tanhsinh`` is written in Python
|
||||
(https://github.com/scipy/scipy/blob/main/scipy/integrate/\_tanhsinh.py).
|
||||
It is used in cases where Newton-Cotes, Gauss-Kronrod, and other
|
||||
formulae do not work due to properties of the integrand or the
|
||||
integration limits. (In SciPy v1.14.1, it is not a public function but
|
||||
it has been marked as public in SciPy v1.15.0rc1).
|
||||
|
||||
This particular function implements an optimized Tanh-Sinh, Sinh-Sinh
|
||||
and Exp-Sinh quadrature algorithm. It is especially applied where
|
||||
singularities or infinite derivatives exist at one or both endpoints.
|
||||
The method uses hyperbolic functions in a change of variables to
|
||||
transform an integral on the interval x ∈ (−1, 1) to an integral on the
|
||||
entire real line t ∈ (−∞, ∞), the two integrals having the same value.
|
||||
After this transformation, the integrand decays with a double
|
||||
exponential rate, and thus, this method is also known as the double
|
||||
exponential (DE) formula
|
||||
(https://en.wikipedia.org/wiki/Tanh-sinh_quadrature).
|
||||
|
||||
As opposed to the three algorithms mentioned before, it also supports
|
||||
integrals with infinite limits like the Gaussian integral
|
||||
(https://en.wikipedia.org/wiki/Gaussian_integral), as shown below.
|
||||
|
||||
The function takes three to five arguments:
|
||||
|
||||
- f, a callable,
|
||||
- a and b, the lower and upper integration limit,
|
||||
- levels=, the number of loops taken to calculate (default 6),
|
||||
- eps=, the error tolerance (default: etolerance)
|
||||
|
||||
The function returns the result and the error estimate as a tuple of
|
||||
floats.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import scipy, numpy as np
|
||||
from math import *
|
||||
f = lambda x: exp(- x**2)
|
||||
result = scipy.integrate.tanhsinh(f, -np.inf, np.inf)
|
||||
print (f"result = {result}")
|
||||
exact = sqrt(pi) # which is the exact value
|
||||
print (f"exact value = {exact}")
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
UsageError: Cell magic `%%micropython` not found.
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in CPython
|
||||
|
||||
|
|
@ -8,10 +8,9 @@ Enter ulab
|
|||
``ulab`` is a ``numpy``-like module for ``micropython`` and its
|
||||
derivatives, meant to simplify and speed up common mathematical
|
||||
operations on arrays. ``ulab`` implements a small subset of ``numpy``
|
||||
and ``scipy``, as well as a number of functions manipulating byte
|
||||
arrays. The functions were chosen such that they might be useful in the
|
||||
context of a microcontroller. However, the project is a living one, and
|
||||
suggestions for new features are always welcome.
|
||||
and ``scipy``. The functions were chosen such that they might be useful
|
||||
in the context of a microcontroller. However, the project is a living
|
||||
one, and suggestions for new features are always welcome.
|
||||
|
||||
This document discusses how you can use the library, starting from
|
||||
building your own firmware, through questions like what affects the
|
||||
|
|
@ -266,9 +265,9 @@ functions that are part of ``numpy``, you have to import ``numpy`` as
|
|||
p = np.array([1, 2, 3])
|
||||
np.polyval(p, x)
|
||||
|
||||
There are a couple of exceptions to this rule, namely ``fft``,
|
||||
``linalg``, and ``random``, which are sub-modules even in ``numpy``,
|
||||
thus you have to write them out as
|
||||
There are a couple of exceptions to this rule, namely ``fft``, and
|
||||
``linalg``, which are sub-modules even in ``numpy``, thus you have to
|
||||
write them out as
|
||||
|
||||
.. code:: python
|
||||
|
||||
|
|
|
|||
|
|
@ -1814,12 +1814,12 @@ array.
|
|||
Binary operators
|
||||
================
|
||||
|
||||
``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``%``,
|
||||
``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``,
|
||||
``/=``, ``**=``, ``%=`` binary operators, as well as the ``AND``,
|
||||
``OR``, ``XOR`` bit-wise operators that work element-wise. Note that the
|
||||
bit-wise operators will raise an exception, if either of the operands is
|
||||
of ``float`` or ``complex`` type.
|
||||
``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``<``,
|
||||
``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, ``/=``,
|
||||
``**=`` binary operators, as well as the ``AND``, ``OR``, ``XOR``
|
||||
bit-wise operators that work element-wise. Note that the bit-wise
|
||||
operators will raise an exception, if either of the operands is of
|
||||
``float`` or ``complex`` type.
|
||||
|
||||
Broadcasting is available, meaning that the two operands do not even
|
||||
have to have the same shape. If the lengths along the respective axes
|
||||
|
|
@ -2330,12 +2330,12 @@ future version of ``ulab``.
|
|||
|
||||
a = np.array(range(9), dtype=np.float)
|
||||
print("a:\t", a)
|
||||
print("a[a < 5]:\t", a[a < 5])
|
||||
print("a < 5:\t", a[a < 5])
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a: array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float)
|
||||
a[a < 5]: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
|
||||
a < 5: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ the ``user`` module:
|
|||
|
||||
.. code:: c
|
||||
|
||||
static const mp_rom_map_elem_t ulab_user_globals_table[] = {
|
||||
STATIC const mp_rom_map_elem_t ulab_user_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,9 +52,7 @@ Here is an example without keyword arguments
|
|||
a = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
|
||||
print('a: ', a)
|
||||
print()
|
||||
print('unsigned integers: ', utils.from_uint32_buffe
|
||||
print('original vector:\n', y)
|
||||
print('\nspectrum:\n', a)r(a))
|
||||
print('unsigned integers: ', utils.from_uint32_buffer(a))
|
||||
|
||||
b = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
|
||||
print('\nb: ', b)
|
||||
|
|
@ -146,53 +144,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, also known as the power spectrum. This
|
||||
could be used to find the dominant spectral component in a time series.
|
||||
The positional arguments are treated in the same way as in ``fft``, and
|
||||
``ifft``. This means that, if the firmware was compiled with complex
|
||||
support and ``ULAB_FFT_IS_NUMPY_COMPATIBLE`` is defined to be 1 in
|
||||
``ulab.h``, the input can also be a complex array.
|
||||
|
||||
And easy way to find out if the FFT is ``numpy``-compatible is to check
|
||||
the number of values ``fft.fft`` returns, when called with a single real
|
||||
argument of length other than 2:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
if len(np.fft.fft(np.zeros(4))) == 2:
|
||||
print('FFT is NOT numpy compatible (real and imaginary parts are treated separately)')
|
||||
else:
|
||||
print('FFT is numpy compatible (complex inputs/outputs)')
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
FFT is numpy compatible (complex inputs/outputs)
|
||||
|
||||
|
||||
|
||||
|
||||
Depending on the ``numpy``-compatibility of the FFT, the ``spectrogram``
|
||||
function takes one or two positional arguments, and three keyword
|
||||
arguments. If the FFT is ``numpy`` compatible, one positional argument
|
||||
is allowed, and it is a 1D real or complex ``ndarray``. If the FFT is
|
||||
not ``numpy``-compatible, if a single argument is supplied, it will be
|
||||
treated as the real part of the input, and if two positional arguments
|
||||
are supplied, they are treated as the real and imaginary parts of the
|
||||
signal.
|
||||
|
||||
The keyword arguments are as follows:
|
||||
|
||||
1. ``scratchpad = None``: must be a 1D, dense, floating point array,
|
||||
twice as long as the input array; the ``scratchpad`` will be used as
|
||||
a temporary internal buffer to perform the Fourier transform; the
|
||||
``scratchpad`` can repeatedly be re-used.
|
||||
2. ``out = None``: must be a 1D, not necessarily dense, floating point
|
||||
array that will store the results
|
||||
3. ``log = False``: must be either ``True``, or ``False``; if ``True``,
|
||||
the ``spectrogram`` returns the logarithm of the absolute values of
|
||||
the Fourier transform.
|
||||
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.
|
||||
|
||||
.. code::
|
||||
|
||||
|
|
@ -215,24 +169,17 @@ The keyword arguments are as follows:
|
|||
array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64)
|
||||
|
||||
spectrum:
|
||||
array([187.8635087634578, 315.3112063607119, 347.8814873399375, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
|
||||
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
As such, ``spectrogram`` is really just a shorthand for
|
||||
``np.abs(np.fft.fft(signal))``, if the FFT is ``numpy``-compatible, or
|
||||
``np.sqrt(a*a + b*b)`` if the FFT returns the real (``a``) and imaginary
|
||||
(``b``) parts separately. However, ``spectrogram`` saves significant
|
||||
amounts of RAM: the expression ``a*a + b*b`` has to allocate memory for
|
||||
``a*a``, ``b*b``, and finally, their sum. Similarly, ``np.abs`` returns
|
||||
a new array. This issue is compounded even more, if ``np.log()`` is used
|
||||
on the absolute value.
|
||||
|
||||
In contrast, ``spectrogram`` handles all calculations in the same
|
||||
internal arrays, and allows one to re-use previously reserved RAM. This
|
||||
can be especially useful in cases, when ``spectogram`` is called
|
||||
repeatedly, as in the snippet below.
|
||||
``np.sqrt(a*a + b*b)``, however, it saves significant amounts of RAM:
|
||||
the expression ``a*a + b*b`` has to allocate memory for ``a*a``,
|
||||
``b*b``, and finally, their sum. In contrast, ``spectrogram`` calculates
|
||||
the spectrum internally, and stores it in the memory segment that was
|
||||
reserved for the real part of the Fourier transform.
|
||||
|
||||
.. code::
|
||||
|
||||
|
|
@ -241,34 +188,25 @@ repeatedly, as in the snippet below.
|
|||
from ulab import numpy as np
|
||||
from ulab import utils as utils
|
||||
|
||||
n = 1024
|
||||
t = np.linspace(0, 2 * np.pi, num=1024)
|
||||
scratchpad = np.zeros(2 * n)
|
||||
x = np.linspace(0, 10, num=1024)
|
||||
y = np.sin(x)
|
||||
|
||||
for _ in range(10):
|
||||
signal = np.sin(t)
|
||||
utils.spectrogram(signal, out=signal, scratchpad=scratchpad, log=True)
|
||||
a, b = np.fft.fft(y)
|
||||
|
||||
print('signal: ', signal)
|
||||
print('\nspectrum calculated the hard way:\n', np.sqrt(a*a + b*b))
|
||||
|
||||
for _ in range(10):
|
||||
signal = np.sin(t)
|
||||
out = np.log(utils.spectrogram(signal))
|
||||
a = utils.spectrogram(y)
|
||||
|
||||
print('out: ', out)
|
||||
print('\nspectrum calculated the lazy way:\n', a)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
signal: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)
|
||||
out: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)
|
||||
|
||||
spectrum calculated the hard way:
|
||||
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
|
||||
|
||||
spectrum calculated the lazy way:
|
||||
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
Note that ``scratchpad`` is reserved only once, and then is re-used in
|
||||
the first loop. By assigning ``signal`` to the output, we save
|
||||
additional RAM. This approach avoids the usual problem of memory
|
||||
fragmentation, which would happen in the second loop, where both
|
||||
``spectrogram``, and ``np.log`` must reserve RAM in each iteration.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-02-01T17:37:25.505687Z",
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-02-01T17:37:25.717714Z",
|
||||
|
|
@ -230,7 +230,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This section of the manual discusses those functions that were adapted from `numpy`. Functions with an asterisk accept complex arrays as arguments, if the firmware was compiled with complex support.\n",
|
||||
"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",
|
||||
|
|
@ -277,7 +277,6 @@
|
|||
"1. [numpy.sort_complex*](#sort_complex)\n",
|
||||
"1. [numpy.std](#std)\n",
|
||||
"1. [numpy.sum](#sum)\n",
|
||||
"1. [numpy.take*](#take)\n",
|
||||
"1. [numpy.trace](#trace)\n",
|
||||
"1. [numpy.trapz](#trapz)\n",
|
||||
"1. [numpy.where](#where)"
|
||||
|
|
@ -2683,63 +2682,6 @@
|
|||
"print('std, vertical: ', np.sum(a, axis=0))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## take\n",
|
||||
"\n",
|
||||
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.take.html\n",
|
||||
"\n",
|
||||
"The `take` method takes elements from an array along an axis. The function accepts two positional arguments, the array, and the indices, which is either a `python` iterable, or a one-dimensional `ndarray`, as well as three keyword arguments, the `axis`, which can be `None`, or an integer, `out`, which can be `None`, or an `ndarray` with the proper dimensions, and `mode`, which can be one of the strings `raise`, `wrap`, or `clip`. This last argument determines how out-of-bounds indices will be treated. The default value is `raise`, which raises an exception. `wrap` takes the indices modulo the length of the `axis`, while `clip` pegs the values at the 0, and the length of the `axis`. If `axis` is `None`, then `take` operates on the flattened array.\n",
|
||||
"\n",
|
||||
"The function can be regarded as a method of advanced slicing: as opposed to standard slicing, where the indices are distributed uniformly and in either increasing or decreasing order, `take` can take indices in an arbitrary order."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"a: array([[0.0, 1.0, 2.0, 3.0],\n",
|
||||
" [4.0, 5.0, 6.0, 7.0],\n",
|
||||
" [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"slices taken along first axis\n",
|
||||
"array([[0.0, 1.0, 2.0, 3.0],\n",
|
||||
" [8.0, 9.0, 10.0, 11.0],\n",
|
||||
" [8.0, 9.0, 10.0, 11.0],\n",
|
||||
" [4.0, 5.0, 6.0, 7.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"slices taken along second axis\n",
|
||||
"array([[0.0, 2.0, 2.0, 1.0],\n",
|
||||
" [2.0, 3.0, 4.0, 5.0],\n",
|
||||
" [6.0, 7.0, 8.0, 9.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"\n",
|
||||
"a = np.array(range(12)).reshape((3, 4))\n",
|
||||
"print('\\na:', a)\n",
|
||||
"\n",
|
||||
"print('\\nslices taken along first axis')\n",
|
||||
"print(np.take(a, (0, 2, 2, 1), axis=0))\n",
|
||||
"\n",
|
||||
"print('\\nslices taken along second axis')\n",
|
||||
"print(np.take(a, (0, 2, 2, 1), axis=1))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
|
|
@ -2958,7 +2900,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.7"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
|
|
|
|||
|
|
@ -1,510 +0,0 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2021-01-12T16:11:12.111639Z",
|
||||
"start_time": "2021-01-12T16:11:11.914041Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Populating the interactive namespace from numpy and matplotlib\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pylab inline"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Notebook magic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-29T20:50:20.813162Z",
|
||||
"start_time": "2022-01-29T20:50:20.794562Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from IPython.core.magic import Magics, magics_class, line_cell_magic\n",
|
||||
"from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n",
|
||||
"from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n",
|
||||
"import subprocess\n",
|
||||
"import os"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-29T20:50:21.613220Z",
|
||||
"start_time": "2022-01-29T20:50:21.557819Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@magics_class\n",
|
||||
"class PyboardMagic(Magics):\n",
|
||||
" @cell_magic\n",
|
||||
" @magic_arguments()\n",
|
||||
" @argument('-skip')\n",
|
||||
" @argument('-unix')\n",
|
||||
" @argument('-pyboard')\n",
|
||||
" @argument('-file')\n",
|
||||
" @argument('-data')\n",
|
||||
" @argument('-time')\n",
|
||||
" @argument('-memory')\n",
|
||||
" def micropython(self, line='', cell=None):\n",
|
||||
" args = parse_argstring(self.micropython, line)\n",
|
||||
" if args.skip: # doesn't care about the cell's content\n",
|
||||
" print('skipped execution')\n",
|
||||
" return None # do not parse the rest\n",
|
||||
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
|
||||
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
|
||||
" fout.write(cell)\n",
|
||||
" proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n",
|
||||
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
|
||||
" print(proc.stdout.read().decode(\"utf-8\"))\n",
|
||||
" print(proc.stderr.read().decode(\"utf-8\"))\n",
|
||||
" return None\n",
|
||||
" if args.file: # can be used to copy the cell content onto the pyboard's flash\n",
|
||||
" spaces = \" \"\n",
|
||||
" try:\n",
|
||||
" with open(args.file, 'w') as fout:\n",
|
||||
" fout.write(cell.replace('\\t', spaces))\n",
|
||||
" printf('written cell to {}'.format(args.file))\n",
|
||||
" except:\n",
|
||||
" print('Failed to write to disc!')\n",
|
||||
" return None # do not parse the rest\n",
|
||||
" if args.data: # can be used to load data from the pyboard directly into kernel space\n",
|
||||
" message = pyb.exec(cell)\n",
|
||||
" if len(message) == 0:\n",
|
||||
" print('pyboard >>>')\n",
|
||||
" else:\n",
|
||||
" print(message.decode('utf-8'))\n",
|
||||
" # register new variable in user namespace\n",
|
||||
" self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n",
|
||||
" \n",
|
||||
" if args.time: # measures the time of executions\n",
|
||||
" pyb.exec('import utime')\n",
|
||||
" message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n",
|
||||
" \"\\nprint('execution time: {:d} us'.format(delta))\")\n",
|
||||
" print(message.decode('utf-8'))\n",
|
||||
" \n",
|
||||
" if args.memory: # prints out memory information \n",
|
||||
" message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n",
|
||||
" print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n",
|
||||
" message = pyb.exec(cell)\n",
|
||||
" print(\">>> \", message.decode('utf-8'))\n",
|
||||
" message = pyb.exec('print(mem_info())')\n",
|
||||
" print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n",
|
||||
"\n",
|
||||
" if args.pyboard:\n",
|
||||
" message = pyb.exec(cell)\n",
|
||||
" print(message.decode('utf-8'))\n",
|
||||
"\n",
|
||||
"ip = get_ipython()\n",
|
||||
"ip.register_magics(PyboardMagic)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## pyboard"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 57,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-05-07T07:35:35.126401Z",
|
||||
"start_time": "2020-05-07T07:35:35.105824Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pyboard\n",
|
||||
"pyb = pyboard.Pyboard('/dev/ttyACM0')\n",
|
||||
"pyb.enter_raw_repl()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-05-19T19:11:18.145548Z",
|
||||
"start_time": "2020-05-19T19:11:18.137468Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pyb.exit_raw_repl()\n",
|
||||
"pyb.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 58,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-05-07T07:35:38.725924Z",
|
||||
"start_time": "2020-05-07T07:35:38.645488Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -pyboard 1\n",
|
||||
"\n",
|
||||
"import utime\n",
|
||||
"import ulab as np\n",
|
||||
"\n",
|
||||
"def timeit(n=1000):\n",
|
||||
" def wrapper(f, *args, **kwargs):\n",
|
||||
" func_name = str(f).split(' ')[1]\n",
|
||||
" def new_func(*args, **kwargs):\n",
|
||||
" run_times = np.zeros(n, dtype=np.uint16)\n",
|
||||
" for i in range(n):\n",
|
||||
" t = utime.ticks_us()\n",
|
||||
" result = f(*args, **kwargs)\n",
|
||||
" run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n",
|
||||
" print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n",
|
||||
" print('\\tbest: %d us'%np.min(run_times))\n",
|
||||
" print('\\tworst: %d us'%np.max(run_times))\n",
|
||||
" print('\\taverage: %d us'%np.mean(run_times))\n",
|
||||
" print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n",
|
||||
" return result\n",
|
||||
" return new_func\n",
|
||||
" return wrapper\n",
|
||||
"\n",
|
||||
"def timeit(f, *args, **kwargs):\n",
|
||||
" func_name = str(f).split(' ')[1]\n",
|
||||
" def new_func(*args, **kwargs):\n",
|
||||
" t = utime.ticks_us()\n",
|
||||
" result = f(*args, **kwargs)\n",
|
||||
" print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n",
|
||||
" return result\n",
|
||||
" return new_func"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"__END_OF_DEFS__"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# scipy.integrate\n",
|
||||
"\n",
|
||||
"This module provides a simplified subset of CPython's `scipy.integrate` module. The algorithms were not ported from CPython's `scipy.integrate` for the sake of resource usage, but derived from a paper found in https://www.genivia.com/qthsh.html. There are four numerical integration algorithms:\n",
|
||||
"\n",
|
||||
"1. [scipy.integrate.quad](#quad)\n",
|
||||
"2. [scipy.integrate.romberg](#romberg)\n",
|
||||
"3. [scipy.integrate.simpson](#simpson)\n",
|
||||
"4. [scipy.integrate.tanhsinh](#tanhsinh)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Introduction\n",
|
||||
"\n",
|
||||
"Numerical integration works best with float64 math enabled. If you require float64 math, be sure to set `MICROPY_OBJ_REPR_A` and `MICROPY_FLOAT_IMPL_DOUBLE`. This being said, the modules work equally well using float32, albeit with reduced precision. The required error tolerance can be specified for each of the function calls using the \"eps=\" option, defaulting to the compiled in `etolerance` value (1e-14 for fp64, 1e-8 for fp32).\n",
|
||||
"\n",
|
||||
"The submodule can be enabled by setting `ULAB_SCIPY_HAS_INTEGRATE_MODULE` in `code/ulab.h`. As for the individual integration algorithms, you can select which to include by setting one or more of `ULAB_INTEGRATE_HAS_QUAD`, `ULAB_INTEGRATE_HAS_ROMBERG`, `ULAB_INTEGRATE_HAS_SIMPSON`, and `ULAB_INTEGRATE_HAS_TANHSINH`.\n",
|
||||
"\n",
|
||||
"Also note that these algorithms do not support complex numbers, although it is certainly possible to implement complex integration in MicroPython on top of this module, e.g. as in https://stackoverflow.com/questions/5965583/use-scipy-integrate-quad-to-integrate-complex-numbers. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## quad\n",
|
||||
"\n",
|
||||
"`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html \n",
|
||||
"\n",
|
||||
"In CPython `scipy.integrate`, `quad` is a wrapper implementing many algorithms based on the Fortran QUADPACK package. Gauss-Kronrod is just one of them, and it is useful for most general-purpose tasks. This particular function implements an Adaptive Gauss-Kronrod (G10,K21) quadrature algorithm. The Gauss–Kronrod quadrature formula is a variant of Gaussian quadrature, in which the evaluation points are chosen so that an accurate approximation can be computed by re-using the information produced by the computation of a less accurate approximation (https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula). \n",
|
||||
"\n",
|
||||
"The function takes three to five arguments: \n",
|
||||
"\n",
|
||||
"* f, a callable,\n",
|
||||
"* a and b, the lower and upper integration limit, \n",
|
||||
"* order=, the order of integration (default 5),\n",
|
||||
"* eps=, the error tolerance (default etolerance) \n",
|
||||
"\n",
|
||||
"The function returns the result and the error estimate as a tuple of floats. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-06-19T20:24:10.529668Z",
|
||||
"start_time": "2020-06-19T20:24:10.520389Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"UsageError: Cell magic `%%micropython` not found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import scipy\n",
|
||||
"\n",
|
||||
"f = lambda x: x**2 + 2*x + 1\n",
|
||||
"result = scipy.integrate.quad(f, 0, 5, order=5, eps=1e-10)\n",
|
||||
"print (f\"result = {result}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## romberg\n",
|
||||
"\n",
|
||||
"`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html \n",
|
||||
"\n",
|
||||
"This function implements the Romberg quadrature algorithm. Romberg's method is a Newton–Cotes formula – it evaluates the integrand at equally spaced points. The integrand must have continuous derivatives, though fairly good results may be obtained if only a few derivatives exist. If it is possible to evaluate the integrand at unequally spaced points, then other methods such as Gaussian quadrature and Clenshaw–Curtis quadrature are generally more accurate (https://en.wikipedia.org/wiki/Romberg%27s_method). \n",
|
||||
"\n",
|
||||
"Please note: This function is deprecated as of SciPy 1.12.0 and will be removed in SciPy 1.15.0. Please use `scipy.integrate.quad` instead. \n",
|
||||
"\n",
|
||||
"The function takes three to five arguments: \n",
|
||||
"\n",
|
||||
"* f, a callable,\n",
|
||||
"* a and b, the lower and upper integration limit, \n",
|
||||
"* steps=, the number of steps taken to calculate (default 100),\n",
|
||||
"* eps=, the error tolerance (default etolerance) \n",
|
||||
"\n",
|
||||
"The function returns the result as a float.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"UsageError: Cell magic `%%micropython` not found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import scipy\n",
|
||||
"\n",
|
||||
"f = lambda x: x**2 + 2*x + 1\n",
|
||||
"result = scipy.integrate.romberg(f, 0, 5)\n",
|
||||
"print (f\"result = {result}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## simpson\n",
|
||||
"\n",
|
||||
"`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.simpson.html \n",
|
||||
"\n",
|
||||
"This function is different from CPython's `simpson` method in that it does not take an array of function values but determines the optimal spacing of samples itself. Adaptive Simpson's method, also called adaptive Simpson's rule, is a method of numerical integration proposed by G.F. Kuncir in 1962. It is probably the first recursive adaptive algorithm for numerical integration to appear in print, although more modern adaptive methods based on Gauss–Kronrod quadrature and Clenshaw–Curtis quadrature are now generally preferred (https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method). \n",
|
||||
"\n",
|
||||
"The function takes three to five arguments: \n",
|
||||
"\n",
|
||||
"* f, a callable,\n",
|
||||
"* a and b, the lower and upper integration limit, \n",
|
||||
"* steps=, the number of steps taken to calculate (default 100),\n",
|
||||
"* eps=, the error tolerance (default etolerance) \n",
|
||||
"\n",
|
||||
"The function returns the result as a float."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"UsageError: Cell magic `%%micropython` not found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import scipy\n",
|
||||
"\n",
|
||||
"f = lambda x: x**2 + 2*x + 1\n",
|
||||
"result = scipy.integrate.simpson(f, 0, 5)\n",
|
||||
"print (f\"result = {result}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## tanhsinh\n",
|
||||
"\n",
|
||||
"`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html \n",
|
||||
"\n",
|
||||
"In CPython `scipy.integrate`, `tanhsinh` is written in Python (https://github.com/scipy/scipy/blob/main/scipy/integrate/_tanhsinh.py). It is used in cases where Newton-Cotes, Gauss-Kronrod, and other formulae do not work due to properties of the integrand or the integration limits. (In SciPy v1.14.1, it is not a public function but it has been marked as public in SciPy v1.15.0rc1). \n",
|
||||
"\n",
|
||||
"This particular function implements an optimized Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature algorithm. It is especially applied where singularities or infinite derivatives exist at one or both endpoints. The method uses hyperbolic functions in a change of variables to transform an integral on the interval x ∈ (−1, 1) to an integral on the entire real line t ∈ (−∞, ∞), the two integrals having the same value. After this transformation, the integrand decays with a double exponential rate, and thus, this method is also known as the double exponential (DE) formula (https://en.wikipedia.org/wiki/Tanh-sinh_quadrature). \n",
|
||||
"\n",
|
||||
"As opposed to the three algorithms mentioned before, it also supports integrals with infinite limits like the Gaussian integral (https://en.wikipedia.org/wiki/Gaussian_integral), as shown below. \n",
|
||||
"\n",
|
||||
"The function takes three to five arguments: \n",
|
||||
"\n",
|
||||
"* f, a callable,\n",
|
||||
"* a and b, the lower and upper integration limit, \n",
|
||||
"* levels=, the number of loops taken to calculate (default 6),\n",
|
||||
"* eps=, the error tolerance (default: etolerance)\n",
|
||||
"\n",
|
||||
"The function returns the result and the error estimate as a tuple of floats.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"UsageError: Cell magic `%%micropython` not found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import scipy, numpy as np\n",
|
||||
"from math import *\n",
|
||||
"f = lambda x: exp(- x**2)\n",
|
||||
"result = scipy.integrate.tanhsinh(f, -np.inf, np.inf)\n",
|
||||
"print (f\"result = {result}\")\n",
|
||||
"exact = sqrt(pi) # which is the exact value\n",
|
||||
"print (f\"exact value = {exact}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.3"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
"nav_menu": {},
|
||||
"number_sections": true,
|
||||
"sideBar": true,
|
||||
"skip_h1_title": false,
|
||||
"title_cell": "Table of Contents",
|
||||
"title_sidebar": "Contents",
|
||||
"toc_cell": false,
|
||||
"toc_position": {
|
||||
"height": "calc(100% - 180px)",
|
||||
"left": "10px",
|
||||
"top": "150px",
|
||||
"width": "382.797px"
|
||||
},
|
||||
"toc_section_display": true,
|
||||
"toc_window_display": true
|
||||
},
|
||||
"varInspector": {
|
||||
"cols": {
|
||||
"lenName": 16,
|
||||
"lenType": 16,
|
||||
"lenVar": 40
|
||||
},
|
||||
"kernels_config": {
|
||||
"python": {
|
||||
"delete_cmd_postfix": "",
|
||||
"delete_cmd_prefix": "del ",
|
||||
"library": "var_list.py",
|
||||
"varRefreshCmd": "print(var_dic_list())"
|
||||
},
|
||||
"r": {
|
||||
"delete_cmd_postfix": ") ",
|
||||
"delete_cmd_prefix": "rm(",
|
||||
"library": "var_list.r",
|
||||
"varRefreshCmd": "cat(var_dic_list()) "
|
||||
}
|
||||
},
|
||||
"types_to_exclude": [
|
||||
"module",
|
||||
"function",
|
||||
"builtin_function_or_method",
|
||||
"instance",
|
||||
"_Feature"
|
||||
],
|
||||
"window_display": false
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -1,87 +1,3 @@
|
|||
Fri, 06 Jun 2025
|
||||
|
||||
version 6.8.0
|
||||
|
||||
expose ndim property
|
||||
|
||||
Fri, 06 Jun 2025
|
||||
|
||||
version 6.7.7
|
||||
|
||||
fix ndarray type inference for micropython objects
|
||||
|
||||
Thu, 29 May 2025
|
||||
|
||||
version 6.7.6
|
||||
|
||||
loadtxt can deal with multi-line comments
|
||||
|
||||
Thu, 29 May 2025
|
||||
|
||||
version 6.7.5
|
||||
|
||||
fix typo and shape in radnom module
|
||||
|
||||
Sun, 16 Mar 2025
|
||||
|
||||
version 6.7.4
|
||||
|
||||
re-name integration constants to avoid name clash with EPS ports
|
||||
|
||||
Sun, 26 Jan 2025
|
||||
|
||||
version 6.7.3
|
||||
|
||||
fix keepdims for min, max, argmin, argmax
|
||||
|
||||
Sun, 19 Jan 2025
|
||||
|
||||
version 6.7.2
|
||||
|
||||
fix keepdims for std, remove redundant macros from numerical.h, update documentation
|
||||
|
||||
Mon, 30 Dec 2024
|
||||
|
||||
version 6.7.1
|
||||
|
||||
add keepdims keyword argument to numerical functions
|
||||
|
||||
Sun, 15 Dec 2024
|
||||
|
||||
version 6.7.0
|
||||
|
||||
add scipy.integrate module
|
||||
|
||||
Sun, 24 Nov 2024
|
||||
|
||||
version 6.6.1
|
||||
|
||||
fix compilation error, for complexes
|
||||
|
||||
Wed, 9 Oct 2024
|
||||
|
||||
version 6.6.0
|
||||
|
||||
add numpy.take
|
||||
|
||||
Sat, 14 Sep 2024
|
||||
|
||||
version 6.5.5
|
||||
|
||||
add scratchpad, out, log keyword arguments to spectrum
|
||||
|
||||
Sat, 14 Sep 2024
|
||||
|
||||
version 6.5.4
|
||||
|
||||
fix roll, when shift is 0
|
||||
|
||||
Wed, 6 Mar 2024
|
||||
|
||||
version 6.5.2
|
||||
|
||||
allow loadtxt to parse numbers, even if built-in complexes are not supported
|
||||
|
||||
Tue, 9 Jan 2024
|
||||
|
||||
version 6.5.0
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-02-09T06:27:15.118699Z",
|
||||
|
|
@ -57,11 +57,11 @@
|
|||
"# -- Project information -----------------------------------------------------\n",
|
||||
"\n",
|
||||
"project = 'The ulab book'\n",
|
||||
"copyright = '2019-2025, 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 = '6.9.0'\n",
|
||||
"release = '5.1.0'\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# -- General configuration ---------------------------------------------------\n",
|
||||
|
|
@ -190,8 +190,6 @@
|
|||
" numpy-universal\n",
|
||||
" numpy-fft\n",
|
||||
" numpy-linalg\n",
|
||||
" numpy-random\n",
|
||||
" scipy-integrate\n",
|
||||
" scipy-linalg\n",
|
||||
" scipy-optimize\n",
|
||||
" scipy-signal\n",
|
||||
|
|
@ -217,7 +215,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-02-09T06:27:21.647179Z",
|
||||
|
|
@ -258,51 +256,14 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-02-09T06:27:42.024028Z",
|
||||
"start_time": "2022-02-09T06:27:36.109093Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n",
|
||||
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
|
||||
" _, nbc = validator.normalize(nbc)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"files = ['ulab-intro',\n",
|
||||
" 'ulab-ndarray',\n",
|
||||
|
|
@ -310,8 +271,6 @@
|
|||
" 'numpy-universal',\n",
|
||||
" 'numpy-fft',\n",
|
||||
" 'numpy-linalg',\n",
|
||||
" 'numpy-random',\n",
|
||||
" 'scipy-integrate',\n",
|
||||
" 'scipy-linalg',\n",
|
||||
" 'scipy-optimize',\n",
|
||||
" 'scipy-signal',\n",
|
||||
|
|
@ -476,7 +435,7 @@
|
|||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "base",
|
||||
"display_name": "Python 3.8.5 ('base')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
|
|
@ -490,7 +449,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.7"
|
||||
"version": "3.8.5"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
|
|
@ -538,6 +497,11 @@
|
|||
"_Feature"
|
||||
],
|
||||
"window_display": false
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@
|
|||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Matplotlib is building the font cache; this may take a moment.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
|
|
@ -31,7 +38,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-07T18:13:14.590799Z",
|
||||
|
|
@ -232,7 +239,7 @@
|
|||
"source": [
|
||||
"## Enter ulab\n",
|
||||
"\n",
|
||||
"`ulab` is a `numpy`-like module for `micropython` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. `ulab` implements a small subset of `numpy` and `scipy`, as well as a number of functions manipulating byte arrays. The functions were chosen such that they might be useful in the context of a microcontroller. However, the project is a living one, and suggestions for new features are always welcome. \n",
|
||||
"`ulab` is a `numpy`-like module for `micropython` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. `ulab` implements a small subset of `numpy` and `scipy`. The functions were chosen such that they might be useful in the context of a microcontroller. However, the project is a living one, and suggestions for new features are always welcome. \n",
|
||||
"\n",
|
||||
"This document discusses how you can use the library, starting from building your own firmware, through questions like what affects the firmware size, what are the trade-offs, and what are the most important differences to `numpy` and `scipy`, respectively. The document is organised as follows:\n",
|
||||
"\n",
|
||||
|
|
@ -397,7 +404,7 @@
|
|||
"np.polyval(p, x)\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"There are a couple of exceptions to this rule, namely `fft`, `linalg`, and `random`, which are sub-modules even in `numpy`, thus you have to write them out as \n",
|
||||
"There are a couple of exceptions to this rule, namely `fft`, and `linalg`, which are sub-modules even in `numpy`, thus you have to write them out as \n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"from ulab import numpy as np\n",
|
||||
|
|
@ -835,7 +842,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.13"
|
||||
"version": "3.8.5"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
|
|
|
|||
|
|
@ -2599,7 +2599,7 @@
|
|||
"source": [
|
||||
"# Binary operators\n",
|
||||
"\n",
|
||||
"`ulab` implements the `+`, `-`, `*`, `/`, `**`, `%`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n",
|
||||
"`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n",
|
||||
"\n",
|
||||
"Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n",
|
||||
"A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n",
|
||||
|
|
@ -3270,7 +3270,7 @@
|
|||
"output_type": "stream",
|
||||
"text": [
|
||||
"a:\t array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float)\n",
|
||||
"a[a < 5]:\t array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)\n",
|
||||
"a < 5:\t array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
|
|
@ -3283,7 +3283,7 @@
|
|||
"\n",
|
||||
"a = np.array(range(9), dtype=np.float)\n",
|
||||
"print(\"a:\\t\", a)\n",
|
||||
"print(\"a[a < 5]:\\t\", a[a < 5])"
|
||||
"print(\"a < 5:\\t\", a[a < 5])"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"%pylab is deprecated, use %matplotlib inline and import the required libraries.\n",
|
||||
"Populating the interactive namespace from numpy and matplotlib\n"
|
||||
]
|
||||
}
|
||||
|
|
@ -32,7 +31,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-07T19:16:29.118001Z",
|
||||
|
|
@ -78,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/build-2/micropython-2\", \"/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",
|
||||
|
|
@ -183,7 +182,7 @@
|
|||
"%%micropython -pyboard 1\n",
|
||||
"\n",
|
||||
"import utime\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"\n",
|
||||
"def timeit(n=1000):\n",
|
||||
" def wrapper(f, *args, **kwargs):\n",
|
||||
|
|
@ -245,14 +244,145 @@
|
|||
"\n",
|
||||
"**WARNING:** Difference to `numpy`: the `out` keyword argument is not implemented.\n",
|
||||
"\n",
|
||||
"These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined. The functions also accept the `keepdims=True` or `keepdims=False` keyword argument. The latter case is the default, while the former keeps the dimensions (the number of axes) of the supplied array. \n",
|
||||
"These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined.\n",
|
||||
"\n",
|
||||
"`argmin/argmax` return the position (index) of the minimum/maximum in the sequence."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 108,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-10-17T21:26:22.507996Z",
|
||||
"start_time": "2020-10-17T21:26:22.492543Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"array([1.0, 2.0, 3.0], dtype=float)\n",
|
||||
"array([], dtype=float)\n",
|
||||
"[] 0\n",
|
||||
"array([1.0, 2.0, 3.0], dtype=float)\n",
|
||||
"array([], dtype=float)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"import ulab as np\n",
|
||||
"\n",
|
||||
"a = np.array([1, 2, 3])\n",
|
||||
"print(a)\n",
|
||||
"print(a[-1:-1:-3])\n",
|
||||
"try:\n",
|
||||
" sa = list(a[-1:-1:-3])\n",
|
||||
" la = len(sa)\n",
|
||||
"except IndexError as e:\n",
|
||||
" sa = str(e)\n",
|
||||
" la = -1\n",
|
||||
" \n",
|
||||
"print(sa, la)\n",
|
||||
"\n",
|
||||
"a[-1:-1:-3] = np.ones(0)\n",
|
||||
"print(a)\n",
|
||||
"\n",
|
||||
"b = np.ones(0) + 1\n",
|
||||
"print(b)\n",
|
||||
"# print('b', b.shape())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 122,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-10-17T21:54:49.123748Z",
|
||||
"start_time": "2020-10-17T21:54:49.093819Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"0, 1, -3array([], dtype=float)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"import ulab as np\n",
|
||||
"a = np.array([1, 2, 3])\n",
|
||||
"print(a[0:1:-3])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 127,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-10-17T21:57:01.482277Z",
|
||||
"start_time": "2020-10-17T21:57:01.477362Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[0]"
|
||||
]
|
||||
},
|
||||
"execution_count": 127,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"l = list(range(13))\n",
|
||||
"\n",
|
||||
"l[0:10:113]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 81,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-10-17T20:59:58.285134Z",
|
||||
"start_time": "2020-10-17T20:59:58.263605Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0,)"
|
||||
]
|
||||
},
|
||||
"execution_count": 81,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"a = np.array([1, 2, 3])\n",
|
||||
"np.ones(0, dtype=uint8) / np.zeros(0, dtype=uint16)\n",
|
||||
"np.ones(0).shape"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 375,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2019-10-18T13:08:28.113525Z",
|
||||
|
|
@ -264,16 +394,16 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float64)\n",
|
||||
"a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float)\n",
|
||||
"min of a: 0.0\n",
|
||||
"argmin of a: 2\n",
|
||||
"\n",
|
||||
"b:\n",
|
||||
" array([[1.0, 2.0, 0.0],\n",
|
||||
" [1.0, 10.0, -1.0]], dtype=float64)\n",
|
||||
"\t [1.0, 10.0, -1.0]], dtype=float)\n",
|
||||
"min of b (flattened): -1.0\n",
|
||||
"min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float64)\n",
|
||||
"min of b (axis=1): array([0.0, -1.0], dtype=float64)\n",
|
||||
"min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float)\n",
|
||||
"min of b (axis=1): array([0.0, -1.0], dtype=float)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
|
|
@ -282,55 +412,19 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([1, 2, 0, 1, 10])\n",
|
||||
"print('a:', a)\n",
|
||||
"print('min of a:', np.min(a))\n",
|
||||
"print('argmin of a:', np.argmin(a))\n",
|
||||
"print('min of a:', numerical.min(a))\n",
|
||||
"print('argmin of a:', numerical.argmin(a))\n",
|
||||
"\n",
|
||||
"b = np.array([[1, 2, 0], [1, 10, -1]])\n",
|
||||
"print('\\nb:\\n', b)\n",
|
||||
"print('min of b (flattened):', np.min(b))\n",
|
||||
"print('min of b (axis=0):', np.min(b, axis=0))\n",
|
||||
"print('min of b (axis=1):', np.min(b, axis=1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"a: array([[0.0, 1.0, 2.0, 3.0],\n",
|
||||
" [4.0, 5.0, 6.0, 7.0],\n",
|
||||
" [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"min of a (axis=1):\n",
|
||||
" array([[0.0],\n",
|
||||
" [4.0],\n",
|
||||
" [8.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"min of a (axis=0):\n",
|
||||
" array([[0.0, 1.0, 2.0, 3.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"\n",
|
||||
"a = np.array(range(12)).reshape((3, 4))\n",
|
||||
"\n",
|
||||
"print('a:', a)\n",
|
||||
"print('\\nmin of a (axis=1):\\n', np.min(a, axis=1, keepdims=True))\n",
|
||||
"print('\\nmin of a (axis=0):\\n', np.min(a, axis=0, keepdims=True))"
|
||||
"print('min of b (flattened):', numerical.min(b))\n",
|
||||
"print('min of b (axis=0):', numerical.min(b, axis=0))\n",
|
||||
"print('min of b (axis=1):', numerical.min(b, axis=1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -345,14 +439,12 @@
|
|||
"\n",
|
||||
"`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.mean.html\n",
|
||||
"\n",
|
||||
"These three functions follow the same pattern: if the `axis` keyword is not specified, they assume the default value of `None`, and return the result of the computation for the flattened array. Otherwise, the calculation is along the given axis. \n",
|
||||
"\n",
|
||||
"If the `axis` keyword argument is a number (this can also be negative to signify counting from the rightmost axis) the functions contract the arrays, i.e., the results will have one axis fewer than the input array. The only exception to this rule is when the `keepdims` keyword argument is supplied with a value `True`, in which case, the results will have the same number of axis as the input, but the axis specified in `axis` will have a length of 1. This is useful in cases, when the output is to be broadcast with the input in subsequent computations."
|
||||
"These three functions follow the same pattern: if the axis keyword is not specified, it assumes the default value of `None`, and returns the result of the computation for the flattened array. Otherwise, the calculation is along the given axis."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 527,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2019-10-20T06:51:58.845076Z",
|
||||
|
|
@ -366,73 +458,29 @@
|
|||
"text": [
|
||||
"a: \n",
|
||||
" array([[1.0, 2.0, 3.0],\n",
|
||||
" [4.0, 5.0, 6.0],\n",
|
||||
" [7.0, 8.0, 9.0]], dtype=float64)\n",
|
||||
"\t [4.0, 5.0, 6.0],\n",
|
||||
"\t [7.0, 8.0, 9.0]], dtype=float)\n",
|
||||
"sum, flat array: 45.0\n",
|
||||
"mean, horizontal: array([2.0, 5.0, 8.0], dtype=float64)\n",
|
||||
"std, vertical: array([2.449489742783178, 2.449489742783178, 2.449489742783178], dtype=float64)\n",
|
||||
"\n",
|
||||
"mean, horizontal: array([2.0, 5.0, 8.0], dtype=float)\n",
|
||||
"std, vertical: array([2.44949, 2.44949, 2.44949], dtype=float)\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"%%micropython -pyboard 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
|
||||
"print('a: \\n', a)\n",
|
||||
"\n",
|
||||
"print('sum, flat array: ', np.sum(a))\n",
|
||||
"print('sum, flat array: ', numerical.sum(a))\n",
|
||||
"\n",
|
||||
"print('mean, horizontal: ', np.mean(a, axis=1))\n",
|
||||
"print('mean, horizontal: ', numerical.mean(a, axis=1))\n",
|
||||
"\n",
|
||||
"print('std, vertical: ', np.std(a, axis=0))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 52,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"a: \n",
|
||||
" array([[1.0, 2.0, 3.0],\n",
|
||||
" [4.0, 5.0, 6.0],\n",
|
||||
" [7.0, 8.0, 9.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"std, along 0th axis:\n",
|
||||
" array([2.449489742783178, 2.449489742783178, 2.449489742783178], dtype=float64)\n",
|
||||
"\n",
|
||||
"a: \n",
|
||||
" array([[1.0, 2.0, 3.0],\n",
|
||||
" [4.0, 5.0, 6.0],\n",
|
||||
" [7.0, 8.0, 9.0]], dtype=float64)\n",
|
||||
"\n",
|
||||
"std, along 1st axis, keeping dimensions:\n",
|
||||
" array([[0.8164965809277261],\n",
|
||||
" [0.8164965809277261],\n",
|
||||
" [0.8164965809277261]], dtype=float64)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"\n",
|
||||
"a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
|
||||
"print('a: \\n', a)\n",
|
||||
"print('\\nstd, along 0th axis:\\n', np.std(a, axis=0))\n",
|
||||
"\n",
|
||||
"print('\\na: \\n', a)\n",
|
||||
"print('\\nstd, along 1st axis, keeping dimensions:\\n', np.std(a, axis=1, keepdims=True))"
|
||||
"print('std, vertical: ', numerical.std(a, axis=0))"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -471,12 +519,13 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([1, 2, 3, 4, 5, 6, 7, 8])\n",
|
||||
"print(\"a:\\t\\t\\t\", a)\n",
|
||||
"\n",
|
||||
"np.roll(a, 2)\n",
|
||||
"numerical.roll(a, 2)\n",
|
||||
"print(\"a rolled to the left:\\t\", a)\n",
|
||||
"\n",
|
||||
"# this should be the original vector\n",
|
||||
|
|
@ -532,18 +581,19 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])\n",
|
||||
"print(\"a:\\n\", a)\n",
|
||||
"\n",
|
||||
"np.roll(a, 2)\n",
|
||||
"numerical.roll(a, 2)\n",
|
||||
"print(\"\\na rolled to the left:\\n\", a)\n",
|
||||
"\n",
|
||||
"np.roll(a, -1, axis=1)\n",
|
||||
"numerical.roll(a, -1, axis=1)\n",
|
||||
"print(\"\\na rolled up:\\n\", a)\n",
|
||||
"\n",
|
||||
"np.roll(a, 1, axis=None)\n",
|
||||
"numerical.roll(a, 1, axis=None)\n",
|
||||
"print(\"\\na rolled with None:\\n\", a)"
|
||||
]
|
||||
},
|
||||
|
|
@ -599,7 +649,9 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"from ulab import vector\n",
|
||||
"\n",
|
||||
"def dummy_adc():\n",
|
||||
" # dummy adc function, so that the results are reproducible\n",
|
||||
|
|
@ -607,8 +659,8 @@
|
|||
" \n",
|
||||
"n = 10\n",
|
||||
"# These are the normalised weights; the last entry is the most dominant\n",
|
||||
"weight = np.exp([1, 2, 3, 4, 5])\n",
|
||||
"weight = weight/np.sum(weight)\n",
|
||||
"weight = vector.exp([1, 2, 3, 4, 5])\n",
|
||||
"weight = weight/numerical.sum(weight)\n",
|
||||
"\n",
|
||||
"print(weight)\n",
|
||||
"# initial array of samples\n",
|
||||
|
|
@ -617,10 +669,10 @@
|
|||
"for i in range(n):\n",
|
||||
" # a new datum is inserted on the right hand side. This simply overwrites whatever was in the last slot\n",
|
||||
" samples[-1] = dummy_adc()\n",
|
||||
" print(np.mean(samples[-5:]*weight))\n",
|
||||
" print(numerical.mean(samples[-5:]*weight))\n",
|
||||
" print(samples[-5:])\n",
|
||||
" # the data are shifted by one position to the left\n",
|
||||
" numerical.np(samples, 1)"
|
||||
" numerical.roll(samples, 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -673,16 +725,17 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([1, 2, 3, 4, 5])\n",
|
||||
"print(\"a: \\t\", a)\n",
|
||||
"print(\"a flipped:\\t\", np.flip(a))\n",
|
||||
"\n",
|
||||
"a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.uint8)\n",
|
||||
"print(\"\\na flipped horizontally\\n\", np.flip(a, axis=1))\n",
|
||||
"print(\"\\na flipped vertically\\n\", np.flip(a, axis=0))\n",
|
||||
"print(\"\\na flipped horizontally+vertically\\n\", np.flip(a))"
|
||||
"print(\"\\na flipped horizontally\\n\", numerical.flip(a, axis=1))\n",
|
||||
"print(\"\\na flipped vertically\\n\", numerical.flip(a, axis=0))\n",
|
||||
"print(\"\\na flipped horizontally+vertically\\n\", numerical.flip(a))"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -747,18 +800,19 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array(range(9), dtype=np.uint8)\n",
|
||||
"print('a:\\n', a)\n",
|
||||
"\n",
|
||||
"print('\\nfirst derivative:\\n', np.diff(a, n=1))\n",
|
||||
"print('\\nsecond derivative:\\n', np.diff(a, n=2))\n",
|
||||
"print('\\nfirst derivative:\\n', numerical.diff(a, n=1))\n",
|
||||
"print('\\nsecond derivative:\\n', numerical.diff(a, n=2))\n",
|
||||
"\n",
|
||||
"c = np.array([[1, 2, 3, 4], [4, 3, 2, 1], [1, 4, 9, 16], [0, 0, 0, 0]])\n",
|
||||
"print('\\nc:\\n', c)\n",
|
||||
"print('\\nfirst derivative, first axis:\\n', np.diff(c, axis=0))\n",
|
||||
"print('\\nfirst derivative, second axis:\\n', np.diff(c, axis=1))"
|
||||
"print('\\nfirst derivative, first axis:\\n', numerical.diff(c, axis=0))\n",
|
||||
"print('\\nfirst derivative, second axis:\\n', numerical.diff(c, axis=1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -804,7 +858,7 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"\n",
|
||||
"a = np.array(range(12), dtype=np.int8).reshape((3, 4))\n",
|
||||
"print('a:\\n', a)\n",
|
||||
|
|
@ -871,17 +925,18 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n",
|
||||
"print('\\na:\\n', a)\n",
|
||||
"b = np.sort(a, axis=0)\n",
|
||||
"b = numerical.sort(a, axis=0)\n",
|
||||
"print('\\na sorted along vertical axis:\\n', b)\n",
|
||||
"\n",
|
||||
"c = np.sort(a, axis=1)\n",
|
||||
"c = numerical.sort(a, axis=1)\n",
|
||||
"print('\\na sorted along horizontal axis:\\n', c)\n",
|
||||
"\n",
|
||||
"c = np.sort(a, axis=None)\n",
|
||||
"c = numerical.sort(a, axis=None)\n",
|
||||
"print('\\nflattened a sorted:\\n', c)"
|
||||
]
|
||||
},
|
||||
|
|
@ -900,13 +955,15 @@
|
|||
"source": [
|
||||
"%%micropython -pyboard 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import vector\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"@timeit\n",
|
||||
"def sort_time(array):\n",
|
||||
" return np.sort(array)\n",
|
||||
" return numerical.sort(array)\n",
|
||||
"\n",
|
||||
"b = np.sin(np.linspace(0, 6.28, num=1000))\n",
|
||||
"b = vector.sin(np.linspace(0, 6.28, num=1000))\n",
|
||||
"print('b: ', b)\n",
|
||||
"sort_time(b)\n",
|
||||
"print('\\nb sorted:\\n', b)"
|
||||
|
|
@ -968,17 +1025,18 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n",
|
||||
"print('\\na:\\n', a)\n",
|
||||
"b = np.argsort(a, axis=0)\n",
|
||||
"b = numerical.argsort(a, axis=0)\n",
|
||||
"print('\\na sorted along vertical axis:\\n', b)\n",
|
||||
"\n",
|
||||
"c = np.argsort(a, axis=1)\n",
|
||||
"c = numerical.argsort(a, axis=1)\n",
|
||||
"print('\\na sorted along horizontal axis:\\n', c)\n",
|
||||
"\n",
|
||||
"c = np.argsort(a, axis=None)\n",
|
||||
"c = numerical.argsort(a, axis=None)\n",
|
||||
"print('\\nflattened a sorted:\\n', c)"
|
||||
]
|
||||
},
|
||||
|
|
@ -1020,11 +1078,12 @@
|
|||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"import ulab as np\n",
|
||||
"from ulab import numerical\n",
|
||||
"\n",
|
||||
"a = np.array([0, 5, 1, 3, 2, 4], dtype=np.uint8)\n",
|
||||
"print('\\na:\\n', a)\n",
|
||||
"b = np.argsort(a, axis=1)\n",
|
||||
"b = numerical.argsort(a, axis=1)\n",
|
||||
"print('\\nsorting indices:\\n', b)\n",
|
||||
"print('\\nthe original array:\\n', a)"
|
||||
]
|
||||
|
|
@ -1032,7 +1091,7 @@
|
|||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "base",
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
|
|
@ -1046,7 +1105,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.7"
|
||||
"version": "3.8.5"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
|
|
|
|||
1059
docs/ulab-pid.ipynb
Normal file
1059
docs/ulab-pid.ipynb
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -718,7 +718,7 @@
|
|||
"Finally, we have to bind this function object in the globals table of the `user` module: \n",
|
||||
"\n",
|
||||
"```c\n",
|
||||
"static const mp_rom_map_elem_t ulab_user_globals_table[] = {\n",
|
||||
"STATIC const mp_rom_map_elem_t ulab_user_globals_table[] = {\n",
|
||||
" { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) },\n",
|
||||
" { MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj },\n",
|
||||
"};\n",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-29T16:53:11.972661Z",
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-29T16:59:24.652277Z",
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
|
||||
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
|
||||
" fout.write(cell)\n",
|
||||
" proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/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",
|
||||
|
|
@ -291,9 +291,7 @@
|
|||
"a = bytearray([1, 1, 0, 0, 0, 0, 0, 255])\n",
|
||||
"print('a: ', a)\n",
|
||||
"print()\n",
|
||||
"print('unsigned integers: ', utils.from_uint32_buffe\n",
|
||||
"print('original vector:\\n', y)\n",
|
||||
"print('\\nspectrum:\\n', a)r(a))\n",
|
||||
"print('unsigned integers: ', utils.from_uint32_buffer(a))\n",
|
||||
"\n",
|
||||
"b = bytearray([1, 1, 0, 0, 0, 0, 0, 255])\n",
|
||||
"print('\\nb: ', b)\n",
|
||||
|
|
@ -400,53 +398,12 @@
|
|||
"source": [
|
||||
"## spectrogram\n",
|
||||
"\n",
|
||||
"In addition to the Fourier transform and its inverse, `ulab` also sports a function called `spectrogram`, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. The positional arguments are treated in the same way as in `fft`, and `ifft`. This means that, if the firmware was compiled with complex support and `ULAB_FFT_IS_NUMPY_COMPATIBLE` is defined to be 1 in `ulab.h`, the input can also be a complex array. \n",
|
||||
"\n",
|
||||
"And easy way to find out if the FFT is `numpy`-compatible is to check the number of values `fft.fft` returns, when called with a single real argument of length other than 2: "
|
||||
"In addition to the Fourier transform and its inverse, `ulab` also sports a function called `spectrogram`, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. The arguments are treated in the same way as in `fft`, and `ifft`. This means that, if the firmware was compiled with complex support, the input can also be a complex array."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"FFT is numpy compatible (complex inputs/outputs)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%micropython -unix 1\n",
|
||||
"\n",
|
||||
"from ulab import numpy as np\n",
|
||||
"\n",
|
||||
"if len(np.fft.fft(np.zeros(4))) == 2:\n",
|
||||
" print('FFT is NOT numpy compatible (real and imaginary parts are treated separately)')\n",
|
||||
"else:\n",
|
||||
" print('FFT is numpy compatible (complex inputs/outputs)')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Depending on the `numpy`-compatibility of the FFT, the `spectrogram` function takes one or two positional arguments, and three keyword arguments. If the FFT is `numpy` compatible, one positional argument is allowed, and it is a 1D real or complex `ndarray`. If the FFT is not `numpy`-compatible, if a single argument is supplied, it will be treated as the real part of the input, and if two positional arguments are supplied, they are treated as the real and imaginary parts of the signal.\n",
|
||||
"\n",
|
||||
"The keyword arguments are as follows:\n",
|
||||
"\n",
|
||||
"1. `scratchpad = None`: must be a 1D, dense, floating point array, twice as long as the input array; the `scratchpad` will be used as a temporary internal buffer to perform the Fourier transform; the `scratchpad` can repeatedly be re-used.\n",
|
||||
"1. `out = None`: must be a 1D, not necessarily dense, floating point array that will store the results\n",
|
||||
"1. `log = False`: must be either `True`, or `False`; if `True`, the `spectrogram` returns the logarithm of the absolute values of the Fourier transform."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-29T16:59:56.400603Z",
|
||||
|
|
@ -462,7 +419,7 @@
|
|||
" array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64)\n",
|
||||
"\n",
|
||||
"spectrum:\n",
|
||||
" array([187.8635087634578, 315.3112063607119, 347.8814873399375, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
|
||||
" array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
|
|
@ -487,14 +444,12 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"As such, `spectrogram` is really just a shorthand for `np.abs(np.fft.fft(signal))`, if the FFT is `numpy`-compatible, or `np.sqrt(a*a + b*b)` if the FFT returns the real (`a`) and imaginary (`b`) parts separately. However, `spectrogram` saves significant amounts of RAM: the expression `a*a + b*b` has to allocate memory for `a*a`, `b*b`, and finally, their sum. Similarly, `np.abs` returns a new array. This issue is compounded even more, if `np.log()` is used on the absolute value. \n",
|
||||
"\n",
|
||||
"In contrast, `spectrogram` handles all calculations in the same internal arrays, and allows one to re-use previously reserved RAM. This can be especially useful in cases, when `spectogram` is called repeatedly, as in the snippet below."
|
||||
"As such, `spectrogram` is really just a shorthand for `np.sqrt(a*a + b*b)`, however, it saves significant amounts of RAM: the expression `a*a + b*b` has to allocate memory for `a*a`, `b*b`, and finally, their sum. In contrast, `spectrogram` calculates the spectrum internally, and stores it in the memory segment that was reserved for the real part of the Fourier transform."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2022-01-29T16:59:48.485610Z",
|
||||
|
|
@ -506,8 +461,12 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"signal: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)\n",
|
||||
"out: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)\n",
|
||||
"\n",
|
||||
"spectrum calculated the hard way:\n",
|
||||
" array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
|
||||
"\n",
|
||||
"spectrum calculated the lazy way:\n",
|
||||
" array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
|
|
@ -519,34 +478,17 @@
|
|||
"from ulab import numpy as np\n",
|
||||
"from ulab import utils as utils\n",
|
||||
"\n",
|
||||
"n = 1024\n",
|
||||
"t = np.linspace(0, 2 * np.pi, num=1024)\n",
|
||||
"scratchpad = np.zeros(2 * n)\n",
|
||||
"x = np.linspace(0, 10, num=1024)\n",
|
||||
"y = np.sin(x)\n",
|
||||
"\n",
|
||||
"for _ in range(10):\n",
|
||||
" signal = np.sin(t)\n",
|
||||
" utils.spectrogram(signal, out=signal, scratchpad=scratchpad, log=True)\n",
|
||||
"a, b = np.fft.fft(y)\n",
|
||||
"\n",
|
||||
"print('signal: ', signal)\n",
|
||||
"print('\\nspectrum calculated the hard way:\\n', np.sqrt(a*a + b*b))\n",
|
||||
"\n",
|
||||
"for _ in range(10):\n",
|
||||
" signal = np.sin(t)\n",
|
||||
" out = np.log(utils.spectrogram(signal))\n",
|
||||
"a = utils.spectrogram(y)\n",
|
||||
"\n",
|
||||
"print('out: ', out)"
|
||||
"print('\\nspectrum calculated the lazy way:\\n', a)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that `scratchpad` is reserved only once, and then is re-used in the first loop. By assigning `signal` to the output, we save additional RAM. This approach avoids the usual problem of memory fragmentation, which would happen in the second loop, where both `spectrogram`, and `np.log` must reserve RAM in each iteration."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
|
@ -565,7 +507,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.7"
|
||||
"version": "3.8.5"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
|
|
|
|||
|
|
@ -10,12 +10,8 @@ x = np.linspace(-np.pi, np.pi, num=8)
|
|||
y = np.sin(x)
|
||||
|
||||
if use_ulab:
|
||||
if 'real' in dir(np):
|
||||
a = np.fft.fft(y)
|
||||
c = np.real(np.fft.ifft(a))
|
||||
else:
|
||||
a, b = np.fft.fft(y)
|
||||
c, d = np.fft.ifft(a, b)
|
||||
a, b = np.fft.fft(y)
|
||||
c, d = np.fft.ifft(a, b)
|
||||
# c should be equal to y
|
||||
cmp_result = []
|
||||
for p,q in zip(list(y), list(c)):
|
||||
|
|
@ -23,12 +19,8 @@ if use_ulab:
|
|||
print(cmp_result)
|
||||
|
||||
z = np.zeros(len(x))
|
||||
if 'real' in dir(np):
|
||||
a = np.fft.fft(y)
|
||||
c = np.real(np.fft.ifft(a))
|
||||
else:
|
||||
a, b = np.fft.fft(y, z)
|
||||
c, d = np.fft.ifft(a, b)
|
||||
a, b = np.fft.fft(y, z)
|
||||
c, d = np.fft.ifft(a, b)
|
||||
# c should be equal to y
|
||||
cmp_result = []
|
||||
for p,q in zip(list(y), list(c)):
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
try:
|
||||
from ulab import numpy as np
|
||||
except:
|
||||
import numpy as np
|
||||
|
||||
|
||||
dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float)
|
||||
|
||||
for dtype1 in dtypes:
|
||||
x1 = np.array(range(6), dtype=dtype1).reshape((2, 3))
|
||||
for dtype2 in dtypes:
|
||||
x2 = np.array(range(1, 4), dtype=dtype2)
|
||||
print(x1 % x2)
|
||||
|
||||
print()
|
||||
print('=' * 30)
|
||||
print('inplace modulo')
|
||||
print('=' * 30)
|
||||
print()
|
||||
|
||||
for dtype1 in dtypes:
|
||||
x1 = np.array(range(6), dtype=dtype1).reshape((2, 3))
|
||||
for dtype2 in dtypes:
|
||||
x2 = np.array(range(1, 4), dtype=dtype2)
|
||||
x1 %= x2
|
||||
print(x1)
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=uint8)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=uint16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int8)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 0, 1],
|
||||
[0, 2, 0]], dtype=uint8)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=uint16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
|
||||
==============================
|
||||
inplace modulo
|
||||
==============================
|
||||
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=uint8)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0, 0, 1],
|
||||
[0, 2, 0]], dtype=uint8)
|
||||
array([[0, 0, 1],
|
||||
[0, 0, 0]], dtype=int16)
|
||||
array([[0.0, 0.0, 1.0],
|
||||
[0.0, 0.0, 0.0]], dtype=float64)
|
||||
array([[0.0, 0.0, 1.0],
|
||||
[0.0, 0.0, 0.0]], dtype=float64)
|
||||
array([[0.0, 0.0, 1.0],
|
||||
[0.0, 0.0, 0.0]], dtype=float64)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0, 1, 2],
|
||||
[0, 0, 2]], dtype=int16)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[0.0, 0.0, 2.0]], dtype=float64)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
try:
|
||||
from ulab import numpy as np
|
||||
except ImportError:
|
||||
import numpy as np
|
||||
|
||||
rng = np.random.Generator(1234)
|
||||
|
||||
for generator in (rng.normal, rng.random, rng.uniform):
|
||||
random_array = generator(size=(1, 2))
|
||||
print("array shape:", random_array.shape)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
array shape: (1, 2)
|
||||
array shape: (1, 2)
|
||||
array shape: (1, 2)
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
try:
|
||||
from ulab import numpy as np
|
||||
except ImportError:
|
||||
import numpy as np
|
||||
|
||||
dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float)
|
||||
|
||||
for dtype in dtypes:
|
||||
print()
|
||||
print('=' * 50)
|
||||
a = np.array(range(12), dtype=dtype).reshape((3, 4))
|
||||
print(a)
|
||||
b = a[0,:]
|
||||
print(b.reshape((1,4)))
|
||||
b = a[:,0]
|
||||
print(b.reshape((1,3)))
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
|
||||
==================================================
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=uint8)
|
||||
array([[0, 1, 2, 3]], dtype=uint8)
|
||||
array([[0, 4, 8]], dtype=uint8)
|
||||
|
||||
==================================================
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=int8)
|
||||
array([[0, 1, 2, 3]], dtype=int8)
|
||||
array([[0, 4, 8]], dtype=int8)
|
||||
|
||||
==================================================
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=uint16)
|
||||
array([[0, 1, 2, 3]], dtype=uint16)
|
||||
array([[0, 4, 8]], dtype=uint16)
|
||||
|
||||
==================================================
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=int16)
|
||||
array([[0, 1, 2, 3]], dtype=int16)
|
||||
array([[0, 4, 8]], dtype=int16)
|
||||
|
||||
==================================================
|
||||
array([[0.0, 1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0, 7.0],
|
||||
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0, 3.0]], dtype=float64)
|
||||
array([[0.0, 4.0, 8.0]], dtype=float64)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
try:
|
||||
from ulab import numpy as np
|
||||
except ImportError:
|
||||
import numpy as np
|
||||
|
||||
for dtype in (np.uint8, np.int8, np.uint16, np.int8, np.float):
|
||||
a = np.array(range(12), dtype=dtype)
|
||||
b = a.reshape((3, 4))
|
||||
|
||||
print(a)
|
||||
print(b)
|
||||
print()
|
||||
|
||||
print(np.sum(a))
|
||||
print(np.sum(a, axis=0))
|
||||
print(np.sum(a, axis=0, keepdims=True))
|
||||
|
||||
print()
|
||||
print(np.sum(b))
|
||||
print(np.sum(b, axis=0))
|
||||
print(np.sum(b, axis=1))
|
||||
print(np.sum(b, axis=0, keepdims=True))
|
||||
print(np.sum(b, axis=1, keepdims=True))
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
array([0, 1, 2, ..., 9, 10, 11], dtype=uint8)
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=uint8)
|
||||
|
||||
66
|
||||
66
|
||||
array([66], dtype=uint8)
|
||||
|
||||
66
|
||||
array([12, 15, 18, 21], dtype=uint8)
|
||||
array([6, 22, 38], dtype=uint8)
|
||||
array([[12, 15, 18, 21]], dtype=uint8)
|
||||
array([[6],
|
||||
[22],
|
||||
[38]], dtype=uint8)
|
||||
array([0, 1, 2, ..., 9, 10, 11], dtype=int8)
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=int8)
|
||||
|
||||
66
|
||||
66
|
||||
array([66], dtype=int8)
|
||||
|
||||
66
|
||||
array([12, 15, 18, 21], dtype=int8)
|
||||
array([6, 22, 38], dtype=int8)
|
||||
array([[12, 15, 18, 21]], dtype=int8)
|
||||
array([[6],
|
||||
[22],
|
||||
[38]], dtype=int8)
|
||||
array([0, 1, 2, ..., 9, 10, 11], dtype=uint16)
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=uint16)
|
||||
|
||||
66
|
||||
66
|
||||
array([66], dtype=uint16)
|
||||
|
||||
66
|
||||
array([12, 15, 18, 21], dtype=uint16)
|
||||
array([6, 22, 38], dtype=uint16)
|
||||
array([[12, 15, 18, 21]], dtype=uint16)
|
||||
array([[6],
|
||||
[22],
|
||||
[38]], dtype=uint16)
|
||||
array([0, 1, 2, ..., 9, 10, 11], dtype=int8)
|
||||
array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=int8)
|
||||
|
||||
66
|
||||
66
|
||||
array([66], dtype=int8)
|
||||
|
||||
66
|
||||
array([12, 15, 18, 21], dtype=int8)
|
||||
array([6, 22, 38], dtype=int8)
|
||||
array([[12, 15, 18, 21]], dtype=int8)
|
||||
array([[6],
|
||||
[22],
|
||||
[38]], dtype=int8)
|
||||
array([0.0, 1.0, 2.0, ..., 9.0, 10.0, 11.0], dtype=float64)
|
||||
array([[0.0, 1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0, 7.0],
|
||||
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
|
||||
|
||||
66.0
|
||||
66.0
|
||||
array([66.0], dtype=float64)
|
||||
|
||||
66.0
|
||||
array([12.0, 15.0, 18.0, 21.0], dtype=float64)
|
||||
array([6.0, 22.0, 38.0], dtype=float64)
|
||||
array([[12.0, 15.0, 18.0, 21.0]], dtype=float64)
|
||||
array([[6.0],
|
||||
[22.0],
|
||||
[38.0]], dtype=float64)
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
try:
|
||||
from ulab import numpy as np
|
||||
except:
|
||||
import numpy as np
|
||||
|
||||
dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float)
|
||||
|
||||
print('flattened array')
|
||||
for dtype in dtypes:
|
||||
a = np.array(range(12), dtype=dtype).reshape((3, 4))
|
||||
print(np.take(a, (0, 10)))
|
||||
|
||||
print('\n1D arrays')
|
||||
for dtype in dtypes:
|
||||
a = np.array(range(12), dtype=dtype)
|
||||
print('\na:', a)
|
||||
indices = (0, 2, 2, 1)
|
||||
print(np.take(a, indices))
|
||||
indices = np.array([0, 2, 2, 1], dtype=np.uint8)
|
||||
print(np.take(a, indices))
|
||||
|
||||
print('\n2D arrays')
|
||||
for dtype in dtypes:
|
||||
a = np.array(range(12), dtype=dtype).reshape((3, 4))
|
||||
print('\na:', a)
|
||||
print('\nfirst axis')
|
||||
print(np.take(a, (0, 2, 2, 1), axis=0))
|
||||
print('\nsecond axis')
|
||||
print(np.take(a, (0, 2, 2, 1), axis=1))
|
||||
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
flattened array
|
||||
array([0, 10], dtype=uint8)
|
||||
array([0, 10], dtype=int8)
|
||||
array([0, 10], dtype=uint16)
|
||||
array([0, 10], dtype=int16)
|
||||
array([0.0, 10.0], dtype=float64)
|
||||
|
||||
1D arrays
|
||||
|
||||
a: array([0, 1, 2, ..., 9, 10, 11], dtype=uint8)
|
||||
array([0, 2, 2, 1], dtype=uint8)
|
||||
array([0, 2, 2, 1], dtype=uint8)
|
||||
|
||||
a: array([0, 1, 2, ..., 9, 10, 11], dtype=int8)
|
||||
array([0, 2, 2, 1], dtype=int8)
|
||||
array([0, 2, 2, 1], dtype=int8)
|
||||
|
||||
a: array([0, 1, 2, ..., 9, 10, 11], dtype=uint16)
|
||||
array([0, 2, 2, 1], dtype=uint16)
|
||||
array([0, 2, 2, 1], dtype=uint16)
|
||||
|
||||
a: array([0, 1, 2, ..., 9, 10, 11], dtype=int16)
|
||||
array([0, 2, 2, 1], dtype=int16)
|
||||
array([0, 2, 2, 1], dtype=int16)
|
||||
|
||||
a: array([0.0, 1.0, 2.0, ..., 9.0, 10.0, 11.0], dtype=float64)
|
||||
array([0.0, 2.0, 2.0, 1.0], dtype=float64)
|
||||
array([0.0, 2.0, 2.0, 1.0], dtype=float64)
|
||||
|
||||
2D arrays
|
||||
|
||||
a: array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=uint8)
|
||||
|
||||
first axis
|
||||
array([[0, 1, 2, 3],
|
||||
[8, 9, 10, 11],
|
||||
[8, 9, 10, 11],
|
||||
[4, 5, 6, 7]], dtype=uint8)
|
||||
|
||||
second axis
|
||||
array([[0, 2, 2, 1],
|
||||
[4, 6, 6, 5],
|
||||
[8, 10, 10, 9]], dtype=uint8)
|
||||
|
||||
a: array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=int8)
|
||||
|
||||
first axis
|
||||
array([[0, 1, 2, 3],
|
||||
[8, 9, 10, 11],
|
||||
[8, 9, 10, 11],
|
||||
[4, 5, 6, 7]], dtype=int8)
|
||||
|
||||
second axis
|
||||
array([[0, 2, 2, 1],
|
||||
[4, 6, 6, 5],
|
||||
[8, 10, 10, 9]], dtype=int8)
|
||||
|
||||
a: array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=uint16)
|
||||
|
||||
first axis
|
||||
array([[0, 1, 2, 3],
|
||||
[8, 9, 10, 11],
|
||||
[8, 9, 10, 11],
|
||||
[4, 5, 6, 7]], dtype=uint16)
|
||||
|
||||
second axis
|
||||
array([[0, 2, 2, 1],
|
||||
[4, 6, 6, 5],
|
||||
[8, 10, 10, 9]], dtype=uint16)
|
||||
|
||||
a: array([[0, 1, 2, 3],
|
||||
[4, 5, 6, 7],
|
||||
[8, 9, 10, 11]], dtype=int16)
|
||||
|
||||
first axis
|
||||
array([[0, 1, 2, 3],
|
||||
[8, 9, 10, 11],
|
||||
[8, 9, 10, 11],
|
||||
[4, 5, 6, 7]], dtype=int16)
|
||||
|
||||
second axis
|
||||
array([[0, 2, 2, 1],
|
||||
[4, 6, 6, 5],
|
||||
[8, 10, 10, 9]], dtype=int16)
|
||||
|
||||
a: array([[0.0, 1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0, 7.0],
|
||||
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
|
||||
|
||||
first axis
|
||||
array([[0.0, 1.0, 2.0, 3.0],
|
||||
[8.0, 9.0, 10.0, 11.0],
|
||||
[8.0, 9.0, 10.0, 11.0],
|
||||
[4.0, 5.0, 6.0, 7.0]], dtype=float64)
|
||||
|
||||
second axis
|
||||
array([[0.0, 2.0, 2.0, 1.0],
|
||||
[4.0, 6.0, 6.0, 5.0],
|
||||
[8.0, 10.0, 10.0, 9.0]], dtype=float64)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import sys
|
||||
from math import *
|
||||
|
||||
try:
|
||||
from ulab import scipy
|
||||
except ImportError:
|
||||
import scipy
|
||||
|
||||
f = lambda x: x * sin(x) * exp(x)
|
||||
a=1
|
||||
b=2
|
||||
|
||||
(res, err) = scipy.integrate.tanhsinh(f, a, b)
|
||||
if isclose (res, 7.11263821415851) and isclose (err, 5.438231077315757e-14):
|
||||
print (res, err)
|
||||
|
||||
res = scipy.integrate.romberg(f, a, b)
|
||||
if isclose (res, 7.112638214158507):
|
||||
print (res)
|
||||
|
||||
res = scipy.integrate.simpson(f, a, b)
|
||||
if isclose (res, 7.112638214158494):
|
||||
print (res)
|
||||
|
||||
(res, err) = scipy.integrate.quad(f, a, b)
|
||||
if isclose (res, 7.112638214158507) and isclose (err, 7.686723611780195e-14):
|
||||
print (res, err)
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
7.11263821415851 5.438231077315757e-14
|
||||
7.112638214158507
|
||||
7.112638214158494
|
||||
7.112638214158507 7.686723611780195e-14
|
||||
Loading…
Reference in a new issue