Compare commits

...

303 commits

Author SHA1 Message Date
57ab95cc70 Add scripts and CI for ulab-circuitpython
This will check
 - that the unix port builds and passes its tests
 - that the doc build succeeds
2020-10-24 15:50:15 -05:00
d0f68c8560 Fix doc build problem in CircuitPython
CircuitPython doc build fails with this diagnostic:
```
/home/runner/work/circuitpython/circuitpython/shared-bindings/ulab/index.rst:220:'any' reference target not found: List[float]
```

Use double backticks instead of single backticks to differentiate between
a reference to a type and just "show in monospace font".
2020-10-24 15:08:48 -05:00
Zoltán Vörös
7f2c1ae52b
Merge pull request #184 from v923z/slice-patch
wrong type in indexing raises TypeError now
2020-10-24 18:46:11 +02:00
Zoltán Vörös
ee8b72addd wrong type in indexing raises TypeError now 2020-10-24 18:43:32 +02:00
Zoltán Vörös
04fa205ece
Merge pull request #182 from v923z/slice-patch
Slice patch
2020-10-23 22:12:54 +02:00
Zoltán Vörös
3617b0735e fixed indexing error 2020-10-23 22:09:49 +02:00
Zoltán Vörös
864d5a68bb fixed compilation error in approx 2020-10-23 22:00:41 +02:00
Zoltán Vörös
ccb6be5b11 fixed compilation error in eye 2020-10-23 21:55:32 +02:00
Zoltán Vörös
96be400e08 bumped version number 2020-10-23 21:47:43 +02:00
Zoltán Vörös
e2be5a001e slicing throws an IndexError, if the index is not the proper type 2020-10-23 21:44:54 +02:00
Zoltán Vörös
5c75f08118
Merge pull request #175 from DeqingSun/patch-2
Fix diagonal 0 error in row-reduction process
2020-08-24 20:17:07 +02:00
Deqing Sun
1061251e06
add MICROPY_FLOAT_CONST for mp_obj_new_float 2020-08-24 14:11:17 -04:00
Deqing Sun
07c7404d95
add name 2020-08-24 07:29:07 -04:00
Deqing Sun
b03d36bcee
add MICROPY_FLOAT_CONST to float numbers
other files may need change too.
2020-08-24 07:28:00 -04:00
Deqing Sun
d56a001f16
fix bug for invert with diagonal 0 matrix
If there is any diagonal 0 in the process of Gauss-Jordan method, swap that row with a non-0 row below.
2020-08-23 19:36:12 -04:00
Deqing Sun
df0af031f1
fix bug for determinant with diagonal 0 matrix
If there is any diagonal 0 in the process of row reduction, swap that row with a non-0 row below.
2020-08-23 19:05:57 -04:00
Zoltán Vörös
bbe39d9e41
Merge pull request #170 from v923z/cholesky-fix
fixed small error in linalg
2020-08-17 16:51:44 +02:00
Zoltán Vörös
115b3da2f6 fixed small error in linalg 2020-08-17 16:48:21 +02:00
Zoltán Vörös
fd0751144e
added link to ulab_samples 2020-08-13 10:07:56 +02:00
Zoltán Vörös
a2f27760c6
Update README.md
updated link to compiled firmware
2020-08-07 18:03:18 +02:00
Zoltán Vörös
47af016806
Merge pull request #167 from ciscorn/pyi
Minor fixes on Python stubs
2020-08-07 16:38:30 +02:00
Taku Fukada
f9322380de Minor fixes on Python stubs 2020-08-06 23:49:51 +09:00
Zoltán Vörös
72e1924479
Merge pull request #166 from mdaeron/master
Remove double parentheses in numerical.c to avoid errors when compiling unix port on MacOS
2020-08-04 19:47:37 +02:00
mdaeron
60f6fa6e6b
Remove double parentheses in numerical.c
This avoids errors when compiling the unix port on MacOS.
2020-08-04 15:31:19 +02:00
mdaeron
60c8d62d4b
Merge pull request #1 from v923z/master
Update to current v923z/ulab
2020-08-04 15:07:34 +02:00
Zoltán Vörös
8f5d1a949e
Merge pull request #165 from v923z/argsort
fixed small issue in argsort, and updated documentation
2020-08-03 20:44:41 +02:00
Zoltán Vörös
368b23ff8b fixed small issue in argsort, and updated documentation 2020-08-03 20:41:12 +02:00
Zoltán Vörös
3aa37c8e55
Update README.md
added link to ESP32 firmware
2020-08-03 19:42:13 +02:00
Zoltán Vörös
4a771347bb
Update README.md
fixed links to compiled firmware
2020-07-31 20:35:31 +02:00
Zoltán Vörös
3ba186b19c
Merge pull request #159 from ciscorn/pyi
Add type hints to Python stubs
2020-07-31 16:14:46 +02:00
Taku Fukada
129ad86b8e Add type hints to Python stubs 2020-07-31 01:06:48 +09:00
Zoltán Vörös
4e218f9d81 bumped version number to 0.54.1 2020-07-29 18:51:57 +02:00
Zoltán Vörös
4c1a7c0933
Merge pull request #158 from v923z/sizet
fixes https://github.com/v923z/micropython-ulab/issues/157
2020-07-29 18:47:33 +02:00
Zoltán Vörös
4690ef7c2c fixed error in ulab_create.c 2020-07-29 18:44:51 +02:00
Zoltán Vörös
345b74e3ca switched to size_t length, and added sanity checks in approx.c 2020-07-29 18:38:08 +02:00
Zoltán Vörös
3a6deef855 switched to size_t length in linspace 2020-07-29 18:22:25 +02:00
Zoltán Vörös
ddfe1754ca switched to size_t length in reshape 2020-07-29 18:18:42 +02:00
Zoltán Vörös
bae51f8edb switched to size_t length in poly.c 2020-07-29 18:17:11 +02:00
Zoltán Vörös
a789bd67d0 switched to size_t length in fft 2020-07-29 18:12:27 +02:00
Zoltán Vörös
11a7ecff6d
Update README.md
Fixed typo in readme.
2020-07-28 07:55:56 +02:00
Zoltán Vörös
e77f74df29
Update README.md
added link to compiled firmware repository
2020-07-27 19:43:26 +02:00
Zoltán Vörös
9159465d62
Update README.md
Added a list of firmware variants with `ulab` included.
2020-07-26 23:14:35 +02:00
Zoltán Vörös
61073f79a8
Merge pull request #154 from jonathanhogg/single_floats
Add MICROPY_FLOAT_CONST to all fp constants.
2020-07-24 16:04:50 +02:00
Jonathan Hogg
f41d3eeeb7 Add MICROPY_FLOAT_CONST to all fp constants.
In order to get ulab to compile correctly against single floating point 
all of the constants need to switch to single format. Conveniently 
MicroPython has provided a macro to manage this switch. Use this 
througout.

Unfortunately also introduces a bunch of whitespace changes because 
there is a mass of trailing whitespace in the codebase and my editor is 
(correctly) configured to remove this.
2020-07-24 11:58:45 +01:00
Zoltán Vörös
d04d280f9d
Merge pull request #153 from rcolistete/fix_numerical
numerical.c : fixed 'out' may be used uninitialized error in function…
2020-07-24 08:36:35 +02:00
Roberto Colistete Jr
39afe5f2ad numerical.c : fixed 'out' may be used uninitialized error in function 'numerical_sort_helper' 2020-07-23 18:18:29 -03:00
Zoltán Vörös
1f1e181f5d
Update README.md
fixed some smaller issues in the readme.
2020-07-23 22:55:34 +02:00
Zoltán Vörös
05f9f9a7b3
Merge pull request #152 from v923z/docs
updated docs to reflect recent changes
2020-07-23 22:48:23 +02:00
Zoltán Vörös
e1801bad43 updated docs to reflect recent changes 2020-07-23 22:47:16 +02:00
Zoltán Vörös
359cf78f35
Merge pull request #151 from v923z/docs
fixed indentation error in approx.c
2020-07-23 22:11:03 +02:00
Zoltán Vörös
f8bf869a45 fixed indentation error in approx.c 2020-07-23 22:08:29 +02:00
Zoltán Vörös
9a82e70728
Merge pull request #145 from rcolistete/norm
added linalg.norm : for vector or matrix
2020-07-23 21:59:27 +02:00
Roberto Colistete Jr
c98cdfdb97 lingalg.norm : fixed comments again 2020-07-23 16:57:25 -03:00
Roberto Colistete Jr
2d12162d8f lingalg.norm : moved after inv and fixed coments 2020-07-23 16:42:35 -03:00
Roberto Colistete Jr
7ad3542de6 lingalg.norm : fixed coments 2020-07-23 16:19:05 -03:00
Roberto Colistete Jr
0064995d2c lingalg.norm : added coments and v0.54.0 to ulab-change-log 2020-07-23 16:15:51 -03:00
Roberto Colistete Jr
86581222c4 fix lingalg.norm to new folder structure 2020-07-23 16:07:11 -03:00
Zoltán Vörös
ab085333ca
Merge pull request #150 from v923z/create-rename
renamed create.c/h, moved stubs from ulab.c to ulab_create.c
2020-07-23 19:13:52 +02:00
Zoltán Vörös
fe8a10dd94 renamed create.c/h, moved stubs from ulab.c to ulab_create.c 2020-07-23 19:10:50 +02:00
20243311e1 Remove trace of 'extras' module
.. this caused a build error in circuitpython:

../../extmod/ulab/code/ulab.c:162:9: error: "ULAB_EXTRAS_MODULE" is not defined, evaluates to 0 [-Werror=undef]
  162 |     #if ULAB_EXTRAS_MODULE
      |         ^~~~~~~~~~~~~~~~~~
2020-07-23 12:01:58 -05:00
Zoltán Vörös
f6fd605300
Merge pull request #149 from v923z/documentation
Documentation
2020-07-23 19:01:15 +02:00
Zoltán Vörös
a3678b0de7 re-arranged functions in compare.c 2020-07-23 18:56:18 +02:00
Zoltán Vörös
e91e2db0dd re-arranged functions in approx.c 2020-07-23 18:54:03 +02:00
Zoltán Vörös
ac38e8bc5a re-arranged functions in poly.c 2020-07-23 18:51:57 +02:00
Zoltán Vörös
9cf30ad9f2 re-arranged functions in linalg.c 2020-07-23 18:50:47 +02:00
Zoltán Vörös
2b55b79471 re-arranged functions in numerical.c 2020-07-23 18:47:14 +02:00
Zoltán Vörös
e5341bcf35 added STATIC to create.c 2020-07-23 18:23:52 +02:00
Zoltán Vörös
c93faa9a9c fixed docs in approx.c 2020-07-22 21:56:06 +02:00
Zoltán Vörös
b6252474e9 fixed docs in fft.c 2020-07-22 21:53:10 +02:00
Zoltán Vörös
4013206dc2 fixed typos 2020-07-22 21:20:29 +02:00
Zoltán Vörös
4724b516c2 updated readme 2020-07-22 21:17:08 +02:00
Zoltán Vörös
e4d07b733a
Merge pull request #148 from v923z/documentation
Documentation
2020-07-22 21:10:10 +02:00
23e1ef3b11 Add newlines where required in .rst files 2020-07-22 14:05:46 -05:00
Zoltán Vörös
4759264994 added user/ files 2020-07-22 20:34:48 +02:00
Zoltán Vörös
80503300ad re-organised code, so that circuitpython documentation can automatically be generated 2020-07-22 20:19:14 +02:00
Zoltán Vörös
651f2029af
Merge pull request #147 from v923z/documentation
Documentation
2020-07-22 19:18:29 +02:00
Zoltán Vörös
cc721aa216 added stubs to vectorise.c 2020-07-22 19:13:09 +02:00
Zoltán Vörös
d6ae0bcdde added stubs to poly.c 2020-07-22 18:56:28 +02:00
Zoltán Vörös
f1dab38726 added stubs to numerical.c 2020-07-22 18:52:51 +02:00
Zoltán Vörös
8b268aedd1 added stubs to linalg.c 2020-07-22 18:44:45 +02:00
Zoltán Vörös
e33ef4ec52
Merge pull request #146 from v923z/arange-fix
fixed arange
2020-07-22 18:35:34 +02:00
Zoltán Vörös
b60e14d380 fixed arange 2020-07-22 18:31:40 +02:00
Roberto Colistete Jr
8d439c3332 added linalg.norm : for vector or matrix 2020-07-22 04:45:51 -03:00
Zoltán Vörös
b3e76e1f0a
Merge pull request #143 from rcolistete/dot_vectors
linalg.dot : also accepts 2 vectors of same dimension
2020-07-22 08:37:12 +02:00
Roberto Colistete Jr
a68b7daa32 linalg.dot : changed error message: vectors must have same lengths 2020-07-22 03:34:39 -03:00
Zoltán Vörös
4b1741515e
Merge pull request #141 from rcolistete/fix_create_arange_ceil
fix arange : missing MICROPY_FLOAT_C_FUN(ceil) in create.c
2020-07-22 08:07:15 +02:00
Roberto Colistete Jr
0d80175933 linalg.dot : also accepts 2 vectors of same dimension 2020-07-21 19:28:55 -03:00
Roberto Colistete Jr
f7358cd65f fix arange : inserted missing MICROPY_FLOAT_C_FUN(ceil) in code/create.c 2020-07-21 12:35:38 -03:00
Zoltán Vörös
3a5d17c10e added stubs to filter.c 2020-07-20 22:35:35 +02:00
Zoltán Vörös
3acd543630 added stubs to fft.c 2020-07-20 22:33:36 +02:00
Zoltán Vörös
5e72d76b6e added stubs to compare.c 2020-07-20 22:27:48 +02:00
Zoltán Vörös
4194cce461 added stubs to approx.c 2020-07-20 22:08:56 +02:00
Zoltán Vörös
b10117a849
Merge pull request #140 from v923z/arange
add arange
2020-07-20 20:50:40 +02:00
Zoltán Vörös
3b041e1059 found and fixed small error in create.h 2020-07-20 20:48:38 +02:00
Zoltán Vörös
161d8b0ed0 removed unused one constant in create.c 2020-07-20 20:41:34 +02:00
Zoltán Vörös
eea4c15ad0 found and fixed small bug in dtype assignment 2020-07-20 20:29:55 +02:00
Zoltán Vörös
c6f2c928b6 fixed dtype inconsistency in arange 2020-07-20 20:22:12 +02:00
Zoltán Vörös
e202d38b0e fixed argument parsing error in arange 2020-07-20 18:31:46 +02:00
Zoltán Vörös
932e0a2b91 first implementation of arange 2020-07-19 22:19:57 +02:00
Zoltán Vörös
c6afd5ad0d
Merge pull request #138 from v923z/trapz
first implementation of trapz
2020-07-19 19:43:58 +02:00
Zoltán Vörös
3820dac6ee
Merge branch 'master' into trapz 2020-07-16 22:32:10 +02:00
Zoltán Vörös
4581034325 no functional changes 2020-07-16 22:28:20 +02:00
Zoltán Vörös
13ef14c29a even more linting 2020-07-16 22:27:49 +02:00
Zoltán Vörös
7739ae21f8 even more linting 2020-07-16 22:27:01 +02:00
Zoltán Vörös
49be9e0ba7 more linting 2020-07-16 22:07:00 +02:00
Zoltán Vörös
bdaa4b3984 fixed linting issues 2020-07-16 22:05:33 +02:00
Zoltán Vörös
9ab8ce2153 fixed typo 2020-07-16 21:50:10 +02:00
Zoltán Vörös
fbe8151f50 removed trapz from approx 2020-07-16 21:28:08 +02:00
Zoltán Vörös
33451ee285
Merge branch 'master' into trapz 2020-07-16 21:18:09 +02:00
Zoltán Vörös
dfe560150a first implementation of trapz 2020-07-16 21:12:46 +02:00
Zoltán Vörös
f5c15f0c4c reverted changes in master 2020-07-16 21:10:14 +02:00
Zoltán Vörös
0443a2a3ed fixed small bug in trapz 2020-07-16 20:55:42 +02:00
Zoltán Vörös
059994774d first implementation of trapz 2020-07-16 20:52:53 +02:00
Zoltán Vörös
f171235a95
Merge pull request #136 from rcolistete/fix_gcc-arm_v10.1
Fix Incompatibility between gcc-arm 10.1 and ulab + MicroPython
2020-07-16 08:49:37 +02:00
Roberto Colistete Junior
e75729cb27
Update vectorise.h 2020-07-16 03:38:15 -03:00
Roberto Colistete Junior
3bb1417211
Update extras.h 2020-07-16 03:37:35 -03:00
Zoltán Vörös
cc134935f3
Merge pull request #134 from rcolistete/fix_thread
fixed incompatibility when enabling '_thread' module from the make command
2020-07-14 21:33:43 +02:00
Roberto Colistete Junior
40503d0f7e
not compatible with '_thread' module
ulab not compatible with enabling '_thread' module from the make command :
- building ulab with thread enabled in MicroPython :
`$ make -j8 CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' BOARD=PYBV11 USER_C_MODULES=../../../ulab all`
yields a firmware with '_thread' module but without 'ulab' module, because 'CFLAGS_EXTRA' is used in the make command and in 'ulab/code/micropython.mk', so the value of the last is ignored;
- the solution is to use 'override' and '+=' in in last line of 'ulab/code/micropython.mk' :
`override CFLAGS_EXTRA += -DMODULE_ULAB_ENABLED=1`
2020-07-14 10:55:10 -03:00
Zoltán Vörös
48cb939839
Merge pull request #130 from v923z/argmin-fix
fixed argmin/argmax error in issue #129
2020-06-29 21:49:28 +02:00
Zoltán Vörös
fe7aa27238 fixed argmin/argmax error in issue #129 2020-06-29 21:46:45 +02:00
Zoltán Vörös
e9258fe4e7
Merge pull request #127 from v923z/sosfilter
Sosfilter
2020-06-22 19:58:20 +02:00
Zoltán Vörös
2f55b71542 updated user manual 2020-06-19 22:29:00 +02:00
Zoltán Vörös
526ffb4c78 updated readme, version number and change log 2020-06-19 22:06:47 +02:00
Zoltán Vörös
17c6d605ca added zi keyword argument to sosfilt 2020-06-18 22:24:26 +02:00
Zoltán Vörös
f9cf519843 first implementation of sosfilt 2020-06-17 22:24:17 +02:00
Zoltán Vörös
a223de9c73
Merge pull request #125 from v923z/openmv-fix
fixes compilation error in openmv
2020-06-16 20:13:16 +02:00
Zoltán Vörös
37140d531e added mp_obj_slice_indices to ndarray.c, so that it compiles for openmv 2020-06-16 19:32:41 +02:00
Zoltán Vörös
1231ac877e version that actually compiles in openmv 2020-06-15 22:46:46 +02:00
Zoltán Vörös
a73201a5a4 fixes compilation error (compatibility with older versions of micropython) in openmv 2020-06-12 07:50:37 +02:00
Zoltán Vörös
4a3c12e427
Merge pull request #123 from jepler/fix-actions-paths
github actions: Fix paths directives
2020-06-01 18:29:18 +02:00
d3b9096790 github actions: Fix paths directives
I noticed on #122 that no actions were run.  I believe this is because
the paths directives had no wildcards, contrary to the examples at
https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths

Eliminating the paths: restrictions entirely may be an equally valid
course of action.
2020-06-01 10:28:48 -05:00
Jeff Epler
0394801933
Merge pull request #122 from jepler/circuitpy-is-int
Updates needed to build latest code on CircuitPython
2020-06-01 10:26:07 -05:00
Jeff Epler
79f424759c
Merge pull request #121 from v923z/max-fix
fixes min/max error from adafruit/circuitpython #2984
2020-06-01 09:21:32 -05:00
cbdd1295c1 Mark parameters as unused
As far as I can tell, these are not checked for the sake of efficiency.

This silences compiler diagnostics when building CircuitPython
```
../../extmod/ulab/code/approx.c:25:12: error: no previous prototype for ‘approx_python_call’ [-Werror=missing-prototypes]
 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) {
            ^~~~~~~~~~~~~~~~~~
```
2020-06-01 09:00:42 -05:00
a2aa5d3a58 approx: Mark functions as stat if they are used only in this file
This fixes the diagnostic when building circuitpython:
```
../../extmod/ulab/code/approx.c:25:12: error: no previous prototype for ‘approx_python_call’ [-Werror=missing-prototypes]
 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) {
            ^~~~~~~~~~~~~~~~~~
```
2020-06-01 08:59:35 -05:00
1f3836d16f ndarray: Let mp_obj_is_int work on circuitpython 2020-06-01 08:29:34 -05:00
Zoltán Vörös
666dc77fad fixes min/max error from adafruit/circuitpython #2984 2020-06-01 08:42:43 +02:00
Zoltán Vörös
9da0a5ee0a
Merge pull request #120 from v923z/approx
added benchmark to bisect, extended readme
2020-05-19 21:15:08 +02:00
Zoltán Vörös
ddbbca7462 added benchmark to bisect, extended readme 2020-05-19 21:12:53 +02:00
Zoltán Vörös
0249fcd304
Merge pull request #118 from v923z/approx
Approx
2020-05-19 20:55:10 +02:00
Zoltán Vörös
077bcc51ab
Merge branch 'master' into approx 2020-05-19 20:53:33 +02:00
Zoltán Vörös
e509d3caa8
Merge pull request #114 from v923z/print
added set_printoptions/get_printoptions functions
2020-05-19 20:46:56 +02:00
Zoltán Vörös
78438727c4
Merge branch 'master' into print 2020-05-19 20:44:54 +02:00
Zoltán Vörös
17b76a60bb
Merge pull request #119 from v923z/binary_fix
fix for issue 117
2020-05-19 20:37:07 +02:00
Zoltán Vörös
8a79c80232 changed order of items in table of contents 2020-05-19 19:07:05 +02:00
Zoltán Vörös
33d9a5e410 fixed documentation error 2020-05-19 19:04:29 +02:00
Zoltán Vörös
f00d5dc6de fix for issue 117 2020-05-19 08:56:34 +02:00
Zoltán Vörös
8631b771d2 cleaned up documentation on approx sub-module 2020-05-18 19:44:26 +02:00
Zoltán Vörös
e8bac80c52
Merge pull request #116 from v923z/vectorize
Vectorize
2020-05-07 10:03:08 +02:00
Zoltán Vörös
8056b4e959 updated manual with a section on function vectorisation 2020-05-07 09:42:19 +02:00
Zoltán Vörös
e31d7ab906 updated readme 2020-05-07 00:27:11 +02:00
Zoltán Vörös
4a311c1d64 updated manual 2020-05-07 00:23:53 +02:00
Zoltán Vörös
e2cae23253 vectorize can now take scalars 2020-05-06 23:49:07 +02:00
Zoltán Vörös
6590acc6c4 added vectorisation of generic python function 2020-05-06 23:29:27 +02:00
Zoltán Vörös
d5e59cc21f no functional change, backup 2020-05-06 21:22:54 +02:00
Zoltán Vörös
df6e3065b8
Merge pull request #115 from v923z/equal
added equal/not_equal to compare.c
2020-05-05 21:18:15 +02:00
Zoltán Vörös
8f5edbb808 bumped ulab version number to 0.45 2020-05-05 21:16:10 +02:00
Zoltán Vörös
35cdc9f976 began work with curve_fit 2020-05-05 21:13:53 +02:00
Zoltán Vörös
a6ec2e65dd updated readme 2020-05-03 17:07:50 +02:00
Zoltán Vörös
5c027e44c1 added function wrapper approx_python_call 2020-05-03 17:05:27 +02:00
Zoltán Vörös
2ead6cbc21 added equal/not_equal to compare.c 2020-05-03 11:00:41 +02:00
Zoltán Vörös
d991d9d248 moved the section on set_printoptions in the manual 2020-05-01 18:46:38 +02:00
Zoltán Vörös
70666817ce added set_printoptions/get_printoptions functions 2020-05-01 11:53:39 +02:00
Zoltán Vörös
6615290fdf add approx sub-module with fmin, newton, and bisect functions 2020-04-30 22:33:21 +02:00
Zoltán Vörös
cf61d728e7
Merge pull request #110 from jepler/excluding-modules
ulab.h: Actually allow excluding modules
2020-04-27 08:36:04 +02:00
a7502f6243 ulab.h: Actually allow excluding modules 2020-04-26 20:37:14 -05:00
Zoltán Vörös
314bb010bb
Merge pull request #106 from v923z/minimax
compare sub-module with minimum/maximum and clip functions
2020-04-21 23:30:03 +02:00
Zoltán Vörös
a081c28998
Merge branch 'master' into minimax 2020-04-21 23:27:31 +02:00
Zoltán Vörös
6ff9d2cb04 add test script for compare module 2020-04-21 23:24:24 +02:00
Zoltán Vörös
67be0c6762 updated manual with minimum/maximum and clip functions 2020-04-21 22:36:11 +02:00
Zoltán Vörös
855384f579 implemented minimum/maximum and clip in the compare module 2020-04-21 22:35:17 +02:00
Zoltán Vörös
c7e2c8a2e2
Merge pull request #105 from v923z/poly
pulling in jepler's argument handling improvements
2020-04-20 23:22:24 +02:00
Zoltán Vörös
fb05e2585c pulling in jepler's argument handling improvements 2020-04-20 23:20:30 +02:00
Zoltán Vörös
c9519e59a8
Merge pull request #103 from v923z/revert-101-polyfit-argument-checking
Revert "polyfit: Argument handling improvements"
2020-04-20 23:05:01 +02:00
Zoltán Vörös
3c1fb52efb
Merge pull request #104 from v923z/compile
fix compilation errors due to casting of floats
2020-04-20 23:03:12 +02:00
Zoltán Vörös
a0e5f3a474 fix compilation errors due to casting of floats 2020-04-20 22:55:21 +02:00
Zoltán Vörös
f014128595 backup commit 2020-04-20 20:54:24 +02:00
Zoltán Vörös
d29cc8632e
Revert "polyfit: Argument handling improvements" 2020-04-20 16:20:44 +02:00
Zoltán Vörös
a844e1bfb9
Merge pull request #101 from jepler/polyfit-argument-checking
polyfit: Argument handling improvements
2020-04-20 16:15:52 +02:00
d0b11a6081 polyfit: Argument handling improvements
* In the 3-args case, the lengths of the arguments were not checked
 * in the 3-args case, the type of the 2nd argument was not checked
 * gcc falsely diagnosed a `maybe-uninitialized` variable because it
   did not see that the branches of the if() statement were mutually
   exclusive

It's this third issue that originally drew my attention to this code,
adafruit/circuitpython#2787
2020-04-20 07:58:30 -05:00
Zoltán Vörös
434211d401
updated README.md
added a short comment on the `numerical` sub-module
2020-04-18 11:45:41 +02:00
Zoltán Vörös
778c7e9bd8
Merge pull request #99 from v923z/compile
fixed compilation error in issue #98
2020-04-18 09:08:27 +02:00
Zoltán Vörös
d1f2d80581 fixed compilation error in issue #98 2020-04-18 09:05:47 +02:00
Zoltán Vörös
a746bd8e09
Merge pull request #96 from v923z/dot
fix indexing error in linalg.dot
2020-04-14 09:08:11 +02:00
Zoltán Vörös
2b7d96f25d fix indexing error in linalg.dot 2020-04-14 09:05:22 +02:00
Jeff Epler
22813d6736
Merge pull request #91 from v923z/argmax
fixed argmin/argmax function for iterables
2020-04-13 15:33:48 -05:00
470ea6578a add expected-output of tests 2020-04-13 15:29:45 -05:00
5fefea4871 add test 2020-04-13 15:28:49 -05:00
Zoltán Vörös
e83ef7a31d fixed another indexing error in argmin/argmax 2020-04-13 21:56:55 +02:00
Zoltán Vörös
1a4ac564d4 fixed indexing error in argmin/argmax 2020-04-13 21:27:41 +02:00
Zoltán Vörös
78ae57005c argmin/argmax raises ValueError with empty iterables 2020-04-12 19:32:08 +02:00
Zoltán Vörös
12d2c9127e
Merge pull request #92 from v923z/transpose
fix transpose function to conform to numpy
2020-04-09 21:27:43 +02:00
Zoltán Vörös
84150d4166 fix transpose function to conform to numpy 2020-04-09 12:38:19 +02:00
Zoltán Vörös
7b8d894413
extended readme 2020-04-09 10:18:52 +02:00
Zoltán Vörös
7ec8a25a84 fixed argmin/argmax function for iterables 2020-04-07 23:06:41 +02:00
Zoltán Vörös
a09c5f4c25
Merge pull request #90 from v923z/argsort
fix error in argsort
2020-04-07 22:35:59 +02:00
Zoltán Vörös
ab9a26dc98 fix error in argsort 2020-04-07 22:34:00 +02:00
Zoltán Vörös
55fb2e073b
Merge pull request #88 from jepler/fix-compiler-diagnostics
Fix compiler diagnostics normally enabled in CircuitPython; fix arg parsing bugs
2020-04-06 23:54:54 +02:00
86e7155ab7 generate_slice: Properly fix -Wsign-compare diagnostic
Instead of making the local unsigned--it must be permitted to
contain negative values--make the function parameter signed.
2020-04-06 16:38:51 -05:00
Zoltán Vörös
280f04660e
Merge pull request #86 from jepler/mp-flags-compat
Restore circuitpython compatibility after #84
2020-04-06 21:07:00 +02:00
f1f10c27a8 Fix -Wsign-compare diagnostics
.. by adding casts or changing the type of a local variable.
2020-04-06 08:38:43 -05:00
d41fb86fd0 add test of linspace 2020-04-06 08:36:25 -05:00
daf4b07ef7 Fix -Wunused-parameter diagnostics
Here we actually get bugs fixed!  At many sites, mp_parse_arg_all's
first argument was the number of positional arguments required, rather
than the number actually supplied.

This fixes e.g., the bug where the linspace third positional argument
was not used as the "num" argmuent, and where too many positional args
were not treated as an error:

    >>> ulab.linspace(0, 1, 9)
    array([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0], dtype=float)
    >>> ulab.linspace(0, 1, 1, 1)
    TypeError: extra positional arguments given

In the case of argmin/argmax of an iterable, it is now signaled to the user
that only the axis=None case is supported, instead of giving a wrong
result.
2020-04-06 08:33:18 -05:00
22605c6fe9 Fix -Wshadow diagnostics
A declaration of a local variable can "shadow" a parameter or another
local variable declared in an outer scope.

Since this can create confusion about which variable is referred to, it
is better to fix these diagnostics.

A variety of changes were made:
 - Add curly braces to reduce the scope of a variable (ndarray_unary_op)
   so that its scope has ended before the next scope where it is used
 - be consistent about whether a variable is declared with for-loop scope
   or not (ndarray_print_row)
 - remove a declaration so that the outer variable is used, after verifying
   this will function properly (insert_slice_list)
 - rename a variable introduced by a macro (RUN_SUM, RUN_STD) so it does
   not shadow another variable
2020-04-06 08:12:47 -05:00
8aebfd9f4e remove unused function get_nditerable_len 2020-04-06 08:06:33 -05:00
be1e2b28e0 Fix -Wmissing-prototypes diagnostics
These functions need prototypes in a header, as they are defined in one
source file and used in another.
2020-04-06 08:05:43 -05:00
11ed69dacf Fix -Wmissing-prototypes diagnostics
This diagnostic flag, enabled by default in CircuitPython, requires that
nonstatic functions have a previous prototype.  Since placing prototypes
in header files is the only way that mismatched arguments between function
definition and use sites can be detected, this is a valuable warning.

In the case of ulab, the majority of the  changes required were to mark
functions as "static" that were used only in a single C source file.
2020-04-06 08:05:20 -05:00
59cc5ed6c6 circuitpython compatibility 2020-04-06 07:38:12 -05:00
Zoltán Vörös
ae7cadee8a
Merge pull request #84 from v923z/equal 2020-04-06 09:14:42 +02:00
Zoltán Vörös
1001ed44f5 fixed small glitch in == and != 2020-04-06 09:08:44 +02:00
Zoltán Vörös
c69f2d9256 implemented the ==, and != binary operators 2020-04-04 22:14:36 +02:00
Zoltán Vörös
8b072b7c0d
Merge pull request #83 from v923z/trace
This PR adds the trace function to linalg, and fixes a couple of glitches in the documentation.
2020-04-03 22:31:40 +02:00
Zoltán Vörös
acfd1d0760 added documentation to the trace function, and cleaned up other bits 2020-04-03 13:01:04 +02:00
Zoltán Vörös
ceaede8cb8 added trace to linalg, trimmed other code 2020-04-03 12:07:11 +02:00
Zoltán Vörös
034cdaf7e3
Merge pull request #82 from v923z/sort
another attempt at fixing issue #79
2020-04-02 21:27:57 +02:00
Zoltán Vörös
32e0bf5bde
Merge branch 'master' into sort 2020-04-02 21:26:06 +02:00
Zoltán Vörös
89d73974e3 another attempt at fixing issue #79 2020-04-02 21:22:45 +02:00
Zoltán Vörös
89579b6e36
Merge pull request #75 from v923z/power
adds an implementation of the `**` binary operator, and the reversed binary operators
2020-04-02 19:10:06 +02:00
Zoltán Vörös
b4f83997e7
Merge branch 'master' into power 2020-04-02 19:08:00 +02:00
Jeff Epler
0d5dc6b0d4
Merge pull request #78 from v923z/argmax
contraction returns scalar, if dimension is 0
2020-04-02 08:56:08 -05:00
Zoltán Vörös
411a5ffbd2
Merge pull request #81 from v923z/unistd
added unistd.h to ndarray.c to fix issue #73
2020-04-02 11:06:56 +02:00
Zoltán Vörös
2e27a356f5 added unistd.h to ndarray.c to fix issue #73 2020-04-02 11:04:23 +02:00
Zoltán Vörös
26d740ef77
Merge pull request #80 from v923z/copy
added fast initialisation option to ndarray_make_new_core, and updated docs
2020-04-02 09:55:33 +02:00
Zoltán Vörös
a3d77a3fa1 updated docs for ndarray_make_new_core 2020-04-02 09:52:04 +02:00
Zoltán Vörös
1648325d69 added fast ndarray initialisation option 2020-04-02 09:41:13 +02:00
Zoltán Vörös
272685dc50
Merge pull request #79 from v923z/sort
fixed indexing error in sort function
2020-04-01 23:46:38 +02:00
Zoltán Vörös
04422f3da5 fixed indexing error in sort function 2020-04-01 23:43:31 +02:00
Zoltán Vörös
c2c525247c contraction returns scalar, if dimension is 0 2020-04-01 22:56:07 +02:00
Zoltán Vörös
2517d8be25 incremented version number, modified change log 2020-03-31 23:14:54 +02:00
Zoltán Vörös
3153c86f09 fixed empty array error 2020-03-31 23:02:14 +02:00
Zoltán Vörös
ae0dfbc126 fixed binary error with empty arrays 2020-03-31 22:52:06 +02:00
Zoltán Vörös
a2d52b6454 implemented the ** operator, and the reversed binary operators 2020-03-31 21:38:47 +02:00
Zoltán Vörös
ac3f03c3ba
Merge pull request #70 from v923z/testfix
added slicing2.py.exp to the test suite
2020-03-30 23:34:13 +02:00
Zoltán Vörös
d55df3d4a0 added slicing2.py.exp to the test suite 2020-03-30 23:31:14 +02:00
Zoltán Vörös
3ec9f5a5bb
Merge pull request #69 from jepler/boolean-cpy
Add to #68 for CircuitPython compatibility
2020-03-30 22:36:54 +02:00
c66509f66e add test of boolean slicing 2020-03-30 15:34:26 -05:00
53c158bde3 ndarray.c: circuitpython needs translate() for mp_raise 2020-03-30 15:34:01 -05:00
cbe41034a3 ndarray.h: provide mp_obj_is_bool for circuitpython 2020-03-30 15:33:45 -05:00
Zoltán Vörös
b3562ae78c Boolean indexing raises TypeError, if index is not of Boolean type 2020-03-30 12:58:04 +02:00
Zoltán Vörös
ea4a7422ef fixing Boolean indexing issue 2020-03-30 12:45:54 +02:00
Zoltán Vörös
97f23da0c1
removed warning about missing roundf.c
The file has been appended to the makefile upstream.
2020-03-25 08:57:00 +01:00
Zoltán Vörös
84558f9447
added a workaround for a linker error 2020-03-20 22:14:58 +01:00
Zoltán Vörös
a91b36986d
Merge pull request #65 from jepler/slice-length-crash
slice_length: avoid implementation-defined division by negative number
2020-03-17 08:15:48 +01:00
3dc52575f0 slice_length: avoid implementation-defined division by negative number
In CircuitPython (only), a the slice assignment to [-1👎-3] of an
ndarray of length 1 caused a crash.  This appears to be because in
CircuitPython, the internal pointer of an empty array was NULL rather than
pointing at some memory which happened to be valid and assignable.

This appears to be a corner case of how integer promotion rules work in C.
The expression `slice.stop - slice.start + (slice.step + correction)`
is type `unsigned long` and on a LP64 platform its value is
18446744073709551614.  This led to the function returning that this slice
had length 1 instead, during the automated tests.

By casting to signed types capable of holding indices and sizes, the
problem is corrected on both platforms.
2020-03-16 22:09:39 -05:00
Zoltán Vörös
155e6eea60
Merge pull request #63 from v923z/round
added around and arctan2 to the vector sub-module, and extended ndarray initialisation options
2020-03-16 22:19:05 +01:00
Zoltán Vörös
94e5b304d2 added arctan2 to vectorise.c 2020-03-16 19:36:37 +01:00
Zoltán Vörös
49e2e68f9b added around to vectorise.c, and implemented array initialiation from another ndarray 2020-03-12 17:28:23 +01:00
Zoltán Vörös
3e53136a93 ndarrays can now be initialised from ndarrays 2020-03-12 07:17:54 +01:00
Zoltán Vörös
7ec399c58b
Merge pull request #62 from v923z/cholesky-test
added tests for linalg, and poly
2020-03-11 21:38:35 +01:00
Zoltán Vörös
47fd7964e8 generated .exp test files 2020-03-11 21:31:36 +01:00
Zoltán Vörös
e3a74453a8 trying to fix test routines 2020-03-11 18:53:06 +01:00
Zoltán Vörös
18d13e4252 added tests for inv, and det 2020-03-11 17:15:30 +01:00
Zoltán Vörös
cb1b1d352b added test files for the poly sub-module 2020-03-11 07:31:00 +01:00
Zoltán Vörös
c354657eda added test files for the Cholesky decomposition 2020-03-11 07:04:26 +01:00
Zoltán Vörös
037cd6e733 re-named spectrum->spectrogram, updated manual 2020-03-10 21:11:44 +01:00
Jeff Epler
1095994a4a
Merge pull request #59 from v923z/cholesky
added Cholesky decomposition to linalg.c, updated documentation
2020-03-10 14:51:48 -05:00
Jeff Epler
ea2bf3c236
Merge pull request #57 from v923z/spectrum
moved spectrum to extras module
2020-03-10 14:51:37 -05:00
Zoltán Vörös
525fbb6527 added Cholesky decomposition to linalg.c, updated documentation 2020-03-10 20:40:11 +01:00
Zoltán Vörös
5d0eab244b added function declarations to linalg.h 2020-03-09 21:22:31 +01:00
Zoltán Vörös
6b3d43846f moved spectrum to extras module 2020-03-09 20:47:58 +01:00
Jeff Epler
8241546378
Merge pull request #56 from v923z/workflow
include only code changes in workflow file
2020-03-07 20:31:00 -06:00
Zoltán Vörös
0434045293
Update README.md
Clarified statement on CP builds.
2020-03-07 09:46:50 +01:00
Zoltán Vörös
34c2355a2a tried to fix workflow file 2020-03-07 09:36:10 +01:00
Zoltán Vörös
0faa89e3a5 tried to fix workflow file 2020-03-07 09:33:48 +01:00
Zoltán Vörös
c0979509b4 run CI only for changes in code/, and tests/ 2020-03-07 09:31:30 +01:00
Zoltán Vörös
ef2c91c1fb
Merge pull request #55 from v923z/readme
updated readme
2020-03-06 21:18:54 +01:00
Zoltán Vörös
fb1153d3b3 updated readme 2020-03-06 21:15:49 +01:00
Zoltán Vörös
847c7f9d63
Merge pull request #54 from v923z/readme
updated readme
2020-03-06 18:31:04 +01:00
Zoltán Vörös
ab8b5fe4b1 updated readme 2020-03-06 18:27:59 +01:00
Zoltán Vörös
700e3ff1ac
Merge pull request #53 from v923z/jepler-patch-1
Update README.md with circuitpython mentions
2020-03-05 19:24:42 +01:00
Jeff Epler
a35c4ff1d8
Update README.md with circuitpython mentions 2020-03-05 07:22:22 -06:00
Zoltán Vörös
a6ebfc1ade
Merge pull request #52 from codemee/master
Deleting "#define MODULE_ULAB_ENABLED (1)"
2020-03-02 07:57:33 +01:00
codemee
882294dabf
Deleting "#define MODULE_ULAB_ENABLED (1)"
Since the micropython.mk file has added the following line

```
CFLAGS_EXTRA = -DMODULE_ULAB_ENABLED=1
```

There's no need to add #define MODULE_ULAB_ENABLED (1) in the mpconfigport.h.Or it would make redefined errors while compiling code.
2020-03-02 12:26:19 +08:00
Zoltán Vörös
adda973b56
Merge pull request #50 from jepler/slicing-fixes
Slicing fixes
2020-03-01 18:47:59 +01:00
Jeff Epler
903016ec44 slicing: add test of slice assignment
This tests that ulab and python3/numpy match on various slice assignments
that preserve the length of the array.  slice assignments that change
the length of the array are not tested.

Unlike the case of "load slice", this case of "modify slice" is not
compared to the built in list type, since "modify slice" is only
implemented in micropython for simple (stride=1) slices.
2020-02-29 17:11:56 -06:00
Jeff Epler
380b8b0347 Add new test of slicing
Closes: #32
2020-02-29 17:00:53 -06:00
Jeff Epler
f9fabc5079 Fix handling of negative indices
I don't know why, but mp_seq_get_fast_slice_indexes adjusts "stop" in a
surprising way, and that led to the (remaining) differences in slicing
between ulab and built-in lists.

    // If the index is negative then stop points to the last item, not after it
    if (indexes->step < 0) {
        indexes->stop++;
    }

Call the underlying routine, mp_obj_slice_indices, instead.
2020-02-29 17:00:42 -06:00
Jeff Epler
585513ce76 Return empty slices as empty ndarrays, not exceptions
This matches the behavior of built-in lists as well as numpy
2020-02-29 16:57:59 -06:00
Jeff Epler
2ea9656d3f build: include debug information (may also disable optimization)
.. this makes it much more pleasant to trace down problems using gdb.
2020-02-29 16:57:09 -06:00
Jeff Epler
66b89de8c7 Always include creation functions 2020-02-27 14:07:04 -06:00
Jeff Epler
844b85018b
Merge pull request #49 from jepler/gitignore
ignore files created by ./build.sh
2020-02-27 13:58:25 -06:00
Jeff Epler
1c1a693a2b
Merge pull request #48 from v923z/create
created new create sub-module for ndarray initialisation functions
2020-02-27 13:58:14 -06:00
Jeff Epler
f81e950513 ignore files created by ./build.sh 2020-02-27 13:56:09 -06:00
Jeff Epler
ffff7606c8 fix tests after 'eye' was moved 2020-02-27 13:53:29 -06:00
Zoltán Vörös
bee25781b9 added new source file... 2020-02-27 20:46:52 +01:00
Zoltán Vörös
47bf2ec9a7 created new create sub-module for ndarray initialisation functions 2020-02-27 20:39:13 +01:00
Jeff Epler
badeee48df
Merge pull request #47 from jepler/local-build-script
a script to build and run tests locally
2020-02-27 10:12:03 -06:00
Jeff Epler
e370e56a15 a script to build and run tests locally 2020-02-27 10:11:34 -06:00
Jeff Epler
db5f1f85bb
Merge pull request #46 from jepler/ndarray-properties-sort
Ndarray properties sort
2020-02-27 10:11:17 -06:00
Jeff Epler
2bddc94df5
Merge pull request #45 from jepler/move-ones-zeros
Move zeros(), ones() to base ulab module
2020-02-27 10:08:29 -06:00
Jeff Epler
aa5ef4afb9 Enable sort method in circuitpython 2020-02-27 10:06:33 -06:00
Jeff Epler
d99d834d87 Enable properties in circuitpython
I verified that these work for us as coded.
2020-02-27 10:06:27 -06:00
Jeff Epler
83479f115b Move zeros(), ones() to base ulab module 2020-02-27 10:05:50 -06:00
924dc7012a
Merge pull request #44 from jepler/circuitpy-fixes
Circuitpy fixes
2020-02-27 09:29:43 -06:00
Jeff Epler
daaacac16f Remove CIRCUITPY special cases 2020-02-27 08:56:07 -06:00
Jeff Epler
aa4d53e292 Use circuitpy-compat for none 2020-02-27 08:56:04 -06:00
3febd79aa0
Merge pull request #41 from v923z/2dim
Split ulab into multiple modules
2020-02-26 11:29:17 -06:00
Zoltán Vörös
7e2be88dff Merge branch '2dim' of github.com:v923z/micropython-ulab into 2dim
added circuitpython-related stuff to code and manual
2020-02-26 18:06:19 +01:00
Zoltán Vörös
e0e840f6d5 added circuitpython-related stuff to the manual 2020-02-26 18:05:49 +01:00
102ba5032e add missing expected-file 2020-02-18 21:40:31 -06:00
b83ed3e2ca add more tests 2020-02-18 21:36:49 -06:00
1e5ebe739d numerical: add __name__ 2020-02-18 21:27:05 -06:00
Zoltán Vörös
8300de7f11 backup commit, absolutely nothing essential 2020-02-16 19:53:30 +01:00
60 changed files with 9582 additions and 7941 deletions

View file

@ -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
View file

@ -0,0 +1,3 @@
/micropython
/*.exp
/*.out

123
README.md
View file

@ -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
View 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
View 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
View 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
View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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

View file

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

View file

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

View file

@ -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
View 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
View 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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

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

View file

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

View file

@ -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);

View file

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

View file

@ -12,7 +12,8 @@
#ifndef _POLY_
#define _POLY_
#include "ulab.h"
#include "../ulab.h"
#include "../ndarray.h"
#if ULAB_POLY_MODULE

View file

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

View file

@ -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
View 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
View 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
View 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

View file

@ -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
View 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

View file

@ -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)); \
}

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,2 @@
from ulab import linalg
print(linalg.eye(3))
import ulab
print(ulab.eye(3))

62
tests/argminmax.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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]