Compare commits
303 commits
2dim
...
ci-circuit
| Author | SHA1 | Date | |
|---|---|---|---|
| 57ab95cc70 | |||
| d0f68c8560 | |||
|
|
7f2c1ae52b | ||
|
|
ee8b72addd | ||
|
|
04fa205ece | ||
|
|
3617b0735e | ||
|
|
864d5a68bb | ||
|
|
ccb6be5b11 | ||
|
|
96be400e08 | ||
|
|
e2be5a001e | ||
|
|
5c75f08118 | ||
|
|
1061251e06 | ||
|
|
07c7404d95 | ||
|
|
b03d36bcee | ||
|
|
d56a001f16 | ||
|
|
df0af031f1 | ||
|
|
bbe39d9e41 | ||
|
|
115b3da2f6 | ||
|
|
fd0751144e | ||
|
|
a2f27760c6 | ||
|
|
47af016806 | ||
|
|
f9322380de | ||
|
|
72e1924479 | ||
|
|
60f6fa6e6b | ||
|
|
60c8d62d4b | ||
|
|
8f5d1a949e | ||
|
|
368b23ff8b | ||
|
|
3aa37c8e55 | ||
|
|
4a771347bb | ||
|
|
3ba186b19c | ||
|
|
129ad86b8e | ||
|
|
4e218f9d81 | ||
|
|
4c1a7c0933 | ||
|
|
4690ef7c2c | ||
|
|
345b74e3ca | ||
|
|
3a6deef855 | ||
|
|
ddfe1754ca | ||
|
|
bae51f8edb | ||
|
|
a789bd67d0 | ||
|
|
11a7ecff6d | ||
|
|
e77f74df29 | ||
|
|
9159465d62 | ||
|
|
61073f79a8 | ||
|
|
f41d3eeeb7 | ||
|
|
d04d280f9d | ||
|
|
39afe5f2ad | ||
|
|
1f1e181f5d | ||
|
|
05f9f9a7b3 | ||
|
|
e1801bad43 | ||
|
|
359cf78f35 | ||
|
|
f8bf869a45 | ||
|
|
9a82e70728 | ||
|
|
c98cdfdb97 | ||
|
|
2d12162d8f | ||
|
|
7ad3542de6 | ||
|
|
0064995d2c | ||
|
|
86581222c4 | ||
|
|
ab085333ca | ||
|
|
fe8a10dd94 | ||
| 20243311e1 | |||
|
|
f6fd605300 | ||
|
|
a3678b0de7 | ||
|
|
e91e2db0dd | ||
|
|
ac38e8bc5a | ||
|
|
9cf30ad9f2 | ||
|
|
2b55b79471 | ||
|
|
e5341bcf35 | ||
|
|
c93faa9a9c | ||
|
|
b6252474e9 | ||
|
|
4013206dc2 | ||
|
|
4724b516c2 | ||
|
|
e4d07b733a | ||
| 23e1ef3b11 | |||
|
|
4759264994 | ||
|
|
80503300ad | ||
|
|
651f2029af | ||
|
|
cc721aa216 | ||
|
|
d6ae0bcdde | ||
|
|
f1dab38726 | ||
|
|
8b268aedd1 | ||
|
|
e33ef4ec52 | ||
|
|
b60e14d380 | ||
|
|
8d439c3332 | ||
|
|
b3e76e1f0a | ||
|
|
a68b7daa32 | ||
|
|
4b1741515e | ||
|
|
0d80175933 | ||
|
|
f7358cd65f | ||
|
|
3a5d17c10e | ||
|
|
3acd543630 | ||
|
|
5e72d76b6e | ||
|
|
4194cce461 | ||
|
|
b10117a849 | ||
|
|
3b041e1059 | ||
|
|
161d8b0ed0 | ||
|
|
eea4c15ad0 | ||
|
|
c6f2c928b6 | ||
|
|
e202d38b0e | ||
|
|
932e0a2b91 | ||
|
|
c6afd5ad0d | ||
|
|
3820dac6ee | ||
|
|
4581034325 | ||
|
|
13ef14c29a | ||
|
|
7739ae21f8 | ||
|
|
49be9e0ba7 | ||
|
|
bdaa4b3984 | ||
|
|
9ab8ce2153 | ||
|
|
fbe8151f50 | ||
|
|
33451ee285 | ||
|
|
dfe560150a | ||
|
|
f5c15f0c4c | ||
|
|
0443a2a3ed | ||
|
|
059994774d | ||
|
|
f171235a95 | ||
|
|
e75729cb27 | ||
|
|
3bb1417211 | ||
|
|
cc134935f3 | ||
|
|
40503d0f7e | ||
|
|
48cb939839 | ||
|
|
fe7aa27238 | ||
|
|
e9258fe4e7 | ||
|
|
2f55b71542 | ||
|
|
526ffb4c78 | ||
|
|
17c6d605ca | ||
|
|
f9cf519843 | ||
|
|
a223de9c73 | ||
|
|
37140d531e | ||
|
|
1231ac877e | ||
|
|
a73201a5a4 | ||
|
|
4a3c12e427 | ||
| d3b9096790 | |||
|
|
0394801933 | ||
|
|
79f424759c | ||
| cbdd1295c1 | |||
| a2aa5d3a58 | |||
| 1f3836d16f | |||
|
|
666dc77fad | ||
|
|
9da0a5ee0a | ||
|
|
ddbbca7462 | ||
|
|
0249fcd304 | ||
|
|
077bcc51ab | ||
|
|
e509d3caa8 | ||
|
|
78438727c4 | ||
|
|
17b76a60bb | ||
|
|
8a79c80232 | ||
|
|
33d9a5e410 | ||
|
|
f00d5dc6de | ||
|
|
8631b771d2 | ||
|
|
e8bac80c52 | ||
|
|
8056b4e959 | ||
|
|
e31d7ab906 | ||
|
|
4a311c1d64 | ||
|
|
e2cae23253 | ||
|
|
6590acc6c4 | ||
|
|
d5e59cc21f | ||
|
|
df6e3065b8 | ||
|
|
8f5edbb808 | ||
|
|
35cdc9f976 | ||
|
|
a6ec2e65dd | ||
|
|
5c027e44c1 | ||
|
|
2ead6cbc21 | ||
|
|
d991d9d248 | ||
|
|
70666817ce | ||
|
|
6615290fdf | ||
|
|
cf61d728e7 | ||
| a7502f6243 | |||
|
|
314bb010bb | ||
|
|
a081c28998 | ||
|
|
6ff9d2cb04 | ||
|
|
67be0c6762 | ||
|
|
855384f579 | ||
|
|
c7e2c8a2e2 | ||
|
|
fb05e2585c | ||
|
|
c9519e59a8 | ||
|
|
3c1fb52efb | ||
|
|
a0e5f3a474 | ||
|
|
f014128595 | ||
|
|
d29cc8632e | ||
|
|
a844e1bfb9 | ||
| d0b11a6081 | |||
|
|
434211d401 | ||
|
|
778c7e9bd8 | ||
|
|
d1f2d80581 | ||
|
|
a746bd8e09 | ||
|
|
2b7d96f25d | ||
|
|
22813d6736 | ||
| 470ea6578a | |||
| 5fefea4871 | |||
|
|
e83ef7a31d | ||
|
|
1a4ac564d4 | ||
|
|
78ae57005c | ||
|
|
12d2c9127e | ||
|
|
84150d4166 | ||
|
|
7b8d894413 | ||
|
|
7ec8a25a84 | ||
|
|
a09c5f4c25 | ||
|
|
ab9a26dc98 | ||
|
|
55fb2e073b | ||
| 86e7155ab7 | |||
|
|
280f04660e | ||
| f1f10c27a8 | |||
| d41fb86fd0 | |||
| daf4b07ef7 | |||
| 22605c6fe9 | |||
| 8aebfd9f4e | |||
| be1e2b28e0 | |||
| 11ed69dacf | |||
| 59cc5ed6c6 | |||
|
|
ae7cadee8a | ||
|
|
1001ed44f5 | ||
|
|
c69f2d9256 | ||
|
|
8b072b7c0d | ||
|
|
acfd1d0760 | ||
|
|
ceaede8cb8 | ||
|
|
034cdaf7e3 | ||
|
|
32e0bf5bde | ||
|
|
89d73974e3 | ||
|
|
89579b6e36 | ||
|
|
b4f83997e7 | ||
|
|
0d5dc6b0d4 | ||
|
|
411a5ffbd2 | ||
|
|
2e27a356f5 | ||
|
|
26d740ef77 | ||
|
|
a3d77a3fa1 | ||
|
|
1648325d69 | ||
|
|
272685dc50 | ||
|
|
04422f3da5 | ||
|
|
c2c525247c | ||
|
|
2517d8be25 | ||
|
|
3153c86f09 | ||
|
|
ae0dfbc126 | ||
|
|
a2d52b6454 | ||
|
|
ac3f03c3ba | ||
|
|
d55df3d4a0 | ||
|
|
3ec9f5a5bb | ||
| c66509f66e | |||
| 53c158bde3 | |||
| cbe41034a3 | |||
|
|
b3562ae78c | ||
|
|
ea4a7422ef | ||
|
|
97f23da0c1 | ||
|
|
84558f9447 | ||
|
|
a91b36986d | ||
| 3dc52575f0 | |||
|
|
155e6eea60 | ||
|
|
94e5b304d2 | ||
|
|
49e2e68f9b | ||
|
|
3e53136a93 | ||
|
|
7ec399c58b | ||
|
|
47fd7964e8 | ||
|
|
e3a74453a8 | ||
|
|
18d13e4252 | ||
|
|
cb1b1d352b | ||
|
|
c354657eda | ||
|
|
037cd6e733 | ||
|
|
1095994a4a | ||
|
|
ea2bf3c236 | ||
|
|
525fbb6527 | ||
|
|
5d0eab244b | ||
|
|
6b3d43846f | ||
|
|
8241546378 | ||
|
|
0434045293 | ||
|
|
34c2355a2a | ||
|
|
0faa89e3a5 | ||
|
|
c0979509b4 | ||
|
|
ef2c91c1fb | ||
|
|
fb1153d3b3 | ||
|
|
847c7f9d63 | ||
|
|
ab8b5fe4b1 | ||
|
|
700e3ff1ac | ||
|
|
a35c4ff1d8 | ||
|
|
a6ebfc1ade | ||
|
|
882294dabf | ||
|
|
adda973b56 | ||
|
|
903016ec44 | ||
|
|
380b8b0347 | ||
|
|
f9fabc5079 | ||
|
|
585513ce76 | ||
|
|
2ea9656d3f | ||
|
|
66b89de8c7 | ||
|
|
844b85018b | ||
|
|
1c1a693a2b | ||
|
|
f81e950513 | ||
|
|
ffff7606c8 | ||
|
|
bee25781b9 | ||
|
|
47bf2ec9a7 | ||
|
|
badeee48df | ||
|
|
e370e56a15 | ||
|
|
db5f1f85bb | ||
|
|
2bddc94df5 | ||
|
|
aa5ef4afb9 | ||
|
|
d99d834d87 | ||
|
|
83479f115b | ||
| 924dc7012a | |||
|
|
daaacac16f | ||
|
|
aa4d53e292 | ||
| 3febd79aa0 | |||
|
|
7e2be88dff | ||
|
|
e0e840f6d5 | ||
| 102ba5032e | |||
| b83ed3e2ca | |||
| 1e5ebe739d | |||
|
|
8300de7f11 |
60 changed files with 9582 additions and 7941 deletions
37
.github/workflows/build.yml
vendored
37
.github/workflows/build.yml
vendored
|
|
@ -3,23 +3,27 @@ name: Build CI
|
|||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'code/**'
|
||||
- 'tests/**'
|
||||
- '.github/workflows/**'
|
||||
release:
|
||||
types: [published]
|
||||
check_suite:
|
||||
types: [rerequested]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
micropython:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set up Python 3.5
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.5
|
||||
python-version: 3.8
|
||||
|
||||
- name: Versions
|
||||
run: |
|
||||
|
|
@ -56,3 +60,30 @@ jobs:
|
|||
done
|
||||
if: failure()
|
||||
|
||||
circuitpython:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set up Python 3.5
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Versions
|
||||
run: |
|
||||
gcc --version
|
||||
python3 --version
|
||||
|
||||
- name: Checkout ulab
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
sudo apt install librsvg2-bin gettext
|
||||
pip install "Sphinx<4" sphinx-rtd-theme recommonmark sphinx-autoapi sphinxcontrib-svg2pdfconverter astroid isort polib black
|
||||
|
||||
- name: Run build-cp.sh
|
||||
run: ./build-cp.sh
|
||||
|
|
|
|||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/micropython
|
||||
/*.exp
|
||||
/*.out
|
||||
123
README.md
123
README.md
|
|
@ -1,25 +1,108 @@
|
|||
# micropython-ulab
|
||||
# ulab
|
||||
|
||||
ulab is a numpy-like array manipulation library for micropython.
|
||||
The module is written in C, defines compact containers for numerical
|
||||
data, and is fast.
|
||||
`ulab` is a `numpy`-like array manipulation library for `micropython` and `CircuitPython`.
|
||||
The module is written in C, defines compact containers for numerical
|
||||
data, and is fast. The library is a software-only standard `micropython` user module,
|
||||
i.e., it has no hardware dependencies, and can be compiled for any platform.
|
||||
The `float` implementation of `micropython` (`float`, or `double`) is automatically detected.
|
||||
|
||||
Documentation can be found under https://micropython-ulab.readthedocs.io/en/latest/
|
||||
The source for the manual is in https://github.com/v923z/micropython-ulab/blob/master/docs/ulab-manual.ipynb,
|
||||
while developer help is in https://github.com/v923z/micropython-ulab/blob/master/docs/ulab.ipynb.
|
||||
# Supported functions
|
||||
|
||||
|
||||
## ndarray
|
||||
|
||||
`ulab` implements `numpy`'s `ndarray` with the `==`, `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `/`, `*`, and `**` 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, or by means of the `arange`, `eye`, `linspace`,
|
||||
`ones`, or `zeros` functions.
|
||||
|
||||
`ndarray`s can be iterated on, and have a number of their own methods, such as `shape`, `reshape`, `transpose`, `size`, and `itemsize`.
|
||||
|
||||
## Modules
|
||||
|
||||
In addition to the `ndarray`'s operators and methods, seven modules define a great number of functions that can
|
||||
take `ndarray`s or `micropython` iterables as their arguments. If flash space is a concern, unnecessary sub-modules
|
||||
can be excluded from the compiled firmware with a pre-processor switch.
|
||||
|
||||
### approx
|
||||
|
||||
The `approx` sub-module contains the implementation of the `interp`, and `trapz` functions of `numpy`, and `newton`, `bisect`,
|
||||
and `fmin` from `scipy`.
|
||||
|
||||
### compare
|
||||
|
||||
The `compare` sub-module contains the implementation of the `equal`, `not_equal`, `minimum`, `maximum`, and `clip` functions.
|
||||
|
||||
### fft
|
||||
|
||||
The `fft` sub-module implements the fast Fourier transform, and its inverse for one-dimensional `ndarray`s,
|
||||
as well as the `spectrogram` function from `scipy`.
|
||||
|
||||
### filter
|
||||
|
||||
The `filter` sub-module implements `convolve` for one-dimensional convolution,
|
||||
as well as the cascaded second-order sections filter, `sosfilt` from `scipy`.
|
||||
|
||||
### linalg
|
||||
|
||||
The `linalg` sub-module implements functions for matrix inversion, dot product, and the calculation of the
|
||||
determinant, eigenvalues, eigenvectors, Cholesky decomposition, and trace.
|
||||
|
||||
### numerical
|
||||
|
||||
The `numerical` sub-module defines the `roll`, `flip`, `diff`, `sort` and `argsort` functions for `ndarray`s, and,
|
||||
in addition, the `min`, `max`, `argmin`, `argmax`, `sum`, `mean`, `std` functions that work with `ndarray`s, as
|
||||
well as generic one-dimensional iterables.
|
||||
|
||||
### poly
|
||||
|
||||
The `poly` sub-module defines the `polyval`, and `polyfit` functions from `numpy`.
|
||||
|
||||
### vector
|
||||
|
||||
The `vector` sub-module implements all functions of the `math` package (e.g., `acos`, `acosh`, ..., `tan`, `tanh`)
|
||||
of `micropython` for `ndarray`s and iterables. In addition, it also provided tools for vectorising generic,
|
||||
user-defined `python` functions.
|
||||
|
||||
### user
|
||||
|
||||
The `user` sub-module is meant as a user-extendable module, and contains a dummy function only.
|
||||
|
||||
# Finding help
|
||||
|
||||
Documentation can be found on [readthedocs](https://readthedocs.org/) under
|
||||
[micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest),
|
||||
as well as at [circuitpython-ulab](https://circuitpython.readthedocs.io/en/latest/shared-bindings/ulab/__init__.html).
|
||||
A number of practical examples are listed in the excellent
|
||||
[circuitpython-ulab](https://learn.adafruit.com/ulab-crunch-numbers-fast-with-circuitpython/overview) overview.
|
||||
|
||||
# Benchmarks
|
||||
|
||||
Representative numbers on performance can be found under [ulab samples](https://github.com/thiagofe/ulab_samples).
|
||||
|
||||
# Firmware
|
||||
|
||||
Firmware for pyboard.v.1.1, and PYBD_SF6 is updated once in a while, and can be downloaded
|
||||
from https://github.com/v923z/micropython-ulab/releases.
|
||||
Compiled firmware for many hardware platforms can be downloaded from Roberto Colistete's
|
||||
[gitlab repository](https://gitlab.com/rcolistete/micropython-firmwares/-/tree/master/). These include the pyboard, the ESP32, the ESP8266,
|
||||
and the Pycom boards. Since a number of features can be set in the firmware (threading, support for SD card, LEDs, user switch etc.), and it is
|
||||
impossible to create something that suits everyone, these releases should only be used for
|
||||
quick testing of `ulab`. Otherwise, compilation from the source is required with
|
||||
the appropriate settings, which are usually defined in the `mpconfigboard.h` file of the port
|
||||
in question.
|
||||
|
||||
`ulab` is also included in the following compiled `micropython` variants and derivatives:
|
||||
|
||||
1. `CircuitPython` for SAMD51 and nRF microcontrollers https://github.com/adafruit/circuitpython
|
||||
1. `MicroPython for K210` https://github.com/loboris/MicroPython_K210_LoBo
|
||||
1. `MaixPy` https://github.com/sipeed/MaixPy
|
||||
1. `OpenMV` https://github.com/openmv/openmv
|
||||
|
||||
## Compiling
|
||||
|
||||
If you want to try the latest version of `ulab`, or your hardware is
|
||||
different to pyboard.v.1.1, or PYBD_SF6, the firmware can be compiled
|
||||
If you want to try the latest version of `ulab` on `micropython` or one of its forks, the firmware can be compiled
|
||||
from the source by following these steps:
|
||||
|
||||
First, you have to clone the micropython repository by running
|
||||
First, you have to clone the `micropython` repository by running
|
||||
|
||||
```
|
||||
git clone https://github.com/micropython/micropython.git
|
||||
|
|
@ -30,18 +113,13 @@ on the command line. This will create a new repository with the name `micropytho
|
|||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
```
|
||||
|
||||
Then you have to include `ulab` in the compilation process by editing `mpconfigport.h` of the directory of the port for which you want to compile, so, still on the command line, navigate to `micropython/ports/unix`, or `micropython/ports/stm32`, or whichever port is your favourite, and edit the `mpconfigport.h` file there. All you have to do is add a single line at the end:
|
||||
|
||||
```
|
||||
#define MODULE_ULAB_ENABLED (1)
|
||||
```
|
||||
|
||||
This line will inform the compiler that you want `ulab` in the resulting firmware. If you don't have the cross-compiler installed, your might want to do that now, for instance on Linux by executing
|
||||
If you don't have the cross-compiler installed, your might want to do that now, for instance on Linux by executing
|
||||
|
||||
```
|
||||
sudo apt-get install gcc-arm-none-eabi
|
||||
```
|
||||
If that was successful, you can try to run the make command in the port's directory as
|
||||
|
||||
If this step was successful, you can try to run the `make` command in the port's directory as
|
||||
```
|
||||
make BOARD=PYBV11 USER_C_MODULES=../../../ulab all
|
||||
```
|
||||
|
|
@ -49,8 +127,9 @@ which will prepare the firmware for pyboard.v.11. Similarly,
|
|||
```
|
||||
make BOARD=PYBD_SF6 USER_C_MODULES=../../../ulab all
|
||||
```
|
||||
will compile for the SF6 member of the PYBD series. Provided that you managed to compile the firmware, you would upload that by running
|
||||
either
|
||||
will compile for the SF6 member of the PYBD series. If your target is `unix`, you don't need to specify the `BOARD` parameter.
|
||||
|
||||
Provided that you managed to compile the firmware, you would upload that by running either
|
||||
```
|
||||
dfu-util --alt 0 -D firmware.dfu
|
||||
```
|
||||
|
|
|
|||
20
build-cp.sh
Executable file
20
build-cp.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
nproc=$(python -c 'import multiprocessing; print(multiprocessing.cpu_count())')
|
||||
HERE="$(dirname -- "$(readlink -f -- "${0}")" )"
|
||||
[ -e circuitpython/py/py.mk ] || (git clone --depth 100 --branch 6.0.x https://github.com/adafruit/circuitpython && cd circuitpython && git submodule update --init)
|
||||
rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab
|
||||
make -C circuitpython/mpy-cross -j$nproc
|
||||
make -C circuitpython/ports/unix -j$nproc deplibs
|
||||
make -C circuitpython/ports/unix -j$nproc
|
||||
|
||||
if ! env MICROPY_MICROPYTHON=circuitpython/ports/unix/micropython circuitpython/tests/run-tests -d tests; then
|
||||
for exp in *.exp; do
|
||||
testbase=$(basename $exp .exp);
|
||||
echo -e "\nFAILURE $testbase";
|
||||
diff -u $testbase.exp $testbase.out;
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd circuitpython && sphinx-build -E -W -b html . _build/html)
|
||||
17
build.sh
Executable file
17
build.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
HERE="$(dirname -- "$(readlink -f -- "${0}")" )"
|
||||
[ -e micropython/py/py.mk ] || git clone https://github.com/micropython/micropython
|
||||
[ -e micropython/lib/libffi/autogen.sh ] || (cd micropython && git submodule update --init lib/libffi )
|
||||
#git clone https://github.com/micropython/micropython
|
||||
make -C micropython/mpy-cross -j$(nproc)
|
||||
make -C micropython/ports/unix -j$(nproc) deplibs
|
||||
make -C micropython/ports/unix -j$(nproc) USER_C_MODULES="${HERE}" DEBUG=1 STRIP=:
|
||||
|
||||
if ! env MICROPY_MICROPYTHON=micropython/ports/unix/micropython micropython/tests/run-tests -d tests; then
|
||||
for exp in *.exp; do
|
||||
testbase=$(basename $exp .exp);
|
||||
echo -e "\nFAILURE $testbase";
|
||||
diff -u $testbase.exp $testbase.out;
|
||||
done
|
||||
fi
|
||||
553
code/approx/approx.c
Normal file
553
code/approx/approx.c
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Zoltán Vörös
|
||||
* 2020 Diego Elio Pettenò
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "../linalg/linalg.h"
|
||||
#include "approx.h"
|
||||
|
||||
#if ULAB_APPROX_MODULE
|
||||
|
||||
//| """Numerical approximation methods"""
|
||||
//|
|
||||
|
||||
|
||||
const mp_obj_float_t xtolerance = {{&mp_type_float}, MICROPY_FLOAT_CONST(2.4e-7)};
|
||||
const mp_obj_float_t rtolerance = {{&mp_type_float}, MICROPY_FLOAT_CONST(0.0)};
|
||||
const mp_obj_float_t approx_trapz_dx = {{&mp_type_float}, MICROPY_FLOAT_CONST(1.0)};
|
||||
|
||||
STATIC mp_float_t approx_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(type->call(fun, nparams+1, 0, fargs));
|
||||
}
|
||||
|
||||
//| def bisect(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| xtol: float = 2.4e-7,
|
||||
//| maxiter: int = 100
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to bisect
|
||||
//| :param float a: The left side of the interval
|
||||
//| :param float b: The right side of the interval
|
||||
//| :param float xtol: The tolerance value
|
||||
//| :param float maxiter: The maximum number of iterations to perform
|
||||
//|
|
||||
//| Find a solution (zero) of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the bisection method. The result is accurate to within
|
||||
//| ``xtol`` unless more than ``maxiter`` steps are required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
STATIC mp_obj_t approx_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// Simple bisection routine
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
{ MP_QSTR_xtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
|
||||
};
|
||||
|
||||
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(type->call == NULL) {
|
||||
mp_raise_TypeError(translate("first argument must be a function"));
|
||||
}
|
||||
mp_float_t xtol = mp_obj_get_float(args[3].u_obj);
|
||||
mp_obj_t *fargs = m_new(mp_obj_t, 1);
|
||||
mp_float_t left, right;
|
||||
mp_float_t x_mid;
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
left = approx_python_call(type, fun, a, fargs, 0);
|
||||
right = approx_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_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"));
|
||||
}
|
||||
for(uint16_t i=0; i < args[4].u_int; i++) {
|
||||
dx *= MICROPY_FLOAT_CONST(0.5);
|
||||
x_mid = rtb + dx;
|
||||
if(approx_python_call(type, fun, x_mid, fargs, 0) < MICROPY_FLOAT_CONST(0.0)) {
|
||||
rtb = x_mid;
|
||||
}
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(dx) < xtol) break;
|
||||
}
|
||||
return mp_obj_new_float(rtb);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_bisect_obj, 3, approx_bisect);
|
||||
|
||||
//| def fmin(
|
||||
//| fun: Callable[[float], float],
|
||||
//| x0: float,
|
||||
//| *,
|
||||
//| xatol: float = 2.4e-7,
|
||||
//| fatol: float = 2.4e-7,
|
||||
//| maxiter: int = 200
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to bisect
|
||||
//| :param float x0: The initial x value
|
||||
//| :param float xatol: The absolute tolerance value
|
||||
//| :param float fatol: The relative tolerance value
|
||||
//|
|
||||
//| Find a minimum of the function ``f(x)`` using the downhill simplex method.
|
||||
//| The located ``x`` is within ``fxtol`` of the actual minimum, and ``f(x)``
|
||||
//| is within ``fatol`` of the actual minimum unless more than ``maxiter``
|
||||
//| steps are requried."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
STATIC mp_obj_t approx_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// downhill simplex method in 1D
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(type->call == NULL) {
|
||||
mp_raise_TypeError(translate("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);
|
||||
uint16_t maxiter;
|
||||
if(args[4].u_obj == mp_const_none) {
|
||||
maxiter = 200;
|
||||
} else {
|
||||
if(mp_obj_get_int(args[4].u_obj) < 0) {
|
||||
mp_raise_TypeError(translate("maxiter must be > 0"));
|
||||
}
|
||||
maxiter = (uint16_t)mp_obj_get_int(args[4].u_obj);
|
||||
}
|
||||
|
||||
mp_float_t x0 = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t x1 = x0 != MICROPY_FLOAT_CONST(0.0) ? (MICROPY_FLOAT_CONST(1.0) + APPROX_NONZDELTA) * x0 : APPROX_ZDELTA;
|
||||
mp_obj_t *fargs = m_new(mp_obj_t, 1);
|
||||
mp_float_t f0 = approx_python_call(type, fun, x0, fargs, 0);
|
||||
mp_float_t f1 = approx_python_call(type, fun, x1, fargs, 0);
|
||||
if(f1 < f0) {
|
||||
SWAP(mp_float_t, x0, x1);
|
||||
SWAP(mp_float_t, f0, f1);
|
||||
}
|
||||
for(uint16_t i=0; i < maxiter; i++) {
|
||||
uint8_t shrink = 0;
|
||||
f0 = approx_python_call(type, fun, x0, fargs, 0);
|
||||
f1 = approx_python_call(type, fun, x1, fargs, 0);
|
||||
|
||||
// reflection
|
||||
mp_float_t xr = (MICROPY_FLOAT_CONST(1.0) + APPROX_ALPHA) * x0 - APPROX_ALPHA * x1;
|
||||
mp_float_t fr = approx_python_call(type, fun, xr, fargs, 0);
|
||||
if(fr < f0) { // expansion
|
||||
mp_float_t xe = (1 + APPROX_ALPHA * APPROX_BETA) * x0 - APPROX_ALPHA * APPROX_BETA * x1;
|
||||
mp_float_t fe = approx_python_call(type, fun, xe, fargs, 0);
|
||||
if(fe < fr) {
|
||||
x1 = xe;
|
||||
f1 = fe;
|
||||
} else {
|
||||
x1 = xr;
|
||||
f1 = fr;
|
||||
}
|
||||
} else {
|
||||
if(fr < f1) { // contraction
|
||||
mp_float_t xc = (1 + APPROX_GAMMA * APPROX_ALPHA) * x0 - APPROX_GAMMA * APPROX_ALPHA * x1;
|
||||
mp_float_t fc = approx_python_call(type, fun, xc, fargs, 0);
|
||||
if(fc < fr) {
|
||||
x1 = xc;
|
||||
f1 = fc;
|
||||
} else {
|
||||
shrink = 1;
|
||||
}
|
||||
} else { // inside contraction
|
||||
mp_float_t xc = (MICROPY_FLOAT_CONST(1.0) - APPROX_GAMMA) * x0 + APPROX_GAMMA * x1;
|
||||
mp_float_t fc = approx_python_call(type, fun, xc, fargs, 0);
|
||||
if(fc < f1) {
|
||||
x1 = xc;
|
||||
f1 = fc;
|
||||
} else {
|
||||
shrink = 1;
|
||||
}
|
||||
}
|
||||
if(shrink == 1) {
|
||||
x1 = x0 + APPROX_DELTA * (x1 - x0);
|
||||
f1 = approx_python_call(type, fun, x1, fargs, 0);
|
||||
}
|
||||
if((MICROPY_FLOAT_C_FUN(fabs)(f1 - f0) < fatol) ||
|
||||
(MICROPY_FLOAT_C_FUN(fabs)(x1 - x0) < xatol)) {
|
||||
break;
|
||||
}
|
||||
if(f1 < f0) {
|
||||
SWAP(mp_float_t, x0, x1);
|
||||
SWAP(mp_float_t, f0, f1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mp_obj_new_float(x0);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_fmin_obj, 2, approx_fmin);
|
||||
|
||||
#if 0
|
||||
static void approx_jacobi(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t *x, mp_float_t *y, uint16_t len, mp_float_t *params, uint8_t nparams, mp_float_t *jacobi, mp_float_t *grad) {
|
||||
/* Calculates the Jacobian and the gradient of the cost function
|
||||
*
|
||||
* The entries in the Jacobian are
|
||||
* J(m, n) = de_m/da_n,
|
||||
*
|
||||
* where
|
||||
*
|
||||
* e_m = (f(x_m, a1, a2, ...) - y_m)/sigma_m is the error at x_m,
|
||||
*
|
||||
* and
|
||||
*
|
||||
* a1, a2, ..., a_n are the free parameters
|
||||
*/
|
||||
mp_obj_t *fargs0 = m_new(mp_obj_t, lenp+1);
|
||||
mp_obj_t *fargs1 = m_new(mp_obj_t, lenp+1);
|
||||
for(uint8_t p=0; p < nparams; p++) {
|
||||
fargs0[p+1] = mp_obj_new_float(params[p]);
|
||||
fargs1[p+1] = mp_obj_new_float(params[p]);
|
||||
}
|
||||
for(uint8_t p=0; p < nparams; p++) {
|
||||
mp_float_t da = params[p] != MICROPY_FLOAT_CONST(0.0) ? (MICROPY_FLOAT_CONST(1.0) + APPROX_NONZDELTA) * params[p] : APPROX_ZDELTA;
|
||||
fargs1[p+1] = mp_obj_new_float(params[p] + da);
|
||||
grad[p] = MICROPY_FLOAT_CONST(0.0);
|
||||
for(uint16_t i=0; i < len; i++) {
|
||||
mp_float_t f0 = approx_python_call(type, fun, x[i], fargs0, nparams);
|
||||
mp_float_t f1 = approx_python_call(type, fun, x[i], fargs1, nparams);
|
||||
jacobi[i*nparamp+p] = (f1 - f0) / da;
|
||||
grad[p] += (f0 - y[i]) * jacobi[i*nparamp+p];
|
||||
}
|
||||
fargs1[p+1] = fargs0[p+1]; // set back to the original value
|
||||
}
|
||||
}
|
||||
|
||||
static void approx_delta(mp_float_t *jacobi, mp_float_t *grad, uint16_t len, uint8_t nparams, mp_float_t lambda) {
|
||||
//
|
||||
}
|
||||
|
||||
mp_obj_t approx_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// Levenberg-Marquardt non-linear fit
|
||||
// The implementation follows the introductory discussion in Mark Tanstrum's paper, https://arxiv.org/abs/1201.5885
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_p0, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
};
|
||||
|
||||
mp_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(type->call == NULL) {
|
||||
mp_raise_TypeError(translate("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_nditerable(x_obj) || !ndarray_object_is_nditerable(y_obj)) {
|
||||
mp_raise_TypeError(translate("data must be iterable"));
|
||||
}
|
||||
if(!ndarray_object_is_nditerable(p0_obj)) {
|
||||
mp_raise_TypeError(translate("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_float_t *x = m_new(mp_float_t, len);
|
||||
fill_array_iterable(x, x_obj);
|
||||
mp_float_t *y = m_new(mp_float_t, len);
|
||||
fill_array_iterable(y, y_obj);
|
||||
mp_float_t *p0 = m_new(mp_float_t, lenp);
|
||||
fill_array_iterable(p0, p0_obj);
|
||||
mp_float_t *grad = m_new(mp_float_t, len);
|
||||
mp_float_t *jacobi = m_new(mp_float_t, len*len);
|
||||
mp_obj_t *fargs = m_new(mp_obj_t, lenp+1);
|
||||
|
||||
m_del(mp_float_t, p0, lenp);
|
||||
// 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);
|
||||
|
||||
// this has finite binary representation; we will multiply/divide by 4
|
||||
//mp_float_t lambda = 0.0078125;
|
||||
|
||||
//linalg_invert_matrix(mp_float_t *data, size_t N)
|
||||
|
||||
m_del(mp_float_t, x, len);
|
||||
m_del(mp_float_t, y, len);
|
||||
m_del(mp_float_t, grad, len);
|
||||
m_del(mp_float_t, jacobi, len*len);
|
||||
m_del(mp_obj_t, fargs, lenp+1);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_curve_fit_obj, 2, approx_curve_fit);
|
||||
|
||||
#endif
|
||||
|
||||
//| def interp(
|
||||
//| x: ulab.array,
|
||||
//| xp: ulab.array,
|
||||
//| fp: ulab.array,
|
||||
//| *,
|
||||
//| left: Optional[float] = None,
|
||||
//| right: Optional[float] = None
|
||||
//| ) -> ulab.array:
|
||||
//| """
|
||||
//| :param ulab.array x: The x-coordinates at which to evaluate the interpolated values.
|
||||
//| :param ulab.array xp: The x-coordinates of the data points, must be increasing
|
||||
//| :param ulab.array fp: The y-coordinates of the data points, same length as xp
|
||||
//| :param left: Value to return for ``x < xp[0]``, default is ``fp[0]``.
|
||||
//| :param right: Value to return for ``x > xp[-1]``, default is ``fp[-1]``.
|
||||
//|
|
||||
//| Returns the one-dimensional piecewise linear interpolant to a function with given discrete data points (xp, fp), evaluated at x."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
{ MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj);
|
||||
ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj); // xp must hold an increasing sequence of independent values
|
||||
ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj);
|
||||
if(((xp->m != 1) && (xp->n != 1)) || ((fp->m != 1) && (fp->n != 1)) ||
|
||||
(xp->array->len < 2) || (fp->array->len < 2) || (xp->array->len != fp->array->len)) {
|
||||
mp_raise_ValueError(translate("interp is defined for 1D arrays of equal length"));
|
||||
}
|
||||
ndarray_obj_t *y = create_new_ndarray(x->m, x->n, NDARRAY_FLOAT);
|
||||
mp_float_t left_value, right_value;
|
||||
mp_float_t xp_left = ndarray_get_float_value(xp->array->items, xp->array->typecode, 0);
|
||||
mp_float_t xp_right = ndarray_get_float_value(xp->array->items, xp->array->typecode, xp->array->len-1);
|
||||
if(args[3].u_obj == mp_const_none) {
|
||||
left_value = ndarray_get_float_value(fp->array->items, fp->array->typecode, 0);
|
||||
} else {
|
||||
left_value = mp_obj_get_float(args[3].u_obj);
|
||||
}
|
||||
if(args[4].u_obj == mp_const_none) {
|
||||
right_value = ndarray_get_float_value(fp->array->items, fp->array->typecode, fp->array->len-1);
|
||||
} else {
|
||||
right_value = mp_obj_get_float(args[4].u_obj);
|
||||
}
|
||||
mp_float_t *yarray = (mp_float_t *)y->array->items;
|
||||
for(size_t i=0; i < x->array->len; i++, yarray++) {
|
||||
mp_float_t x_value = ndarray_get_float_value(x->array->items, x->array->typecode, i);
|
||||
if(x_value <= xp_left) {
|
||||
*yarray = left_value;
|
||||
} else if(x_value >= xp_right) {
|
||||
*yarray = right_value;
|
||||
} else { // do the binary search here
|
||||
mp_float_t xp_left_, xp_right_;
|
||||
mp_float_t fp_left, fp_right;
|
||||
size_t left_index = 0, right_index = xp->array->len - 1, middle_index;
|
||||
while(right_index - left_index > 1) {
|
||||
middle_index = left_index + (right_index - left_index) / 2;
|
||||
mp_float_t xp_middle = ndarray_get_float_value(xp->array->items, xp->array->typecode, middle_index);
|
||||
if(x_value <= xp_middle) {
|
||||
right_index = middle_index;
|
||||
} else {
|
||||
left_index = middle_index;
|
||||
}
|
||||
}
|
||||
xp_left_ = ndarray_get_float_value(xp->array->items, xp->array->typecode, left_index);
|
||||
xp_right_ = ndarray_get_float_value(xp->array->items, xp->array->typecode, right_index);
|
||||
fp_left = ndarray_get_float_value(fp->array->items, fp->array->typecode, left_index);
|
||||
fp_right = ndarray_get_float_value(fp->array->items, fp->array->typecode, right_index);
|
||||
*yarray = fp_left + (x_value - xp_left_) * (fp_right - fp_left) / (xp_right_ - xp_left_);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(y);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp);
|
||||
|
||||
//| def newton(
|
||||
//| fun: Callable[[float], float],
|
||||
//| x0: float,
|
||||
//| *,
|
||||
//| xtol: float = 2.4e-7,
|
||||
//| rtol: float = 0.0,
|
||||
//| maxiter: int = 50
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to bisect
|
||||
//| :param float x0: The initial x value
|
||||
//| :param float xtol: The absolute tolerance value
|
||||
//| :param float rtol: The relative tolerance value
|
||||
//| :param float maxiter: The maximum number of iterations to perform
|
||||
//|
|
||||
//| Find a solution (zero) of the function ``f(x)`` using Newton's Method.
|
||||
//| The result is accurate to within ``xtol * rtol * |f(x)|`` unless more than
|
||||
//| ``maxiter`` steps are requried."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
STATIC mp_obj_t approx_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// this is actually the secant method, as the first derivative of the function
|
||||
// is not accepted as an argument. The function whose root we want to solve for
|
||||
// must depend on a single variable without parameters, i.e., f(x)
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_rtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&rtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 50} },
|
||||
};
|
||||
|
||||
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(type->call == NULL) {
|
||||
mp_raise_TypeError(translate("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);
|
||||
mp_float_t rtol = mp_obj_get_float(args[3].u_obj);
|
||||
mp_float_t dx, df, fx;
|
||||
dx = x > MICROPY_FLOAT_CONST(0.0) ? APPROX_EPS * x : -APPROX_EPS * x;
|
||||
mp_obj_t *fargs = m_new(mp_obj_t, 1);
|
||||
if(args[4].u_int < 0) {
|
||||
mp_raise_ValueError(translate("maxiter must be > 0"));
|
||||
}
|
||||
for(uint16_t i=0; i < args[4].u_int; i++) {
|
||||
fx = approx_python_call(type, fun, x, fargs, 0);
|
||||
df = (approx_python_call(type, fun, x + dx, fargs, 0) - fx) / dx;
|
||||
dx = fx / df;
|
||||
x -= dx;
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(dx) < (tol + rtol * MICROPY_FLOAT_C_FUN(fabs)(x))) break;
|
||||
}
|
||||
return mp_obj_new_float(x);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_newton_obj, 2, approx_newton);
|
||||
|
||||
//| def trapz(y: ulab.array, x: Optional[ulab.array] = None, dx: float = 1.0) -> float:
|
||||
//| """
|
||||
//| :param 1D ulab.array y: the values of the dependent variable
|
||||
//| :param 1D ulab.array x: optional, the coordinates of the independent variable. Defaults to uniformly spaced values.
|
||||
//| :param float dx: the spacing between sample points, if x=None
|
||||
//|
|
||||
//| Returns the integral of y(x) using the trapezoidal rule.
|
||||
//| """
|
||||
//| ...
|
||||
//|
|
||||
|
||||
STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&approx_trapz_dx)} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj);
|
||||
ndarray_obj_t *x;
|
||||
mp_float_t sum = MICROPY_FLOAT_CONST(0.0);
|
||||
if(y->array->len < 2) {
|
||||
return mp_obj_new_float(sum);
|
||||
}
|
||||
if(args[1].u_obj != mp_const_none) {
|
||||
x = ndarray_from_mp_obj(args[1].u_obj); // x must hold an increasing sequence of independent values
|
||||
if(((y->m != 1) && (y->n != 1)) || ((x->m != 1) && (x->n != 1)) || (y->array->len != x->array->len)) {
|
||||
mp_raise_ValueError(translate("trapz is defined for 1D arrays of equal length"));
|
||||
}
|
||||
mp_float_t x1, x2, y1, y2;
|
||||
y1 = ndarray_get_float_value(y->array->items, y->array->typecode, 0);
|
||||
x1 = ndarray_get_float_value(x->array->items, x->array->typecode, 0);
|
||||
for(size_t i=1; i < y->array->len; i++) {
|
||||
y2 = ndarray_get_float_value(y->array->items, y->array->typecode, i);
|
||||
x2 = ndarray_get_float_value(x->array->items, x->array->typecode, i);
|
||||
sum += (x2 - x1) * (y2 + y1);
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
} else {
|
||||
mp_float_t y1, y2;
|
||||
mp_float_t dx = mp_obj_get_float(args[2].u_obj);
|
||||
y1 = ndarray_get_float_value(y->array->items, y->array->typecode, 0);
|
||||
for(size_t i=1; i < y->array->len; i++) {
|
||||
y2 = ndarray_get_float_value(y->array->items, y->array->typecode, i);
|
||||
sum += (y2 + y1);
|
||||
y1 = y2;
|
||||
}
|
||||
sum *= dx;
|
||||
}
|
||||
return mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)*sum);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_trapz_obj, 1, approx_trapz);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_approx_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_approx) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_bisect), (mp_obj_t)&approx_bisect_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_fmin), (mp_obj_t)&approx_fmin_obj },
|
||||
// { MP_OBJ_NEW_QSTR(MP_QSTR_curve_fit), (mp_obj_t)&approx_curve_fit_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_interp), (mp_obj_t)&approx_interp_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_newton), (mp_obj_t)&approx_newton_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_trapz), (mp_obj_t)&approx_trapz_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_approx_globals, ulab_approx_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_approx_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_approx_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
37
code/approx/approx.h
Normal file
37
code/approx/approx.h
Normal 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) 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _APPROX_
|
||||
#define _APPROX_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_APPROX_MODULE
|
||||
|
||||
#define APPROX_EPS MICROPY_FLOAT_CONST(1.0e-4)
|
||||
#define APPROX_NONZDELTA MICROPY_FLOAT_CONST(0.05)
|
||||
#define APPROX_ZDELTA MICROPY_FLOAT_CONST(0.00025)
|
||||
#define APPROX_ALPHA MICROPY_FLOAT_CONST(1.0)
|
||||
#define APPROX_BETA MICROPY_FLOAT_CONST(2.0)
|
||||
#define APPROX_GAMMA MICROPY_FLOAT_CONST(0.5)
|
||||
#define APPROX_DELTA MICROPY_FLOAT_CONST(0.5)
|
||||
|
||||
extern mp_obj_module_t ulab_approx_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_bisect_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_newton_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_fmin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_interp_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_trapz_obj);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
241
code/compare/compare.c
Normal file
241
code/compare/compare.c
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "compare.h"
|
||||
|
||||
#if ULAB_COMPARE_MODULE
|
||||
|
||||
//| """Comparison functions"""
|
||||
//|
|
||||
|
||||
static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) {
|
||||
// the function is implemented for scalars and ndarrays only, with partial
|
||||
// broadcasting: arguments must be either scalars, or ndarrays of equal size/shape
|
||||
if(!(MP_OBJ_IS_INT(x1) || mp_obj_is_float(x1) || MP_OBJ_IS_TYPE(x1, &ulab_ndarray_type)) &&
|
||||
!(MP_OBJ_IS_INT(x2) || mp_obj_is_float(x2) || MP_OBJ_IS_TYPE(x2, &ulab_ndarray_type))) {
|
||||
mp_raise_TypeError(translate("function is implemented for scalars and ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *ndarray1 = ndarray_from_mp_obj(x1);
|
||||
ndarray_obj_t *ndarray2 = ndarray_from_mp_obj(x2);
|
||||
// check, whether partial broadcasting is possible here
|
||||
if((ndarray1->m != ndarray2->m) || (ndarray1->n != ndarray2->n)) {
|
||||
if((ndarray1->array->len != 1) && (ndarray2->array->len != 1)) {
|
||||
mp_raise_ValueError(translate("operands could not be broadcast together"));
|
||||
}
|
||||
}
|
||||
if((comptype == MP_BINARY_OP_EQUAL) || (comptype == MP_BINARY_OP_NOT_EQUAL)) {
|
||||
return ndarray_binary_op(comptype, x1, x2);
|
||||
}
|
||||
size_t m = MAX(ndarray1->m, ndarray2->m);
|
||||
size_t n = MAX(ndarray1->n, ndarray2->n);
|
||||
size_t len = MAX(ndarray1->array->len, ndarray2->array->len);
|
||||
size_t inc1 = ndarray1->array->len == 1 ? 0 : 1;
|
||||
size_t inc2 = ndarray2->array->len == 1 ? 0 : 1;
|
||||
// These are the upcasting rules
|
||||
// float always becomes float
|
||||
// operation on identical types preserves type
|
||||
// uint8 + int8 => int16
|
||||
// uint8 + int16 => int16
|
||||
// uint8 + uint16 => uint16
|
||||
// int8 + int16 => int16
|
||||
// int8 + uint16 => uint16
|
||||
// uint16 + int16 => float
|
||||
// The parameters of RUN_BINARY_LOOP are
|
||||
// typecode of result, type_out, type_left, type_right, lhs operand, rhs operand, operator
|
||||
if(ndarray1->array->typecode == NDARRAY_UINT8) {
|
||||
if(ndarray2->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT8, uint8_t, uint8_t, uint8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint8_t, uint16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
}
|
||||
} else if(ndarray1->array->typecode == NDARRAY_INT8) {
|
||||
if(ndarray2->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT8, int8_t, int8_t, int8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, int16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int8_t, mp_float_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
}
|
||||
} else if(ndarray1->array->typecode == NDARRAY_UINT16) {
|
||||
if(ndarray2->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, int8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, int16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
}
|
||||
} else if(ndarray1->array->typecode == NDARRAY_INT16) {
|
||||
if(ndarray2->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, uint8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, uint16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, mp_float_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
}
|
||||
} else if(ndarray1->array->typecode == NDARRAY_FLOAT) {
|
||||
if(ndarray2->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int8_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int16_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
} else if(ndarray2->array->typecode == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, mp_float_t, ndarray1, ndarray2, comptype, m, n, len, inc1, inc2);
|
||||
}
|
||||
}
|
||||
return mp_const_none; // we should never reach this point
|
||||
}
|
||||
|
||||
static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) {
|
||||
// scalar comparisons should return a single object of mp_obj_t type
|
||||
mp_obj_t result = compare_function(x1, x2, comptype);
|
||||
if((MP_OBJ_IS_INT(x1) || mp_obj_is_float(x1)) && (MP_OBJ_IS_INT(x2) || mp_obj_is_float(x2))) {
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(result, &iter_buf);
|
||||
mp_obj_t item = mp_iternext(iterable);
|
||||
return item;
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
//| def clip(
|
||||
//| x1: Union[ulab.array, float],
|
||||
//| x2: Union[ulab.array, float],
|
||||
//| x3: Union[ulab.array, float],
|
||||
//| ) -> ulab.array:
|
||||
//| """
|
||||
//| Constrain the values from ``x1`` to be between ``x2`` and ``x3``.
|
||||
//| ``x2`` is assumed to be less than or equal to ``x3``.
|
||||
//|
|
||||
//| Arguments may be ulab arrays or numbers. All array arguments
|
||||
//| must be the same size. If the inputs are all scalars, a 1-element
|
||||
//| array is returned.
|
||||
//|
|
||||
//| Shorthand for ``ulab.maximum(x2, ulab.minimum(x1, x3))``"""
|
||||
//| ...
|
||||
//|
|
||||
static 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
|
||||
// would not gain a factor of two in speed, since the two comparisons should still be
|
||||
// evaluated. In contrast, calling the function twice adds only 140 bytes to the firmware
|
||||
return compare_function(x2, compare_function(x1, x3, COMPARE_MINIMUM), COMPARE_MAXIMUM);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip);
|
||||
|
||||
//| def equal(x1: Union[ulab.array, float], x2: Union[ulab.array, float]) -> List[bool]:
|
||||
//| """Return an array of bool which is true where x1[i] == x2[i] and false elsewhere"""
|
||||
//| ...
|
||||
//|
|
||||
static mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) {
|
||||
return compare_equal_helper(x1, x2, MP_BINARY_OP_EQUAL);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal);
|
||||
|
||||
//| def not_equal(x1: Union[ulab.array, float], x2: Union[ulab.array, float]) -> List[bool]:
|
||||
//| """Return an array of bool which is false where x1[i] == x2[i] and true elsewhere"""
|
||||
//| ...
|
||||
//|
|
||||
static mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
|
||||
return compare_equal_helper(x1, x2, MP_BINARY_OP_NOT_EQUAL);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_not_equal_obj, compare_not_equal);
|
||||
|
||||
//| def maximum(x1: Union[ulab.array, float], x2: Union[ulab.array, float]) -> ulab.array:
|
||||
//| """
|
||||
//| Compute the element by element maximum of the arguments.
|
||||
//|
|
||||
//| Arguments may be ulab arrays or numbers. All array arguments
|
||||
//| must be the same size. If the inputs are both scalars, a number is
|
||||
//| returned"""
|
||||
//| ...
|
||||
//|
|
||||
static 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);
|
||||
if((MP_OBJ_IS_INT(x1) || mp_obj_is_float(x1)) && (MP_OBJ_IS_INT(x2) || mp_obj_is_float(x2))) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result);
|
||||
return mp_binary_get_val_array(ndarray->array->typecode, ndarray->array->items, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum);
|
||||
|
||||
//| def minimum(x1: Union[ulab.array, float], x2: Union[ulab.array, float]) -> ulab.array:
|
||||
//| """Compute the element by element minimum of the arguments.
|
||||
//|
|
||||
//| Arguments may be ulab arrays or numbers. All array arguments
|
||||
//| must be the same size. If the inputs are both scalars, a number is
|
||||
//| returned"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static 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);
|
||||
if((MP_OBJ_IS_INT(x1) || mp_obj_is_float(x1)) && (MP_OBJ_IS_INT(x2) || mp_obj_is_float(x2))) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result);
|
||||
return mp_binary_get_val_array(ndarray->array->typecode, ndarray->array->items, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_compare_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_compare) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_clip), (mp_obj_t)&compare_clip_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_equal), (mp_obj_t)&compare_equal_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_not_equal), (mp_obj_t)&compare_not_equal_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_maximum), (mp_obj_t)&compare_maximum_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_minimum), (mp_obj_t)&compare_minimum_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_compare_globals, ulab_compare_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_compare_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_compare_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
45
code/compare/compare.h
Normal file
45
code/compare/compare.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _COMPARE_
|
||||
#define _COMPARE_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_COMPARE_MODULE
|
||||
|
||||
enum COMPARE_FUNCTION_TYPE {
|
||||
COMPARE_MINIMUM,
|
||||
COMPARE_MAXIMUM,
|
||||
COMPARE_CLIP,
|
||||
};
|
||||
|
||||
extern mp_obj_module_t ulab_compare_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj);
|
||||
|
||||
#define RUN_COMPARE_LOOP(typecode, type_out, type1, type2, nd1, nd2, op, m, n, len, inc1, inc2) do {\
|
||||
type1 *array1 = (type1 *)(nd1)->array->items;\
|
||||
type2 *array2 = (type2 *)(nd2)->array->items;\
|
||||
ndarray_obj_t *out = create_new_ndarray((m), (n), (typecode));\
|
||||
type_out *(odata) = (type_out *)out->array->items;\
|
||||
if((op) == COMPARE_MINIMUM) { for(size_t i=0; i < (len); i++, array1+=(inc1), array2+=(inc2)) *odata++ = *array1 < *array2 ? *array1 : *array2; }\
|
||||
if((op) == COMPARE_MAXIMUM) { for(size_t i=0; i < (len); i++, array1+=(inc1), array2+=(inc2)) *odata++ = *array1 > *array2 ? *array1 : *array2; }\
|
||||
return MP_OBJ_FROM_PTR(out);\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "extras.h"
|
||||
|
||||
#if ULAB_EXTRAS_MODULE
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_filter_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_extras) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_extras_globals, ulab_extras_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_filter_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_extras_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
|
@ -18,27 +19,23 @@
|
|||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
#include "ndarray.h"
|
||||
#include "fft.h"
|
||||
|
||||
#if ULAB_FFT_MODULE
|
||||
|
||||
enum FFT_TYPE {
|
||||
FFT_FFT,
|
||||
FFT_IFFT,
|
||||
FFT_SPECTRUM,
|
||||
};
|
||||
//| """Frequency-domain functions"""
|
||||
//|
|
||||
|
||||
void fft_kernel(mp_float_t *real, mp_float_t *imag, int n, int isign) {
|
||||
static void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) {
|
||||
// This is basically a modification of four1 from Numerical Recipes
|
||||
// The main difference is that this function takes two arrays, one
|
||||
// for the real, and one for the imaginary parts.
|
||||
int j, m, mmax, istep;
|
||||
// The main difference is that this function takes two arrays, one
|
||||
// for the real, and one for the imaginary parts.
|
||||
size_t j, m, mmax, istep;
|
||||
mp_float_t tempr, tempi;
|
||||
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
|
||||
|
||||
j = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
if (j > i) {
|
||||
SWAP(mp_float_t, real[i], real[j]);
|
||||
SWAP(mp_float_t, imag[i], imag[j]);
|
||||
|
|
@ -54,14 +51,14 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, int n, int isign) {
|
|||
mmax = 1;
|
||||
while (n > mmax) {
|
||||
istep = mmax << 1;
|
||||
theta = -2.0*isign*MP_PI/istep;
|
||||
wtemp = MICROPY_FLOAT_C_FUN(sin)(0.5 * theta);
|
||||
wpr = -2.0 * wtemp * wtemp;
|
||||
theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
|
||||
wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
|
||||
wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
|
||||
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
|
||||
wr = 1.0;
|
||||
wi = 0.0;
|
||||
wr = MICROPY_FLOAT_CONST(1.0);
|
||||
wi = MICROPY_FLOAT_CONST(0.0);
|
||||
for(m = 0; m < mmax; m++) {
|
||||
for(int i = m; i < n; i += istep) {
|
||||
for(size_t i = m; i < n; i += istep) {
|
||||
j = i + mmax;
|
||||
tempr = wr * real[j] - wi * imag[j];
|
||||
tempi = wr * imag[j] + wi * real[j];
|
||||
|
|
@ -81,7 +78,7 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, int n, int isign) {
|
|||
mp_obj_t fft_fft_ifft_spectrum(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"));
|
||||
}
|
||||
}
|
||||
if(n_args == 2) {
|
||||
if(!MP_OBJ_IS_TYPE(arg_im, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
|
||||
|
|
@ -89,15 +86,15 @@ mp_obj_t fft_fft_ifft_spectrum(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im,
|
|||
}
|
||||
// Check if input is of length of power of 2
|
||||
ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re);
|
||||
uint16_t len = re->array->len;
|
||||
size_t len = re->array->len;
|
||||
if((len & (len-1)) != 0) {
|
||||
mp_raise_ValueError(translate("input array length must be power of 2"));
|
||||
}
|
||||
|
||||
|
||||
ndarray_obj_t *out_re = create_new_ndarray(1, len, NDARRAY_FLOAT);
|
||||
mp_float_t *data_re = (mp_float_t *)out_re->array->items;
|
||||
|
||||
if(re->array->typecode == NDARRAY_FLOAT) {
|
||||
|
||||
if(re->array->typecode == NDARRAY_FLOAT) {
|
||||
// By treating this case separately, we can save a bit of time.
|
||||
// I don't know if it is worthwhile, though...
|
||||
memcpy((mp_float_t *)out_re->array->items, (mp_float_t *)re->array->items, re->bytes);
|
||||
|
|
@ -125,9 +122,9 @@ mp_obj_t fft_fft_ifft_spectrum(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im,
|
|||
}
|
||||
}
|
||||
|
||||
if((type == FFT_FFT) || (type == FFT_SPECTRUM)) {
|
||||
if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) {
|
||||
fft_kernel(data_re, data_im, len, 1);
|
||||
if(type == FFT_SPECTRUM) {
|
||||
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++;
|
||||
|
|
@ -142,7 +139,7 @@ mp_obj_t fft_fft_ifft_spectrum(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im,
|
|||
*data_im++ /= len;
|
||||
}
|
||||
}
|
||||
if(type == FFT_SPECTRUM) {
|
||||
if(type == FFT_SPECTROGRAM) {
|
||||
return MP_OBJ_TO_PTR(out_re);
|
||||
} else {
|
||||
mp_obj_t tuple[2];
|
||||
|
|
@ -152,17 +149,39 @@ mp_obj_t fft_fft_ifft_spectrum(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im,
|
|||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
|
||||
//| def fft(r: ulab.array, c: Optional[ulab.array] = None) -> Tuple[ulab.array, ulab.array]:
|
||||
//| """
|
||||
//| :param ulab.array r: A 1-dimension array of values whose size is a power of 2
|
||||
//| :param ulab.array c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value
|
||||
//| :return tuple (r, c): The real and complex parts of the FFT
|
||||
//|
|
||||
//| Perform a Fast Fourier Transform from the time domain into the frequency domain
|
||||
//|
|
||||
//| See also ~ulab.extras.spectrum, which computes the magnitude of the fft,
|
||||
//| rather than separately returning its real and imaginary parts."""
|
||||
//| ...
|
||||
//|
|
||||
static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_FFT);
|
||||
} else {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_FFT);
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_FFT);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
|
||||
|
||||
mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
|
||||
//| def ifft(r: ulab.array, c: Optional[ulab.array] = None) -> Tuple[ulab.array, ulab.array]:
|
||||
//| """
|
||||
//| :param ulab.array r: A 1-dimension array of values whose size is a power of 2
|
||||
//| :param ulab.array c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value
|
||||
//| :return tuple (r, c): The real and complex parts of the inverse FFT
|
||||
//|
|
||||
//| Perform an Inverse Fast Fourier Transform from the frequeny domain into the time domain"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_IFFT);
|
||||
} else {
|
||||
|
|
@ -172,22 +191,30 @@ mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
|
|||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
|
||||
|
||||
mp_obj_t fft_spectrum(size_t n_args, const mp_obj_t *args) {
|
||||
//| def spectrogram(r: ulab.array) -> ulab.array:
|
||||
//| """
|
||||
//| :param ulab.array r: A 1-dimension array of values whose size is a power of 2
|
||||
//|
|
||||
//| Computes the spectrum of the input signal. This is the absolute value of the (complex-valued) fft of the signal.
|
||||
//| This function is similar to scipy's ``scipy.signal.spectrogram``."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t fft_spectrogram(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_SPECTRUM);
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_SPECTROGRAM);
|
||||
} else {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_SPECTRUM);
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_SPECTROGRAM);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_spectrum_obj, 1, 2, fft_spectrum);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_spectrogram_obj, 1, 2, fft_spectrogram);
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fft) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_fft), (mp_obj_t)&fft_fft_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrum), (mp_obj_t)&fft_spectrum_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrogram), (mp_obj_t)&fft_spectrogram_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table);
|
||||
|
|
@ -196,6 +223,5 @@ mp_obj_module_t ulab_fft_module = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
#ifndef _FFT_
|
||||
#define _FFT_
|
||||
#include "ulab.h"
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
|
|
@ -19,6 +20,12 @@
|
|||
|
||||
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
|
||||
|
||||
enum FFT_TYPE {
|
||||
FFT_FFT,
|
||||
FFT_IFFT,
|
||||
FFT_SPECTROGRAM,
|
||||
};
|
||||
|
||||
#if ULAB_FFT_MODULE
|
||||
|
||||
extern mp_obj_module_t ulab_fft_module;
|
||||
|
|
@ -27,5 +34,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj);
|
|||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_spectrum_obj);
|
||||
|
||||
mp_obj_t fft_fft_ifft_spectrum(size_t , mp_obj_t , mp_obj_t , uint8_t );
|
||||
|
||||
#endif
|
||||
#endif
|
||||
101
code/filter.c
101
code/filter.c
|
|
@ -1,101 +0,0 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "filter.h"
|
||||
|
||||
#if ULAB_FILTER_MODULE
|
||||
mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(2, 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"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
int len_a = a->array->len;
|
||||
int len_c = c->array->len;
|
||||
// deal with linear arrays only
|
||||
if(a->m*a->n != len_a || c->m*c->n != len_c) {
|
||||
mp_raise_TypeError(translate("convolve arguments must be linear arrays"));
|
||||
}
|
||||
if(len_a == 0 || len_c == 0) {
|
||||
mp_raise_TypeError(translate("convolve arguments must not be empty"));
|
||||
}
|
||||
|
||||
int len = len_a + len_c - 1; // convolve mode "full"
|
||||
ndarray_obj_t *out = create_new_ndarray(1, len, NDARRAY_FLOAT);
|
||||
mp_float_t *outptr = out->array->items;
|
||||
int off = len_c-1;
|
||||
|
||||
if(a->array->typecode == NDARRAY_FLOAT && c->array->typecode == NDARRAY_FLOAT) {
|
||||
mp_float_t* a_items = (mp_float_t*)a->array->items;
|
||||
mp_float_t* c_items = (mp_float_t*)c->array->items;
|
||||
for(int k=-off; k<len-off; k++) {
|
||||
mp_float_t accum = (mp_float_t)0;
|
||||
int top_n = MIN(len_c, len_a - k);
|
||||
int bot_n = MAX(-k, 0);
|
||||
mp_float_t* a_ptr = a_items + bot_n + k;
|
||||
mp_float_t* a_end = a_ptr + (top_n - bot_n);
|
||||
mp_float_t* c_ptr = c_items + len_c - bot_n - 1;
|
||||
for(; a_ptr != a_end;) {
|
||||
accum += *a_ptr++ * *c_ptr--;
|
||||
}
|
||||
*outptr++ = accum;
|
||||
}
|
||||
} else {
|
||||
for(int k=-off; k<len-off; k++) {
|
||||
mp_float_t accum = (mp_float_t)0;
|
||||
int top_n = MIN(len_c, len_a - k);
|
||||
int bot_n = MAX(-k, 0);
|
||||
for(int n=bot_n; n<top_n; n++) {
|
||||
int idx_c = len_c - n - 1;
|
||||
int idx_a = n+k;
|
||||
mp_float_t ai = ndarray_get_float_value(a->array->items, a->array->typecode, idx_a);
|
||||
mp_float_t ci = ndarray_get_float_value(c->array->items, c->array->typecode, idx_c);
|
||||
accum += ai * ci;
|
||||
}
|
||||
*outptr++ = accum;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve);
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_rom_map_elem_t ulab_filter_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_filter) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_convolve), (mp_obj_t)&filter_convolve_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_filter_globals, ulab_filter_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_filter_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_filter_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
227
code/filter/filter.c
Normal file
227
code/filter/filter.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "filter.h"
|
||||
|
||||
#if ULAB_FILTER_MODULE
|
||||
|
||||
//| """Filtering functions"""
|
||||
//|
|
||||
|
||||
//| def convolve(a: ulab.array, v: ulab.array) -> ulab.array:
|
||||
//| """
|
||||
//| :param ulab.array a:
|
||||
//| :param ulab.array v:
|
||||
//|
|
||||
//| Returns the discrete, linear convolution of two one-dimensional sequences.
|
||||
//| The result is always an array of float. Only the ``full`` mode is supported,
|
||||
//| and the ``mode`` named parameter of numpy is not accepted. Note that all other
|
||||
//| modes can be had by slicing a ``full`` result.
|
||||
//|
|
||||
//| Convolution filters can implement high pass, low pass, band pass, etc.,
|
||||
//| filtering operations. Convolution filters are typically constructed ahead
|
||||
//| of time. This can be done using desktop python with scipy, or on web pages
|
||||
//| such as https://fiiir.com/
|
||||
//|
|
||||
//| Convolution is most time-efficient when both inputs are of float type."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
|
||||
mp_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_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("convolve arguments must be ndarrays"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
size_t len_a = a->array->len;
|
||||
size_t len_c = c->array->len;
|
||||
// deal with linear arrays only
|
||||
if(a->m*a->n != len_a || c->m*c->n != len_c) {
|
||||
mp_raise_TypeError(translate("convolve arguments must be linear arrays"));
|
||||
}
|
||||
if(len_a == 0 || len_c == 0) {
|
||||
mp_raise_TypeError(translate("convolve arguments must not be empty"));
|
||||
}
|
||||
|
||||
int len = len_a + len_c - 1; // convolve mode "full"
|
||||
ndarray_obj_t *out = create_new_ndarray(1, len, NDARRAY_FLOAT);
|
||||
mp_float_t *outptr = out->array->items;
|
||||
int off = len_c-1;
|
||||
|
||||
if(a->array->typecode == NDARRAY_FLOAT && c->array->typecode == NDARRAY_FLOAT) {
|
||||
mp_float_t* a_items = (mp_float_t*)a->array->items;
|
||||
mp_float_t* c_items = (mp_float_t*)c->array->items;
|
||||
for(int k=-off; k<len-off; k++) {
|
||||
mp_float_t accum = (mp_float_t)0;
|
||||
int top_n = MIN(len_c, len_a - k);
|
||||
int bot_n = MAX(-k, 0);
|
||||
mp_float_t* a_ptr = a_items + bot_n + k;
|
||||
mp_float_t* a_end = a_ptr + (top_n - bot_n);
|
||||
mp_float_t* c_ptr = c_items + len_c - bot_n - 1;
|
||||
for(; a_ptr != a_end;) {
|
||||
accum += *a_ptr++ * *c_ptr--;
|
||||
}
|
||||
*outptr++ = accum;
|
||||
}
|
||||
} else {
|
||||
for(int k=-off; k<len-off; k++) {
|
||||
mp_float_t accum = (mp_float_t)0;
|
||||
int top_n = MIN(len_c, len_a - k);
|
||||
int bot_n = MAX(-k, 0);
|
||||
for(int n=bot_n; n<top_n; n++) {
|
||||
int idx_c = len_c - n - 1;
|
||||
int idx_a = n+k;
|
||||
mp_float_t ai = ndarray_get_float_value(a->array->items, a->array->typecode, idx_a);
|
||||
mp_float_t ci = ndarray_get_float_value(c->array->items, c->array->typecode, idx_c);
|
||||
accum += ai * ci;
|
||||
}
|
||||
*outptr++ = accum;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve);
|
||||
|
||||
static void filter_sosfilt_array(mp_float_t *x, const mp_float_t *coeffs, mp_float_t *zf, const size_t len) {
|
||||
for(size_t i=0; i < len; i++) {
|
||||
mp_float_t xn = *x;
|
||||
*x = coeffs[0] * xn + zf[0];
|
||||
zf[0] = zf[1] + coeffs[1] * xn - coeffs[4] * *x;
|
||||
zf[1] = coeffs[2] * xn - coeffs[5] * *x;
|
||||
x++;
|
||||
}
|
||||
x -= len;
|
||||
}
|
||||
|
||||
//| @overload
|
||||
//| def sosfilt(sos: ulab.array, x: ulab.array) -> ulab.array: ...
|
||||
//| @overload
|
||||
//| def sosfilt(sos: ulab.array, x: ulab.array, *, xi: ulab.array) -> Tuple[ulab.array, ulab.array]:
|
||||
//| """
|
||||
//| :param ulab.array sos: Array of second-order filter coefficients, must have shape (n_sections, 6). Each row corresponds to a second-order section, with the first three columns providing the numerator coefficients and the last three providing the denominator coefficients.
|
||||
//| :param ulab.array x: The data to be filtered
|
||||
//| :param ulab.array zi: Optional initial conditions for the filter
|
||||
//| :return: If ``xi`` is not specified, the filter result alone is returned. If ``xi`` is specified, the return value is a 2-tuple of the filter result and the final filter conditions.
|
||||
//|
|
||||
//| Filter data along one dimension using cascaded second-order sections.
|
||||
//|
|
||||
//| Filter a data sequence, x, using a digital IIR filter defined by sos.
|
||||
//|
|
||||
//| The filter function is implemented as a series of second-order filters with direct-form II transposed structure. It is designed to minimize numerical precision errors for high-order filters.
|
||||
//|
|
||||
//| Filter coefficients can be generated by using scipy's filter generators such as ``signal.ellip(..., output='sos')``."""
|
||||
//| ...
|
||||
//|
|
||||
static mp_obj_t filter_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_sos, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_zi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
|
||||
mp_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(!ndarray_object_is_nditerable(args[0].u_obj) || !ndarray_object_is_nditerable(args[1].u_obj)) {
|
||||
mp_raise_TypeError(translate("sosfilt requires iterable arguments"));
|
||||
}
|
||||
size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj));
|
||||
ndarray_obj_t *y = create_new_ndarray(1, lenx, NDARRAY_FLOAT);
|
||||
mp_float_t *yarray = (mp_float_t *)y->array->items;
|
||||
mp_float_t coeffs[6];
|
||||
if(MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *inarray = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
for(size_t i=0; i < lenx; i++) {
|
||||
*yarray++ = ndarray_get_float_value(inarray->array->items, inarray->array->typecode, i);
|
||||
}
|
||||
yarray -= lenx;
|
||||
} else {
|
||||
fill_array_iterable(yarray, args[1].u_obj);
|
||||
}
|
||||
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(args[0].u_obj, &iter_buf);
|
||||
size_t lensos = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0].u_obj));
|
||||
|
||||
ndarray_obj_t *zf = create_new_ndarray(lensos, 2, NDARRAY_FLOAT);
|
||||
mp_float_t *zf_array = (mp_float_t *)zf->array->items;
|
||||
|
||||
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"));
|
||||
} else {
|
||||
ndarray_obj_t *zi = MP_OBJ_TO_PTR(args[2].u_obj);
|
||||
if((zi->m != lensos) || (zi->n != 2)) {
|
||||
mp_raise_ValueError(translate("zi must be of shape (n_section, 2)"));
|
||||
}
|
||||
if(zi->array->typecode != NDARRAY_FLOAT) {
|
||||
mp_raise_ValueError(translate("zi must be of float type"));
|
||||
}
|
||||
memcpy(zf_array, zi->array->items, 2*lensos*sizeof(mp_float_t));
|
||||
}
|
||||
}
|
||||
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)"));
|
||||
} else {
|
||||
fill_array_iterable(coeffs, item);
|
||||
if(coeffs[3] != MICROPY_FLOAT_CONST(1.0)) {
|
||||
mp_raise_ValueError(translate("sos[:, 3] should be all ones"));
|
||||
}
|
||||
filter_sosfilt_array(yarray, coeffs, zf_array, lenx);
|
||||
zf_array += 2;
|
||||
}
|
||||
}
|
||||
if(args[2].u_obj == mp_const_none) {
|
||||
return MP_OBJ_FROM_PTR(y);
|
||||
} else {
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(y);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(zf);
|
||||
return tuple;
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(filter_sosfilt_obj, 2, filter_sosfilt);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_filter_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_filter) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_convolve), (mp_obj_t)&filter_convolve_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sosfilt), (mp_obj_t)&filter_sosfilt_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_filter_globals, ulab_filter_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_filter_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_filter_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -7,19 +7,21 @@
|
|||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _FILTER_
|
||||
#define _FILTER_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_FILTER_MODULE
|
||||
|
||||
extern mp_obj_module_t ulab_filter_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(filter_convolve_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(filter_sosfilt_obj);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
448
code/linalg.c
448
code/linalg.c
|
|
@ -1,448 +0,0 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "linalg.h"
|
||||
|
||||
#if ULAB_LINALG_MODULE
|
||||
|
||||
mp_obj_t linalg_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, 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("size is defined for ndarrays only"));
|
||||
} else {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(args[1].u_obj == mp_const_none) {
|
||||
return mp_obj_new_int(ndarray->array->len);
|
||||
} else if(MP_OBJ_IS_INT(args[1].u_obj)) {
|
||||
uint8_t ax = mp_obj_get_int(args[1].u_obj);
|
||||
if(ax == 0) {
|
||||
if(ndarray->m == 1) {
|
||||
return mp_obj_new_int(ndarray->n);
|
||||
} else {
|
||||
return mp_obj_new_int(ndarray->m);
|
||||
}
|
||||
} else if(ax == 1) {
|
||||
if(ndarray->m == 1) {
|
||||
mp_raise_ValueError(translate("tuple index out of range"));
|
||||
} else {
|
||||
return mp_obj_new_int(ndarray->n);
|
||||
}
|
||||
} else {
|
||||
mp_raise_ValueError(translate("tuple index out of range"));
|
||||
}
|
||||
} else {
|
||||
mp_raise_TypeError(translate("wrong argument type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_size_obj, 1, linalg_size);
|
||||
|
||||
bool linalg_invert_matrix(mp_float_t *data, size_t N) {
|
||||
// returns true, of the inversion was successful,
|
||||
// false, if the matrix is singular
|
||||
|
||||
// initially, this is the unit matrix: the contents of this matrix is what
|
||||
// will be returned after all the transformations
|
||||
mp_float_t *unit = m_new(mp_float_t, N*N);
|
||||
|
||||
mp_float_t elem = 1.0;
|
||||
// initialise the unit matrix
|
||||
memset(unit, 0, sizeof(mp_float_t)*N*N);
|
||||
for(size_t m=0; m < N; m++) {
|
||||
memcpy(&unit[m*(N+1)], &elem, sizeof(mp_float_t));
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
// this could be faster with ((c < epsilon) && (c > -epsilon))
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(data[m*(N+1)]) < epsilon) {
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return false;
|
||||
}
|
||||
for(size_t n=0; n < N; n++){
|
||||
if(m != n){
|
||||
elem = data[N*n+m] / data[m*(N+1)];
|
||||
for(size_t k=0; k < N; k++){
|
||||
data[N*n+k] -= elem * data[N*m+k];
|
||||
unit[N*n+k] -= elem * unit[N*m+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
elem = data[m*(N+1)];
|
||||
for(size_t n=0; n < N; n++){
|
||||
data[N*m+n] /= elem;
|
||||
unit[N*m+n] /= elem;
|
||||
}
|
||||
}
|
||||
memcpy(data, unit, sizeof(mp_float_t)*N*N);
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return true;
|
||||
}
|
||||
|
||||
mp_obj_t linalg_inv(mp_obj_t o_in) {
|
||||
// since inv is not a class method, we have to inspect the input argument first
|
||||
if(!MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("only ndarrays can be inverted"));
|
||||
}
|
||||
ndarray_obj_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
if(!MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("only ndarray objects can be inverted"));
|
||||
}
|
||||
if(o->m != o->n) {
|
||||
mp_raise_ValueError(translate("only square matrices can be inverted"));
|
||||
}
|
||||
ndarray_obj_t *inverted = create_new_ndarray(o->m, o->n, NDARRAY_FLOAT);
|
||||
mp_float_t *data = (mp_float_t *)inverted->array->items;
|
||||
mp_obj_t elem;
|
||||
for(size_t m=0; m < o->m; m++) { // rows first
|
||||
for(size_t n=0; n < o->n; n++) { // columns next
|
||||
// this could, perhaps, be done in single line...
|
||||
// On the other hand, we probably spend little time here
|
||||
elem = mp_binary_get_val_array(o->array->typecode, o->array->items, m*o->n+n);
|
||||
data[m*o->n+n] = (mp_float_t)mp_obj_get_float(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if(!linalg_invert_matrix(data, o->m)) {
|
||||
// TODO: I am not sure this is needed here. Otherwise,
|
||||
// how should we free up the unused RAM of inverted?
|
||||
m_del(mp_float_t, inverted->array->items, o->n*o->n);
|
||||
mp_raise_ValueError(translate("input matrix is singular"));
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(inverted);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
|
||||
|
||||
mp_obj_t linalg_dot(mp_obj_t _m1, mp_obj_t _m2) {
|
||||
// TODO: should the results be upcast?
|
||||
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"));
|
||||
}
|
||||
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
|
||||
ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
|
||||
if(m1->n != m2->m) {
|
||||
mp_raise_ValueError(translate("matrix dimensions do not match"));
|
||||
}
|
||||
// TODO: numpy uses upcasting here
|
||||
ndarray_obj_t *out = create_new_ndarray(m1->m, m2->n, NDARRAY_FLOAT);
|
||||
mp_float_t *outdata = (mp_float_t *)out->array->items;
|
||||
mp_float_t sum, v1, v2;
|
||||
for(size_t i=0; i < m1->m; i++) { // rows of m1
|
||||
for(size_t j=0; j < m2->n; j++) { // columns of m2
|
||||
sum = 0.0;
|
||||
for(size_t k=0; k < m2->m; k++) {
|
||||
// (i, k) * (k, j)
|
||||
v1 = ndarray_get_float_value(m1->array->items, m1->array->typecode, i*m1->n+k);
|
||||
v2 = ndarray_get_float_value(m2->array->items, m2->array->typecode, k*m2->n+j);
|
||||
sum += v1 * v2;
|
||||
}
|
||||
outdata[j*m1->m+i] = sum;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(linalg_dot_obj, linalg_dot);
|
||||
|
||||
mp_obj_t linalg_zeros_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t kind) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} } ,
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
uint8_t dtype = args[1].u_int;
|
||||
if(!MP_OBJ_IS_INT(args[0].u_obj) && !MP_OBJ_IS_TYPE(args[0].u_obj, &mp_type_tuple)) {
|
||||
mp_raise_TypeError(translate("input argument must be an integer or a 2-tuple"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
if(MP_OBJ_IS_INT(args[0].u_obj)) {
|
||||
size_t n = mp_obj_get_int(args[0].u_obj);
|
||||
ndarray = create_new_ndarray(1, n, dtype);
|
||||
} else if(MP_OBJ_IS_TYPE(args[0].u_obj, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(tuple->len != 2) {
|
||||
mp_raise_TypeError(translate("input argument must be an integer or a 2-tuple"));
|
||||
}
|
||||
ndarray = create_new_ndarray(mp_obj_get_int(tuple->items[0]),
|
||||
mp_obj_get_int(tuple->items[1]), dtype);
|
||||
}
|
||||
if(kind == 1) {
|
||||
mp_obj_t one = mp_obj_new_int(1);
|
||||
for(size_t i=0; i < ndarray->array->len; i++) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, i, one);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_zeros(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return linalg_zeros_ones(n_args, pos_args, kw_args, 0);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_zeros_obj, 0, linalg_zeros);
|
||||
|
||||
mp_obj_t linalg_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return linalg_zeros_ones(n_args, pos_args, kw_args, 1);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_ones_obj, 0, linalg_ones);
|
||||
|
||||
mp_obj_t linalg_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
size_t n = args[0].u_int, m;
|
||||
int16_t k = args[2].u_int;
|
||||
uint8_t dtype = args[3].u_int;
|
||||
if(args[1].u_rom_obj == mp_const_none) {
|
||||
m = n;
|
||||
} else {
|
||||
m = mp_obj_get_int(args[1].u_rom_obj);
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(m, n, dtype);
|
||||
mp_obj_t one = mp_obj_new_int(1);
|
||||
size_t i = 0;
|
||||
if((k >= 0) && (k < n)) {
|
||||
while(k < n) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, i*n+k, one);
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
} else if((k < 0) && (-k < m)) {
|
||||
k = -k;
|
||||
i = 0;
|
||||
while(k < m) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, k*n+i, one);
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_eye_obj, 0, linalg_eye);
|
||||
|
||||
mp_obj_t linalg_det(mp_obj_t oin) {
|
||||
if(!MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("function defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
if(in->m != in->n) {
|
||||
mp_raise_ValueError(translate("input must be square matrix"));
|
||||
}
|
||||
|
||||
mp_float_t *tmp = m_new(mp_float_t, in->n*in->n);
|
||||
for(size_t i=0; i < in->array->len; i++){
|
||||
tmp[i] = ndarray_get_float_value(in->array->items, in->array->typecode, i);
|
||||
}
|
||||
mp_float_t c;
|
||||
for(size_t m=0; m < in->m-1; m++){
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m*(in->n+1)]) < epsilon) {
|
||||
m_del(mp_float_t, tmp, in->n*in->n);
|
||||
return mp_obj_new_float(0.0);
|
||||
}
|
||||
for(size_t n=0; n < in->n; n++){
|
||||
if(m != n) {
|
||||
c = tmp[in->n*n+m] / tmp[m*(in->n+1)];
|
||||
for(size_t k=0; k < in->n; k++){
|
||||
tmp[in->n*n+k] -= c * tmp[in->n*m+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mp_float_t det = 1.0;
|
||||
|
||||
for(size_t m=0; m < in->m; m++){
|
||||
det *= tmp[m*(in->n+1)];
|
||||
}
|
||||
m_del(mp_float_t, tmp, in->n*in->n);
|
||||
return mp_obj_new_float(det);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
|
||||
|
||||
mp_obj_t linalg_eig(mp_obj_t oin) {
|
||||
if(!MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("function defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
if(in->m != in->n) {
|
||||
mp_raise_ValueError(translate("input must be square matrix"));
|
||||
}
|
||||
mp_float_t *array = m_new(mp_float_t, in->array->len);
|
||||
for(size_t i=0; i < in->array->len; i++) {
|
||||
array[i] = ndarray_get_float_value(in->array->items, in->array->typecode, i);
|
||||
}
|
||||
// make sure the matrix is symmetric
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
for(size_t n=m+1; n < in->n; n++) {
|
||||
// compare entry (m, n) to (n, m)
|
||||
// TODO: this must probably be scaled!
|
||||
if(epsilon < MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n] - array[n*in->n + m])) {
|
||||
mp_raise_ValueError(translate("input matrix is asymmetric"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got this far, then the matrix will be symmetric
|
||||
|
||||
ndarray_obj_t *eigenvectors = create_new_ndarray(in->m, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array->items;
|
||||
// start out with the unit matrix
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
eigvectors[m*(in->n+1)] = 1.0;
|
||||
}
|
||||
mp_float_t largest, w, t, c, s, tau, aMk, aNk, vm, vn;
|
||||
size_t M, N;
|
||||
size_t iterations = JACOBI_MAX*in->n*in->n;
|
||||
do {
|
||||
iterations--;
|
||||
// find the pivot here
|
||||
M = 0;
|
||||
N = 0;
|
||||
largest = 0.0;
|
||||
for(size_t m=0; m < in->m-1; m++) { // -1: no need to inspect last row
|
||||
for(size_t n=m+1; n < in->n; n++) {
|
||||
w = MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n]);
|
||||
if((largest < w) && (epsilon < w)) {
|
||||
M = m;
|
||||
N = n;
|
||||
largest = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(M+N == 0) { // all entries are smaller than epsilon, there is not much we can do...
|
||||
break;
|
||||
}
|
||||
// at this point, we have the pivot, and it is the entry (M, N)
|
||||
// now we have to find the rotation angle
|
||||
w = (array[N*in->n + N] - array[M*in->n + M]) / (2.0*array[M*in->n + N]);
|
||||
// The following if/else chooses the smaller absolute value for the tangent
|
||||
// of the rotation angle. Going with the smaller should be numerically stabler.
|
||||
if(w > 0) {
|
||||
t = MICROPY_FLOAT_C_FUN(sqrt)(w*w + 1.0) - w;
|
||||
} else {
|
||||
t = -1.0*(MICROPY_FLOAT_C_FUN(sqrt)(w*w + 1.0) + w);
|
||||
}
|
||||
s = t / MICROPY_FLOAT_C_FUN(sqrt)(t*t + 1.0); // the sine of the rotation angle
|
||||
c = 1.0 / MICROPY_FLOAT_C_FUN(sqrt)(t*t + 1.0); // the cosine of the rotation angle
|
||||
tau = (1.0-c)/s; // this is equal to the tangent of the half of the rotation angle
|
||||
|
||||
// at this point, we have the rotation angles, so we can transform the matrix
|
||||
// first the two diagonal elements
|
||||
// a(M, M) = a(M, M) - t*a(M, N)
|
||||
array[M*in->n + M] = array[M*in->n + M] - t * array[M*in->n + N];
|
||||
// a(N, N) = a(N, N) + t*a(M, N)
|
||||
array[N*in->n + N] = array[N*in->n + N] + t * array[M*in->n + N];
|
||||
// after the rotation, the a(M, N), and a(N, M) entries should become zero
|
||||
array[M*in->n + N] = array[N*in->n + M] = 0.0;
|
||||
// then all other elements in the column
|
||||
for(size_t k=0; k < in->m; k++) {
|
||||
if((k == M) || (k == N)) {
|
||||
continue;
|
||||
}
|
||||
aMk = array[M*in->n + k];
|
||||
aNk = array[N*in->n + k];
|
||||
// a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k))
|
||||
array[M*in->n + k] -= s*(aNk + tau*aMk);
|
||||
// a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k))
|
||||
array[N*in->n + k] += s*(aMk - tau*aNk);
|
||||
// a(k, M) = a(M, k)
|
||||
array[k*in->n + M] = array[M*in->n + k];
|
||||
// a(k, N) = a(N, k)
|
||||
array[k*in->n + N] = array[N*in->n + k];
|
||||
}
|
||||
// now we have to update the eigenvectors
|
||||
// the rotation matrix, R, multiplies from the right
|
||||
// R is the unit matrix, except for the
|
||||
// R(M,M) = R(N, N) = c
|
||||
// R(N, M) = s
|
||||
// (M, N) = -s
|
||||
// entries. This means that only the Mth, and Nth columns will change
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
vm = eigvectors[m*in->n+M];
|
||||
vn = eigvectors[m*in->n+N];
|
||||
// the new value of eigvectors(m, M)
|
||||
eigvectors[m*in->n+M] = c * vm - s * vn;
|
||||
// the new value of eigvectors(m, N)
|
||||
eigvectors[m*in->n+N] = s * vm + c * vn;
|
||||
}
|
||||
} while(iterations > 0);
|
||||
|
||||
if(iterations == 0) {
|
||||
// the computation did not converge; numpy raises LinAlgError
|
||||
m_del(mp_float_t, array, in->array->len);
|
||||
mp_raise_ValueError(translate("iterations did not converge"));
|
||||
}
|
||||
ndarray_obj_t *eigenvalues = create_new_ndarray(1, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array->items;
|
||||
for(size_t i=0; i < in->n; i++) {
|
||||
eigvalues[i] = array[i*(in->n+1)];
|
||||
}
|
||||
m_del(mp_float_t, array, in->array->len);
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors);
|
||||
return tuple;
|
||||
return MP_OBJ_FROM_PTR(eigenvalues);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_size), (mp_obj_t)&linalg_size_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_inv), (mp_obj_t)&linalg_inv_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_dot), (mp_obj_t)&linalg_dot_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_zeros), (mp_obj_t)&linalg_zeros_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ones), (mp_obj_t)&linalg_ones_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_eye), (mp_obj_t)&linalg_eye_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_det), (mp_obj_t)&linalg_det_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_eig), (mp_obj_t)&linalg_eig_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_linalg_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _LINALG_
|
||||
#define _LINALG_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define epsilon 1.2e-7
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define epsilon 2.3e-16
|
||||
#endif
|
||||
|
||||
#define JACOBI_MAX 20
|
||||
|
||||
#if ULAB_LINALG_MODULE || ULAB_POLY_MODULE
|
||||
bool linalg_invert_matrix(mp_float_t *, size_t );
|
||||
#endif
|
||||
|
||||
#if ULAB_LINALG_MODULE
|
||||
|
||||
extern mp_obj_module_t ulab_linalg_module;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
547
code/linalg/linalg.c
Normal file
547
code/linalg/linalg.c
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Roberto Colistete Jr.
|
||||
* 2020 Deqing Sun
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "linalg.h"
|
||||
|
||||
#if ULAB_LINALG_MODULE
|
||||
|
||||
//| """Linear algebra functions"""
|
||||
//|
|
||||
|
||||
static ndarray_obj_t *linalg_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"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj);
|
||||
if(ndarray->m != ndarray->n) {
|
||||
mp_raise_ValueError(translate("input must be square matrix"));
|
||||
}
|
||||
return ndarray;
|
||||
}
|
||||
|
||||
bool linalg_invert_matrix(mp_float_t *data, size_t N) {
|
||||
// returns true, of the inversion was successful,
|
||||
// false, if the matrix is singular
|
||||
|
||||
// initially, this is the unit matrix: the contents of this matrix is what
|
||||
// will be returned after all the transformations
|
||||
mp_float_t *unit = m_new(mp_float_t, N*N);
|
||||
|
||||
mp_float_t elem = MICROPY_FLOAT_CONST(1.0);
|
||||
// initialise the unit matrix
|
||||
memset(unit, 0, sizeof(mp_float_t)*N*N);
|
||||
for(size_t m=0; m < N; m++) {
|
||||
memcpy(&unit[m*(N+1)], &elem, sizeof(mp_float_t));
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
// this could be faster with ((c < epsilon) && (c > -epsilon))
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(data[m*(N+1)]) < epsilon) {
|
||||
//look for a line to swap
|
||||
size_t m1=m+1;
|
||||
for(; m1 < N; m1++){
|
||||
if ( !(MICROPY_FLOAT_C_FUN(fabs)(data[m1*N+m]) < epsilon) ){
|
||||
for(size_t m2=0; m2 < N; m2++){
|
||||
mp_float_t swapVal = data[m*N+m2];
|
||||
data[m*N+m2] = data[m1*N+m2];
|
||||
data[m1*N+m2] = swapVal;
|
||||
swapVal = unit[m*N+m2];
|
||||
unit[m*N+m2] = unit[m1*N+m2];
|
||||
unit[m1*N+m2] = swapVal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m1 >= N){
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for(size_t n=0; n < N; n++){
|
||||
if(m != n){
|
||||
elem = data[N*n+m] / data[m*(N+1)];
|
||||
for(size_t k=0; k < N; k++){
|
||||
data[N*n+k] -= elem * data[N*m+k];
|
||||
unit[N*n+k] -= elem * unit[N*m+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
elem = data[m*(N+1)];
|
||||
for(size_t n=0; n < N; n++){
|
||||
data[N*m+n] /= elem;
|
||||
unit[N*m+n] /= elem;
|
||||
}
|
||||
}
|
||||
memcpy(data, unit, sizeof(mp_float_t)*N*N);
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return true;
|
||||
}
|
||||
|
||||
//| def cholesky(A: ulab.array) -> ulab.array:
|
||||
//| """
|
||||
//| :param ~ulab.array A: a positive definite, symmetric square matrix
|
||||
//| :return ~ulab.array L: a square root matrix in the lower triangular form
|
||||
//| :raises ValueError: If the input does not fulfill the necessary conditions
|
||||
//|
|
||||
//| The returned matrix satisfies the equation m=LL*"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_cholesky(mp_obj_t oin) {
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
ndarray_obj_t *L = create_new_ndarray(in->n, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *array = (mp_float_t *)L->array->items;
|
||||
|
||||
size_t pos = 0;
|
||||
for(size_t m=0; m < in->m; m++) { // rows
|
||||
for(size_t n=0; n < in->n; n++) { // columns
|
||||
array[m*in->m+n] = ndarray_get_float_value(in->array->items, in->array->typecode, pos++);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the matrix is symmetric
|
||||
for(size_t m=0; m < in->m; m++) { // rows
|
||||
for(size_t n=m+1; n < in->n; n++) { // columns
|
||||
// compare entry (m, n) to (n, m)
|
||||
if(epsilon < MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n] - array[n*in->n + m])) {
|
||||
mp_raise_ValueError(translate("input matrix is asymmetric"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is actually not needed, but Cholesky in numpy returns the lower triangular matrix
|
||||
for(size_t i=0; i < in->m; i++) { // rows
|
||||
for(size_t j=i+1; j < in->n; j++) { // columns
|
||||
array[i*in->m + j] = MICROPY_FLOAT_CONST(0.0);
|
||||
}
|
||||
}
|
||||
mp_float_t sum = MICROPY_FLOAT_CONST(0.0);
|
||||
for(size_t i=0; i < in->m; i++) { // rows
|
||||
for(size_t j=0; j <= i; j++) { // columns
|
||||
sum = array[i*in->m + j];
|
||||
for(size_t k=0; k < j; k++) {
|
||||
sum -= array[i*in->n + k] * array[j*in->n + k];
|
||||
}
|
||||
if(i == j) {
|
||||
if(sum <= MICROPY_FLOAT_CONST(0.0)) {
|
||||
mp_raise_ValueError(translate("matrix is not positive definite"));
|
||||
} else {
|
||||
array[i*in->m+i] = MICROPY_FLOAT_C_FUN(sqrt)(sum);
|
||||
}
|
||||
} else {
|
||||
array[i*in->m + j] = sum / array[j*in->m+j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(L);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky);
|
||||
|
||||
//| def det(m: ulab.array) -> float:
|
||||
//| """
|
||||
//| :param: m, a square matrix
|
||||
//| :return float: The determinant of the matrix
|
||||
//|
|
||||
//| Computes the eigenvalues and eigenvectors of a square matrix"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_det(mp_obj_t oin) {
|
||||
ndarray_obj_t *in = linalg_object_is_square(oin);
|
||||
mp_float_t *tmp = m_new(mp_float_t, in->n*in->n);
|
||||
for(size_t i=0; i < in->array->len; i++){
|
||||
tmp[i] = ndarray_get_float_value(in->array->items, in->array->typecode, i);
|
||||
}
|
||||
mp_float_t c;
|
||||
mp_float_t det_sign = MICROPY_FLOAT_CONST(1.0);
|
||||
|
||||
for(size_t m=0; m < in->m-1; m++){
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m*(in->n+1)]) < epsilon) {
|
||||
//look for a line to swap
|
||||
size_t m1=m+1;
|
||||
for(; m1 < in->m; m1++){
|
||||
if ( !(MICROPY_FLOAT_C_FUN(fabs)(tmp[m1*(in->n)+m]) < epsilon) ){
|
||||
for(size_t m2=0; m2 < in->n; m2++){
|
||||
mp_float_t swapVal = tmp[m*(in->n)+m2];
|
||||
tmp[m*(in->n)+m2] = tmp[m1*(in->n)+m2];
|
||||
tmp[m1*(in->n)+m2] = swapVal;
|
||||
}
|
||||
det_sign = -det_sign;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m1 >= in->m){
|
||||
m_del(mp_float_t, tmp, in->n*in->n);
|
||||
return mp_obj_new_float(MICROPY_FLOAT_CONST(0.0));
|
||||
}
|
||||
}
|
||||
for(size_t n=0; n < in->n; n++){
|
||||
if(m != n) {
|
||||
c = tmp[in->n*n+m] / tmp[m*(in->n+1)];
|
||||
for(size_t k=0; k < in->n; k++){
|
||||
tmp[in->n*n+k] -= c * tmp[in->n*m+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mp_float_t det = det_sign;
|
||||
|
||||
for(size_t m=0; m < in->m; m++){
|
||||
det *= tmp[m*(in->n+1)];
|
||||
}
|
||||
m_del(mp_float_t, tmp, in->n*in->n);
|
||||
return mp_obj_new_float(det);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
|
||||
|
||||
//| def dot(m1: ulab.array, m2: ulab.array) -> Union[ulab.array, float]:
|
||||
//| """
|
||||
//| :param ~ulab.array m1: a matrix, or a vector
|
||||
//| :param ~ulab.array m2: a matrix, or a vector
|
||||
//|
|
||||
//| Computes the product of two matrices, or two vectors. In the letter case, the inner product is returned."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_dot(mp_obj_t _m1, mp_obj_t _m2) {
|
||||
// TODO: should the results be upcast?
|
||||
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"));
|
||||
}
|
||||
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
|
||||
ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
|
||||
|
||||
if ((m1->m == 1) && (m2->m == 1)) {
|
||||
// 2 vectors
|
||||
if (m1->array->len != m2->array->len) {
|
||||
mp_raise_ValueError(translate("vectors must have same lengths"));
|
||||
}
|
||||
mp_float_t dot = MICROPY_FLOAT_CONST(0.0);
|
||||
for (size_t pos=0; pos < m1->array->len; pos++) {
|
||||
dot += ndarray_get_float_value(m1->array->items, m1->array->typecode, pos)*ndarray_get_float_value(m2->array->items, m2->array->typecode, pos);
|
||||
}
|
||||
return mp_obj_new_float(dot);
|
||||
} else {
|
||||
// 2 matrices
|
||||
if(m1->n != m2->m) {
|
||||
mp_raise_ValueError(translate("matrix dimensions do not match"));
|
||||
}
|
||||
// TODO: numpy uses upcasting here
|
||||
ndarray_obj_t *out = create_new_ndarray(m1->m, m2->n, NDARRAY_FLOAT);
|
||||
mp_float_t *outdata = (mp_float_t *)out->array->items;
|
||||
for(size_t i=0; i < m1->m; i++) { // rows of m1
|
||||
for(size_t j=0; j < m2->n; j++) { // columns of m2
|
||||
mp_float_t sum = MICROPY_FLOAT_CONST(0.0), v1, v2;
|
||||
for(size_t k=0; k < m2->m; k++) {
|
||||
// (i, k) * (k, j)
|
||||
v1 = ndarray_get_float_value(m1->array->items, m1->array->typecode, i*m1->n+k);
|
||||
v2 = ndarray_get_float_value(m2->array->items, m2->array->typecode, k*m2->n+j);
|
||||
sum += v1 * v2;
|
||||
}
|
||||
outdata[i*m2->n+j] = sum;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(linalg_dot_obj, linalg_dot);
|
||||
|
||||
//| def eig(m: ulab.array) -> Tuple[ulab.array, ulab.array]:
|
||||
//| """
|
||||
//| :param m: a square matrix
|
||||
//| :return tuple (eigenvectors, eigenvalues):
|
||||
//|
|
||||
//| Computes the eigenvalues and eigenvectors of a square matrix"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_eig(mp_obj_t oin) {
|
||||
ndarray_obj_t *in = linalg_object_is_square(oin);
|
||||
mp_float_t *array = m_new(mp_float_t, in->array->len);
|
||||
for(size_t i=0; i < in->array->len; i++) {
|
||||
array[i] = ndarray_get_float_value(in->array->items, in->array->typecode, i);
|
||||
}
|
||||
// make sure the matrix is symmetric
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
for(size_t n=m+1; n < in->n; n++) {
|
||||
// compare entry (m, n) to (n, m)
|
||||
// TODO: this must probably be scaled!
|
||||
if(epsilon < MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n] - array[n*in->n + m])) {
|
||||
mp_raise_ValueError(translate("input matrix is asymmetric"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got this far, then the matrix will be symmetric
|
||||
|
||||
ndarray_obj_t *eigenvectors = create_new_ndarray(in->m, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array->items;
|
||||
// start out with the unit matrix
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
eigvectors[m*(in->n+1)] = MICROPY_FLOAT_CONST(1.0);
|
||||
}
|
||||
mp_float_t largest, w, t, c, s, tau, aMk, aNk, vm, vn;
|
||||
size_t M, N;
|
||||
size_t iterations = JACOBI_MAX*in->n*in->n;
|
||||
do {
|
||||
iterations--;
|
||||
// find the pivot here
|
||||
M = 0;
|
||||
N = 0;
|
||||
largest = MICROPY_FLOAT_CONST(0.0);
|
||||
for(size_t m=0; m < in->m-1; m++) { // -1: no need to inspect last row
|
||||
for(size_t n=m+1; n < in->n; n++) {
|
||||
w = MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n]);
|
||||
if((largest < w) && (epsilon < w)) {
|
||||
M = m;
|
||||
N = n;
|
||||
largest = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(M+N == 0) { // all entries are smaller than epsilon, there is not much we can do...
|
||||
break;
|
||||
}
|
||||
// at this point, we have the pivot, and it is the entry (M, N)
|
||||
// now we have to find the rotation angle
|
||||
w = (array[N*in->n + N] - array[M*in->n + M]) / (MICROPY_FLOAT_CONST(2.0)*array[M*in->n + N]);
|
||||
// The following if/else chooses the smaller absolute value for the tangent
|
||||
// of the rotation angle. Going with the smaller should be numerically stabler.
|
||||
if(w > 0) {
|
||||
t = MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) - w;
|
||||
} else {
|
||||
t = MICROPY_FLOAT_CONST(-1.0)*(MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) + w);
|
||||
}
|
||||
s = t / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the sine of the rotation angle
|
||||
c = MICROPY_FLOAT_CONST(1.0) / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the cosine of the rotation angle
|
||||
tau = (MICROPY_FLOAT_CONST(1.0)-c)/s; // this is equal to the tangent of the half of the rotation angle
|
||||
|
||||
// at this point, we have the rotation angles, so we can transform the matrix
|
||||
// first the two diagonal elements
|
||||
// a(M, M) = a(M, M) - t*a(M, N)
|
||||
array[M*in->n + M] = array[M*in->n + M] - t * array[M*in->n + N];
|
||||
// a(N, N) = a(N, N) + t*a(M, N)
|
||||
array[N*in->n + N] = array[N*in->n + N] + t * array[M*in->n + N];
|
||||
// after the rotation, the a(M, N), and a(N, M) entries should become zero
|
||||
array[M*in->n + N] = array[N*in->n + M] = MICROPY_FLOAT_CONST(0.0);
|
||||
// then all other elements in the column
|
||||
for(size_t k=0; k < in->m; k++) {
|
||||
if((k == M) || (k == N)) {
|
||||
continue;
|
||||
}
|
||||
aMk = array[M*in->n + k];
|
||||
aNk = array[N*in->n + k];
|
||||
// a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k))
|
||||
array[M*in->n + k] -= s*(aNk + tau*aMk);
|
||||
// a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k))
|
||||
array[N*in->n + k] += s*(aMk - tau*aNk);
|
||||
// a(k, M) = a(M, k)
|
||||
array[k*in->n + M] = array[M*in->n + k];
|
||||
// a(k, N) = a(N, k)
|
||||
array[k*in->n + N] = array[N*in->n + k];
|
||||
}
|
||||
// now we have to update the eigenvectors
|
||||
// the rotation matrix, R, multiplies from the right
|
||||
// R is the unit matrix, except for the
|
||||
// R(M,M) = R(N, N) = c
|
||||
// R(N, M) = s
|
||||
// (M, N) = -s
|
||||
// entries. This means that only the Mth, and Nth columns will change
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
vm = eigvectors[m*in->n+M];
|
||||
vn = eigvectors[m*in->n+N];
|
||||
// the new value of eigvectors(m, M)
|
||||
eigvectors[m*in->n+M] = c * vm - s * vn;
|
||||
// the new value of eigvectors(m, N)
|
||||
eigvectors[m*in->n+N] = s * vm + c * vn;
|
||||
}
|
||||
} while(iterations > 0);
|
||||
|
||||
if(iterations == 0) {
|
||||
// the computation did not converge; numpy raises LinAlgError
|
||||
m_del(mp_float_t, array, in->array->len);
|
||||
mp_raise_ValueError(translate("iterations did not converge"));
|
||||
}
|
||||
ndarray_obj_t *eigenvalues = create_new_ndarray(1, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array->items;
|
||||
for(size_t i=0; i < in->n; i++) {
|
||||
eigvalues[i] = array[i*(in->n+1)];
|
||||
}
|
||||
m_del(mp_float_t, array, in->array->len);
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors);
|
||||
return tuple;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
|
||||
|
||||
//| def inv(m: ulab.array) -> ulab.array:
|
||||
//| """
|
||||
//| :param ~ulab.array m: a square matrix
|
||||
//| :return: The inverse of the matrix, if it exists
|
||||
//| :raises ValueError: if the matrix is not invertible
|
||||
//|
|
||||
//| Computes the inverse of a square matrix"""
|
||||
//| ...
|
||||
//|
|
||||
static mp_obj_t linalg_inv(mp_obj_t o_in) {
|
||||
ndarray_obj_t *o = linalg_object_is_square(o_in);
|
||||
ndarray_obj_t *inverted = create_new_ndarray(o->m, o->n, NDARRAY_FLOAT);
|
||||
mp_float_t *data = (mp_float_t *)inverted->array->items;
|
||||
mp_obj_t elem;
|
||||
for(size_t m=0; m < o->m; m++) { // rows first
|
||||
for(size_t n=0; n < o->n; n++) { // columns next
|
||||
// this could, perhaps, be done in single line...
|
||||
// On the other hand, we probably spend little time here
|
||||
elem = mp_binary_get_val_array(o->array->typecode, o->array->items, m*o->n+n);
|
||||
data[m*o->n+n] = (mp_float_t)mp_obj_get_float(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if(!linalg_invert_matrix(data, o->m)) {
|
||||
// TODO: I am not sure this is needed here. Otherwise,
|
||||
// how should we free up the unused RAM of inverted?
|
||||
m_del(mp_float_t, inverted->array->items, o->n*o->n);
|
||||
mp_raise_ValueError(translate("input matrix is singular"));
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(inverted);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
|
||||
|
||||
//| def norm(x: ulab.array) -> float:
|
||||
//| """
|
||||
//| :param ~ulab.array x: a vector or a matrix
|
||||
//|
|
||||
//| Computes the 2-norm of a vector or a matrix, i.e., ``sqrt(sum(x*x))``, however, without the RAM overhead."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_norm(mp_obj_t _x) {
|
||||
if (!MP_OBJ_IS_TYPE(_x, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("argument must be ndarray"));
|
||||
}
|
||||
ndarray_obj_t *x = MP_OBJ_TO_PTR(_x);
|
||||
mp_float_t dot = MICROPY_FLOAT_CONST(0.0), v;
|
||||
for (size_t pos=0; pos < x->array->len; pos++) {
|
||||
v = ndarray_get_float_value(x->array->items, x->array->typecode, pos);
|
||||
dot += v*v;
|
||||
}
|
||||
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_norm_obj, linalg_norm);
|
||||
|
||||
//| def size(array: ulab.array) -> int:
|
||||
//| """Return the total number of elements in the array, as an integer."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
|
||||
mp_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("size is defined for ndarrays only"));
|
||||
} else {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(args[1].u_obj == mp_const_none) {
|
||||
return mp_obj_new_int(ndarray->array->len);
|
||||
} else if(MP_OBJ_IS_INT(args[1].u_obj)) {
|
||||
uint8_t ax = mp_obj_get_int(args[1].u_obj);
|
||||
if(ax == 0) {
|
||||
if(ndarray->m == 1) {
|
||||
return mp_obj_new_int(ndarray->n);
|
||||
} else {
|
||||
return mp_obj_new_int(ndarray->m);
|
||||
}
|
||||
} else if(ax == 1) {
|
||||
if(ndarray->m == 1) {
|
||||
mp_raise_ValueError(translate("tuple index out of range"));
|
||||
} else {
|
||||
return mp_obj_new_int(ndarray->n);
|
||||
}
|
||||
} else {
|
||||
mp_raise_ValueError(translate("tuple index out of range"));
|
||||
}
|
||||
} else {
|
||||
mp_raise_TypeError(translate("wrong argument type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_size_obj, 1, linalg_size);
|
||||
|
||||
//| def trace(m: ulab.array) -> float:
|
||||
//| """
|
||||
//| :param m: a square matrix
|
||||
//|
|
||||
//| Compute the trace of the matrix, the sum of its diagonal elements."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_trace(mp_obj_t oin) {
|
||||
ndarray_obj_t *ndarray = linalg_object_is_square(oin);
|
||||
mp_float_t trace = MICROPY_FLOAT_CONST(0.0);
|
||||
for(size_t pos=0; pos < ndarray->array->len; pos+=(ndarray->m+1)) {
|
||||
trace += ndarray_get_float_value(ndarray->array->items, ndarray->array->typecode, pos);
|
||||
}
|
||||
if(ndarray->array->typecode == NDARRAY_FLOAT) {
|
||||
return mp_obj_new_float(trace);
|
||||
}
|
||||
return mp_obj_new_int_from_float(trace);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_trace_obj, linalg_trace);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_cholesky), (mp_obj_t)&linalg_cholesky_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_det), (mp_obj_t)&linalg_det_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_dot), (mp_obj_t)&linalg_dot_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_eig), (mp_obj_t)&linalg_eig_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_inv), (mp_obj_t)&linalg_inv_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_norm), (mp_obj_t)&linalg_norm_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_size), (mp_obj_t)&linalg_size_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_trace), (mp_obj_t)&linalg_trace_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_linalg_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
43
code/linalg/linalg.h
Normal file
43
code/linalg/linalg.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _LINALG_
|
||||
#define _LINALG_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define epsilon MICROPY_FLOAT_CONST(1.2e-7)
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define epsilon MICROPY_FLOAT_CONST(2.3e-16)
|
||||
#endif
|
||||
|
||||
#define JACOBI_MAX 20
|
||||
|
||||
#if ULAB_LINALG_MODULE || ULAB_POLY_MODULE
|
||||
bool linalg_invert_matrix(mp_float_t *, size_t );
|
||||
#endif
|
||||
|
||||
#if ULAB_LINALG_MODULE
|
||||
|
||||
extern mp_obj_module_t ulab_linalg_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_cholesky_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_size_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_inv_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(linalg_dot_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(linalg_norm_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_det_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_eig_obj);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -3,17 +3,18 @@ USERMODULES_DIR := $(USERMOD_DIR)
|
|||
|
||||
# Add all C files to SRC_USERMOD.
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ndarray.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/linalg.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/vectorise.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/poly.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/fft.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numerical.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/filter.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/extras.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ulab_create.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/linalg/linalg.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/vector/vectorise.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/poly/poly.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/fft/fft.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numerical/numerical.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/filter/filter.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/compare/compare.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/approx/approx.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/user/user.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ulab.c
|
||||
|
||||
# We can add our module folder to include paths if needed
|
||||
# This is not actually needed in this example.
|
||||
CFLAGS_USERMOD += -I$(USERMODULES_DIR)
|
||||
|
||||
CFLAGS_EXTRA = -DMODULE_ULAB_ENABLED=1
|
||||
override CFLAGS_EXTRA += -DMODULE_ULAB_ENABLED=1
|
||||
|
|
|
|||
823
code/ndarray.c
823
code/ndarray.c
File diff suppressed because it is too large
Load diff
|
|
@ -17,7 +17,8 @@
|
|||
#include "py/objstr.h"
|
||||
#include "py/objlist.h"
|
||||
|
||||
#define PRINT_MAX 10
|
||||
#define NDARRAY_PRINT_THRESHOLD 10
|
||||
#define NDARRAY_PRINT_EDGEITEMS 3
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define FLOAT_TYPECODE 'f'
|
||||
|
|
@ -25,8 +26,31 @@
|
|||
#define FLOAT_TYPECODE 'd'
|
||||
#endif
|
||||
|
||||
#if !CIRCUITPY
|
||||
// this typedef is lifted from objfloat.c, because mp_obj_float_t is not exposed
|
||||
typedef struct _mp_obj_float_t {
|
||||
mp_obj_base_t base;
|
||||
mp_float_t value;
|
||||
} mp_obj_float_t;
|
||||
|
||||
#ifdef OPENMV
|
||||
#define mp_obj_is_bool(o) (MP_OBJ_IS_TYPE((o), &mp_type_bool))
|
||||
#define translate(x) x
|
||||
|
||||
typedef struct _mp_obj_slice_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t start;
|
||||
mp_obj_t stop;
|
||||
mp_obj_t step;
|
||||
} mp_obj_slice_t;
|
||||
|
||||
void mp_obj_slice_get(mp_obj_t self_in, mp_obj_t *, mp_obj_t *, mp_obj_t *);
|
||||
#else
|
||||
#if CIRCUITPY
|
||||
#define mp_obj_is_bool(o) (MP_OBJ_IS_TYPE((o), &mp_type_bool))
|
||||
#define mp_obj_is_int(x) (MP_OBJ_IS_INT((x)))
|
||||
#else
|
||||
#define translate(x) MP_ERROR_TEXT(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
|
||||
|
|
@ -52,8 +76,13 @@ typedef struct _ndarray_obj_t {
|
|||
mp_obj_t mp_obj_new_ndarray_iterator(mp_obj_t , size_t , mp_obj_iter_buf_t *);
|
||||
|
||||
mp_float_t ndarray_get_float_value(void *, uint8_t , size_t );
|
||||
bool ndarray_object_is_nditerable(mp_obj_t );
|
||||
void fill_array_iterable(mp_float_t *, mp_obj_t );
|
||||
|
||||
mp_obj_t ndarray_set_printoptions(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_set_printoptions_obj);
|
||||
mp_obj_t ndarray_get_printoptions(void);
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(ndarray_get_printoptions_obj);
|
||||
void ndarray_print_row(const mp_print_t *, mp_obj_array_t *, size_t , size_t );
|
||||
void ndarray_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
|
||||
void ndarray_assign_elements(mp_obj_array_t *, mp_obj_t , uint8_t , size_t *);
|
||||
|
|
@ -84,6 +113,7 @@ MP_DECLARE_CONST_FUN_OBJ_1(ndarray_transpose_obj);
|
|||
mp_int_t ndarray_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags);
|
||||
//void ndarray_attributes(mp_obj_t , qstr , mp_obj_t *);
|
||||
|
||||
ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t );
|
||||
|
||||
#define CREATE_SINGLE_ITEM(outarray, type, typecode, value) do {\
|
||||
ndarray_obj_t *tmp = create_new_ndarray(1, 1, (typecode));\
|
||||
|
|
@ -99,45 +129,38 @@ mp_int_t ndarray_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t f
|
|||
should work outside the loop, but it doesn't. Go figure!
|
||||
*/
|
||||
|
||||
#define RUN_BINARY_LOOP(typecode, type_out, type_left, type_right, ol, or, op) do {\
|
||||
#define RUN_BINARY_LOOP(typecode, type_out, type_left, type_right, ol, or, op, m, n, len, linc, rinc) do {\
|
||||
type_left *left = (type_left *)(ol)->array->items;\
|
||||
type_right *right = (type_right *)(or)->array->items;\
|
||||
uint8_t inc = 0;\
|
||||
if((or)->array->len > 1) inc = 1;\
|
||||
if(((op) == MP_BINARY_OP_ADD) || ((op) == MP_BINARY_OP_SUBTRACT) || ((op) == MP_BINARY_OP_MULTIPLY)) {\
|
||||
ndarray_obj_t *out = create_new_ndarray(ol->m, ol->n, typecode);\
|
||||
if(((op) == MP_BINARY_OP_ADD) || ((op) == MP_BINARY_OP_SUBTRACT) || ((op) == MP_BINARY_OP_MULTIPLY) || ((op) == MP_BINARY_OP_POWER)) {\
|
||||
ndarray_obj_t *out = create_new_ndarray((m), (n), (typecode));\
|
||||
type_out *(odata) = (type_out *)out->array->items;\
|
||||
if((op) == MP_BINARY_OP_ADD) { for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = left[i] + right[j];}\
|
||||
if((op) == MP_BINARY_OP_SUBTRACT) { for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = left[i] - right[j];}\
|
||||
if((op) == MP_BINARY_OP_MULTIPLY) { for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = left[i] * right[j];}\
|
||||
if((op) == MP_BINARY_OP_ADD) { for(size_t i=0; i < (len); i++, left+=linc, right+=rinc) *odata++ = *left + *right; }\
|
||||
else if((op) == MP_BINARY_OP_MULTIPLY) { for(size_t i=0; i < (len); i++, left+=linc, right+=rinc) *odata++ = *left * *right; }\
|
||||
else if((op) == MP_BINARY_OP_POWER) { for(size_t i=0; i < (len); i++, left+=linc, right+=rinc) *odata++ = (type_out)MICROPY_FLOAT_C_FUN(pow)(*left, *right); }\
|
||||
else if((op) == MP_BINARY_OP_SUBTRACT) { for(size_t i=0; i < (len); i++, left+=linc, right+=rinc) *odata++ = *left - *right; }\
|
||||
return MP_OBJ_FROM_PTR(out);\
|
||||
} else if((op) == MP_BINARY_OP_TRUE_DIVIDE) {\
|
||||
ndarray_obj_t *out = create_new_ndarray(ol->m, ol->n, NDARRAY_FLOAT);\
|
||||
} else if((op) == MP_BINARY_OP_TRUE_DIVIDE) {\
|
||||
ndarray_obj_t *out = create_new_ndarray((m), (n), NDARRAY_FLOAT);\
|
||||
mp_float_t *odata = (mp_float_t *)out->array->items;\
|
||||
for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = (mp_float_t)left[i]/(mp_float_t)right[j];\
|
||||
for(size_t i=0; i < (len); i++, left+=linc, right+=rinc) {*odata++ = (mp_float_t)(*left)/(mp_float_t)(*right);}\
|
||||
return MP_OBJ_FROM_PTR(out);\
|
||||
} else if(((op) == MP_BINARY_OP_LESS) || ((op) == MP_BINARY_OP_LESS_EQUAL) || \
|
||||
((op) == MP_BINARY_OP_MORE) || ((op) == MP_BINARY_OP_MORE_EQUAL)) {\
|
||||
} else if(((op) == MP_BINARY_OP_LESS) || ((op) == MP_BINARY_OP_LESS_EQUAL) || \
|
||||
((op) == MP_BINARY_OP_MORE) || ((op) == MP_BINARY_OP_MORE_EQUAL) || \
|
||||
((op) == MP_BINARY_OP_EQUAL) || ((op) == MP_BINARY_OP_NOT_EQUAL)) {\
|
||||
mp_obj_t out_list = mp_obj_new_list(0, NULL);\
|
||||
size_t m = (ol)->m, n = (ol)->n;\
|
||||
for(size_t i=0, r=0; i < m; i++, r+=inc) {\
|
||||
for(size_t i=0; i < m; i++) {\
|
||||
mp_obj_t row = mp_obj_new_list(n, NULL);\
|
||||
mp_obj_list_t *row_ptr = MP_OBJ_TO_PTR(row);\
|
||||
for(size_t j=0, s=0; j < n; j++, s+=inc) {\
|
||||
row_ptr->items[j] = mp_const_false;\
|
||||
if((op) == MP_BINARY_OP_LESS) {\
|
||||
if(left[i*n+j] < right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
} else if((op) == MP_BINARY_OP_LESS_EQUAL) {\
|
||||
if(left[i*n+j] <= right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
} else if((op) == MP_BINARY_OP_MORE) {\
|
||||
if(left[i*n+j] > right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
} else if((op) == MP_BINARY_OP_MORE_EQUAL) {\
|
||||
if(left[i*n+j] >= right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
}\
|
||||
}\
|
||||
if((op) == MP_BINARY_OP_LESS) { for(size_t j=0; j < n; j++, left+=linc, right+=rinc) row_ptr->items[j] = *left < *right ? mp_const_true : mp_const_false; }\
|
||||
else if((op) == MP_BINARY_OP_LESS_EQUAL) { for(size_t j=0; j < n; j++, left+=linc, right+=rinc) row_ptr->items[j] = *left <= *right ? mp_const_true : mp_const_false; }\
|
||||
else if((op) == MP_BINARY_OP_MORE) { for(size_t j=0; j < n; j++, left+=linc, right+=rinc) row_ptr->items[j] = *left > *right ? mp_const_true : mp_const_false; }\
|
||||
else if((op) == MP_BINARY_OP_MORE_EQUAL) { for(size_t j=0; j < n; j++, left+=linc, right+=rinc) row_ptr->items[j] = *left >= *right ? mp_const_true : mp_const_false; }\
|
||||
else if((op) == MP_BINARY_OP_EQUAL) { for(size_t j=0; j < n; j++, left+=linc, right+=rinc) row_ptr->items[j] = *left == *right ? mp_const_true : mp_const_false; }\
|
||||
else if((op) == MP_BINARY_OP_NOT_EQUAL) { for(size_t j=0; j < n; j++, left+=linc, right+=rinc) row_ptr->items[j] = *left != *right ? mp_const_true : mp_const_false; }\
|
||||
if(m == 1) return row;\
|
||||
mp_obj_list_append(out_list, row);\
|
||||
}\
|
||||
}\
|
||||
return out_list;\
|
||||
}\
|
||||
} while(0)
|
||||
|
|
|
|||
|
|
@ -20,43 +20,42 @@
|
|||
|
||||
#include "ndarray.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;
|
||||
|
||||
/* v923z: it is not at all clear to me, why this must be declared; it should already be in obj.h */
|
||||
typedef struct _mp_obj_none_t {
|
||||
mp_obj_base_t base;
|
||||
} mp_obj_none_t;
|
||||
|
||||
const mp_obj_type_t mp_type_NoneType;
|
||||
const mp_obj_none_t mp_const_none_obj = {{&mp_type_NoneType}};
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_shape_obj, ndarray_shape);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_size_obj, ndarray_size);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_itemsize_obj, ndarray_itemsize);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten);
|
||||
|
||||
STATIC const mp_obj_property_t ndarray_shape_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&ndarray_get_shape_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
mp_const_none,
|
||||
mp_const_none },
|
||||
};
|
||||
|
||||
STATIC const mp_obj_property_t ndarray_size_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&ndarray_get_size_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
mp_const_none,
|
||||
mp_const_none },
|
||||
};
|
||||
|
||||
STATIC const mp_obj_property_t ndarray_itemsize_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&ndarray_get_itemsize_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
mp_const_none,
|
||||
mp_const_none },
|
||||
};
|
||||
#else
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
|
@ -31,58 +32,15 @@ enum NUMERICAL_FUNCTION_TYPE {
|
|||
NUMERICAL_STD,
|
||||
};
|
||||
|
||||
mp_obj_t numerical_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_num, MP_ARG_INT, {.u_int = 50} },
|
||||
{ MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_true} },
|
||||
{ MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_false} },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
//| """Numerical and Statistical functions
|
||||
//|
|
||||
//| Most of these functions take an "axis" argument, which indicates whether to
|
||||
//| operate over the flattened array (None), rows (0), or columns (1)."""
|
||||
//|
|
||||
//| from ulab import _ArrayLike
|
||||
//|
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(2, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
uint16_t len = args[2].u_int;
|
||||
if(len < 2) {
|
||||
mp_raise_ValueError(translate("number of points must be at least 2"));
|
||||
}
|
||||
mp_float_t value, step;
|
||||
value = mp_obj_get_float(args[0].u_obj);
|
||||
uint8_t typecode = args[5].u_int;
|
||||
if(args[3].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj)-value)/(len-1);
|
||||
else step = (mp_obj_get_float(args[1].u_obj)-value)/len;
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(1, len, typecode);
|
||||
if(typecode == NDARRAY_UINT8) {
|
||||
uint8_t *array = (uint8_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (uint8_t)value;
|
||||
} else if(typecode == NDARRAY_INT8) {
|
||||
int8_t *array = (int8_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (int8_t)value;
|
||||
} else if(typecode == NDARRAY_UINT16) {
|
||||
uint16_t *array = (uint16_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (uint16_t)value;
|
||||
} else if(typecode == NDARRAY_INT16) {
|
||||
int16_t *array = (int16_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (int16_t)value;
|
||||
} else {
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = value;
|
||||
}
|
||||
if(args[4].u_obj == mp_const_false) {
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = ndarray;
|
||||
tuple[1] = mp_obj_new_float(step);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_linspace_obj, 2, numerical_linspace);
|
||||
|
||||
void axis_sorter(ndarray_obj_t *ndarray, mp_obj_t axis, size_t *m, size_t *n, size_t *N,
|
||||
static void axis_sorter(ndarray_obj_t *ndarray, mp_obj_t axis, size_t *m, size_t *n, size_t *N,
|
||||
size_t *increment, size_t *len, size_t *start_inc) {
|
||||
if(axis == mp_const_none) { // flatten the array
|
||||
*m = 1;
|
||||
|
|
@ -108,7 +66,7 @@ void axis_sorter(ndarray_obj_t *ndarray, mp_obj_t axis, size_t *m, size_t *n, si
|
|||
}
|
||||
}
|
||||
|
||||
mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, size_t ddof) {
|
||||
static mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, size_t ddof) {
|
||||
mp_float_t value, sum = 0.0, sq_sum = 0.0;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(oin, &iter_buf);
|
||||
|
|
@ -166,7 +124,7 @@ STATIC mp_obj_t numerical_sum_mean_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis
|
|||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, size_t ddof) {
|
||||
static mp_obj_t numerical_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, size_t ddof) {
|
||||
size_t m, n, increment, start, start_inc, N, len;
|
||||
mp_float_t sum, sum_sq;
|
||||
|
||||
|
|
@ -199,20 +157,32 @@ mp_obj_t numerical_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, size_t ddo
|
|||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
|
||||
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"));
|
||||
}
|
||||
size_t idx = 0, best_idx = 0;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(oin, &iter_buf);
|
||||
mp_obj_t best_obj = MP_OBJ_NULL;
|
||||
mp_obj_t item;
|
||||
mp_uint_t op = MP_BINARY_OP_LESS;
|
||||
if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = MP_BINARY_OP_MORE;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if ((best_obj == MP_OBJ_NULL) || (mp_binary_op(op, item, best_obj) == mp_const_true)) {
|
||||
best_obj = item;
|
||||
best_idx = idx;
|
||||
}
|
||||
idx++;
|
||||
uint8_t op = 0; // argmin, min
|
||||
if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = 1;
|
||||
item = mp_iternext(iterable);
|
||||
mp_obj_t best_obj = item;
|
||||
mp_float_t value, best_value = mp_obj_get_float(item);
|
||||
value = best_value;
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
idx++;
|
||||
value = mp_obj_get_float(item);
|
||||
if((op == 0) && (value < best_value)) {
|
||||
best_obj = item;
|
||||
best_idx = idx;
|
||||
best_value = value;
|
||||
} else if((op == 1) && (value > best_value)) {
|
||||
best_obj = item;
|
||||
best_idx = idx;
|
||||
best_value = value;
|
||||
}
|
||||
}
|
||||
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
|
||||
return MP_OBJ_NEW_SMALL_INT(best_idx);
|
||||
|
|
@ -221,14 +191,14 @@ mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, mp_obj_t axis, uint8_t o
|
|||
}
|
||||
}
|
||||
|
||||
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 axis, uint8_t optype) {
|
||||
size_t m, n, increment, start, start_inc, N, len;
|
||||
axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);
|
||||
ndarray_obj_t *results;
|
||||
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
|
||||
// we could save some RAM by taking NDARRAY_UINT8, if the dimensions
|
||||
// are smaller than 256, but the code would become more involving
|
||||
// (we would also need extra flash space)
|
||||
if(ndarray->array->len == 0) {
|
||||
mp_raise_ValueError(translate("attempt to get argmin/argmax of an empty sequence"));
|
||||
}
|
||||
results = create_new_ndarray(m, n, NDARRAY_UINT16);
|
||||
} else {
|
||||
results = create_new_ndarray(m, n, ndarray->array->typecode);
|
||||
|
|
@ -236,22 +206,35 @@ mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis,
|
|||
|
||||
for(size_t j=0; j < N; j++) { // result index
|
||||
start = j * start_inc;
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
|
||||
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
|
||||
if(ndarray->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_ARGMIN(ndarray, results, uint8_t, uint8_t, len, start, increment, optype, j);
|
||||
} else if(ndarray->array->typecode == NDARRAY_INT8) {
|
||||
RUN_ARGMIN(ndarray, results, int8_t, int8_t, len, start, increment, optype, j);
|
||||
} else if(ndarray->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_ARGMIN(ndarray, results, uint16_t, uint16_t, len, start, increment, optype, j);
|
||||
} else if(ndarray->array->typecode == NDARRAY_INT16) {
|
||||
RUN_ARGMIN(ndarray, results, int16_t, int16_t, len, start, increment, optype, j);
|
||||
} else {
|
||||
RUN_ARGMIN(ndarray, results, uint8_t, uint16_t, len, start, increment, optype, j);
|
||||
}
|
||||
} else if((ndarray->array->typecode == NDARRAY_UINT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
|
||||
RUN_ARGMIN(ndarray, results, uint16_t, uint16_t, len, start, increment, optype, j);
|
||||
} else {
|
||||
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
|
||||
RUN_ARGMIN(ndarray, results, mp_float_t, mp_float_t, len, start, increment, optype, j);
|
||||
}
|
||||
} else { // argmin/argmax
|
||||
if(ndarray->array->typecode == NDARRAY_UINT8) {
|
||||
RUN_ARGMIN(ndarray, results, uint8_t, uint16_t, len, start, increment, optype, j);
|
||||
} else if(ndarray->array->typecode == NDARRAY_INT8) {
|
||||
RUN_ARGMIN(ndarray, results, int8_t, uint16_t, len, start, increment, optype, j);
|
||||
} else if(ndarray->array->typecode == NDARRAY_UINT16) {
|
||||
RUN_ARGMIN(ndarray, results, uint16_t, uint16_t, len, start, increment, optype, j);
|
||||
} else if(ndarray->array->typecode == NDARRAY_INT16) {
|
||||
RUN_ARGMIN(ndarray, results, int16_t, uint16_t, len, start, increment, optype, j);
|
||||
} else {
|
||||
RUN_ARGMIN(ndarray, results, mp_float_t, uint16_t, len, start, increment, optype, j);
|
||||
RUN_ARGMIN(ndarray, results, mp_float_t, uint16_t, len, start, increment, optype, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(results->array->len == 1) {
|
||||
return mp_binary_get_val_array(results->array->typecode, results->array->items, 0);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +261,7 @@ STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
case NUMERICAL_ARGMIN:
|
||||
case NUMERICAL_MAX:
|
||||
case NUMERICAL_ARGMAX:
|
||||
return numerical_argmin_argmax_iterable(oin, axis, optype);
|
||||
return numerical_argmin_argmax_iterable(oin, optype);
|
||||
case NUMERICAL_SUM:
|
||||
case NUMERICAL_MEAN:
|
||||
return numerical_sum_mean_std_iterable(oin, optype, 0);
|
||||
|
|
@ -305,73 +288,332 @@ STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t numerical_min(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MIN);
|
||||
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"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray;
|
||||
if(inplace == 1) {
|
||||
ndarray = MP_OBJ_TO_PTR(oin);
|
||||
} else {
|
||||
mp_obj_t out = ndarray_copy(oin);
|
||||
ndarray = MP_OBJ_TO_PTR(out);
|
||||
}
|
||||
size_t increment, start_inc, end, N;
|
||||
if(axis == mp_const_none) { // flatten the array
|
||||
ndarray->m = 1;
|
||||
ndarray->n = ndarray->array->len;
|
||||
increment = 1;
|
||||
start_inc = ndarray->n;
|
||||
end = ndarray->n;
|
||||
N = ndarray->n;
|
||||
} else if((mp_obj_get_int(axis) == -1) ||
|
||||
(mp_obj_get_int(axis) == 1)) { // sort along the horizontal axis
|
||||
increment = 1;
|
||||
start_inc = ndarray->n;
|
||||
end = ndarray->array->len;
|
||||
N = ndarray->n;
|
||||
} else if(mp_obj_get_int(axis) == 0) { // sort along vertical axis
|
||||
increment = ndarray->n;
|
||||
start_inc = 1;
|
||||
end = ndarray->n;
|
||||
N = ndarray->m;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("axis must be -1, 0, None, or 1"));
|
||||
}
|
||||
|
||||
size_t q, k, p, c;
|
||||
|
||||
for(size_t start=0; start < end; start+=start_inc) {
|
||||
q = N;
|
||||
k = (q >> 1);
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
HEAPSORT(uint8_t, ndarray);
|
||||
} else if((ndarray->array->typecode == NDARRAY_INT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
|
||||
HEAPSORT(uint16_t, ndarray);
|
||||
} else {
|
||||
HEAPSORT(mp_float_t, ndarray);
|
||||
}
|
||||
}
|
||||
if(inplace == 1) {
|
||||
return mp_const_none;
|
||||
} else {
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min);
|
||||
//| def argmax(array: _ArrayLike, *, axis: Optional[int] = None) -> int:
|
||||
//| """Return the index of the maximum element of the 1D array"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t numerical_max(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MAX);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_max_obj, 1, numerical_max);
|
||||
|
||||
mp_obj_t numerical_argmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMIN);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin);
|
||||
|
||||
mp_obj_t numerical_argmax(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static mp_obj_t numerical_argmax(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMAX);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmax_obj, 1, numerical_argmax);
|
||||
|
||||
mp_obj_t numerical_sum(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_SUM);
|
||||
//| def argmin(array: _ArrayLike, *, axis: Optional[int] = None) -> int:
|
||||
//| """Return the index of the minimum element of the 1D array"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_argmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMIN);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sum_obj, 1, numerical_sum);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin);
|
||||
|
||||
mp_obj_t numerical_mean(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MEAN);
|
||||
}
|
||||
//| def argsort(array: ulab.array, *, axis: Optional[int] = None) -> ulab.array:
|
||||
//| """Returns an array which gives indices into the input array from least to greatest."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean);
|
||||
|
||||
mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } } ,
|
||||
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
|
||||
};
|
||||
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"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
size_t increment, start_inc, end, N, m, n;
|
||||
if(args[1].u_obj == mp_const_none) { // flatten the array
|
||||
m = 1;
|
||||
n = ndarray->array->len;
|
||||
increment = 1;
|
||||
start_inc = ndarray->n;
|
||||
end = ndarray->n;
|
||||
N = n;
|
||||
} else if((mp_obj_get_int(args[1].u_obj) == -1) || (mp_obj_get_int(args[1].u_obj) == 1)) { // sort along the horizontal axis
|
||||
m = ndarray->m;
|
||||
n = ndarray->n;
|
||||
increment = 1;
|
||||
start_inc = n;
|
||||
end = ndarray->array->len;
|
||||
N = n;
|
||||
} else if(mp_obj_get_int(args[1].u_obj) == 0) { // sort along vertical axis
|
||||
m = ndarray->m;
|
||||
n = ndarray->n;
|
||||
increment = n;
|
||||
start_inc = 1;
|
||||
end = n;
|
||||
N = m;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("axis must be -1, 0, None, or 1"));
|
||||
}
|
||||
|
||||
if((m > 65535) || (n > 65535)) {
|
||||
mp_raise_ValueError(translate("sorted axis can't be longer than 65535"));
|
||||
}
|
||||
// at the expense of flash, we could save RAM by creating
|
||||
// an NDARRAY_UINT16 ndarray only, if needed, otherwise, NDARRAY_UINT8
|
||||
ndarray_obj_t *indices = create_new_ndarray(m, n, NDARRAY_UINT16);
|
||||
uint16_t *index_array = (uint16_t *)indices->array->items;
|
||||
// initialise the index array
|
||||
// if array is flat: 0 to indices->n
|
||||
// if sorting vertically, identical indices are arranged row-wise
|
||||
// if sorting horizontally, identical indices are arranged colunn-wise
|
||||
for(uint16_t start=0; start < end; start+=start_inc) {
|
||||
for(uint16_t s=0; s < N; s++) {
|
||||
index_array[start+s*increment] = s;
|
||||
}
|
||||
}
|
||||
|
||||
size_t q, k, p, c;
|
||||
for(size_t start=0; start < end; start+=start_inc) {
|
||||
q = N;
|
||||
k = (q >> 1);
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
HEAP_ARGSORT(uint8_t, ndarray, index_array);
|
||||
} else if((ndarray->array->typecode == NDARRAY_INT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
|
||||
HEAP_ARGSORT(uint16_t, ndarray, index_array);
|
||||
} else {
|
||||
HEAP_ARGSORT(mp_float_t, ndarray, index_array);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(indices);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argsort_obj, 1, numerical_argsort);
|
||||
|
||||
//| def diff(array: ulab.array, *, axis: int = 1) -> ulab.array:
|
||||
//| """Return the numerical derivative of successive elements of the array, as
|
||||
//| an array. axis=None is not supported."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } },
|
||||
};
|
||||
|
||||
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 oin = args[0].u_obj;
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
size_t ddof = args[2].u_int;
|
||||
if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
|
||||
// this seems to pass with False, and True...
|
||||
mp_raise_ValueError(translate("axis must be None, 0, or 1"));
|
||||
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("diff argument must be an ndarray"));
|
||||
}
|
||||
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_std_ndarray(ndarray, axis, ddof);
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
size_t increment, N, M;
|
||||
if((args[2].u_int == -1) || (args[2].u_int == 1)) { // differentiate along the horizontal axis
|
||||
increment = 1;
|
||||
} else if(args[2].u_int == 0) { // differtiate along vertical axis
|
||||
increment = in->n;
|
||||
} else {
|
||||
mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray"));
|
||||
mp_raise_ValueError(translate("axis must be -1, 0, or 1"));
|
||||
}
|
||||
return mp_const_none;
|
||||
if((args[1].u_int < 0) || (args[1].u_int > 9)) {
|
||||
mp_raise_ValueError(translate("n must be between 0, and 9"));
|
||||
}
|
||||
uint8_t n = args[1].u_int;
|
||||
int8_t *stencil = m_new(int8_t, n+1);
|
||||
stencil[0] = 1;
|
||||
for(uint8_t i=1; i < n+1; i++) {
|
||||
stencil[i] = -stencil[i-1]*(n-i+1)/i;
|
||||
}
|
||||
|
||||
ndarray_obj_t *out;
|
||||
|
||||
if(increment == 1) { // differentiate along the horizontal axis
|
||||
if(n >= in->n) {
|
||||
out = create_new_ndarray(in->m, 0, in->array->typecode);
|
||||
m_del(uint8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
N = in->n - n;
|
||||
M = in->m;
|
||||
} else { // differentiate along vertical axis
|
||||
if(n >= in->m) {
|
||||
out = create_new_ndarray(0, in->n, in->array->typecode);
|
||||
m_del(uint8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
M = in->m - n;
|
||||
N = in->n;
|
||||
}
|
||||
out = create_new_ndarray(M, N, in->array->typecode);
|
||||
if(in->array->typecode == NDARRAY_UINT8) {
|
||||
CALCULATE_DIFF(in, out, uint8_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_INT8) {
|
||||
CALCULATE_DIFF(in, out, int8_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_UINT16) {
|
||||
CALCULATE_DIFF(in, out, uint16_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_INT16) {
|
||||
CALCULATE_DIFF(in, out, int16_t, M, N, in->n, increment);
|
||||
} else {
|
||||
CALCULATE_DIFF(in, out, mp_float_t, M, N, in->n, increment);
|
||||
}
|
||||
m_del(int8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
|
||||
|
||||
mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
//| def flip(array: ulab.array, *, axis: Optional[int] = None) -> ulab.array:
|
||||
//| """Returns a new array that reverses the order of the elements along the
|
||||
//| given axis, or along all axes if axis is None."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
|
||||
mp_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("flip argument must be an ndarray"));
|
||||
}
|
||||
if((args[1].u_obj != mp_const_none) &&
|
||||
(mp_obj_get_int(args[1].u_obj) != 0) &&
|
||||
(mp_obj_get_int(args[1].u_obj) != 1)) {
|
||||
mp_raise_ValueError(translate("axis must be None, 0, or 1"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
mp_obj_t oout = ndarray_copy(args[0].u_obj);
|
||||
ndarray_obj_t *out = MP_OBJ_TO_PTR(oout);
|
||||
uint8_t _sizeof = mp_binary_get_size('@', in->array->typecode, NULL);
|
||||
uint8_t *array_in = (uint8_t *)in->array->items;
|
||||
uint8_t *array_out = (uint8_t *)out->array->items;
|
||||
size_t len;
|
||||
if((args[1].u_obj == mp_const_none) || (mp_obj_get_int(args[1].u_obj) == 1)) { // flip horizontally
|
||||
uint16_t M = in->m;
|
||||
len = in->n;
|
||||
if(args[1].u_obj == mp_const_none) { // flip flattened array
|
||||
len = in->array->len;
|
||||
M = 1;
|
||||
}
|
||||
for(size_t m=0; m < M; m++) {
|
||||
for(size_t n=0; n < len; n++) {
|
||||
memcpy(array_out+_sizeof*(m*len+n), array_in+_sizeof*((m+1)*len-n-1), _sizeof);
|
||||
}
|
||||
}
|
||||
} else { // flip vertically
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
for(size_t n=0; n < in->n; n++) {
|
||||
memcpy(array_out+_sizeof*(m*in->n+n), array_in+_sizeof*((in->m-m-1)*in->n+n), _sizeof);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
|
||||
|
||||
//| def max(array: _ArrayLike, *, axis: Optional[int] = None) -> float:
|
||||
//| """Return the maximum element of the 1D array"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_max(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MAX);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_max_obj, 1, numerical_max);
|
||||
|
||||
//| def mean(array: _ArrayLike, *, axis: Optional[int] = None) -> float:
|
||||
//| """Return the mean element of the 1D array, as a number if axis is None, otherwise as an array."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_mean(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MEAN);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean);
|
||||
|
||||
//| def min(array: _ArrayLike, *, axis: Optional[int] = None) -> float:
|
||||
//| """Return the minimum element of the 1D array"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_min(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MIN);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min);
|
||||
|
||||
//| def roll(array: ulab.array, distance: int, *, axis: Optional[int] = None) -> None:
|
||||
//| """Shift the content of a vector by the positions given as the second
|
||||
//| argument. If the ``axis`` keyword is supplied, the shift is applied to
|
||||
//| the given axis. The array is modified in place."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
|
|
@ -379,7 +621,7 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
|
|||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(2, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t oin = args[0].u_obj;
|
||||
int16_t shift = mp_obj_get_int(args[1].u_obj);
|
||||
|
|
@ -453,190 +695,20 @@ mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
|
|||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll);
|
||||
|
||||
mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
};
|
||||
//| def sort(array: ulab.array, *, axis: Optional[int] = 0) -> ulab.array:
|
||||
//| """Sort the array along the given axis, or along all axes if axis is None.
|
||||
//| The array is modified in place."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, 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"));
|
||||
}
|
||||
if((args[1].u_obj != mp_const_none) &&
|
||||
(mp_obj_get_int(args[1].u_obj) != 0) &&
|
||||
(mp_obj_get_int(args[1].u_obj) != 1)) {
|
||||
mp_raise_ValueError(translate("axis must be None, 0, or 1"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
mp_obj_t oout = ndarray_copy(args[0].u_obj);
|
||||
ndarray_obj_t *out = MP_OBJ_TO_PTR(oout);
|
||||
uint8_t _sizeof = mp_binary_get_size('@', in->array->typecode, NULL);
|
||||
uint8_t *array_in = (uint8_t *)in->array->items;
|
||||
uint8_t *array_out = (uint8_t *)out->array->items;
|
||||
size_t len;
|
||||
if((args[1].u_obj == mp_const_none) || (mp_obj_get_int(args[1].u_obj) == 1)) { // flip horizontally
|
||||
uint16_t M = in->m;
|
||||
len = in->n;
|
||||
if(args[1].u_obj == mp_const_none) { // flip flattened array
|
||||
len = in->array->len;
|
||||
M = 1;
|
||||
}
|
||||
for(size_t m=0; m < M; m++) {
|
||||
for(size_t n=0; n < len; n++) {
|
||||
memcpy(array_out+_sizeof*(m*len+n), array_in+_sizeof*((m+1)*len-n-1), _sizeof);
|
||||
}
|
||||
}
|
||||
} else { // flip vertically
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
for(size_t n=0; n < in->n; n++) {
|
||||
memcpy(array_out+_sizeof*(m*in->n+n), array_in+_sizeof*((in->m-m-1)*in->n+n), _sizeof);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
|
||||
|
||||
mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, 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"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
size_t increment, N, M;
|
||||
if((args[2].u_int == -1) || (args[2].u_int == 1)) { // differentiate along the horizontal axis
|
||||
increment = 1;
|
||||
} else if(args[2].u_int == 0) { // differtiate along vertical axis
|
||||
increment = in->n;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("axis must be -1, 0, or 1"));
|
||||
}
|
||||
if((args[1].u_int < 0) || (args[1].u_int > 9)) {
|
||||
mp_raise_ValueError(translate("n must be between 0, and 9"));
|
||||
}
|
||||
uint8_t n = args[1].u_int;
|
||||
int8_t *stencil = m_new(int8_t, n+1);
|
||||
stencil[0] = 1;
|
||||
for(uint8_t i=1; i < n+1; i++) {
|
||||
stencil[i] = -stencil[i-1]*(n-i+1)/i;
|
||||
}
|
||||
|
||||
ndarray_obj_t *out;
|
||||
|
||||
if(increment == 1) { // differentiate along the horizontal axis
|
||||
if(n >= in->n) {
|
||||
out = create_new_ndarray(in->m, 0, in->array->typecode);
|
||||
m_del(uint8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
N = in->n - n;
|
||||
M = in->m;
|
||||
} else { // differentiate along vertical axis
|
||||
if(n >= in->m) {
|
||||
out = create_new_ndarray(0, in->n, in->array->typecode);
|
||||
m_del(uint8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
M = in->m - n;
|
||||
N = in->n;
|
||||
}
|
||||
out = create_new_ndarray(M, N, in->array->typecode);
|
||||
if(in->array->typecode == NDARRAY_UINT8) {
|
||||
CALCULATE_DIFF(in, out, uint8_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_INT8) {
|
||||
CALCULATE_DIFF(in, out, int8_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_UINT16) {
|
||||
CALCULATE_DIFF(in, out, uint16_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_INT16) {
|
||||
CALCULATE_DIFF(in, out, int16_t, M, N, in->n, increment);
|
||||
} else {
|
||||
CALCULATE_DIFF(in, out, mp_float_t, M, N, in->n, increment);
|
||||
}
|
||||
m_del(int8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray;
|
||||
mp_obj_t out;
|
||||
if(inplace == 1) {
|
||||
ndarray = MP_OBJ_TO_PTR(oin);
|
||||
} else {
|
||||
out = ndarray_copy(oin);
|
||||
ndarray = MP_OBJ_TO_PTR(out);
|
||||
}
|
||||
size_t increment, start_inc, end, N;
|
||||
if(axis == mp_const_none) { // flatten the array
|
||||
ndarray->m = 1;
|
||||
ndarray->n = ndarray->array->len;
|
||||
increment = 1;
|
||||
start_inc = ndarray->n;
|
||||
end = ndarray->n;
|
||||
N = ndarray->n;
|
||||
} else if((mp_obj_get_int(axis) == -1) ||
|
||||
(mp_obj_get_int(axis) == 1)) { // sort along the horizontal axis
|
||||
increment = 1;
|
||||
start_inc = ndarray->n;
|
||||
end = ndarray->array->len;
|
||||
N = ndarray->n;
|
||||
} else if(mp_obj_get_int(axis) == 0) { // sort along vertical axis
|
||||
increment = ndarray->n;
|
||||
start_inc = 1;
|
||||
end = ndarray->m;
|
||||
N = ndarray->m;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("axis must be -1, 0, None, or 1"));
|
||||
}
|
||||
|
||||
size_t q, k, p, c;
|
||||
|
||||
for(size_t start=0; start < end; start+=start_inc) {
|
||||
q = N;
|
||||
k = (q >> 1);
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
HEAPSORT(uint8_t, ndarray);
|
||||
} else if((ndarray->array->typecode == NDARRAY_INT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
|
||||
HEAPSORT(uint16_t, ndarray);
|
||||
} else {
|
||||
HEAPSORT(mp_float_t, ndarray);
|
||||
}
|
||||
}
|
||||
if(inplace == 1) {
|
||||
return mp_const_none;
|
||||
} else {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
// numpy function
|
||||
mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
return numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0);
|
||||
}
|
||||
|
|
@ -644,107 +716,80 @@ mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
|
|||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_obj, 1, numerical_sort);
|
||||
|
||||
// method of an ndarray
|
||||
mp_obj_t numerical_sort_inplace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static mp_obj_t numerical_sort_inplace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
return numerical_sort_helper(args[0].u_obj, args[1].u_obj, 1);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj, 1, numerical_sort_inplace);
|
||||
|
||||
mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
//| def std(array: _ArrayLike, *, axis: Optional[int] = None) -> float:
|
||||
//| """Return the standard deviation of the array, as a number if axis is None, otherwise as an array."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } } ,
|
||||
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, 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_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t oin = args[0].u_obj;
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
size_t ddof = args[2].u_int;
|
||||
if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
|
||||
// this seems to pass with False, and True...
|
||||
mp_raise_ValueError(translate("axis must be None, 0, or 1"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
size_t increment, start_inc, end, N, m, n;
|
||||
if(args[1].u_obj == mp_const_none) { // flatten the array
|
||||
m = 1;
|
||||
n = ndarray->array->len;
|
||||
ndarray->m = m;
|
||||
ndarray->n = n;
|
||||
increment = 1;
|
||||
start_inc = ndarray->n;
|
||||
end = ndarray->n;
|
||||
N = n;
|
||||
} else if((mp_obj_get_int(args[1].u_obj) == -1) ||
|
||||
(mp_obj_get_int(args[1].u_obj) == 1)) { // sort along the horizontal axis
|
||||
m = ndarray->m;
|
||||
n = ndarray->n;
|
||||
increment = 1;
|
||||
start_inc = n;
|
||||
end = ndarray->array->len;
|
||||
N = n;
|
||||
} else if(mp_obj_get_int(args[1].u_obj) == 0) { // sort along vertical axis
|
||||
m = ndarray->m;
|
||||
n = ndarray->n;
|
||||
increment = n;
|
||||
start_inc = 1;
|
||||
end = m;
|
||||
N = m;
|
||||
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_std_ndarray(ndarray, axis, ddof);
|
||||
} else {
|
||||
mp_raise_ValueError(translate("axis must be -1, 0, None, or 1"));
|
||||
mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray"));
|
||||
}
|
||||
|
||||
// at the expense of flash, we could save RAM by creating
|
||||
// an NDARRAY_UINT16 ndarray only, if needed, otherwise, NDARRAY_UINT8
|
||||
ndarray_obj_t *indices = create_new_ndarray(m, n, NDARRAY_UINT16);
|
||||
uint16_t *index_array = (uint16_t *)indices->array->items;
|
||||
// initialise the index array
|
||||
// if array is flat: 0 to indices->n
|
||||
// if sorting vertically, identical indices are arranged row-wise
|
||||
// if sorting horizontally, identical indices are arranged colunn-wise
|
||||
for(uint16_t start=0; start < end; start+=start_inc) {
|
||||
for(uint16_t s=0; s < N; s++) {
|
||||
index_array[start+s*increment] = s;
|
||||
}
|
||||
}
|
||||
|
||||
size_t q, k, p, c;
|
||||
for(size_t start=0; start < end; start+=start_inc) {
|
||||
q = N;
|
||||
k = (q >> 1);
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
HEAP_ARGSORT(uint8_t, ndarray, index_array);
|
||||
} else if((ndarray->array->typecode == NDARRAY_INT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
|
||||
HEAP_ARGSORT(uint16_t, ndarray, index_array);
|
||||
} else {
|
||||
HEAP_ARGSORT(mp_float_t, ndarray, index_array);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(indices);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argsort_obj, 1, numerical_argsort);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std);
|
||||
|
||||
//| def sum(array: _ArrayLike, *, axis: Optional[int] = None) -> Union[float, int, ulab.array]:
|
||||
//| """Return the sum of the array, as a number if axis is None, otherwise as an array."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t numerical_sum(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_SUM);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sum_obj, 1, numerical_sum);
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_rom_map_elem_t ulab_numerical_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_linspace), (mp_obj_t)&numerical_linspace_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&numerical_sum_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&numerical_mean_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_std), (mp_obj_t)&numerical_std_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_min), (mp_obj_t)&numerical_min_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_max), (mp_obj_t)&numerical_max_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argmin), (mp_obj_t)&numerical_argmin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_numerical) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argmax), (mp_obj_t)&numerical_argmax_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argmin), (mp_obj_t)&numerical_argmin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argsort), (mp_obj_t)&numerical_argsort_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_max), (mp_obj_t)&numerical_max_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&numerical_mean_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_min), (mp_obj_t)&numerical_min_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sort), (mp_obj_t)&numerical_sort_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argsort), (mp_obj_t)&numerical_argsort_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_std), (mp_obj_t)&numerical_std_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&numerical_sum_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_numerical_globals, ulab_numerical_globals_table);
|
||||
|
|
@ -753,6 +798,5 @@ mp_obj_module_t ulab_numerical_module = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_numerical_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -12,16 +12,14 @@
|
|||
#ifndef _NUMERICAL_
|
||||
#define _NUMERICAL_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_NUMERICAL_MODULE
|
||||
|
||||
extern mp_obj_module_t ulab_numerical_module;
|
||||
|
||||
// TODO: implement minimum/maximum, and cumsum
|
||||
//mp_obj_t numerical_minimum(mp_obj_t , mp_obj_t );
|
||||
//mp_obj_t numerical_maximum(mp_obj_t , mp_obj_t );
|
||||
// TODO: implement cumsum
|
||||
//mp_obj_t numerical_cumsum(size_t , const mp_obj_t *, mp_map_t *);
|
||||
|
||||
#define RUN_ARGMIN(in, out, typein, typeout, len, start, increment, op, pos) do {\
|
||||
|
|
@ -32,13 +30,13 @@ extern mp_obj_module_t ulab_numerical_module;
|
|||
for(size_t i=1; i < (len); i++) {\
|
||||
if(array[(start)+i*(increment)] > array[(start)+best_index*(increment)]) best_index = i;\
|
||||
}\
|
||||
if((op) == NUMERICAL_MAX) outarray[(pos)] = array[(start)+best_index*(increment)];\
|
||||
if((op) == NUMERICAL_MAX) outarray[(pos)] = (typeout)array[(start)+best_index*(increment)];\
|
||||
else outarray[(pos)] = best_index;\
|
||||
} else{\
|
||||
for(size_t i=1; i < (len); i++) {\
|
||||
if(array[(start)+i*(increment)] < array[(start)+best_index*(increment)]) best_index = i;\
|
||||
}\
|
||||
if((op) == NUMERICAL_MIN) outarray[(pos)] = array[(start)+best_index*(increment)];\
|
||||
if((op) == NUMERICAL_MIN) outarray[(pos)] = (typeout)array[(start)+best_index*(increment)];\
|
||||
else outarray[(pos)] = best_index;\
|
||||
}\
|
||||
} while(0)
|
||||
|
|
@ -46,8 +44,8 @@ extern mp_obj_module_t ulab_numerical_module;
|
|||
#define RUN_SUM(ndarray, type, optype, len, start, increment) do {\
|
||||
type *array = (type *)(ndarray)->array->items;\
|
||||
type value;\
|
||||
for(size_t j=0; j < (len); j++) {\
|
||||
value = array[(start)+j*(increment)];\
|
||||
for(size_t k=0; k < (len); k++) {\
|
||||
value = array[(start)+k*(increment)];\
|
||||
sum += value;\
|
||||
}\
|
||||
} while(0)
|
||||
|
|
@ -55,12 +53,12 @@ extern mp_obj_module_t ulab_numerical_module;
|
|||
#define RUN_STD(ndarray, type, len, start, increment) do {\
|
||||
type *array = (type *)(ndarray)->array->items;\
|
||||
mp_float_t value;\
|
||||
for(size_t j=0; j < (len); j++) {\
|
||||
sum += array[(start)+j*(increment)];\
|
||||
for(size_t k=0; k < (len); k++) {\
|
||||
sum += array[(start)+k*(increment)];\
|
||||
}\
|
||||
sum /= (len);\
|
||||
for(size_t j=0; j < (len); j++) {\
|
||||
value = (array[(start)+j*(increment)] - sum);\
|
||||
for(size_t k=0; k < (len); k++) {\
|
||||
value = (array[(start)+k*(increment)] - sum);\
|
||||
sum_sq += value * value;\
|
||||
}\
|
||||
} while(0)
|
||||
|
|
@ -79,33 +77,34 @@ extern mp_obj_module_t ulab_numerical_module;
|
|||
|
||||
#define HEAPSORT(type, ndarray) do {\
|
||||
type *array = (type *)(ndarray)->array->items;\
|
||||
array += start;\
|
||||
type tmp;\
|
||||
for (;;) {\
|
||||
if (k > 0) {\
|
||||
tmp = array[start+(--k)*increment];\
|
||||
tmp = array[(--k)*increment];\
|
||||
} else {\
|
||||
q--;\
|
||||
if(q == 0) {\
|
||||
break;\
|
||||
}\
|
||||
tmp = array[start+q*increment];\
|
||||
array[start+q*increment] = array[start];\
|
||||
tmp = array[q*increment];\
|
||||
array[q*increment] = array[0];\
|
||||
}\
|
||||
p = k;\
|
||||
c = k + k + 1;\
|
||||
while (c < q) {\
|
||||
if((c + 1 < q) && (array[start+(c+1)*increment] > array[start+c*increment])) {\
|
||||
if((c + 1 < q) && (array[(c+1)*increment] > array[c*increment])) {\
|
||||
c++;\
|
||||
}\
|
||||
if(array[start+c*increment] > tmp) {\
|
||||
array[start+p*increment] = array[start+c*increment];\
|
||||
if(array[c*increment] > tmp) {\
|
||||
array[p*increment] = array[c*increment];\
|
||||
p = c;\
|
||||
c = p + p + 1;\
|
||||
} else {\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
array[start+p*increment] = tmp;\
|
||||
array[p*increment] = tmp;\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
|
|
@ -148,7 +147,6 @@ extern mp_obj_module_t ulab_numerical_module;
|
|||
}\
|
||||
} while(0)
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_linspace_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_min_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_max_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmin_obj);
|
||||
|
|
@ -7,36 +7,153 @@
|
|||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/objarray.h"
|
||||
#include "ndarray.h"
|
||||
#include "linalg.h"
|
||||
#include "../linalg/linalg.h"
|
||||
#include "poly.h"
|
||||
|
||||
#if ULAB_POLY_MODULE
|
||||
bool object_is_nditerable(mp_obj_t o_in) {
|
||||
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type) ||
|
||||
MP_OBJ_IS_TYPE(o_in, &mp_type_tuple) ||
|
||||
MP_OBJ_IS_TYPE(o_in, &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(o_in, &mp_type_range)) {
|
||||
return true;
|
||||
|
||||
//| """Polynomial functions"""
|
||||
//|
|
||||
//| from ulab import _ArrayLike
|
||||
//|
|
||||
|
||||
//| @overload
|
||||
//| def polyfit(y: _ArrayLike, degree: int) -> ulab.array: ...
|
||||
//| @overload
|
||||
//| def polyfit(x: _ArrayLike, y: _ArrayLike, degree: int) -> ulab.array:
|
||||
//| """Return a polynomial of given degree that approximates the function
|
||||
//| f(x)=y. If x is not supplied, it is the range(len(y))."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
|
||||
if((n_args != 2) && (n_args != 3)) {
|
||||
mp_raise_ValueError(translate("number of arguments must be 2, or 3"));
|
||||
}
|
||||
return false;
|
||||
if(!ndarray_object_is_nditerable(args[0])) {
|
||||
mp_raise_ValueError(translate("input data must be an iterable"));
|
||||
}
|
||||
size_t lenx = 0, leny = 0;
|
||||
uint8_t deg = 0;
|
||||
mp_float_t *x, *XT, *y, *prod;
|
||||
|
||||
if(n_args == 2) { // only the y values are supplied
|
||||
// TODO: this is actually not enough: the first argument can very well be a matrix,
|
||||
// in which case we are between the rock and a hard place
|
||||
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"));
|
||||
}
|
||||
lenx = leny;
|
||||
x = m_new(mp_float_t, lenx); // assume uniformly spaced data points
|
||||
for(size_t i=0; i < lenx; i++) {
|
||||
x[i] = i;
|
||||
}
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[0]);
|
||||
} else /* n_args == 3 */ {
|
||||
if(!ndarray_object_is_nditerable(args[1])) {
|
||||
mp_raise_ValueError(translate("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"));
|
||||
}
|
||||
deg = (uint8_t)mp_obj_get_int(args[2]);
|
||||
if(leny < deg) {
|
||||
mp_raise_ValueError(translate("more degrees of freedom than data points"));
|
||||
}
|
||||
x = m_new(mp_float_t, lenx);
|
||||
fill_array_iterable(x, args[0]);
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[1]);
|
||||
}
|
||||
|
||||
// one could probably express X as a function of XT,
|
||||
// and thereby save RAM, because X is used only in the product
|
||||
XT = m_new(mp_float_t, (deg+1)*leny); // XT is a matrix of shape (deg+1, len) (rows, columns)
|
||||
for(size_t i=0; i < leny; i++) { // column index
|
||||
XT[i+0*lenx] = 1.0; // top row
|
||||
for(uint8_t j=1; j < deg+1; j++) { // row index
|
||||
XT[i+j*leny] = XT[i+(j-1)*leny]*x[i];
|
||||
}
|
||||
}
|
||||
|
||||
prod = m_new(mp_float_t, (deg+1)*(deg+1)); // the product matrix is of shape (deg+1, deg+1)
|
||||
mp_float_t sum;
|
||||
for(uint8_t i=0; i < deg+1; i++) { // column index
|
||||
for(uint8_t j=0; j < deg+1; j++) { // row index
|
||||
sum = 0.0;
|
||||
for(size_t k=0; k < lenx; k++) {
|
||||
// (j, k) * (k, i)
|
||||
// Note that the second matrix is simply the transpose of the first:
|
||||
// X(k, i) = XT(i, k) = XT[k*lenx+i]
|
||||
sum += XT[j*lenx+k]*XT[i*lenx+k]; // X[k*(deg+1)+i];
|
||||
}
|
||||
prod[j*(deg+1)+i] = sum;
|
||||
}
|
||||
}
|
||||
if(!linalg_invert_matrix(prod, deg+1)) {
|
||||
// Although X was a Vandermonde matrix, whose inverse is guaranteed to exist,
|
||||
// we bail out here, if prod couldn't be inverted: if the values in x are not all
|
||||
// distinct, prod is singular
|
||||
m_del(mp_float_t, XT, (deg+1)*lenx);
|
||||
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"));
|
||||
}
|
||||
// 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
|
||||
for(uint8_t i=0; i < deg+1; i++) { // row index
|
||||
sum = 0.0;
|
||||
for(size_t j=0; j < lenx; j++) { // column index
|
||||
sum += XT[i*lenx+j]*y[j];
|
||||
}
|
||||
x[i] = sum;
|
||||
}
|
||||
// XT is no longer needed
|
||||
m_del(mp_float_t, XT, (deg+1)*leny);
|
||||
|
||||
ndarray_obj_t *beta = create_new_ndarray(deg+1, 1, NDARRAY_FLOAT);
|
||||
mp_float_t *betav = (mp_float_t *)beta->array->items;
|
||||
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
|
||||
m_del(float, y, leny);
|
||||
|
||||
// now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
|
||||
for(uint8_t i=0; i < deg+1; i++) {
|
||||
sum = 0.0;
|
||||
for(uint8_t j=0; j < deg+1; j++) {
|
||||
sum += prod[i*(deg+1)+j]*x[j];
|
||||
}
|
||||
betav[i] = sum;
|
||||
}
|
||||
m_del(mp_float_t, x, lenx);
|
||||
m_del(mp_float_t, prod, (deg+1)*(deg+1));
|
||||
for(uint8_t i=0; i < (deg+1)/2; i++) {
|
||||
// We have to reverse the array, for the leading coefficient comes first.
|
||||
SWAP(mp_float_t, betav[i], betav[deg-i]);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(beta);
|
||||
}
|
||||
|
||||
size_t get_nditerable_len(mp_obj_t o_in) {
|
||||
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(o_in);
|
||||
return in->array->len;
|
||||
} else {
|
||||
return (size_t)mp_obj_get_int(mp_obj_len_maybe(o_in));
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
|
||||
|
||||
mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
||||
//| def polyval(p: _ArrayLike, x: _ArrayLike) -> ulab.array:
|
||||
//| """Evaluate the polynomial p at the points x. x must be an array."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
||||
// TODO: return immediately, if o_p is not an iterable
|
||||
// TODO: there is a bug here: matrices won't work,
|
||||
// because there is a single iteration loop
|
||||
|
|
@ -64,7 +181,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
|||
uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p));
|
||||
mp_float_t *p = m_new(mp_float_t, plen);
|
||||
p_iterable = mp_getiter(o_p, &p_buf);
|
||||
uint16_t i = 0;
|
||||
uint8_t i = 0;
|
||||
while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
p[i] = mp_obj_get_float(p_item);
|
||||
i++;
|
||||
|
|
@ -85,123 +202,10 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
|||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
|
||||
|
||||
mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
|
||||
if((n_args != 2) && (n_args != 3)) {
|
||||
mp_raise_ValueError(translate("number of arguments must be 2, or 3"));
|
||||
}
|
||||
if(!object_is_nditerable(args[0])) {
|
||||
mp_raise_ValueError(translate("input data must be an iterable"));
|
||||
}
|
||||
uint16_t lenx = 0, leny = 0;
|
||||
uint8_t deg = 0;
|
||||
mp_float_t *x, *XT, *y, *prod;
|
||||
|
||||
if(n_args == 2) { // only the y values are supplied
|
||||
// TODO: this is actually not enough: the first argument can very well be a matrix,
|
||||
// in which case we are between the rock and a hard place
|
||||
leny = (uint16_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"));
|
||||
}
|
||||
lenx = leny;
|
||||
x = m_new(mp_float_t, lenx); // assume uniformly spaced data points
|
||||
for(size_t i=0; i < lenx; i++) {
|
||||
x[i] = i;
|
||||
}
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[0]);
|
||||
} else if(n_args == 3) {
|
||||
lenx = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
leny = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
if(lenx != leny) {
|
||||
mp_raise_ValueError(translate("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"));
|
||||
}
|
||||
x = m_new(mp_float_t, lenx);
|
||||
fill_array_iterable(x, args[0]);
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[1]);
|
||||
}
|
||||
|
||||
// one could probably express X as a function of XT,
|
||||
// and thereby save RAM, because X is used only in the product
|
||||
XT = m_new(mp_float_t, (deg+1)*leny); // XT is a matrix of shape (deg+1, len) (rows, columns)
|
||||
for(uint8_t i=0; i < leny; i++) { // column index
|
||||
XT[i+0*lenx] = 1.0; // top row
|
||||
for(uint8_t j=1; j < deg+1; j++) { // row index
|
||||
XT[i+j*leny] = XT[i+(j-1)*leny]*x[i];
|
||||
}
|
||||
}
|
||||
|
||||
prod = m_new(mp_float_t, (deg+1)*(deg+1)); // the product matrix is of shape (deg+1, deg+1)
|
||||
mp_float_t sum;
|
||||
for(uint16_t i=0; i < deg+1; i++) { // column index
|
||||
for(uint16_t j=0; j < deg+1; j++) { // row index
|
||||
sum = 0.0;
|
||||
for(size_t k=0; k < lenx; k++) {
|
||||
// (j, k) * (k, i)
|
||||
// Note that the second matrix is simply the transpose of the first:
|
||||
// X(k, i) = XT(i, k) = XT[k*lenx+i]
|
||||
sum += XT[j*lenx+k]*XT[i*lenx+k]; // X[k*(deg+1)+i];
|
||||
}
|
||||
prod[j*(deg+1)+i] = sum;
|
||||
}
|
||||
}
|
||||
if(!linalg_invert_matrix(prod, deg+1)) {
|
||||
// Although X was a Vandermonde matrix, whose inverse is guaranteed to exist,
|
||||
// we bail out here, if prod couldn't be inverted: if the values in x are not all
|
||||
// distinct, prod is singular
|
||||
m_del(mp_float_t, XT, (deg+1)*lenx);
|
||||
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"));
|
||||
}
|
||||
// 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
|
||||
for(uint16_t i=0; i < deg+1; i++) { // row index
|
||||
sum = 0.0;
|
||||
for(uint16_t j=0; j < lenx; j++) { // column index
|
||||
sum += XT[i*lenx+j]*y[j];
|
||||
}
|
||||
x[i] = sum;
|
||||
}
|
||||
// XT is no longer needed
|
||||
m_del(mp_float_t, XT, (deg+1)*leny);
|
||||
|
||||
ndarray_obj_t *beta = create_new_ndarray(deg+1, 1, NDARRAY_FLOAT);
|
||||
mp_float_t *betav = (mp_float_t *)beta->array->items;
|
||||
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
|
||||
m_del(float, y, leny);
|
||||
|
||||
// now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
|
||||
for(uint16_t i=0; i < deg+1; i++) {
|
||||
sum = 0.0;
|
||||
for(uint16_t j=0; j < deg+1; j++) {
|
||||
sum += prod[i*(deg+1)+j]*x[j];
|
||||
}
|
||||
betav[i] = sum;
|
||||
}
|
||||
m_del(mp_float_t, x, lenx);
|
||||
m_del(mp_float_t, prod, (deg+1)*(deg+1));
|
||||
for(uint8_t i=0; i < (deg+1)/2; i++) {
|
||||
// We have to reverse the array, for the leading coefficient comes first.
|
||||
SWAP(mp_float_t, betav[i], betav[deg-i]);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(beta);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_rom_map_elem_t ulab_poly_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_poly) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyfit), (mp_obj_t)&poly_polyfit_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_poly_globals, ulab_poly_globals_table);
|
||||
|
|
@ -210,6 +214,5 @@ mp_obj_module_t ulab_poly_module = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_poly_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -12,7 +12,8 @@
|
|||
#ifndef _POLY_
|
||||
#define _POLY_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_POLY_MODULE
|
||||
|
||||
55
code/ulab.c
55
code/ulab.c
|
|
@ -21,30 +21,40 @@
|
|||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "ndarray_properties.h"
|
||||
#include "linalg.h"
|
||||
#include "vectorise.h"
|
||||
#include "poly.h"
|
||||
#include "fft.h"
|
||||
#include "filter.h"
|
||||
#include "numerical.h"
|
||||
#include "extras.h"
|
||||
#include "ulab_create.h"
|
||||
#include "approx/approx.h"
|
||||
#include "compare/compare.h"
|
||||
#include "numerical/numerical.h"
|
||||
#include "fft/fft.h"
|
||||
#include "filter/filter.h"
|
||||
#include "linalg/linalg.h"
|
||||
#include "poly/poly.h"
|
||||
#include "user/user.h"
|
||||
#include "vector/vectorise.h"
|
||||
|
||||
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, "0.34.0");
|
||||
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, "0.54.5");
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&ndarray_transpose_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&ndarray_size_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_itemsize), MP_ROM_PTR(&ndarray_itemsize_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) },
|
||||
#if CIRCUITPY
|
||||
{ MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t ulab_ndarray_type = {
|
||||
{ &mp_type_type },
|
||||
#if defined(MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) && defined(MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)
|
||||
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST,
|
||||
#endif
|
||||
.name = MP_QSTR_ndarray,
|
||||
.print = ndarray_print,
|
||||
.make_new = ndarray_make_new,
|
||||
|
|
@ -56,11 +66,17 @@ const mp_obj_type_t ulab_ndarray_type = {
|
|||
.locals_dict = (mp_obj_dict_t*)&ulab_ndarray_locals_dict,
|
||||
};
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_map_elem_t ulab_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ulab) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_printoptions), (mp_obj_t)&ndarray_set_printoptions_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_printoptions), (mp_obj_t)&ndarray_get_printoptions_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_array), (mp_obj_t)&ulab_ndarray_type },
|
||||
{ MP_ROM_QSTR(MP_QSTR_arange), (mp_obj_t)&create_arange_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_eye), (mp_obj_t)&create_eye_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_linspace), (mp_obj_t)&create_linspace_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ones), (mp_obj_t)&create_ones_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_zeros), (mp_obj_t)&create_zeros_obj },
|
||||
#if ULAB_LINALG_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) },
|
||||
#endif
|
||||
|
|
@ -79,8 +95,14 @@ STATIC const mp_map_elem_t ulab_globals_table[] = {
|
|||
#if ULAB_FILTER_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&ulab_filter_module) },
|
||||
#endif
|
||||
#if ULAB_EXTRAS_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_extras), MP_ROM_PTR(&ulab_extras_module) },
|
||||
#if ULAB_COMPARE_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_compare), MP_ROM_PTR(&ulab_compare_module) },
|
||||
#endif
|
||||
#if ULAB_APPROX_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_approx), MP_ROM_PTR(&ulab_approx_module) },
|
||||
#endif
|
||||
#if ULAB_USER_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_user), MP_ROM_PTR(&ulab_user_module) },
|
||||
#endif
|
||||
// class constants
|
||||
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
|
||||
|
|
@ -95,10 +117,13 @@ STATIC MP_DEFINE_CONST_DICT (
|
|||
ulab_globals_table
|
||||
);
|
||||
|
||||
mp_obj_module_t ulab_user_cmodule = {
|
||||
#ifdef OPENMV
|
||||
const struct _mp_obj_module_t ulab_user_cmodule = {
|
||||
#else
|
||||
const mp_obj_module_t ulab_user_cmodule = {
|
||||
#endif
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED);
|
||||
#endif
|
||||
|
|
|
|||
39
code/ulab.h
39
code/ulab.h
|
|
@ -12,25 +12,52 @@
|
|||
#ifndef __ULAB__
|
||||
#define __ULAB__
|
||||
|
||||
// vectorise (all functions) takes approx. 3 kB of flash space
|
||||
// the create module is always included
|
||||
#define ULAB_CREATE_MODULE (1)
|
||||
|
||||
// vectorise (all functions) takes approx. 6 kB of flash space
|
||||
#ifndef ULAB_VECTORISE_MODULE
|
||||
#define ULAB_VECTORISE_MODULE (1)
|
||||
#endif
|
||||
|
||||
// linalg adds around 6 kB
|
||||
// linalg adds around 6 kB to the firmware
|
||||
#ifndef ULAB_LINALG_MODULE
|
||||
#define ULAB_LINALG_MODULE (1)
|
||||
#endif
|
||||
|
||||
// poly is approx. 2.5 kB
|
||||
// poly requires approx. 2.5 kB
|
||||
#ifndef ULAB_POLY_MODULE
|
||||
#define ULAB_POLY_MODULE (1)
|
||||
#endif
|
||||
|
||||
// numerical is about 12 kB
|
||||
#ifndef ULAB_NUMERICAL_MODULE
|
||||
#define ULAB_NUMERICAL_MODULE (1)
|
||||
#endif
|
||||
|
||||
// FFT costs about 2 kB of flash space
|
||||
#ifndef ULAB_FFT_MODULE
|
||||
#define ULAB_FFT_MODULE (1)
|
||||
#endif
|
||||
|
||||
// the filter module takes about 1 kB of flash space
|
||||
// the filter module occupies about 1 kB of flash space
|
||||
#ifndef ULAB_FILTER_MODULE
|
||||
#define ULAB_FILTER_MODULE (1)
|
||||
#endif
|
||||
|
||||
// user-defined modules
|
||||
#define ULAB_EXTRAS_MODULE (0)
|
||||
// the compare module consumes about 4 kB of flash space
|
||||
#ifndef ULAB_COMPARE_MODULE
|
||||
#define ULAB_COMPARE_MODULE (1)
|
||||
#endif
|
||||
|
||||
// the approx module consumes about 4.5 kB of flash space
|
||||
#ifndef ULAB_APPROX_MODULE
|
||||
#define ULAB_APPROX_MODULE (1)
|
||||
#endif
|
||||
|
||||
// user-defined module
|
||||
#ifndef ULAB_USER_MODULE
|
||||
#define ULAB_USER_MODULE (1)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
281
code/ulab_create.c
Normal file
281
code/ulab_create.c
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "ulab_create.h"
|
||||
|
||||
static mp_obj_t create_zeros_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t kind) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} } ,
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
uint8_t dtype = args[1].u_int;
|
||||
if(!MP_OBJ_IS_INT(args[0].u_obj) && !MP_OBJ_IS_TYPE(args[0].u_obj, &mp_type_tuple)) {
|
||||
mp_raise_TypeError(translate("input argument must be an integer or a 2-tuple"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
if(MP_OBJ_IS_INT(args[0].u_obj)) {
|
||||
size_t n = mp_obj_get_int(args[0].u_obj);
|
||||
ndarray = create_new_ndarray(1, n, dtype);
|
||||
} else if(MP_OBJ_IS_TYPE(args[0].u_obj, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(tuple->len != 2) {
|
||||
mp_raise_TypeError(translate("input argument must be an integer or a 2-tuple"));
|
||||
}
|
||||
ndarray = create_new_ndarray(mp_obj_get_int(tuple->items[0]),
|
||||
mp_obj_get_int(tuple->items[1]), dtype);
|
||||
}
|
||||
if(kind == 1) {
|
||||
mp_obj_t one = mp_obj_new_int(1);
|
||||
for(size_t i=0; i < ndarray->array->len; i++) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, i, one);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
STATIC ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, size_t len, uint8_t typecode) {
|
||||
mp_float_t value = start;
|
||||
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(1, len, typecode);
|
||||
if(typecode == NDARRAY_UINT8) {
|
||||
uint8_t *array = (uint8_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (uint8_t)value;
|
||||
} else if(typecode == NDARRAY_INT8) {
|
||||
int8_t *array = (int8_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (int8_t)value;
|
||||
} else if(typecode == NDARRAY_UINT16) {
|
||||
uint16_t *array = (uint16_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (uint16_t)value;
|
||||
} else if(typecode == NDARRAY_INT16) {
|
||||
int16_t *array = (int16_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (int16_t)value;
|
||||
} else {
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = value;
|
||||
}
|
||||
return ndarray;
|
||||
}
|
||||
|
||||
//| @overload
|
||||
//| def arange(stop: _float, step: _float = 1, dtype: _DType = float) -> array: ...
|
||||
//| @overload
|
||||
//| def arange(start: _float, stop: _float, step: _float = 1, dtype: _DType = float) -> array:
|
||||
//| """
|
||||
//| .. param: start
|
||||
//| First value in the array, optional, defaults to 0
|
||||
//| .. param: stop
|
||||
//| Final value in the array
|
||||
//| .. param: step
|
||||
//| Difference between consecutive elements, optional, defaults to 1.0
|
||||
//| .. param: dtype
|
||||
//| Type of values in the array
|
||||
//|
|
||||
//| Return a new 1-D array with elements ranging from ``start`` to ``stop``, with step size ``step``."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED| MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
};
|
||||
|
||||
mp_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);
|
||||
uint8_t typecode = NDARRAY_FLOAT;
|
||||
mp_float_t start, stop, step;
|
||||
if(n_args == 1) {
|
||||
start = 0.0;
|
||||
stop = mp_obj_get_float(args[0].u_obj);
|
||||
step = 1.0;
|
||||
if(mp_obj_is_int(args[0].u_obj)) typecode = NDARRAY_INT16;
|
||||
} else if(n_args == 2) {
|
||||
start = mp_obj_get_float(args[0].u_obj);
|
||||
stop = mp_obj_get_float(args[1].u_obj);
|
||||
step = 1.0;
|
||||
if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj)) typecode = NDARRAY_INT16;
|
||||
} else if(n_args == 3) {
|
||||
start = mp_obj_get_float(args[0].u_obj);
|
||||
stop = mp_obj_get_float(args[1].u_obj);
|
||||
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)) typecode = NDARRAY_INT16;
|
||||
} else {
|
||||
mp_raise_TypeError(translate("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)) {
|
||||
typecode = NDARRAY_FLOAT;
|
||||
}
|
||||
if(args[3].u_obj != mp_const_none) {
|
||||
typecode = (uint8_t)mp_obj_get_int(args[3].u_obj);
|
||||
}
|
||||
ndarray_obj_t *ndarray;
|
||||
if((stop - start)/step < 0) {
|
||||
ndarray = create_new_ndarray(0, 0, typecode);
|
||||
} else {
|
||||
size_t len = (size_t)(MICROPY_FLOAT_C_FUN(ceil)((stop - start)/step));
|
||||
ndarray = create_linspace_arange(start, step, len, typecode);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(create_arange_obj, 1, create_arange);
|
||||
|
||||
//| def eye(size: int, *, dtype: _DType = float) -> array:
|
||||
//| """Return a new square array of size, with the diagonal elements set to 1
|
||||
//| and the other elements set to 0."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t create_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
size_t n = args[0].u_int, m;
|
||||
size_t k = args[2].u_int > 0 ? (size_t)args[2].u_int : (size_t)(-args[2].u_int);
|
||||
uint8_t dtype = args[3].u_int;
|
||||
if(args[1].u_rom_obj == mp_const_none) {
|
||||
m = n;
|
||||
} else {
|
||||
m = mp_obj_get_int(args[1].u_rom_obj);
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(m, n, dtype);
|
||||
mp_obj_t one = mp_obj_new_int(1);
|
||||
size_t i = 0;
|
||||
if(args[2].u_int >= 0) {
|
||||
while(k < n) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, i*n+k, one);
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
while(k < m) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, k*n+i, one);
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(create_eye_obj, 0, create_eye);
|
||||
|
||||
//| def linspace(
|
||||
//| start: _float,
|
||||
//| stop: _float,
|
||||
//| *,
|
||||
//| dtype: _DType = float,
|
||||
//| num: int = 50,
|
||||
//| endpoint: bool = True
|
||||
//| ) -> array:
|
||||
//| """
|
||||
//| .. param: start
|
||||
//| First value in the array
|
||||
//| .. param: stop
|
||||
//| Final value in the array
|
||||
//| .. param int: num
|
||||
//| Count of values in the array
|
||||
//| .. param: dtype
|
||||
//| Type of values in the array
|
||||
//| .. param bool: endpoint
|
||||
//| Whether the ``stop`` value is included. Note that even when
|
||||
//| endpoint=True, the exact ``stop`` value may not be included due to the
|
||||
//| inaccuracy of floating point arithmetic.
|
||||
//|
|
||||
//| Return a new 1-D array with ``num`` elements ranging from ``start`` to ``stop`` linearly."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
|
||||
{ MP_QSTR_num, MP_ARG_INT, {.u_int = 50} },
|
||||
{ MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_true} },
|
||||
{ MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_false} },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(args[2].u_int < 2) {
|
||||
mp_raise_ValueError(translate("number of points must be at least 2"));
|
||||
}
|
||||
size_t len = (size_t)args[2].u_int;
|
||||
mp_float_t start, step;
|
||||
start = mp_obj_get_float(args[0].u_obj);
|
||||
uint8_t typecode = args[5].u_int;
|
||||
if(args[3].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj)-start)/(len-1);
|
||||
else step = (mp_obj_get_float(args[1].u_obj)-start)/len;
|
||||
ndarray_obj_t *ndarray = create_linspace_arange(start, step, len, typecode);
|
||||
if(args[4].u_obj == mp_const_false) {
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = ndarray;
|
||||
tuple[1] = mp_obj_new_float(step);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace);
|
||||
|
||||
//| def ones(shape: Union[int, Tuple[int, int]], *, dtype: _DType = float) -> array:
|
||||
//| """
|
||||
//| .. param: shape
|
||||
//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array)
|
||||
//| .. param: dtype
|
||||
//| Type of values in the array
|
||||
//|
|
||||
//| Return a new array of the given shape with all elements set to 1."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return create_zeros_ones(n_args, pos_args, kw_args, 1);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones);
|
||||
|
||||
//| def zeros(shape: Union[int, Tuple[int, int]], *, dtype: _DType = float) -> array:
|
||||
//| """
|
||||
//| .. param: shape
|
||||
//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array)
|
||||
//| .. param: dtype
|
||||
//| Type of values in the array
|
||||
//|
|
||||
//| Return a new array of the given shape with all elements set to 0."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t create_zeros(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return create_zeros_ones(n_args, pos_args, kw_args, 0);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(create_zeros_obj, 0, create_zeros);
|
||||
30
code/ulab_create.h
Normal file
30
code/ulab_create.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _CREATE_
|
||||
#define _CREATE_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
|
||||
mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t create_eye(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t create_linspace(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_eye_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_linspace_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_arange_obj);
|
||||
|
||||
#endif
|
||||
44
code/user/user.c
Normal file
44
code/user/user.c
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "user.h"
|
||||
|
||||
#if ULAB_USER_MODULE
|
||||
|
||||
//| """This module should hold arbitrary user-defined functions."""
|
||||
//|
|
||||
|
||||
static mp_obj_t user_dummy(mp_obj_t arg) {
|
||||
mp_float_t value = mp_obj_get_float(arg);
|
||||
return mp_obj_new_float(2*value);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(user_dummy_obj, user_dummy);
|
||||
|
||||
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_dummy), (mp_obj_t)&user_dummy_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_user_globals, ulab_user_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_user_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -9,15 +9,15 @@
|
|||
* Copyright (c) 2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _EXTRA_
|
||||
#define _EXTRA_
|
||||
#ifndef _USER_
|
||||
#define _USER_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_EXTRAS_MODULE
|
||||
#if ULAB_USER_MODULE
|
||||
|
||||
mp_obj_module_t ulab_extras_module;
|
||||
extern mp_obj_module_t ulab_user_module;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
495
code/vector/vectorise.c
Normal file
495
code/vector/vectorise.c
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
#include "vectorise.h"
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
#if ULAB_VECTORISE_MODULE
|
||||
|
||||
//| """Element-by-element functions
|
||||
//|
|
||||
//| These functions can operate on numbers, 1-D iterables, 1-D arrays, or 2-D arrays by
|
||||
//| applying the function to every element in the array. This is typically
|
||||
//| much more efficient than expressing the same operation as a Python loop."""
|
||||
//|
|
||||
//| from ulab import _DType, _ArrayLike
|
||||
//|
|
||||
|
||||
static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
|
||||
// Return a single value, if o_in is not iterable
|
||||
if(mp_obj_is_float(o_in) || MP_OBJ_IS_INT(o_in)) {
|
||||
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
|
||||
}
|
||||
mp_float_t x;
|
||||
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(source->m, source->n, NDARRAY_FLOAT);
|
||||
mp_float_t *dataout = (mp_float_t *)ndarray->array->items;
|
||||
if(source->array->typecode == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_INT8) {
|
||||
ITERATE_VECTOR(int8_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_UINT16) {
|
||||
ITERATE_VECTOR(uint16_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_INT16) {
|
||||
ITERATE_VECTOR(int16_t, source, dataout);
|
||||
} else {
|
||||
ITERATE_VECTOR(mp_float_t, source, dataout);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(MP_OBJ_IS_TYPE(o_in, &mp_type_tuple) || MP_OBJ_IS_TYPE(o_in, &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(o_in, &mp_type_range)) { // i.e., the input is a generic iterable
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
ndarray_obj_t *out = create_new_ndarray(1, o->len, NDARRAY_FLOAT);
|
||||
mp_float_t *dataout = (mp_float_t *)out->array->items;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(o_in, &iter_buf);
|
||||
size_t i=0;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
x = mp_obj_get_float(item);
|
||||
dataout[i++] = f(x);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
//| def acos(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(acos, acos);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos);
|
||||
|
||||
//| def acosh(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse hyperbolic cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(acosh, acosh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh);
|
||||
|
||||
|
||||
//| def asin(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(asin, asin);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin);
|
||||
|
||||
//| def asinh(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse hyperbolic sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(asinh, asinh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh);
|
||||
|
||||
|
||||
//| def around(a: _ArrayLike, *, decimals: int = 0) -> ulab.array:
|
||||
//| """Returns a new float array in which each element is rounded to
|
||||
//| ``decimals`` places."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
{ MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } }
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(translate("first argument must be an ndarray"));
|
||||
}
|
||||
int8_t n = args[1].u_int;
|
||||
mp_float_t mul = MICROPY_FLOAT_C_FUN(pow)(10.0, n);
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
ndarray_obj_t *result = create_new_ndarray(ndarray->m, ndarray->n, NDARRAY_FLOAT);
|
||||
mp_float_t *array = (mp_float_t *)result->array->items;
|
||||
for(size_t i=0; i < ndarray->array->len; i++) {
|
||||
mp_float_t f = ndarray_get_float_value(ndarray->array->items, ndarray->array->typecode, i);
|
||||
*array++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(result);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_around_obj, 1, vectorise_around);
|
||||
|
||||
//| def atan(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse tangent function; the return values are in the
|
||||
//| range [-pi/2,pi/2]."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(atan, atan);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan);
|
||||
|
||||
//| def atan2(ya: _ArrayLike, xa: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse tangent function of y/x; the return values are in
|
||||
//| the range [-pi, pi]."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t vectorise_arctan2(mp_obj_t x, mp_obj_t y) {
|
||||
// the function is implemented for scalars and ndarrays only, with partial
|
||||
// broadcasting: arguments must be either scalars, or ndarrays of equal size/shape
|
||||
if(!(MP_OBJ_IS_INT(x) || mp_obj_is_float(x) || MP_OBJ_IS_TYPE(x, &ulab_ndarray_type)) &&
|
||||
!(MP_OBJ_IS_INT(y) || mp_obj_is_float(y) || MP_OBJ_IS_TYPE(y, &ulab_ndarray_type))) {
|
||||
mp_raise_TypeError(translate("arctan2 is implemented for scalars and ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *ndarray_x, *ndarray_y;
|
||||
if(MP_OBJ_IS_INT(x) || mp_obj_is_float(x)) {
|
||||
ndarray_x = create_new_ndarray(1, 1, NDARRAY_FLOAT);
|
||||
mp_float_t *array_x = (mp_float_t *)ndarray_x->array->items;
|
||||
*array_x = mp_obj_get_float(x);
|
||||
} else {
|
||||
ndarray_x = MP_OBJ_TO_PTR(x);
|
||||
}
|
||||
if(MP_OBJ_IS_INT(y) || mp_obj_is_float(y)) {
|
||||
ndarray_y = create_new_ndarray(1, 1, NDARRAY_FLOAT);
|
||||
mp_float_t *array_y = (mp_float_t *)ndarray_y->array->items;
|
||||
*array_y = mp_obj_get_float(y);
|
||||
} else {
|
||||
ndarray_y = MP_OBJ_TO_PTR(y);
|
||||
}
|
||||
// check, whether partial broadcasting is possible here
|
||||
if((ndarray_x->m != ndarray_y->m) || (ndarray_x->n != ndarray_y->n)) {
|
||||
if((ndarray_x->array->len != 1) && (ndarray_y->array->len != 1)) {
|
||||
mp_raise_ValueError(translate("operands could not be broadcast together"));
|
||||
}
|
||||
}
|
||||
size_t xinc = 0, yinc = 0;
|
||||
size_t m = MAX(ndarray_x->m, ndarray_y->m);
|
||||
size_t n = MAX(ndarray_x->n, ndarray_y->n);
|
||||
size_t len = MAX(ndarray_x->array->len, ndarray_y->array->len);
|
||||
if(ndarray_x->array->len != 1) {
|
||||
xinc = 1;
|
||||
}
|
||||
if(ndarray_y->array->len != 1) {
|
||||
yinc = 1;
|
||||
}
|
||||
size_t posx = 0, posy = 0;
|
||||
ndarray_obj_t *result = create_new_ndarray(m, n, NDARRAY_FLOAT);
|
||||
mp_float_t *array_r = (mp_float_t *)result->array->items;
|
||||
for(size_t i=0; i < len; i++) {
|
||||
mp_float_t value_x = ndarray_get_float_value(ndarray_x->array->items, ndarray_x->array->typecode, posx);
|
||||
mp_float_t value_y = ndarray_get_float_value(ndarray_y->array->items, ndarray_y->array->typecode, posy);
|
||||
*array_r++ = MICROPY_FLOAT_C_FUN(atan2)(value_x, value_y);
|
||||
posx += xinc;
|
||||
posy += yinc;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(result);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(vectorise_arctan2_obj, vectorise_arctan2);
|
||||
|
||||
|
||||
|
||||
//| def atanh(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the inverse hyperbolic tangent function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(atanh, atanh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh);
|
||||
|
||||
//| def ceil(a: _ArrayLike) -> ulab.array:
|
||||
//| """Rounds numbers up to the next whole number"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(ceil, ceil);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil);
|
||||
|
||||
//| def cos(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(cos, cos);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos);
|
||||
|
||||
//| def cosh(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the hyperbolic cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(cosh, cosh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cosh_obj, vectorise_cosh);
|
||||
|
||||
//| def erf(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the error function, which has applications in statistics"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(erf, erf);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf);
|
||||
|
||||
//| def erfc(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the complementary error function, which has applications in statistics"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(erfc, erfc);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc);
|
||||
|
||||
//| def exp(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the exponent function."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(exp, exp);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp);
|
||||
|
||||
//| def expm1(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(expm1, expm1);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1);
|
||||
|
||||
//| def floor(a: _ArrayLike) -> ulab.array:
|
||||
//| """Rounds numbers up to the next whole number"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(floor, floor);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor);
|
||||
|
||||
//| def gamma(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the gamma function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(gamma, tgamma);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma);
|
||||
|
||||
//| def lgamma(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the natural log of the gamma function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(lgamma, lgamma);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma);
|
||||
|
||||
//| def log(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the natural log"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(log, log);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log);
|
||||
|
||||
//| def log10(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the log base 10"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(log10, log10);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10);
|
||||
|
||||
//| def log2(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the log base 2"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(log2, log2);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2);
|
||||
|
||||
//| def sin(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(sin, sin);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin);
|
||||
|
||||
//| def sinh(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the hyperbolic sine"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(sinh, sinh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sinh_obj, vectorise_sinh);
|
||||
|
||||
//| def sqrt(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the square root"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(sqrt, sqrt);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sqrt_obj, vectorise_sqrt);
|
||||
|
||||
//| def tan(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the tangent"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(tan, tan);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan);
|
||||
|
||||
//| def tanh(a: _ArrayLike) -> ulab.array:
|
||||
//| """Computes the hyperbolic tangent"""
|
||||
//| ...
|
||||
|
||||
MATH_FUN_1(tanh, tanh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tanh_obj, vectorise_tanh);
|
||||
|
||||
static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void) n_args;
|
||||
(void) n_kw;
|
||||
vectorized_function_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_obj_t avalue[1];
|
||||
mp_obj_t fvalue;
|
||||
if(MP_OBJ_IS_TYPE(args[0], &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]);
|
||||
ndarray_obj_t *target = create_new_ndarray(source->m, source->n, self->otypes);
|
||||
for(size_t i=0; i < source->array->len; i++) {
|
||||
avalue[0] = mp_binary_get_val_array(source->array->typecode, source->array->items, i);
|
||||
fvalue = self->type->call(self->fun, 1, 0, avalue);
|
||||
mp_binary_set_val_array(self->otypes, target->array->items, i, fvalue);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
} else if(MP_OBJ_IS_TYPE(args[0], &mp_type_tuple) || MP_OBJ_IS_TYPE(args[0], &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(args[0], &mp_type_range)) { // i.e., the input is a generic iterable
|
||||
size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
ndarray_obj_t *target = create_new_ndarray(1, len, self->otypes);
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
|
||||
size_t i=0;
|
||||
while ((avalue[0] = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
fvalue = self->type->call(self->fun, 1, 0, avalue);
|
||||
mp_binary_set_val_array(self->otypes, target->array->items, i, fvalue);
|
||||
i++;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
} else if(mp_obj_is_int(args[0]) || mp_obj_is_float(args[0])) {
|
||||
ndarray_obj_t *target = create_new_ndarray(1, 1, self->otypes);
|
||||
fvalue = self->type->call(self->fun, 1, 0, args);
|
||||
mp_binary_set_val_array(self->otypes, target->array->items, 0, fvalue);
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
} else {
|
||||
mp_raise_ValueError(translate("wrong input type"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
const mp_obj_type_t vectorise_function_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_,
|
||||
.call = vectorise_vectorized_function_call,
|
||||
};
|
||||
|
||||
//| def vectorize(
|
||||
//| f: Union[Callable[[int], float], Callable[[float], float]],
|
||||
//| *,
|
||||
//| otypes: Optional[_DType] = None
|
||||
//| ) -> Callable[[_ArrayLike], ulab.array]:
|
||||
//| """
|
||||
//| :param callable f: The function to wrap
|
||||
//| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float.
|
||||
//|
|
||||
//| Wrap a Python function ``f`` so that it can be applied to arrays.
|
||||
//| The callable must return only values of the types specified by ``otypes``, or the result is undefined."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t vectorise_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
{ MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj);
|
||||
if(type->call == NULL) {
|
||||
mp_raise_TypeError(translate("first argument must be a callable"));
|
||||
}
|
||||
mp_obj_t _otypes = args[1].u_obj;
|
||||
uint8_t otypes = NDARRAY_FLOAT;
|
||||
if(_otypes == mp_const_none) {
|
||||
// TODO: is this what numpy does?
|
||||
otypes = NDARRAY_FLOAT;
|
||||
} else if(mp_obj_is_int(_otypes)) {
|
||||
otypes = mp_obj_get_int(_otypes);
|
||||
if(otypes != NDARRAY_FLOAT && otypes != NDARRAY_UINT8 && otypes != NDARRAY_INT8 &&
|
||||
otypes != NDARRAY_UINT16 && otypes != NDARRAY_INT16) {
|
||||
mp_raise_ValueError(translate("wrong output type"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
mp_raise_ValueError(translate("wrong output type"));
|
||||
}
|
||||
vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t);
|
||||
function->base.type = &vectorise_function_type;
|
||||
function->otypes = otypes;
|
||||
function->fun = args[0].u_obj;
|
||||
function->type = type;
|
||||
return MP_OBJ_FROM_PTR(function);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_vectorize_obj, 1, vectorise_vectorize);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_vectorise_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_vector) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vectorise_acos_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vectorise_acosh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_arctan2), (mp_obj_t)&vectorise_arctan2_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_around), (mp_obj_t)&vectorise_around_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vectorise_asin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vectorise_asinh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vectorise_atan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vectorise_atanh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vectorise_ceil_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vectorise_cos_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vectorise_erf_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vectorise_erfc_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vectorise_exp_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vectorise_expm1_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vectorise_floor_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vectorise_gamma_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_lgamma), (mp_obj_t)&vectorise_lgamma_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vectorise_log_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vectorise_log10_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vectorise_log2_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vectorise_sin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vectorise_sinh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vectorise_sqrt_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vectorise_tan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vectorise_tanh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vectorise_vectorize_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_vectorise_globals, ulab_vectorise_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_vectorise_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_vectorise_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -12,12 +12,19 @@
|
|||
#ifndef _VECTORISE_
|
||||
#define _VECTORISE_
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_VECTORISE_MODULE
|
||||
|
||||
mp_obj_module_t ulab_vectorise_module;
|
||||
typedef struct _vectorized_function_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t otypes;
|
||||
mp_obj_t fun;
|
||||
const mp_obj_type_t *type;
|
||||
} vectorized_function_obj_t;
|
||||
|
||||
extern mp_obj_module_t ulab_vectorise_module;
|
||||
|
||||
#define ITERATE_VECTOR(type, source, out) do {\
|
||||
type *input = (type *)(source)->array->items;\
|
||||
|
|
@ -27,7 +34,7 @@ mp_obj_module_t ulab_vectorise_module;
|
|||
} while(0)
|
||||
|
||||
#define MATH_FUN_1(py_name, c_name) \
|
||||
mp_obj_t vectorise_ ## py_name(mp_obj_t x_obj) { \
|
||||
static mp_obj_t vectorise_ ## py_name(mp_obj_t x_obj) { \
|
||||
return vectorise_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
|
||||
}
|
||||
|
||||
174
code/vectorise.c
174
code/vectorise.c
|
|
@ -1,174 +0,0 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
#include "vectorise.h"
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
#if ULAB_VECTORISE_MODULE
|
||||
mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
|
||||
// Return a single value, if o_in is not iterable
|
||||
if(mp_obj_is_float(o_in) || MP_OBJ_IS_INT(o_in)) {
|
||||
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
|
||||
}
|
||||
mp_float_t x;
|
||||
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(source->m, source->n, NDARRAY_FLOAT);
|
||||
mp_float_t *dataout = (mp_float_t *)ndarray->array->items;
|
||||
if(source->array->typecode == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_INT8) {
|
||||
ITERATE_VECTOR(int8_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_UINT16) {
|
||||
ITERATE_VECTOR(uint16_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_INT16) {
|
||||
ITERATE_VECTOR(int16_t, source, dataout);
|
||||
} else {
|
||||
ITERATE_VECTOR(mp_float_t, source, dataout);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(MP_OBJ_IS_TYPE(o_in, &mp_type_tuple) || MP_OBJ_IS_TYPE(o_in, &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(o_in, &mp_type_range)) { // i.e., the input is a generic iterable
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
ndarray_obj_t *out = create_new_ndarray(1, o->len, NDARRAY_FLOAT);
|
||||
mp_float_t *dataout = (mp_float_t *)out->array->items;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(o_in, &iter_buf);
|
||||
size_t i=0;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
x = mp_obj_get_float(item);
|
||||
dataout[i++] = f(x);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
||||
MATH_FUN_1(acos, acos);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos);
|
||||
|
||||
MATH_FUN_1(acosh, acosh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh);
|
||||
|
||||
MATH_FUN_1(asin, asin);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin);
|
||||
|
||||
MATH_FUN_1(asinh, asinh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh);
|
||||
|
||||
MATH_FUN_1(atan, atan);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan);
|
||||
|
||||
MATH_FUN_1(atanh, atanh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh);
|
||||
|
||||
MATH_FUN_1(ceil, ceil);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil);
|
||||
|
||||
MATH_FUN_1(cos, cos);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos);
|
||||
|
||||
MATH_FUN_1(cosh, cosh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cosh_obj, vectorise_cosh);
|
||||
|
||||
MATH_FUN_1(erf, erf);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf);
|
||||
|
||||
MATH_FUN_1(erfc, erfc);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc);
|
||||
|
||||
MATH_FUN_1(exp, exp);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp);
|
||||
|
||||
MATH_FUN_1(expm1, expm1);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1);
|
||||
|
||||
MATH_FUN_1(floor, floor);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor);
|
||||
|
||||
MATH_FUN_1(gamma, tgamma);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma);
|
||||
|
||||
MATH_FUN_1(lgamma, lgamma);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma);
|
||||
|
||||
MATH_FUN_1(log, log);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log);
|
||||
|
||||
MATH_FUN_1(log10, log10);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10);
|
||||
|
||||
MATH_FUN_1(log2, log2);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2);
|
||||
|
||||
MATH_FUN_1(sin, sin);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin);
|
||||
|
||||
MATH_FUN_1(sinh, sinh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sinh_obj, vectorise_sinh);
|
||||
|
||||
MATH_FUN_1(sqrt, sqrt);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sqrt_obj, vectorise_sqrt);
|
||||
|
||||
MATH_FUN_1(tan, tan);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan);
|
||||
|
||||
MATH_FUN_1(tanh, tanh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tanh_obj, vectorise_tanh);
|
||||
|
||||
#if !CIRCUITPY
|
||||
STATIC const mp_rom_map_elem_t ulab_vectorise_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_vector) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vectorise_acos_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vectorise_acosh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vectorise_asin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vectorise_asinh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vectorise_atan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vectorise_atanh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vectorise_ceil_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vectorise_cos_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vectorise_erf_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vectorise_erfc_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vectorise_exp_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vectorise_expm1_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vectorise_floor_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vectorise_gamma_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_lgamma), (mp_obj_t)&vectorise_lgamma_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vectorise_log_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vectorise_log10_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vectorise_log2_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vectorise_sin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vectorise_sinh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vectorise_sqrt_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vectorise_tan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vectorise_tanh_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_vectorise_globals, ulab_vectorise_globals_table);
|
||||
|
||||
mp_obj_module_t ulab_vectorise_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_vectorise_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -18,11 +18,11 @@
|
|||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'micropython-ulab'
|
||||
copyright = '2019, Zoltán Vörös'
|
||||
copyright = '2019-2020, Zoltán Vörös'
|
||||
author = 'Zoltán Vörös'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.32'
|
||||
release = '0.54.2'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,222 @@
|
|||
Sat, 25 Oct 2020
|
||||
|
||||
version 0.54.5
|
||||
|
||||
wrong type in slices raise TypeError exception
|
||||
|
||||
Fri, 23 Oct 2020
|
||||
|
||||
version 0.54.4
|
||||
|
||||
fixed indexing error in slices
|
||||
|
||||
Mon, 17 Aug 2020
|
||||
|
||||
version 0.54.3
|
||||
|
||||
fixed small error in linalg
|
||||
|
||||
Mon, 03 Aug 2020
|
||||
|
||||
version 0.54.2
|
||||
|
||||
argsort throws an error, if the array is longer than 65535
|
||||
|
||||
Wed, 29 Jul 2020
|
||||
|
||||
version 0.54.1
|
||||
|
||||
changed to size_t for the length of arrays
|
||||
|
||||
Thu, 23 Jul 2020
|
||||
|
||||
version 0.54.0
|
||||
|
||||
added norm to linalg
|
||||
|
||||
Wed, 22 Jul 2020
|
||||
|
||||
version 0.53.2
|
||||
|
||||
added circuitpython documentation stubs to the source files
|
||||
|
||||
Wed, 22 Jul 2020
|
||||
|
||||
version 0.53.1
|
||||
|
||||
fixed arange with negative steps
|
||||
|
||||
Mon, 20 Jul 2020
|
||||
|
||||
version 0.53.0
|
||||
|
||||
added arange to create.c
|
||||
|
||||
Thu, 16 Jul 2020
|
||||
|
||||
version 0.52.0
|
||||
|
||||
added trapz to approx
|
||||
|
||||
Mon, 29 Jun 2020
|
||||
|
||||
version 0.51.1
|
||||
|
||||
fixed argmin/argmax issue
|
||||
|
||||
Fri, 19 Jun 2020
|
||||
|
||||
version 0.51.0
|
||||
|
||||
add sosfilt to the filter sub-module
|
||||
|
||||
Fri, 12 Jun 2020
|
||||
|
||||
version 0.50.2
|
||||
|
||||
fixes compilation error in openmv
|
||||
|
||||
Mon, 1 Jun 2020
|
||||
|
||||
version 0.50.1
|
||||
|
||||
fixes error in numerical max/min
|
||||
|
||||
Mon, 18 May 2020
|
||||
|
||||
version 0.50.0
|
||||
|
||||
move interp to the approx sub-module
|
||||
|
||||
Wed, 06 May 2020
|
||||
|
||||
version 0.46.0
|
||||
|
||||
add curve_fit to the approx sub-module
|
||||
|
||||
version 0.44.0
|
||||
|
||||
add approx sub-module with newton, fmin, and bisect functions
|
||||
|
||||
Thu, 30 Apr 2020
|
||||
|
||||
version 0.44.0
|
||||
|
||||
add approx sub-module with newton, fmin, and bisect functions
|
||||
|
||||
Tue, 19 May 2020
|
||||
|
||||
version 0.46.1
|
||||
|
||||
fixed bad error in binary_op
|
||||
|
||||
Wed, 6 May 2020
|
||||
|
||||
version 0.46
|
||||
|
||||
added vectorisation of python functions
|
||||
|
||||
Sat, 2 May 2020
|
||||
|
||||
version 0.45.0
|
||||
|
||||
add equal/not_equal to the compare module
|
||||
|
||||
Tue, 21 Apr 2020
|
||||
|
||||
version 0.42.0
|
||||
|
||||
add minimum/maximum/clip functions
|
||||
|
||||
Mon, 20 Apr 2020
|
||||
|
||||
version 0.41.6
|
||||
|
||||
argument handling improvement in polyfit
|
||||
|
||||
Mon, 20 Apr 2020
|
||||
|
||||
version 0.41.5
|
||||
|
||||
fix compilation errors due to https://github.com/micropython/micropython/commit/30840ebc9925bb8ef025dbc2d5982b1bfeb75f1b
|
||||
|
||||
Sat, 18 Apr 2020
|
||||
|
||||
version 0.41.4
|
||||
|
||||
fix compilation error on hardware ports
|
||||
|
||||
Tue, 14 Apr 2020
|
||||
|
||||
version 0.41.3
|
||||
|
||||
fix indexing error in dot function
|
||||
|
||||
Thu, 9 Apr 2020
|
||||
|
||||
version 0.41.2
|
||||
|
||||
fix transpose function
|
||||
|
||||
Tue, 7 Apr 2020
|
||||
|
||||
version 0.41.2
|
||||
|
||||
fix discrepancy in argmin/argmax behaviour
|
||||
|
||||
Tue, 7 Apr 2020
|
||||
|
||||
version 0.41.1
|
||||
|
||||
fix error in argsort
|
||||
|
||||
Sat, 4 Apr 2020
|
||||
|
||||
version 0.41.0
|
||||
|
||||
implemented == and != binary operators
|
||||
|
||||
Fri, 3 Apr 2020
|
||||
|
||||
version 0.40.0
|
||||
|
||||
added trace to linalg
|
||||
|
||||
Thu, 2 Apr 2020
|
||||
|
||||
version 0.39.0
|
||||
|
||||
added the ** operator, and operand swapping in binary operators
|
||||
|
||||
Thu, 2 Apr 2020
|
||||
|
||||
version 0.38.1
|
||||
|
||||
added fast option, when initialising from ndarray_properties
|
||||
|
||||
Thu, 12 Mar 2020
|
||||
|
||||
version 0.38.0
|
||||
|
||||
added initialisation from ndarray, and the around function
|
||||
|
||||
Tue, 10 Mar 2020
|
||||
|
||||
version 0.37.0
|
||||
|
||||
added Cholesky decomposition to linalg.c
|
||||
|
||||
Thu, 27 Feb 2020
|
||||
|
||||
version 0.36.0
|
||||
|
||||
moved zeros, ones, eye and linspace into separate module (they are still bound at the top level)
|
||||
|
||||
Thu, 27 Feb 2020
|
||||
|
||||
version 0.35.0
|
||||
|
||||
Move zeros, ones back into top level ulab module
|
||||
|
||||
Tue, 18 Feb 2020
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
5125
docs/ulab.ipynb
5125
docs/ulab.ipynb
File diff suppressed because it is too large
Load diff
|
|
@ -1,2 +1,2 @@
|
|||
from ulab import linalg
|
||||
print(linalg.eye(3))
|
||||
import ulab
|
||||
print(ulab.eye(3))
|
||||
|
|
|
|||
62
tests/argminmax.py
Normal file
62
tests/argminmax.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import ulab
|
||||
|
||||
# Adapted from https://docs.python.org/3.8/library/itertools.html#itertools.permutations
|
||||
def permutations(iterable, r=None):
|
||||
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
|
||||
# permutations(range(3)) --> 012 021 102 120 201 210
|
||||
pool = tuple(iterable)
|
||||
n = len(pool)
|
||||
r = n if r is None else r
|
||||
if r > n:
|
||||
return
|
||||
indices = list(range(n))
|
||||
cycles = list(range(n, n-r, -1))
|
||||
yield tuple(pool[i] for i in indices[:r])
|
||||
while n:
|
||||
for i in reversed(range(r)):
|
||||
cycles[i] -= 1
|
||||
if cycles[i] == 0:
|
||||
indices[i:] = indices[i+1:] + indices[i:i+1]
|
||||
cycles[i] = n - i
|
||||
else:
|
||||
j = cycles[i]
|
||||
indices[i], indices[-j] = indices[-j], indices[i]
|
||||
yield tuple(pool[i] for i in indices[:r])
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
# Combinations expected to throw
|
||||
try:
|
||||
print(ulab.numerical.argmin([]))
|
||||
except ValueError:
|
||||
print("ValueError")
|
||||
|
||||
try:
|
||||
print(ulab.numerical.argmax([]))
|
||||
except ValueError:
|
||||
print("ValueError")
|
||||
|
||||
# Combinations expected to succeed
|
||||
print(ulab.numerical.argmin([1]))
|
||||
print(ulab.numerical.argmax([1]))
|
||||
print(ulab.numerical.argmin(ulab.array([1])))
|
||||
print(ulab.numerical.argmax(ulab.array([1])))
|
||||
|
||||
print()
|
||||
print("max tests")
|
||||
for p in permutations((100,200,300)):
|
||||
m1 = ulab.numerical.argmax(p)
|
||||
m2 = ulab.numerical.argmax(ulab.array(p))
|
||||
print(p, m1, m2)
|
||||
if m1 != m2 or p[m1] != max(p):
|
||||
print("FAIL", p, m1, m2, max(p))
|
||||
|
||||
print()
|
||||
print("min tests")
|
||||
for p in permutations((100,200,300)):
|
||||
m1 = ulab.numerical.argmin(p)
|
||||
m2 = ulab.numerical.argmin(ulab.array(p))
|
||||
print(p, m1, m2)
|
||||
if m1 != m2 or p[m1] != min(p):
|
||||
print("FAIL", p, m1, m2, min(p))
|
||||
22
tests/argminmax.py.exp
Normal file
22
tests/argminmax.py.exp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
ValueError
|
||||
ValueError
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
|
||||
max tests
|
||||
(100, 200, 300) 2 2
|
||||
(100, 300, 200) 1 1
|
||||
(200, 100, 300) 2 2
|
||||
(200, 300, 100) 1 1
|
||||
(300, 100, 200) 0 0
|
||||
(300, 200, 100) 0 0
|
||||
|
||||
min tests
|
||||
(100, 200, 300) 0 0
|
||||
(100, 300, 200) 0 0
|
||||
(200, 100, 300) 1 1
|
||||
(200, 300, 100) 2 2
|
||||
(300, 100, 200) 1 1
|
||||
(300, 200, 100) 2 2
|
||||
17
tests/cholesky.py
Normal file
17
tests/cholesky.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import ulab
|
||||
from ulab import linalg
|
||||
|
||||
a = ulab.array([[1, 2], [2, 5]])
|
||||
print(linalg.cholesky(a))
|
||||
|
||||
b = a = ulab.array([[25, 15, -5], [15, 18, 0], [-5, 0, 11]])
|
||||
print(linalg.cholesky(b))
|
||||
|
||||
c = ulab.array([[18, 22, 54, 42], [22, 70, 86, 62], [54, 86, 174, 134], [42, 62, 134, 106]])
|
||||
print(linalg.cholesky(c))
|
||||
|
||||
# this throw a ValueError exception
|
||||
d = ulab.array([[25, 15, -5], [15, 18, 0], [-5, 0, 1]])
|
||||
|
||||
|
||||
|
||||
9
tests/cholesky.py.exp
Normal file
9
tests/cholesky.py.exp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
array([[1.0, 0.0],
|
||||
[2.0, 1.0]], dtype=float)
|
||||
array([[5.0, 0.0, 0.0],
|
||||
[3.0, 3.0, 0.0],
|
||||
[-1.0, 1.0, 3.0]], dtype=float)
|
||||
array([[4.242640687119285, 0.0, 0.0, 0.0],
|
||||
[5.185449728701349, 6.565905201197403, 0.0, 0.0],
|
||||
[12.72792206135786, 3.046038495400855, 1.649742247909068, 0.0],
|
||||
[9.899494936611665, 1.624553864213789, 1.849711005231386, 1.392621247645583]], dtype=float)
|
||||
14
tests/compare.py
Normal file
14
tests/compare.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import ulab
|
||||
from ulab import compare
|
||||
|
||||
a = ulab.array([1, 2, 3, 4, 5], dtype=ulab.uint8)
|
||||
b = ulab.array([5, 4, 3, 2, 1], dtype=ulab.float)
|
||||
print(compare.minimum(a, b))
|
||||
print(compare.maximum(a, b))
|
||||
print(compare.maximum(1, 5.5))
|
||||
|
||||
a = ulab.array(range(9), dtype=ulab.uint8)
|
||||
print(compare.clip(a, 3, 7))
|
||||
|
||||
b = 3 * ulab.ones(len(a), dtype=ulab.float)
|
||||
print(compare.clip(a, b, 7))
|
||||
5
tests/compare.py.exp
Normal file
5
tests/compare.py.exp
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
array([1.0, 2.0, 3.0, 2.0, 1.0], dtype=float)
|
||||
array([5.0, 4.0, 3.0, 4.0, 5.0], dtype=float)
|
||||
5.5
|
||||
array([3, 3, 3, 3, 4, 5, 6, 7, 7], dtype=uint8)
|
||||
array([3.0, 3.0, 3.0, 3.0, 4.0, 5.0, 6.0, 7.0, 7.0], dtype=float)
|
||||
13
tests/constructors.py
Normal file
13
tests/constructors.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from ulab import linalg
|
||||
import ulab
|
||||
print(ulab.ones(3))
|
||||
print(ulab.ones((2,3)))
|
||||
print(ulab.zeros(3))
|
||||
print(ulab.zeros((2,3)))
|
||||
print(ulab.eye(3))
|
||||
print(ulab.ones(1, dtype=ulab.int8))
|
||||
print(ulab.ones(2, dtype=ulab.uint8))
|
||||
print(ulab.ones(3, dtype=ulab.int16))
|
||||
print(ulab.ones(4, dtype=ulab.uint16))
|
||||
print(ulab.ones(5, dtype=ulab.float))
|
||||
print(ulab.linspace(0, 1, 9))
|
||||
15
tests/constructors.py.exp
Normal file
15
tests/constructors.py.exp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
array([1.0, 1.0, 1.0], dtype=float)
|
||||
array([[1.0, 1.0, 1.0],
|
||||
[1.0, 1.0, 1.0]], dtype=float)
|
||||
array([0.0, 0.0, 0.0], dtype=float)
|
||||
array([[0.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 0.0]], dtype=float)
|
||||
array([[1.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 1.0]], dtype=float)
|
||||
array([1], dtype=int8)
|
||||
array([1, 1], dtype=uint8)
|
||||
array([1, 1, 1], dtype=int16)
|
||||
array([1, 1, 1, 1], dtype=uint16)
|
||||
array([1.0, 1.0, 1.0, 1.0, 1.0], dtype=float)
|
||||
array([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0], dtype=float)
|
||||
17
tests/linalg.py
Normal file
17
tests/linalg.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import ulab
|
||||
from ulab import linalg
|
||||
|
||||
a = ulab.array([[1, 2], [3, 4]])
|
||||
print(linalg.inv(a))
|
||||
|
||||
b = ulab.array([[1, 2, 3], [4, 5, 6], [7, 8, 7]])
|
||||
print(linalg.inv(b))
|
||||
|
||||
c = ulab.array([[1, 2, 0, 0], [0, 6, 7, 0], [0, 0, 8, 9], [0, 0, 15, 13]])
|
||||
print(linalg.inv(c))
|
||||
|
||||
print(linalg.det(a))
|
||||
print(linalg.det(b))
|
||||
print(linalg.det(c))
|
||||
|
||||
|
||||
12
tests/linalg.py.exp
Normal file
12
tests/linalg.py.exp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
array([[-2.0, 1.0],
|
||||
[1.5, -0.5]], dtype=float)
|
||||
array([[-2.166666666666667, 1.666666666666667, -0.5],
|
||||
[2.333333333333333, -2.333333333333333, 1.0],
|
||||
[-0.5, 1.0, -0.5]], dtype=float)
|
||||
array([[1.0, -0.3333333333333333, -0.9784946236559136, 0.6774193548387095],
|
||||
[0.0, 0.1666666666666667, 0.489247311827957, -0.3387096774193548],
|
||||
[0.0, 0.0, -0.4193548387096775, 0.2903225806451613],
|
||||
[-0.0, -0.0, 0.4838709677419355, -0.2580645161290323]], dtype=float)
|
||||
-2.0
|
||||
6.0
|
||||
-186.0
|
||||
20
tests/operators.py
Normal file
20
tests/operators.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import ulab
|
||||
a = ulab.ones(3)
|
||||
print(a+a)
|
||||
print(a-a)
|
||||
print(a*a)
|
||||
print(a/a)
|
||||
print(a+2)
|
||||
print(a-2)
|
||||
print(a*2)
|
||||
print(a/2)
|
||||
print(a<1)
|
||||
print(a<2)
|
||||
print(a<=0)
|
||||
print(a<=1)
|
||||
print(a>1)
|
||||
print(a>2)
|
||||
print(a>=0)
|
||||
print(a>=1)
|
||||
#print(a==0) # These print just true or false. Is it right? is it a micropython limitation?
|
||||
#print(a==1)
|
||||
16
tests/operators.py.exp
Normal file
16
tests/operators.py.exp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
array([2.0, 2.0, 2.0], dtype=float)
|
||||
array([0.0, 0.0, 0.0], dtype=float)
|
||||
array([1.0, 1.0, 1.0], dtype=float)
|
||||
array([1.0, 1.0, 1.0], dtype=float)
|
||||
array([3.0, 3.0, 3.0], dtype=float)
|
||||
array([-1.0, -1.0, -1.0], dtype=float)
|
||||
array([2.0, 2.0, 2.0], dtype=float)
|
||||
array([0.5, 0.5, 0.5], dtype=float)
|
||||
[False, False, False]
|
||||
[True, True, True]
|
||||
[False, False, False]
|
||||
[True, True, True]
|
||||
[False, False, False]
|
||||
[False, False, False]
|
||||
[True, True, True]
|
||||
[True, True, True]
|
||||
27
tests/poly.py
Normal file
27
tests/poly.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import ulab
|
||||
from ulab import poly
|
||||
from ulab import vector
|
||||
|
||||
# polynom evaluation
|
||||
x = ulab.linspace(0, 10, num=9)
|
||||
p = [1, 2, 3]
|
||||
y = poly.polyval(p, x)
|
||||
print(y)
|
||||
|
||||
# linear fit
|
||||
x = ulab.linspace(-5, 5, num=11)
|
||||
y = x + vector.sin(x)
|
||||
p = poly.polyfit(x, y, 1)
|
||||
print(p)
|
||||
|
||||
# quadratic fit
|
||||
x = ulab.linspace(-5, 5, num=11)
|
||||
y = x*x + vector.sin(x)*3.0
|
||||
p = poly.polyfit(x, y, 2)
|
||||
print(p)
|
||||
|
||||
# cubic fit
|
||||
x = ulab.linspace(-5, 5, num=11)
|
||||
y = x*x*x + vector.sin(x)*10.0
|
||||
p = poly.polyfit(x, y, 3)
|
||||
print(p)
|
||||
4
tests/poly.py.exp
Normal file
4
tests/poly.py.exp
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
array([3.0, 7.0625, 14.25, 24.5625, 38.0, 54.56250000000001, 74.25000000000001, 97.06250000000001, 123.0], dtype=float)
|
||||
array([0.9138471728743898, -8.074349270001139e-17], dtype=float)
|
||||
array([1.0, -0.2584584813768293, 3.552713678800501e-15], dtype=float)
|
||||
array([0.766798803225857, 0.0, 3.289453031323674, 0.0], dtype=float)
|
||||
25
tests/slicing.py
Normal file
25
tests/slicing.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
try:
|
||||
import ulab as np
|
||||
except:
|
||||
import numpy as np
|
||||
|
||||
for num in range(1,4):
|
||||
for start in range(-num, num+1):
|
||||
for end in range(-num, num+1):
|
||||
for stride in (-3, -2, -1, 1, 2, 3):
|
||||
l = list(range(num))
|
||||
a = np.array(l, dtype=np.int8)
|
||||
sl = l[start:end:stride]
|
||||
ll = len(sl)
|
||||
try:
|
||||
sa = list(a[start:end:stride])
|
||||
la = len(sa)
|
||||
except IndexError as e:
|
||||
sa = str(e)
|
||||
la = -1
|
||||
print("%2d [% d:% d:% d] %-24r %-24r%s" % (
|
||||
num, start, end, stride, sl, sa, " ***" if sa != sl else ""))
|
||||
|
||||
a[start:end:stride] = np.ones(len(sl)) * -1
|
||||
print("%2d [% d:% d:% d] %r" % (
|
||||
num, start, end, stride, list(a)))
|
||||
996
tests/slicing.py.exp
Normal file
996
tests/slicing.py.exp
Normal file
|
|
@ -0,0 +1,996 @@
|
|||
1 [-1:-1:-3] [] []
|
||||
1 [-1:-1:-3] [0]
|
||||
1 [-1:-1:-2] [] []
|
||||
1 [-1:-1:-2] [0]
|
||||
1 [-1:-1:-1] [] []
|
||||
1 [-1:-1:-1] [0]
|
||||
1 [-1:-1: 1] [] []
|
||||
1 [-1:-1: 1] [0]
|
||||
1 [-1:-1: 2] [] []
|
||||
1 [-1:-1: 2] [0]
|
||||
1 [-1:-1: 3] [] []
|
||||
1 [-1:-1: 3] [0]
|
||||
1 [-1: 0:-3] [] []
|
||||
1 [-1: 0:-3] [0]
|
||||
1 [-1: 0:-2] [] []
|
||||
1 [-1: 0:-2] [0]
|
||||
1 [-1: 0:-1] [] []
|
||||
1 [-1: 0:-1] [0]
|
||||
1 [-1: 0: 1] [] []
|
||||
1 [-1: 0: 1] [0]
|
||||
1 [-1: 0: 2] [] []
|
||||
1 [-1: 0: 2] [0]
|
||||
1 [-1: 0: 3] [] []
|
||||
1 [-1: 0: 3] [0]
|
||||
1 [-1: 1:-3] [] []
|
||||
1 [-1: 1:-3] [0]
|
||||
1 [-1: 1:-2] [] []
|
||||
1 [-1: 1:-2] [0]
|
||||
1 [-1: 1:-1] [] []
|
||||
1 [-1: 1:-1] [0]
|
||||
1 [-1: 1: 1] [0] [0]
|
||||
1 [-1: 1: 1] [-1]
|
||||
1 [-1: 1: 2] [0] [0]
|
||||
1 [-1: 1: 2] [-1]
|
||||
1 [-1: 1: 3] [0] [0]
|
||||
1 [-1: 1: 3] [-1]
|
||||
1 [ 0:-1:-3] [] []
|
||||
1 [ 0:-1:-3] [0]
|
||||
1 [ 0:-1:-2] [] []
|
||||
1 [ 0:-1:-2] [0]
|
||||
1 [ 0:-1:-1] [] []
|
||||
1 [ 0:-1:-1] [0]
|
||||
1 [ 0:-1: 1] [] []
|
||||
1 [ 0:-1: 1] [0]
|
||||
1 [ 0:-1: 2] [] []
|
||||
1 [ 0:-1: 2] [0]
|
||||
1 [ 0:-1: 3] [] []
|
||||
1 [ 0:-1: 3] [0]
|
||||
1 [ 0: 0:-3] [] []
|
||||
1 [ 0: 0:-3] [0]
|
||||
1 [ 0: 0:-2] [] []
|
||||
1 [ 0: 0:-2] [0]
|
||||
1 [ 0: 0:-1] [] []
|
||||
1 [ 0: 0:-1] [0]
|
||||
1 [ 0: 0: 1] [] []
|
||||
1 [ 0: 0: 1] [0]
|
||||
1 [ 0: 0: 2] [] []
|
||||
1 [ 0: 0: 2] [0]
|
||||
1 [ 0: 0: 3] [] []
|
||||
1 [ 0: 0: 3] [0]
|
||||
1 [ 0: 1:-3] [] []
|
||||
1 [ 0: 1:-3] [0]
|
||||
1 [ 0: 1:-2] [] []
|
||||
1 [ 0: 1:-2] [0]
|
||||
1 [ 0: 1:-1] [] []
|
||||
1 [ 0: 1:-1] [0]
|
||||
1 [ 0: 1: 1] [0] [0]
|
||||
1 [ 0: 1: 1] [-1]
|
||||
1 [ 0: 1: 2] [0] [0]
|
||||
1 [ 0: 1: 2] [-1]
|
||||
1 [ 0: 1: 3] [0] [0]
|
||||
1 [ 0: 1: 3] [-1]
|
||||
1 [ 1:-1:-3] [] []
|
||||
1 [ 1:-1:-3] [0]
|
||||
1 [ 1:-1:-2] [] []
|
||||
1 [ 1:-1:-2] [0]
|
||||
1 [ 1:-1:-1] [] []
|
||||
1 [ 1:-1:-1] [0]
|
||||
1 [ 1:-1: 1] [] []
|
||||
1 [ 1:-1: 1] [0]
|
||||
1 [ 1:-1: 2] [] []
|
||||
1 [ 1:-1: 2] [0]
|
||||
1 [ 1:-1: 3] [] []
|
||||
1 [ 1:-1: 3] [0]
|
||||
1 [ 1: 0:-3] [] []
|
||||
1 [ 1: 0:-3] [0]
|
||||
1 [ 1: 0:-2] [] []
|
||||
1 [ 1: 0:-2] [0]
|
||||
1 [ 1: 0:-1] [] []
|
||||
1 [ 1: 0:-1] [0]
|
||||
1 [ 1: 0: 1] [] []
|
||||
1 [ 1: 0: 1] [0]
|
||||
1 [ 1: 0: 2] [] []
|
||||
1 [ 1: 0: 2] [0]
|
||||
1 [ 1: 0: 3] [] []
|
||||
1 [ 1: 0: 3] [0]
|
||||
1 [ 1: 1:-3] [] []
|
||||
1 [ 1: 1:-3] [0]
|
||||
1 [ 1: 1:-2] [] []
|
||||
1 [ 1: 1:-2] [0]
|
||||
1 [ 1: 1:-1] [] []
|
||||
1 [ 1: 1:-1] [0]
|
||||
1 [ 1: 1: 1] [] []
|
||||
1 [ 1: 1: 1] [0]
|
||||
1 [ 1: 1: 2] [] []
|
||||
1 [ 1: 1: 2] [0]
|
||||
1 [ 1: 1: 3] [] []
|
||||
1 [ 1: 1: 3] [0]
|
||||
2 [-2:-2:-3] [] []
|
||||
2 [-2:-2:-3] [0, 1]
|
||||
2 [-2:-2:-2] [] []
|
||||
2 [-2:-2:-2] [0, 1]
|
||||
2 [-2:-2:-1] [] []
|
||||
2 [-2:-2:-1] [0, 1]
|
||||
2 [-2:-2: 1] [] []
|
||||
2 [-2:-2: 1] [0, 1]
|
||||
2 [-2:-2: 2] [] []
|
||||
2 [-2:-2: 2] [0, 1]
|
||||
2 [-2:-2: 3] [] []
|
||||
2 [-2:-2: 3] [0, 1]
|
||||
2 [-2:-1:-3] [] []
|
||||
2 [-2:-1:-3] [0, 1]
|
||||
2 [-2:-1:-2] [] []
|
||||
2 [-2:-1:-2] [0, 1]
|
||||
2 [-2:-1:-1] [] []
|
||||
2 [-2:-1:-1] [0, 1]
|
||||
2 [-2:-1: 1] [0] [0]
|
||||
2 [-2:-1: 1] [-1, 1]
|
||||
2 [-2:-1: 2] [0] [0]
|
||||
2 [-2:-1: 2] [-1, 1]
|
||||
2 [-2:-1: 3] [0] [0]
|
||||
2 [-2:-1: 3] [-1, 1]
|
||||
2 [-2: 0:-3] [] []
|
||||
2 [-2: 0:-3] [0, 1]
|
||||
2 [-2: 0:-2] [] []
|
||||
2 [-2: 0:-2] [0, 1]
|
||||
2 [-2: 0:-1] [] []
|
||||
2 [-2: 0:-1] [0, 1]
|
||||
2 [-2: 0: 1] [] []
|
||||
2 [-2: 0: 1] [0, 1]
|
||||
2 [-2: 0: 2] [] []
|
||||
2 [-2: 0: 2] [0, 1]
|
||||
2 [-2: 0: 3] [] []
|
||||
2 [-2: 0: 3] [0, 1]
|
||||
2 [-2: 1:-3] [] []
|
||||
2 [-2: 1:-3] [0, 1]
|
||||
2 [-2: 1:-2] [] []
|
||||
2 [-2: 1:-2] [0, 1]
|
||||
2 [-2: 1:-1] [] []
|
||||
2 [-2: 1:-1] [0, 1]
|
||||
2 [-2: 1: 1] [0] [0]
|
||||
2 [-2: 1: 1] [-1, 1]
|
||||
2 [-2: 1: 2] [0] [0]
|
||||
2 [-2: 1: 2] [-1, 1]
|
||||
2 [-2: 1: 3] [0] [0]
|
||||
2 [-2: 1: 3] [-1, 1]
|
||||
2 [-2: 2:-3] [] []
|
||||
2 [-2: 2:-3] [0, 1]
|
||||
2 [-2: 2:-2] [] []
|
||||
2 [-2: 2:-2] [0, 1]
|
||||
2 [-2: 2:-1] [] []
|
||||
2 [-2: 2:-1] [0, 1]
|
||||
2 [-2: 2: 1] [0, 1] [0, 1]
|
||||
2 [-2: 2: 1] [-1, -1]
|
||||
2 [-2: 2: 2] [0] [0]
|
||||
2 [-2: 2: 2] [-1, 1]
|
||||
2 [-2: 2: 3] [0] [0]
|
||||
2 [-2: 2: 3] [-1, 1]
|
||||
2 [-1:-2:-3] [1] [1]
|
||||
2 [-1:-2:-3] [0, -1]
|
||||
2 [-1:-2:-2] [1] [1]
|
||||
2 [-1:-2:-2] [0, -1]
|
||||
2 [-1:-2:-1] [1] [1]
|
||||
2 [-1:-2:-1] [0, -1]
|
||||
2 [-1:-2: 1] [] []
|
||||
2 [-1:-2: 1] [0, 1]
|
||||
2 [-1:-2: 2] [] []
|
||||
2 [-1:-2: 2] [0, 1]
|
||||
2 [-1:-2: 3] [] []
|
||||
2 [-1:-2: 3] [0, 1]
|
||||
2 [-1:-1:-3] [] []
|
||||
2 [-1:-1:-3] [0, 1]
|
||||
2 [-1:-1:-2] [] []
|
||||
2 [-1:-1:-2] [0, 1]
|
||||
2 [-1:-1:-1] [] []
|
||||
2 [-1:-1:-1] [0, 1]
|
||||
2 [-1:-1: 1] [] []
|
||||
2 [-1:-1: 1] [0, 1]
|
||||
2 [-1:-1: 2] [] []
|
||||
2 [-1:-1: 2] [0, 1]
|
||||
2 [-1:-1: 3] [] []
|
||||
2 [-1:-1: 3] [0, 1]
|
||||
2 [-1: 0:-3] [1] [1]
|
||||
2 [-1: 0:-3] [0, -1]
|
||||
2 [-1: 0:-2] [1] [1]
|
||||
2 [-1: 0:-2] [0, -1]
|
||||
2 [-1: 0:-1] [1] [1]
|
||||
2 [-1: 0:-1] [0, -1]
|
||||
2 [-1: 0: 1] [] []
|
||||
2 [-1: 0: 1] [0, 1]
|
||||
2 [-1: 0: 2] [] []
|
||||
2 [-1: 0: 2] [0, 1]
|
||||
2 [-1: 0: 3] [] []
|
||||
2 [-1: 0: 3] [0, 1]
|
||||
2 [-1: 1:-3] [] []
|
||||
2 [-1: 1:-3] [0, 1]
|
||||
2 [-1: 1:-2] [] []
|
||||
2 [-1: 1:-2] [0, 1]
|
||||
2 [-1: 1:-1] [] []
|
||||
2 [-1: 1:-1] [0, 1]
|
||||
2 [-1: 1: 1] [] []
|
||||
2 [-1: 1: 1] [0, 1]
|
||||
2 [-1: 1: 2] [] []
|
||||
2 [-1: 1: 2] [0, 1]
|
||||
2 [-1: 1: 3] [] []
|
||||
2 [-1: 1: 3] [0, 1]
|
||||
2 [-1: 2:-3] [] []
|
||||
2 [-1: 2:-3] [0, 1]
|
||||
2 [-1: 2:-2] [] []
|
||||
2 [-1: 2:-2] [0, 1]
|
||||
2 [-1: 2:-1] [] []
|
||||
2 [-1: 2:-1] [0, 1]
|
||||
2 [-1: 2: 1] [1] [1]
|
||||
2 [-1: 2: 1] [0, -1]
|
||||
2 [-1: 2: 2] [1] [1]
|
||||
2 [-1: 2: 2] [0, -1]
|
||||
2 [-1: 2: 3] [1] [1]
|
||||
2 [-1: 2: 3] [0, -1]
|
||||
2 [ 0:-2:-3] [] []
|
||||
2 [ 0:-2:-3] [0, 1]
|
||||
2 [ 0:-2:-2] [] []
|
||||
2 [ 0:-2:-2] [0, 1]
|
||||
2 [ 0:-2:-1] [] []
|
||||
2 [ 0:-2:-1] [0, 1]
|
||||
2 [ 0:-2: 1] [] []
|
||||
2 [ 0:-2: 1] [0, 1]
|
||||
2 [ 0:-2: 2] [] []
|
||||
2 [ 0:-2: 2] [0, 1]
|
||||
2 [ 0:-2: 3] [] []
|
||||
2 [ 0:-2: 3] [0, 1]
|
||||
2 [ 0:-1:-3] [] []
|
||||
2 [ 0:-1:-3] [0, 1]
|
||||
2 [ 0:-1:-2] [] []
|
||||
2 [ 0:-1:-2] [0, 1]
|
||||
2 [ 0:-1:-1] [] []
|
||||
2 [ 0:-1:-1] [0, 1]
|
||||
2 [ 0:-1: 1] [0] [0]
|
||||
2 [ 0:-1: 1] [-1, 1]
|
||||
2 [ 0:-1: 2] [0] [0]
|
||||
2 [ 0:-1: 2] [-1, 1]
|
||||
2 [ 0:-1: 3] [0] [0]
|
||||
2 [ 0:-1: 3] [-1, 1]
|
||||
2 [ 0: 0:-3] [] []
|
||||
2 [ 0: 0:-3] [0, 1]
|
||||
2 [ 0: 0:-2] [] []
|
||||
2 [ 0: 0:-2] [0, 1]
|
||||
2 [ 0: 0:-1] [] []
|
||||
2 [ 0: 0:-1] [0, 1]
|
||||
2 [ 0: 0: 1] [] []
|
||||
2 [ 0: 0: 1] [0, 1]
|
||||
2 [ 0: 0: 2] [] []
|
||||
2 [ 0: 0: 2] [0, 1]
|
||||
2 [ 0: 0: 3] [] []
|
||||
2 [ 0: 0: 3] [0, 1]
|
||||
2 [ 0: 1:-3] [] []
|
||||
2 [ 0: 1:-3] [0, 1]
|
||||
2 [ 0: 1:-2] [] []
|
||||
2 [ 0: 1:-2] [0, 1]
|
||||
2 [ 0: 1:-1] [] []
|
||||
2 [ 0: 1:-1] [0, 1]
|
||||
2 [ 0: 1: 1] [0] [0]
|
||||
2 [ 0: 1: 1] [-1, 1]
|
||||
2 [ 0: 1: 2] [0] [0]
|
||||
2 [ 0: 1: 2] [-1, 1]
|
||||
2 [ 0: 1: 3] [0] [0]
|
||||
2 [ 0: 1: 3] [-1, 1]
|
||||
2 [ 0: 2:-3] [] []
|
||||
2 [ 0: 2:-3] [0, 1]
|
||||
2 [ 0: 2:-2] [] []
|
||||
2 [ 0: 2:-2] [0, 1]
|
||||
2 [ 0: 2:-1] [] []
|
||||
2 [ 0: 2:-1] [0, 1]
|
||||
2 [ 0: 2: 1] [0, 1] [0, 1]
|
||||
2 [ 0: 2: 1] [-1, -1]
|
||||
2 [ 0: 2: 2] [0] [0]
|
||||
2 [ 0: 2: 2] [-1, 1]
|
||||
2 [ 0: 2: 3] [0] [0]
|
||||
2 [ 0: 2: 3] [-1, 1]
|
||||
2 [ 1:-2:-3] [1] [1]
|
||||
2 [ 1:-2:-3] [0, -1]
|
||||
2 [ 1:-2:-2] [1] [1]
|
||||
2 [ 1:-2:-2] [0, -1]
|
||||
2 [ 1:-2:-1] [1] [1]
|
||||
2 [ 1:-2:-1] [0, -1]
|
||||
2 [ 1:-2: 1] [] []
|
||||
2 [ 1:-2: 1] [0, 1]
|
||||
2 [ 1:-2: 2] [] []
|
||||
2 [ 1:-2: 2] [0, 1]
|
||||
2 [ 1:-2: 3] [] []
|
||||
2 [ 1:-2: 3] [0, 1]
|
||||
2 [ 1:-1:-3] [] []
|
||||
2 [ 1:-1:-3] [0, 1]
|
||||
2 [ 1:-1:-2] [] []
|
||||
2 [ 1:-1:-2] [0, 1]
|
||||
2 [ 1:-1:-1] [] []
|
||||
2 [ 1:-1:-1] [0, 1]
|
||||
2 [ 1:-1: 1] [] []
|
||||
2 [ 1:-1: 1] [0, 1]
|
||||
2 [ 1:-1: 2] [] []
|
||||
2 [ 1:-1: 2] [0, 1]
|
||||
2 [ 1:-1: 3] [] []
|
||||
2 [ 1:-1: 3] [0, 1]
|
||||
2 [ 1: 0:-3] [1] [1]
|
||||
2 [ 1: 0:-3] [0, -1]
|
||||
2 [ 1: 0:-2] [1] [1]
|
||||
2 [ 1: 0:-2] [0, -1]
|
||||
2 [ 1: 0:-1] [1] [1]
|
||||
2 [ 1: 0:-1] [0, -1]
|
||||
2 [ 1: 0: 1] [] []
|
||||
2 [ 1: 0: 1] [0, 1]
|
||||
2 [ 1: 0: 2] [] []
|
||||
2 [ 1: 0: 2] [0, 1]
|
||||
2 [ 1: 0: 3] [] []
|
||||
2 [ 1: 0: 3] [0, 1]
|
||||
2 [ 1: 1:-3] [] []
|
||||
2 [ 1: 1:-3] [0, 1]
|
||||
2 [ 1: 1:-2] [] []
|
||||
2 [ 1: 1:-2] [0, 1]
|
||||
2 [ 1: 1:-1] [] []
|
||||
2 [ 1: 1:-1] [0, 1]
|
||||
2 [ 1: 1: 1] [] []
|
||||
2 [ 1: 1: 1] [0, 1]
|
||||
2 [ 1: 1: 2] [] []
|
||||
2 [ 1: 1: 2] [0, 1]
|
||||
2 [ 1: 1: 3] [] []
|
||||
2 [ 1: 1: 3] [0, 1]
|
||||
2 [ 1: 2:-3] [] []
|
||||
2 [ 1: 2:-3] [0, 1]
|
||||
2 [ 1: 2:-2] [] []
|
||||
2 [ 1: 2:-2] [0, 1]
|
||||
2 [ 1: 2:-1] [] []
|
||||
2 [ 1: 2:-1] [0, 1]
|
||||
2 [ 1: 2: 1] [1] [1]
|
||||
2 [ 1: 2: 1] [0, -1]
|
||||
2 [ 1: 2: 2] [1] [1]
|
||||
2 [ 1: 2: 2] [0, -1]
|
||||
2 [ 1: 2: 3] [1] [1]
|
||||
2 [ 1: 2: 3] [0, -1]
|
||||
2 [ 2:-2:-3] [1] [1]
|
||||
2 [ 2:-2:-3] [0, -1]
|
||||
2 [ 2:-2:-2] [1] [1]
|
||||
2 [ 2:-2:-2] [0, -1]
|
||||
2 [ 2:-2:-1] [1] [1]
|
||||
2 [ 2:-2:-1] [0, -1]
|
||||
2 [ 2:-2: 1] [] []
|
||||
2 [ 2:-2: 1] [0, 1]
|
||||
2 [ 2:-2: 2] [] []
|
||||
2 [ 2:-2: 2] [0, 1]
|
||||
2 [ 2:-2: 3] [] []
|
||||
2 [ 2:-2: 3] [0, 1]
|
||||
2 [ 2:-1:-3] [] []
|
||||
2 [ 2:-1:-3] [0, 1]
|
||||
2 [ 2:-1:-2] [] []
|
||||
2 [ 2:-1:-2] [0, 1]
|
||||
2 [ 2:-1:-1] [] []
|
||||
2 [ 2:-1:-1] [0, 1]
|
||||
2 [ 2:-1: 1] [] []
|
||||
2 [ 2:-1: 1] [0, 1]
|
||||
2 [ 2:-1: 2] [] []
|
||||
2 [ 2:-1: 2] [0, 1]
|
||||
2 [ 2:-1: 3] [] []
|
||||
2 [ 2:-1: 3] [0, 1]
|
||||
2 [ 2: 0:-3] [1] [1]
|
||||
2 [ 2: 0:-3] [0, -1]
|
||||
2 [ 2: 0:-2] [1] [1]
|
||||
2 [ 2: 0:-2] [0, -1]
|
||||
2 [ 2: 0:-1] [1] [1]
|
||||
2 [ 2: 0:-1] [0, -1]
|
||||
2 [ 2: 0: 1] [] []
|
||||
2 [ 2: 0: 1] [0, 1]
|
||||
2 [ 2: 0: 2] [] []
|
||||
2 [ 2: 0: 2] [0, 1]
|
||||
2 [ 2: 0: 3] [] []
|
||||
2 [ 2: 0: 3] [0, 1]
|
||||
2 [ 2: 1:-3] [] []
|
||||
2 [ 2: 1:-3] [0, 1]
|
||||
2 [ 2: 1:-2] [] []
|
||||
2 [ 2: 1:-2] [0, 1]
|
||||
2 [ 2: 1:-1] [] []
|
||||
2 [ 2: 1:-1] [0, 1]
|
||||
2 [ 2: 1: 1] [] []
|
||||
2 [ 2: 1: 1] [0, 1]
|
||||
2 [ 2: 1: 2] [] []
|
||||
2 [ 2: 1: 2] [0, 1]
|
||||
2 [ 2: 1: 3] [] []
|
||||
2 [ 2: 1: 3] [0, 1]
|
||||
2 [ 2: 2:-3] [] []
|
||||
2 [ 2: 2:-3] [0, 1]
|
||||
2 [ 2: 2:-2] [] []
|
||||
2 [ 2: 2:-2] [0, 1]
|
||||
2 [ 2: 2:-1] [] []
|
||||
2 [ 2: 2:-1] [0, 1]
|
||||
2 [ 2: 2: 1] [] []
|
||||
2 [ 2: 2: 1] [0, 1]
|
||||
2 [ 2: 2: 2] [] []
|
||||
2 [ 2: 2: 2] [0, 1]
|
||||
2 [ 2: 2: 3] [] []
|
||||
2 [ 2: 2: 3] [0, 1]
|
||||
3 [-3:-3:-3] [] []
|
||||
3 [-3:-3:-3] [0, 1, 2]
|
||||
3 [-3:-3:-2] [] []
|
||||
3 [-3:-3:-2] [0, 1, 2]
|
||||
3 [-3:-3:-1] [] []
|
||||
3 [-3:-3:-1] [0, 1, 2]
|
||||
3 [-3:-3: 1] [] []
|
||||
3 [-3:-3: 1] [0, 1, 2]
|
||||
3 [-3:-3: 2] [] []
|
||||
3 [-3:-3: 2] [0, 1, 2]
|
||||
3 [-3:-3: 3] [] []
|
||||
3 [-3:-3: 3] [0, 1, 2]
|
||||
3 [-3:-2:-3] [] []
|
||||
3 [-3:-2:-3] [0, 1, 2]
|
||||
3 [-3:-2:-2] [] []
|
||||
3 [-3:-2:-2] [0, 1, 2]
|
||||
3 [-3:-2:-1] [] []
|
||||
3 [-3:-2:-1] [0, 1, 2]
|
||||
3 [-3:-2: 1] [0] [0]
|
||||
3 [-3:-2: 1] [-1, 1, 2]
|
||||
3 [-3:-2: 2] [0] [0]
|
||||
3 [-3:-2: 2] [-1, 1, 2]
|
||||
3 [-3:-2: 3] [0] [0]
|
||||
3 [-3:-2: 3] [-1, 1, 2]
|
||||
3 [-3:-1:-3] [] []
|
||||
3 [-3:-1:-3] [0, 1, 2]
|
||||
3 [-3:-1:-2] [] []
|
||||
3 [-3:-1:-2] [0, 1, 2]
|
||||
3 [-3:-1:-1] [] []
|
||||
3 [-3:-1:-1] [0, 1, 2]
|
||||
3 [-3:-1: 1] [0, 1] [0, 1]
|
||||
3 [-3:-1: 1] [-1, -1, 2]
|
||||
3 [-3:-1: 2] [0] [0]
|
||||
3 [-3:-1: 2] [-1, 1, 2]
|
||||
3 [-3:-1: 3] [0] [0]
|
||||
3 [-3:-1: 3] [-1, 1, 2]
|
||||
3 [-3: 0:-3] [] []
|
||||
3 [-3: 0:-3] [0, 1, 2]
|
||||
3 [-3: 0:-2] [] []
|
||||
3 [-3: 0:-2] [0, 1, 2]
|
||||
3 [-3: 0:-1] [] []
|
||||
3 [-3: 0:-1] [0, 1, 2]
|
||||
3 [-3: 0: 1] [] []
|
||||
3 [-3: 0: 1] [0, 1, 2]
|
||||
3 [-3: 0: 2] [] []
|
||||
3 [-3: 0: 2] [0, 1, 2]
|
||||
3 [-3: 0: 3] [] []
|
||||
3 [-3: 0: 3] [0, 1, 2]
|
||||
3 [-3: 1:-3] [] []
|
||||
3 [-3: 1:-3] [0, 1, 2]
|
||||
3 [-3: 1:-2] [] []
|
||||
3 [-3: 1:-2] [0, 1, 2]
|
||||
3 [-3: 1:-1] [] []
|
||||
3 [-3: 1:-1] [0, 1, 2]
|
||||
3 [-3: 1: 1] [0] [0]
|
||||
3 [-3: 1: 1] [-1, 1, 2]
|
||||
3 [-3: 1: 2] [0] [0]
|
||||
3 [-3: 1: 2] [-1, 1, 2]
|
||||
3 [-3: 1: 3] [0] [0]
|
||||
3 [-3: 1: 3] [-1, 1, 2]
|
||||
3 [-3: 2:-3] [] []
|
||||
3 [-3: 2:-3] [0, 1, 2]
|
||||
3 [-3: 2:-2] [] []
|
||||
3 [-3: 2:-2] [0, 1, 2]
|
||||
3 [-3: 2:-1] [] []
|
||||
3 [-3: 2:-1] [0, 1, 2]
|
||||
3 [-3: 2: 1] [0, 1] [0, 1]
|
||||
3 [-3: 2: 1] [-1, -1, 2]
|
||||
3 [-3: 2: 2] [0] [0]
|
||||
3 [-3: 2: 2] [-1, 1, 2]
|
||||
3 [-3: 2: 3] [0] [0]
|
||||
3 [-3: 2: 3] [-1, 1, 2]
|
||||
3 [-3: 3:-3] [] []
|
||||
3 [-3: 3:-3] [0, 1, 2]
|
||||
3 [-3: 3:-2] [] []
|
||||
3 [-3: 3:-2] [0, 1, 2]
|
||||
3 [-3: 3:-1] [] []
|
||||
3 [-3: 3:-1] [0, 1, 2]
|
||||
3 [-3: 3: 1] [0, 1, 2] [0, 1, 2]
|
||||
3 [-3: 3: 1] [-1, -1, -1]
|
||||
3 [-3: 3: 2] [0, 2] [0, 2]
|
||||
3 [-3: 3: 2] [-1, 1, -1]
|
||||
3 [-3: 3: 3] [0] [0]
|
||||
3 [-3: 3: 3] [-1, 1, 2]
|
||||
3 [-2:-3:-3] [1] [1]
|
||||
3 [-2:-3:-3] [0, -1, 2]
|
||||
3 [-2:-3:-2] [1] [1]
|
||||
3 [-2:-3:-2] [0, -1, 2]
|
||||
3 [-2:-3:-1] [1] [1]
|
||||
3 [-2:-3:-1] [0, -1, 2]
|
||||
3 [-2:-3: 1] [] []
|
||||
3 [-2:-3: 1] [0, 1, 2]
|
||||
3 [-2:-3: 2] [] []
|
||||
3 [-2:-3: 2] [0, 1, 2]
|
||||
3 [-2:-3: 3] [] []
|
||||
3 [-2:-3: 3] [0, 1, 2]
|
||||
3 [-2:-2:-3] [] []
|
||||
3 [-2:-2:-3] [0, 1, 2]
|
||||
3 [-2:-2:-2] [] []
|
||||
3 [-2:-2:-2] [0, 1, 2]
|
||||
3 [-2:-2:-1] [] []
|
||||
3 [-2:-2:-1] [0, 1, 2]
|
||||
3 [-2:-2: 1] [] []
|
||||
3 [-2:-2: 1] [0, 1, 2]
|
||||
3 [-2:-2: 2] [] []
|
||||
3 [-2:-2: 2] [0, 1, 2]
|
||||
3 [-2:-2: 3] [] []
|
||||
3 [-2:-2: 3] [0, 1, 2]
|
||||
3 [-2:-1:-3] [] []
|
||||
3 [-2:-1:-3] [0, 1, 2]
|
||||
3 [-2:-1:-2] [] []
|
||||
3 [-2:-1:-2] [0, 1, 2]
|
||||
3 [-2:-1:-1] [] []
|
||||
3 [-2:-1:-1] [0, 1, 2]
|
||||
3 [-2:-1: 1] [1] [1]
|
||||
3 [-2:-1: 1] [0, -1, 2]
|
||||
3 [-2:-1: 2] [1] [1]
|
||||
3 [-2:-1: 2] [0, -1, 2]
|
||||
3 [-2:-1: 3] [1] [1]
|
||||
3 [-2:-1: 3] [0, -1, 2]
|
||||
3 [-2: 0:-3] [1] [1]
|
||||
3 [-2: 0:-3] [0, -1, 2]
|
||||
3 [-2: 0:-2] [1] [1]
|
||||
3 [-2: 0:-2] [0, -1, 2]
|
||||
3 [-2: 0:-1] [1] [1]
|
||||
3 [-2: 0:-1] [0, -1, 2]
|
||||
3 [-2: 0: 1] [] []
|
||||
3 [-2: 0: 1] [0, 1, 2]
|
||||
3 [-2: 0: 2] [] []
|
||||
3 [-2: 0: 2] [0, 1, 2]
|
||||
3 [-2: 0: 3] [] []
|
||||
3 [-2: 0: 3] [0, 1, 2]
|
||||
3 [-2: 1:-3] [] []
|
||||
3 [-2: 1:-3] [0, 1, 2]
|
||||
3 [-2: 1:-2] [] []
|
||||
3 [-2: 1:-2] [0, 1, 2]
|
||||
3 [-2: 1:-1] [] []
|
||||
3 [-2: 1:-1] [0, 1, 2]
|
||||
3 [-2: 1: 1] [] []
|
||||
3 [-2: 1: 1] [0, 1, 2]
|
||||
3 [-2: 1: 2] [] []
|
||||
3 [-2: 1: 2] [0, 1, 2]
|
||||
3 [-2: 1: 3] [] []
|
||||
3 [-2: 1: 3] [0, 1, 2]
|
||||
3 [-2: 2:-3] [] []
|
||||
3 [-2: 2:-3] [0, 1, 2]
|
||||
3 [-2: 2:-2] [] []
|
||||
3 [-2: 2:-2] [0, 1, 2]
|
||||
3 [-2: 2:-1] [] []
|
||||
3 [-2: 2:-1] [0, 1, 2]
|
||||
3 [-2: 2: 1] [1] [1]
|
||||
3 [-2: 2: 1] [0, -1, 2]
|
||||
3 [-2: 2: 2] [1] [1]
|
||||
3 [-2: 2: 2] [0, -1, 2]
|
||||
3 [-2: 2: 3] [1] [1]
|
||||
3 [-2: 2: 3] [0, -1, 2]
|
||||
3 [-2: 3:-3] [] []
|
||||
3 [-2: 3:-3] [0, 1, 2]
|
||||
3 [-2: 3:-2] [] []
|
||||
3 [-2: 3:-2] [0, 1, 2]
|
||||
3 [-2: 3:-1] [] []
|
||||
3 [-2: 3:-1] [0, 1, 2]
|
||||
3 [-2: 3: 1] [1, 2] [1, 2]
|
||||
3 [-2: 3: 1] [0, -1, -1]
|
||||
3 [-2: 3: 2] [1] [1]
|
||||
3 [-2: 3: 2] [0, -1, 2]
|
||||
3 [-2: 3: 3] [1] [1]
|
||||
3 [-2: 3: 3] [0, -1, 2]
|
||||
3 [-1:-3:-3] [2] [2]
|
||||
3 [-1:-3:-3] [0, 1, -1]
|
||||
3 [-1:-3:-2] [2] [2]
|
||||
3 [-1:-3:-2] [0, 1, -1]
|
||||
3 [-1:-3:-1] [2, 1] [2, 1]
|
||||
3 [-1:-3:-1] [0, -1, -1]
|
||||
3 [-1:-3: 1] [] []
|
||||
3 [-1:-3: 1] [0, 1, 2]
|
||||
3 [-1:-3: 2] [] []
|
||||
3 [-1:-3: 2] [0, 1, 2]
|
||||
3 [-1:-3: 3] [] []
|
||||
3 [-1:-3: 3] [0, 1, 2]
|
||||
3 [-1:-2:-3] [2] [2]
|
||||
3 [-1:-2:-3] [0, 1, -1]
|
||||
3 [-1:-2:-2] [2] [2]
|
||||
3 [-1:-2:-2] [0, 1, -1]
|
||||
3 [-1:-2:-1] [2] [2]
|
||||
3 [-1:-2:-1] [0, 1, -1]
|
||||
3 [-1:-2: 1] [] []
|
||||
3 [-1:-2: 1] [0, 1, 2]
|
||||
3 [-1:-2: 2] [] []
|
||||
3 [-1:-2: 2] [0, 1, 2]
|
||||
3 [-1:-2: 3] [] []
|
||||
3 [-1:-2: 3] [0, 1, 2]
|
||||
3 [-1:-1:-3] [] []
|
||||
3 [-1:-1:-3] [0, 1, 2]
|
||||
3 [-1:-1:-2] [] []
|
||||
3 [-1:-1:-2] [0, 1, 2]
|
||||
3 [-1:-1:-1] [] []
|
||||
3 [-1:-1:-1] [0, 1, 2]
|
||||
3 [-1:-1: 1] [] []
|
||||
3 [-1:-1: 1] [0, 1, 2]
|
||||
3 [-1:-1: 2] [] []
|
||||
3 [-1:-1: 2] [0, 1, 2]
|
||||
3 [-1:-1: 3] [] []
|
||||
3 [-1:-1: 3] [0, 1, 2]
|
||||
3 [-1: 0:-3] [2] [2]
|
||||
3 [-1: 0:-3] [0, 1, -1]
|
||||
3 [-1: 0:-2] [2] [2]
|
||||
3 [-1: 0:-2] [0, 1, -1]
|
||||
3 [-1: 0:-1] [2, 1] [2, 1]
|
||||
3 [-1: 0:-1] [0, -1, -1]
|
||||
3 [-1: 0: 1] [] []
|
||||
3 [-1: 0: 1] [0, 1, 2]
|
||||
3 [-1: 0: 2] [] []
|
||||
3 [-1: 0: 2] [0, 1, 2]
|
||||
3 [-1: 0: 3] [] []
|
||||
3 [-1: 0: 3] [0, 1, 2]
|
||||
3 [-1: 1:-3] [2] [2]
|
||||
3 [-1: 1:-3] [0, 1, -1]
|
||||
3 [-1: 1:-2] [2] [2]
|
||||
3 [-1: 1:-2] [0, 1, -1]
|
||||
3 [-1: 1:-1] [2] [2]
|
||||
3 [-1: 1:-1] [0, 1, -1]
|
||||
3 [-1: 1: 1] [] []
|
||||
3 [-1: 1: 1] [0, 1, 2]
|
||||
3 [-1: 1: 2] [] []
|
||||
3 [-1: 1: 2] [0, 1, 2]
|
||||
3 [-1: 1: 3] [] []
|
||||
3 [-1: 1: 3] [0, 1, 2]
|
||||
3 [-1: 2:-3] [] []
|
||||
3 [-1: 2:-3] [0, 1, 2]
|
||||
3 [-1: 2:-2] [] []
|
||||
3 [-1: 2:-2] [0, 1, 2]
|
||||
3 [-1: 2:-1] [] []
|
||||
3 [-1: 2:-1] [0, 1, 2]
|
||||
3 [-1: 2: 1] [] []
|
||||
3 [-1: 2: 1] [0, 1, 2]
|
||||
3 [-1: 2: 2] [] []
|
||||
3 [-1: 2: 2] [0, 1, 2]
|
||||
3 [-1: 2: 3] [] []
|
||||
3 [-1: 2: 3] [0, 1, 2]
|
||||
3 [-1: 3:-3] [] []
|
||||
3 [-1: 3:-3] [0, 1, 2]
|
||||
3 [-1: 3:-2] [] []
|
||||
3 [-1: 3:-2] [0, 1, 2]
|
||||
3 [-1: 3:-1] [] []
|
||||
3 [-1: 3:-1] [0, 1, 2]
|
||||
3 [-1: 3: 1] [2] [2]
|
||||
3 [-1: 3: 1] [0, 1, -1]
|
||||
3 [-1: 3: 2] [2] [2]
|
||||
3 [-1: 3: 2] [0, 1, -1]
|
||||
3 [-1: 3: 3] [2] [2]
|
||||
3 [-1: 3: 3] [0, 1, -1]
|
||||
3 [ 0:-3:-3] [] []
|
||||
3 [ 0:-3:-3] [0, 1, 2]
|
||||
3 [ 0:-3:-2] [] []
|
||||
3 [ 0:-3:-2] [0, 1, 2]
|
||||
3 [ 0:-3:-1] [] []
|
||||
3 [ 0:-3:-1] [0, 1, 2]
|
||||
3 [ 0:-3: 1] [] []
|
||||
3 [ 0:-3: 1] [0, 1, 2]
|
||||
3 [ 0:-3: 2] [] []
|
||||
3 [ 0:-3: 2] [0, 1, 2]
|
||||
3 [ 0:-3: 3] [] []
|
||||
3 [ 0:-3: 3] [0, 1, 2]
|
||||
3 [ 0:-2:-3] [] []
|
||||
3 [ 0:-2:-3] [0, 1, 2]
|
||||
3 [ 0:-2:-2] [] []
|
||||
3 [ 0:-2:-2] [0, 1, 2]
|
||||
3 [ 0:-2:-1] [] []
|
||||
3 [ 0:-2:-1] [0, 1, 2]
|
||||
3 [ 0:-2: 1] [0] [0]
|
||||
3 [ 0:-2: 1] [-1, 1, 2]
|
||||
3 [ 0:-2: 2] [0] [0]
|
||||
3 [ 0:-2: 2] [-1, 1, 2]
|
||||
3 [ 0:-2: 3] [0] [0]
|
||||
3 [ 0:-2: 3] [-1, 1, 2]
|
||||
3 [ 0:-1:-3] [] []
|
||||
3 [ 0:-1:-3] [0, 1, 2]
|
||||
3 [ 0:-1:-2] [] []
|
||||
3 [ 0:-1:-2] [0, 1, 2]
|
||||
3 [ 0:-1:-1] [] []
|
||||
3 [ 0:-1:-1] [0, 1, 2]
|
||||
3 [ 0:-1: 1] [0, 1] [0, 1]
|
||||
3 [ 0:-1: 1] [-1, -1, 2]
|
||||
3 [ 0:-1: 2] [0] [0]
|
||||
3 [ 0:-1: 2] [-1, 1, 2]
|
||||
3 [ 0:-1: 3] [0] [0]
|
||||
3 [ 0:-1: 3] [-1, 1, 2]
|
||||
3 [ 0: 0:-3] [] []
|
||||
3 [ 0: 0:-3] [0, 1, 2]
|
||||
3 [ 0: 0:-2] [] []
|
||||
3 [ 0: 0:-2] [0, 1, 2]
|
||||
3 [ 0: 0:-1] [] []
|
||||
3 [ 0: 0:-1] [0, 1, 2]
|
||||
3 [ 0: 0: 1] [] []
|
||||
3 [ 0: 0: 1] [0, 1, 2]
|
||||
3 [ 0: 0: 2] [] []
|
||||
3 [ 0: 0: 2] [0, 1, 2]
|
||||
3 [ 0: 0: 3] [] []
|
||||
3 [ 0: 0: 3] [0, 1, 2]
|
||||
3 [ 0: 1:-3] [] []
|
||||
3 [ 0: 1:-3] [0, 1, 2]
|
||||
3 [ 0: 1:-2] [] []
|
||||
3 [ 0: 1:-2] [0, 1, 2]
|
||||
3 [ 0: 1:-1] [] []
|
||||
3 [ 0: 1:-1] [0, 1, 2]
|
||||
3 [ 0: 1: 1] [0] [0]
|
||||
3 [ 0: 1: 1] [-1, 1, 2]
|
||||
3 [ 0: 1: 2] [0] [0]
|
||||
3 [ 0: 1: 2] [-1, 1, 2]
|
||||
3 [ 0: 1: 3] [0] [0]
|
||||
3 [ 0: 1: 3] [-1, 1, 2]
|
||||
3 [ 0: 2:-3] [] []
|
||||
3 [ 0: 2:-3] [0, 1, 2]
|
||||
3 [ 0: 2:-2] [] []
|
||||
3 [ 0: 2:-2] [0, 1, 2]
|
||||
3 [ 0: 2:-1] [] []
|
||||
3 [ 0: 2:-1] [0, 1, 2]
|
||||
3 [ 0: 2: 1] [0, 1] [0, 1]
|
||||
3 [ 0: 2: 1] [-1, -1, 2]
|
||||
3 [ 0: 2: 2] [0] [0]
|
||||
3 [ 0: 2: 2] [-1, 1, 2]
|
||||
3 [ 0: 2: 3] [0] [0]
|
||||
3 [ 0: 2: 3] [-1, 1, 2]
|
||||
3 [ 0: 3:-3] [] []
|
||||
3 [ 0: 3:-3] [0, 1, 2]
|
||||
3 [ 0: 3:-2] [] []
|
||||
3 [ 0: 3:-2] [0, 1, 2]
|
||||
3 [ 0: 3:-1] [] []
|
||||
3 [ 0: 3:-1] [0, 1, 2]
|
||||
3 [ 0: 3: 1] [0, 1, 2] [0, 1, 2]
|
||||
3 [ 0: 3: 1] [-1, -1, -1]
|
||||
3 [ 0: 3: 2] [0, 2] [0, 2]
|
||||
3 [ 0: 3: 2] [-1, 1, -1]
|
||||
3 [ 0: 3: 3] [0] [0]
|
||||
3 [ 0: 3: 3] [-1, 1, 2]
|
||||
3 [ 1:-3:-3] [1] [1]
|
||||
3 [ 1:-3:-3] [0, -1, 2]
|
||||
3 [ 1:-3:-2] [1] [1]
|
||||
3 [ 1:-3:-2] [0, -1, 2]
|
||||
3 [ 1:-3:-1] [1] [1]
|
||||
3 [ 1:-3:-1] [0, -1, 2]
|
||||
3 [ 1:-3: 1] [] []
|
||||
3 [ 1:-3: 1] [0, 1, 2]
|
||||
3 [ 1:-3: 2] [] []
|
||||
3 [ 1:-3: 2] [0, 1, 2]
|
||||
3 [ 1:-3: 3] [] []
|
||||
3 [ 1:-3: 3] [0, 1, 2]
|
||||
3 [ 1:-2:-3] [] []
|
||||
3 [ 1:-2:-3] [0, 1, 2]
|
||||
3 [ 1:-2:-2] [] []
|
||||
3 [ 1:-2:-2] [0, 1, 2]
|
||||
3 [ 1:-2:-1] [] []
|
||||
3 [ 1:-2:-1] [0, 1, 2]
|
||||
3 [ 1:-2: 1] [] []
|
||||
3 [ 1:-2: 1] [0, 1, 2]
|
||||
3 [ 1:-2: 2] [] []
|
||||
3 [ 1:-2: 2] [0, 1, 2]
|
||||
3 [ 1:-2: 3] [] []
|
||||
3 [ 1:-2: 3] [0, 1, 2]
|
||||
3 [ 1:-1:-3] [] []
|
||||
3 [ 1:-1:-3] [0, 1, 2]
|
||||
3 [ 1:-1:-2] [] []
|
||||
3 [ 1:-1:-2] [0, 1, 2]
|
||||
3 [ 1:-1:-1] [] []
|
||||
3 [ 1:-1:-1] [0, 1, 2]
|
||||
3 [ 1:-1: 1] [1] [1]
|
||||
3 [ 1:-1: 1] [0, -1, 2]
|
||||
3 [ 1:-1: 2] [1] [1]
|
||||
3 [ 1:-1: 2] [0, -1, 2]
|
||||
3 [ 1:-1: 3] [1] [1]
|
||||
3 [ 1:-1: 3] [0, -1, 2]
|
||||
3 [ 1: 0:-3] [1] [1]
|
||||
3 [ 1: 0:-3] [0, -1, 2]
|
||||
3 [ 1: 0:-2] [1] [1]
|
||||
3 [ 1: 0:-2] [0, -1, 2]
|
||||
3 [ 1: 0:-1] [1] [1]
|
||||
3 [ 1: 0:-1] [0, -1, 2]
|
||||
3 [ 1: 0: 1] [] []
|
||||
3 [ 1: 0: 1] [0, 1, 2]
|
||||
3 [ 1: 0: 2] [] []
|
||||
3 [ 1: 0: 2] [0, 1, 2]
|
||||
3 [ 1: 0: 3] [] []
|
||||
3 [ 1: 0: 3] [0, 1, 2]
|
||||
3 [ 1: 1:-3] [] []
|
||||
3 [ 1: 1:-3] [0, 1, 2]
|
||||
3 [ 1: 1:-2] [] []
|
||||
3 [ 1: 1:-2] [0, 1, 2]
|
||||
3 [ 1: 1:-1] [] []
|
||||
3 [ 1: 1:-1] [0, 1, 2]
|
||||
3 [ 1: 1: 1] [] []
|
||||
3 [ 1: 1: 1] [0, 1, 2]
|
||||
3 [ 1: 1: 2] [] []
|
||||
3 [ 1: 1: 2] [0, 1, 2]
|
||||
3 [ 1: 1: 3] [] []
|
||||
3 [ 1: 1: 3] [0, 1, 2]
|
||||
3 [ 1: 2:-3] [] []
|
||||
3 [ 1: 2:-3] [0, 1, 2]
|
||||
3 [ 1: 2:-2] [] []
|
||||
3 [ 1: 2:-2] [0, 1, 2]
|
||||
3 [ 1: 2:-1] [] []
|
||||
3 [ 1: 2:-1] [0, 1, 2]
|
||||
3 [ 1: 2: 1] [1] [1]
|
||||
3 [ 1: 2: 1] [0, -1, 2]
|
||||
3 [ 1: 2: 2] [1] [1]
|
||||
3 [ 1: 2: 2] [0, -1, 2]
|
||||
3 [ 1: 2: 3] [1] [1]
|
||||
3 [ 1: 2: 3] [0, -1, 2]
|
||||
3 [ 1: 3:-3] [] []
|
||||
3 [ 1: 3:-3] [0, 1, 2]
|
||||
3 [ 1: 3:-2] [] []
|
||||
3 [ 1: 3:-2] [0, 1, 2]
|
||||
3 [ 1: 3:-1] [] []
|
||||
3 [ 1: 3:-1] [0, 1, 2]
|
||||
3 [ 1: 3: 1] [1, 2] [1, 2]
|
||||
3 [ 1: 3: 1] [0, -1, -1]
|
||||
3 [ 1: 3: 2] [1] [1]
|
||||
3 [ 1: 3: 2] [0, -1, 2]
|
||||
3 [ 1: 3: 3] [1] [1]
|
||||
3 [ 1: 3: 3] [0, -1, 2]
|
||||
3 [ 2:-3:-3] [2] [2]
|
||||
3 [ 2:-3:-3] [0, 1, -1]
|
||||
3 [ 2:-3:-2] [2] [2]
|
||||
3 [ 2:-3:-2] [0, 1, -1]
|
||||
3 [ 2:-3:-1] [2, 1] [2, 1]
|
||||
3 [ 2:-3:-1] [0, -1, -1]
|
||||
3 [ 2:-3: 1] [] []
|
||||
3 [ 2:-3: 1] [0, 1, 2]
|
||||
3 [ 2:-3: 2] [] []
|
||||
3 [ 2:-3: 2] [0, 1, 2]
|
||||
3 [ 2:-3: 3] [] []
|
||||
3 [ 2:-3: 3] [0, 1, 2]
|
||||
3 [ 2:-2:-3] [2] [2]
|
||||
3 [ 2:-2:-3] [0, 1, -1]
|
||||
3 [ 2:-2:-2] [2] [2]
|
||||
3 [ 2:-2:-2] [0, 1, -1]
|
||||
3 [ 2:-2:-1] [2] [2]
|
||||
3 [ 2:-2:-1] [0, 1, -1]
|
||||
3 [ 2:-2: 1] [] []
|
||||
3 [ 2:-2: 1] [0, 1, 2]
|
||||
3 [ 2:-2: 2] [] []
|
||||
3 [ 2:-2: 2] [0, 1, 2]
|
||||
3 [ 2:-2: 3] [] []
|
||||
3 [ 2:-2: 3] [0, 1, 2]
|
||||
3 [ 2:-1:-3] [] []
|
||||
3 [ 2:-1:-3] [0, 1, 2]
|
||||
3 [ 2:-1:-2] [] []
|
||||
3 [ 2:-1:-2] [0, 1, 2]
|
||||
3 [ 2:-1:-1] [] []
|
||||
3 [ 2:-1:-1] [0, 1, 2]
|
||||
3 [ 2:-1: 1] [] []
|
||||
3 [ 2:-1: 1] [0, 1, 2]
|
||||
3 [ 2:-1: 2] [] []
|
||||
3 [ 2:-1: 2] [0, 1, 2]
|
||||
3 [ 2:-1: 3] [] []
|
||||
3 [ 2:-1: 3] [0, 1, 2]
|
||||
3 [ 2: 0:-3] [2] [2]
|
||||
3 [ 2: 0:-3] [0, 1, -1]
|
||||
3 [ 2: 0:-2] [2] [2]
|
||||
3 [ 2: 0:-2] [0, 1, -1]
|
||||
3 [ 2: 0:-1] [2, 1] [2, 1]
|
||||
3 [ 2: 0:-1] [0, -1, -1]
|
||||
3 [ 2: 0: 1] [] []
|
||||
3 [ 2: 0: 1] [0, 1, 2]
|
||||
3 [ 2: 0: 2] [] []
|
||||
3 [ 2: 0: 2] [0, 1, 2]
|
||||
3 [ 2: 0: 3] [] []
|
||||
3 [ 2: 0: 3] [0, 1, 2]
|
||||
3 [ 2: 1:-3] [2] [2]
|
||||
3 [ 2: 1:-3] [0, 1, -1]
|
||||
3 [ 2: 1:-2] [2] [2]
|
||||
3 [ 2: 1:-2] [0, 1, -1]
|
||||
3 [ 2: 1:-1] [2] [2]
|
||||
3 [ 2: 1:-1] [0, 1, -1]
|
||||
3 [ 2: 1: 1] [] []
|
||||
3 [ 2: 1: 1] [0, 1, 2]
|
||||
3 [ 2: 1: 2] [] []
|
||||
3 [ 2: 1: 2] [0, 1, 2]
|
||||
3 [ 2: 1: 3] [] []
|
||||
3 [ 2: 1: 3] [0, 1, 2]
|
||||
3 [ 2: 2:-3] [] []
|
||||
3 [ 2: 2:-3] [0, 1, 2]
|
||||
3 [ 2: 2:-2] [] []
|
||||
3 [ 2: 2:-2] [0, 1, 2]
|
||||
3 [ 2: 2:-1] [] []
|
||||
3 [ 2: 2:-1] [0, 1, 2]
|
||||
3 [ 2: 2: 1] [] []
|
||||
3 [ 2: 2: 1] [0, 1, 2]
|
||||
3 [ 2: 2: 2] [] []
|
||||
3 [ 2: 2: 2] [0, 1, 2]
|
||||
3 [ 2: 2: 3] [] []
|
||||
3 [ 2: 2: 3] [0, 1, 2]
|
||||
3 [ 2: 3:-3] [] []
|
||||
3 [ 2: 3:-3] [0, 1, 2]
|
||||
3 [ 2: 3:-2] [] []
|
||||
3 [ 2: 3:-2] [0, 1, 2]
|
||||
3 [ 2: 3:-1] [] []
|
||||
3 [ 2: 3:-1] [0, 1, 2]
|
||||
3 [ 2: 3: 1] [2] [2]
|
||||
3 [ 2: 3: 1] [0, 1, -1]
|
||||
3 [ 2: 3: 2] [2] [2]
|
||||
3 [ 2: 3: 2] [0, 1, -1]
|
||||
3 [ 2: 3: 3] [2] [2]
|
||||
3 [ 2: 3: 3] [0, 1, -1]
|
||||
3 [ 3:-3:-3] [2] [2]
|
||||
3 [ 3:-3:-3] [0, 1, -1]
|
||||
3 [ 3:-3:-2] [2] [2]
|
||||
3 [ 3:-3:-2] [0, 1, -1]
|
||||
3 [ 3:-3:-1] [2, 1] [2, 1]
|
||||
3 [ 3:-3:-1] [0, -1, -1]
|
||||
3 [ 3:-3: 1] [] []
|
||||
3 [ 3:-3: 1] [0, 1, 2]
|
||||
3 [ 3:-3: 2] [] []
|
||||
3 [ 3:-3: 2] [0, 1, 2]
|
||||
3 [ 3:-3: 3] [] []
|
||||
3 [ 3:-3: 3] [0, 1, 2]
|
||||
3 [ 3:-2:-3] [2] [2]
|
||||
3 [ 3:-2:-3] [0, 1, -1]
|
||||
3 [ 3:-2:-2] [2] [2]
|
||||
3 [ 3:-2:-2] [0, 1, -1]
|
||||
3 [ 3:-2:-1] [2] [2]
|
||||
3 [ 3:-2:-1] [0, 1, -1]
|
||||
3 [ 3:-2: 1] [] []
|
||||
3 [ 3:-2: 1] [0, 1, 2]
|
||||
3 [ 3:-2: 2] [] []
|
||||
3 [ 3:-2: 2] [0, 1, 2]
|
||||
3 [ 3:-2: 3] [] []
|
||||
3 [ 3:-2: 3] [0, 1, 2]
|
||||
3 [ 3:-1:-3] [] []
|
||||
3 [ 3:-1:-3] [0, 1, 2]
|
||||
3 [ 3:-1:-2] [] []
|
||||
3 [ 3:-1:-2] [0, 1, 2]
|
||||
3 [ 3:-1:-1] [] []
|
||||
3 [ 3:-1:-1] [0, 1, 2]
|
||||
3 [ 3:-1: 1] [] []
|
||||
3 [ 3:-1: 1] [0, 1, 2]
|
||||
3 [ 3:-1: 2] [] []
|
||||
3 [ 3:-1: 2] [0, 1, 2]
|
||||
3 [ 3:-1: 3] [] []
|
||||
3 [ 3:-1: 3] [0, 1, 2]
|
||||
3 [ 3: 0:-3] [2] [2]
|
||||
3 [ 3: 0:-3] [0, 1, -1]
|
||||
3 [ 3: 0:-2] [2] [2]
|
||||
3 [ 3: 0:-2] [0, 1, -1]
|
||||
3 [ 3: 0:-1] [2, 1] [2, 1]
|
||||
3 [ 3: 0:-1] [0, -1, -1]
|
||||
3 [ 3: 0: 1] [] []
|
||||
3 [ 3: 0: 1] [0, 1, 2]
|
||||
3 [ 3: 0: 2] [] []
|
||||
3 [ 3: 0: 2] [0, 1, 2]
|
||||
3 [ 3: 0: 3] [] []
|
||||
3 [ 3: 0: 3] [0, 1, 2]
|
||||
3 [ 3: 1:-3] [2] [2]
|
||||
3 [ 3: 1:-3] [0, 1, -1]
|
||||
3 [ 3: 1:-2] [2] [2]
|
||||
3 [ 3: 1:-2] [0, 1, -1]
|
||||
3 [ 3: 1:-1] [2] [2]
|
||||
3 [ 3: 1:-1] [0, 1, -1]
|
||||
3 [ 3: 1: 1] [] []
|
||||
3 [ 3: 1: 1] [0, 1, 2]
|
||||
3 [ 3: 1: 2] [] []
|
||||
3 [ 3: 1: 2] [0, 1, 2]
|
||||
3 [ 3: 1: 3] [] []
|
||||
3 [ 3: 1: 3] [0, 1, 2]
|
||||
3 [ 3: 2:-3] [] []
|
||||
3 [ 3: 2:-3] [0, 1, 2]
|
||||
3 [ 3: 2:-2] [] []
|
||||
3 [ 3: 2:-2] [0, 1, 2]
|
||||
3 [ 3: 2:-1] [] []
|
||||
3 [ 3: 2:-1] [0, 1, 2]
|
||||
3 [ 3: 2: 1] [] []
|
||||
3 [ 3: 2: 1] [0, 1, 2]
|
||||
3 [ 3: 2: 2] [] []
|
||||
3 [ 3: 2: 2] [0, 1, 2]
|
||||
3 [ 3: 2: 3] [] []
|
||||
3 [ 3: 2: 3] [0, 1, 2]
|
||||
3 [ 3: 3:-3] [] []
|
||||
3 [ 3: 3:-3] [0, 1, 2]
|
||||
3 [ 3: 3:-2] [] []
|
||||
3 [ 3: 3:-2] [0, 1, 2]
|
||||
3 [ 3: 3:-1] [] []
|
||||
3 [ 3: 3:-1] [0, 1, 2]
|
||||
3 [ 3: 3: 1] [] []
|
||||
3 [ 3: 3: 1] [0, 1, 2]
|
||||
3 [ 3: 3: 2] [] []
|
||||
3 [ 3: 3: 2] [0, 1, 2]
|
||||
3 [ 3: 3: 3] [] []
|
||||
3 [ 3: 3: 3] [0, 1, 2]
|
||||
8
tests/slicing2.py
Normal file
8
tests/slicing2.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
try:
|
||||
import ulab as np
|
||||
except:
|
||||
import numpy as np
|
||||
|
||||
a = np.array(range(9), dtype=np.float)
|
||||
print("a:\t", list(a))
|
||||
print("a < 5:\t", list(a[a < 5]))
|
||||
2
tests/slicing2.py.exp
Normal file
2
tests/slicing2.py.exp
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
a: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]
|
||||
a < 5: [0.0, 1.0, 2.0, 3.0, 4.0]
|
||||
Loading…
Reference in a new issue