Compare commits

..

57 commits

Author SHA1 Message Date
Zoltán Vörös
068da5fc96
Modulo (#734)
* add modulo operator

* fix module loops

* add in-place modulo operator

* update readme

* add test files, update documentation
2025-08-05 20:40:40 +02:00
Zoltán Vörös
a0999aba79
add modulo operator (#733)
* add modulo operator

* fix modulo loops

* add in-place modulo operator

* update readme
2025-08-04 22:55:02 +02:00
Zoltán Vörös
8eb8eaf5a1 update changelog; will push directly into master 2025-06-06 21:47:50 +02:00
Zoltán Vörös
66e9eb3ed3
add ndim property (#725) 2025-06-06 21:46:21 +02:00
Zoltán Vörös
844f6e5e1e
fix type inference
* fix type inference

* make checks inclusive for negative values of integers

* move to newer version of ubuntu

* fix iterator macros

* fix iterator macros
2025-06-06 21:29:41 +02:00
Zoltán Vörös
1398a8606f
loadtxt can deal with multi-line comments (#720)
* loadtxt can deal with multi-line comments

* multiline headers/footers are treated correctly

* add macro to traverse arrays
2025-06-06 20:58:31 +02:00
Zoltán Vörös
88ef893540
Random fix (#718)
* fix typo and shape in radnom module

* add random test files

* fix expected file
2025-05-29 19:16:43 +02:00
Zoltán Vörös
825ec2b143
re-name integration constants (#714) 2025-03-16 20:37:00 +01:00
Brian Whitman
66daa8960a
Adding tulipcc to ulab's users (#711) 2025-02-16 19:36:33 +01:00
Zoltán Vörös
20f7259a47
ix keepdims for min, max, argmin, argmax (#707) 2025-01-26 21:04:20 +01:00
Zoltán Vörös
be15d62632
fix keepdims for std, update docs (#704) 2025-01-19 22:51:02 +01:00
Zoltán Vörös
903506ca9a
Add keepdims keyword argument (#701)
* add function to deal with keepdims=True

* preliminary keepdims fix

* fux keepdims code

* remove out-commented code
2025-01-15 10:29:27 +01:00
Harald Milz
73ed8cc11f
Add scipy integration (#699)
* adding scipy integrate, initial check-in

* compile unix double-precision, select integrations algos

* bumping ulab version number to 6.7.0

* adding documentation

* documentation fix

* documentation fix

* documentation fix

* rewritten in some places

* complex number error handling

* added test cases

* resolved importing scipy.integrate

* resolved importing scipy.integrate #2

* build integrate only when we have MICROPY_FLOAT_IMPL_DOUBLE

* reverting commit a4c0c0b

* re-pushing failed commit

* Revert "re-pushing failed commit"

This reverts commit a10e89fe14.

* improve tests using math.isclose()

* enabled fp32 builds

* removed conditional includes

* adapted to new function names, corrected importing

* function names similar to in CPython scipy.integrate, some minor corrections

* major rewrite representing the name changes, mapping to CPython scipy.integrate, more background info
2024-12-15 18:49:08 +01:00
Zoltán Vörös
303e8d790a
fix compilation error for complexes (#694) 2024-11-24 19:56:15 +01:00
Zoltán Vörös
2b74236c8c
Take (#688)
* add numpy.take
2024-10-09 21:10:25 +02:00
Zoltán Vörös
c0b3262be4
Add keyword arguments to spectrogram (#657)
* re-work spectrogram method, so that RAM can be re-used

* update docs with spectrogram changes
2024-09-14 12:18:14 +02:00
Zoltán Vörös
45f23ebc82
Roll fix (#687)
* fix roll, when shift is 0
2024-09-14 11:38:04 +02:00
Dan Halbert
1d3ddd8f52
numpy/random.c: fix use of MICROPY_PY_RANDOM_SEED_INIT_FUNC (#684) 2024-09-09 06:55:05 +02:00
Pablo Martínez
a77022dcd0
add missing typing (#680) 2024-08-25 16:09:47 +02:00
page200
99cb54a426
Correct a[a < 5] in ulab-ndarray.ipynb (#678) 2024-08-06 11:07:32 +02:00
Zoltán Vörös
41c4363f11
address issue raised in https://github.com/v923z/micropython-ulab/issues/676 (#677)
* ndarrays can be created from buffer
2024-07-23 18:59:29 +02:00
Zoltán Vörös
e40a667e3c
Update README.md 2024-07-23 17:20:56 +02:00
Andy Piper
519c48149f
Removed Pycom reference (#675)
UInfortunately Pycom is no longer a thing, removed from the platforms list.
2024-07-21 13:20:18 +02:00
Philip Howard
6fb60ef9cf
CI: Update some deprecated dependency versions (#669)
* CI: Bump various build workflow versions.

* CI: Continue other jobs on failure.

Give a more complete overview of failures, rather than forcing the developer to play whack-a-mole.

* CI: Do not modify unix mpconfigport.h.

Since `MICROPY_PY_UHASHLIB` does not occur in unix/mpconfigport.h this would have simply
duplicated the config file and supplied it as `MP_CONFIGFILE` triggering a bug in how this
define is handled.
2024-06-06 21:54:24 +02:00
Zoltán Vörös
65c941a805
fix loadtxt for the case, when built-in complexes are not supported (#666) 2024-03-06 18:59:32 +01:00
Philip Howard
63dfbd178b
Remove the STATIC macro. (#664)
Reflect the changes proposed in micropython/micropython#13763.
2024-02-29 21:34:50 +01:00
KB Sriram
c49110572d
Update type annotations in compare.c and vector.c (#663)
- Add type annotations for functions in compare.c
- Update annotations in vector.c to match behavior

Fixes https://github.com/v923z/micropython-ulab/issues/662
2024-02-28 20:27:29 +01:00
Zoltán Vörös
acfec3e9af
fix reshape (#660) 2024-02-10 20:46:34 +01:00
Zoltán Vörös
1c37edbee6
add link to random module documentation in readme (#659) 2024-02-10 18:59:06 +01:00
Zoltán Vörös
9a1d03d90d
Update rp2w.sh 2024-01-16 20:55:21 +01:00
Zoltán Vörös
c3d1cbd7c2
add random documentation rst file (#655) 2024-01-13 18:54:01 +01:00
Zoltán Vörös
f2fad82a97
add random module (#654)
* add random module skeleton

* add Generator object

* add placeholder for random.random method

* add rudimentary random.random implementation

* generator object accept seed(s) argument

* add out keyword

* add support for out keyword argument

* update change log

* add links to header files

* fix file link

* fix error messages

* add uniform to random module

* add normal distribution

* fix argument options in normal and uniform

* update documentation
2024-01-13 18:42:43 +01:00
yyyz
7a9370612f
fix the np.delete bug (#653)
* fix the `np.delete` bug

* fix the `np.delete` bug, add unittest code

* increment the version number and update the change log

* update the expected file `delete.py.exp`
2023-12-25 10:56:16 +01:00
sol
e32920645c
Fix (u)int8 upcasting as per docs and numpy (#650)
* fix wrong #if guard in ndarray_inplace_ams

* implement (u)int8 upcasting rules as per documentation

* bump version
2023-12-11 22:34:21 +01:00
4bde4efa9d
Merge pull request #647 from v923z/circuitpython9
Drop certain CircuitPython workarounds that are no longer needed
2023-10-30 10:41:32 +01:00
eacb0c9af4
fix some more translate()s 2023-10-30 09:54:14 +01:00
8c444ef75d
Merge remote-tracking branch 'origin/master' into circuitpython9 2023-10-30 09:53:55 +01:00
9c9e9532ac
Switch to using MP_ERROR_TEXT in CircuitPython, change ulab accordingly 2023-10-30 09:50:39 +01:00
2df210f87a
Drop certain CircuitPython workarounds that are no longer needed
* ndarray_set_value: in CircuitPython 9
 * mp_obj_slice_indices: ditto
 * Use modern MP_REGISTER_MODULE calls: ditto
 * use MP_OBJ_SENTINEL to forward to locals dict (was never necessary?)
2023-09-22 14:47:02 -05:00
Xuebin Ruan
a05ec05351
Fix #643 (#645)
* Fix #643

* Update to version 6.4.1
2023-08-10 10:25:19 +02:00
Zoltán Vörös
5279de73ab
implement AND, OR, XOR binary operators (#639)
* implement AND, OR, XOR binary operators

* fix unterminated if

* add missing linebreak

* add more linebreaks

* remove leading linebreak
2023-07-21 21:57:31 +02:00
KB Sriram
84f99f17fc
numpy/vector.c: remove usage of fpclassify (#636)
Fixes https://github.com/v923z/micropython-ulab/issues/635

Verified by re-compiling circuitpython with this change.
2023-07-17 22:03:30 +02:00
d025aa3cf6
Per github docs, run apt-get update (#637)
https://docs.github.com/en/actions/using-github-hosted-runners/customizing-github-hosted-runners
2023-07-17 10:10:23 +02:00
Zoltán Vörös
d072086c56
allow function iterator in math functions (#633)
* allow function iterator in math functions

* increment version number
2023-07-02 10:02:29 +02:00
38caf84b2d
Fix -Wunused-variable diagnostic when !ULAB_SUPPORTS_COMPLEX (#631)
* Fix -Wunused-variable diagnostic when !ULAB_SUPPORTS_COMPLEX

`o_in` is only used in the SUPPORTS_COMPLEX case, so the variable
definition needs to be moved accordingly.

* update version and changelog
2023-06-28 21:52:04 +02:00
HugoNumworks
112d4f82d3
Polyval handles non-array as second argument (#601)
* Factorize polynomial evaluation

* Polyval handles non-array as second argument

---------

Co-authored-by: Zoltán Vörös <zvoros@gmail.com>
2023-06-27 21:13:53 +02:00
FelixNumworks
319df10cfe
[ndarray] Fix ndarray_from_tuple reading out of _shape->items bounds (#630) 2023-06-26 22:21:50 +02:00
FelixNumworks
26051d70d2
Int overflow (#629)
* Prevent ndarray from overflowing size_t

* Use size_t for polyval array len

* Fix infinite arange

* 6.3.1 version
2023-06-22 14:55:16 +02:00
Derfies
2cde1280a4
Bitwise (#628)
* add bitwise operators

* add build to requirements

* [EDIT] - Tweaked test data and saved test results.

* Tweaked test values for and / or

* [EDIT] - Setting print options to be verbose for test comparisons.

* [EDIT] - Removed call to set_printoptions and added output from ulab instead of numpy. Of note - there seems to be a discrepancy between the numpy and ulab output for one of the left_shift cases.

* [EDIT] - Added newline at end of file for diffing purposes.

* [EDIT] - Added print options back in as output seemed truncated.

---------

Co-authored-by: Zoltán Vörös <zvoros@gmail.com>
Co-authored-by: JamieDouugh <jamie.davies@douugh.com>
2023-06-21 13:46:30 +02:00
Zoltán Vörös
ef248b684d
add bitwise operators (#616)
* add bitwise operators

* add build to requirements
2023-06-20 21:44:58 +02:00
38a4976b58
fix fetching submodules (cp build process change) (#627) 2023-06-20 13:09:01 +02:00
Zoltán Vörös
73dbbf79bb
add the out keyword argument to universal functions (#621)
* add optional out keyword argument to math functions

* fix the keyword handling in sqrt

* run micropython build on ubuntu 20.04 instead of latest

* fix unused variable error in vector_generic_vector
2023-05-28 17:33:24 +02:00
Zoltán Vörös
3e996d9bd9
fix ndarray subscription method (#619) 2023-05-17 20:45:07 +02:00
6619c20b3b
Merge pull request #617 from jepler/sinc-function
ulab.numpy: implement sinc for creating audio filters
2023-05-16 11:47:29 -05:00
f3e6e1c6d2
include new requirement of circuitpython build 2023-05-16 07:33:20 -05:00
6000743c45
fix docstring of sinc 2023-05-16 07:32:25 -05:00
1150554ad5
ulab.numpy: implement sinc for creating audio filters
This is useful for generating FIR filters using code snippets generated at
https://fiiir.com/ (at least those with a rectangular window type; other
window types need additional functions but we can revisit it later if needed)

I think this will come in handy for folks who are using the advanced
features of our audio synthesizer module, synthio.

e.g., the following block now gives highly similar results on ulab
or numpy:

```py
try:
    import numpy as np
except:
    from ulab import numpy as np

# Example code, computes the coefficients of a low-pass windowed-sinc filter.

# Configuration.
fS = 48000  # Sampling rate.
fL = 4000  # Cutoff frequency.
N = 23  # Filter length, must be odd.

# Compute sinc filter.
h = np.sinc(2 * fL / fS * (np.arange(N) - (N - 1) / 2))

# Normalize to get unity gain.
h /= np.sum(h)

# Applying the filter to a signal s can be as simple as writing
# s = np.convolve(s, h)
2023-05-15 18:00:59 -05:00
103 changed files with 7294 additions and 1821 deletions

View file

@ -16,10 +16,11 @@ on:
jobs:
micropython:
continue-on-error: true
strategy:
matrix:
os:
- ubuntu-latest
- ubuntu-24.04
- macOS-latest
dims: [1, 2, 3, 4]
runs-on: ${{ matrix.os }}
@ -28,15 +29,15 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Set up Python 3.10
uses: actions/setup-python@v1
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
- name: Install requirements
run: |
if type -path apt-get; then
sudo apt-get install gcc-multilib
sudo apt update && sudo apt-get install gcc-multilib
fi
- name: Versions
@ -44,10 +45,10 @@ jobs:
gcc --version
python3 --version
- name: Checkout ulab
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Checkout micropython repo
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
repository: micropython/micropython
path: micropython
@ -56,10 +57,11 @@ jobs:
run: ./build.sh ${{ matrix.dims }}
circuitpython:
continue-on-error: true
strategy:
matrix:
os:
- ubuntu-20.04
- ubuntu-24.04
- macOS-latest
dims: [1, 2, 3, 4]
runs-on: ${{ matrix.os }}
@ -68,10 +70,10 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Set up Python 3.10
uses: actions/setup-python@v1
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
- name: Versions
run: |
@ -79,12 +81,12 @@ jobs:
python3 --version
- name: Checkout ulab
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Install requirements
run: |
if type -path apt-get; then
sudo apt-get install gettext librsvg2-bin
sudo apt update && sudo apt-get install gettext librsvg2-bin
else
brew install gettext librsvg
echo >>$GITHUB_PATH /usr/local/opt/gettext/bin

View file

@ -1,6 +1,6 @@
# ulab
[![Documentation Status](https://readthedocs.org/projects/micropython-ulab-robert/badge/?version=latest)](https://micropython-ulab-robert.readthedocs.io/en/latest/?badge=latest)
[![Documentation Status](https://readthedocs.org/projects/micropython-ulab/badge/?version=latest)](https://micropython-ulab.readthedocs.io/en/latest/index.html)
`ulab` is a `numpy`-like array manipulation library for [micropython](http://micropython.org/) and [CircuitPython](https://circuitpython.org/).
The module is written in C, defines compact containers (`ndarray`s) for numerical data of one to four
@ -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), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest).
In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`numpy.random`](https://micropython-ulab.readthedocs.io/en/latest/numpy-random.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest).
## `ulab` utilities
@ -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
3. `pycom` https://pycom.io/
1. `Tulip Creative Computer` https://github.com/shorepine/tulipcc
## Compiling

View file

@ -37,12 +37,11 @@ readlinkf_posix() {
}
NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())')
HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )"
[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-submodules && git submodule update --init lib/uzlib tools)
[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-all-submodules && git submodule update --init lib/uzlib tools)
rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab
dims=${1-2}
make -C circuitpython/mpy-cross -j$NPROC
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
make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims
# bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims"

View file

@ -1,6 +1,6 @@
#!/bin/bash
export BOARD=PICO_W
export BOARD=RPI_PICO_W
export BUILD_DIR=$(pwd)
export MPY_DIR=$BUILD_DIR/micropython
export ULAB_DIR=$BUILD_DIR/../code

View file

@ -2,6 +2,7 @@
USERMODULES_DIR := $(USERMOD_DIR)
# Add all C files to SRC_USERMOD.
SRC_USERMOD += $(USERMODULES_DIR)/scipy/integrate/integrate.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/linalg/linalg.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/optimize/optimize.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/signal/signal.c
@ -12,6 +13,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/ndarray.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/ndarray/ndarray_iter.c
SRC_USERMOD += $(USERMODULES_DIR)/ndarray_properties.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/approx.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/bitwise.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/compare.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c
@ -24,6 +26,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg_tools.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numerical.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/poly.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/random/random.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/stats.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c

View file

@ -6,7 +6,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2022 Zoltán Vörös
* Copyright (c) 2019-2024 Zoltán Vörös
* 2020 Jeff Epler for Adafruit Industries
* 2020 Taku Fukada
*/
@ -61,98 +61,6 @@ void ndarray_set_complex_value(void *p, size_t index, mp_obj_t value) {
}
}
#ifdef CIRCUITPY
void ndarray_set_value(char typecode, void *p, size_t index, mp_obj_t val_in) {
switch (typecode) {
case NDARRAY_INT8:
((signed char *)p)[index] = mp_obj_get_int(val_in);
break;
case NDARRAY_UINT8:
((unsigned char *)p)[index] = mp_obj_get_int(val_in);
break;
case NDARRAY_INT16:
((short *)p)[index] = mp_obj_get_int(val_in);
break;
case NDARRAY_UINT16:
((unsigned short *)p)[index] = mp_obj_get_int(val_in);
break;
case NDARRAY_FLOAT:
((mp_float_t *)p)[index] = mp_obj_get_float(val_in);
break;
#if ULAB_SUPPORTS_COMPLEX
case NDARRAY_COMPLEX:
ndarray_set_complex_value(p, index, val_in);
break;
#endif
}
}
#endif
#if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 11
void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result) {
mp_obj_slice_t *self = MP_OBJ_TO_PTR(self_in);
mp_int_t start, stop, step;
if (self->step == mp_const_none) {
step = 1;
} else {
step = mp_obj_get_int(self->step);
if (step == 0) {
mp_raise_ValueError(translate("slice step can't be zero"));
}
}
if (step > 0) {
// Positive step
if (self->start == mp_const_none) {
start = 0;
} else {
start = mp_obj_get_int(self->start);
if (start < 0) {
start += length;
}
start = MIN(length, MAX(start, 0));
}
if (self->stop == mp_const_none) {
stop = length;
} else {
stop = mp_obj_get_int(self->stop);
if (stop < 0) {
stop += length;
}
stop = MIN(length, MAX(stop, 0));
}
} else {
// Negative step
if (self->start == mp_const_none) {
start = length - 1;
} else {
start = mp_obj_get_int(self->start);
if (start < 0) {
start += length;
}
start = MIN(length - 1, MAX(start, -1));
}
if (self->stop == mp_const_none) {
stop = -1;
} else {
stop = mp_obj_get_int(self->stop);
if (stop < 0) {
stop += length;
}
stop = MIN(length - 1, MAX(stop, -1));
}
}
result->start = start;
result->stop = stop;
result->step = step;
}
#endif /* MICROPY_VERSION v1.11 */
void ndarray_fill_array_iterable(mp_float_t *array, mp_obj_t iterable) {
mp_obj_iter_buf_t x_buf;
mp_obj_t x_item, x_iterable = mp_getiter(iterable, &x_buf);
@ -291,7 +199,7 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t
if((_dtype != NDARRAY_BOOL) && (_dtype != NDARRAY_UINT8)
&& (_dtype != NDARRAY_INT8) && (_dtype != NDARRAY_UINT16)
&& (_dtype != NDARRAY_INT16) && (_dtype != NDARRAY_FLOAT)) {
mp_raise_TypeError(translate("data type not understood"));
mp_raise_TypeError(MP_ERROR_TEXT("data type not understood"));
}
} else {
GET_STR_DATA_LEN(_args[0].u_obj, _dtype_, len);
@ -312,7 +220,7 @@ mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t
}
#endif
else {
mp_raise_TypeError(translate("data type not understood"));
mp_raise_TypeError(MP_ERROR_TEXT("data type not understood"));
}
}
dtype->dtype = _dtype;
@ -344,7 +252,7 @@ mp_obj_t ndarray_dtype(mp_obj_t self_in) {
&& (*_dtype != NDARRAY_COMPLEX)
#endif
)) {
mp_raise_TypeError(translate("data type not understood"));
mp_raise_TypeError(MP_ERROR_TEXT("data type not understood"));
}
dtype = *_dtype;
}
@ -596,13 +504,14 @@ bool ndarray_is_dense(ndarray_obj_t *ndarray) {
static size_t multiply_size(size_t a, size_t b) {
size_t result;
if (__builtin_mul_overflow(a, b, &result)) {
mp_raise_ValueError(translate("array is too big"));
mp_raise_ValueError(MP_ERROR_TEXT("array is too big"));
}
return result;
}
ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype) {
ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype, uint8_t *buffer) {
// Creates the base ndarray with shape, and initialises the values to straight 0s
// optionally, values can be supplied via the last argument
ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t);
ndarray->base.type = &ulab_ndarray_type;
ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype;
@ -622,11 +531,19 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides
ndarray->len = multiply_size(ndarray->len, shape[i-1]);
}
if (SIZE_MAX / ndarray->itemsize <= ndarray->len) {
mp_raise_ValueError(MP_ERROR_TEXT("ndarray length overflows"));
}
// if the length is 0, still allocate a single item, so that contractions can be handled
size_t len = multiply_size(ndarray->itemsize, MAX(1, ndarray->len));
uint8_t *array = m_new0(byte, len);
// this should set all elements to 0, irrespective of the of the dtype (all bits are zero)
// we could, perhaps, leave this step out, and initialise the array only, when needed
uint8_t *array;
array = buffer;
if(array == NULL) {
// this should set all elements to 0, irrespective of the of the dtype (all bits are zero)
// we could, perhaps, leave this step out, and initialise the array only, when needed
array = m_new0(byte, len);
}
ndarray->array = array;
ndarray->origin = array;
return ndarray;
@ -635,24 +552,23 @@ ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides
ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dtype) {
// creates a dense array, i.e., one, where the strides are derived directly from the shapes
// the function should work in the general n-dimensional case
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
strides[ULAB_MAX_DIMS-1] = (int32_t)ulab_binary_get_size(dtype);
for(size_t i=ULAB_MAX_DIMS; i > 1; i--) {
strides[i-2] = strides[i-1] * MAX(1, shape[i-1]);
}
return ndarray_new_ndarray(ndim, shape, strides, dtype);
// int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
// strides[ULAB_MAX_DIMS - 1] = (int32_t)ulab_binary_get_size(dtype);
// for(size_t i = ULAB_MAX_DIMS; i > 1; i--) {
// strides[i-2] = strides[i-1] * MAX(1, shape[i-1]);
// }
return ndarray_new_ndarray(ndim, shape, NULL, dtype, NULL);
}
ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dtype) {
// creates a dense array from a tuple
// the function should work in the general n-dimensional case
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
for(size_t i=0; i < ULAB_MAX_DIMS; i++) {
if(i < ULAB_MAX_DIMS - _shape->len) {
shape[i] = 0;
} else {
shape[i] = mp_obj_get_int(_shape->items[i]);
}
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]);
}
return ndarray_new_dense_ndarray(_shape->len, shape, dtype);
}
@ -670,43 +586,10 @@ void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target, uint8_t sh
}
#endif
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
memcpy(tarray, sarray, target->itemsize);
tarray += target->itemsize;
sarray += source->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
sarray += source->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
sarray += source->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
sarray += source->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
#endif
ITERATOR_HEAD();
memcpy(tarray, sarray, target->itemsize);
tarray += target->itemsize;
ITERATOR_TAIL(source, sarray);
}
ndarray_obj_t *ndarray_new_view(ndarray_obj_t *source, uint8_t ndim, size_t *shape, int32_t *strides, int32_t offset) {
@ -742,7 +625,7 @@ ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) {
if(source->boolean) {
dtype = NDARRAY_BOOL;
}
ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype);
ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype, NULL);
ndarray_copy_array(source, ndarray, 0);
return ndarray;
}
@ -760,69 +643,36 @@ ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *source, uint8_t dty
uint8_t complex_size = 2 * sizeof(mp_float_t);
#endif
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
ITERATOR_HEAD()
mp_obj_t item;
#if ULAB_SUPPORTS_COMPLEX
if(source->dtype == NDARRAY_COMPLEX) {
if(dtype != NDARRAY_COMPLEX) {
mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex type"));
} else {
memcpy(array, sarray, complex_size);
}
} else {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
mp_obj_t item;
#if ULAB_SUPPORTS_COMPLEX
if(source->dtype == NDARRAY_COMPLEX) {
if(dtype != NDARRAY_COMPLEX) {
mp_raise_TypeError(translate("cannot convert complex type"));
} else {
memcpy(array, sarray, complex_size);
}
} else {
#endif
if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) {
// floats must be treated separately, because they can't directly be converted to integer types
mp_float_t f = ndarray_get_float_value(sarray, source->dtype);
item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(f));
} else {
item = mp_binary_get_val_array(source->dtype, sarray, 0);
}
#if ULAB_SUPPORTS_COMPLEX
if(dtype == NDARRAY_COMPLEX) {
ndarray_set_value(NDARRAY_FLOAT, array, 0, item);
} else {
ndarray_set_value(dtype, array, 0, item);
}
}
#else
ndarray_set_value(dtype, array, 0, item);
#endif
array += ndarray->itemsize;
sarray += source->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
sarray += source->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
sarray += source->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) {
// floats must be treated separately, because they can't directly be converted to integer types
mp_float_t f = ndarray_get_float_value(sarray, source->dtype);
item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(f));
} else {
item = mp_binary_get_val_array(source->dtype, sarray, 0);
}
#if ULAB_SUPPORTS_COMPLEX
if(dtype == NDARRAY_COMPLEX) {
ndarray_set_value(NDARRAY_FLOAT, array, 0, item);
} else {
ndarray_set_value(dtype, array, 0, item);
}
}
#else
ndarray_set_value(dtype, array, 0, item);
#endif
#if ULAB_MAX_DIMS > 3
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
sarray += source->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
#endif
array += ndarray->itemsize;
ITERATOR_TAIL(source, sarray);
return ndarray;
}
@ -849,54 +699,21 @@ mp_obj_t ndarray_byteswap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
return MP_OBJ_FROM_PTR(ndarray);
} else {
uint8_t *array = (uint8_t *)ndarray->array;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
ITERATOR_HEAD();
if(self->dtype == NDARRAY_FLOAT) {
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
SWAP(uint8_t, array[0], array[3]);
SWAP(uint8_t, array[1], array[2]);
#else
SWAP(uint8_t, array[0], array[7]);
SWAP(uint8_t, array[1], array[6]);
SWAP(uint8_t, array[2], array[5]);
SWAP(uint8_t, array[3], array[4]);
#endif
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
} else {
SWAP(uint8_t, array[0], array[1]);
}
ITERATOR_TAIL(ndarray, array);
}
return MP_OBJ_FROM_PTR(ndarray);
}
@ -944,7 +761,7 @@ ndarray_obj_t *ndarray_from_iterable(mp_obj_t obj, uint8_t dtype) {
break;
}
if(ndim == ULAB_MAX_DIMS) {
mp_raise_ValueError(translate("too many dimensions"));
mp_raise_ValueError(MP_ERROR_TEXT("too many dimensions"));
}
shape[ndim] = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(item));
if(shape[ndim] == 0) {
@ -1009,7 +826,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) } },
@ -1032,7 +849,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)) {
@ -1134,13 +951,13 @@ static mp_bound_slice_t generate_slice(mp_int_t n, mp_obj_t index) {
_index += n;
}
if((_index >= n) || (_index < 0)) {
mp_raise_msg(&mp_type_IndexError, translate("index is out of bounds"));
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("index is out of bounds"));
}
slice.start = _index;
slice.stop = _index + 1;
slice.step = 1;
} else {
mp_raise_msg(&mp_type_IndexError, translate("indices must be integers, slices, or Boolean lists"));
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("indices must be integers, slices, or Boolean lists"));
}
return slice;
}
@ -1166,7 +983,7 @@ static ndarray_obj_t *ndarray_view_from_slices(ndarray_obj_t *ndarray, mp_obj_tu
k += ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i];
}
if((k >= (int32_t)ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i]) || (k < 0)) {
mp_raise_msg(&mp_type_IndexError, translate("index is out of bounds"));
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("index is out of bounds"));
}
offset += ndarray->strides[ULAB_MAX_DIMS - ndarray->ndim + i] * k;
// ... and then we have to shift the shapes to the right
@ -1193,7 +1010,7 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) {
int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS);
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
if(!ndarray_can_broadcast(view, values, &ndim, shape, lstrides, rstrides)) {
mp_raise_ValueError(translate("operands could not be broadcast together"));
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
} else {
ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(values, view->dtype);
@ -1259,7 +1076,7 @@ void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) {
static mp_obj_t ndarray_from_boolean_index(ndarray_obj_t *ndarray, ndarray_obj_t *index) {
// returns a 1D array, indexed by a Boolean array
if(ndarray->len != index->len) {
mp_raise_ValueError(translate("array and index length must be equal"));
mp_raise_ValueError(MP_ERROR_TEXT("array and index length must be equal"));
}
uint8_t *iarray = (uint8_t *)index->array;
// first we have to find out how many trues there are
@ -1311,7 +1128,7 @@ static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarra
#if ULAB_SUPPORTS_COMPLEX
if(values->dtype == NDARRAY_COMPLEX) {
if(ndarray->dtype != NDARRAY_COMPLEX) {
mp_raise_TypeError(translate("cannot convert complex to dtype"));
mp_raise_TypeError(MP_ERROR_TEXT("cannot convert complex to dtype"));
} else {
uint8_t *array = (uint8_t *)ndarray->array;
for(size_t i = 0; i < ndarray->len; i++) {
@ -1402,7 +1219,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra
if(mp_obj_is_type(index, &ulab_ndarray_type)) {
ndarray_obj_t *nindex = MP_OBJ_TO_PTR(index);
if((nindex->ndim > 1) || (nindex->boolean == false)) {
mp_raise_NotImplementedError(translate("operation is implemented for 1D Boolean arrays only"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("operation is implemented for 1D Boolean arrays only"));
}
if(values == NULL) { // return value(s)
return ndarray_from_boolean_index(ndarray, nindex);
@ -1415,7 +1232,7 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra
if(mp_obj_is_type(index, &mp_type_tuple)) {
tuple = MP_OBJ_TO_PTR(index);
if(tuple->len > ndarray->ndim) {
mp_raise_msg(&mp_type_IndexError, translate("too many indices"));
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("too many indices"));
}
} else {
mp_obj_t *items = m_new(mp_obj_t, 1);
@ -1438,6 +1255,9 @@ static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarra
}
mp_obj_t ndarray_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
if(value == MP_OBJ_NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("cannot delete array elements"));
}
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (value == MP_OBJ_SENTINEL) { // return value(s)
@ -1514,7 +1334,7 @@ mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
ndarray_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
GET_STR_DATA_LEN(args[0].u_obj, order, len);
if((len != 1) || ((memcmp(order, "C", 1) != 0) && (memcmp(order, "F", 1) != 0))) {
mp_raise_ValueError(translate("flattening order must be either 'C', or 'F'"));
mp_raise_ValueError(MP_ERROR_TEXT("flattening order must be either 'C', or 'F'"));
}
uint8_t *sarray = (uint8_t *)self->array;
@ -1522,43 +1342,10 @@ mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
uint8_t *array = (uint8_t *)ndarray->array;
if(memcmp(order, "C", 1) == 0) { // C-type ordering
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
memcpy(array, sarray, self->itemsize);
array += ndarray->strides[ULAB_MAX_DIMS - 1];
sarray += self->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < self->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
sarray -= self->strides[ULAB_MAX_DIMS - 1] * self->shape[ULAB_MAX_DIMS-1];
sarray += self->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < self->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
sarray -= self->strides[ULAB_MAX_DIMS - 2] * self->shape[ULAB_MAX_DIMS-2];
sarray += self->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < self->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
sarray -= self->strides[ULAB_MAX_DIMS - 3] * self->shape[ULAB_MAX_DIMS-3];
sarray += self->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < self->shape[ULAB_MAX_DIMS - 4]);
#endif
ITERATOR_HEAD();
memcpy(array, sarray, self->itemsize);
array += ndarray->strides[ULAB_MAX_DIMS - 1];
ITERATOR_TAIL(self, sarray);
} else { // 'F', Fortran-type ordering
#if ULAB_MAX_DIMS > 3
size_t i = 0;
@ -1611,6 +1398,13 @@ mp_obj_t ndarray_itemsize(mp_obj_t self_in) {
}
#endif
#if NDARRAY_HAS_NDIM
mp_obj_t ndarray_ndim(mp_obj_t self_in) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(self->ndim);
}
#endif
#if NDARRAY_HAS_SHAPE
mp_obj_t ndarray_shape(mp_obj_t self_in) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -1652,7 +1446,7 @@ mp_obj_t ndarray_tobytes(mp_obj_t self_in) {
// Piping into a bytearray makes sense for dense arrays only,
// so bail out, if that is not the case
if(!ndarray_is_dense(self)) {
mp_raise_ValueError(translate("tobytes can be invoked for dense arrays only"));
mp_raise_ValueError(MP_ERROR_TEXT("tobytes can be invoked for dense arrays only"));
}
return mp_obj_new_bytearray_by_ref(self->itemsize * self->len, self->array);
}
@ -1695,7 +1489,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) {
if(mp_obj_is_int(obj)) {
int32_t ivalue = mp_obj_get_int(obj);
if((ivalue < -32767) || (ivalue > 32767)) {
if((ivalue < -32768) || (ivalue > 65535)) {
// the integer value clearly does not fit the ulab integer types, so move on to float
ndarray = ndarray_new_linear_array(1, NDARRAY_FLOAT);
mp_float_t *array = (mp_float_t *)ndarray->array;
@ -1703,7 +1497,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;
@ -1731,7 +1525,7 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) {
mp_float_t *array = (mp_float_t *)ndarray->array;
array[0] = mp_obj_get_float(obj);
} else if(mp_obj_is_bool(obj)) {
ndarray = ndarray_new_linear_array(1, NDARRAY_BOOLEAN);
ndarray = ndarray_new_linear_array(1, NDARRAY_BOOL);
uint8_t *array = (uint8_t *)ndarray->array;
if(obj == mp_const_true) {
*array = 1;
@ -1792,7 +1586,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) {
broadcastable = ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides);
}
if(!broadcastable) {
mp_raise_ValueError(translate("operands could not be broadcast together"));
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
@ -1854,6 +1648,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) {
return ndarray_inplace_ams(lhs, rhs, rstrides, op);
break;
#endif
#if NDARRAY_HAS_INPLACE_MODULO
case MP_BINARY_OP_INPLACE_MODULO:
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
return ndarray_inplace_modulo(lhs, rhs, rstrides);
break;
#endif
#if NDARRAY_HAS_INPLACE_MULTIPLY
case MP_BINARY_OP_INPLACE_MULTIPLY:
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
@ -1909,6 +1709,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) {
return ndarray_binary_add(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if NDARRAY_HAS_BINARY_OP_MODULO
case MP_BINARY_OP_MODULO:
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
return ndarray_binary_modulo(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if NDARRAY_HAS_BINARY_OP_MULTIPLY
case MP_BINARY_OP_MULTIPLY:
return ndarray_binary_multiply(lhs, rhs, ndim, shape, lstrides, rstrides);
@ -1942,6 +1748,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) {
return ndarray_binary_power(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if NDARRAY_HAS_BINARY_OP_OR | NDARRAY_HAS_BINARY_OP_XOR | NDARRAY_HAS_BINARY_OP_AND
case MP_BINARY_OP_OR:
case MP_BINARY_OP_XOR:
case MP_BINARY_OP_AND:
return ndarray_binary_logical(lhs, rhs, ndim, shape, lstrides, rstrides, op);
#endif
#if NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE
case MP_BINARY_OP_FLOOR_DIVIDE:
COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype);
@ -1967,7 +1779,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
#if ULAB_SUPPORTS_COMPLEX
if(self->dtype == NDARRAY_COMPLEX) {
int32_t *strides = strides_from_shape(self->shape, NDARRAY_FLOAT);
ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT);
ndarray_obj_t *target = ndarray_new_ndarray(self->ndim, self->shape, strides, NDARRAY_FLOAT, NULL);
ndarray = MP_OBJ_TO_PTR(carray_abs(self, target));
} else {
#endif
@ -2002,7 +1814,7 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
#else
if(self->dtype == NDARRAY_FLOAT) {
#endif
mp_raise_ValueError(translate("operation is not supported for given type"));
mp_raise_ValueError(MP_ERROR_TEXT("operation is not supported for given type"));
}
// we can invert the content byte by byte, no need to distinguish between different dtypes
ndarray = ndarray_copy_view(self); // from this point, this is a dense copy
@ -2091,7 +1903,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_transpose_obj, ndarray_transpose);
mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(oin);
if(!mp_obj_is_type(_shape, &mp_type_tuple) && !mp_obj_is_int(_shape)) {
mp_raise_TypeError(translate("shape must be integer or tuple of integers"));
mp_raise_TypeError(MP_ERROR_TEXT("shape must be integer or tuple of integers"));
}
mp_obj_tuple_t *shape;
@ -2100,12 +1912,12 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) {
mp_obj_t *items = m_new(mp_obj_t, 1);
items[0] = _shape;
shape = mp_obj_new_tuple(1, items);
} else {
} else { // at this point it's certain that _shape is a tuple
shape = MP_OBJ_TO_PTR(_shape);
}
if(shape->len > ULAB_MAX_DIMS) {
mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS)));
mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS)));
}
size_t new_length = 1;
@ -2125,14 +1937,14 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) {
}
if(unknown_dim > 1) {
mp_raise_ValueError(translate("can only specify one unknown dimension"));
mp_raise_ValueError(MP_ERROR_TEXT("can only specify one unknown dimension"));
} else if(unknown_dim == 1) {
new_shape[unknown_index] = source->len / new_length;
new_length = source->len;
}
if(source->len != new_length) {
mp_raise_ValueError(translate("cannot reshape array"));
mp_raise_ValueError(MP_ERROR_TEXT("cannot reshape array"));
}
ndarray_obj_t *ndarray;
@ -2149,13 +1961,9 @@ mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) {
}
} else {
if(inplace) {
mp_raise_ValueError(translate("cannot assign new shape"));
}
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);
mp_raise_ValueError(MP_ERROR_TEXT("cannot assign new shape"));
}
ndarray = ndarray_new_dense_ndarray(shape->len, new_shape, source->dtype);
ndarray_copy_array(source, ndarray, 0);
}
return MP_OBJ_FROM_PTR(ndarray);
@ -2172,7 +1980,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(ndarray_reshape_obj, ndarray_reshape);
#if ULAB_NUMPY_HAS_NDINFO
mp_obj_t ndarray_info(mp_obj_t obj_in) {
if(!mp_obj_is_type(obj_in, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("function is defined for ndarrays only"));
mp_raise_TypeError(MP_ERROR_TEXT("function is defined for ndarrays only"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj_in);
mp_printf(MP_PYTHON_PRINTER, "class: ndarray\n");

View file

@ -40,7 +40,7 @@
// Constant float objects are a struct in ROM and are referenced via their pointer.
// Use ULAB_DEFINE_FLOAT_CONST to define a constant float object.
// id is the name of the constant, num is it's floating point value.
// id is the name of the constant, num is its floating point value.
// hex32 is computed as: hex(int.from_bytes(array.array('f', [num]), 'little'))
// hex64 is computed as: hex(int.from_bytes(array.array('d', [num]), 'little'))
@ -111,13 +111,7 @@ typedef struct _mp_obj_slice_t {
#endif
#endif
#if !CIRCUITPY
#define translate(x) MP_ERROR_TEXT(x)
#define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d)
#else
void ndarray_set_value(char , void *, size_t , mp_obj_t );
#endif
void ndarray_set_complex_value(void *, size_t , mp_obj_t );
#define NDARRAY_NUMERIC 0
@ -194,7 +188,7 @@ int32_t *ndarray_contract_strides(ndarray_obj_t *, uint8_t );
ndarray_obj_t *ndarray_from_iterable(mp_obj_t , uint8_t );
ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t , size_t *, uint8_t );
ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *, uint8_t );
ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t );
ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t , uint8_t *);
ndarray_obj_t *ndarray_new_linear_array(size_t , uint8_t );
ndarray_obj_t *ndarray_new_view(ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t );
bool ndarray_is_dense(ndarray_obj_t *);
@ -238,6 +232,10 @@ mp_obj_t ndarray_dtype(mp_obj_t );
mp_obj_t ndarray_itemsize(mp_obj_t );
#endif
#if NDARRAY_HAS_NDIM
mp_obj_t ndarray_ndim(mp_obj_t );
#endif
#if NDARRAY_HAS_SIZE
mp_obj_t ndarray_size(mp_obj_t );
#endif
@ -715,4 +713,89 @@ ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t );
#endif /* ULAB_MAX_DIMS == 4 */
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
// iterator macro for traversing arrays over all dimensions
#if ULAB_MAX_DIMS == 1
#define ITERATOR_HEAD()\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define ITERATOR_HEAD()\
size_t _k_ = 0;\
do {\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
_k_++;\
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define ITERATOR_HEAD()\
size_t _j_ = 0;\
do {\
size_t _k_ = 0;\
do {\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
_k_++;\
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
_j_++;\
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define ITERATOR_HEAD()\
size_t _i_ = 0;\
do {\
size_t _j_ = 0;\
do {\
size_t _k_ = 0;\
do {\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
_k_++;\
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
_j_++;\
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 3] * (_source_)->shape[ULAB_MAX_DIMS - 3];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 4];\
_i_++;\
} while(_i_ < (_source_)->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS == 4 */
#endif

View file

@ -181,8 +181,8 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, +);
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, +);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, +);
@ -248,6 +248,105 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
}
#endif /* NDARRAY_HAS_BINARY_OP_ADD */
#if NDARRAY_HAS_BINARY_OP_MODULO
mp_obj_t ndarray_binary_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_FLOAT) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_FLOAT) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
BINARY_LOOP(results, mp_float_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_FLOAT) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
BINARY_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, %);
} else if(rhs->dtype == NDARRAY_FLOAT) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides);
}
} else if(lhs->dtype == NDARRAY_FLOAT) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides);
} else if(rhs->dtype == NDARRAY_FLOAT) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides);
}
}
return MP_OBJ_FROM_PTR(results);
}
#endif /* NDARRAY_HAS_BINARY_OP_MODULO */
#if NDARRAY_HAS_BINARY_OP_MULTIPLY
mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
@ -264,8 +363,8 @@ mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, *);
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, *);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, *);
@ -857,11 +956,199 @@ mp_obj_t ndarray_binary_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
}
#endif /* NDARRAY_HAS_BINARY_OP_POWER */
#if NDARRAY_HAS_BINARY_OP_OR | NDARRAY_HAS_BINARY_OP_XOR | NDARRAY_HAS_BINARY_OP_AND
mp_obj_t ndarray_binary_logical(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) {
#if ULAB_SUPPORTS_COMPLEX
if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX) || (lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) {
mp_raise_TypeError(MP_ERROR_TEXT("operation not supported for the input types"));
}
#else
if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) {
mp_raise_TypeError(MP_ERROR_TEXT("operation not supported for the input types"));
}
#endif
// bail out, if both inputs are of 16-bit types, but differ in sign;
// numpy promotes the result to int32
if(((lhs->dtype == NDARRAY_INT16) && (rhs->dtype == NDARRAY_UINT16)) ||
((lhs->dtype == NDARRAY_UINT16) && (rhs->dtype == NDARRAY_INT16))) {
mp_raise_TypeError(MP_ERROR_TEXT("dtype of int32 is not supported"));
}
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
switch(op) {
case MP_BINARY_OP_XOR:
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
if(lhs->boolean & rhs->boolean) {
results->boolean = 1;
}
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ^);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ^);
} else {
return ndarray_binary_op(MP_BINARY_OP_XOR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ^);
} else {
return ndarray_binary_op(MP_BINARY_OP_XOR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ^);
} else {
return ndarray_binary_op(MP_BINARY_OP_XOR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
}
break;
case MP_BINARY_OP_OR:
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
if(lhs->boolean & rhs->boolean) {
results->boolean = 1;
}
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, |);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, |);
} else {
return ndarray_binary_op(MP_BINARY_OP_OR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, |);
} else {
return ndarray_binary_op(MP_BINARY_OP_OR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, |);
} else {
return ndarray_binary_op(MP_BINARY_OP_OR, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
}
break;
case MP_BINARY_OP_AND:
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
if(lhs->boolean & rhs->boolean) {
results->boolean = 1;
}
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, &);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, &);
} else {
return ndarray_binary_op(MP_BINARY_OP_AND, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, &);
} else {
return ndarray_binary_op(MP_BINARY_OP_AND, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, &);
} else {
return ndarray_binary_op(MP_BINARY_OP_AND, MP_OBJ_FROM_PTR(rhs), MP_OBJ_FROM_PTR(lhs));
}
}
break;
default:
return MP_OBJ_NULL; // op not supported
break;
}
return MP_OBJ_FROM_PTR(results);
}
#endif /* NDARRAY_HAS_BINARY_OP_OR | NDARRAY_HAS_BINARY_OP_XOR | NDARRAY_HAS_BINARY_OP_AND */
#if NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT
mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides, uint8_t optype) {
if((lhs->dtype != NDARRAY_FLOAT) && (rhs->dtype == NDARRAY_FLOAT)) {
mp_raise_TypeError(translate("cannot cast output with casting rule"));
mp_raise_TypeError(MP_ERROR_TEXT("cannot cast output with casting rule"));
}
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
@ -871,7 +1158,7 @@ mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rs
UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, +=);
}
#endif
#if NDARRAY_HAS_INPLACE_ADD
#if NDARRAY_HAS_INPLACE_MULTIPLY
if(optype == MP_BINARY_OP_INPLACE_MULTIPLY) {
UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, *=);
}
@ -886,11 +1173,34 @@ 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) {
if((lhs->dtype != NDARRAY_FLOAT)) {
mp_raise_TypeError(translate("results cannot be cast to specified type"));
mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type"));
}
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
@ -914,7 +1224,7 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t
mp_obj_t ndarray_inplace_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) {
if((lhs->dtype != NDARRAY_FLOAT)) {
mp_raise_TypeError(translate("results cannot be cast to specified type"));
mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type"));
}
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;

View file

@ -12,14 +12,17 @@
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 *);
mp_obj_t ndarray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t );
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 *);
@ -536,3 +539,176 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#define MODULO_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
({\
size_t l = 0;\
do {\
*(array)++ = MICROPY_FLOAT_C_FUN(fmod)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#if ULAB_MAX_DIMS == 1
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t l = 0;\
do {\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#define INPLACE_MODULO_FLOAT1(results, type_right, larray, rarray, rstrides)\
({\
size_t l = 0;\
do {\
*((mp_float_t *)larray) = MICROPY_FLOAT_C_FUN(fmod)(*((mp_float_t *)(larray)), *((type_right *)(rarray)));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#if ULAB_MAX_DIMS == 1
#define INPLACE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
size_t l = 0;\
do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */

View file

@ -6,7 +6,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Zoltán Vörös
* Copyright (c) 2021-2025 Zoltán Vörös
*
*/
@ -24,29 +24,6 @@
#include "numpy/carray/carray.h"
#endif
#ifndef CIRCUITPY
// a somewhat hackish implementation of property getters/setters;
// this functions is hooked into the attr member of ndarray
STATIC void call_local_method(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
const mp_obj_type_t *type = mp_obj_get_type(obj);
while (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) {
assert(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->base.type == &mp_type_dict); // MicroPython restriction, for now
mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map;
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
if (elem != NULL) {
mp_convert_member_lookup(obj, type, elem->value, dest);
break;
}
if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) {
break;
}
type = MP_OBJ_TYPE_GET_SLOT(type, parent);
}
}
void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
if (dest[0] == MP_OBJ_NULL) {
switch(attr) {
@ -65,6 +42,11 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
dest[0] = ndarray_itemsize(self_in);
break;
#endif
#if NDARRAY_HAS_NDIM
case MP_QSTR_ndim:
dest[0] = ndarray_ndim(self_in);
break;
#endif
#if NDARRAY_HAS_SHAPE
case MP_QSTR_shape:
dest[0] = ndarray_shape(self_in);
@ -98,7 +80,8 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
#endif
#endif /* ULAB_SUPPORTS_COMPLEX */
default:
call_local_method(self_in, attr, dest);
// forward to locals dict
dest[1] = MP_OBJ_SENTINEL;
break;
}
} else {
@ -119,5 +102,3 @@ void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
}
}
}
#endif /* CIRCUITPY */

View file

@ -7,7 +7,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
* 2020-2021 Zoltán Vörös
* 2020-2025 Zoltán Vörös
*/
#ifndef _NDARRAY_PROPERTIES_
@ -22,74 +22,6 @@
#include "ndarray.h"
#include "numpy/ndarray/ndarray_iter.h"
#if CIRCUITPY
typedef struct _mp_obj_property_t {
mp_obj_base_t base;
mp_obj_t proxy[3]; // getter, setter, deleter
} mp_obj_property_t;
#if NDARRAY_HAS_DTYPE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_dtype_obj, ndarray_dtype);
STATIC const mp_obj_property_t ndarray_dtype_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&ndarray_get_dtype_obj,
mp_const_none,
mp_const_none },
};
#endif /* NDARRAY_HAS_DTYPE */
#if NDARRAY_HAS_FLATITER
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_new);
STATIC const mp_obj_property_t ndarray_flat_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&ndarray_flatiter_make_new_obj,
mp_const_none,
mp_const_none },
};
#endif /* NDARRAY_HAS_FLATITER */
#if NDARRAY_HAS_ITEMSIZE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_itemsize_obj, ndarray_itemsize);
STATIC const mp_obj_property_t ndarray_itemsize_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&ndarray_get_itemsize_obj,
mp_const_none,
mp_const_none },
};
#endif /* NDARRAY_HAS_ITEMSIZE */
#if NDARRAY_HAS_SHAPE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_shape_obj, ndarray_shape);
STATIC const mp_obj_property_t ndarray_shape_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&ndarray_get_shape_obj,
mp_const_none,
mp_const_none },
};
#endif /* NDARRAY_HAS_SHAPE */
#if NDARRAY_HAS_SIZE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_size_obj, ndarray_size);
STATIC const mp_obj_property_t ndarray_size_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&ndarray_get_size_obj,
mp_const_none,
mp_const_none },
};
#endif /* NDARRAY_HAS_SIZE */
#if NDARRAY_HAS_STRIDES
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_strides_obj, ndarray_strides);
STATIC const mp_obj_property_t ndarray_strides_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&ndarray_get_strides_obj,
mp_const_none,
mp_const_none },
};
#endif /* NDARRAY_HAS_STRIDES */
#else
void ndarray_properties_attr(mp_obj_t , qstr , mp_obj_t *);
#if NDARRAY_HAS_DTYPE
@ -104,6 +36,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_n
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize);
#endif
#if NDARRAY_HAS_NDIM
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_ndim_obj, ndarray_ndim);
#endif
#if NDARRAY_HAS_SHAPE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
#endif
@ -116,6 +52,4 @@ MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size);
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_strides_obj, ndarray_strides);
#endif
#endif /* CIRCUITPY */
#endif

View file

@ -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 } },
@ -65,7 +65,7 @@ STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t
COMPLEX_DTYPE_NOT_IMPLEMENTED(xp->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(fp->dtype)
if((xp->ndim != 1) || (fp->ndim != 1) || (xp->len < 2) || (fp->len < 2) || (xp->len != fp->len)) {
mp_raise_ValueError(translate("interp is defined for 1D iterables of equal length"));
mp_raise_ValueError(MP_ERROR_TEXT("interp is defined for 1D iterables of equal length"));
}
ndarray_obj_t *y = ndarray_new_linear_array(x->len, NDARRAY_FLOAT);
@ -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 } },
@ -168,7 +168,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
return mp_obj_new_float(mean);
}
if((y->ndim != 1)) {
mp_raise_ValueError(translate("trapz is defined for 1D iterables"));
mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D iterables"));
}
mp_float_t (*funcy)(void *) = ndarray_get_float_function(y->dtype);
@ -181,7 +181,7 @@ STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
x = ndarray_from_mp_obj(args[1].u_obj, 0); // x must hold an increasing sequence of independent values
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
if((x->ndim != 1) || (y->len != x->len)) {
mp_raise_ValueError(translate("trapz is defined for 1D arrays of equal length"));
mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D arrays of equal length"));
}
mp_float_t (*funcx)(void *) = ndarray_get_float_function(x->dtype);

431
code/numpy/bitwise.c Normal file
View file

@ -0,0 +1,431 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Zoltán Vörös
*
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "bitwise.h"
#if ULAB_NUMPY_HAS_BITWISE_AND
ndarray_obj_t *bitwise_bitwise_and_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
// AND is commutative, so simply swap the order, if a particular combination has already been inspected
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, &);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, &);
} else {
return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, &);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, &);
} else {
return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, &);
} else {
return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
}
return results;
}
#endif
#if ULAB_NUMPY_HAS_BITWISE_OR
ndarray_obj_t *bitwise_bitwise_or_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
// OR is commutative, so simply swap the order, if a particular combination has already been inspected
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, |);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, |);
} else {
return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, |);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, |);
} else {
return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, |);
} else {
return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
}
return results;
}
#endif
#if ULAB_NUMPY_HAS_BITWISE_XOR
ndarray_obj_t *bitwise_bitwise_xor_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
// OR is commutative, so simply swap the order, if a particular combination has already been inspected
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ^);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ^);
} else {
return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
} else if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ^);
} else {
return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_INT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ^);
} else {
return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
}
}
return results;
}
#endif
#if ULAB_NUMPY_HAS_LEFT_SHIFT
ndarray_obj_t *bitwise_left_shift_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, <<);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, <<);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, <<);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, <<);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, <<);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, <<);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, <<);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, uint16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, <<);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, <<);
}
}
return results;
}
#endif
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
ndarray_obj_t *bitwise_right_shift_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = NULL;
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, >>);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, >>);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, >>);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, >>);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, >>);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >>);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_UINT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_INT8) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, >>);
} else if(rhs->dtype == NDARRAY_UINT16) {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, uint16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >>);
} else {
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, >>);
}
}
return results;
}
#endif
mp_obj_t *bitwise_binary_operators(mp_obj_t x1, mp_obj_t x2, uint8_t optype) {
ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0);
ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0);
#if ULAB_SUPPORTS_COMPLEX
if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT) || (lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) {
mp_raise_ValueError(MP_ERROR_TEXT("not supported for input types"));
}
#else
if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) {
mp_raise_ValueError(MP_ERROR_TEXT("not supported for input types"));
}
#endif
uint8_t ndim = 0;
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS);
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) {
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
}
ndarray_obj_t *results = NULL;
switch(optype) {
#if ULAB_NUMPY_HAS_BITWISE_AND
case BITWISE_AND:
results = bitwise_bitwise_and_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if ULAB_NUMPY_HAS_BITWISE_OR
case BITWISE_OR:
results = bitwise_bitwise_or_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if ULAB_NUMPY_HAS_BITWISE_XOR
case BITWISE_XOR:
results = bitwise_bitwise_xor_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if ULAB_NUMPY_HAS_LEFT_SHIFT
case BITWISE_LEFT_SHIFT:
results = bitwise_left_shift_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
case BITWISE_RIGHT_SHIFT:
results = bitwise_right_shift_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
break;
#endif
default:
break;
}
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
return MP_OBJ_FROM_PTR(results);
}
#if ULAB_NUMPY_HAS_BITWISE_AND
mp_obj_t bitwise_bitwise_and(mp_obj_t x1, mp_obj_t x2) {
return bitwise_binary_operators(x1, x2, BITWISE_AND);
}
MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_and_obj, bitwise_bitwise_and);
#endif
#if ULAB_NUMPY_HAS_BITWISE_OR
mp_obj_t bitwise_bitwise_or(mp_obj_t x1, mp_obj_t x2) {
return bitwise_binary_operators(x1, x2, BITWISE_OR);
}
MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_or_obj, bitwise_bitwise_or);
#endif
#if ULAB_NUMPY_HAS_BITWISE_XOR
mp_obj_t bitwise_bitwise_xor(mp_obj_t x1, mp_obj_t x2) {
return bitwise_binary_operators(x1, x2, BITWISE_XOR);
}
MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_xor_obj, bitwise_bitwise_xor);
#endif
#if ULAB_NUMPY_HAS_LEFT_SHIFT
mp_obj_t bitwise_left_shift(mp_obj_t x1, mp_obj_t x2) {
return bitwise_binary_operators(x1, x2, BITWISE_LEFT_SHIFT);
}
MP_DEFINE_CONST_FUN_OBJ_2(left_shift_obj, bitwise_left_shift);
#endif
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
mp_obj_t bitwise_right_shift(mp_obj_t x1, mp_obj_t x2) {
return bitwise_binary_operators(x1, x2, BITWISE_RIGHT_SHIFT);
}
MP_DEFINE_CONST_FUN_OBJ_2(right_shift_obj, bitwise_right_shift);
#endif

32
code/numpy/bitwise.h Normal file
View file

@ -0,0 +1,32 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Zoltán Vörös
*/
#ifndef _BITWISE_
#define _BITWISE_
#include "../ulab.h"
#include "../ndarray.h"
enum BITWISE_FUNCTION_TYPE {
BITWISE_AND,
BITWISE_OR,
BITWISE_XOR,
BITWISE_LEFT_SHIFT,
BITWISE_RIGHT_SHIFT,
};
MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_and_obj);
MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_or_obj);
MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_xor_obj);
MP_DECLARE_CONST_FUN_OBJ_2(left_shift_obj);
MP_DECLARE_CONST_FUN_OBJ_2(right_shift_obj);
#endif /* _BITWISE_ */

View file

@ -25,9 +25,11 @@
#if ULAB_SUPPORTS_COMPLEX
//| import builtins
//|
//| import ulab.numpy
//| def real(val):
//| def real(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| Return the real part of the complex argument, which can be
//| either an ndarray, or a scalar."""
@ -47,14 +49,14 @@ mp_obj_t carray_real(mp_obj_t _source) {
return MP_OBJ_FROM_PTR(target);
}
} else {
mp_raise_NotImplementedError(translate("function is implemented for ndarrays only"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only"));
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real);
//| def imag(val):
//| def imag(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| Return the imaginary part of the complex argument, which can be
//| either an ndarray, or a scalar."""
@ -73,7 +75,7 @@ mp_obj_t carray_imag(mp_obj_t _source) {
return MP_OBJ_FROM_PTR(target);
}
} else {
mp_raise_NotImplementedError(translate("function is implemented for ndarrays only"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only"));
}
return mp_const_none;
}
@ -82,7 +84,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag);
#if ULAB_NUMPY_HAS_CONJUGATE
//| def conjugate(val):
//| def conjugate(
//| val: builtins.complex | ulab.numpy.ndarray
//| ) -> builtins.complex | ulab.numpy.ndarray:
//| """
//| Return the conjugate of the complex argument, which can be
//| either an ndarray, or a scalar."""
@ -111,7 +115,7 @@ mp_obj_t carray_conjugate(mp_obj_t _source) {
} else if(mp_obj_is_int(_source) || mp_obj_is_float(_source)) {
return _source;
} else {
mp_raise_TypeError(translate("input must be an ndarray, or a scalar"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray, or a scalar"));
}
}
// this should never happen
@ -183,11 +187,11 @@ static void carray_sort_complex_(mp_float_t *array, size_t len) {
mp_obj_t carray_sort_complex(mp_obj_t _source) {
if(!mp_obj_is_type(_source, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("input must be a 1D ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray"));
}
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
if(source->ndim != 1) {
mp_raise_TypeError(translate("input must be a 1D ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray"));
}
ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(source, NDARRAY_COMPLEX);

View file

@ -22,7 +22,7 @@
#if ULAB_SUPPORTS_COMPLEX
void raise_complex_NotImplementedError(void) {
mp_raise_NotImplementedError(translate("not implemented for complex dtype"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("not implemented for complex dtype"));
}
#endif

View file

@ -36,7 +36,7 @@ static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) {
int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS);
int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS);
if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) {
mp_raise_ValueError(translate("operands could not be broadcast together"));
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
@ -140,7 +140,23 @@ static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype)
#endif
#if ULAB_NUMPY_HAS_CLIP
//| def clip(
//| a: _ScalarOrArrayLike,
//| a_min: _ScalarOrArrayLike,
//| a_max: _ScalarOrArrayLike,
//| ) -> _ScalarOrNdArray:
//| """
//| Clips (limits) the values in an array.
//|
//| :param a: Scalar or array containing elements to clip.
//| :param a_min: Minimum value, it will be broadcast against ``a``.
//| :param a_max: Maximum value, it will be broadcast against ``a``.
//| :return:
//| A scalar or array with the elements of ``a``, but where
//| values < ``a_min`` are replaced with ``a_min``, and those
//| > ``a_max`` with ``a_max``.
//| """
//| ...
mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) {
// Note: this function could be made faster by implementing a single-loop comparison in
// RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we
@ -166,7 +182,18 @@ MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip);
#endif
#if ULAB_NUMPY_HAS_EQUAL
//| def equal(x: _ScalarOrArrayLike, y: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """
//| Returns ``x == y`` element-wise.
//|
//| :param x, y:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A boolean scalar or array with the element-wise result of ``x == y``.
//| """
//| ...
mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) {
return compare_equal_helper(x1, x2, COMPARE_EQUAL);
}
@ -175,7 +202,21 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal);
#endif
#if ULAB_NUMPY_HAS_NOTEQUAL
//| def not_equal(
//| x: _ScalarOrArrayLike,
//| y: _ScalarOrArrayLike,
//| ) -> Union[_bool, ulab.numpy.ndarray]:
//| """
//| Returns ``x != y`` element-wise.
//|
//| :param x, y:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A boolean scalar or array with the element-wise result of ``x != y``.
//| """
//| ...
mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL);
}
@ -219,57 +260,33 @@ static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
}
uint8_t *xarray = (uint8_t *)x->array;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
mp_float_t value = *(mp_float_t *)xarray;
if(isnan(value)) {
*rarray++ = 0;
} else {
*rarray++ = isinf(value) ? mask : 1 - mask;
}
xarray += x->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < x->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
xarray -= x->strides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
xarray += x->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < x->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
xarray -= x->strides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
xarray += x->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < x->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
xarray -= x->strides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
xarray += x->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < x->shape[ULAB_MAX_DIMS - 4]);
#endif
ITERATOR_HEAD();
mp_float_t value = *(mp_float_t *)xarray;
if(isnan(value)) {
*rarray++ = 0;
} else {
*rarray++ = isinf(value) ? mask : 1 - mask;
}
ITERATOR_TAIL(x, xarray);
return MP_OBJ_FROM_PTR(results);
} else {
mp_raise_TypeError(translate("wrong input type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
return mp_const_none;
}
#endif
#if ULAB_NUMPY_HAS_ISFINITE
//| def isfinite(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
//| """
//| Tests element-wise for finiteness (i.e., it should not be infinity or a NaN).
//|
//| :param x: Input scalar or ndarray.
//| :return:
//| A boolean scalar or array with True where ``x`` is finite, and
//| False otherwise.
//| """
//| ...
mp_obj_t compare_isfinite(mp_obj_t _x) {
return compare_isinf_isfinite(_x, 0);
}
@ -278,6 +295,16 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite);
#endif
#if ULAB_NUMPY_HAS_ISINF
//| def isinf(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
//| """
//| Tests element-wise for positive or negative infinity.
//|
//| :param x: Input scalar or ndarray.
//| :return:
//| A boolean scalar or array with True where ``x`` is positive or
//| negative infinity, and False otherwise.
//| """
//| ...
mp_obj_t compare_isinf(mp_obj_t _x) {
return compare_isinf_isfinite(_x, 1);
}
@ -286,6 +313,18 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf);
#endif
#if ULAB_NUMPY_HAS_MAXIMUM
//| def maximum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """
//| Returns the element-wise maximum.
//|
//| :param x1, x2:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A scalar or array with the element-wise maximum of ``x1`` and ``x2``.
//| """
//| ...
mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) {
// extra round, so that we can return maximum(3, 4) properly
mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM);
@ -301,6 +340,18 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum);
#if ULAB_NUMPY_HAS_MINIMUM
//| def minimum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """
//| Returns the element-wise minimum.
//|
//| :param x1, x2:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A scalar or array with the element-wise minimum of ``x1`` and ``x2``.
//| """
//| ...
mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) {
// extra round, so that we can return minimum(3, 4) properly
mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM);
@ -316,6 +367,17 @@ MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum);
#if ULAB_NUMPY_HAS_NONZERO
//| def nonzero(x: _ScalarOrArrayLike) -> ulab.numpy.ndarray:
//| """
//| Returns the indices of elements that are non-zero.
//|
//| :param x:
//| Input scalar or array. If ``x`` is a scalar, it is treated
//| as a single-element 1-d array.
//| :return:
//| An array of indices that are non-zero.
//| """
//| ...
mp_obj_t compare_nonzero(mp_obj_t x) {
ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
// since ndarray_new_linear_array calls m_new0, the content of zero is a single zero
@ -446,6 +508,27 @@ MP_DEFINE_CONST_FUN_OBJ_1(compare_nonzero_obj, compare_nonzero);
#if ULAB_NUMPY_HAS_WHERE
//| def where(
//| condition: _ScalarOrArrayLike,
//| x: _ScalarOrArrayLike,
//| y: _ScalarOrArrayLike,
//| ) -> ulab.numpy.ndarray:
//| """
//| Returns elements from ``x`` or ``y`` depending on ``condition``.
//|
//| :param condition:
//| Input scalar or array. If an element (or scalar) is truthy,
//| the corresponding element from ``x`` is chosen, otherwise
//| ``y`` is used. ``condition``, ``x`` and ``y`` must also be
//| broadcastable to the same shape (which becomes the output
//| shape.)
//| :param x, y:
//| Input scalar or array.
//| :return:
//| An array with elements from ``x`` when ``condition`` is
//| truthy, and ``y`` elsewhere.
//| """
//| ...
mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) {
// this implementation will work with ndarrays, and scalars only
ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0);
@ -470,7 +553,7 @@ mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) {
if(!ndarray_can_broadcast(c, x, &ndim, oshape, cstrides, ystrides) ||
!ndarray_can_broadcast(c, y, &ndim, oshape, cstrides, ystrides) ||
!ndarray_can_broadcast(x, y, &ndim, oshape, xstrides, ystrides)) {
mp_raise_ValueError(translate("operands could not be broadcast together"));
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
}
ndim = MAX(MAX(c->ndim, x->ndim), y->ndim);

View file

@ -6,7 +6,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
* 2019-2021 Zoltán Vörös
* 2019-2024 Zoltán Vörös
* 2020 Taku Fukada
*/
@ -24,7 +24,7 @@
#if ULAB_NUMPY_HAS_ONES | ULAB_NUMPY_HAS_ZEROS | ULAB_NUMPY_HAS_FULL | ULAB_NUMPY_HAS_EMPTY
static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t value) {
if(!mp_obj_is_int(oshape) && !mp_obj_is_type(oshape, &mp_type_tuple) && !mp_obj_is_type(oshape, &mp_type_list)) {
mp_raise_TypeError(translate("input argument must be an integer, a tuple, or a list"));
mp_raise_TypeError(MP_ERROR_TEXT("input argument must be an integer, a tuple, or a list"));
}
ndarray_obj_t *ndarray = NULL;
if(mp_obj_is_int(oshape)) {
@ -33,7 +33,7 @@ static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t
} else if(mp_obj_is_type(oshape, &mp_type_tuple) || mp_obj_is_type(oshape, &mp_type_list)) {
uint8_t len = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(oshape));
if(len > ULAB_MAX_DIMS) {
mp_raise_TypeError(translate("too many dimensions"));
mp_raise_TypeError(MP_ERROR_TEXT("too many dimensions"));
}
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
@ -144,7 +144,7 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
step = mp_obj_get_float(args[2].u_obj);
if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj) && mp_obj_is_int(args[2].u_obj)) dtype = NDARRAY_INT16;
} else {
mp_raise_TypeError(translate("wrong number of arguments"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments"));
}
if((MICROPY_FLOAT_C_FUN(fabs)(stop) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(start) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(step) > 32768)) {
dtype = NDARRAY_FLOAT;
@ -158,8 +158,8 @@ mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero"));
}
if(isnan(start) || isnan(stop) || isnan(step)) {
mp_raise_ValueError(translate("arange: cannot compute length"));
if(!isfinite(start) || !isfinite(stop) || !isfinite(step)) {
mp_raise_ValueError(MP_ERROR_TEXT("arange: cannot compute length"));
}
ndarray_obj_t *ndarray;
@ -222,7 +222,7 @@ mp_obj_t create_asarray(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
}
return MP_OBJ_FROM_PTR(ndarray_from_iterable(args[0].u_obj, _dtype));
} else {
mp_raise_TypeError(translate("wrong input type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
return mp_const_none; // this should never happen
}
@ -252,7 +252,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) {
mp_raise_TypeError(translate("first argument must be a tuple of ndarrays"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a tuple of ndarrays"));
}
int8_t axis = (int8_t)args[1].u_int;
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
@ -260,7 +260,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
for(uint8_t i = 0; i < ndarrays->len; i++) {
if(!mp_obj_is_type(ndarrays->items[i], &ulab_ndarray_type)) {
mp_raise_ValueError(translate("only ndarrays can be concatenated"));
mp_raise_ValueError(MP_ERROR_TEXT("only ndarrays can be concatenated"));
}
}
@ -272,7 +272,7 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
axis += ndim;
}
if((axis < 0) || (axis >= ndim)) {
mp_raise_ValueError(translate("wrong axis specified"));
mp_raise_ValueError(MP_ERROR_TEXT("wrong axis specified"));
}
// shift axis
axis = ULAB_MAX_DIMS - ndim + axis;
@ -284,14 +284,14 @@ mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
_ndarray = MP_OBJ_TO_PTR(ndarrays->items[i]);
// check, whether the arrays are compatible
if((dtype != _ndarray->dtype) || (ndim != _ndarray->ndim)) {
mp_raise_ValueError(translate("input arrays are not compatible"));
mp_raise_ValueError(MP_ERROR_TEXT("input arrays are not compatible"));
}
for(uint8_t j=0; j < ULAB_MAX_DIMS; j++) {
if(j == axis) {
shape[j] += _ndarray->shape[j];
} else {
if(shape[j] != _ndarray->shape[j]) {
mp_raise_ValueError(translate("input arrays are not compatible"));
mp_raise_ValueError(MP_ERROR_TEXT("input arrays are not compatible"));
}
}
}
@ -431,7 +431,7 @@ mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
}
#if ULAB_MAX_DIMS > 2
else {
mp_raise_ValueError(translate("input must be 1- or 2-d"));
mp_raise_ValueError(MP_ERROR_TEXT("input must be 1- or 2-d"));
}
#endif
@ -585,7 +585,7 @@ mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(args[2].u_int < 2) {
mp_raise_ValueError(translate("number of points must be at least 2"));
mp_raise_ValueError(MP_ERROR_TEXT("number of points must be at least 2"));
}
size_t len = (size_t)args[2].u_int;
mp_float_t start, step, stop;
@ -708,7 +708,7 @@ mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(args[2].u_int < 2) {
mp_raise_ValueError(translate("number of points must be at least 2"));
mp_raise_ValueError(MP_ERROR_TEXT("number of points must be at least 2"));
}
size_t len = (size_t)args[2].u_int;
mp_float_t start, step, quotient;
@ -776,6 +776,235 @@ mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones);
#endif
#if ULAB_NUMPY_HAS_TAKE
//| def take(
//| a: ulab.numpy.ndarray,
//| indices: _ArrayLike,
//| axis: Optional[int] = None,
//| out: Optional[ulab.numpy.ndarray] = None,
//| mode: Optional[str] = None) -> ulab.numpy.ndarray:
//| """
//| .. param: a
//| The source array.
//| .. param: indices
//| The indices of the values to extract.
//| .. param: axis
//| The axis over which to select values. By default, the flattened input array is used.
//| .. param: out
//| If provided, the result will be placed in this array. It should be of the appropriate shape and dtype.
//| .. param: mode
//| Specifies how out-of-bounds indices will behave.
//| - `raise`: raise an error (default)
//| - `wrap`: wrap around
//| - `clip`: clip to the range
//| `clip` mode means that all indices that are too large are replaced by the
//| index that addresses the last element along that axis. Note that this disables
//| indexing with negative numbers.
//|
//| Return a new array."""
//| ...
//|
enum CREATE_TAKE_MODE {
CREATE_TAKE_RAISE,
CREATE_TAKE_WRAP,
CREATE_TAKE_CLIP,
};
mp_obj_t create_take(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("input is not an array"));
}
ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
int8_t axis = 0;
int8_t axis_index = 0;
int32_t axis_len;
uint8_t mode = CREATE_TAKE_RAISE;
uint8_t ndim;
// axis keyword argument
if(args[2].u_obj == mp_const_none) {
// work with the flattened array
axis_len = a->len;
ndim = 1;
} else { // i.e., axis is an integer
// TODO: this pops up at quite a few places, write it as a function
axis = mp_obj_get_int(args[2].u_obj);
ndim = a->ndim;
if(axis < 0) axis += a->ndim;
if((axis < 0) || (axis > a->ndim - 1)) {
mp_raise_ValueError(MP_ERROR_TEXT("index out of range"));
}
axis_index = ULAB_MAX_DIMS - a->ndim + axis;
axis_len = (int32_t)a->shape[axis_index];
}
size_t _len;
// mode keyword argument
if(mp_obj_is_str(args[4].u_obj)) {
const char *_mode = mp_obj_str_get_data(args[4].u_obj, &_len);
if(memcmp(_mode, "raise", 5) == 0) {
mode = CREATE_TAKE_RAISE;
} else if(memcmp(_mode, "wrap", 4) == 0) {
mode = CREATE_TAKE_WRAP;
} else if(memcmp(_mode, "clip", 4) == 0) {
mode = CREATE_TAKE_CLIP;
} else {
mp_raise_ValueError(MP_ERROR_TEXT("mode should be raise, wrap or clip"));
}
}
size_t indices_len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj));
size_t *indices = m_new(size_t, indices_len);
mp_obj_iter_buf_t buf;
mp_obj_t item, iterable = mp_getiter(args[1].u_obj, &buf);
size_t z = 0;
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
int32_t index = mp_obj_get_int(item);
if(mode == CREATE_TAKE_RAISE) {
if(index < 0) {
index += axis_len;
}
if((index < 0) || (index > axis_len - 1)) {
m_del(size_t, indices, indices_len);
mp_raise_ValueError(MP_ERROR_TEXT("index out of range"));
}
} else if(mode == CREATE_TAKE_WRAP) {
index %= axis_len;
} else { // mode == CREATE_TAKE_CLIP
if(index < 0) {
m_del(size_t, indices, indices_len);
mp_raise_ValueError(MP_ERROR_TEXT("index must not be negative"));
}
if(index > axis_len - 1) {
index = axis_len - 1;
}
}
indices[z++] = (size_t)index;
}
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
if(args[2].u_obj == mp_const_none) { // flattened array
shape[ULAB_MAX_DIMS - 1] = indices_len;
} else {
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
shape[i] = a->shape[i];
if(i == axis_index) {
shape[i] = indices_len;
}
}
}
ndarray_obj_t *out = NULL;
if(args[3].u_obj == mp_const_none) {
// no output was supplied
out = ndarray_new_dense_ndarray(ndim, shape, a->dtype);
} else {
// TODO: deal with last argument being false!
out = ulab_tools_inspect_out(args[3].u_obj, a->dtype, ndim, shape, true);
}
#if ULAB_MAX_DIMS > 1 // we can save the hassle, if there is only one possible dimension
if((args[2].u_obj == mp_const_none) || (a->ndim == 1)) { // flattened array
#endif
uint8_t *out_array = (uint8_t *)out->array;
for(size_t x = 0; x < indices_len; x++) {
uint8_t *a_array = (uint8_t *)a->array;
size_t remainder = indices[x];
uint8_t q = ULAB_MAX_DIMS - 1;
do {
size_t div = (remainder / a->shape[q]);
a_array += remainder * a->strides[q];
remainder -= div * a->shape[q];
q--;
} while(q > ULAB_MAX_DIMS - a->ndim);
// NOTE: for floats and complexes, this might be
// better with memcpy(out_array, a_array, a->itemsize)
for(uint8_t p = 0; p < a->itemsize; p++) {
out_array[p] = a_array[p];
}
out_array += a->itemsize;
}
#if ULAB_MAX_DIMS > 1
} else {
// move the axis shape/stride to the leftmost position:
SWAP(size_t, a->shape[0], a->shape[axis_index]);
SWAP(size_t, out->shape[0], out->shape[axis_index]);
SWAP(int32_t, a->strides[0], a->strides[axis_index]);
SWAP(int32_t, out->strides[0], out->strides[axis_index]);
for(size_t x = 0; x < indices_len; x++) {
uint8_t *a_array = (uint8_t *)a->array;
uint8_t *out_array = (uint8_t *)out->array;
a_array += indices[x] * a->strides[0];
out_array += x * out->strides[0];
#if ULAB_MAX_DIMS > 3
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t k = 0;
do {
#endif
size_t l = 0;
do {
// NOTE: for floats and complexes, this might be
// better with memcpy(out_array, a_array, a->itemsize)
for(uint8_t p = 0; p < a->itemsize; p++) {
out_array[p] = a_array[p];
}
out_array += out->strides[ULAB_MAX_DIMS - 1];
a_array += a->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < a->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 2
out_array -= out->strides[ULAB_MAX_DIMS - 1] * out->shape[ULAB_MAX_DIMS - 1];
out_array += out->strides[ULAB_MAX_DIMS - 2];
a_array -= a->strides[ULAB_MAX_DIMS - 1] * a->shape[ULAB_MAX_DIMS - 1];
a_array += a->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < a->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 3
out_array -= out->strides[ULAB_MAX_DIMS - 2] * out->shape[ULAB_MAX_DIMS - 2];
out_array += out->strides[ULAB_MAX_DIMS - 3];
a_array -= a->strides[ULAB_MAX_DIMS - 2] * a->shape[ULAB_MAX_DIMS - 2];
a_array += a->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < a->shape[ULAB_MAX_DIMS - 3]);
#endif
}
// revert back to the original order
SWAP(size_t, a->shape[0], a->shape[axis_index]);
SWAP(size_t, out->shape[0], out->shape[axis_index]);
SWAP(int32_t, a->strides[0], a->strides[axis_index]);
SWAP(int32_t, out->strides[0], out->strides[axis_index]);
}
#endif /* ULAB_MAX_DIMS > 1 */
m_del(size_t, indices, indices_len);
return MP_OBJ_FROM_PTR(out);
}
MP_DEFINE_CONST_FUN_OBJ_KW(create_take_obj, 2, create_take);
#endif /* ULAB_NUMPY_HAS_TAKE */
#if ULAB_NUMPY_HAS_ZEROS
//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
//| """
@ -824,33 +1053,24 @@ mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
size_t sz = ulab_binary_get_size(dtype);
if(bufinfo.len < offset) {
mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length"));
mp_raise_ValueError(MP_ERROR_TEXT("offset must be non-negative and no greater than buffer length"));
}
size_t len = (bufinfo.len - offset) / sz;
if((len * sz) != (bufinfo.len - offset)) {
mp_raise_ValueError(translate("buffer size must be a multiple of element size"));
mp_raise_ValueError(MP_ERROR_TEXT("buffer size must be a multiple of element size"));
}
if(mp_obj_get_int(args[2].u_obj) > 0) {
size_t count = mp_obj_get_int(args[2].u_obj);
if(len < count) {
mp_raise_ValueError(translate("buffer is smaller than requested size"));
mp_raise_ValueError(MP_ERROR_TEXT("buffer is smaller than requested size"));
} else {
len = count;
}
}
ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t);
ndarray->base.type = &ulab_ndarray_type;
ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype;
ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC;
ndarray->ndim = 1;
ndarray->len = len;
ndarray->itemsize = sz;
ndarray->shape[ULAB_MAX_DIMS - 1] = len;
ndarray->strides[ULAB_MAX_DIMS - 1] = sz;
size_t *shape = ndarray_shape_vector(0, 0, 0, len);
uint8_t *buffer = bufinfo.buf;
ndarray->array = buffer + offset;
return MP_OBJ_FROM_PTR(ndarray);
return ndarray_new_ndarray(1, shape, NULL, dtype, buffer + offset);
}
return mp_const_none;
}

View file

@ -62,6 +62,11 @@ mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj);
#endif
#if ULAB_NUMPY_HAS_TAKE
mp_obj_t create_take(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_take_obj);
#endif
#if ULAB_NUMPY_HAS_ZEROS
mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj);

View file

@ -5,7 +5,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
* Copyright (c) 2019-2024 Zoltán Vörös
* 2020 Scott Shawcroft for Adafruit Industries
* 2020 Taku Fukada
*/
@ -43,16 +43,16 @@
//|
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
static mp_obj_t fft_fft(mp_obj_t arg) {
return fft_fft_ifft_spectrogram(arg, FFT_FFT);
return fft_fft_ifft(arg, FFT_FFT);
}
MP_DEFINE_CONST_FUN_OBJ_1(fft_fft_obj, fft_fft);
#else
static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
if(n_args == 2) {
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_FFT);
return fft_fft_ifft(n_args, args[0], args[1], FFT_FFT);
} else {
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_FFT);
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_FFT);
}
}
@ -71,7 +71,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
static mp_obj_t fft_ifft(mp_obj_t arg) {
return fft_fft_ifft_spectrogram(arg, FFT_IFFT);
return fft_fft_ifft(arg, FFT_IFFT);
}
MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
@ -79,31 +79,27 @@ MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
NOT_IMPLEMENTED_FOR_COMPLEX()
if(n_args == 2) {
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_IFFT);
return fft_fft_ifft(n_args, args[0], args[1], FFT_IFFT);
} else {
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_IFFT);
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_IFFT);
}
}
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 },
.globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module);
#endif
#endif

View file

@ -5,7 +5,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
* Copyright (c) 2019-2024 Zoltán Vörös
*/
#include <math.h>
@ -45,7 +45,7 @@
imag[i] = data[2i+1]
*/
void fft_kernel_complex(mp_float_t *data, size_t n, int isign) {
void fft_kernel(mp_float_t *data, size_t n, int isign) {
size_t j, m, mmax, istep;
mp_float_t tempr, tempi;
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
@ -94,22 +94,22 @@ void fft_kernel_complex(mp_float_t *data, size_t n, int isign) {
/*
* The following function is a helper interface to the python side.
* It has been factored out from fft.c, so that the same argument parsing
* routine can be called from scipy.signal.spectrogram.
* routine can be called from utils.spectrogram.
*/
mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) {
mp_obj_t fft_fft_ifft(mp_obj_t data_in, uint8_t type) {
if(!mp_obj_is_type(data_in, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(data_in);
#if ULAB_MAX_DIMS > 1
if(in->ndim != 1) {
mp_raise_TypeError(translate("FFT is implemented for linear arrays only"));
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
}
#endif
size_t len = in->len;
// Check if input is of length of power of 2
if((len & (len-1)) != 0) {
mp_raise_ValueError(translate("input array length must be power of 2"));
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
}
ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_COMPLEX);
@ -134,20 +134,10 @@ mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) {
}
data -= 2 * len;
if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) {
fft_kernel_complex(data, len, 1);
if(type == FFT_SPECTROGRAM) {
ndarray_obj_t *spectrum = ndarray_new_linear_array(len, NDARRAY_FLOAT);
mp_float_t *sarray = (mp_float_t *)spectrum->array;
for(size_t i = 0; i < len; i++) {
*sarray++ = MICROPY_FLOAT_C_FUN(sqrt)(data[0] * data[0] + data[1] * data[1]);
data += 2;
}
m_del(mp_float_t, data, 2 * len);
return MP_OBJ_FROM_PTR(spectrum);
}
if(type == FFT_FFT) {
fft_kernel(data, len, 1);
} else { // inverse transform
fft_kernel_complex(data, len, -1);
fft_kernel(data, len, -1);
// TODO: numpy accepts the norm keyword argument
for(size_t i = 0; i < 2 * len; i++) {
*data++ /= len;
@ -202,26 +192,26 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) {
}
}
mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
}
if(n_args == 2) {
if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
}
}
ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re);
#if ULAB_MAX_DIMS > 1
if(re->ndim != 1) {
COMPLEX_DTYPE_NOT_IMPLEMENTED(re->dtype)
mp_raise_TypeError(translate("FFT is implemented for linear arrays only"));
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
}
#endif
size_t len = re->len;
// Check if input is of length of power of 2
if((len & (len-1)) != 0) {
mp_raise_ValueError(translate("input array length must be power of 2"));
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
}
ndarray_obj_t *out_re = ndarray_new_linear_array(len, NDARRAY_FLOAT);
@ -243,11 +233,11 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i
#if ULAB_MAX_DIMS > 1
if(im->ndim != 1) {
COMPLEX_DTYPE_NOT_IMPLEMENTED(im->dtype)
mp_raise_TypeError(translate("FFT is implemented for linear arrays only"));
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
}
#endif
if (re->len != im->len) {
mp_raise_ValueError(translate("real and imaginary parts must be of equal length"));
mp_raise_ValueError(MP_ERROR_TEXT("real and imaginary parts must be of equal length"));
}
array = (uint8_t *)im->array;
func = ndarray_get_float_function(im->dtype);
@ -258,15 +248,8 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i
data_im -= len;
}
if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) {
if(type == FFT_FFT) {
fft_kernel(data_re, data_im, len, 1);
if(type == FFT_SPECTROGRAM) {
for(size_t i=0; i < len; i++) {
*data_re = MICROPY_FLOAT_C_FUN(sqrt)(*data_re * *data_re + *data_im * *data_im);
data_re++;
data_im++;
}
}
} else { // inverse transform
fft_kernel(data_re, data_im, len, -1);
// TODO: numpy accepts the norm keyword argument
@ -275,13 +258,9 @@ mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_i
*data_im++ /= len;
}
}
if(type == FFT_SPECTROGRAM) {
return MP_OBJ_FROM_PTR(out_re);
} else {
mp_obj_t tuple[2];
tuple[0] = MP_OBJ_FROM_PTR(out_re);
tuple[1] = MP_OBJ_FROM_PTR(out_im);
return mp_obj_new_tuple(2, tuple);
}
mp_obj_t tuple[2];
tuple[0] = MP_OBJ_FROM_PTR(out_re);
tuple[1] = MP_OBJ_FROM_PTR(out_im);
return mp_obj_new_tuple(2, tuple);
}
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */

View file

@ -14,15 +14,14 @@
enum FFT_TYPE {
FFT_FFT,
FFT_IFFT,
FFT_SPECTROGRAM,
};
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
void fft_kernel(mp_float_t *, size_t , int );
mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t , uint8_t );
mp_obj_t fft_fft_ifft(mp_obj_t , uint8_t );
#else
void fft_kernel(mp_float_t *, mp_float_t *, size_t , int );
mp_obj_t fft_fft_ifft_spectrogram(size_t , mp_obj_t , mp_obj_t , uint8_t );
mp_obj_t fft_fft_ifft(size_t , mp_obj_t , mp_obj_t , uint8_t );
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
#endif /* _FFT_TOOLS_ */

View file

@ -36,7 +36,7 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("convolve arguments must be ndarrays"));
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be ndarrays"));
}
ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
@ -44,13 +44,13 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
// deal with linear arrays only
#if ULAB_MAX_DIMS > 1
if((a->ndim != 1) || (c->ndim != 1)) {
mp_raise_TypeError(translate("convolve arguments must be linear arrays"));
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be linear arrays"));
}
#endif
size_t len_a = a->len;
size_t len_c = c->len;
if(len_a == 0 || len_c == 0) {
mp_raise_TypeError(translate("convolve arguments must not be empty"));
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must not be empty"));
}
int len = len_a + len_c - 1; // convolve mode "full"

View file

@ -46,13 +46,13 @@ static void io_read_(mp_obj_t stream, const mp_stream_p_t *stream_p, char *buffe
}
if(fail) {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, error);
mp_raise_msg(&mp_type_RuntimeError, translate("corrupted file"));
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
}
}
static mp_obj_t io_load(mp_obj_t file) {
if(!mp_obj_is_str(file)) {
mp_raise_TypeError(translate("wrong input type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
int error;
@ -126,7 +126,7 @@ static mp_obj_t io_load(mp_obj_t file) {
#endif /* ULAB_SUPPORT_COPMLEX */
else {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
mp_raise_TypeError(translate("wrong dtype"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong dtype"));
}
io_read_(stream, stream_p, buffer, "', 'fortran_order': False, 'shape': (", 37, &error);
@ -169,7 +169,7 @@ static mp_obj_t io_load(mp_obj_t file) {
}
else {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
mp_raise_msg(&mp_type_RuntimeError, translate("corrupted file"));
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
}
needle++;
}
@ -188,7 +188,7 @@ static mp_obj_t io_load(mp_obj_t file) {
size_t read = stream_p->read(stream, array, ndarray->len * ndarray->itemsize, &error);
if(read != ndarray->len * ndarray->itemsize) {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
mp_raise_msg(&mp_type_RuntimeError, translate("corrupted file"));
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
}
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
@ -239,7 +239,11 @@ MP_DEFINE_CONST_FUN_OBJ_1(io_load_obj, io_load);
#if ULAB_NUMPY_HAS_LOADTXT
static void io_assign_value(const char *clipboard, uint8_t len, ndarray_obj_t *ndarray, size_t *idx, uint8_t dtype) {
#if MICROPY_PY_BUILTINS_COMPLEX
mp_obj_t value = mp_parse_num_decimal(clipboard, len, false, false, NULL);
#else
mp_obj_t value = mp_parse_num_float(clipboard, len, false, NULL);
#endif
if(dtype != NDARRAY_FLOAT) {
mp_float_t _value = mp_obj_get_float(value);
value = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(_value));
@ -303,7 +307,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
cols[0] = (uint16_t)mp_obj_get_int(args[4].u_obj);
} else {
#if ULAB_MAX_DIMS == 1
mp_raise_ValueError(translate("usecols keyword must be specified"));
mp_raise_ValueError(MP_ERROR_TEXT("usecols keyword must be specified"));
#else
// assume that the argument is an iterable
used_columns = (uint16_t)mp_obj_get_int(mp_obj_len(args[4].u_obj));
@ -334,7 +338,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
buffer[read] = '\0';
offset = buffer;
while(*offset != '\0') {
if(*offset == comment_char) {
while(*offset == comment_char) {
// clear the line till the end, or the buffer's end
while((*offset != '\0')) {
offset++;
@ -379,12 +383,12 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
} while((read > 0) && (all_rows < max_rows));
if(rows == 0) {
mp_raise_ValueError(translate("empty file"));
mp_raise_ValueError(MP_ERROR_TEXT("empty file"));
}
uint16_t columns = items / rows;
if(columns < used_columns) {
mp_raise_ValueError(translate("usecols is too high"));
mp_raise_ValueError(MP_ERROR_TEXT("usecols is too high"));
}
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
@ -421,7 +425,7 @@ static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
offset = buffer;
while(*offset != '\0') {
if(*offset == comment_char) {
while(*offset == comment_char) {
// clear the line till the end, or the buffer's end
while((*offset != '\0')) {
offset++;
@ -526,7 +530,7 @@ static uint8_t io_sprintf(char *buffer, const char *comma, size_t x) {
static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) {
if(!mp_obj_is_str(file) || !mp_obj_is_type(ndarray_, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("wrong input type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(ndarray_);
@ -615,48 +619,14 @@ static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) {
uint8_t *array = (uint8_t *)ndarray->array;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
memcpy(buffer+offset, array, sz);
offset += sz;
if(offset == ULAB_IO_BUFFER_SIZE) {
stream_p->write(stream, buffer, offset, &error);
offset = 0;
}
array += ndarray->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
array += ndarray->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
array += ndarray->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < ndarray->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
array += ndarray->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < ndarray->shape[ULAB_MAX_DIMS - 4]);
#endif
ITERATOR_HEAD();
memcpy(buffer+offset, array, sz);
offset += sz;
if(offset == ULAB_IO_BUFFER_SIZE) {
stream_p->write(stream, buffer, offset, &error);
offset = 0;
}
ITERATOR_TAIL(ndarray, array);
stream_p->write(stream, buffer, offset, &error);
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
@ -725,14 +695,14 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_str(args[0].u_obj) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("wrong input type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
#if ULAB_MAX_DIMS > 2
if(ndarray->ndim > 2) {
mp_raise_ValueError(translate("array has too many dimensions"));
mp_raise_ValueError(MP_ERROR_TEXT("array has too many dimensions"));
}
#endif
@ -747,16 +717,32 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
int error;
size_t len_comment;
char *comments;
if(mp_obj_is_str(args[5].u_obj)) {
const char *_comments = mp_obj_str_get_data(args[5].u_obj, &len_comment);
comments = (char *)_comments;
} else {
len_comment = 2;
comments = m_new(char, len_comment);
comments[0] = '#';
comments[1] = ' ';
}
if(mp_obj_is_str(args[3].u_obj)) {
size_t _len;
if(mp_obj_is_str(args[5].u_obj)) {
const char *comments = mp_obj_str_get_data(args[5].u_obj, &_len);
stream_p->write(stream, comments, _len, &error);
} else {
stream_p->write(stream, "# ", 2, &error);
}
const char *header = mp_obj_str_get_data(args[3].u_obj, &_len);
stream_p->write(stream, header, _len, &error);
stream_p->write(stream, comments, len_comment, &error);
// We can't write the header in the single chunk, for it might contain line breaks
for(size_t i = 0; i < _len; header++, i++) {
stream_p->write(stream, header, 1, &error);
if((*header == '\n') && (i < _len)) {
stream_p->write(stream, comments, len_comment, &error);
}
}
stream_p->write(stream, "\n", 1, &error);
}
@ -795,16 +781,19 @@ static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
#endif
if(mp_obj_is_str(args[4].u_obj)) {
if(mp_obj_is_str(args[4].u_obj)) { // footer string
size_t _len;
if(mp_obj_is_str(args[5].u_obj)) {
const char *comments = mp_obj_str_get_data(args[5].u_obj, &_len);
stream_p->write(stream, comments, _len, &error);
} else {
stream_p->write(stream, "# ", 2, &error);
}
const char *footer = mp_obj_str_get_data(args[4].u_obj, &_len);
stream_p->write(stream, footer, _len, &error);
stream_p->write(stream, comments, len_comment, &error);
// We can't write the header in the single chunk, for it might contain line breaks
for(size_t i = 0; i < _len; footer++, i++) {
stream_p->write(stream, footer, 1, &error);
if((*footer == '\n') && (i < _len)) {
stream_p->write(stream, comments, len_comment, &error);
}
}
stream_p->write(stream, "\n", 1, &error);
}

View file

@ -67,7 +67,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) {
for(size_t n=m+1; n < N; n++) { // columns
// compare entry (m, n) to (n, m)
if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(Larray[m * N + n] - Larray[n * N + m])) {
mp_raise_ValueError(translate("input matrix is asymmetric"));
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric"));
}
}
}
@ -87,7 +87,7 @@ static mp_obj_t linalg_cholesky(mp_obj_t oin) {
}
if(i == j) {
if(sum <= MICROPY_FLOAT_CONST(0.0)) {
mp_raise_ValueError(translate("matrix is not positive definite"));
mp_raise_ValueError(MP_ERROR_TEXT("matrix is not positive definite"));
} else {
Larray[i * N + i] = MICROPY_FLOAT_C_FUN(sqrt)(sum);
}
@ -204,7 +204,7 @@ static mp_obj_t linalg_eig(mp_obj_t oin) {
// compare entry (m, n) to (n, m)
// TODO: this must probably be scaled!
if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n] - array[n * S + m])) {
mp_raise_ValueError(translate("input matrix is asymmetric"));
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric"));
}
}
}
@ -219,7 +219,7 @@ static mp_obj_t linalg_eig(mp_obj_t oin) {
if(iterations == 0) {
// the computation did not converge; numpy raises LinAlgError
m_del(mp_float_t, array, in->len);
mp_raise_ValueError(translate("iterations did not converge"));
mp_raise_ValueError(MP_ERROR_TEXT("iterations did not converge"));
}
ndarray_obj_t *eigenvalues = ndarray_new_linear_array(S, NDARRAY_FLOAT);
mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array;
@ -267,7 +267,7 @@ static mp_obj_t linalg_inv(mp_obj_t o_in) {
iarray -= N*N;
if(!linalg_invert_matrix(iarray, N)) {
mp_raise_ValueError(translate("input matrix is singular"));
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular"));
}
return MP_OBJ_FROM_PTR(inverted);
}
@ -393,11 +393,11 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("operation is defined for ndarrays only"));
mp_raise_TypeError(MP_ERROR_TEXT("operation is defined for ndarrays only"));
}
ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
if(source->ndim != 2) {
mp_raise_ValueError(translate("operation is defined for 2D arrays only"));
mp_raise_ValueError(MP_ERROR_TEXT("operation is defined for 2D arrays only"));
}
size_t m = source->shape[ULAB_MAX_DIMS - 2]; // rows
@ -498,7 +498,7 @@ static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
tuple->items[0] = MP_OBJ_FROM_PTR(q);
tuple->items[1] = MP_OBJ_FROM_PTR(r);
} else {
mp_raise_ValueError(translate("mode must be complete, or reduced"));
mp_raise_ValueError(MP_ERROR_TEXT("mode must be complete, or reduced"));
}
return MP_OBJ_FROM_PTR(tuple);
}
@ -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,17 +530,13 @@ 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 },
.globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module);
#endif
#endif
#endif

View file

@ -45,6 +45,8 @@ enum NUMERICAL_FUNCTION_TYPE {
//| from typing import Dict
//|
//| _ArrayLike = Union[ndarray, List[_float], Tuple[_float], range]
//| _ScalarOrArrayLike = Union[int, _float, _ArrayLike]
//| _ScalarOrNdArray = Union[int, _float, ndarray]
//|
//| _DType = int
//| """`ulab.numpy.int8`, `ulab.numpy.uint8`, `ulab.numpy.int16`, `ulab.numpy.uint16`, `ulab.numpy.float` or `ulab.numpy.bool`"""
@ -272,7 +274,7 @@ static mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, si
}
}
static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype, size_t ddof) {
static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, mp_obj_t keepdims, uint8_t optype, size_t ddof) {
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
uint8_t *array = (uint8_t *)ndarray->array;
shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
@ -370,7 +372,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
mp_float_t norm = (mp_float_t)_shape_strides.shape[0];
// re-wind the array here
farray = (mp_float_t *)results->array;
for(size_t i=0; i < results->len; i++) {
for(size_t i = 0; i < results->len; i++) {
*farray++ *= norm;
}
}
@ -378,7 +380,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
bool isStd = optype == NUMERICAL_STD ? 1 : 0;
results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT);
farray = (mp_float_t *)results->array;
// we can return the 0 array here, if the degrees of freedom is larger than the length of the axis
// we can return the 0 array here, if the degrees of freedom are larger than the length of the axis
if((optype == NUMERICAL_STD) && (_shape_strides.shape[0] <= ddof)) {
return MP_OBJ_FROM_PTR(results);
}
@ -395,11 +397,9 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, div, isStd);
}
}
if(results->ndim == 0) { // return a scalar here
return mp_binary_get_val_array(results->dtype, results->array, 0);
}
return MP_OBJ_FROM_PTR(results);
return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides);
}
// we should never get to this point
return mp_const_none;
}
#endif
@ -407,7 +407,7 @@ static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t
#if ULAB_NUMPY_HAS_ARGMINMAX
static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) {
if(MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(oin)) == 0) {
mp_raise_ValueError(translate("attempt to get argmin/argmax of an empty sequence"));
mp_raise_ValueError(MP_ERROR_TEXT("attempt to get argmin/argmax of an empty sequence"));
}
size_t idx = 0, best_idx = 0;
mp_obj_iter_buf_t iter_buf;
@ -439,10 +439,10 @@ static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) {
}
}
static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {
static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t keepdims, mp_obj_t axis, uint8_t optype) {
// TODO: treat the flattened array
if(ndarray->len == 0) {
mp_raise_ValueError(translate("attempt to get (arg)min/(arg)max of empty sequence"));
mp_raise_ValueError(MP_ERROR_TEXT("attempt to get (arg)min/(arg)max of empty sequence"));
}
if(axis == mp_const_none) {
@ -519,7 +519,9 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t
int32_t *strides = m_new0(int32_t, ULAB_MAX_DIMS);
numerical_reduce_axes(ndarray, ax, shape, strides);
uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax;
shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
uint8_t index = _shape_strides.axis;
ndarray_obj_t *results = NULL;
@ -544,12 +546,9 @@ static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t
}
m_del(int32_t, strides, ULAB_MAX_DIMS);
if(results->len == 1) {
return mp_binary_get_val_array(results->dtype, results->array, 0);
}
return MP_OBJ_FROM_PTR(results);
return ulab_tools_restore_dims(ndarray, results, keepdims, _shape_strides);
}
// we should never get to this point
return mp_const_none;
}
#endif
@ -558,6 +557,7 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
{ MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_keepdims, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -565,8 +565,10 @@ 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(translate("axis must be None, or an integer"));
mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer"));
}
#if ULAB_NUMPY_HAS_ALL | ULAB_NUMPY_HAS_ANY
@ -596,16 +598,17 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
case NUMERICAL_ARGMIN:
case NUMERICAL_ARGMAX:
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
return numerical_argmin_argmax_ndarray(ndarray, axis, optype);
case NUMERICAL_SUM:
return numerical_argmin_argmax_ndarray(ndarray, keepdims, axis, optype);
case NUMERICAL_MEAN:
case NUMERICAL_STD:
case NUMERICAL_SUM:
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
return numerical_sum_mean_std_ndarray(ndarray, axis, optype, 0);
return numerical_sum_mean_std_ndarray(ndarray, axis, keepdims, optype, 0);
default:
mp_raise_NotImplementedError(translate("operation is not implemented on ndarrays"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("operation is not implemented on ndarrays"));
}
} else {
mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be tuple, list, range, or ndarray"));
}
return mp_const_none;
}
@ -613,7 +616,7 @@ static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
#if ULAB_NUMPY_HAS_SORT | NDARRAY_HAS_SORT
static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inplace) {
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("sort argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("sort argument must be an ndarray"));
}
ndarray_obj_t *ndarray;
@ -721,20 +724,20 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("argsort argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("argsort argument must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
if(args[1].u_obj == mp_const_none) {
// bail out, though dense arrays could still be sorted
mp_raise_NotImplementedError(translate("argsort is not implemented for flattened arrays"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("argsort is not implemented for flattened arrays"));
}
// Since we are returning an NDARRAY_UINT16 array, bail out,
// if the axis is longer than what we can hold
for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) {
if(ndarray->shape[i] > 65535) {
mp_raise_ValueError(translate("axis too long"));
mp_raise_ValueError(MP_ERROR_TEXT("axis too long"));
}
}
int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim);
@ -744,7 +747,7 @@ mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
numerical_reduce_axes(ndarray, ax, shape, strides);
// We could return an NDARRAY_UINT8 array, if all lengths are shorter than 256
ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16);
ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16, NULL);
int32_t *istrides = m_new0(int32_t, ULAB_MAX_DIMS);
numerical_reduce_axes(indices, ax, shape, istrides);
@ -827,14 +830,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argsort_obj, 1, numerical_argsort);
static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) {
if (!mp_obj_is_type(_a, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("arguments must be ndarrays"));
mp_raise_TypeError(MP_ERROR_TEXT("arguments must be ndarrays"));
}
ndarray_obj_t *a = MP_OBJ_TO_PTR(_a);
ndarray_obj_t *b = MP_OBJ_TO_PTR(_b);
COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(b->dtype)
if((a->ndim != 1) || (b->ndim != 1) || (a->len != b->len) || (a->len != 3)) {
mp_raise_ValueError(translate("cross is defined for 1D arrays of length 3"));
mp_raise_ValueError(MP_ERROR_TEXT("cross is defined for 1D arrays of length 3"));
}
mp_float_t *results = m_new(mp_float_t, 3);
@ -917,7 +920,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("diff argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("diff argument must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
@ -926,16 +929,16 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
if(ax < 0) ax += ndarray->ndim;
if((ax < 0) || (ax > ndarray->ndim - 1)) {
mp_raise_ValueError(translate("index out of range"));
mp_raise_ValueError(MP_ERROR_TEXT("index out of range"));
}
if((args[1].u_int < 0) || (args[1].u_int > 9)) {
mp_raise_ValueError(translate("differentiation order out of range"));
mp_raise_ValueError(MP_ERROR_TEXT("differentiation order out of range"));
}
uint8_t N = (uint8_t)args[1].u_int;
uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax;
if(N > ndarray->shape[index]) {
mp_raise_ValueError(translate("differentiation order out of range"));
mp_raise_ValueError(MP_ERROR_TEXT("differentiation order out of range"));
}
int8_t *stencil = m_new(int8_t, N+1);
@ -996,7 +999,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("flip argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("flip argument must be an ndarray"));
}
ndarray_obj_t *results = NULL;
@ -1016,7 +1019,7 @@ mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
results = ndarray_new_view(ndarray, ndarray->ndim, ndarray->shape, ndarray->strides, offset);
results->strides[ax] = -results->strides[ax];
} else {
mp_raise_TypeError(translate("wrong axis index"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong axis index"));
}
return MP_OBJ_FROM_PTR(results);
}
@ -1065,7 +1068,7 @@ mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("median argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("median argument must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
@ -1181,16 +1184,22 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("roll argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("roll argument must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
uint8_t *array = ndarray->array;
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
@ -1318,7 +1327,7 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
} else {
mp_raise_TypeError(translate("wrong axis index"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong axis index"));
}
return MP_OBJ_FROM_PTR(results);
@ -1377,6 +1386,7 @@ mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } } ,
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_keepdims, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -1385,17 +1395,19 @@ 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(translate("axis must be None, or an integer"));
mp_raise_ValueError(MP_ERROR_TEXT("axis must be None, or an integer"));
}
if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || mp_obj_is_type(oin, &mp_type_range)) {
return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof);
} else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
return numerical_sum_mean_std_ndarray(ndarray, axis, NUMERICAL_STD, ddof);
return numerical_sum_mean_std_ndarray(ndarray, axis, keepdims, NUMERICAL_STD, ddof);
} else {
mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be tuple, list, range, or ndarray"));
}
return mp_const_none;
}

View file

@ -57,35 +57,9 @@
(rarray) += (results)->itemsize;\
})
// The mean could be calculated by simply dividing the sum by
// the number of elements, but that method is numerically unstable
#define RUN_MEAN1(type, array, rarray, ss)\
({\
mp_float_t M = 0.0;\
for(size_t i=0; i < (ss).shape[0]; i++) {\
mp_float_t value = (mp_float_t)(*(type *)(array));\
M = M + (value - M) / (mp_float_t)(i+1);\
(array) += (ss).strides[0];\
}\
*(rarray)++ = M;\
})
// Instead of the straightforward implementation of the definition,
// we take the numerically stable Welford algorithm here
// https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
#define RUN_STD1(type, array, rarray, ss, div)\
({\
mp_float_t M = 0.0, m = 0.0, S = 0.0;\
for(size_t i=0; i < (ss).shape[0]; i++) {\
mp_float_t value = (mp_float_t)(*(type *)(array));\
m = M + (value - M) / (mp_float_t)(i+1);\
S = S + (value - M) * (value - m);\
M = m;\
(array) += (ss).strides[0];\
}\
*(rarray)++ = MICROPY_FLOAT_C_FUN(sqrt)(S / (div));\
})
#define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\
({\
mp_float_t M = 0.0, m = 0.0, S = 0.0;\
@ -193,14 +167,6 @@
RUN_SUM1(type, (array), (results), (rarray), (ss));\
} while(0)
#define RUN_MEAN(type, array, rarray, ss) do {\
RUN_MEAN1(type, (array), (rarray), (ss));\
} while(0)
#define RUN_STD(type, array, rarray, ss, div) do {\
RUN_STD1(type, (array), (results), (rarray), (ss), (div));\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
} while(0)
@ -234,26 +200,6 @@
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#define RUN_MEAN(type, array, rarray, ss) do {\
size_t l = 0;\
do {\
RUN_MEAN1(type, (array), (rarray), (ss));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#define RUN_STD(type, array, rarray, ss, div) do {\
size_t l = 0;\
do {\
RUN_STD1(type, (array), (rarray), (ss), (div));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
size_t l = 0;\
do {\
@ -325,38 +271,6 @@
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define RUN_MEAN(type, array, rarray, ss) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_MEAN1(type, (array), (rarray), (ss));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define RUN_STD(type, array, rarray, ss, div) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_STD1(type, (array), (rarray), (ss), (div));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
size_t k = 0;\
do {\
@ -467,50 +381,6 @@
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define RUN_MEAN(type, array, rarray, ss) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_MEAN1(type, (array), (rarray), (ss));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define RUN_STD(type, array, rarray, ss, div) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_STD1(type, (array), (rarray), (ss), (div));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
size_t j = 0;\
do {\

View file

@ -18,6 +18,7 @@
#include "numpy.h"
#include "approx.h"
#include "bitwise.h"
#include "carray/carray.h"
#include "compare.h"
#include "create.h"
@ -26,6 +27,7 @@
#include "io/io.h"
#include "linalg/linalg.h"
#include "numerical.h"
#include "random/random.h"
#include "stats.h"
#include "transform.h"
#include "poly.h"
@ -109,6 +111,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
#if ULAB_NUMPY_HAS_LINALG_MODULE
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) },
#endif
#if ULAB_NUMPY_HAS_RANDOM_MODULE
{ MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&ulab_numpy_random_module) },
#endif
#if ULAB_HAS_PRINTOPTIONS
{ MP_ROM_QSTR(MP_QSTR_set_printoptions), MP_ROM_PTR(&ndarray_set_printoptions_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_printoptions), MP_ROM_PTR(&ndarray_get_printoptions_obj) },
@ -164,7 +169,6 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
#if ULAB_NUMPY_HAS_ZEROS
{ MP_ROM_QSTR(MP_QSTR_zeros), MP_ROM_PTR(&create_zeros_obj) },
#endif
// functions of the compare sub-module
#if ULAB_NUMPY_HAS_CLIP
{ MP_ROM_QSTR(MP_QSTR_clip), MP_ROM_PTR(&compare_clip_obj) },
#endif
@ -189,10 +193,25 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
#if ULAB_NUMPY_HAS_NONZERO
{ MP_ROM_QSTR(MP_QSTR_nonzero), MP_ROM_PTR(&compare_nonzero_obj) },
#endif
#if ULAB_NUMPY_HAS_WHERE
{ MP_ROM_QSTR(MP_QSTR_where), MP_ROM_PTR(&compare_where_obj) },
#endif
// bitwise operators
#if ULAB_NUMPY_HAS_BITWISE_AND
{ MP_ROM_QSTR(MP_QSTR_bitwise_and), MP_ROM_PTR(&bitwise_bitwise_and_obj) },
#endif
#if ULAB_NUMPY_HAS_BITWISE_OR
{ MP_ROM_QSTR(MP_QSTR_bitwise_or), MP_ROM_PTR(&bitwise_bitwise_or_obj) },
#endif
#if ULAB_NUMPY_HAS_BITWISE_XOR
{ MP_ROM_QSTR(MP_QSTR_bitwise_xor), MP_ROM_PTR(&bitwise_bitwise_xor_obj) },
#endif
#if ULAB_NUMPY_HAS_LEFT_SHIFT
{ MP_ROM_QSTR(MP_QSTR_left_shift), MP_ROM_PTR(&left_shift_obj) },
#endif
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
{ MP_ROM_QSTR(MP_QSTR_right_shift), MP_ROM_PTR(&right_shift_obj) },
#endif
// functions of the filter sub-module
#if ULAB_NUMPY_HAS_CONVOLVE
{ MP_ROM_QSTR(MP_QSTR_convolve), MP_ROM_PTR(&filter_convolve_obj) },
@ -272,6 +291,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
#if ULAB_NUMPY_HAS_SUM
{ MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&numerical_sum_obj) },
#endif
#if ULAB_NUMPY_HAS_TAKE
{ MP_ROM_QSTR(MP_QSTR_take), MP_ROM_PTR(&create_take_obj) },
#endif
// functions of the poly sub-module
#if ULAB_NUMPY_HAS_POLYFIT
{ MP_ROM_QSTR(MP_QSTR_polyfit), MP_ROM_PTR(&poly_polyfit_obj) },
@ -340,6 +362,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
#if ULAB_NUMPY_HAS_SIN
{ MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&vector_sin_obj) },
#endif
#if ULAB_NUMPY_HAS_SINC
{ MP_ROM_QSTR(MP_QSTR_sinc), MP_ROM_PTR(&vector_sinc_obj) },
#endif
#if ULAB_NUMPY_HAS_SINH
{ MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&vector_sinh_obj) },
#endif
@ -379,9 +404,5 @@ const mp_obj_module_t ulab_numpy_module = {
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module);
#endif
#endif

View file

@ -26,7 +26,7 @@
mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
if(!ndarray_object_is_array_like(args[0])) {
mp_raise_ValueError(translate("input data must be an iterable"));
mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable"));
}
#if ULAB_SUPPORTS_COMPLEX
if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
@ -44,7 +44,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
deg = (uint8_t)mp_obj_get_int(args[1]);
if(leny < deg) {
mp_raise_ValueError(translate("more degrees of freedom than data points"));
mp_raise_ValueError(MP_ERROR_TEXT("more degrees of freedom than data points"));
}
lenx = leny;
x = m_new(mp_float_t, lenx); // assume uniformly spaced data points
@ -55,16 +55,16 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
fill_array_iterable(y, args[0]);
} else /* n_args == 3 */ {
if(!ndarray_object_is_array_like(args[1])) {
mp_raise_ValueError(translate("input data must be an iterable"));
mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable"));
}
lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1]));
if(lenx != leny) {
mp_raise_ValueError(translate("input vectors must be of equal length"));
mp_raise_ValueError(MP_ERROR_TEXT("input vectors must be of equal length"));
}
deg = (uint8_t)mp_obj_get_int(args[2]);
if(leny < deg) {
mp_raise_ValueError(translate("more degrees of freedom than data points"));
mp_raise_ValueError(MP_ERROR_TEXT("more degrees of freedom than data points"));
}
x = m_new(mp_float_t, lenx);
fill_array_iterable(x, args[0]);
@ -104,7 +104,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
m_del(mp_float_t, x, lenx);
m_del(mp_float_t, y, lenx);
m_del(mp_float_t, prod, (deg+1)*(deg+1));
mp_raise_ValueError(translate("could not invert Vandermonde matrix"));
mp_raise_ValueError(MP_ERROR_TEXT("could not invert Vandermonde matrix"));
}
// at this point, we have the inverse of X^T * X
// y is a column vector; x is free now, we can use it for storing intermediate values
@ -121,7 +121,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
ndarray_obj_t *beta = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT);
mp_float_t *betav = (mp_float_t *)beta->array;
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
m_del(float, y, leny);
m_del(mp_float_t, y, leny);
// now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
for(uint8_t i=0; i < deg+1; i++) {
@ -145,9 +145,18 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
#if ULAB_NUMPY_HAS_POLYVAL
static mp_float_t poly_eval(mp_float_t x, mp_float_t *p, uint8_t plen) {
mp_float_t y = p[0];
for(uint8_t j=0; j < plen-1; j++) {
y *= x;
y += p[j+1];
}
return y;
}
mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
if(!ndarray_object_is_array_like(o_p) || !ndarray_object_is_array_like(o_x)) {
mp_raise_TypeError(translate("inputs are not iterable"));
if(!ndarray_object_is_array_like(o_p)) {
mp_raise_TypeError(MP_ERROR_TEXT("input is not iterable"));
}
#if ULAB_SUPPORTS_COMPLEX
ndarray_obj_t *input;
@ -161,7 +170,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
}
#endif
// p had better be a one-dimensional standard iterable
uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p));
size_t plen = (size_t)mp_obj_get_int(mp_obj_len_maybe(o_p));
mp_float_t *p = m_new(mp_float_t, plen);
mp_obj_iter_buf_t p_buf;
mp_obj_t p_item, p_iterable = mp_getiter(o_p, &p_buf);
@ -171,6 +180,10 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
i++;
}
if(!ndarray_object_is_array_like(o_x)) {
return mp_obj_new_float(poly_eval(mp_obj_get_float(o_x), p, plen));
}
// polynomials are going to be of type float, except, when both
// the coefficients and the independent variable are integers
ndarray_obj_t *ndarray;
@ -184,48 +197,9 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
// TODO: these loops are really nothing, but the re-impplementation of
// ITERATE_VECTOR from vectorise.c. We could pass a function pointer here
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
mp_float_t y = p[0];
mp_float_t _x = func(sarray);
for(uint8_t m=0; m < plen-1; m++) {
y *= _x;
y += p[m+1];
}
*array++ = y;
sarray += source->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
sarray += source->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
sarray += source->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
sarray += source->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
#endif
ITERATOR_HEAD();
*array++ = poly_eval(func(sarray), p, plen);
ITERATOR_TAIL(source, sarray);
} else {
// o_x had better be a one-dimensional standard iterable
ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT);
@ -233,13 +207,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
mp_obj_iter_buf_t x_buf;
mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf);
while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) {
mp_float_t _x = mp_obj_get_float(x_item);
mp_float_t y = p[0];
for(uint8_t j=0; j < plen-1; j++) {
y *= _x;
y += p[j+1];
}
*array++ = y;
*array++ = poly_eval(mp_obj_get_float(x_item), p, plen);
}
}
m_del(mp_float_t, p, plen);

361
code/numpy/random/random.c Normal file
View file

@ -0,0 +1,361 @@
/*
* 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 "py/builtin.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "random.h"
ULAB_DEFINE_FLOAT_CONST(random_zero, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL);
ULAB_DEFINE_FLOAT_CONST(random_one, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL);
// methods of the Generator object
static const mp_rom_map_elem_t random_generator_locals_dict_table[] = {
#if ULAB_NUMPY_RANDOM_HAS_NORMAL
{ MP_ROM_QSTR(MP_QSTR_normal), MP_ROM_PTR(&random_normal_obj) },
#endif
#if ULAB_NUMPY_RANDOM_HAS_RANDOM
{ MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) },
#endif
#if ULAB_NUMPY_RANDOM_HAS_UNIFORM
{ MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(random_generator_locals_dict, random_generator_locals_dict_table);
// random's Generator object is defined here
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
MP_DEFINE_CONST_OBJ_TYPE(
random_generator_type,
MP_QSTR_generator,
MP_TYPE_FLAG_NONE,
print, random_generator_print,
make_new, random_generator_make_new,
locals_dict, &random_generator_locals_dict
);
#else
const mp_obj_type_t random_generator_type = {
{ &mp_type_type },
.name = MP_QSTR_generator,
.print = random_generator_print,
.make_new = random_generator_make_new,
.locals_dict = (mp_obj_dict_t*)&random_generator_locals_dict
};
#endif
void random_generator_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
random_generator_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(MP_PYTHON_PRINTER, "Generator() at 0x%p", self);
}
mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void) type;
mp_arg_check_num(n_args, n_kw, 0, 1, true);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args);
if(args[0] == mp_const_none) {
#ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC
mp_raise_ValueError(MP_ERROR_TEXT("no default seed"));
#else
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
generator->base.type = &random_generator_type;
generator->state = MICROPY_PY_RANDOM_SEED_INIT_FUNC;
return MP_OBJ_FROM_PTR(generator);
#endif
} else if(mp_obj_is_int(args[0])) {
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
generator->base.type = &random_generator_type;
generator->state = (size_t)mp_obj_get_int(args[0]);
return MP_OBJ_FROM_PTR(generator);
} else if(mp_obj_is_type(args[0], &mp_type_tuple)){
mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]);
mp_obj_t *items = m_new(mp_obj_t, seeds->len);
for(uint8_t i = 0; i < seeds->len; i++) {
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
generator->base.type = &random_generator_type;
generator->state = (size_t)mp_obj_get_int(seeds->items[i]);
items[i] = generator;
}
return mp_obj_new_tuple(seeds->len, items);
} else {
mp_raise_TypeError(MP_ERROR_TEXT("argument must be None, an integer or a tuple of integers"));
}
// we should never end up here
return mp_const_none;
}
// END OF GENERATOR COMPONENTS
static inline uint32_t pcg32_next(uint64_t *state) {
uint64_t old_state = *state;
*state = old_state * PCG_MULTIPLIER_64 + PCG_INCREMENT_64;
uint32_t value = (uint32_t)((old_state ^ (old_state >> 18)) >> 27);
int rot = old_state >> 59;
return rot ? (value >> rot) | (value << (32 - rot)) : value;
}
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
static inline uint64_t pcg32_next64(uint64_t *state) {
uint64_t value = pcg32_next(state);
value <<= 32;
value |= pcg32_next(state);
return value;
}
#endif
#if ULAB_NUMPY_RANDOM_HAS_NORMAL
static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_loc, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } },
{ MP_QSTR_scale, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } },
{ MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj);
mp_float_t loc = mp_obj_get_float(args[1].u_obj);
mp_float_t scale = mp_obj_get_float(args[2].u_obj);
mp_obj_t size = args[3].u_obj;
ndarray_obj_t *ndarray = NULL;
mp_float_t u, v, value;
if(size != mp_const_none) {
if(mp_obj_is_int(size)) {
ndarray = ndarray_new_linear_array((size_t)mp_obj_get_int(size), NDARRAY_FLOAT);
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
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"));
}
} else {
// return single value
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
uint32_t x = pcg32_next(&self->state);
u = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
x = pcg32_next(&self->state);
v = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
#else
uint64_t x = pcg32_next64(&self->state);
u = (double)(int64_t)(x >> 11) * 0x1.0p-53;
x = pcg32_next64(&self->state);
v = (double)(int64_t)(x >> 11) * 0x1.0p-53;
#endif
mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u));
value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v);
return mp_obj_new_float(loc + scale * value);
}
mp_float_t *array = (mp_float_t *)ndarray->array;
// numpy's random supports only dense output arrays, so we can simply
// loop through the elements in a linear fashion
for(size_t i = 0; i < ndarray->len; i = i + 2) {
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
uint32_t x = pcg32_next(&self->state);
u = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
x = pcg32_next(&self->state);
v = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
#else
uint64_t x = pcg32_next64(&self->state);
u = (double)(int64_t)(x >> 11) * 0x1.0p-53;
x = pcg32_next64(&self->state);
v = (double)(int64_t)(x >> 11) * 0x1.0p-53;
#endif
mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u));
value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v);
*array++ = loc + scale * value;
if((i & 1) == 0) {
value = sqrt_log * MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v);
*array++ = loc + scale * value;
}
}
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_KW(random_normal_obj, 1, random_normal);
#endif /* ULAB_NUMPY_RANDOM_HAS_NORMAL */
#if ULAB_NUMPY_RANDOM_HAS_RANDOM
static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj);
mp_obj_t size = args[1].u_obj;
mp_obj_t out = args[2].u_obj;
ndarray_obj_t *ndarray = NULL;
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
if(size != mp_const_none) {
if(mp_obj_is_int(size)) {
shape[ULAB_MAX_DIMS - 1] = (size_t)mp_obj_get_int(size);
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
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"));
}
}
if(out != mp_const_none) {
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
}
ndarray = MP_OBJ_TO_PTR(out);
if(ndarray->dtype != NDARRAY_FLOAT) {
mp_raise_TypeError(MP_ERROR_TEXT("output array has wrong type"));
}
if(size != mp_const_none) {
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
if(ndarray->shape[i] != shape[i]) {
mp_raise_ValueError(MP_ERROR_TEXT("size must match out.shape when used together"));
}
}
}
if(!ndarray_is_dense(ndarray)) {
mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous"));
}
} else { // out == None
if(size != mp_const_none) {
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
} else {
// return single value
mp_float_t value;
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
uint32_t x = pcg32_next(&self->state);
value = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
#else
uint64_t x = pcg32_next64(&self->state);
value = (double)(int64_t)(x >> 11) * 0x1.0p-53;
#endif
return mp_obj_new_float(value);
}
}
mp_float_t *array = (mp_float_t *)ndarray->array;
// numpy's random supports only dense output arrays, so we can simply
// loop through the elements in a linear fashion
for(size_t i = 0; i < ndarray->len; i++) {
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
uint32_t x = pcg32_next(&self->state);
*array = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
#else
uint64_t x = pcg32_next64(&self->state);
*array = (double)(int64_t)(x >> 11) * 0x1.0p-53;
#endif
array++;
}
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random);
#endif /* ULAB_NUMPY_RANDOM_HAS_RANDOM */
#if ULAB_NUMPY_RANDOM_HAS_UNIFORM
static mp_obj_t random_uniform(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_low, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } },
{ MP_QSTR_high, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } },
{ MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj);
mp_float_t low = mp_obj_get_float(args[1].u_obj);
mp_float_t high = mp_obj_get_float(args[2].u_obj);
mp_obj_t size = args[3].u_obj;
ndarray_obj_t *ndarray = NULL;
if(size == mp_const_none) {
// return single value
mp_float_t value;
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
uint32_t x = pcg32_next(&self->state);
value = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
#else
uint64_t x = pcg32_next64(&self->state);
value = (double)(int64_t)(x >> 11) * 0x1.0p-53;
#endif
return mp_obj_new_float(value);
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
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_float_t *array = (mp_float_t *)ndarray->array;
mp_float_t diff = high - low;
for(size_t i = 0; i < ndarray->len; i++) {
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
uint32_t x = pcg32_next(&self->state);
*array = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
#else
uint64_t x = pcg32_next64(&self->state);
*array = (double)(int64_t)(x >> 11) * 0x1.0p-53;
#endif
*array = low + diff * *array;
array++;
}
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_KW(random_uniform_obj, 1, random_uniform);
#endif /* ULAB_NUMPY_RANDOM_HAS_UNIFORM */
static const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) },
{ MP_ROM_QSTR(MP_QSTR_Generator), MP_ROM_PTR(&random_generator_type) },
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_random_globals, ulab_numpy_random_globals_table);
const mp_obj_module_t ulab_numpy_random_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_random_globals,
};

View file

@ -0,0 +1,37 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Zoltán Vörös
*/
#include "../../ndarray.h"
#ifndef _NUMPY_RANDOM_
#define _NUMPY_RANDOM_
#define PCG_MULTIPLIER_64 6364136223846793005ULL
#define PCG_INCREMENT_64 1442695040888963407ULL
extern const mp_obj_module_t ulab_numpy_random_module;
extern const mp_obj_type_t random_generator_type;
typedef struct _random_generator_obj_t {
mp_obj_base_t base;
uint64_t state;
} random_generator_obj_t;
mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
MP_DECLARE_CONST_FUN_OBJ_KW(random_normal_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(random_random_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(random_uniform_obj);
#endif

View file

@ -38,7 +38,7 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m
mp_obj_t condition = args[0].u_obj;
if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("wrong input type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
uint8_t *array = (uint8_t *)ndarray->array;
@ -55,7 +55,7 @@ static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_m
if(((axis == mp_const_none) && (len != ndarray->len)) ||
((axis != mp_const_none) && (len != ndarray->shape[shift_ax]))) {
mp_raise_ValueError(translate("wrong length of condition array"));
mp_raise_ValueError(MP_ERROR_TEXT("wrong length of condition array"));
}
size_t true_count = 0;
@ -175,7 +175,7 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("first argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
uint8_t *array = (uint8_t *)ndarray->array;
@ -201,13 +201,18 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map
index_len = 1;
} else {
if(mp_obj_len_maybe(indices) == MP_OBJ_NULL) {
mp_raise_TypeError(translate("wrong index type"));
mp_raise_TypeError(MP_ERROR_TEXT("wrong index type"));
}
index_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(indices));
if (index_len == 0){
// if the second positional argument is empty
// return the original array
return MP_OBJ_FROM_PTR(ndarray);
}
}
if(index_len > axis_len) {
mp_raise_ValueError(translate("wrong length of index array"));
mp_raise_ValueError(MP_ERROR_TEXT("wrong length of index array"));
}
size_t *index_array = m_new(size_t, index_len);
@ -218,7 +223,7 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map
value += axis_len;
}
if((value < 0) || (value > (ssize_t)axis_len)) {
mp_raise_ValueError(translate("index is out of bounds"));
mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds"));
} else {
*index_array++ = (size_t)value;
}
@ -231,7 +236,7 @@ static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map
value += axis_len;
}
if((value < 0) || (value > (ssize_t)axis_len)) {
mp_raise_ValueError(translate("index is out of bounds"));
mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds"));
} else {
*index_array++ = (size_t)value;
}
@ -356,7 +361,7 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) {
// TODO: should the results be upcast?
// This implements 2D operations only!
if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("arguments must be ndarrays"));
mp_raise_TypeError(MP_ERROR_TEXT("arguments must be ndarrays"));
}
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
@ -370,7 +375,7 @@ mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) {
mp_float_t (*func2)(void *) = ndarray_get_float_function(m2->dtype);
if(m1->shape[ULAB_MAX_DIMS - 1] != m2->shape[ULAB_MAX_DIMS - m2->ndim]) {
mp_raise_ValueError(translate("dimensions do not match"));
mp_raise_ValueError(MP_ERROR_TEXT("dimensions do not match"));
}
uint8_t ndim = MIN(m1->ndim, m2->ndim);
size_t shape1 = m1->ndim == 2 ? m1->shape[ULAB_MAX_DIMS - m1->ndim] : 1;
@ -428,7 +433,7 @@ static mp_obj_t transform_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t
}
if(!ndarray_object_is_array_like(args[0].u_obj)) {
mp_raise_TypeError(translate("first argument must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
}
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
return mp_obj_len_maybe(args[0].u_obj);

File diff suppressed because it is too large Load diff

View file

@ -15,10 +15,37 @@
#include "../ulab.h"
#include "../ndarray.h"
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DECLARE_CONST_FUN_OBJ_KW(vector_acos_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_acosh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_asin_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_asinh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_atan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_atanh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_ceil_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_cos_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_cosh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_degrees_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_erf_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_erfc_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_exp_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_expm1_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_floor_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_gamma_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_lgamma_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log10_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log2_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_radians_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sin_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinc_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_tan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_tanh_obj);
#else
MP_DECLARE_CONST_FUN_OBJ_1(vector_acos_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_acosh_obj);
MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_asin_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_asinh_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_atan_obj);
@ -39,14 +66,21 @@ MP_DECLARE_CONST_FUN_OBJ_1(vector_log10_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_log2_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_radians_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_sin_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_sinc_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_sinh_obj);
#if ULAB_SUPPORTS_COMPLEX
MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj);
#endif
MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj);
#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sqrt_obj);
#else
MP_DECLARE_CONST_FUN_OBJ_1(vector_sqrt_obj);
#endif
MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_vectorize_obj);
typedef struct _vectorized_function_obj_t {
@ -56,14 +90,17 @@ typedef struct _vectorized_function_obj_t {
const mp_obj_type_t *type;
} vectorized_function_obj_t;
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
#if ULAB_HAS_FUNCTION_ITERATOR
#define ITERATE_VECTOR(type, array, source, sarray, shift)\
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray)\
({\
size_t *scoords = ndarray_new_coords((source)->ndim);\
for(size_t i=0; i < (source)->len/(source)->shape[ULAB_MAX_DIMS -1]; i++) {\
for(size_t l=0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
*(array) = f(*((type *)(sarray)));\
(array) += (shift);\
for(size_t i = 0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\
for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
*(tarray) = f(*((type *)(sarray)));\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
}\
ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
@ -72,9 +109,69 @@ typedef struct _vectorized_function_obj_t {
#else
#if ULAB_MAX_DIMS == 1
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t i=0;\
#define ITERATE_VECTOR(type, target, tshape, tstrides, source, sarray) do {\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
@ -82,24 +179,83 @@ typedef struct _vectorized_function_obj_t {
do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS-3]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS-3];\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 3] * (target)->shape[ULAB_MAX_DIMS - 3];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (source)->shape[ULAB_MAX_DIMS-4]);\
} while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
#define MATH_FUN_1(py_name, c_name) \
static mp_obj_t vector_ ## py_name(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { \
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(c_name)); \
}
#else /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#if ULAB_HAS_FUNCTION_ITERATOR
#define ITERATE_VECTOR(type, array, source, sarray, shift)\
({\
size_t *scoords = ndarray_new_coords((source)->ndim);\
for(size_t i=0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\
for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
*(array) = f(*((type *)(sarray)));\
(array)++;\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
}\
ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
}\
})
#else
#if ULAB_MAX_DIMS == 1
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define ITERATE_VECTOR(type, array, source, sarray) do {\
@ -112,50 +268,52 @@ typedef struct _vectorized_function_obj_t {
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS-3]);\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 2
#if ULAB_MAX_DIMS == 4
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t k = 0;\
size_t i = 0;\
do {\
size_t l = 0;\
size_t j = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#endif /* ULAB_MAX_DIMS == 4 */
#if ULAB_MAX_DIMS == 1
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
#define MATH_FUN_1(py_name, c_name) \
static mp_obj_t vector_ ## py_name(mp_obj_t x_obj) { \
return vector_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
}
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* _VECTOR_ */

View file

@ -0,0 +1,701 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Harald Milz <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
* - GaussKronrod 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

View file

@ -0,0 +1,34 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Harald Milz <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_ */

View file

@ -59,14 +59,14 @@ static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("first two arguments must be ndarrays"));
mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays"));
}
ndarray_obj_t *A = MP_OBJ_TO_PTR(args[0].u_obj);
ndarray_obj_t *b = MP_OBJ_TO_PTR(args[1].u_obj);
if(!ndarray_is_dense(A) || !ndarray_is_dense(b)) {
mp_raise_TypeError(translate("input must be a dense ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
}
size_t A_rows = A->shape[ULAB_MAX_DIMS - 2];
@ -83,7 +83,7 @@ static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map
// check if input matrix A is singular
for (i = 0; i < A_rows; i++) {
if (MICROPY_FLOAT_C_FUN(fabs)(get_A_ele(A_arr)) < LINALG_EPSILON)
mp_raise_ValueError(translate("input matrix is singular"));
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular"));
A_arr += A->strides[ULAB_MAX_DIMS - 2];
A_arr += A->strides[ULAB_MAX_DIMS - 1];
}
@ -161,14 +161,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj, 2, solve_triangular);
static mp_obj_t cho_solve(mp_obj_t _L, mp_obj_t _b) {
if(!mp_obj_is_type(_L, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("first two arguments must be ndarrays"));
mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays"));
}
ndarray_obj_t *L = MP_OBJ_TO_PTR(_L);
ndarray_obj_t *b = MP_OBJ_TO_PTR(_b);
if(!ndarray_is_dense(L) || !ndarray_is_dense(b)) {
mp_raise_TypeError(translate("input must be a dense ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
}
mp_float_t (*get_L_ele)(void *) = ndarray_get_float_function(L->dtype);
@ -276,10 +276,6 @@ const mp_obj_module_t ulab_scipy_linalg_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_linalg_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module);
#endif
#endif
#endif

View file

@ -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 } },
@ -71,7 +71,7 @@ STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(translate("first argument must be a function"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
mp_float_t xtol = mp_obj_get_float(args[3].u_obj);
mp_obj_t fargs[1];
@ -82,12 +82,12 @@ STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_
left = optimize_python_call(type, fun, a, fargs, 0);
right = optimize_python_call(type, fun, b, fargs, 0);
if(left * right > 0) {
mp_raise_ValueError(translate("function has the same sign at the ends of interval"));
mp_raise_ValueError(MP_ERROR_TEXT("function has the same sign at the ends of interval"));
}
mp_float_t rtb = left < MICROPY_FLOAT_CONST(0.0) ? a : b;
mp_float_t dx = left < MICROPY_FLOAT_CONST(0.0) ? b - a : a - b;
if(args[4].u_int < 0) {
mp_raise_ValueError(translate("maxiter should be > 0"));
mp_raise_ValueError(MP_ERROR_TEXT("maxiter should be > 0"));
}
for(uint16_t i=0; i < args[4].u_int; i++) {
dx *= MICROPY_FLOAT_CONST(0.5);
@ -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 } },
@ -141,14 +141,14 @@ STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(translate("first argument must be a function"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
// parameters controlling convergence conditions
mp_float_t xatol = mp_obj_get_float(args[2].u_obj);
mp_float_t fatol = mp_obj_get_float(args[3].u_obj);
if(args[4].u_int <= 0) {
mp_raise_ValueError(translate("maxiter must be > 0"));
mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0"));
}
uint16_t maxiter = (uint16_t)args[4].u_int;
@ -277,22 +277,22 @@ mp_obj_t optimize_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(translate("first argument must be a function"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
mp_obj_t x_obj = args[1].u_obj;
mp_obj_t y_obj = args[2].u_obj;
mp_obj_t p0_obj = args[3].u_obj;
if(!ndarray_object_is_array_like(x_obj) || !ndarray_object_is_array_like(y_obj)) {
mp_raise_TypeError(translate("data must be iterable"));
mp_raise_TypeError(MP_ERROR_TEXT("data must be iterable"));
}
if(!ndarray_object_is_nditerable(p0_obj)) {
mp_raise_TypeError(translate("initial values must be iterable"));
mp_raise_TypeError(MP_ERROR_TEXT("initial values must be iterable"));
}
size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(x_obj));
uint8_t lenp = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(p0_obj));
if(len != (uint16_t)mp_obj_get_int(mp_obj_len_maybe(y_obj))) {
mp_raise_ValueError(translate("data must be of equal length"));
mp_raise_ValueError(MP_ERROR_TEXT("data must be of equal length"));
}
mp_float_t *x = m_new(mp_float_t, len);
@ -366,7 +366,7 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(translate("first argument must be a function"));
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
mp_float_t x = mp_obj_get_float(args[1].u_obj);
mp_float_t tol = mp_obj_get_float(args[2].u_obj);
@ -375,7 +375,7 @@ static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_
dx = x > MICROPY_FLOAT_CONST(0.0) ? OPTIMIZE_EPS * x : -OPTIMIZE_EPS * x;
mp_obj_t fargs[1];
if(args[4].u_int <= 0) {
mp_raise_ValueError(translate("maxiter must be > 0"));
mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0"));
}
for(uint16_t i=0; i < args[4].u_int; i++) {
fx = optimize_python_call(type, fun, x, fargs, 0);
@ -413,9 +413,5 @@ const mp_obj_module_t ulab_scipy_optimize_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_optimize_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module);
#endif
#endif

View file

@ -1,4 +1,3 @@
/*
* This file is part of the micropython-ulab project,
*
@ -20,6 +19,8 @@
#include "signal/signal.h"
#include "special/special.h"
#include "linalg/linalg.h"
#include "integrate/integrate.h"
#if ULAB_HAS_SCIPY
@ -28,6 +29,9 @@
static const mp_rom_map_elem_t ulab_scipy_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_scipy) },
#if ULAB_SCIPY_HAS_INTEGRATE_MODULE
{ MP_ROM_QSTR(MP_QSTR_integrate), MP_ROM_PTR(&ulab_scipy_integrate_module) },
#endif
#if ULAB_SCIPY_HAS_LINALG_MODULE
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) },
#endif
@ -49,10 +53,6 @@ const mp_obj_module_t ulab_scipy_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module);
#endif
#endif
#endif /* ULAB_HAS_SCIPY */

View file

@ -43,7 +43,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!ndarray_object_is_array_like(args[0].u_obj) || !ndarray_object_is_array_like(args[1].u_obj)) {
mp_raise_TypeError(translate("sosfilt requires iterable arguments"));
mp_raise_TypeError(MP_ERROR_TEXT("sosfilt requires iterable arguments"));
}
#if ULAB_SUPPORTS_COMPLEX
if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
@ -59,7 +59,7 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
ndarray_obj_t *inarray = MP_OBJ_TO_PTR(args[1].u_obj);
#if ULAB_MAX_DIMS > 1
if(inarray->ndim > 1) {
mp_raise_ValueError(translate("input must be one-dimensional"));
mp_raise_ValueError(MP_ERROR_TEXT("input must be one-dimensional"));
}
#endif
uint8_t *iarray = (uint8_t *)inarray->array;
@ -82,14 +82,14 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
if(args[2].u_obj != mp_const_none) {
if(!mp_obj_is_type(args[2].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("zi must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("zi must be an ndarray"));
} else {
ndarray_obj_t *zi = MP_OBJ_TO_PTR(args[2].u_obj);
if((zi->shape[ULAB_MAX_DIMS - 2] != lensos) || (zi->shape[ULAB_MAX_DIMS - 1] != 2)) {
mp_raise_ValueError(translate("zi must be of shape (n_section, 2)"));
mp_raise_ValueError(MP_ERROR_TEXT("zi must be of shape (n_section, 2)"));
}
if(zi->dtype != NDARRAY_FLOAT) {
mp_raise_ValueError(translate("zi must be of float type"));
mp_raise_ValueError(MP_ERROR_TEXT("zi must be of float type"));
}
// TODO: this won't work with sparse arrays
memcpy(zf_array, zi->array, 2*lensos*sizeof(mp_float_t));
@ -97,11 +97,11 @@ mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
}
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if(mp_obj_get_int(mp_obj_len_maybe(item)) != 6) {
mp_raise_ValueError(translate("sos array must be of shape (n_section, 6)"));
mp_raise_ValueError(MP_ERROR_TEXT("sos array must be of shape (n_section, 6)"));
} else {
fill_array_iterable(coeffs, item);
if(coeffs[3] != MICROPY_FLOAT_CONST(1.0)) {
mp_raise_ValueError(translate("sos[:, 3] should be all ones"));
mp_raise_ValueError(MP_ERROR_TEXT("sos[:, 3] should be all ones"));
}
signal_sosfilt_array(yarray, coeffs, zf_array, lenx);
zf_array += 2;
@ -134,9 +134,5 @@ const mp_obj_module_t ulab_scipy_signal_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_signal_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module);
#endif
#endif

View file

@ -41,9 +41,5 @@ const mp_obj_module_t ulab_scipy_special_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_special_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module);
#endif
#endif

View file

@ -33,7 +33,7 @@
#include "user/user.h"
#include "utils/utils.h"
#define ULAB_VERSION 6.0.12
#define ULAB_VERSION 6.9.0
#define xstr(s) str(s)
#define str(s) #s
@ -43,13 +43,13 @@
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D)
#endif
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING);
static MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING);
#ifdef ULAB_HASH
STATIC MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH));
static MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH));
#endif
STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
static const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
#if ULAB_MAX_DIMS > 1
#if NDARRAY_HAS_RESHAPE
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) },
@ -76,29 +76,9 @@ STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
#if NDARRAY_HAS_SORT
{ MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) },
#endif
#ifdef CIRCUITPY
#if NDARRAY_HAS_DTYPE
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) },
#endif
#if NDARRAY_HAS_FLATITER
{ MP_ROM_QSTR(MP_QSTR_flat), MP_ROM_PTR(&ndarray_flat_obj) },
#endif
#if NDARRAY_HAS_ITEMSIZE
{ MP_ROM_QSTR(MP_QSTR_itemsize), MP_ROM_PTR(&ndarray_itemsize_obj) },
#endif
#if NDARRAY_HAS_SHAPE
{ MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) },
#endif
#if NDARRAY_HAS_SIZE
{ MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&ndarray_size_obj) },
#endif
#if NDARRAY_HAS_STRIDES
{ MP_ROM_QSTR(MP_QSTR_strides), MP_ROM_PTR(&ndarray_strides_obj) },
#endif
#endif /* CIRCUITPY */
};
STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
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).
@ -167,9 +147,7 @@ const mp_obj_type_t ulab_ndarray_type = {
#if NDARRAY_HAS_BINARY_OPS
.binary_op = ndarray_binary_op,
#endif
#ifndef CIRCUITPY
.attr = ndarray_properties_attr,
#endif
.buffer_p = { .get_buffer = ndarray_get_buffer, },
)
};
@ -214,7 +192,7 @@ const mp_obj_type_t ndarray_flatiter_type = {
#endif
#endif
STATIC const mp_rom_map_elem_t ulab_globals_table[] = {
static const mp_rom_map_elem_t ulab_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) },
{ MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) },
#ifdef ULAB_HASH
@ -239,7 +217,7 @@ STATIC const mp_rom_map_elem_t ulab_globals_table[] = {
#endif
};
STATIC MP_DEFINE_CONST_DICT (
static MP_DEFINE_CONST_DICT (
mp_module_ulab_globals,
ulab_globals_table
);
@ -253,10 +231,4 @@ const mp_obj_module_t ulab_user_cmodule = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_globals,
};
// Use old three-argument MP_REGISTER_MODULE for
// MicroPython <= v1.18.0: (1 << 16) | (18 << 8) | 0
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule);
#endif

View file

@ -97,6 +97,10 @@
#define NDARRAY_HAS_BINARY_OP_ADD (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_AND
#define NDARRAY_HAS_BINARY_OP_AND (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_EQUAL
#define NDARRAY_HAS_BINARY_OP_EQUAL (1)
#endif
@ -113,6 +117,10 @@
#define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_MODULO
#define NDARRAY_HAS_BINARY_OP_MODULO (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_MORE
#define NDARRAY_HAS_BINARY_OP_MORE (1)
#endif
@ -129,6 +137,10 @@
#define NDARRAY_HAS_BINARY_OP_NOT_EQUAL (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_OR
#define NDARRAY_HAS_BINARY_OP_OR (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_POWER
#define NDARRAY_HAS_BINARY_OP_POWER (1)
#endif
@ -141,6 +153,10 @@
#define NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_XOR
#define NDARRAY_HAS_BINARY_OP_XOR (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_OPS
#define NDARRAY_HAS_INPLACE_OPS (1)
#endif
@ -149,6 +165,10 @@
#define NDARRAY_HAS_INPLACE_ADD (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_MODULO
#define NDARRAY_HAS_INPLACE_MODU (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_MULTIPLY
#define NDARRAY_HAS_INPLACE_MULTIPLY (1)
#endif
@ -165,6 +185,27 @@
#define NDARRAY_HAS_INPLACE_TRUE_DIVIDE (1)
#endif
// bitwise operators
#ifndef ULAB_NUMPY_HAS_BITWISE_AND
#define ULAB_NUMPY_HAS_BITWISE_AND (1)
#endif
#ifndef ULAB_NUMPY_HAS_BITWISE_OR
#define ULAB_NUMPY_HAS_BITWISE_OR (1)
#endif
#ifndef ULAB_NUMPY_HAS_BITWISE_XOR
#define ULAB_NUMPY_HAS_BITWISE_XOR (1)
#endif
#ifndef ULAB_NUMPY_HAS_LEFT_SHIFT
#define ULAB_NUMPY_HAS_LEFT_SHIFT (1)
#endif
#ifndef ULAB_NUMPY_HAS_RIGHT_SHIFT
#define ULAB_NUMPY_HAS_RIGHT_SHIFT (1)
#endif
// the ndarray unary operators
#ifndef NDARRAY_HAS_UNARY_OPS
#define NDARRAY_HAS_UNARY_OPS (1)
@ -212,6 +253,10 @@
#define NDARRAY_HAS_ITEMSIZE (1)
#endif
#ifndef NDARRAY_HAS_NDIM
#define NDARRAY_HAS_NDIM (1)
#endif
#ifndef NDARRAY_HAS_RESHAPE
#define NDARRAY_HAS_RESHAPE (1)
#endif
@ -365,6 +410,28 @@
#define ULAB_NUMPY_HAS_WHERE (1)
#endif
// the integrate module; functions of the integrate module still have
// to be defined separately
#ifndef ULAB_SCIPY_HAS_INTEGRATE_MODULE
#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_TANHSINH
#define ULAB_INTEGRATE_HAS_TANHSINH (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_ROMBERG
#define ULAB_INTEGRATE_HAS_ROMBERG (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_SIMPSON
#define ULAB_INTEGRATE_HAS_SIMPSON (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_QUAD
#define ULAB_INTEGRATE_HAS_QUAD (1)
#endif
// the linalg module; functions of the linalg module still have
// to be defined separately
#ifndef ULAB_NUMPY_HAS_LINALG_MODULE
@ -407,7 +474,7 @@
// Note that in this case, the input also must be numpythonic,
// i.e., the real an imaginary parts cannot be passed as two arguments
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (0)
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
#endif
#ifndef ULAB_FFT_HAS_FFT
@ -526,6 +593,10 @@
#define ULAB_NUMPY_HAS_SUM (1)
#endif
#ifndef ULAB_NUMPY_HAS_TAKE
#define ULAB_NUMPY_HAS_TAKE (1)
#endif
#ifndef ULAB_NUMPY_HAS_TRACE
#define ULAB_NUMPY_HAS_TRACE (1)
#endif
@ -536,6 +607,12 @@
// vectorised versions of the functions of the math python module, with
// the exception of the functions listed in scipy.special
// if this constant is set, math functions support the out keyword argument
#ifndef ULAB_MATH_FUNCTIONS_OUT_KEYWORD
#define ULAB_MATH_FUNCTIONS_OUT_KEYWORD (1)
#endif
#ifndef ULAB_NUMPY_HAS_ACOS
#define ULAB_NUMPY_HAS_ACOS (1)
#endif
@ -616,6 +693,10 @@
#define ULAB_NUMPY_HAS_SIN (1)
#endif
#ifndef ULAB_NUMPY_HAS_SINC
#define ULAB_NUMPY_HAS_SINC (1)
#endif
#ifndef ULAB_NUMPY_HAS_SINH
#define ULAB_NUMPY_HAS_SINH (1)
#endif
@ -654,6 +735,24 @@
#define ULAB_NUMPY_HAS_SORT_COMPLEX (1)
#endif
// random module
#ifndef ULAB_NUMPY_HAS_RANDOM_MODULE
#define ULAB_NUMPY_HAS_RANDOM_MODULE (1)
#endif
#ifndef ULAB_NUMPY_RANDOM_HAS_NORMAL
#define ULAB_NUMPY_RANDOM_HAS_NORMAL (1)
#endif
#ifndef ULAB_NUMPY_RANDOM_HAS_RANDOM
#define ULAB_NUMPY_RANDOM_HAS_RANDOM (1)
#endif
#ifndef ULAB_NUMPY_RANDOM_HAS_UNIFORM
#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1)
#endif
// scipy modules
#ifndef ULAB_SCIPY_HAS_LINALG_MODULE
#define ULAB_SCIPY_HAS_LINALG_MODULE (1)

View file

@ -162,48 +162,55 @@ 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;
// The shape and strides at `axis` are moved to the zeroth position,
// everything else is aligned to the right
if(!mp_obj_is_int(axis) & (axis != mp_const_none)) {
mp_raise_TypeError(translate("axis must be None, or an integer"));
mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer"));
}
shape_strides _shape_strides;
_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);
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)
_shape_strides.axis = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten)
if(axis != mp_const_none) { // i.e., axis is an integer
int8_t ax = mp_obj_get_int(axis);
if(ax < 0) ax += ndarray->ndim;
if((ax < 0) || (ax > ndarray->ndim - 1)) {
mp_raise_ValueError(translate("index out of range"));
}
index = ULAB_MAX_DIMS - ndarray->ndim + ax;
int8_t ax = tools_get_axis(axis, ndarray->ndim);
_shape_strides.axis = ULAB_MAX_DIMS - ndarray->ndim + ax;
_shape_strides.ndim = ndarray->ndim - 1;
}
// move the value stored at index to the leftmost position, and align everything else to the right
_shape_strides.shape[0] = ndarray->shape[index];
_shape_strides.strides[0] = ndarray->strides[index];
for(uint8_t i = 0; i < index; i++) {
_shape_strides.shape[0] = ndarray->shape[_shape_strides.axis];
_shape_strides.strides[0] = ndarray->strides[_shape_strides.axis];
for(uint8_t i = 0; i < _shape_strides.axis; i++) {
// entries to the right of index must be shifted by one position to the left
_shape_strides.shape[i + 1] = ndarray->shape[i];
_shape_strides.strides[i + 1] = ndarray->strides[i];
@ -213,16 +220,37 @@ shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) {
_shape_strides.increment = 1;
}
if(_shape_strides.ndim == 0) {
_shape_strides.ndim = 1;
_shape_strides.shape[ULAB_MAX_DIMS - 1] = 1;
_shape_strides.strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
}
return _shape_strides;
}
int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) {
int8_t ax = mp_obj_get_int(axis);
if(ax < 0) ax += ndim;
if((ax < 0) || (ax > ndim - 1)) {
mp_raise_ValueError(translate("axis is out of bounds"));
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t *ndarray, ndarray_obj_t *results, mp_obj_t keepdims, shape_strides _shape_strides) {
// restores the contracted dimension, if keepdims is True
if((ndarray->ndim == 1) && (keepdims != mp_const_true)) {
// since the original array has already been contracted and
// we don't want to keep the dimensions here, we have to return a scalar
return mp_binary_get_val_array(results->dtype, results->array, 0);
}
return ax;
if(keepdims == mp_const_true) {
results->ndim += 1;
for(int8_t i = 0; i < ULAB_MAX_DIMS; i++) {
results->shape[i] = ndarray->shape[i];
}
results->shape[_shape_strides.axis] = 1;
results->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
for(uint8_t i = ULAB_MAX_DIMS; i > 1; i--) {
results->strides[i - 2] = results->strides[i - 1] * results->shape[i - 1];
}
}
return MP_OBJ_FROM_PTR(results);
}
#if ULAB_MAX_DIMS > 1
@ -230,11 +258,11 @@ ndarray_obj_t *tools_object_is_square(mp_obj_t obj) {
// Returns an ndarray, if the object is a square ndarray,
// raises the appropriate exception otherwise
if(!mp_obj_is_type(obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("size is defined for ndarrays only"));
mp_raise_TypeError(MP_ERROR_TEXT("size is defined for ndarrays only"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj);
if((ndarray->shape[ULAB_MAX_DIMS - 1] != ndarray->shape[ULAB_MAX_DIMS - 2]) || (ndarray->ndim != 2)) {
mp_raise_ValueError(translate("input must be square matrix"));
mp_raise_ValueError(MP_ERROR_TEXT("input must be square matrix"));
}
return ndarray;
}
@ -274,3 +302,31 @@ bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) {
}
#endif
}
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t out, uint8_t dtype, uint8_t ndim, size_t *shape, bool dense_only) {
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out);
if(ndarray->dtype != dtype) {
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dtype"));
}
if(ndarray->ndim != ndim) {
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dimension"));
}
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
if(ndarray->shape[i] != shape[i]) {
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong shape"));
}
}
if(dense_only) {
if(!ndarray_is_dense(ndarray)) {
mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous"));
}
}
return ndarray;
}

View file

@ -17,6 +17,7 @@
typedef struct _shape_strides_t {
uint8_t increment;
uint8_t axis;
uint8_t ndim;
size_t *shape;
int32_t *strides;
@ -34,6 +35,7 @@ void *ndarray_set_float_function(uint8_t );
shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t );
int8_t tools_get_axis(mp_obj_t , uint8_t );
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t * , ndarray_obj_t * , mp_obj_t , shape_strides );
ndarray_obj_t *tools_object_is_square(mp_obj_t );
uint8_t ulab_binary_get_size(uint8_t );
@ -43,4 +45,7 @@ void ulab_rescale_float_strides(int32_t *);
#endif
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 );
#endif

View file

@ -28,13 +28,13 @@ static mp_obj_t user_square(mp_obj_t arg) {
// raise a TypeError exception, if the input is not an ndarray
if(!mp_obj_is_type(arg, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("input must be an ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(arg);
// make sure that the input is a dense array
if(!ndarray_is_dense(ndarray)) {
mp_raise_TypeError(translate("input must be a dense ndarray"));
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
}
// if the input is a dense array, create `results` with the same number of
@ -92,11 +92,7 @@ const mp_obj_module_t ulab_user_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module, ULAB_HAS_USER_MODULE);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module);
#endif
#endif
#endif

View file

@ -5,7 +5,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
* Copyright (c) 2020-2024 Zoltán Vörös
*/
#include <math.h>
@ -16,6 +16,7 @@
#include "py/misc.h"
#include "utils.h"
#include "../ulab_tools.h"
#include "../numpy/fft/fft_tools.h"
#if ULAB_HAS_UTILS_MODULE
@ -45,7 +46,7 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a
if(args[3].u_obj != mp_const_none) {
ndarray = MP_OBJ_TO_PTR(args[3].u_obj);
if((ndarray->dtype != NDARRAY_FLOAT) || !ndarray_is_dense(ndarray)) {
mp_raise_TypeError(translate("out must be a float dense array"));
mp_raise_TypeError(MP_ERROR_TEXT("out must be a float dense array"));
}
}
@ -54,7 +55,7 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a
mp_buffer_info_t bufinfo;
if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) {
if(bufinfo.len < offset) {
mp_raise_ValueError(translate("offset is too large"));
mp_raise_ValueError(MP_ERROR_TEXT("offset is too large"));
}
uint8_t sz = sizeof(int16_t);
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
@ -65,12 +66,12 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a
size_t len = (bufinfo.len - offset) / sz;
if((len * sz) != (bufinfo.len - offset)) {
mp_raise_ValueError(translate("buffer size must be a multiple of element size"));
mp_raise_ValueError(MP_ERROR_TEXT("buffer size must be a multiple of element size"));
}
if(mp_obj_get_int(args[1].u_obj) > 0) {
size_t count = mp_obj_get_int(args[1].u_obj);
if(len < count) {
mp_raise_ValueError(translate("buffer is smaller than requested size"));
mp_raise_ValueError(MP_ERROR_TEXT("buffer is smaller than requested size"));
} else {
len = count;
}
@ -79,7 +80,7 @@ static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_a
ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT);
} else {
if(ndarray->len < len) {
mp_raise_ValueError(translate("out array is too small"));
mp_raise_ValueError(MP_ERROR_TEXT("out array is too small"));
}
}
uint8_t *buffer = bufinfo.buf;
@ -203,23 +204,180 @@ MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_bu
//| ...
//|
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *args) {
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
return fft_fft_ifft_spectrogram(args[0], FFT_SPECTROGRAM);
#else
if(n_args == 2) {
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_SPECTROGRAM);
} else {
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_SPECTROGRAM);
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE }} ,
#if !ULAB_FFT_IS_NUMPY_COMPATIBLE
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
#endif
{ MP_QSTR_scratchpad, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_log, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
#if ULAB_MAX_DIMS > 1
if(in->ndim != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
}
#endif
size_t len = in->len;
// Check if input is of length of power of 2
if((len & (len-1)) != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
}
ndarray_obj_t *out = NULL;
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
mp_obj_t scratchpad_object = args[1].u_obj;
mp_obj_t out_object = args[2].u_obj;
mp_obj_t log_object = args[3].u_obj;
#else
mp_obj_t scratchpad_object = args[2].u_obj;
mp_obj_t out_object = args[3].u_obj;
mp_obj_t log_object = args[4].u_obj;
#endif
if(out_object != mp_const_none) {
if(!mp_obj_is_type(out_object, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("out must be an ndarray"));
}
out = MP_OBJ_TO_PTR(out_object);
if((out->dtype != NDARRAY_FLOAT) || (out->ndim != 1)){
mp_raise_TypeError(MP_ERROR_TEXT("out array must be a 1D array of float type"));
}
if(len != out->len) {
mp_raise_ValueError(MP_ERROR_TEXT("input and out arrays must have same length"));
}
} else {
out = ndarray_new_linear_array(len, NDARRAY_FLOAT);
}
ndarray_obj_t *scratchpad = NULL;
mp_float_t *tmp = NULL;
if(scratchpad_object != mp_const_none) {
if(!mp_obj_is_type(scratchpad_object, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("scratchpad must be an ndarray"));
}
scratchpad = MP_OBJ_TO_PTR(scratchpad_object);
if(!ndarray_is_dense(scratchpad) || (scratchpad->ndim != 1) || (scratchpad->dtype != NDARRAY_FLOAT)) {
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be a 1D dense float array"));
}
if(scratchpad->len != 2 * len) {
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be twice as long as input"));
}
tmp = (mp_float_t *)scratchpad->array;
} else {
tmp = m_new0(mp_float_t, 2 * len);
}
uint8_t *array = (uint8_t *)in->array;
#if ULAB_FFT_IS_NUMPY_COMPATIBLE & ULAB_SUPPORTS_COMPLEX
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);
}
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 1, utils_spectrogram);
#else
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 2, utils_spectrogram);
#endif
MP_DEFINE_CONST_FUN_OBJ_KW(utils_spectrogram_obj, 1, utils_spectrogram);
#endif /* ULAB_UTILS_HAS_SPECTROGRAM */
@ -250,11 +408,7 @@ const mp_obj_module_t ulab_utils_module = {
.globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals,
};
#if CIRCUITPY_ULAB
#if !defined(MICROPY_VERSION) || MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module, MODULE_ULAB_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module);
#endif
#endif
#endif /* ULAB_HAS_UTILS_MODULE */

View file

@ -23,11 +23,11 @@ from sphinx import addnodes
# -- Project information -----------------------------------------------------
project = 'The ulab book'
copyright = '2019-2022, Zoltán Vörös and contributors'
copyright = '2019-2025, Zoltán Vörös and contributors'
author = 'Zoltán Vörös'
# The full version, including alpha/beta/rc tags
release = '5.1.0'
release = '6.9.0'
# -- General configuration ---------------------------------------------------

View file

@ -22,6 +22,8 @@ Welcome to the ulab book!
numpy-universal
numpy-fft
numpy-linalg
numpy-random
scipy-integrate
scipy-linalg
scipy-optimize
scipy-signal

View file

@ -3,8 +3,8 @@ Numpy functions
===============
This section of the manual discusses those functions that were adapted
from ``numpy``. Starred functions accept complex arrays as arguments, if
the firmware was compiled with complex support.
from ``numpy``. Functions with an asterisk accept complex arrays as
arguments, if the firmware was compiled with complex support.
1. `numpy.all\* <#all>`__
2. `numpy.any\* <#any>`__
@ -12,43 +12,49 @@ the firmware was compiled with complex support.
4. `numpy.argmin <#argmin>`__
5. `numpy.argsort <#argsort>`__
6. `numpy.asarray\* <#asarray>`__
7. `numpy.clip <#clip>`__
8. `numpy.compress\* <#compress>`__
9. `numpy.conjugate\* <#conjugate>`__
10. `numpy.convolve\* <#convolve>`__
11. `numpy.delete <#delete>`__
12. `numpy.diff <#diff>`__
13. `numpy.dot <#dot>`__
14. `numpy.equal <#equal>`__
15. `numpy.flip\* <#flip>`__
16. `numpy.imag\* <#imag>`__
17. `numpy.interp <#interp>`__
18. `numpy.isfinite <#isfinite>`__
19. `numpy.isinf <#isinf>`__
20. `numpy.load <#load>`__
21. `numpy.loadtxt <#loadtxt>`__
22. `numpy.max <#max>`__
23. `numpy.maximum <#maximum>`__
24. `numpy.mean <#mean>`__
25. `numpy.median <#median>`__
26. `numpy.min <#min>`__
27. `numpy.minimum <#minimum>`__
28. `numpy.nozero <#nonzero>`__
29. `numpy.not_equal <#equal>`__
30. `numpy.polyfit <#polyfit>`__
31. `numpy.polyval <#polyval>`__
32. `numpy.real\* <#real>`__
33. `numpy.roll <#roll>`__
34. `numpy.save <#save>`__
35. `numpy.savetxt <#savetxt>`__
36. `numpy.size <#size>`__
37. `numpy.sort <#sort>`__
38. `numpy.sort_complex\* <#sort_complex>`__
39. `numpy.std <#std>`__
40. `numpy.sum <#sum>`__
41. `numpy.trace <#trace>`__
42. `numpy.trapz <#trapz>`__
43. `numpy.where <#where>`__
7. `numpy.bitwise_and <#bitwise_and>`__
8. `numpy.bitwise_or <#bitwise_and>`__
9. `numpy.bitwise_xor <#bitwise_and>`__
10. `numpy.clip <#clip>`__
11. `numpy.compress\* <#compress>`__
12. `numpy.conjugate\* <#conjugate>`__
13. `numpy.convolve\* <#convolve>`__
14. `numpy.delete <#delete>`__
15. `numpy.diff <#diff>`__
16. `numpy.dot <#dot>`__
17. `numpy.equal <#equal>`__
18. `numpy.flip\* <#flip>`__
19. `numpy.imag\* <#imag>`__
20. `numpy.interp <#interp>`__
21. `numpy.isfinite <#isfinite>`__
22. `numpy.isinf <#isinf>`__
23. `numpy.left_shift <#left_shift>`__
24. `numpy.load <#load>`__
25. `numpy.loadtxt <#loadtxt>`__
26. `numpy.max <#max>`__
27. `numpy.maximum <#maximum>`__
28. `numpy.mean <#mean>`__
29. `numpy.median <#median>`__
30. `numpy.min <#min>`__
31. `numpy.minimum <#minimum>`__
32. `numpy.nozero <#nonzero>`__
33. `numpy.not_equal <#equal>`__
34. `numpy.polyfit <#polyfit>`__
35. `numpy.polyval <#polyval>`__
36. `numpy.real\* <#real>`__
37. `numpy.right_shift <#right_shift>`__
38. `numpy.roll <#roll>`__
39. `numpy.save <#save>`__
40. `numpy.savetxt <#savetxt>`__
41. `numpy.size <#size>`__
42. `numpy.sort <#sort>`__
43. `numpy.sort_complex\* <#sort_complex>`__
44. `numpy.std <#std>`__
45. `numpy.sum <#sum>`__
46. `numpy.take\* <#take>`__
47. `numpy.trace <#trace>`__
48. `numpy.trapz <#trapz>`__
49. `numpy.where <#where>`__
all
---
@ -323,6 +329,58 @@ an alias for ``array``.
bitwise_and
-----------
``numpy``: https://numpy.org/doc/stable/reference/routines.bitwise.html
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html
Each of ``bitwise_and``, ``bitwise_or``, and ``bitwise_xor`` takes two
integer-type ``ndarray``\ s as arguments, and returns the element-wise
results of the ``AND``, ``OR``, and ``XOR`` operators. Broadcasting is
supported. If the ``dtype`` of the input arrays is not an integer, and
exception will be raised.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array(range(8), dtype=np.uint8)
b = a + 1
print(a)
print(b)
print('\nbitwise_and:\n', np.bitwise_and(a, b))
print('\nbitwise_or:\n', np.bitwise_or(a, b))
print('\nbitwise_xor:\n', np.bitwise_xor(a, b))
.. parsed-literal::
array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)
array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)
bitwise_and:
array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8)
bitwise_or:
array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8)
bitwise_xor:
array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8)
clip
----
@ -987,6 +1045,52 @@ positions, where the input is infinite. Integer types return the
left_shift
----------
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html
``left_shift``, and ``right_shift`` both take two integer-type
``ndarray``\ s, and bit-wise shift the elements of the first array by an
amount given by the second array to the left, and right, respectively.
Broadcasting is supported. If the ``dtype`` of the input arrays is not
an integer, and exception will be raised.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.ones(7, dtype=np.uint8)
b = np.zeros(7, dtype=np.uint8) + 255
c = np.array(range(7), dtype=np.uint8) + 1
print('a: ', a)
print('b: ', b)
print('c: ', c)
print('\na left shifted by c:\n', np.left_shift(a, c))
print('\nb right shifted by c:\n', np.right_shift(b, c))
.. parsed-literal::
a: array([1, 1, 1, 1, 1, 1, 1], dtype=uint8)
b: array([255, 255, 255, 255, 255, 255, 255], dtype=uint8)
c: array([1, 2, 3, 4, 5, 6, 7], dtype=uint8)
a left shifted by c:
array([2, 4, 8, 16, 32, 64, 128], dtype=uint8)
b right shifted by c:
array([127, 63, 31, 15, 7, 3, 1], dtype=uint8)
load
----
@ -1882,6 +1986,66 @@ array. Otherwise, the calculation is along the given axis.
take
----
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.take.html
The ``take`` method takes elements from an array along an axis. The
function accepts two positional arguments, the array, and the indices,
which is either a ``python`` iterable, or a one-dimensional ``ndarray``,
as well as three keyword arguments, the ``axis``, which can be ``None``,
or an integer, ``out``, which can be ``None``, or an ``ndarray`` with
the proper dimensions, and ``mode``, which can be one of the strings
``raise``, ``wrap``, or ``clip``. This last argument determines how
out-of-bounds indices will be treated. The default value is ``raise``,
which raises an exception. ``wrap`` takes the indices modulo the length
of the ``axis``, while ``clip`` pegs the values at the 0, and the length
of the ``axis``. If ``axis`` is ``None``, then ``take`` operates on the
flattened array.
The function can be regarded as a method of advanced slicing: as opposed
to standard slicing, where the indices are distributed uniformly and in
either increasing or decreasing order, ``take`` can take indices in an
arbitrary order.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array(range(12)).reshape((3, 4))
print('\na:', a)
print('\nslices taken along first axis')
print(np.take(a, (0, 2, 2, 1), axis=0))
print('\nslices taken along second axis')
print(np.take(a, (0, 2, 2, 1), axis=1))
.. parsed-literal::
a: array([[0.0, 1.0, 2.0, 3.0],
[4.0, 5.0, 6.0, 7.0],
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
slices taken along first axis
array([[0.0, 1.0, 2.0, 3.0],
[8.0, 9.0, 10.0, 11.0],
[8.0, 9.0, 10.0, 11.0],
[4.0, 5.0, 6.0, 7.0]], dtype=float64)
slices taken along second axis
array([[0.0, 2.0, 2.0, 1.0],
[2.0, 3.0, 4.0, 5.0],
[6.0, 7.0, 8.0, 9.0]], dtype=float64)
trace
-----

View file

@ -0,0 +1,183 @@
numpy.random
============
Random numbers drawn specific distributions can be generated by
instantiating a ``Generator`` object, and calling its methods. The
module defines the following three functions:
1. `numpy.random.Generator.normal <#normal>`__
2. `numpy.random.Generator.random <#random>`__
3. `numpy.random.Generator.uniform <#uniform>`__
The ``Generator`` object, when instantiated, takes a single integer as
its argument. This integer is the seed, which will be fed to the 32-bit
or 64-bit routine. More details can be found under
https://www.pcg-random.org/index.html. The generator is a standard
``python`` object that keeps track of its state.
``numpy``: https://numpy.org/doc/stable/reference/random/index.html
normal
------
A random set of number from the ``normal`` distribution can be generated
by calling the generators ``normal`` method. The method takes three
optional arguments, ``loc=0.0``, the centre of the distribution,
``scale=1.0``, the width of the distribution, and ``size=None``, a tuple
containing the shape of the returned array. In case ``size`` is
``None``, a single floating point number is returned.
The ``normal`` method of the ``Generator`` object is based on the
`Box-Muller
transform <https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform>`__.
``numpy``:
https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.html
.. code::
# code to be run in micropython
from ulab import numpy as np
rng = np.random.Generator(123456)
print(rng)
# return single number from a distribution of scale 1, and location 0
print(rng.normal())
print(rng.normal(loc=20.0, scale=10.0, size=(3,3)))
# same as above, with positional arguments
print(rng.normal(20.0, 10.0, (3,3)))
.. parsed-literal::
Gnerator() at 0x7fa9dae05340
-6.285246229407202
array([[24.95816273705659, 15.2670302229426, 14.81001577336041],
[20.17589833056986, 23.14539083787544, 26.37772041367461],
[41.94894234387275, 37.11027030608206, 25.65889562100477]], dtype=float64)
array([[21.52562779033434, 12.74685887865834, 24.08404670765186],
[4.728112596365396, 7.667757906857082, 21.61576094228444],
[2.432338873595267, 27.75945683572574, 5.730827584659245]], dtype=float64)
random
------
A random set of number from the uniform distribution in the interval [0,
1] can be generated by calling the generators ``random`` method. The
method takes two optional arguments, ``size=None``, a tuple containing
the shape of the returned array, and ``out``. In case ``size`` is
``None``, a single floating point number is returned.
``out`` can be used, if a floating point array is available. An
exception will be raised, if the array is not of ``float`` ``dtype``, or
if both ``size`` and ``out`` are supplied, and there is a conflict in
their shapes.
If ``size`` is ``None``, a single floating point number will be
returned.
``numpy``:
https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.html
.. code::
# code to be run in micropython
from ulab import numpy as np
rng = np.random.Generator(123456)
print(rng)
# returning new objects
print(rng.random())
print('\n', rng.random(size=(3,3)))
# supplying a buffer
a = np.array(range(9), dtype=np.float).reshape((3,3))
print('\nbuffer array before:\n', a)
rng.random(out=a)
print('\nbuffer array after:\n', a)
.. parsed-literal::
Gnerator() at 0x7f299de05340
6.384615058863119e-11
array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],
[0.8738606263361598, 0.4946080034142021, 0.7765890156101152],
[0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)
buffer array before:
array([[0.0, 1.0, 2.0],
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]], dtype=float64)
buffer array after:
array([[0.8508024287393201, 0.9848489829156055, 0.7598167589604003],
[0.782995698302952, 0.2866337782847831, 0.7915884498022229],
[0.4614071706315902, 0.4792657443088592, 0.1581582066230718]], dtype=float64)
uniform
-------
``uniform`` is similar to ``random``, except that the interval over
which the numbers are distributed can be specified, while the ``out``
argument cannot. In addition to ``size`` specifying the shape of the
output, ``low=0.0``, and ``high=1.0`` are accepted arguments. With the
indicated defaults, ``uniform`` is identical to ``random``, which can be
seen from the fact that the first 3-by-3 tensor below is the same as the
one produced by ``rng.random(size=(3,3))`` above.
If ``size`` is ``None``, a single floating point number will be
returned.
``numpy``:
https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.html
.. code::
# code to be run in micropython
from ulab import numpy as np
rng = np.random.Generator(123456)
print(rng)
print(rng.uniform())
# returning numbers between 0, and 1
print('\n', rng.uniform(size=(3,3)))
# returning numbers between 10, and 20
print('\n', rng.uniform(low=10, high=20, size=(3,3)))
# same as above, without the keywords
print('\n', rng.uniform(10, 20, (3,3)))
.. parsed-literal::
Gnerator() at 0x7f1891205340
6.384615058863119e-11
array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],
[0.8738606263361598, 0.4946080034142021, 0.7765890156101152],
[0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)
array([[18.5080242873932, 19.84848982915605, 17.598167589604],
[17.82995698302952, 12.86633778284783, 17.91588449802223],
[14.6140717063159, 14.79265744308859, 11.58158206623072]], dtype=float64)
array([[14.3380400319162, 12.72487657409978, 15.77119643621117],
[13.61835831436355, 18.96062889255558, 15.78847796795966],
[12.59435855187034, 17.68262037443622, 14.77943040598734]], dtype=float64)

View file

@ -20,12 +20,18 @@ operate on, or can return complex arrays):
``acos``, ``acosh``, ``arctan2``, ``around``, ``asin``, ``asinh``,
``atan``, ``arctan2``, ``atanh``, ``ceil``, ``cos``, ``degrees``,
``exp*``, ``expm1``, ``floor``, ``log``, ``log10``, ``log2``,
``radians``, ``sin``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``.
``radians``, ``sin``, ``sinc``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``.
These functions are applied element-wise to the arguments, thus, e.g.,
the exponential of a matrix cannot be calculated in this way, only the
exponential of the matrix entries.
In order to avoid repeated memory allocations, functions can take the
``out=None`` optional argument, which must be a floating point
``ndarray`` of the same size as the input ``array``. If these conditions
are not fulfilled, and exception will be raised. If ``out=None``, a new
array will be created upon each invocation of the function.
.. code::
# code to be run in micropython
@ -48,6 +54,13 @@ exponential of the matrix entries.
print('\n=============\nc:\n', c)
print('exp(c):\n', np.exp(c))
# using the `out` argument
d = np.array(range(9)).reshape((3, 3))
print('\nd before invoking the function:\n', d)
np.exp(c, out=d)
print('\nd afteri nvoking the function:\n', d)
.. parsed-literal::
a: range(0, 9)
@ -69,6 +82,16 @@ exponential of the matrix entries.
[20.08553692318767, 54.59815003314424, 148.4131591025766],
[403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)
d before invoking the function:
array([[0.0, 1.0, 2.0],
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]], dtype=float64)
d afteri nvoking the function:
array([[1.0, 2.718281828459045, 7.38905609893065],
[20.08553692318767, 54.59815003314424, 148.4131591025766],
[403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)

View file

@ -0,0 +1,220 @@
scipy.integrate
===============
This module provides a simplified subset of CPythons
``scipy.integrate`` module. The algorithms were not ported from
CPythons ``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 GaussKronrod 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. Rombergs
method is a NewtonCotes 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 ClenshawCurtis
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 CPythons ``simpson`` method in that it
does not take an array of function values but determines the optimal
spacing of samples itself. Adaptive Simpsons method, also called
adaptive Simpsons 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 GaussKronrod quadrature and
ClenshawCurtis 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

View file

@ -8,9 +8,10 @@ Enter ulab
``ulab`` is a ``numpy``-like module for ``micropython`` and its
derivatives, meant to simplify and speed up common mathematical
operations on arrays. ``ulab`` implements a small subset of ``numpy``
and ``scipy``. The functions were chosen such that they might be useful
in the context of a microcontroller. However, the project is a living
one, and suggestions for new features are always welcome.
and ``scipy``, as well as a number of functions manipulating byte
arrays. The functions were chosen such that they might be useful in the
context of a microcontroller. However, the project is a living one, and
suggestions for new features are always welcome.
This document discusses how you can use the library, starting from
building your own firmware, through questions like what affects the
@ -265,9 +266,9 @@ functions that are part of ``numpy``, you have to import ``numpy`` as
p = np.array([1, 2, 3])
np.polyval(p, x)
There are a couple of exceptions to this rule, namely ``fft``, and
``linalg``, which are sub-modules even in ``numpy``, thus you have to
write them out as
There are a couple of exceptions to this rule, namely ``fft``,
``linalg``, and ``random``, which are sub-modules even in ``numpy``,
thus you have to write them out as
.. code:: python

View file

@ -1814,14 +1814,19 @@ array.
Binary operators
================
``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``<``,
``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, ``/=``,
``**=`` binary operators that work element-wise. Broadcasting is
available, meaning that the two operands do not even have to have the
same shape. If the lengths along the respective axes are equal, or one
of them is 1, or the axis is missing, the element-wise operation can
still be carried out. A thorough explanation of broadcasting can be
found under https://numpy.org/doc/stable/user/basics.broadcasting.html.
``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
are equal, or one of them is 1, or the axis is missing, the element-wise
operation can still be carried out. A thorough explanation of
broadcasting can be found under
https://numpy.org/doc/stable/user/basics.broadcasting.html.
**WARNING**: note that relational operators (``<``, ``>``, ``<=``,
``>=``, ``==``, ``!=``) should have the ``ndarray`` on their left hand
@ -2325,12 +2330,12 @@ future version of ``ulab``.
a = np.array(range(9), dtype=np.float)
print("a:\t", a)
print("a < 5:\t", a[a < 5])
print("a[a < 5]:\t", a[a < 5])
.. parsed-literal::
a: array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float)
a < 5: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
a[a < 5]: array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)

View file

@ -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 },
};

View file

@ -52,7 +52,9 @@ Here is an example without keyword arguments
a = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
print('a: ', a)
print()
print('unsigned integers: ', utils.from_uint32_buffer(a))
print('unsigned integers: ', utils.from_uint32_buffe
print('original vector:\n', y)
print('\nspectrum:\n', a)r(a))
b = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
print('\nb: ', b)
@ -144,9 +146,53 @@ In addition to the Fourier transform and its inverse, ``ulab`` also
sports a function called ``spectrogram``, which returns the absolute
value of the Fourier transform, also known as the power spectrum. This
could be used to find the dominant spectral component in a time series.
The arguments are treated in the same way as in ``fft``, and ``ifft``.
This means that, if the firmware was compiled with complex support, the
input can also be a complex array.
The positional arguments are treated in the same way as in ``fft``, and
``ifft``. This means that, if the firmware was compiled with complex
support and ``ULAB_FFT_IS_NUMPY_COMPATIBLE`` is defined to be 1 in
``ulab.h``, the input can also be a complex array.
And easy way to find out if the FFT is ``numpy``-compatible is to check
the number of values ``fft.fft`` returns, when called with a single real
argument of length other than 2:
.. code::
# code to be run in micropython
from ulab import numpy as np
if len(np.fft.fft(np.zeros(4))) == 2:
print('FFT is NOT numpy compatible (real and imaginary parts are treated separately)')
else:
print('FFT is numpy compatible (complex inputs/outputs)')
.. parsed-literal::
FFT is numpy compatible (complex inputs/outputs)
Depending on the ``numpy``-compatibility of the FFT, the ``spectrogram``
function takes one or two positional arguments, and three keyword
arguments. If the FFT is ``numpy`` compatible, one positional argument
is allowed, and it is a 1D real or complex ``ndarray``. If the FFT is
not ``numpy``-compatible, if a single argument is supplied, it will be
treated as the real part of the input, and if two positional arguments
are supplied, they are treated as the real and imaginary parts of the
signal.
The keyword arguments are as follows:
1. ``scratchpad = None``: must be a 1D, dense, floating point array,
twice as long as the input array; the ``scratchpad`` will be used as
a temporary internal buffer to perform the Fourier transform; the
``scratchpad`` can repeatedly be re-used.
2. ``out = None``: must be a 1D, not necessarily dense, floating point
array that will store the results
3. ``log = False``: must be either ``True``, or ``False``; if ``True``,
the ``spectrogram`` returns the logarithm of the absolute values of
the Fourier transform.
.. code::
@ -169,17 +215,24 @@ input can also be a complex array.
array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64)
spectrum:
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
array([187.8635087634578, 315.3112063607119, 347.8814873399375, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
As such, ``spectrogram`` is really just a shorthand for
``np.sqrt(a*a + b*b)``, however, it saves significant amounts of RAM:
the expression ``a*a + b*b`` has to allocate memory for ``a*a``,
``b*b``, and finally, their sum. In contrast, ``spectrogram`` calculates
the spectrum internally, and stores it in the memory segment that was
reserved for the real part of the Fourier transform.
``np.abs(np.fft.fft(signal))``, if the FFT is ``numpy``-compatible, or
``np.sqrt(a*a + b*b)`` if the FFT returns the real (``a``) and imaginary
(``b``) parts separately. However, ``spectrogram`` saves significant
amounts of RAM: the expression ``a*a + b*b`` has to allocate memory for
``a*a``, ``b*b``, and finally, their sum. Similarly, ``np.abs`` returns
a new array. This issue is compounded even more, if ``np.log()`` is used
on the absolute value.
In contrast, ``spectrogram`` handles all calculations in the same
internal arrays, and allows one to re-use previously reserved RAM. This
can be especially useful in cases, when ``spectogram`` is called
repeatedly, as in the snippet below.
.. code::
@ -188,25 +241,34 @@ reserved for the real part of the Fourier transform.
from ulab import numpy as np
from ulab import utils as utils
x = np.linspace(0, 10, num=1024)
y = np.sin(x)
n = 1024
t = np.linspace(0, 2 * np.pi, num=1024)
scratchpad = np.zeros(2 * n)
a, b = np.fft.fft(y)
for _ in range(10):
signal = np.sin(t)
utils.spectrogram(signal, out=signal, scratchpad=scratchpad, log=True)
print('\nspectrum calculated the hard way:\n', np.sqrt(a*a + b*b))
print('signal: ', signal)
a = utils.spectrogram(y)
for _ in range(10):
signal = np.sin(t)
out = np.log(utils.spectrogram(signal))
print('\nspectrum calculated the lazy way:\n', a)
print('out: ', out)
.. parsed-literal::
spectrum calculated the hard way:
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
spectrum calculated the lazy way:
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
signal: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)
out: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)
Note that ``scratchpad`` is reserved only once, and then is re-used in
the first loop. By assigning ``signal`` to the output, we save
additional RAM. This approach avoids the usual problem of memory
fragmentation, which would happen in the second loop, where both
``spectrogram``, and ``np.log`` must reserve RAM in each iteration.

View file

@ -31,7 +31,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2022-02-01T17:37:25.505687Z",
@ -49,7 +49,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-02-01T17:37:25.717714Z",
@ -77,7 +77,7 @@
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
" fout.write(cell)\n",
" proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
" print(proc.stdout.read().decode(\"utf-8\"))\n",
" print(proc.stderr.read().decode(\"utf-8\"))\n",
@ -230,7 +230,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This section of the manual discusses those functions that were adapted from `numpy`. Starred functions accept complex arrays as arguments, if the firmware was compiled with complex support.\n",
"This section of the manual discusses those functions that were adapted from `numpy`. Functions with an asterisk accept complex arrays as arguments, if the firmware was compiled with complex support.\n",
"\n",
"1. [numpy.all*](#all)\n",
"1. [numpy.any*](#any)\n",
@ -238,6 +238,9 @@
"1. [numpy.argmin](#argmin)\n",
"1. [numpy.argsort](#argsort)\n",
"1. [numpy.asarray*](#asarray)\n",
"1. [numpy.bitwise_and](#bitwise_and)\n",
"1. [numpy.bitwise_or](#bitwise_and)\n",
"1. [numpy.bitwise_xor](#bitwise_and)\n",
"1. [numpy.clip](#clip)\n",
"1. [numpy.compress*](#compress)\n",
"1. [numpy.conjugate*](#conjugate)\n",
@ -251,6 +254,7 @@
"1. [numpy.interp](#interp)\n",
"1. [numpy.isfinite](#isfinite)\n",
"1. [numpy.isinf](#isinf)\n",
"1. [numpy.left_shift](#left_shift)\n",
"1. [numpy.load](#load)\n",
"1. [numpy.loadtxt](#loadtxt)\n",
"1. [numpy.max](#max)\n",
@ -264,6 +268,7 @@
"1. [numpy.polyfit](#polyfit)\n",
"1. [numpy.polyval](#polyval)\n",
"1. [numpy.real*](#real)\n",
"1. [numpy.right_shift](#right_shift)\n",
"1. [numpy.roll](#roll)\n",
"1. [numpy.save](#save)\n",
"1. [numpy.savetxt](#savetxt)\n",
@ -272,6 +277,7 @@
"1. [numpy.sort_complex*](#sort_complex)\n",
"1. [numpy.std](#std)\n",
"1. [numpy.sum](#sum)\n",
"1. [numpy.take*](#take)\n",
"1. [numpy.trace](#trace)\n",
"1. [numpy.trapz](#trapz)\n",
"1. [numpy.where](#where)"
@ -606,6 +612,63 @@
"print('a == c: {}'.format(a is c))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## bitwise_and\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/routines.bitwise.html\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html\n",
"\n",
"Each of `bitwise_and`, `bitwise_or`, and `bitwise_xor` takes two integer-type `ndarray`s as arguments, and returns the element-wise results of the `AND`, `OR`, and `XOR` operators. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n",
"array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n",
"\n",
"bitwise_and:\n",
" array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8)\n",
"\n",
"bitwise_or:\n",
" array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8)\n",
"\n",
"bitwise_xor:\n",
" array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array(range(8), dtype=np.uint8)\n",
"b = a + 1\n",
"\n",
"print(a)\n",
"print(b)\n",
"print('\\nbitwise_and:\\n', np.bitwise_and(a, b))\n",
"print('\\nbitwise_or:\\n', np.bitwise_or(a, b))\n",
"print('\\nbitwise_xor:\\n', np.bitwise_xor(a, b))"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -1423,6 +1486,58 @@
"print('\\nisinf(c):\\n', np.isinf(c))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## left_shift\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html\n",
"\n",
"`left_shift`, and `right_shift` both take two integer-type `ndarray`s, and bit-wise shift the elements of the first array by an amount given by the second array to the left, and right, respectively. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a: array([1, 1, 1, 1, 1, 1, 1], dtype=uint8)\n",
"b: array([255, 255, 255, 255, 255, 255, 255], dtype=uint8)\n",
"c: array([1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n",
"\n",
"a left shifted by c:\n",
" array([2, 4, 8, 16, 32, 64, 128], dtype=uint8)\n",
"\n",
"b right shifted by c:\n",
" array([127, 63, 31, 15, 7, 3, 1], dtype=uint8)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"a = np.ones(7, dtype=np.uint8)\n",
"b = np.zeros(7, dtype=np.uint8) + 255\n",
"c = np.array(range(7), dtype=np.uint8) + 1\n",
"\n",
"print('a: ', a)\n",
"print('b: ', b)\n",
"print('c: ', c)\n",
"print('\\na left shifted by c:\\n', np.left_shift(a, c))\n",
"print('\\nb right shifted by c:\\n', np.right_shift(b, c))"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -2568,6 +2683,63 @@
"print('std, vertical: ', np.sum(a, axis=0))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## take\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.take.html\n",
"\n",
"The `take` method takes elements from an array along an axis. The function accepts two positional arguments, the array, and the indices, which is either a `python` iterable, or a one-dimensional `ndarray`, as well as three keyword arguments, the `axis`, which can be `None`, or an integer, `out`, which can be `None`, or an `ndarray` with the proper dimensions, and `mode`, which can be one of the strings `raise`, `wrap`, or `clip`. This last argument determines how out-of-bounds indices will be treated. The default value is `raise`, which raises an exception. `wrap` takes the indices modulo the length of the `axis`, while `clip` pegs the values at the 0, and the length of the `axis`. If `axis` is `None`, then `take` operates on the flattened array.\n",
"\n",
"The function can be regarded as a method of advanced slicing: as opposed to standard slicing, where the indices are distributed uniformly and in either increasing or decreasing order, `take` can take indices in an arbitrary order."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"a: array([[0.0, 1.0, 2.0, 3.0],\n",
" [4.0, 5.0, 6.0, 7.0],\n",
" [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
"\n",
"slices taken along first axis\n",
"array([[0.0, 1.0, 2.0, 3.0],\n",
" [8.0, 9.0, 10.0, 11.0],\n",
" [8.0, 9.0, 10.0, 11.0],\n",
" [4.0, 5.0, 6.0, 7.0]], dtype=float64)\n",
"\n",
"slices taken along second axis\n",
"array([[0.0, 2.0, 2.0, 1.0],\n",
" [2.0, 3.0, 4.0, 5.0],\n",
" [6.0, 7.0, 8.0, 9.0]], dtype=float64)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array(range(12)).reshape((3, 4))\n",
"print('\\na:', a)\n",
"\n",
"print('\\nslices taken along first axis')\n",
"print(np.take(a, (0, 2, 2, 1), axis=0))\n",
"\n",
"print('\\nslices taken along second axis')\n",
"print(np.take(a, (0, 2, 2, 1), axis=1))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -2786,7 +2958,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.11.7"
},
"toc": {
"base_numbering": 1,

492
docs/numpy-random.ipynb Normal file
View file

@ -0,0 +1,492 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-01T09:27:13.438054Z",
"start_time": "2020-05-01T09:27:13.191491Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
}
],
"source": [
"%pylab inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Notebook magic"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-07T18:24:48.499467Z",
"start_time": "2022-01-07T18:24:48.488004Z"
}
},
"outputs": [],
"source": [
"from IPython.core.magic import Magics, magics_class, line_cell_magic\n",
"from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n",
"from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n",
"import subprocess\n",
"import os"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2020-07-23T20:31:25.296014Z",
"start_time": "2020-07-23T20:31:25.265937Z"
}
},
"outputs": [],
"source": [
"@magics_class\n",
"class PyboardMagic(Magics):\n",
" @cell_magic\n",
" @magic_arguments()\n",
" @argument('-skip')\n",
" @argument('-unix')\n",
" @argument('-pyboard')\n",
" @argument('-file')\n",
" @argument('-data')\n",
" @argument('-time')\n",
" @argument('-memory')\n",
" def micropython(self, line='', cell=None):\n",
" args = parse_argstring(self.micropython, line)\n",
" if args.skip: # doesn't care about the cell's content\n",
" print('skipped execution')\n",
" return None # do not parse the rest\n",
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
" fout.write(cell)\n",
" proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
" print(proc.stdout.read().decode(\"utf-8\"))\n",
" print(proc.stderr.read().decode(\"utf-8\"))\n",
" return None\n",
" if args.file: # can be used to copy the cell content onto the pyboard's flash\n",
" spaces = \" \"\n",
" try:\n",
" with open(args.file, 'w') as fout:\n",
" fout.write(cell.replace('\\t', spaces))\n",
" printf('written cell to {}'.format(args.file))\n",
" except:\n",
" print('Failed to write to disc!')\n",
" return None # do not parse the rest\n",
" if args.data: # can be used to load data from the pyboard directly into kernel space\n",
" message = pyb.exec(cell)\n",
" if len(message) == 0:\n",
" print('pyboard >>>')\n",
" else:\n",
" print(message.decode('utf-8'))\n",
" # register new variable in user namespace\n",
" self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n",
" \n",
" if args.time: # measures the time of executions\n",
" pyb.exec('import utime')\n",
" message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n",
" \"\\nprint('execution time: {:d} us'.format(delta))\")\n",
" print(message.decode('utf-8'))\n",
" \n",
" if args.memory: # prints out memory information \n",
" message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n",
" print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n",
" message = pyb.exec(cell)\n",
" print(\">>> \", message.decode('utf-8'))\n",
" message = pyb.exec('print(mem_info())')\n",
" print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n",
"\n",
" if args.pyboard:\n",
" message = pyb.exec(cell)\n",
" print(message.decode('utf-8'))\n",
"\n",
"ip = get_ipython()\n",
"ip.register_magics(PyboardMagic)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## pyboard"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-07T07:35:35.126401Z",
"start_time": "2020-05-07T07:35:35.105824Z"
}
},
"outputs": [],
"source": [
"import pyboard\n",
"pyb = pyboard.Pyboard('/dev/ttyACM0')\n",
"pyb.enter_raw_repl()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-19T19:11:18.145548Z",
"start_time": "2020-05-19T19:11:18.137468Z"
}
},
"outputs": [],
"source": [
"pyb.exit_raw_repl()\n",
"pyb.close()"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-07T07:35:38.725924Z",
"start_time": "2020-05-07T07:35:38.645488Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"%%micropython -pyboard 1\n",
"\n",
"import utime\n",
"import ulab as np\n",
"\n",
"def timeit(n=1000):\n",
" def wrapper(f, *args, **kwargs):\n",
" func_name = str(f).split(' ')[1]\n",
" def new_func(*args, **kwargs):\n",
" run_times = np.zeros(n, dtype=np.uint16)\n",
" for i in range(n):\n",
" t = utime.ticks_us()\n",
" result = f(*args, **kwargs)\n",
" run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n",
" print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n",
" print('\\tbest: %d us'%np.min(run_times))\n",
" print('\\tworst: %d us'%np.max(run_times))\n",
" print('\\taverage: %d us'%np.mean(run_times))\n",
" print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n",
" return result\n",
" return new_func\n",
" return wrapper\n",
"\n",
"def timeit(f, *args, **kwargs):\n",
" func_name = str(f).split(' ')[1]\n",
" def new_func(*args, **kwargs):\n",
" t = utime.ticks_us()\n",
" result = f(*args, **kwargs)\n",
" print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n",
" return result\n",
" return new_func"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"__END_OF_DEFS__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# numpy.random\n",
"\n",
"Random numbers drawn specific distributions can be generated by instantiating a `Generator` object, and calling its methods. The module defines the following three functions:\n",
"\n",
"1. [numpy.random.Generator.normal](#normal)\n",
"1. [numpy.random.Generator.random](#random)\n",
"1. [numpy.random.Generator.uniform](#uniform)\n",
"\n",
"\n",
"The `Generator` object, when instantiated, takes a single integer as its argument. This integer is the seed, which will be fed to the 32-bit or 64-bit routine. More details can be found under https://www.pcg-random.org/index.html. The generator is a standard `python` object that keeps track of its state.\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/random/index.html"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## normal\n",
"\n",
"A random set of number from the `normal` distribution can be generated by calling the generator's `normal` method. The method takes three optional arguments, `loc=0.0`, the centre of the distribution, `scale=1.0`, the width of the distribution, and `size=None`, a tuple containing the shape of the returned array. In case `size` is `None`, a single floating point number is returned.\n",
"\n",
"The `normal` method of the `Generator` object is based on the [Box-Muller transform](https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform).\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.html"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-19T13:08:17.647416Z",
"start_time": "2019-10-19T13:08:17.597456Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gnerator() at 0x7fa9dae05340\n",
"-6.285246229407202\n",
"array([[24.95816273705659, 15.2670302229426, 14.81001577336041],\n",
" [20.17589833056986, 23.14539083787544, 26.37772041367461],\n",
" [41.94894234387275, 37.11027030608206, 25.65889562100477]], dtype=float64)\n",
"array([[21.52562779033434, 12.74685887865834, 24.08404670765186],\n",
" [4.728112596365396, 7.667757906857082, 21.61576094228444],\n",
" [2.432338873595267, 27.75945683572574, 5.730827584659245]], dtype=float64)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"rng = np.random.Generator(123456)\n",
"print(rng)\n",
"\n",
"# return single number from a distribution of scale 1, and location 0\n",
"print(rng.normal())\n",
"\n",
"print(rng.normal(loc=20.0, scale=10.0, size=(3,3)))\n",
"# same as above, with positional arguments\n",
"print(rng.normal(20.0, 10.0, (3,3)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## random\n",
"\n",
"A random set of number from the uniform distribution in the interval [0, 1] can be generated by calling the generator's `random` method. The method takes two optional arguments, `size=None`, a tuple containing the shape of the returned array, and `out`. In case `size` is `None`, a single floating point number is returned. \n",
"\n",
"`out` can be used, if a floating point array is available. An exception will be raised, if the array is not of `float` `dtype`, or if both `size` and `out` are supplied, and there is a conflict in their shapes.\n",
"\n",
"If `size` is `None`, a single floating point number will be returned.\n",
"\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.html"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gnerator() at 0x7f299de05340\n",
"6.384615058863119e-11\n",
"\n",
" array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],\n",
" [0.8738606263361598, 0.4946080034142021, 0.7765890156101152],\n",
" [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)\n",
"\n",
"buffer array before:\n",
" array([[0.0, 1.0, 2.0],\n",
" [3.0, 4.0, 5.0],\n",
" [6.0, 7.0, 8.0]], dtype=float64)\n",
"\n",
"buffer array after:\n",
" array([[0.8508024287393201, 0.9848489829156055, 0.7598167589604003],\n",
" [0.782995698302952, 0.2866337782847831, 0.7915884498022229],\n",
" [0.4614071706315902, 0.4792657443088592, 0.1581582066230718]], dtype=float64)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"rng = np.random.Generator(123456)\n",
"print(rng)\n",
"\n",
"# returning new objects\n",
"print(rng.random())\n",
"print('\\n', rng.random(size=(3,3)))\n",
"\n",
"# supplying a buffer\n",
"a = np.array(range(9), dtype=np.float).reshape((3,3))\n",
"print('\\nbuffer array before:\\n', a)\n",
"rng.random(out=a)\n",
"print('\\nbuffer array after:\\n', a)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## uniform\n",
"\n",
"`uniform` is similar to `random`, except that the interval over which the numbers are distributed can be specified, while the `out` argument cannot. In addition to `size` specifying the shape of the output, `low=0.0`, and `high=1.0` are accepted arguments. With the indicated defaults, `uniform` is identical to `random`, which can be seen from the fact that the first 3-by-3 tensor below is the same as the one produced by `rng.random(size=(3,3))` above.\n",
"\n",
"\n",
"If `size` is `None`, a single floating point number will be returned.\n",
"\n",
"\n",
"`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.html"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gnerator() at 0x7f1891205340\n",
"6.384615058863119e-11\n",
"\n",
" array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],\n",
" [0.8738606263361598, 0.4946080034142021, 0.7765890156101152],\n",
" [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)\n",
"\n",
" array([[18.5080242873932, 19.84848982915605, 17.598167589604],\n",
" [17.82995698302952, 12.86633778284783, 17.91588449802223],\n",
" [14.6140717063159, 14.79265744308859, 11.58158206623072]], dtype=float64)\n",
"\n",
" array([[14.3380400319162, 12.72487657409978, 15.77119643621117],\n",
" [13.61835831436355, 18.96062889255558, 15.78847796795966],\n",
" [12.59435855187034, 17.68262037443622, 14.77943040598734]], dtype=float64)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"rng = np.random.Generator(123456)\n",
"print(rng)\n",
"\n",
"print(rng.uniform())\n",
"# returning numbers between 0, and 1\n",
"print('\\n', rng.uniform(size=(3,3)))\n",
"\n",
"# returning numbers between 10, and 20\n",
"print('\\n', rng.uniform(low=10, high=20, size=(3,3)))\n",
"\n",
"# same as above, without the keywords\n",
"print('\\n', rng.uniform(10, 20, (3,3)))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {
"height": "calc(100% - 180px)",
"left": "10px",
"top": "150px",
"width": "382.797px"
},
"toc_section_display": true,
"toc_window_display": true
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View file

@ -31,7 +31,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-07T19:10:30.696795Z",
@ -49,7 +49,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-07T19:10:30.785887Z",
@ -77,7 +77,7 @@
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
" fout.write(cell)\n",
" proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
" print(proc.stdout.read().decode(\"utf-8\"))\n",
" print(proc.stderr.read().decode(\"utf-8\"))\n",
@ -229,14 +229,16 @@
"\n",
"At present, the following functions are supported (starred functions can operate on, or can return complex arrays):\n",
"\n",
"`acos`, `acosh`, `arctan2`, `around`, `asin`, `asinh`, `atan`, `arctan2`, `atanh`, `ceil`, `cos`, `degrees`, `exp*`, `expm1`, `floor`, `log`, `log10`, `log2`, `radians`, `sin`, `sinh`, `sqrt*`, `tan`, `tanh`.\n",
"`acos`, `acosh`, `arctan2`, `around`, `asin`, `asinh`, `atan`, `arctan2`, `atanh`, `ceil`, `cos`, `degrees`, `exp*`, `expm1`, `floor`, `log`, `log10`, `log2`, `radians`, `sin`, `sinc`, `sinh`, `sqrt*`, `tan`, `tanh`.\n",
"\n",
"These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries."
"These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries.\n",
"\n",
"In order to avoid repeated memory allocations, functions can take the `out=None` optional argument, which must be a floating point `ndarray` of the same size as the input `array`. If these conditions are not fulfilled, and exception will be raised. If `out=None`, a new array will be created upon each invocation of the function."
]
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 9,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-13T19:11:07.579601Z",
@ -267,6 +269,16 @@
" [20.08553692318767, 54.59815003314424, 148.4131591025766],\n",
" [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)\n",
"\n",
"d before invoking the function:\n",
" array([[0.0, 1.0, 2.0],\n",
" [3.0, 4.0, 5.0],\n",
" [6.0, 7.0, 8.0]], dtype=float64)\n",
"\n",
"d afteri nvoking the function:\n",
" array([[1.0, 2.718281828459045, 7.38905609893065],\n",
" [20.08553692318767, 54.59815003314424, 148.4131591025766],\n",
" [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)\n",
"\n",
"\n"
]
}
@ -290,7 +302,14 @@
"# as well as with matrices\n",
"c = np.array(range(9)).reshape((3, 3))\n",
"print('\\n=============\\nc:\\n', c)\n",
"print('exp(c):\\n', np.exp(c))"
"print('exp(c):\\n', np.exp(c))\n",
"\n",
"# using the `out` argument\n",
"d = np.array(range(9)).reshape((3, 3))\n",
"\n",
"print('\\nd before invoking the function:\\n', d)\n",
"np.exp(c, out=d)\n",
"print('\\nd afteri nvoking the function:\\n', d)"
]
},
{
@ -814,7 +833,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.9.13"
},
"toc": {
"base_numbering": 1,

510
docs/scipy-integrate.ipynb Normal file
View file

@ -0,0 +1,510 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-12T16:11:12.111639Z",
"start_time": "2021-01-12T16:11:11.914041Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
}
],
"source": [
"%pylab inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Notebook magic"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-29T20:50:20.813162Z",
"start_time": "2022-01-29T20:50:20.794562Z"
}
},
"outputs": [],
"source": [
"from IPython.core.magic import Magics, magics_class, line_cell_magic\n",
"from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n",
"from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n",
"import subprocess\n",
"import os"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-29T20:50:21.613220Z",
"start_time": "2022-01-29T20:50:21.557819Z"
}
},
"outputs": [],
"source": [
"@magics_class\n",
"class PyboardMagic(Magics):\n",
" @cell_magic\n",
" @magic_arguments()\n",
" @argument('-skip')\n",
" @argument('-unix')\n",
" @argument('-pyboard')\n",
" @argument('-file')\n",
" @argument('-data')\n",
" @argument('-time')\n",
" @argument('-memory')\n",
" def micropython(self, line='', cell=None):\n",
" args = parse_argstring(self.micropython, line)\n",
" if args.skip: # doesn't care about the cell's content\n",
" print('skipped execution')\n",
" return None # do not parse the rest\n",
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
" fout.write(cell)\n",
" proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
" print(proc.stdout.read().decode(\"utf-8\"))\n",
" print(proc.stderr.read().decode(\"utf-8\"))\n",
" return None\n",
" if args.file: # can be used to copy the cell content onto the pyboard's flash\n",
" spaces = \" \"\n",
" try:\n",
" with open(args.file, 'w') as fout:\n",
" fout.write(cell.replace('\\t', spaces))\n",
" printf('written cell to {}'.format(args.file))\n",
" except:\n",
" print('Failed to write to disc!')\n",
" return None # do not parse the rest\n",
" if args.data: # can be used to load data from the pyboard directly into kernel space\n",
" message = pyb.exec(cell)\n",
" if len(message) == 0:\n",
" print('pyboard >>>')\n",
" else:\n",
" print(message.decode('utf-8'))\n",
" # register new variable in user namespace\n",
" self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n",
" \n",
" if args.time: # measures the time of executions\n",
" pyb.exec('import utime')\n",
" message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n",
" \"\\nprint('execution time: {:d} us'.format(delta))\")\n",
" print(message.decode('utf-8'))\n",
" \n",
" if args.memory: # prints out memory information \n",
" message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n",
" print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n",
" message = pyb.exec(cell)\n",
" print(\">>> \", message.decode('utf-8'))\n",
" message = pyb.exec('print(mem_info())')\n",
" print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n",
"\n",
" if args.pyboard:\n",
" message = pyb.exec(cell)\n",
" print(message.decode('utf-8'))\n",
"\n",
"ip = get_ipython()\n",
"ip.register_magics(PyboardMagic)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## pyboard"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-07T07:35:35.126401Z",
"start_time": "2020-05-07T07:35:35.105824Z"
}
},
"outputs": [],
"source": [
"import pyboard\n",
"pyb = pyboard.Pyboard('/dev/ttyACM0')\n",
"pyb.enter_raw_repl()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-19T19:11:18.145548Z",
"start_time": "2020-05-19T19:11:18.137468Z"
}
},
"outputs": [],
"source": [
"pyb.exit_raw_repl()\n",
"pyb.close()"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {
"ExecuteTime": {
"end_time": "2020-05-07T07:35:38.725924Z",
"start_time": "2020-05-07T07:35:38.645488Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"%%micropython -pyboard 1\n",
"\n",
"import utime\n",
"import ulab as np\n",
"\n",
"def timeit(n=1000):\n",
" def wrapper(f, *args, **kwargs):\n",
" func_name = str(f).split(' ')[1]\n",
" def new_func(*args, **kwargs):\n",
" run_times = np.zeros(n, dtype=np.uint16)\n",
" for i in range(n):\n",
" t = utime.ticks_us()\n",
" result = f(*args, **kwargs)\n",
" run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n",
" print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n",
" print('\\tbest: %d us'%np.min(run_times))\n",
" print('\\tworst: %d us'%np.max(run_times))\n",
" print('\\taverage: %d us'%np.mean(run_times))\n",
" print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n",
" return result\n",
" return new_func\n",
" return wrapper\n",
"\n",
"def timeit(f, *args, **kwargs):\n",
" func_name = str(f).split(' ')[1]\n",
" def new_func(*args, **kwargs):\n",
" t = utime.ticks_us()\n",
" result = f(*args, **kwargs)\n",
" print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n",
" return result\n",
" return new_func"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"__END_OF_DEFS__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# scipy.integrate\n",
"\n",
"This module provides a simplified subset of CPython's `scipy.integrate` module. The algorithms were not ported from CPython's `scipy.integrate` for the sake of resource usage, but derived from a paper found in https://www.genivia.com/qthsh.html. There are four numerical integration algorithms:\n",
"\n",
"1. [scipy.integrate.quad](#quad)\n",
"2. [scipy.integrate.romberg](#romberg)\n",
"3. [scipy.integrate.simpson](#simpson)\n",
"4. [scipy.integrate.tanhsinh](#tanhsinh)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introduction\n",
"\n",
"Numerical integration works best with float64 math enabled. If you require float64 math, be sure to set `MICROPY_OBJ_REPR_A` and `MICROPY_FLOAT_IMPL_DOUBLE`. This being said, the modules work equally well using float32, albeit with reduced precision. The required error tolerance can be specified for each of the function calls using the \"eps=\" option, defaulting to the compiled in `etolerance` value (1e-14 for fp64, 1e-8 for fp32).\n",
"\n",
"The submodule can be enabled by setting `ULAB_SCIPY_HAS_INTEGRATE_MODULE` in `code/ulab.h`. As for the individual integration algorithms, you can select which to include by setting one or more of `ULAB_INTEGRATE_HAS_QUAD`, `ULAB_INTEGRATE_HAS_ROMBERG`, `ULAB_INTEGRATE_HAS_SIMPSON`, and `ULAB_INTEGRATE_HAS_TANHSINH`.\n",
"\n",
"Also note that these algorithms do not support complex numbers, although it is certainly possible to implement complex integration in MicroPython on top of this module, e.g. as in https://stackoverflow.com/questions/5965583/use-scipy-integrate-quad-to-integrate-complex-numbers. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## quad\n",
"\n",
"`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html \n",
"\n",
"In CPython `scipy.integrate`, `quad` is a wrapper implementing many algorithms based on the Fortran QUADPACK package. Gauss-Kronrod is just one of them, and it is useful for most general-purpose tasks. This particular function implements an Adaptive Gauss-Kronrod (G10,K21) quadrature algorithm. The GaussKronrod 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 NewtonCotes 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 ClenshawCurtis 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 GaussKronrod quadrature and ClenshawCurtis 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
}

View file

@ -1,3 +1,163 @@
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
add random module
Mon, 25 Dec 2023
version 6.4.3
fix the 'np.delete' error that occurs when passing an empty iterable object as the second positional argument (#653)
Thu, 11 Dec 2023
version 6.4.2
fix upcasting with two uint8 operands (#650)
Thu, 10 Aug 2023
version 6.4.1
fix BOOLEAN issue, which would cause numpy.where funciton abnormally on RP2040(#643)
Thu, 20 Jul 2023
version 6.4.0
implement AND, OR, and XOR binary operators
Sat, 1 Jul 2023
version 6.3.5
allow function itertor in math functions with the out keyword
Fri, 12 May 2023
version 6.3.4
fix compile error when COMPLEX support not enabled
version 6.3.3
Polyval handles non-array as second argument (#601)
version 6.3.2
fix out of bound read
version 6.3.1
fix integer overflows
version 6.3.0
add bitwise operators
Wed, 17 May 2023
version 6.1.1
fix ndarray subscription, when value is NULL
Tue, 16 May 2023
version 6.1.0
add sinc function
Fri, 12 May 2023
version 6.0.13
add bitwise operators
Sun, 7 May 2023
version 6.0.12

View file

@ -14,7 +14,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"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-2022, Zoltán Vörös and contributors'\n",
"copyright = '2019-2025, Zoltán Vörös and contributors'\n",
"author = 'Zoltán Vörös'\n",
"\n",
"# The full version, including alpha/beta/rc tags\n",
"release = '5.1.0'\n",
"release = '6.9.0'\n",
"\n",
"\n",
"# -- General configuration ---------------------------------------------------\n",
@ -190,6 +190,8 @@
" numpy-universal\n",
" numpy-fft\n",
" numpy-linalg\n",
" numpy-random\n",
" scipy-integrate\n",
" scipy-linalg\n",
" scipy-optimize\n",
" scipy-signal\n",
@ -215,7 +217,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2022-02-09T06:27:21.647179Z",
@ -256,14 +258,51 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2022-02-09T06:27:42.024028Z",
"start_time": "2022-02-09T06:27:36.109093Z"
}
},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n",
"/home/v923z/anaconda3/lib/python3.11/site-packages/nbconvert/exporters/exporter.py:349: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
" _, nbc = validator.normalize(nbc)\n"
]
}
],
"source": [
"files = ['ulab-intro',\n",
" 'ulab-ndarray',\n",
@ -271,6 +310,8 @@
" 'numpy-universal',\n",
" 'numpy-fft',\n",
" 'numpy-linalg',\n",
" 'numpy-random',\n",
" 'scipy-integrate',\n",
" 'scipy-linalg',\n",
" 'scipy-optimize',\n",
" 'scipy-signal',\n",
@ -435,7 +476,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.8.5 ('base')",
"display_name": "base",
"language": "python",
"name": "python3"
},
@ -449,7 +490,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.11.7"
},
"toc": {
"base_numbering": 1,
@ -497,11 +538,6 @@
"_Feature"
],
"window_display": false
},
"vscode": {
"interpreter": {
"hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10"
}
}
},
"nbformat": 4,

View file

@ -10,13 +10,6 @@
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Matplotlib is building the font cache; this may take a moment.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
@ -38,7 +31,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-07T18:13:14.590799Z",
@ -239,7 +232,7 @@
"source": [
"## Enter ulab\n",
"\n",
"`ulab` is a `numpy`-like module for `micropython` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. `ulab` implements a small subset of `numpy` and `scipy`. The functions were chosen such that they might be useful in the context of a microcontroller. However, the project is a living one, and suggestions for new features are always welcome. \n",
"`ulab` is a `numpy`-like module for `micropython` and its derivatives, meant to simplify and speed up common mathematical operations on arrays. `ulab` implements a small subset of `numpy` and `scipy`, as well as a number of functions manipulating byte arrays. The functions were chosen such that they might be useful in the context of a microcontroller. However, the project is a living one, and suggestions for new features are always welcome. \n",
"\n",
"This document discusses how you can use the library, starting from building your own firmware, through questions like what affects the firmware size, what are the trade-offs, and what are the most important differences to `numpy` and `scipy`, respectively. The document is organised as follows:\n",
"\n",
@ -404,7 +397,7 @@
"np.polyval(p, x)\n",
"```\n",
"\n",
"There are a couple of exceptions to this rule, namely `fft`, and `linalg`, which are sub-modules even in `numpy`, thus you have to write them out as \n",
"There are a couple of exceptions to this rule, namely `fft`, `linalg`, and `random`, which are sub-modules even in `numpy`, thus you have to write them out as \n",
"\n",
"```python\n",
"from ulab import numpy as np\n",
@ -842,7 +835,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.9.13"
},
"toc": {
"base_numbering": 1,

View file

@ -2599,7 +2599,9 @@
"source": [
"# Binary operators\n",
"\n",
"`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators that work element-wise. Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n",
"`ulab` implements the `+`, `-`, `*`, `/`, `**`, `%`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n",
"\n",
"Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n",
"A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n",
"\n",
"**WARNING**: note that relational operators (`<`, `>`, `<=`, `>=`, `==`, `!=`) should have the `ndarray` on their left hand side, when compared to scalars. This means that the following works"
@ -3268,7 +3270,7 @@
"output_type": "stream",
"text": [
"a:\t array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float)\n",
"a < 5:\t array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)\n",
"a[a < 5]:\t array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)\n",
"\n",
"\n"
]
@ -3281,7 +3283,7 @@
"\n",
"a = np.array(range(9), dtype=np.float)\n",
"print(\"a:\\t\", a)\n",
"print(\"a < 5:\\t\", a[a < 5])"
"print(\"a[a < 5]:\\t\", a[a < 5])"
]
},
{

View file

@ -14,6 +14,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"%pylab is deprecated, use %matplotlib inline and import the required libraries.\n",
"Populating the interactive namespace from numpy and matplotlib\n"
]
}
@ -31,7 +32,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-07T19:16:29.118001Z",
@ -77,7 +78,7 @@
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
" fout.write(cell)\n",
" proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
" print(proc.stdout.read().decode(\"utf-8\"))\n",
" print(proc.stderr.read().decode(\"utf-8\"))\n",
@ -182,7 +183,7 @@
"%%micropython -pyboard 1\n",
"\n",
"import utime\n",
"import ulab as np\n",
"from ulab import numpy as np\n",
"\n",
"def timeit(n=1000):\n",
" def wrapper(f, *args, **kwargs):\n",
@ -244,145 +245,14 @@
"\n",
"**WARNING:** Difference to `numpy`: the `out` keyword argument is not implemented.\n",
"\n",
"These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined.\n",
"These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined. The functions also accept the `keepdims=True` or `keepdims=False` keyword argument. The latter case is the default, while the former keeps the dimensions (the number of axes) of the supplied array. \n",
"\n",
"`argmin/argmax` return the position (index) of the minimum/maximum in the sequence."
]
},
{
"cell_type": "code",
"execution_count": 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,
"execution_count": 10,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-18T13:08:28.113525Z",
@ -394,16 +264,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
"a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float)\n",
"a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float64)\n",
"min of a: 0.0\n",
"argmin of a: 2\n",
"\n",
"b:\n",
" array([[1.0, 2.0, 0.0],\n",
"\t [1.0, 10.0, -1.0]], dtype=float)\n",
" [1.0, 10.0, -1.0]], dtype=float64)\n",
"min of b (flattened): -1.0\n",
"min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float)\n",
"min of b (axis=1): array([0.0, -1.0], dtype=float)\n",
"min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float64)\n",
"min of b (axis=1): array([0.0, -1.0], dtype=float64)\n",
"\n",
"\n"
]
@ -412,19 +282,55 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([1, 2, 0, 1, 10])\n",
"print('a:', a)\n",
"print('min of a:', numerical.min(a))\n",
"print('argmin of a:', numerical.argmin(a))\n",
"print('min of a:', np.min(a))\n",
"print('argmin of a:', np.argmin(a))\n",
"\n",
"b = np.array([[1, 2, 0], [1, 10, -1]])\n",
"print('\\nb:\\n', b)\n",
"print('min of b (flattened):', numerical.min(b))\n",
"print('min of b (axis=0):', numerical.min(b, axis=0))\n",
"print('min of b (axis=1):', numerical.min(b, axis=1))"
"print('min of b (flattened):', np.min(b))\n",
"print('min of b (axis=0):', np.min(b, axis=0))\n",
"print('min of b (axis=1):', np.min(b, axis=1))"
]
},
{
"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))"
]
},
{
@ -439,12 +345,14 @@
"\n",
"`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.mean.html\n",
"\n",
"These three functions follow the same pattern: if the axis keyword is not specified, it assumes the default value of `None`, and returns the result of the computation for the flattened array. Otherwise, the calculation is along the given axis."
"These three functions follow the same pattern: if the `axis` keyword is not specified, they assume the default value of `None`, and return the result of the computation for the flattened array. Otherwise, the calculation is along the given axis. \n",
"\n",
"If the `axis` keyword argument is a number (this can also be negative to signify counting from the rightmost axis) the functions contract the arrays, i.e., the results will have one axis fewer than the input array. The only exception to this rule is when the `keepdims` keyword argument is supplied with a value `True`, in which case, the results will have the same number of axis as the input, but the axis specified in `axis` will have a length of 1. This is useful in cases, when the output is to be broadcast with the input in subsequent computations."
]
},
{
"cell_type": "code",
"execution_count": 527,
"execution_count": 12,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-20T06:51:58.845076Z",
@ -458,29 +366,73 @@
"text": [
"a: \n",
" array([[1.0, 2.0, 3.0],\n",
"\t [4.0, 5.0, 6.0],\n",
"\t [7.0, 8.0, 9.0]], dtype=float)\n",
" [4.0, 5.0, 6.0],\n",
" [7.0, 8.0, 9.0]], dtype=float64)\n",
"sum, flat array: 45.0\n",
"mean, horizontal: array([2.0, 5.0, 8.0], dtype=float)\n",
"std, vertical: array([2.44949, 2.44949, 2.44949], dtype=float)\n",
"mean, horizontal: array([2.0, 5.0, 8.0], dtype=float64)\n",
"std, vertical: array([2.449489742783178, 2.449489742783178, 2.449489742783178], dtype=float64)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -pyboard 1\n",
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
"print('a: \\n', a)\n",
"\n",
"print('sum, flat array: ', numerical.sum(a))\n",
"print('sum, flat array: ', np.sum(a))\n",
"\n",
"print('mean, horizontal: ', numerical.mean(a, axis=1))\n",
"print('mean, horizontal: ', np.mean(a, axis=1))\n",
"\n",
"print('std, vertical: ', numerical.std(a, axis=0))"
"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))"
]
},
{
@ -519,13 +471,12 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([1, 2, 3, 4, 5, 6, 7, 8])\n",
"print(\"a:\\t\\t\\t\", a)\n",
"\n",
"numerical.roll(a, 2)\n",
"np.roll(a, 2)\n",
"print(\"a rolled to the left:\\t\", a)\n",
"\n",
"# this should be the original vector\n",
@ -581,19 +532,18 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])\n",
"print(\"a:\\n\", a)\n",
"\n",
"numerical.roll(a, 2)\n",
"np.roll(a, 2)\n",
"print(\"\\na rolled to the left:\\n\", a)\n",
"\n",
"numerical.roll(a, -1, axis=1)\n",
"np.roll(a, -1, axis=1)\n",
"print(\"\\na rolled up:\\n\", a)\n",
"\n",
"numerical.roll(a, 1, axis=None)\n",
"np.roll(a, 1, axis=None)\n",
"print(\"\\na rolled with None:\\n\", a)"
]
},
@ -649,9 +599,7 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import vector\n",
"from ulab import numpy as np\n",
"\n",
"def dummy_adc():\n",
" # dummy adc function, so that the results are reproducible\n",
@ -659,8 +607,8 @@
" \n",
"n = 10\n",
"# These are the normalised weights; the last entry is the most dominant\n",
"weight = vector.exp([1, 2, 3, 4, 5])\n",
"weight = weight/numerical.sum(weight)\n",
"weight = np.exp([1, 2, 3, 4, 5])\n",
"weight = weight/np.sum(weight)\n",
"\n",
"print(weight)\n",
"# initial array of samples\n",
@ -669,10 +617,10 @@
"for i in range(n):\n",
" # a new datum is inserted on the right hand side. This simply overwrites whatever was in the last slot\n",
" samples[-1] = dummy_adc()\n",
" print(numerical.mean(samples[-5:]*weight))\n",
" print(np.mean(samples[-5:]*weight))\n",
" print(samples[-5:])\n",
" # the data are shifted by one position to the left\n",
" numerical.roll(samples, 1)"
" numerical.np(samples, 1)"
]
},
{
@ -725,17 +673,16 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([1, 2, 3, 4, 5])\n",
"print(\"a: \\t\", a)\n",
"print(\"a flipped:\\t\", np.flip(a))\n",
"\n",
"a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.uint8)\n",
"print(\"\\na flipped horizontally\\n\", numerical.flip(a, axis=1))\n",
"print(\"\\na flipped vertically\\n\", numerical.flip(a, axis=0))\n",
"print(\"\\na flipped horizontally+vertically\\n\", numerical.flip(a))"
"print(\"\\na flipped horizontally\\n\", np.flip(a, axis=1))\n",
"print(\"\\na flipped vertically\\n\", np.flip(a, axis=0))\n",
"print(\"\\na flipped horizontally+vertically\\n\", np.flip(a))"
]
},
{
@ -800,19 +747,18 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array(range(9), dtype=np.uint8)\n",
"print('a:\\n', a)\n",
"\n",
"print('\\nfirst derivative:\\n', numerical.diff(a, n=1))\n",
"print('\\nsecond derivative:\\n', numerical.diff(a, n=2))\n",
"print('\\nfirst derivative:\\n', np.diff(a, n=1))\n",
"print('\\nsecond derivative:\\n', np.diff(a, n=2))\n",
"\n",
"c = np.array([[1, 2, 3, 4], [4, 3, 2, 1], [1, 4, 9, 16], [0, 0, 0, 0]])\n",
"print('\\nc:\\n', c)\n",
"print('\\nfirst derivative, first axis:\\n', numerical.diff(c, axis=0))\n",
"print('\\nfirst derivative, second axis:\\n', numerical.diff(c, axis=1))"
"print('\\nfirst derivative, first axis:\\n', np.diff(c, axis=0))\n",
"print('\\nfirst derivative, second axis:\\n', np.diff(c, axis=1))"
]
},
{
@ -858,7 +804,7 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array(range(12), dtype=np.int8).reshape((3, 4))\n",
"print('a:\\n', a)\n",
@ -925,18 +871,17 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n",
"print('\\na:\\n', a)\n",
"b = numerical.sort(a, axis=0)\n",
"b = np.sort(a, axis=0)\n",
"print('\\na sorted along vertical axis:\\n', b)\n",
"\n",
"c = numerical.sort(a, axis=1)\n",
"c = np.sort(a, axis=1)\n",
"print('\\na sorted along horizontal axis:\\n', c)\n",
"\n",
"c = numerical.sort(a, axis=None)\n",
"c = np.sort(a, axis=None)\n",
"print('\\nflattened a sorted:\\n', c)"
]
},
@ -955,15 +900,13 @@
"source": [
"%%micropython -pyboard 1\n",
"\n",
"import ulab as np\n",
"from ulab import vector\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"@timeit\n",
"def sort_time(array):\n",
" return numerical.sort(array)\n",
" return np.sort(array)\n",
"\n",
"b = vector.sin(np.linspace(0, 6.28, num=1000))\n",
"b = np.sin(np.linspace(0, 6.28, num=1000))\n",
"print('b: ', b)\n",
"sort_time(b)\n",
"print('\\nb sorted:\\n', b)"
@ -1025,18 +968,17 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n",
"print('\\na:\\n', a)\n",
"b = numerical.argsort(a, axis=0)\n",
"b = np.argsort(a, axis=0)\n",
"print('\\na sorted along vertical axis:\\n', b)\n",
"\n",
"c = numerical.argsort(a, axis=1)\n",
"c = np.argsort(a, axis=1)\n",
"print('\\na sorted along horizontal axis:\\n', c)\n",
"\n",
"c = numerical.argsort(a, axis=None)\n",
"c = np.argsort(a, axis=None)\n",
"print('\\nflattened a sorted:\\n', c)"
]
},
@ -1078,12 +1020,11 @@
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"from ulab import numerical\n",
"from ulab import numpy as np\n",
"\n",
"a = np.array([0, 5, 1, 3, 2, 4], dtype=np.uint8)\n",
"print('\\na:\\n', a)\n",
"b = numerical.argsort(a, axis=1)\n",
"b = np.argsort(a, axis=1)\n",
"print('\\nsorting indices:\\n', b)\n",
"print('\\nthe original array:\\n', a)"
]
@ -1091,7 +1032,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "base",
"language": "python",
"name": "python3"
},
@ -1105,7 +1046,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.11.7"
},
"toc": {
"base_numbering": 1,

View file

@ -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",

View file

@ -31,7 +31,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-29T16:53:11.972661Z",
@ -49,7 +49,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-29T16:59:24.652277Z",
@ -77,7 +77,7 @@
" if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
" with open('/dev/shm/micropython.py', 'w') as fout:\n",
" fout.write(cell)\n",
" proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n",
" stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
" print(proc.stdout.read().decode(\"utf-8\"))\n",
" print(proc.stderr.read().decode(\"utf-8\"))\n",
@ -291,7 +291,9 @@
"a = bytearray([1, 1, 0, 0, 0, 0, 0, 255])\n",
"print('a: ', a)\n",
"print()\n",
"print('unsigned integers: ', utils.from_uint32_buffer(a))\n",
"print('unsigned integers: ', utils.from_uint32_buffe\n",
"print('original vector:\\n', y)\n",
"print('\\nspectrum:\\n', a)r(a))\n",
"\n",
"b = bytearray([1, 1, 0, 0, 0, 0, 0, 255])\n",
"print('\\nb: ', b)\n",
@ -398,12 +400,53 @@
"source": [
"## spectrogram\n",
"\n",
"In addition to the Fourier transform and its inverse, `ulab` also sports a function called `spectrogram`, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. The arguments are treated in the same way as in `fft`, and `ifft`. This means that, if the firmware was compiled with complex support, the input can also be a complex array."
"In addition to the Fourier transform and its inverse, `ulab` also sports a function called `spectrogram`, which returns the absolute value of the Fourier transform, also known as the power spectrum. This could be used to find the dominant spectral component in a time series. The positional arguments are treated in the same way as in `fft`, and `ifft`. This means that, if the firmware was compiled with complex support and `ULAB_FFT_IS_NUMPY_COMPATIBLE` is defined to be 1 in `ulab.h`, the input can also be a complex array. \n",
"\n",
"And easy way to find out if the FFT is `numpy`-compatible is to check the number of values `fft.fft` returns, when called with a single real argument of length other than 2: "
]
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"FFT is numpy compatible (complex inputs/outputs)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"from ulab import numpy as np\n",
"\n",
"if len(np.fft.fft(np.zeros(4))) == 2:\n",
" print('FFT is NOT numpy compatible (real and imaginary parts are treated separately)')\n",
"else:\n",
" print('FFT is numpy compatible (complex inputs/outputs)')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Depending on the `numpy`-compatibility of the FFT, the `spectrogram` function takes one or two positional arguments, and three keyword arguments. If the FFT is `numpy` compatible, one positional argument is allowed, and it is a 1D real or complex `ndarray`. If the FFT is not `numpy`-compatible, if a single argument is supplied, it will be treated as the real part of the input, and if two positional arguments are supplied, they are treated as the real and imaginary parts of the signal.\n",
"\n",
"The keyword arguments are as follows:\n",
"\n",
"1. `scratchpad = None`: must be a 1D, dense, floating point array, twice as long as the input array; the `scratchpad` will be used as a temporary internal buffer to perform the Fourier transform; the `scratchpad` can repeatedly be re-used.\n",
"1. `out = None`: must be a 1D, not necessarily dense, floating point array that will store the results\n",
"1. `log = False`: must be either `True`, or `False`; if `True`, the `spectrogram` returns the logarithm of the absolute values of the Fourier transform."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-29T16:59:56.400603Z",
@ -419,7 +462,7 @@
" array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64)\n",
"\n",
"spectrum:\n",
" array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
" array([187.8635087634578, 315.3112063607119, 347.8814873399375, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
"\n",
"\n"
]
@ -444,12 +487,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"As such, `spectrogram` is really just a shorthand for `np.sqrt(a*a + b*b)`, however, it saves significant amounts of RAM: the expression `a*a + b*b` has to allocate memory for `a*a`, `b*b`, and finally, their sum. In contrast, `spectrogram` calculates the spectrum internally, and stores it in the memory segment that was reserved for the real part of the Fourier transform."
"As such, `spectrogram` is really just a shorthand for `np.abs(np.fft.fft(signal))`, if the FFT is `numpy`-compatible, or `np.sqrt(a*a + b*b)` if the FFT returns the real (`a`) and imaginary (`b`) parts separately. However, `spectrogram` saves significant amounts of RAM: the expression `a*a + b*b` has to allocate memory for `a*a`, `b*b`, and finally, their sum. Similarly, `np.abs` returns a new array. This issue is compounded even more, if `np.log()` is used on the absolute value. \n",
"\n",
"In contrast, `spectrogram` handles all calculations in the same internal arrays, and allows one to re-use previously reserved RAM. This can be especially useful in cases, when `spectogram` is called repeatedly, as in the snippet below."
]
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 34,
"metadata": {
"ExecuteTime": {
"end_time": "2022-01-29T16:59:48.485610Z",
@ -461,12 +506,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"spectrum calculated the hard way:\n",
" array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
"\n",
"spectrum calculated the lazy way:\n",
" array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)\n",
"signal: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)\n",
"out: array([-27.38260169844543, 6.237834411021073, -0.4038327279002965, ..., -0.9795967096969854, -0.4038327279002969, 6.237834411021073], dtype=float64)\n",
"\n",
"\n"
]
@ -478,17 +519,34 @@
"from ulab import numpy as np\n",
"from ulab import utils as utils\n",
"\n",
"x = np.linspace(0, 10, num=1024)\n",
"y = np.sin(x)\n",
"n = 1024\n",
"t = np.linspace(0, 2 * np.pi, num=1024)\n",
"scratchpad = np.zeros(2 * n)\n",
"\n",
"a, b = np.fft.fft(y)\n",
"for _ in range(10):\n",
" signal = np.sin(t)\n",
" utils.spectrogram(signal, out=signal, scratchpad=scratchpad, log=True)\n",
"\n",
"print('\\nspectrum calculated the hard way:\\n', np.sqrt(a*a + b*b))\n",
"print('signal: ', signal)\n",
"\n",
"a = utils.spectrogram(y)\n",
"for _ in range(10):\n",
" signal = np.sin(t)\n",
" out = np.log(utils.spectrogram(signal))\n",
"\n",
"print('\\nspectrum calculated the lazy way:\\n', a)"
"print('out: ', out)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that `scratchpad` is reserved only once, and then is re-used in the first loop. By assigning `signal` to the output, we save additional RAM. This approach avoids the usual problem of memory fragmentation, which would happen in the second loop, where both `spectrogram`, and `np.log` must reserve RAM in each iteration."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
@ -507,7 +565,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.11.7"
},
"toc": {
"base_numbering": 1,

View file

@ -15,5 +15,4 @@ myst-parser
# For stubs and annotations
adafruit-circuitpython-typing
build

View file

@ -10,8 +10,12 @@ x = np.linspace(-np.pi, np.pi, num=8)
y = np.sin(x)
if use_ulab:
a, b = np.fft.fft(y)
c, d = np.fft.ifft(a, b)
if 'real' in dir(np):
a = np.fft.fft(y)
c = np.real(np.fft.ifft(a))
else:
a, b = np.fft.fft(y)
c, d = np.fft.ifft(a, b)
# c should be equal to y
cmp_result = []
for p,q in zip(list(y), list(c)):
@ -19,8 +23,12 @@ if use_ulab:
print(cmp_result)
z = np.zeros(len(x))
a, b = np.fft.fft(y, z)
c, d = np.fft.ifft(a, b)
if 'real' in dir(np):
a = np.fft.fft(y)
c = np.real(np.fft.ifft(a))
else:
a, b = np.fft.fft(y, z)
c, d = np.fft.ifft(a, b)
# c should be equal to y
cmp_result = []
for p,q in zip(list(y), list(c)):

View file

@ -112,6 +112,13 @@ for i in range(len(x)):
cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))
print(cmp_result)
result = np.sinc(x)
ref_result = np.array([0.03935584386392389, -0.04359862862918773, 1.0, -0.04359862862918773, 0.03935584386392389])
cmp_result = []
for i in range(len(x)):
cmp_result.append(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))
print(cmp_result)
result = (spy.special.erf(np.linspace(-3, 3, num=5)))
ref_result = np.array([-0.9999779095030014, -0.9661051464753108, 0.0, 0.9661051464753108, 0.9999779095030014], dtype=np.float)
cmp_result = []

View file

@ -28,5 +28,6 @@ True
[True, True, True, True, True]
[True, True, True, True, True]
[True, True, True, True, True]
[True, True, True, True, True]
[True, True, True, True]
[True, True, True]

21
tests/2d/numpy/and.py Normal file
View file

@ -0,0 +1,21 @@
try:
from ulab import numpy as np
except ImportError:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
for dtype_a in dtypes:
a = np.array(range(5), dtype=dtype_a)
for dtype_b in dtypes:
b = np.array(range(250, 255), dtype=dtype_b)
try:
print('a & b: ', a & b)
except Exception as e:
print(e)
b = np.array([False, True, False, True, False], dtype=np.bool)
try:
print('a & b (bool): ', a & b)
except Exception as e:
print(e)

20
tests/2d/numpy/and.py.exp Normal file
View file

@ -0,0 +1,20 @@
a & b: array([0, 1, 0, 1, 4], dtype=uint8)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b: array([0, 1, 0, 1, 4], dtype=uint16)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b (bool): array([0, 1, 0, 1, 0], dtype=uint8)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b: array([0, 1, 0, 1, 4], dtype=int8)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b (bool): array([0, 1, 0, 1, 0], dtype=int16)
a & b: array([0, 1, 0, 1, 4], dtype=uint16)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b: array([0, 1, 0, 1, 4], dtype=uint16)
dtype of int32 is not supported
a & b (bool): array([0, 1, 0, 1, 0], dtype=uint16)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b: array([0, 1, 0, 1, 4], dtype=int16)
dtype of int32 is not supported
a & b: array([0, 1, 0, 1, 4], dtype=int16)
a & b (bool): array([0, 1, 0, 1, 0], dtype=int16)

View file

@ -0,0 +1,16 @@
try:
from ulab import numpy as np
except:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
test_values1 = (0, 1, 0, 1, 2, 5)
test_values2 = (0, 1, 1, 0, 2, 7)
for dtype1 in dtypes:
x1 = np.array(test_values1, dtype=dtype1)
for dtype2 in dtypes:
x2 = np.array(test_values2, dtype=dtype2)
print(np.bitwise_and(x1, x2))

View file

@ -0,0 +1,16 @@
array([0, 1, 0, 0, 2, 5], dtype=uint8)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=uint16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=int8)
array([0, 1, 0, 0, 2, 5], dtype=uint16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=uint16)
array([0, 1, 0, 0, 2, 5], dtype=uint16)
array([0, 1, 0, 0, 2, 5], dtype=uint16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=int16)
array([0, 1, 0, 0, 2, 5], dtype=int16)

View file

@ -0,0 +1,16 @@
try:
from ulab import numpy as np
except:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
test_values1 = (0, 1, 0, 1, 2, 5)
test_values2 = (0, 1, 1, 0, 2, 7)
for dtype1 in dtypes:
x1 = np.array(test_values1, dtype=dtype1)
for dtype2 in dtypes:
x2 = np.array(test_values2, dtype=dtype2)
print(np.bitwise_or(x1, x2))

View file

@ -0,0 +1,16 @@
array([0, 1, 1, 1, 2, 7], dtype=uint8)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=uint16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=int8)
array([0, 1, 1, 1, 2, 7], dtype=uint16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=uint16)
array([0, 1, 1, 1, 2, 7], dtype=uint16)
array([0, 1, 1, 1, 2, 7], dtype=uint16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=int16)
array([0, 1, 1, 1, 2, 7], dtype=int16)

View file

@ -0,0 +1,14 @@
try:
from ulab import numpy as np
except:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
for dtype1 in dtypes:
x1 = np.array(range(5), dtype=dtype1)
for dtype2 in dtypes:
x2 = np.array(range(5, 0, -1), dtype=dtype2)
print(np.bitwise_xor(x1, x2))

View file

@ -0,0 +1,16 @@
array([5, 5, 1, 1, 5], dtype=uint8)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=uint16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=int8)
array([5, 5, 1, 1, 5], dtype=uint16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=uint16)
array([5, 5, 1, 1, 5], dtype=uint16)
array([5, 5, 1, 1, 5], dtype=uint16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=int16)
array([5, 5, 1, 1, 5], dtype=int16)

View file

@ -11,7 +11,9 @@ for dtype in dtypes:
a = np.array(range(25), dtype=dtype).reshape((5,5))
print(np.delete(a, [1, 2], axis=0))
print(np.delete(a, [1, 2], axis=1))
print(np.delete(a, [], axis=1))
print(np.delete(a, [1, 5, 10]))
print(np.delete(a, []))
for dtype in dtypes:
a = np.array(range(25), dtype=dtype).reshape((5,5))

View file

@ -6,7 +6,17 @@ array([[0, 3, 4],
[10, 13, 14],
[15, 18, 19],
[20, 23, 24]], dtype=uint8)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=uint8)
array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=uint8)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=uint8)
array([[0, 1, 2, 3, 4],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=int8)
@ -15,7 +25,17 @@ array([[0, 3, 4],
[10, 13, 14],
[15, 18, 19],
[20, 23, 24]], dtype=int8)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=int8)
array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=int8)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=int8)
array([[0, 1, 2, 3, 4],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=uint16)
@ -24,7 +44,17 @@ array([[0, 3, 4],
[10, 13, 14],
[15, 18, 19],
[20, 23, 24]], dtype=uint16)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=uint16)
array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=uint16)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=uint16)
array([[0, 1, 2, 3, 4],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=int16)
@ -33,7 +63,17 @@ array([[0, 3, 4],
[10, 13, 14],
[15, 18, 19],
[20, 23, 24]], dtype=int16)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=int16)
array([0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], dtype=int16)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]], dtype=int16)
array([[0.0, 1.0, 2.0, 3.0, 4.0],
[15.0, 16.0, 17.0, 18.0, 19.0],
[20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64)
@ -42,7 +82,17 @@ array([[0.0, 3.0, 4.0],
[10.0, 13.0, 14.0],
[15.0, 18.0, 19.0],
[20.0, 23.0, 24.0]], dtype=float64)
array([[0.0, 1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0, 9.0],
[10.0, 11.0, 12.0, 13.0, 14.0],
[15.0, 16.0, 17.0, 18.0, 19.0],
[20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64)
array([0.0, 2.0, 3.0, 4.0, 6.0, 7.0, 8.0, 9.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0], dtype=float64)
array([[0.0, 1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0, 9.0],
[10.0, 11.0, 12.0, 13.0, 14.0],
[15.0, 16.0, 17.0, 18.0, 19.0],
[20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64)
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[15, 16, 17, 18, 19],

View file

@ -0,0 +1,21 @@
try:
from ulab import numpy as np
except:
import numpy as np
np.set_printoptions(threshold=100)
shift_values = (
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
)
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
for shift_value in shift_values:
for dtype1 in dtypes:
x1 = np.array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=dtype1)
for dtype2 in dtypes:
x2 = np.array(shift_value, dtype=dtype2)
print(np.left_shift(x1, x2))

View file

@ -0,0 +1,48 @@
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint8)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint8)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int8)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int8)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=uint16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 2, 4, 8, 16, 32, 64, 6, 10, 14, 22, 26], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint8)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, -128, 12, 20, 28, 44, 52], dtype=int8)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16)
array([0, 4, 8, 16, 32, 64, -128, 12, 20, 28, 44, 52], dtype=int8)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=uint16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)
array([0, 4, 8, 16, 32, 64, 128, 12, 20, 28, 44, 52], dtype=int16)

26
tests/2d/numpy/modulo.py Normal file
View file

@ -0,0 +1,26 @@
try:
from ulab import numpy as np
except:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float)
for dtype1 in dtypes:
x1 = np.array(range(6), dtype=dtype1).reshape((2, 3))
for dtype2 in dtypes:
x2 = np.array(range(1, 4), dtype=dtype2)
print(x1 % x2)
print()
print('=' * 30)
print('inplace modulo')
print('=' * 30)
print()
for dtype1 in dtypes:
x1 = np.array(range(6), dtype=dtype1).reshape((2, 3))
for dtype2 in dtypes:
x2 = np.array(range(1, 4), dtype=dtype2)
x1 %= x2
print(x1)

View file

@ -0,0 +1,105 @@
array([[0, 1, 2],
[0, 0, 2]], dtype=uint8)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0, 1, 2],
[0, 0, 2]], dtype=uint16)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0, 1, 2],
[0, 0, 2]], dtype=int8)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 0, 1],
[0, 2, 0]], dtype=uint8)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 1, 2],
[0, 0, 2]], dtype=uint16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
==============================
inplace modulo
==============================
array([[0, 1, 2],
[0, 0, 2]], dtype=uint8)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0, 0, 1],
[0, 2, 0]], dtype=uint8)
array([[0, 0, 1],
[0, 0, 0]], dtype=int16)
array([[0.0, 0.0, 1.0],
[0.0, 0.0, 0.0]], dtype=float64)
array([[0.0, 0.0, 1.0],
[0.0, 0.0, 0.0]], dtype=float64)
array([[0.0, 0.0, 1.0],
[0.0, 0.0, 0.0]], dtype=float64)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0, 1, 2],
[0, 0, 2]], dtype=int16)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)
array([[0.0, 1.0, 2.0],
[0.0, 0.0, 2.0]], dtype=float64)

View file

@ -94,7 +94,7 @@ array([1.0, 2.0, 3.0], dtype=float64)
array([1.0, 32.0, 729.0], dtype=float64)
array([1.0, 32.0, 729.0], dtype=float64)
array([1.0, 32.0, 729.0], dtype=float64)
array([5, 7, 9], dtype=uint16)
array([5, 7, 9], dtype=uint8)
array([5, 7, 9], dtype=int16)
array([5, 7, 9], dtype=int8)
array([5, 7, 9], dtype=uint16)

21
tests/2d/numpy/or.py Normal file
View file

@ -0,0 +1,21 @@
try:
from ulab import numpy as np
except ImportError:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
for dtype_a in dtypes:
a = np.array(range(5), dtype=dtype_a)
for dtype_b in dtypes:
b = np.array(range(250, 255), dtype=dtype_b)
try:
print('a | b: ', a | b)
except Exception as e:
print(e)
b = np.array([False, True, False, True, False], dtype=np.bool)
try:
print('a | b (bool): ', a | b)
except Exception as e:
print(e)

20
tests/2d/numpy/or.py.exp Normal file
View file

@ -0,0 +1,20 @@
a | b: array([250, 251, 254, 255, 254], dtype=uint8)
a | b: array([-6, -5, -2, -1, -2], dtype=int16)
a | b: array([250, 251, 254, 255, 254], dtype=uint16)
a | b: array([250, 251, 254, 255, 254], dtype=int16)
a | b (bool): array([0, 1, 2, 3, 4], dtype=uint8)
a | b: array([250, 251, 254, 255, 254], dtype=int16)
a | b: array([-6, -5, -2, -1, -2], dtype=int8)
a | b: array([250, 251, 254, 255, 254], dtype=int16)
a | b: array([250, 251, 254, 255, 254], dtype=int16)
a | b (bool): array([0, 1, 2, 3, 4], dtype=int16)
a | b: array([250, 251, 254, 255, 254], dtype=uint16)
a | b: array([-6, -5, -2, -1, -2], dtype=int16)
a | b: array([250, 251, 254, 255, 254], dtype=uint16)
dtype of int32 is not supported
a | b (bool): array([0, 1, 2, 3, 4], dtype=uint16)
a | b: array([250, 251, 254, 255, 254], dtype=int16)
a | b: array([-6, -5, -2, -1, -2], dtype=int16)
dtype of int32 is not supported
a | b: array([250, 251, 254, 255, 254], dtype=int16)
a | b (bool): array([0, 1, 2, 3, 4], dtype=int16)

10
tests/2d/numpy/random.py Normal file
View file

@ -0,0 +1,10 @@
try:
from ulab import numpy as np
except ImportError:
import numpy as np
rng = np.random.Generator(1234)
for generator in (rng.normal, rng.random, rng.uniform):
random_array = generator(size=(1, 2))
print("array shape:", random_array.shape)

View file

@ -0,0 +1,3 @@
array shape: (1, 2)
array shape: (1, 2)
array shape: (1, 2)

17
tests/2d/numpy/reshape.py Normal file
View file

@ -0,0 +1,17 @@
try:
from ulab import numpy as np
except ImportError:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float)
for dtype in dtypes:
print()
print('=' * 50)
a = np.array(range(12), dtype=dtype).reshape((3, 4))
print(a)
b = a[0,:]
print(b.reshape((1,4)))
b = a[:,0]
print(b.reshape((1,3)))

View file

@ -0,0 +1,35 @@
==================================================
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=uint8)
array([[0, 1, 2, 3]], dtype=uint8)
array([[0, 4, 8]], dtype=uint8)
==================================================
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=int8)
array([[0, 1, 2, 3]], dtype=int8)
array([[0, 4, 8]], dtype=int8)
==================================================
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=uint16)
array([[0, 1, 2, 3]], dtype=uint16)
array([[0, 4, 8]], dtype=uint16)
==================================================
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=int16)
array([[0, 1, 2, 3]], dtype=int16)
array([[0, 4, 8]], dtype=int16)
==================================================
array([[0.0, 1.0, 2.0, 3.0],
[4.0, 5.0, 6.0, 7.0],
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
array([[0.0, 1.0, 2.0, 3.0]], dtype=float64)
array([[0.0, 4.0, 8.0]], dtype=float64)

View file

@ -0,0 +1,21 @@
try:
from ulab import numpy as np
except:
import numpy as np
np.set_printoptions(threshold=100)
shift_values = (
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
)
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
for shift_value in shift_values:
for dtype1 in dtypes:
x1 = np.array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=dtype1)
for dtype2 in dtypes:
x2 = np.array(shift_value, dtype=dtype2)
print(np.right_shift(x1, x2))

View file

@ -0,0 +1,48 @@
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint8)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int8)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=uint16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 1, 2, 4, 8, 16, 32, 3, 5, 7, 11, 13], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint8)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int8)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int8)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=uint16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 1, 2, 4, 8, 16, 1, 2, 3, 5, 6], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint8)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int8)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int8)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=uint16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)
array([0, 0, 0, 1, 2, 4, 8, 0, 1, 1, 2, 3], dtype=int16)

23
tests/2d/numpy/sum.py Normal file
View file

@ -0,0 +1,23 @@
try:
from ulab import numpy as np
except ImportError:
import numpy as np
for dtype in (np.uint8, np.int8, np.uint16, np.int8, np.float):
a = np.array(range(12), dtype=dtype)
b = a.reshape((3, 4))
print(a)
print(b)
print()
print(np.sum(a))
print(np.sum(a, axis=0))
print(np.sum(a, axis=0, keepdims=True))
print()
print(np.sum(b))
print(np.sum(b, axis=0))
print(np.sum(b, axis=1))
print(np.sum(b, axis=0, keepdims=True))
print(np.sum(b, axis=1, keepdims=True))

80
tests/2d/numpy/sum.py.exp Normal file
View file

@ -0,0 +1,80 @@
array([0, 1, 2, ..., 9, 10, 11], dtype=uint8)
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=uint8)
66
66
array([66], dtype=uint8)
66
array([12, 15, 18, 21], dtype=uint8)
array([6, 22, 38], dtype=uint8)
array([[12, 15, 18, 21]], dtype=uint8)
array([[6],
[22],
[38]], dtype=uint8)
array([0, 1, 2, ..., 9, 10, 11], dtype=int8)
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=int8)
66
66
array([66], dtype=int8)
66
array([12, 15, 18, 21], dtype=int8)
array([6, 22, 38], dtype=int8)
array([[12, 15, 18, 21]], dtype=int8)
array([[6],
[22],
[38]], dtype=int8)
array([0, 1, 2, ..., 9, 10, 11], dtype=uint16)
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=uint16)
66
66
array([66], dtype=uint16)
66
array([12, 15, 18, 21], dtype=uint16)
array([6, 22, 38], dtype=uint16)
array([[12, 15, 18, 21]], dtype=uint16)
array([[6],
[22],
[38]], dtype=uint16)
array([0, 1, 2, ..., 9, 10, 11], dtype=int8)
array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=int8)
66
66
array([66], dtype=int8)
66
array([12, 15, 18, 21], dtype=int8)
array([6, 22, 38], dtype=int8)
array([[12, 15, 18, 21]], dtype=int8)
array([[6],
[22],
[38]], dtype=int8)
array([0.0, 1.0, 2.0, ..., 9.0, 10.0, 11.0], dtype=float64)
array([[0.0, 1.0, 2.0, 3.0],
[4.0, 5.0, 6.0, 7.0],
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
66.0
66.0
array([66.0], dtype=float64)
66.0
array([12.0, 15.0, 18.0, 21.0], dtype=float64)
array([6.0, 22.0, 38.0], dtype=float64)
array([[12.0, 15.0, 18.0, 21.0]], dtype=float64)
array([[6.0],
[22.0],
[38.0]], dtype=float64)

30
tests/2d/numpy/take.py Normal file
View file

@ -0,0 +1,30 @@
try:
from ulab import numpy as np
except:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16, np.float)
print('flattened array')
for dtype in dtypes:
a = np.array(range(12), dtype=dtype).reshape((3, 4))
print(np.take(a, (0, 10)))
print('\n1D arrays')
for dtype in dtypes:
a = np.array(range(12), dtype=dtype)
print('\na:', a)
indices = (0, 2, 2, 1)
print(np.take(a, indices))
indices = np.array([0, 2, 2, 1], dtype=np.uint8)
print(np.take(a, indices))
print('\n2D arrays')
for dtype in dtypes:
a = np.array(range(12), dtype=dtype).reshape((3, 4))
print('\na:', a)
print('\nfirst axis')
print(np.take(a, (0, 2, 2, 1), axis=0))
print('\nsecond axis')
print(np.take(a, (0, 2, 2, 1), axis=1))

105
tests/2d/numpy/take.py.exp Normal file
View file

@ -0,0 +1,105 @@
flattened array
array([0, 10], dtype=uint8)
array([0, 10], dtype=int8)
array([0, 10], dtype=uint16)
array([0, 10], dtype=int16)
array([0.0, 10.0], dtype=float64)
1D arrays
a: array([0, 1, 2, ..., 9, 10, 11], dtype=uint8)
array([0, 2, 2, 1], dtype=uint8)
array([0, 2, 2, 1], dtype=uint8)
a: array([0, 1, 2, ..., 9, 10, 11], dtype=int8)
array([0, 2, 2, 1], dtype=int8)
array([0, 2, 2, 1], dtype=int8)
a: array([0, 1, 2, ..., 9, 10, 11], dtype=uint16)
array([0, 2, 2, 1], dtype=uint16)
array([0, 2, 2, 1], dtype=uint16)
a: array([0, 1, 2, ..., 9, 10, 11], dtype=int16)
array([0, 2, 2, 1], dtype=int16)
array([0, 2, 2, 1], dtype=int16)
a: array([0.0, 1.0, 2.0, ..., 9.0, 10.0, 11.0], dtype=float64)
array([0.0, 2.0, 2.0, 1.0], dtype=float64)
array([0.0, 2.0, 2.0, 1.0], dtype=float64)
2D arrays
a: array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=uint8)
first axis
array([[0, 1, 2, 3],
[8, 9, 10, 11],
[8, 9, 10, 11],
[4, 5, 6, 7]], dtype=uint8)
second axis
array([[0, 2, 2, 1],
[4, 6, 6, 5],
[8, 10, 10, 9]], dtype=uint8)
a: array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=int8)
first axis
array([[0, 1, 2, 3],
[8, 9, 10, 11],
[8, 9, 10, 11],
[4, 5, 6, 7]], dtype=int8)
second axis
array([[0, 2, 2, 1],
[4, 6, 6, 5],
[8, 10, 10, 9]], dtype=int8)
a: array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=uint16)
first axis
array([[0, 1, 2, 3],
[8, 9, 10, 11],
[8, 9, 10, 11],
[4, 5, 6, 7]], dtype=uint16)
second axis
array([[0, 2, 2, 1],
[4, 6, 6, 5],
[8, 10, 10, 9]], dtype=uint16)
a: array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]], dtype=int16)
first axis
array([[0, 1, 2, 3],
[8, 9, 10, 11],
[8, 9, 10, 11],
[4, 5, 6, 7]], dtype=int16)
second axis
array([[0, 2, 2, 1],
[4, 6, 6, 5],
[8, 10, 10, 9]], dtype=int16)
a: array([[0.0, 1.0, 2.0, 3.0],
[4.0, 5.0, 6.0, 7.0],
[8.0, 9.0, 10.0, 11.0]], dtype=float64)
first axis
array([[0.0, 1.0, 2.0, 3.0],
[8.0, 9.0, 10.0, 11.0],
[8.0, 9.0, 10.0, 11.0],
[4.0, 5.0, 6.0, 7.0]], dtype=float64)
second axis
array([[0.0, 2.0, 2.0, 1.0],
[4.0, 6.0, 6.0, 5.0],
[8.0, 10.0, 10.0, 9.0]], dtype=float64)

21
tests/2d/numpy/xor.py Normal file
View file

@ -0,0 +1,21 @@
try:
from ulab import numpy as np
except ImportError:
import numpy as np
dtypes = (np.uint8, np.int8, np.uint16, np.int16)
for dtype_a in dtypes:
a = np.array(range(5), dtype=dtype_a)
for dtype_b in dtypes:
b = np.array(range(250, 255), dtype=dtype_b)
try:
print('a ^ b: ', a ^ b)
except Exception as e:
print(e)
b = np.array([False, True, False, True, False], dtype=np.bool)
try:
print('a ^ b (bool): ', a ^ b)
except Exception as e:
print(e)

Some files were not shown because too many files have changed in this diff Show more