Compare commits

..

1346 commits
0.24 ... master

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

* fix module loops

* add in-place modulo operator

* update readme

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

* fix modulo loops

* add in-place modulo operator

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

* make checks inclusive for negative values of integers

* move to newer version of ubuntu

* fix iterator macros

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

* multiline headers/footers are treated correctly

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

* add random test files

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

* preliminary keepdims fix

* fux keepdims code

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

* compile unix double-precision, select integrations algos

* bumping ulab version number to 6.7.0

* adding documentation

* documentation fix

* documentation fix

* documentation fix

* rewritten in some places

* complex number error handling

* added test cases

* resolved importing scipy.integrate

* resolved importing scipy.integrate #2

* build integrate only when we have MICROPY_FLOAT_IMPL_DOUBLE

* reverting commit a4c0c0b

* re-pushing failed commit

* Revert "re-pushing failed commit"

This reverts commit a10e89fe14.

* improve tests using math.isclose()

* enabled fp32 builds

* removed conditional includes

* adapted to new function names, corrected importing

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

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

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

* CI: Continue other jobs on failure.

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

* CI: Do not modify unix mpconfigport.h.

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

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

* add Generator object

* add placeholder for random.random method

* add rudimentary random.random implementation

* generator object accept seed(s) argument

* add out keyword

* add support for out keyword argument

* update change log

* add links to header files

* fix file link

* fix error messages

* add uniform to random module

* add normal distribution

* fix argument options in normal and uniform

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

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

* increment the version number and update the change log

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

* implement (u)int8 upcasting rules as per documentation

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

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

* fix unterminated if

* add missing linebreak

* add more linebreaks

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

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

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

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

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

* Polyval handles non-array as second argument

---------

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

* Use size_t for polyval array len

* Fix infinite arange

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

* add build to requirements

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

* Tweaked test values for and / or

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

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

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

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

---------

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

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

* fix the keyword handling in sqrt

* run micropython build on ubuntu 20.04 instead of latest

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

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

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

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

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

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

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

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

# Applying the filter to a signal s can be as simple as writing
# s = np.convolve(s, h)
2023-05-15 18:00:59 -05:00
Zoltán Vörös
ac2e9954ed
ndarray_from_mp_obj correctly treats Booleans (#613) 2023-05-07 12:09:02 +02:00
Zoltán Vörös
beda4c19f8
implement unknown shape dimension in reshape (#612) 2023-05-07 00:21:16 +02:00
Zoltán Vörös
412b13fb1c
fix binary division (#611) 2023-05-06 20:40:06 +02:00
FelixNumworks
8c3e1058d4
Fix arange crashing when start, stop or step is nan (#605) 2023-04-27 13:26:04 +02:00
FelixNumworks
ad1a1c54aa
Fix create_arange empty range (#604)
* Fix create_arange crashing when stop == start

This is due to len being equal to zero, which made (len - 1) equal to MAX_UINT16

* trailing whitespaces
2023-04-24 18:46:48 +02:00
HugoNumworks
afc8e4e165
Fix unused function compare_equal_helper warning (#603) 2023-04-24 14:58:38 +02:00
Zoltán Vörös
47ad73ab57
Floordiv (#593)
* implement floor division

* fix 3D, 4D loops

* add missing array declaration in 3D, and 4D

* Add test cases for floor division and fix it for ints (#599)

* Add test cases for floor division

* Fix define name in comment

* Fix floor division of ints

---------

Co-authored-by: Maciej Sokołowski <matemaciek@gmail.com>
2023-04-23 21:14:59 +02:00
yamamotomas
4407f8c404
Add a build script for Raspberry Pi Pico W (#598)
* make a build script for pico W

* add build scipt for pico W
2023-04-20 17:46:16 +02:00
HugoNumworks
5fa9b70766
Add missing constant constraints (#594) 2023-04-14 16:43:09 +02:00
Damien George
8585407df9
Use enum instead of const for float consts with repr C (#591)
Older compilers (eg for esp8266) cannot see that "const uint32_t" is a true
constant at compile time, so use an assigned enum instead.

Don't do this for object representation D because that requires a 64-bit
integer.

Signed-off-by: Damien George <damien@micropython.org>
2023-03-04 08:28:01 +01:00
Zoltán Vörös
f2dd2230c4
fix sorting of empty arrays in sort_complex (#583) 2023-01-23 21:53:41 +01:00
Zoltán Vörös
578ca6670d
raise exception in arange, if step size is 0 (#582) 2023-01-23 21:52:37 +01:00
33398e082c
Merge pull request #578 from jepler/issue576
Check that array size doesn't overflow at construction time
2023-01-21 19:32:33 -06:00
20766d16e1
bump version 2023-01-21 08:03:06 -06:00
315c988393
Check that array size doesn't overflow at construction time
Now, requesting to allocate an array that is too big gives the exception
'array is too big', like numpy.

This does depend on a gcc extension, `__builtin_mul_overflow`, present
since at least version 5. This extension is also supported in clang.
msvc is probably the only compiler of note that does not support it.

Closes: #576
2023-01-19 07:15:58 -06:00
Zoltán Vörös
6fcfeda58d increment version number after dot fix 2023-01-15 11:01:15 +01:00
6e6d24ea45
Allocate 1d results of correct size (#577)
Before, it was erroneously allocated as a 1-element array
instead of a 6-element array in the test case.

Closes #574
2023-01-15 10:58:18 +01:00
Zoltán Vörös
7124eaac74
fix concatenate (#575) 2023-01-14 11:02:33 +01:00
Zoltán Vörös
5be42be128
Update bug_report.md
correct misleading instruction on version number
2023-01-14 09:49:01 +01:00
Zoltán Vörös
e68bb707b2
fix vectorize (#568) 2023-01-04 00:27:41 +01:00
HaydenS3
42172c6575
Added note of dimensions argument for ./build.sh 😿 (#567) 2023-01-04 00:04:52 +01:00
Damien George
1a440d7d12
Fix sort when dtype is uint16 (#563)
Prior to this fix the code was using the mp_float_t data type for uint16
and producing incorrect sort results.

Signed-off-by: Damien George <damien@micropython.org>

Signed-off-by: Damien George <damien@micropython.org>
2022-11-29 08:02:35 +01:00
Zoltán Vörös
25a825e41c
fix segmentation fault bug in fft.ifft (#557) 2022-11-07 17:23:04 +01:00
155dad9994
Use python 3.10 during ci (#558)
CircuitPython now requires 3.9 as a minumum
2022-11-06 10:39:10 +01:00
Jim Mussared
38e99c69af
code/ulab.c: Fix missing comma in ulab_dtype_type definition. (#553)
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2022-10-10 08:48:17 +02:00
Damien George
41fcf1d4cf
Minor compile fixes: comma and new-line at end of files (#550)
* Fix missing comma in type definition

Signed-off-by: Damien George <damien@micropython.org>

* Make sure all files have a new-line at the end

Some very old compilers don't like files without a new-line at the end.

Signed-off-by: Damien George <damien@micropython.org>

* Use math.isclose for universal_functions expm1 test

Signed-off-by: Damien George <damien@micropython.org>

Signed-off-by: Damien George <damien@micropython.org>
2022-09-30 10:42:49 +02:00
Zoltán Vörös
5ccfa5cdd9
Update build.sh
remove tests from nanbox
2022-09-21 20:10:18 +02:00
Zoltán Vörös
0c807bd912 remove comments from methods.py 2022-09-21 20:00:03 +02:00
Zoltán Vörös
294f9d9ef2 fix test script, bring ulab in line with micropython, bump version number to 6.0.0 2022-09-21 19:59:34 +02:00
Jim Mussared
42f396a992
Update mp_obj_type_t definitions for latest MicroPython. (#549)
* build.sh: Fix unix executable path.

This was updated recently to no longer copy to the ports/unix directory.

Use the version in the build directory instead if available.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>

* Update to new style mp_obj_type_t definitions.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2022-09-21 19:46:39 +02:00
Zoltán Vörös
57de23c1fb
fix how arctan2 treats scalars (#546) 2022-08-04 07:35:20 +02:00
Zoltán Vörös
dfed7a844a
implement nonzero (#540)
* implement nonzero for Boolean arrays

* remove axtls from build script

* extend nonzero to ndarrays of arbitrary dtype, and iterable, fix float tests

* temporarily disable circuitpython tests

* add nonzero documentation

* Added test script for np.nonzero()

Co-authored-by: Tejal Ashwini Barnwal <64950661+tejalbarnwal@users.noreply.github.com>
2022-08-03 20:56:45 +02:00
thetazero
a2c5ece991
Add numpy.block snippet (#539) 2022-07-16 18:00:10 +02:00
Damien George
2b4292abcc
Make optimize's bisect,fmin,newton use C stack instead of heap (#536)
Signed-off-by: Damien George <damien.p.george@gmail.com>
2022-07-11 19:18:40 +02:00
d5762fda3c
Merge pull request #535 from dpgeorge/fix-float-const-use
fix use of MICROPY_FLOAT_CONST, and build single-precision as part of CI
2022-07-11 06:39:41 -05:00
Damien George
99f6fa66a1 Add single-precision float to build.sh
Signed-off-by: Damien George <damien.p.george@gmail.com>
2022-07-11 15:27:49 +10:00
Damien George
35b58d0037 Remove MICROPY_FLOAT_CONST from ULAB_DEFINE_FLOAT_CONST
A given use of ULAB_DEFINE_FLOAT_CONST may already have the correct form
of the float constant, so wrapping it in MICROPY_FLOAT_CONST may be the
wrong thing to do.  So let the user of ULAB_DEFINE_FLOAT_CONST control how
the constant is formed.

Signed-off-by: Damien George <damien.p.george@gmail.com>
2022-07-11 15:27:49 +10:00
770ae31556
Merge pull request #534 from jepler/fix-repr-a
Fix repr a
2022-07-07 19:54:49 -05:00
346c936e14
Fix E, PI constants on REPR_A
REPR_A is used for e.g., broadcom build raspberrypi_pi4b.

This is a trick, so that the appended 'f' is attached to the "0.".

Without this, a diagnostic occurred:
```
../../extmod/ulab/code/numpy/../ndarray.h:27:34: error: invalid suffix "FF" on floating constant
```
2022-07-07 17:15:09 -05:00
fc6e200afe
Fix INF, NAN constants on REPR_A
REPR_A is used for e.g., broadcom build raspberrypi_pi4b.

This is a trick, so that the appended 'f' is attached to the "0.".
Infinity plus zero is still zero, and nan plus zero is still zero.

Without this, a diagnostic occurred:
```
../../extmod/ulab/code/numpy/numpy.c:64:60: error: pasting ")" and "F" does not give a valid preprocessing token
```
2022-07-07 17:14:35 -05:00
308627c9aa
Fix several build errors in CircuitPython (#533)
* Properly register submodules of ulab

This is related to
 * https://github.com/adafruit/circuitpython/issues/6066

in which, after the merge of 1.18 into CircuitPython, we lost the ability
to import submodules of built-in modules.

While reconstructing the changes we had made locally to enable this,
I discovered that there was an easier way: simply register the dotted
module names via MP_REGISTER_MODULE.

* Fix finding processor count when no `python` executable is installed

debian likes to install only `python3`, and not `python` (which was,
for many decades, python2).

This was previously done for `build.sh` but not for `build-cp.sh`.

* Only use this submodule feature in CircuitPython

.. as it does not work properly in MicroPython.

Also, modules to be const. This saves a small amount of RAM

* Fix -Werror=undef diagnostic

Most CircuitPython ports build with -Werror=undef, so that use of an
undefined preprocessor flag is an error. Also, CircuitPython's micropython
version is old enough that MICROPY_VERSION is not (ever) defined.

Defensively check for this macro being defined, and use the older style
of MP_REGISTER_MODULE when it is not.

* Fix -Werror=discarded-qualifiers diagnostics

Most CircuitPython ports build with -Werror=discarded-qualifiers.
This detected a problem where string constants were passed to functions
with non-constant parameter types.

* bump version number

* Use MicroPython-compatible registration of submodules

* straggler

* Remove spurious casts

these were build errors for micropython

* Run tests for both nanbox and regular variant during CI
2022-07-07 20:17:49 +02:00
Damien George
1347694672
Fix build for MICROPY_OBJ_REPR_D configurations (#531)
* Fix use of object pointers so code builds with MICROPY_OBJ_REPR_D

Signed-off-by: Damien George <damien.p.george@gmail.com>

* Fix use of float constants so they work with MICROPY_OBJ_REPR_D

Signed-off-by: Damien George <damien.p.george@gmail.com>

* Use new float-const macros to simplify definitions of e,inf,nan,pi.

Signed-off-by: Damien George <damien.p.george@gmail.com>

* Add support for MICROPY_OBJ_REPR_C

Signed-off-by: Damien George <damien.p.george@gmail.com>

* Add unix-nanbox build to build.sh script

Building nanbox requires gcc-multilib because it forces 32-bit mode.

Signed-off-by: Damien George <damien.p.george@gmail.com>

* Bump version to 5.0.8

Signed-off-by: Damien George <damien.p.george@gmail.com>
2022-07-06 20:38:20 +02:00
Damien George
1898c2fc71
Fix error message for maximum number of dimensions (#532)
Signed-off-by: Damien George <damien.p.george@gmail.com>
2022-07-06 05:55:46 +02:00
Philip Howard
00139bdd96
Drop third argument from MP_REGISTER_MODULE. (#529)
Adopt pattern from efe23aca71/docs/develop/cmodules.rst

Signed-off-by: Phil Howard <phil@pimoroni.com>
2022-06-16 14:26:10 +02:00
Zoltán Vörös
d438344943
fix in-place assignment from slices (#524)
* fix in-place assignment from slices
2022-05-17 21:25:20 +02:00
Zoltán Vörös
53bc8d6b0e
replace m_new with m_new0, wherever reasonable (#521)
replace m_new with m_new0, wherever reasonable, and remove dangling memory fragments created by m_new0
2022-04-22 22:10:01 +02:00
Zoltán Vörös
da33f6276a
Merge pull request #522 from tekktrik/doc/fix-spectrum-reference
Update reference to spectrum
2022-04-22 18:38:54 +02:00
Alec Delaney
a715eed7d9 Fix reference 2022-04-22 12:10:42 -04:00
Alec Delaney
8da81e123e
Fix typo of name from spectrum to spectrogram 2022-04-22 11:30:49 -04:00
Alec Delaney
8c2cd49ffa
Fix reference to location of spectrum() 2022-04-20 18:08:57 -04:00
Alec Delaney
2772fd8e91
Update reference to spectrum 2022-04-19 21:45:34 -04:00
Zoltán Vörös
10b79f2041
Merge pull request #519 from v923z/empty-fix2
fix sorting on empty arrays
2022-04-18 10:51:35 +02:00
Zoltán Vörös
dde8d24461 add sort tests 2022-04-18 10:34:02 +02:00
Zoltán Vörös
48b3b7f641 fix sorting on empty arrays 2022-04-18 10:18:19 +02:00
Zoltán Vörös
c7cfae9397
Merge pull request #518 from jepler/update-cp-doc-build
update cp pip dependencies
2022-04-18 09:57:06 +02:00
15d9df16ee
update cp pip dependencies 2022-04-17 15:28:11 -05:00
Zoltán Vörös
4407bec88c
Merge pull request #507 from v923z/empty-fix
fix binary ops on empty arrays
2022-02-23 22:30:37 +01:00
Zoltán Vörös
d2939dbf22 fix binary ops on empty arrays 2022-02-18 19:34:30 +01:00
Zoltán Vörös
ec5d14e8e2
Merge pull request #505 from v923z/c-fix
fix complex slicing
2022-02-12 18:41:16 +01:00
Zoltán Vörös
607fd303bb remove printf 2022-02-12 18:40:44 +01:00
Zoltán Vörös
0ef2341386 fix complex slicing 2022-02-10 19:57:19 +01:00
Zoltán Vörös
08cfcbfb46
Merge pull request #503 from v923z/diag-fix
fix np.diag
2022-02-09 07:59:57 +01:00
Zoltán Vörös
4705c95fb2 add diag tests 2022-02-09 07:45:15 +01:00
Zoltán Vörös
f2502fa037 update diag doc, re-organise ulab-ndarray docs 2022-02-09 07:29:26 +01:00
Zoltán Vörös
72f7df520a fix diag for the ndim = 2 case 2022-02-09 07:06:54 +01:00
Zoltán Vörös
4dc9afc1f8 fix np.diag 2022-02-08 17:37:00 +01:00
Zoltán Vörös
973c1f596d
Merge pull request #501 from v923z/hash
add ULAB_HASH
2022-02-03 22:00:54 +01:00
Zoltán Vörös
cc14841afb fix build script 2022-02-03 21:41:49 +01:00
Zoltán Vörös
effbd3cf38 add ULAB_HASH 2022-02-03 21:23:11 +01:00
Zoltán Vörös
e852f7fd42 add link to micropython-builder 2022-02-03 08:19:20 +01:00
Zoltán Vörös
ccd9481969
Merge pull request #499 from v923z/spectrogram
move spectrogram to utils module
2022-02-01 20:40:11 +01:00
Zoltán Vörös
ae9604db21
Merge branch 'master' into spectrogram 2022-02-01 20:29:53 +01:00
Zoltán Vörös
88231bc3a1
Merge pull request #500 from v923z/loadtxt-fix
add dtype, and skiprows keywords to loadtxt
2022-02-01 20:19:35 +01:00
Zoltán Vörös
6dcad4424c extend loadtxt documentation 2022-02-01 18:42:58 +01:00
Zoltán Vörös
9ba136acd4 fix savetxt comments default value 2022-01-31 22:28:50 +01:00
Zoltán Vörös
f5f42c3643 add skiprows keyword 2022-01-31 22:12:00 +01:00
Zoltán Vörös
65d6f8e947 fix buffer alignment error 2022-01-30 22:47:44 +01:00
Zoltán Vörös
e8c89935ab fix array dtype conversion, linalg float constants 2022-01-30 19:55:59 +01:00
Zoltán Vörös
e980564d64 assign array elements via function 2022-01-29 22:41:03 +01:00
Zoltán Vörös
19282b47d1 add dtype keyword to loadtxt 2022-01-29 22:30:11 +01:00
Zoltán Vörös
e86b6b8507 fix version number in change log 2022-01-29 21:58:37 +01:00
Zoltán Vörös
9d79960ef6 fix docs glitch, bump version number to 5.0.0 2022-01-29 21:53:42 +01:00
Zoltán Vörös
67538b0ee0 move spectrogram to utils module 2022-01-29 18:02:48 +01:00
Zoltán Vörös
9dc9b776d5
Merge pull request #492 from v923z/io-savetxt
implement savetxt, and loadtxt
2022-01-29 17:02:59 +01:00
Zoltán Vörös
80274175ec
Update README.md 2022-01-29 16:59:17 +01:00
Zoltán Vörös
87e5fa3bd9 add loadtxt and savetxt documentation 2022-01-28 19:58:40 +01:00
Zoltán Vörös
7a09815a84 add loadtxt and savetxt documentation 2022-01-28 19:57:32 +01:00
Zoltán Vörös
089000df30
Merge branch 'master' into io-savetxt 2022-01-28 19:28:43 +01:00
Zoltán Vörös
2530fc7e8b update savetxt test script 2022-01-27 23:46:50 +01:00
Zoltán Vörös
bb07482efc implement max_roas keyword 2022-01-27 23:44:29 +01:00
Zoltán Vörös
004f228428
Merge pull request #496 from v923z/rp-fix
fix compilation problem on the rp2 port
2022-01-27 07:55:24 +01:00
Zoltán Vörös
f31555044a implement loadtxt keywords 2022-01-26 22:31:36 +01:00
Zoltán Vörös
f7b557c6b0 get rid of unnecessary counter in loadtxt 2022-01-26 08:09:05 +01:00
Zoltán Vörös
9f444dd718 use mp_builtin_open_obj.fun.kw instead of mp_builtin_open 2022-01-26 07:43:17 +01:00
Zoltán Vörös
a39869c43c remove redundant includes 2022-01-25 23:09:10 +01:00
Zoltán Vörös
5395c76d98 first implementation of loadtxt 2022-01-25 23:00:08 +01:00
Zoltán Vörös
77a856e84e fix compilation problem on the rp2 port 2022-01-25 18:10:06 +01:00
Zoltán Vörös
b4802a7f4d
Merge pull request #495 from v923z/savetxt-fix
add io_sprint for systems that don't have sprintf
2022-01-24 20:52:06 +01:00
Zoltán Vörös
8d39e5a430 update test for savetxt 2022-01-24 20:40:33 +01:00
Zoltán Vörös
d56dd2f921 add io_sprint for systems that don't have sprintf 2022-01-24 20:37:33 +01:00
Zoltán Vörös
6dfdc44c4e
Merge pull request #491 from v923z/sosfilt-fix
fix scipy.signal.sosfilt
2022-01-21 17:29:40 +01:00
Zoltán Vörös
26d28166f8
Merge branch 'master' into sosfilt-fix 2022-01-21 17:29:28 +01:00
Zoltán Vörös
0640f90083
allow all formats in the documentation 2022-01-19 20:20:18 +01:00
Zoltán Vörös
957ae1c50a fix macintosh compilation 2022-01-19 20:04:41 +01:00
Zoltán Vörös
16b277fe1a
Merge pull request #471 from v923z/io
implement numpy.save, numpy.load
2022-01-19 19:57:38 +01:00
Zoltán Vörös
73fcd895db
Merge branch 'master' into io 2022-01-19 19:56:44 +01:00
Zoltán Vörös
15918fdb89
Merge pull request #488 from v923z/bool-invert
fix ndarray_copy_view for Boolean dtypes
2022-01-19 19:54:40 +01:00
Zoltán Vörös
932060afcc fully implement savetxt 2022-01-19 19:52:50 +01:00
Zoltán Vörös
640a3bf777 do not include sosfilt, if ULAB_MAX_DIMS = 1 2022-01-19 17:49:01 +01:00
Zoltán Vörös
6092d1d406 trying to solve macintosh compilation error 2022-01-19 17:46:41 +01:00
Zoltán Vörös
3f97d7bb84 fix scipy.signal.sosfilt 2022-01-19 17:37:54 +01:00
Zoltán Vörös
209e7ff251 add savetxt implementation 2022-01-18 21:20:57 +01:00
Zoltán Vörös
4739e06473 fix ndarray_copy_view for Boolean dtypes 2022-01-18 16:42:03 +01:00
Zoltán Vörös
67e592670c
Merge pull request #486 from v923z/delete-fix
include uinstd.h header
2022-01-16 18:55:10 +01:00
Zoltán Vörös
4f828f188d
Merge pull request #485 from v923z/json-fix
remove printout
2022-01-16 16:21:16 +01:00
Zoltán Vörös
74ee335758 remove printout 2022-01-16 16:20:53 +01:00
Zoltán Vörös
bd57ac8938 include uinstd.h header 2022-01-16 16:17:06 +01:00
Zoltán Vörös
b756c36f73 rebase from master 2022-01-15 10:09:24 +01:00
Zoltán Vörös
024b3ea6e8 change npy, fin, fout to stream, stream_p for consistency 2022-01-15 09:54:23 +01:00
Zoltán Vörös
97d2c2c3c5 always close file before exception, change exception type 2022-01-15 09:54:23 +01:00
Zoltán Vörös
b9d46c7f92 try to fix uninitialised variable issue 2022-01-15 09:54:23 +01:00
Zoltán Vörös
a1f1cd255b rebasing from master 2022-01-15 09:54:15 +01:00
Zoltán Vörös
834ddc8218 attempt to fix eps32 compilation error 2022-01-15 09:46:24 +01:00
Zoltán Vörös
4b0c3215fd close file in save implementation 2022-01-15 09:46:24 +01:00
Zoltán Vörös
acb81aeded fix loop index in numpy.save 2022-01-15 09:46:24 +01:00
Zoltán Vörös
56b3d800e3 add input type checking 2022-01-15 09:46:24 +01:00
Zoltán Vörös
030463f7ff include stdio.h for mac 2022-01-15 09:46:24 +01:00
Zoltán Vörös
d8ce4b395f rebase from master 2022-01-15 09:46:13 +01:00
Zoltán Vörös
fa143c2e63
Merge pull request #483 from v923z/json-mod
change keyword value to True, replace __numpy__ key by array
2022-01-15 08:21:43 +01:00
Zoltán Vörös
406d9c7407 change keyword value to True, replace __numpy__ key by array 2022-01-15 08:21:06 +01:00
Zoltán Vörös
8c7ca97971
Merge pull request #481 from v923z/size
add implementation of size and asarray
2022-01-15 08:14:44 +01:00
Zoltán Vörös
32ebcb4c06 fix size argument handling 2022-01-14 22:54:04 +01:00
Zoltán Vörös
b12f3d0b4d fix asarray initialiser 2022-01-14 22:25:17 +01:00
Zoltán Vörös
d40672d946 add asarray, size documentation 2022-01-14 21:06:55 +01:00
Zoltán Vörös
1fc2f18358 fix size return value 2022-01-14 20:49:42 +01:00
Zoltán Vörös
74533d18ff add asarray, and tests for asarray and size 2022-01-14 20:45:14 +01:00
Zoltán Vörös
63ab589b8c add numpy.size 2022-01-14 19:57:18 +01:00
Zoltán Vörös
8154accf1b
Merge pull request #475 from wired8/filter-snippets
Filter snippets
2022-01-14 19:25:06 +01:00
PhilJ
05ad1a5d96 Added (c) notice 2022-01-14 10:09:34 -08:00
Zoltán Vörös
a88fbaf8c8
Merge pull request #478 from v923z/json
add copyright notice, short documentation, and remove tests
2022-01-14 19:06:04 +01:00
Zoltán Vörös
aa543d0c57 add copyright notice, short documentation, and remove tests 2022-01-14 19:04:42 +01:00
PhilJ
d5ab0cf716 Replace delete implementation with ulab delete 2022-01-14 09:57:10 -08:00
Zoltán Vörös
4e6b89afd2
Merge pull request #477 from v923z/json
json snippets
2022-01-14 18:51:54 +01:00
PhilJ
8e8aa263e9 Merge branch 'master' into filter-snippets 2022-01-14 09:48:04 -08:00
Zoltán Vörös
178af9a9fa fix byteswapping segment 2022-01-14 17:43:00 +01:00
Zoltán Vörös
4232a5923f add json_to_ndarray 2022-01-14 17:39:26 +01:00
Zoltán Vörös
d2e6af03d1 fix endianness code 2022-01-14 17:04:51 +01:00
Zoltán Vörös
d3ba090b1a fix base64 encoding 2022-01-14 17:01:07 +01:00
Zoltán Vörös
e762275a96 add ndarray-json converter 2022-01-14 15:20:22 +01:00
Zoltán Vörös
7672ab9d44 change npy, fin, fout to stream, stream_p for consistency 2022-01-14 14:10:04 +01:00
Zoltán Vörös
66189452ac always close file before exception, change exception type 2022-01-14 14:01:39 +01:00
PhilJ
a128762279 Removed .idea 2022-01-13 23:18:51 -08:00
PhilJ
612d07218e Added snippet tests 2022-01-13 23:11:55 -08:00
PhilJ
45fde86060 Working sos filter test 2022-01-12 16:53:48 -08:00
Zoltán Vörös
b5d94e8fdc
Merge branch 'master' into io 2022-01-12 21:55:14 +01:00
Zoltán Vörös
c3d1a5e1ee
Merge pull request #474 from v923z/complex-fix
fix complex printout for long arrays
2022-01-12 21:49:30 +01:00
Zoltán Vörös
fdf23c9956
Merge branch 'master' into complex-fix 2022-01-12 21:25:31 +01:00
Zoltán Vörös
eecd72de04 try to fix uninitialised variable issue 2022-01-12 21:24:19 +01:00
Zoltán Vörös
a09b5bd64e
Merge pull request #473 from v923z/delete
add numpy.delete
2022-01-12 20:19:44 +01:00
Zoltán Vörös
ca3bbca1b1 Merge branch 'io-load' into io 2022-01-12 20:18:06 +01:00
Zoltán Vörös
17afe29124 implement load 2022-01-12 20:17:24 +01:00
Zoltán Vörös
8e42afe72e attempt to fix eps32 compilation error 2022-01-12 18:10:34 +01:00
Zoltán Vörös
923cb823a3 add documentation for delete, extend test script, handle negative scalars 2022-01-12 18:05:23 +01:00
Zoltán Vörös
6eda5ec53c update delete test 2022-01-11 22:11:43 +01:00
Zoltán Vörös
34353d7545 fix scalar indexing 2022-01-11 22:07:32 +01:00
Zoltán Vörös
9d84125232 close file in save implementation 2022-01-11 19:18:48 +01:00
Zoltán Vörös
faaae7d2f2 update test scripts 2022-01-10 23:34:33 +01:00
Zoltán Vörös
97b368412c fix complex printout for long arrays 2022-01-10 23:23:09 +01:00
PhilJ
ef4a5b7fd1 Initial commit 2022-01-10 13:59:51 -08:00
Zoltán Vörös
e5020f8d57 add numpy.delete 2022-01-10 22:54:09 +01:00
Zoltán Vörös
3fd60dedf9 fix loop index in numpy.save 2022-01-09 20:12:09 +01:00
Zoltán Vörös
78ba99f009 add input type checking 2022-01-08 20:48:30 +01:00
Zoltán Vörös
68f8098016 include stdio.h for mac 2022-01-08 20:31:00 +01:00
Zoltán Vörös
a0dcd1ebab implement numpy.save 2022-01-08 20:27:02 +01:00
Zoltán Vörös
25d009587e update changelog 2022-01-08 12:20:38 +01:00
Zoltán Vörös
0c7c6b88f3
Merge pull request #456 from v923z/complex2
Complex2
2022-01-08 12:11:37 +01:00
Zoltán Vörös
6552cfdc38 remove misleading comment from ulab.h 2022-01-07 21:19:06 +01:00
Zoltán Vörös
665c32dbb4 add documentation for compress 2022-01-07 20:53:31 +01:00
Zoltán Vörös
6609afd006 update documentation 2022-01-07 20:43:14 +01:00
Zoltán Vörös
969afdec7f implement ==, and != for complex dtypes 2022-01-07 14:05:38 +01:00
Zoltán Vörös
ecaa355bab fix sort_complex, add tests to sort_complex, conjugate 2022-01-07 12:47:42 +01:00
Zoltán Vörös
ad9e1e9950 fix linspace error, extend to complex dtype, factor out tests 2022-01-06 19:55:34 +01:00
Zoltán Vörös
d1b3d40b47 add complex support to all/any 2022-01-06 09:22:45 +01:00
Zoltán Vörös
8efdec785e extend convolve for the complex case 2022-01-05 20:46:03 +01:00
Zoltán Vörös
6a7d20dd58 add real/imag properties to ndarray 2022-01-02 19:14:12 +01:00
Zoltán Vörös
d39549e3ac add sort_complex function 2022-01-02 17:34:38 +01:00
Zoltán Vörös
cb3f4b3898 add compress function 2022-01-02 12:46:12 +01:00
Zoltán Vörös
2b578ae333 add tolist method to ndarray 2022-01-01 18:16:04 +01:00
Zoltán Vörös
c11dac322d fix array initialisation from complex array 2022-01-01 09:20:00 +01:00
Zoltán Vörös
4855baa8cc add conjugate function 2022-01-01 00:33:28 +01:00
Zoltán Vörös
7e13fab911 support ones, zeros, and full for complex dtype 2021-12-31 23:39:58 +01:00
Zoltán Vörös
94d6b13f8d make fft optionally numpy-compatible, when complex is supported 2021-12-31 23:11:59 +01:00
Zoltán Vörös
9eebed2d3a add basic complex tests 2021-12-30 11:32:07 +01:00
Zoltán Vörös
e01e547b62 fix dtype in stubs 2021-12-30 09:09:41 +01:00
Zoltán Vörös
4d7e279996 fix circuitpython documentation 2021-12-30 08:54:52 +01:00
Zoltán Vörös
7b17796c91 fix complex sqrt 2021-12-30 08:10:54 +01:00
Zoltán Vörös
ee197ef03f bring in a99e0b9 in an attempt to fix the docs error 2021-12-28 22:30:17 +01:00
Zoltán Vörös
dbd570a9fb implement complex scalar binary operations 2021-12-28 22:18:37 +01:00
Zoltán Vörös
14b5c13d50 add complex flag to version string 2021-12-28 21:55:00 +01:00
Zoltán Vörös
5b7395d6df implement complex divide 2021-12-28 21:51:32 +01:00
Zoltán Vörös
13cebfeccc add complex subtraction, fix addition 2021-12-28 20:42:00 +01:00
Zoltán Vörös
a99e0b9878
Merge pull request #461 from jepler/fix-mypy-920
Change how we alias the float and bool types for mypy
2021-12-17 08:37:41 +01:00
71b3ba15af
Change how we alias the float and bool types for mypy
mypy 0.920 started giving an error stating
```
ulab/numpy/__init__.pyi:51: error: Cannot assign multiple types to name "_float" without an explicit "Type[...]" annotation
ulab/numpy/__init__.pyi:54: error: Cannot assign multiple types to name "_bool" without an explicit "Type[...]" annotation
```

this quiets the error, and is also compatible with the previous version
(0.910).
2021-12-16 21:39:20 -06:00
Zoltán Vörös
76218f2386 fix 3D loop 2021-12-15 18:54:14 +01:00
Zoltán Vörös
8a156fe505 implement multiplication for complex arrays 2021-12-15 18:52:07 +01:00
Zoltán Vörös
cc06735cff fix binary add for 1D 2021-12-15 08:02:26 +01:00
Zoltán Vörös
864ab31766 implement binary add for complex arrays 2021-12-15 06:52:42 +01:00
Zoltán Vörös
3557e16cd1 implement sqrt for complex arrays 2021-12-07 19:55:15 +01:00
Zoltán Vörös
b275fb303d Merge branch 'complex2' of github.com:v923z/micropython-ulab into complex2 2021-12-03 09:12:08 +01:00
Zoltán Vörös
235329c3fb fix itemsize glitch 2021-12-03 09:11:41 +01:00
Zoltán Vörös
ce26a0d76b implement slicing for complex dtype 2021-12-03 09:06:49 +01:00
Zoltán Vörös
78455afa5d simplify binary_get_size function 2021-12-03 09:06:49 +01:00
Zoltán Vörös
93409e759a implement iterator for complex arrays 2021-12-03 09:06:49 +01:00
Zoltán Vörös
3b3639a560 implement complex exponential 2021-12-03 09:06:49 +01:00
Zoltán Vörös
d0b1d40cf1 rebasing on master with any/all fix 2021-12-03 09:06:05 +01:00
Zoltán Vörös
316f84174d rebasing with any/all fix 2021-12-03 09:04:04 +01:00
Zoltán Vörös
ef8989ecff implement unary operators for complex arrays 2021-12-03 08:59:23 +01:00
Zoltán Vörös
8a6741b350 fix macro call in poly.c 2021-12-03 08:59:23 +01:00
Zoltán Vörös
08313e3f02 fix uppercase mp_obj_is_type macro calls 2021-12-03 08:59:23 +01:00
Zoltán Vörös
8db5ee897e add missing newline character 2021-12-03 08:59:23 +01:00
Zoltán Vörös
911fdb4587 ulab can now be compiled with complex support 2021-12-03 08:59:23 +01:00
Zoltán Vörös
1013daa902 add complex utilities 2021-12-03 08:59:23 +01:00
Zoltán Vörös
91c01ef5fe
Merge pull request #460 from v923z/any-fix
fix any/all function
2021-12-03 08:58:24 +01:00
Zoltán Vörös
8e5bbe0955 fix any/all function 2021-12-03 08:42:56 +01:00
Zoltán Vörös
9a2449c0c6 implement slicing for complex dtype 2021-12-03 00:06:20 +01:00
Zoltán Vörös
322145d3ac simplify binary_get_size function 2021-12-02 21:42:14 +01:00
Zoltán Vörös
add4f41b2f implement iterator for complex arrays 2021-12-02 21:21:38 +01:00
Zoltán Vörös
a1a391b90d implement complex exponential 2021-12-01 21:37:05 +01:00
Zoltán Vörös
defecb664f Merge branch 'complex2' of github.com:v923z/micropython-ulab into complex2
pulling in remote
2021-12-01 20:06:24 +01:00
Zoltán Vörös
b9eea417bd implement unary operators for complex arrays 2021-12-01 07:49:29 +01:00
Zoltán Vörös
cd7c1de441 fix macro call in poly.c 2021-11-30 21:21:12 +01:00
Zoltán Vörös
6b85ca08e3 fix uppercase mp_obj_is_type macro calls 2021-11-30 21:21:12 +01:00
Zoltán Vörös
c603aff3a0 add missing newline character 2021-11-30 21:21:12 +01:00
Zoltán Vörös
ee3181a1d9 ulab can now be compiled with complex support 2021-11-30 21:21:12 +01:00
Zoltán Vörös
5512219777 add complex utilities 2021-11-30 21:21:12 +01:00
Zoltán Vörös
c03990cc56
Merge pull request #458 from v923z/sum-fix
Sum fix
2021-11-30 20:53:52 +01:00
Zoltán Vörös
e655c94108 add simple test for sum() 2021-11-30 20:38:43 +01:00
Zoltán Vörös
a097744455 fix sum() for integer/Boolean types 2021-11-30 20:29:22 +01:00
Zoltán Vörös
cd70c91f01
Merge pull request #455 from v923z/shape-fix
fix ndarray_shape for arrays of zero length
2021-11-24 06:57:36 +01:00
Zoltán Vörös
ebcf600b4b fix macro call in poly.c 2021-11-23 20:05:45 +01:00
Zoltán Vörös
fc8a9bde85 fix uppercase mp_obj_is_type macro calls 2021-11-23 20:03:11 +01:00
Zoltán Vörös
58b0db5171 add missing newline character 2021-11-23 19:57:34 +01:00
Zoltán Vörös
92b3513804 ulab can now be compiled with complex support 2021-11-23 18:40:12 +01:00
Zoltán Vörös
175c7337bb fix ndarray_shape for arrays of zero length 2021-11-20 12:11:21 +01:00
Zoltán Vörös
3d00480443 add complex utilities 2021-11-20 11:10:06 +01:00
Zoltán Vörös
bf15edecf9
Merge pull request #450 from RobertoRoos/feature/rtd_config
Created .readthedocs.yaml config file for latest versions
2021-11-19 20:38:39 +01:00
Roberto-R
2c7843eb87 Added RTD badge to README.md 2021-11-19 13:03:43 +01:00
Roberto-R
3b52dec187 Added basic RTD config file, updated conf.py path to non-default 2021-11-19 12:16:28 +01:00
Zoltán Vörös
c6bcc058b2
Merge pull request #446 from JorgeGMarques/patch-1
Fix Makefile misspelling
2021-11-07 22:19:20 +01:00
Jorge Marques
766babbf20
Fix Makefile misspelling
Was building without ulab module because it was not patching the Makefile, but trying the nonexistent MakeFile.
2021-11-07 16:49:47 -03:00
Zoltán Vörös
68fa115c4b
Merge pull request #444 from t-ikegami/fix_compare
Fix compare between (uint16|int16) vs float.
2021-11-07 12:51:29 +01:00
Tsutomu IKEGAMI
4d2a11ed00 Up version to 3.3.5. 2021-11-07 20:49:16 +09:00
Tsutomu IKEGAMI
14b39d5dbd Fix compare between (uint16|int16) vs float. 2021-11-05 12:09:18 +09:00
Zoltán Vörös
a2455ca7ce
Merge pull request #442 from mdaeron/master
Update esp32-cmake.sh
2021-10-20 06:57:43 +02:00
mdaeron
3254b0e3d8
Update esp32-cmake.sh
Fix esp32-cmake compile script
2021-10-20 01:17:57 +02:00
Jeff Epler
8a73015345
Merge pull request #440 from jepler/build-micropython-ubuntu-latest
use ubuntu-latest for building micropython
2021-10-16 16:10:03 -05:00
Jeff Epler
d68171bb46
Merge pull request #439 from jepler/circuitpython-make-new-compat
ndarray: drop compat workaround for circuitpython
2021-10-16 16:02:16 -05:00
d1d825b11d use ubuntu-latest for building micropython
This is what micropython uses now.
2021-10-16 15:59:09 -05:00
b913d064e5 ndarray: drop compat workaround for circuitpython
It's anticipated that circuitpython will no longer need this compat code
after merging 1.17.
2021-10-14 14:26:38 -05:00
Zoltán Vörös
1d9670096f
Merge pull request #434 from v923z/qr-docs
add docs for linalg.qr
2021-08-07 07:24:31 +02:00
Zoltán Vörös
04629f5521 add docs for linalg.qr 2021-08-07 07:21:55 +02:00
Jeff Epler
548aaba97d
Merge pull request #433 from jepler/remove-compat-alias
remove temporary workaround
2021-08-03 14:37:34 -05:00
Jeff Epler
73ce3bf590
Merge pull request #432 from jepler/build-all-dims2
Build & test for 1, 2, 3, and 4 dims
2021-07-23 14:21:18 -05:00
7dec159981 show where to find ulab.numpy.ndarray
.. interestingly, this error only showed up on macos, not ubuntu!
2021-07-23 10:26:06 -05:00
0b62231d56 show where to find ulab.numpy.ndarray
.. interestingly, this error only showed up on macos, not ubuntu!
2021-07-23 10:12:15 -05:00
5ff8bd662f remove temporary workaround 2021-07-23 09:57:17 -05:00
Jeff Epler
04eecad5b2
Merge pull request #431 from v923z/fix-1d
fix compilation for 1D
2021-07-23 09:40:20 -05:00
f64d2760d6 Update macOS version
During CI, the following diagnostic is shown:
> The macOS virtual environment has been updated to Catalina (v10.15). Please
> update your workflow and change the line 'runs-on: macOS-10.14' to 'runs-on:
> macOS-latest'

The advice seems dubious, since it will then automatically update to some other
macos version without warning, but follow it anyway.
2021-07-23 09:27:23 -05:00
ecf579ecb3 build 1d for CI 2021-07-23 09:21:20 -05:00
69011c8696 Re-organize tests 2021-07-23 09:19:10 -05:00
8b3b4d1829 Merge commit 'refs/pull/431/head' of https://github.com/v923z/micropython-ulab into build-all-dimensions 2021-07-23 09:05:25 -05:00
Zoltán Vörös
ba0cd7c661 re-set the value of ULAB_MAX 2021-07-23 08:29:37 +02:00
Zoltán Vörös
3817d7698a fix compilation for 1D 2021-07-23 07:28:17 +02:00
b6abde70bc workflows: Build with 2, 3, and 4 dimensions
1d doesn't seem to work at the moment:
```
/home/jepler/src/ulab/code/numpy/numerical.c:324:100: error: macro "RUN_MEAN_STD1" passed 7 arguments, but takes just 6
  324 | p_float_t, array, farray, _shape_strides, MICROPY_FLOAT_CONST(0.0), 0);
      |                                                                      ^
```
2021-07-22 14:15:37 -05:00
68eb0a623a circuitpython build: Build & test in a selected number of dimensions 2021-07-22 14:14:15 -05:00
81d78fe23f micropython build: Build & test in a selected number of dimensions 2021-07-22 14:14:08 -05:00
Zoltán Vörös
e26a6a9272
Merge pull request #425 from v923z/samd-fix
fix compile error on SAMD devices
2021-07-22 20:28:50 +02:00
Zoltán Vörös
91e08c8679
Merge branch 'master' into samd-fix 2021-07-22 20:28:34 +02:00
Zoltán Vörös
8d93ddeaf3
Merge pull request #428 from v923z/fix-4d
fix sum for 4D arrays
2021-07-22 20:11:05 +02:00
Zoltán Vörös
593a33df5d
Merge branch 'master' into fix-4d 2021-07-22 20:09:59 +02:00
Zoltán Vörös
d8a4dbbc48
Merge pull request #427 from v923z/qr2
add qr implementation
2021-07-22 20:08:30 +02:00
Zoltán Vörös
18bb09da45 fix sum for 4D arrays 2021-07-22 20:08:09 +02:00
Zoltán Vörös
d18a37929e update change log 2021-07-22 19:57:36 +02:00
Zoltán Vörös
a3ce0ce29a add qr implementation 2021-07-22 19:40:57 +02:00
Jeff Epler
90cb0959b9
Merge pull request #426 from adafruit/circuitpython-doc-move
Circuitpython doc move
2021-07-22 08:46:33 -05:00
Jeff Epler
8ef203f21b Only use the _float alias for the float built-in type in ulab.numpy proper
Otherwise, an error occurred, but only on the macos build(!)
```
ulab/scipy/optimize/__init__.pyi:7: error: Name "_float" is not defined
```
It's not clear why `_float` was apparently defined on Linux in this
context.
2021-07-22 08:37:12 -05:00
Jeff Epler
0cdea5ebac bump ubuntu version to the same as circuitpython uses 2021-07-22 08:22:39 -05:00
Jeff Epler
f3d0639ee6 Fix python version in a message to match what is installed 2021-07-22 08:19:36 -05:00
Jeff Epler
cbe4c7499b docs: A workaround for the problem where ndarray can't be in two places
This allows the build to be green in ulab before the changes in
circuitpython. After circuitpython's _typing/__init__.py is fixed to
use the new canonical location, this temporary alias can be removed.
2021-07-22 08:15:23 -05:00
Jeff Epler
7032a92339 Fix documentation build
* Move most documentation out of the ulab base module.
 * float -> _float
 * ulab.ndarray -> ulab.numpy.ndarray

This still does not build unless it is taken together with a modification
to CircuitPython that _also_ moves references to ulab.numpy.
Because of this, this PR will continue to show red.  The suitability of
the changes can be gaged by looking at the related CircuitPython PR build
or by running locally the build-cp.sh script with the right ref checked
out in circuitpython/
2021-07-20 17:26:43 -05:00
Zoltán Vörös
70e8b548ba fix compile error on SAMD devices 2021-07-16 10:03:34 +02:00
Zoltán Vörös
96550c8ed4 re-arrange files, so that circuitpython docs can be generated 2021-07-14 23:15:19 +02:00
Zoltán Vörös
85f9926423
Merge pull request #422 from v923z/flatiter
Flatiter
2021-07-14 16:42:10 +02:00
69d3c20c7b fix build on circuitpython 2021-07-14 08:42:51 -05:00
Zoltán Vörös
c152871dba insert circuitpython hooks for flatiter 2021-07-13 22:38:01 +02:00
Zoltán Vörös
7690c7a166 Merge branch 'flatiter' of github.com:v923z/micropython-ulab into flatiter 2021-07-13 21:37:42 +02:00
Zoltán Vörös
af34d8fe2a fix assertion 2021-07-13 21:35:01 +02:00
Zoltán Vörös
8c5a14d166 add flat iterator to documentation 2021-07-13 21:35:01 +02:00
Zoltán Vörös
6ed12969d3 implement flat iterator 2021-07-13 21:35:01 +02:00
Zoltán Vörös
7954a266eb fix assertion 2021-07-13 18:30:38 +02:00
Zoltán Vörös
1ee36944f6 add flat iterator to documentation 2021-07-13 18:24:35 +02:00
Zoltán Vörös
35d57db1d1 implement flat iterator 2021-07-13 17:29:24 +02:00
Jeff Epler
e881ccb30c
Merge pull request #418 from jepler/split-type-objects
support circuitpython-style split type objects
2021-07-12 19:53:38 -05:00
9b8cd47640 Rename mp_type_call -> mp_type_get_call_slot 2021-07-12 08:47:15 -05:00
1214afd9ce Rename EXTENDED_FIELDS -> MP_TYPE_EXTENDED_FIELDS 2021-07-12 08:47:15 -05:00
Jeff Epler
75c7e05eac rename the type flag to EXTENDED for consistency 2021-07-09 14:56:20 -05:00
Jeff Epler
deda9d74d8 support circuitpython-style split type objects 2021-07-06 11:20:26 -05:00
Zoltán Vörös
764d5b9a0c
Merge pull request #415 from v923z/remote-header
remove unused header imports
2021-06-22 22:09:34 +02:00
Zoltán Vörös
14b1ab88f1 remove unused header imports 2021-06-22 22:06:27 +02:00
Zoltán Vörös
008dcc7dc4
Merge pull request #414 from v923z/float_constant_fix
fix float comparison in scipy/linalg.c
2021-06-22 15:49:20 +02:00
Zoltán Vörös
be9fd86f60 fix float comparison in scipy/linalg.c 2021-06-22 07:06:10 +02:00
Zoltán Vörös
ef584e4bb9
Merge pull request #412 from v923z/properties
implement ndarray.shape assignment
2021-06-19 16:33:13 +02:00
Zoltán Vörös
6490a619a4 update documentation 2021-06-19 16:06:19 +02:00
Zoltán Vörös
7f38ad9315 implement ndarray.shape assigment 2021-06-19 15:58:56 +02:00
Zoltán Vörös
af121eef1a
Update README.md 2021-06-19 11:24:39 +02:00
Zoltán Vörös
90bec72ca2
Merge pull request #411 from v923z/T-transpose
T transpose
2021-06-17 22:03:04 +02:00
Zoltán Vörös
353eec999b update docs 2021-06-17 21:58:42 +02:00
Zoltán Vörös
947ef80f9f add the .T ndarray property 2021-06-17 21:52:19 +02:00
Zoltán Vörös
047e51b220
Update README.md 2021-06-17 21:32:03 +02:00
Jeff Epler
161a728848
Merge pull request #408 from v923z/attr-prop
implement ndarray properties
2021-06-10 12:24:11 -05:00
Zoltán Vörös
cd3d479b30 remove redudant cases from ndarray_set_value 2021-06-10 19:01:31 +02:00
Zoltán Vörös
f946abd2ee patch circuitpython 2021-06-09 07:34:18 +02:00
Zoltán Vörös
d7e41ad82c update properties documentation 2021-06-09 07:13:39 +02:00
Zoltán Vörös
02acbe4ec3 fix circuitpython properties 2021-06-08 21:59:26 +02:00
Zoltán Vörös
dd8b404c14 re-organise test scripts 2021-06-08 21:18:33 +02:00
Zoltán Vörös
b9c4d3b026 remove redundant flag 2021-06-08 21:02:12 +02:00
Zoltán Vörös
9f6787b2bc update test script 2021-06-08 20:59:28 +02:00
Zoltán Vörös
b9a4d5a2db fix attribute functions 2021-06-08 20:56:55 +02:00
Zoltán Vörös
1196526182 implement ndarray properties 2021-06-08 07:34:44 +02:00
Zoltán Vörös
23093209c9
Merge pull request #407 from v923z/empty
add empty, update docs
2021-06-03 11:51:40 +02:00
Zoltán Vörös
4b2ab160b6 add empty, update docs 2021-06-03 11:39:26 +02:00
Zoltán Vörös
4fdddeabd8
Merge pull request #406 from v923z/doc-fix
fix section header in scipy.linalg docs
2021-06-03 10:50:00 +02:00
Zoltán Vörös
696331ae55 fix section header in scipy.linalg docs 2021-06-03 10:49:31 +02:00
Zoltán Vörös
cdf13c3661
Merge pull request #404 from v923z/docs
add cho_solve documentation
2021-06-03 10:40:53 +02:00
Zoltán Vörös
3e1f08aa96 add cho_solve documentation 2021-06-03 10:39:07 +02:00
Zoltán Vörös
28d6530c9c
Update README.md 2021-06-03 10:12:16 +02:00
Zoltán Vörös
32d37f82b5
Merge pull request #403 from v923z/approx
allow approx functions to take iterable arguments
2021-06-03 09:53:38 +02:00
Zoltán Vörös
0609da8dae change version number 2021-06-03 09:53:12 +02:00
Zoltán Vörös
58cde9a4b1 allow approx functions to take iterable arguments 2021-06-03 09:43:18 +02:00
Zoltán Vörös
6f766908db
Merge pull request #402 from v923z/vector
Vector
2021-06-03 08:39:42 +02:00
Zoltán Vörös
8493f8ed98 add type specifier to pointer 2021-06-03 08:32:10 +02:00
Zoltán Vörös
9aefb4242d vectorised functions work nested iterables now 2021-06-03 08:27:23 +02:00
Zoltán Vörös
424a0abe37
Merge pull request #401 from v923z/from_iterable
factor out array creation from iterables
2021-06-03 08:14:21 +02:00
Zoltán Vörös
e01ddaca0b add ndarray_from_iterable to ndarray_from_mp_obj 2021-06-02 07:50:35 +02:00
Zoltán Vörös
53f644a44b factor out array creation from iterables 2021-06-02 07:37:15 +02:00
Zoltán Vörös
d425641138
Update esp32-cmake.sh 2021-06-01 19:23:27 +02:00
Zoltán Vörös
c94699f017
remove quotes 2021-06-01 19:23:13 +02:00
Zoltán Vörös
e5a0da79f1
Merge pull request #398 from v923z/upcasting
Upcasting
2021-06-01 18:35:56 +02:00
Zoltán Vörös
8e365eca62 Merge branch 'upcasting' of github.com:v923z/micropython-ulab into upcasting 2021-06-01 18:19:49 +02:00
Zoltán Vörös
f91411da95 fix version number 2021-06-01 18:19:31 +02:00
Zoltán Vörös
a6def3d20e
Merge branch 'master' into upcasting 2021-06-01 18:19:08 +02:00
Zoltán Vörös
8979b8a67d fix endianness problem in ndarray_from_mp_obj 2021-06-01 18:17:19 +02:00
Zoltán Vörös
4edb6aa318 fix upcasting rules for ndarray + scalar 2021-06-01 17:32:18 +02:00
Zoltán Vörös
4648697458
Merge pull request #397 from v923z/arange
initialise arange values via macro
2021-05-31 19:34:54 +02:00
Zoltán Vörös
8bb9951fdd change version number to 2.8.4 2021-05-31 19:27:16 +02:00
Zoltán Vörös
1196e2f3ed initialise arange values via macro 2021-05-31 19:25:18 +02:00
Zoltán Vörös
9cc3638604
Update README.md 2021-05-24 21:30:06 +02:00
Zoltán Vörös
add77681ad
Merge pull request #395 from v923z/empty-fix
fix nan return value
2021-05-24 20:10:26 +02:00
Zoltán Vörös
370d6c9bed fix nan return value 2021-05-24 20:02:38 +02:00
Zoltán Vörös
6c7144f59d
Merge pull request #393 from v923z/empty-fix
Empty fix
2021-05-22 14:58:27 +02:00
Zoltán Vörös
52795a86ed fix nan for macintosh 2021-05-22 14:46:10 +02:00
Zoltán Vörös
2c84309139 fix all/any/median for empty arrays 2021-05-22 14:31:13 +02:00
Zoltán Vörös
bfe672532d fix all/any for empty arrays 2021-05-22 14:20:57 +02:00
Zoltán Vörös
f029495d5f
Merge pull request #392 from v923z/empty-fix
fix array initialisation with empty iterables
2021-05-18 22:08:21 +02:00
Zoltán Vörös
6420ec9c45 fix array initialisation with empty iterables 2021-05-18 22:02:42 +02:00
Zoltán Vörös
8a8b64b0c2
Merge pull request #391 from RoboticExplorationLab/master
Implementation of cho_solve function in scipy.linalg module
2021-05-16 18:44:34 +02:00
vikas-udupa
b0679e6d16 implemented cho_solve function in scipy.linalg 2021-05-15 23:42:02 -04:00
Zoltán Vörös
d157fc2393
Merge pull request #390 from teuler/master
Update README.md
2021-05-15 08:47:36 +02:00
teuler
a35c209f1e Update README.md
Hint to later esp-idf version and name change for TINYPICO board
2021-05-15 08:13:16 +02:00
Zoltán Vörös
05e2c575fc
Merge pull request #388 from v923z/gc-fix
fix garbage collection problem
2021-05-13 22:12:05 +02:00
Zoltán Vörös
c08cba3cdf add simple garbage collector test 2021-05-13 22:10:34 +02:00
Zoltán Vörös
f1a2aadc6e fix garbage collection problem 2021-05-13 21:54:50 +02:00
Zoltán Vörös
90824d28e5
Merge pull request #386 from v923z/trick-fix
fix section header
2021-05-12 07:19:36 +02:00
Zoltán Vörös
2a64202e38 fix section header 2021-05-12 07:18:52 +02:00
Zoltán Vörös
7271c132e5
Merge pull request #385 from v923z/trick-fix
finish incomplete example snippet
2021-05-11 18:54:45 +02:00
Zoltán Vörös
f80be5f86d finish incomplete example snippet 2021-05-11 18:54:07 +02:00
Zoltán Vörös
17cef6ad24
Merge pull request #384 from v923z/tricks
add tricks chapter
2021-05-11 18:41:01 +02:00
Zoltán Vörös
3141d2afd6 add tricks chapter 2021-05-11 18:26:33 +02:00
Jeff Epler
7616e44fcb
Merge pull request #383 from jepler/fix-cp-build
Fix cp build
2021-05-10 11:46:33 -05:00
6b20ae83db ignore an error in upstream circuitpython 2021-05-10 11:06:23 -05:00
9b082fc4d9 Build on changes to build scripts or requirements 2021-05-10 10:42:26 -05:00
1d05843315 requirements_cp_dev: Update with requirements from circuitpython 2021-05-10 10:41:32 -05:00
ebdff02e6e build-cp.sh: Drop unneeded workaround. 2021-05-10 09:03:58 -05:00
Zoltán Vörös
5f1d4f5673
Merge pull request #381 from v923z/doc-fix
update docs
2021-05-09 08:29:29 +02:00
Zoltán Vörös
43d7de1558 update docs 2021-05-09 08:27:16 +02:00
Zoltán Vörös
3c39995349
Merge pull request #380 from v923z/solve-docs
add docs for solve_triangular
2021-05-09 08:14:10 +02:00
Zoltán Vörös
df2c9942aa add docs for solve_triangular 2021-05-09 08:08:20 +02:00
Zoltán Vörös
1c027267e2
fix circuitpython CI
change to lowercase macro style, so that `circuitpython` can automatically build
2021-05-07 07:31:19 +02:00
Zoltán Vörös
a61cd88f24
Merge pull request #377 from RoboticExplorationLab/master
Adding solve_triangular function in numpy.linalg module
2021-05-07 07:28:18 +02:00
vikas-udupa
e52fa96c23 removed redundant function pointers, added copyright note 2021-05-05 18:47:15 -04:00
vikas-udupa
ba6409a7ad corrected date in ulab-change-log 2021-05-05 11:40:42 -04:00
vikas-udupa
f9b0e4381b corrections to solve_triangular, moved solve_triangular to scipy.linalg module 2021-05-05 11:24:05 -04:00
vikas-udupa
3e63c8e144 implemented solve_triangular and syncing with original repo 2021-04-29 09:26:33 -04:00
vikas-udupa
ab60c5c98c implemented solve_triangular function in numpy.linalg module along with two tests 2021-04-29 09:20:17 -04:00
Zoltán Vörös
6e0b9bb7d9
Merge pull request #376 from v923z/readme
update ESP32 compile instructions in readme
2021-04-28 07:58:13 +02:00
Zoltán Vörös
9464cb772b refer to build script in readme 2021-04-28 07:52:08 +02:00
Zoltán Vörös
16af74132a add build script for ESP32 cmake 2021-04-28 07:49:29 +02:00
Zoltán Vörös
e01f8059c5 add ESP32 cmake instructions to readme 2021-04-28 07:43:51 +02:00
Zoltán Vörös
3b2d7ddf04
Merge pull request #370 from adafruit/lowercase_macros
Lowercase MP macros
2021-04-27 21:43:12 +02:00
Zoltán Vörös
ecae9e8fed
Merge pull request #375 from v923z/readme
add circuitpython compilation section to README
2021-04-27 19:19:51 +02:00
Zoltán Vörös
0924daa819
Update README.md
Co-authored-by: Dan Halbert <halbert@halwitz.org>
2021-04-27 16:59:26 +02:00
Zoltán Vörös
c79a993b33
Update README.md
Co-authored-by: Dan Halbert <halbert@halwitz.org>
2021-04-27 16:59:18 +02:00
Zoltán Vörös
bd3b03080b add circuitpython compilation section to README 2021-04-27 07:30:39 +02:00
Zoltán Vörös
64d53aa2c9
Merge pull request #374 from v923z/optimize-fix
fix small glitch in optimize
2021-04-26 22:04:45 +02:00
Zoltán Vörös
f3753597ff fix small glitch in optimize 2021-04-26 21:52:51 +02:00
Zoltán Vörös
8a7f7a7934
increment version number 2021-04-23 17:15:38 +02:00
Zoltán Vörös
95bac60238
Merge pull request #367 from t-ikegami/ulab_const_float_fix
Fix implementation of math constants (e, inf, nan, pi).
2021-04-23 17:13:28 +02:00
Tsutomu IKEGAMI
93cb294765 Fix implementation of math constants (e, inf, nan, pi).
Originally, math constants are implemented for MICROPY_OBJ_REPR = A and B.
Modify float object representations to work with MICROPY_OBJ_REPR = C and D.
2021-04-23 15:54:16 +09:00
Scott Shawcroft
e3bf07cabb
Lowercase MP macros 2021-04-22 17:54:02 -07:00
Zoltán Vörös
fb44eafb9c
correct esp32 instructions
Added small correction based on https://github.com/v923z/micropython-ulab/issues/365
2021-04-18 20:05:40 +02:00
Jeff Epler
6d852a3b25
Merge pull request #362 from jepler/ci-circuitpython-main-docs-stubs
workflows: Add CircuitPython check-stubs & doc building to CI
2021-04-04 20:26:40 -05:00
Jeff Epler
d9082f86ed workflows: Add CircuitPython check-stubs & doc building to CI 2021-04-04 20:20:57 -05:00
Jeff Epler
dd990e65cb
Merge pull request #361 from jepler/ci-circuitpython-main
workflows: Reenable CI against CircuitPython (main branch)
2021-04-04 20:10:11 -05:00
Jeff Epler
25c8701d75 workflows: Reenable CI against CircuitPython (main branch) 2021-04-04 20:03:30 -05:00
Jeff Epler
e66eb74361
Merge pull request #360 from jepler/cp-2.6.0
Changes relative to 2.6.0 to allow building on CircuitPython
2021-04-04 20:00:41 -05:00
ef65415b55 CircuitPython make check-stubs fixes 2021-04-03 12:10:08 -05:00
4666bbd4de Fix diagnostics in CircuitPython 2021-04-03 12:07:12 -05:00
9cd13000b7 ulab.array -> ulab.ndarray
This was flagged as an error in building circuitpython, since ulab.array
doesn't name a type object.

# Conflicts:
#	code/numpy/linalg/linalg.c
2021-04-03 12:06:07 -05:00
fe6677335f code: Add a docstring for numpy, scipy packages
.. this fixes a problem in CircuitPython where building the docs would say
"../shared-bindings/ulab/numpy/approx/index.rst: WARNING: document isn't included in any toctree"
2021-04-03 12:05:42 -05:00
Zoltán Vörös
c1b8f9d687
updated version number 2021-04-02 13:00:10 +02:00
Zoltán Vörös
27203ea6dd
Merge pull request #357 from Crazyhackelkorn/ardupy_openmv
The PR adds pre-processor switches for compilation with older `micropython` versions.
2021-04-01 10:35:14 +02:00
Stephan Hartl
da9eb89e9a Changed define OPENMV to micropython version check
The issue with is solved only coresponed to micropython v1.11
2021-04-01 09:49:42 +02:00
Zoltán Vörös
5c6d43bae5
Merge pull request #358 from v923z/snippets
add rclass
2021-04-01 07:54:55 +02:00
Zoltán Vörös
7e6a5f9fc9
Update snippets/rclass.py
Co-authored-by: Cal Hays <callumjhays@gmail.com>
2021-04-01 07:53:04 +02:00
Zoltán Vörös
4701338c28 add rclass 2021-03-31 20:49:53 +02:00
Stephan Hartl
04f6fd68f9 Readd some OPENMV code
I used the OPENMV define to get ulab 1.6.1 under ArduPy (Mircopython v1.11) running.
To update to 2.1.5 i have readded the missing code
2021-03-31 16:18:30 +02:00
Stephan Hartl
07545f5156 add relativ path for ulab.h and ndarray.h
Some header files didn't had relative path.
Compiling Ardupy didn't finde the files.
In C files ist the path right
2021-03-31 16:11:09 +02:00
Zoltán Vörös
7a183e1fa0
Merge pull request #353 from v923z/where
add where
2021-03-31 08:04:47 +02:00
Zoltán Vörös
3f0068beb3
Merge pull request #356 from pimoroni/patch-cmake-instructions
Update RP2 build script and README instructions
2021-03-31 08:04:20 +02:00
Zoltán Vörös
2407d5e289 fix trivial error in ndarray_from_mp_object 2021-03-31 07:24:52 +02:00
Zoltán Vörös
032e891545 add where tests 2021-03-31 07:11:33 +02:00
Phil Howard
adc260a2b9 Update RP2 build instructions to use upstream 2021-03-30 16:48:39 +01:00
Phil Howard
962450a3ab Update build/rp2.sh to use upstream micropython 2021-03-30 16:47:55 +01:00
Zoltán Vörös
24caf74d90
Update README.md 2021-03-28 16:19:33 +02:00
Zoltán Vörös
1af6c9b0d1
Update README.md 2021-03-28 16:17:07 +02:00
Zoltán Vörös
7a6207079a
Update README.md 2021-03-28 16:14:43 +02:00
Zoltán Vörös
5890f0e193
Merge pull request #352 from pimoroni/patch-cmake-debug
Remove debug output from code/usermod.cmake
2021-03-26 09:44:07 +01:00
Phil Howard
a630f2ae23 Add build script for rp2 port 2021-03-25 21:38:38 +00:00
Phil Howard
a0ebea086b Update CMake documentation for rp2/esp32 2021-03-25 21:11:13 +00:00
Phil Howard
571286a10e code/micropython.cmake: drop redundant -D 2021-03-25 21:11:13 +00:00
Phil Howard
74371a8acb Move code/usermod.cmake to code/micropython.cmake 2021-03-25 21:11:13 +00:00
Phil Howard
655aa2ccd1 Remove debug output from code/usermod.cmake 2021-03-25 21:11:13 +00:00
Zoltán Vörös
064cb67b88
fix esp32 link 2021-03-23 19:39:57 +01:00
Zoltán Vörös
74e11c9bff
add link to esp32 build script 2021-03-23 19:38:31 +01:00
Zoltán Vörös
84caddf2a4
Merge pull request #354 from v923z/build
add build script for esp32
2021-03-23 19:27:30 +01:00
Zoltán Vörös
97e1ab98fa add build script for esp32 2021-03-23 19:26:22 +01:00
Zoltán Vörös
b996a16be3 add where documentation 2021-03-23 17:28:17 +01:00
Zoltán Vörös
6668bb3060 add where implementation 2021-03-23 17:09:27 +01:00
Zoltán Vörös
fbca8d8396
Merge pull request #351 from v923z/esp
fix ESP32 compilation error and instructions
2021-03-21 18:53:48 +01:00
Zoltán Vörös
8d77d959cc fix ulab directory 2021-03-21 18:52:17 +01:00
Zoltán Vörös
8d185cf2fc spell out installation directory 2021-03-21 18:50:51 +01:00
Zoltán Vörös
bdca1c328e fix typo 2021-03-21 18:47:57 +01:00
Zoltán Vörös
db71303fb0 fix ESP32 compilation error and instructions 2021-03-21 18:43:18 +01:00
Zoltán Vörös
714a3b8727
Merge pull request #348 from v923z/all-any-test
add any/all test script
2021-03-13 18:15:44 +01:00
Zoltán Vörös
fde76a4160 add any/all test script 2021-03-13 18:12:50 +01:00
Zoltán Vörös
ee7fb20898
Update README.md 2021-03-12 22:06:19 +01:00
Zoltán Vörös
ba46921524
Merge pull request #346 from pimoroni/patch-cmake-user-c-module-support
USER_C_MODULES cmake support for rp2 and esp32
2021-03-12 22:00:24 +01:00
Phil Howard
51b5f8b930 USER_C_MODULES cmake support for rp2 and esp32
This change introduces `usermod.cmake`, the CMake configuration required to make
this library compatible with the CMake build systems for the rp2 (Raspberry Pi Pico/RP2040)
and esp32 ports of MicroPython.

This is effectively 1:1 in functionality with `micropython.mk` and defines the source files,
include directories and compile arguments needed to build the module.

See https://github.com/micropython/micropython/pull/6960 for discussion.
2021-03-12 20:53:33 +00:00
Zoltán Vörös
d460dbcc7f
Update README.md 2021-03-12 21:11:47 +01:00
Zoltán Vörös
ef80991418
Merge pull request #347 from jsimonrichard/patch-1
📝 Add rp2 firmware instructions
2021-03-11 06:58:19 +01:00
Simon Richard
17be6e6188
📝 Add rp2 firmware instructions
Add instructions for compiling micropython with ulab for the rp2 port.
2021-03-10 16:58:21 -05:00
Zoltán Vörös
b3b22ab638
Merge pull request #344 from v923z/dot-test
fix dot test
2021-03-08 18:45:12 +01:00
Zoltán Vörös
95e9cd9c59
Merge pull request #343 from v923z/linspace-fix
fix linspace/logspace/arange for Boolean dtypes
2021-03-08 18:44:13 +01:00
Zoltán Vörös
e0b65442a5
Merge branch 'master' into linspace-fix 2021-03-08 18:42:47 +01:00
Zoltán Vörös
779cd3c5a0 fix linspace/logspace/arange for Boolean dtypes 2021-03-08 18:41:19 +01:00
Zoltán Vörös
a8b78fa051 fixed dot test 2021-03-08 17:15:18 +01:00
Zoltán Vörös
c00c7c9c39
Update ulab-utils.rst 2021-03-08 09:17:55 +01:00
Zoltán Vörös
84062fc1cd
Update index.rst 2021-03-08 09:17:06 +01:00
Zoltán Vörös
c147c90a1e
Merge pull request #342 from v923z/utils
added first implementation of utils sub-module
2021-03-08 09:07:35 +01:00
Zoltán Vörös
c501569dd7 add from_buffer tests 2021-03-08 07:18:55 +01:00
Zoltán Vörös
663a7d2fe0 add from_int16_buffer, from_uint16_buffer functions 2021-03-05 15:25:46 +01:00
Zoltán Vörös
48418d46ab fix header constants 2021-03-05 08:03:56 +01:00
Zoltán Vörös
e6ac55a142 change function names to from_int32_buffer, from_uint32_buffer 2021-03-05 07:59:39 +01:00
Zoltán Vörös
9a78b59ab5 fix loop counter 2021-03-05 07:39:37 +01:00
Zoltán Vörös
ebf2cc4bc1 wrote docs for the utils module 2021-03-04 20:11:22 +01:00
Zoltán Vörös
3929e6b49d added from_uintbuffer function 2021-03-04 19:16:34 +01:00
Zoltán Vörös
c84bf46d7e added first implementation of utils sub-module 2021-03-03 20:15:06 +01:00
Zoltán Vörös
cd51012fa0
Merge pull request #340 from v923z/dot-fix
fix dot function
2021-02-23 23:23:58 +01:00
Zoltán Vörös
3fb04aedac fix dot function 2021-02-23 23:17:45 +01:00
Zoltán Vörös
42212622ff
Merge pull request #338 from v923z/master-buffer
re-introduce ndarray_get_buffer, and buffer protocol
2021-02-21 21:34:06 +01:00
Zoltán Vörös
06cb1c2388 buffer tests pass now 2021-02-21 21:31:46 +01:00
Zoltán Vörös
b8b98be91b add trailing newline in buffer.py.exp 2021-02-21 21:21:17 +01:00
Zoltán Vörös
f821c2a8fe fix import error buffer test 2021-02-21 21:19:47 +01:00
Zoltán Vörös
0aef4a9e25 add buffer tests 2021-02-21 21:09:28 +01:00
Zoltán Vörös
6fb6f2383f re-introduce ndarray_get_buffer, and buffer protocol 2021-02-21 21:06:02 +01:00
Zoltán Vörös
ce763eba4b
Merge pull request #337 from v923z/dense-fix
fix ndarray_is_dense, eye, ones, full, and zeros for Boolean type
2021-02-21 18:07:00 +01:00
Zoltán Vörös
54ff3f30dd fix ndarray_is_dense, eye, ones, full, and zeros for Boolean type 2021-02-21 18:02:40 +01:00
Zoltán Vörös
34c7a456b6
Merge pull request #326 from retsyo/patch-1
Update README.md
2021-02-17 18:26:09 +01:00
retsyo
09fc46aa92
Update README.md
README.md says
```
try:
    from ulab import numpy as np
    from ulab import scipy as spy
except ImportError:
    import numpy as np
    import scipy as spy

x = np.array([1, 2, 3])
spy.special.erf(x)
```

but in fact, for CPython
```
>>> import scipy as spy
>>> spy.special.erf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'scipy' has no attribute 'special'
>>> import scipy.special
>>>
```

so I think the README should be
```
try:
    from ulab import numpy
    from ulab import scipy
except ImportError:
    import numpy
    import scipy.special

x = numpy.array([1, 2, 3])
print(x)

print(scipy.special.erf(x))
```
2021-02-17 22:30:45 +08:00
Zoltán Vörös
6f65034c24
Merge pull request #325 from ubIQio/byteswaptest
Add test for byteswap
2021-02-15 22:00:33 +01:00
jetpax
fa29c5c92a Add test for byteswao 2021-02-15 12:57:43 -08:00
Zoltán Vörös
442fed040f
Create CONTRIBUTING.md 2021-02-15 19:40:53 +01:00
Zoltán Vörös
679784a3f1 Update issue templates 2021-02-15 19:32:52 +01:00
Zoltán Vörös
30d67360a4 Update issue templates 2021-02-15 19:30:09 +01:00
Zoltán Vörös
d760d885fd
updated readme
This PR incorporates the instructions in https://github.com/v923z/micropython-ulab/issues/323
2021-02-15 18:54:21 +01:00
Zoltán Vörös
487658e22f
Merge pull request #319 from v923z/dot-fix
fixed faulty dot function
2021-02-15 18:09:56 +01:00
Zoltán Vörös
74f819e5fb
Merge branch 'master' into dot-fix 2021-02-15 18:08:45 +01:00
Zoltán Vörös
bd9cd88192
Merge pull request #324 from v923z/byteswap-doc
updated docs with a section on byteswap
2021-02-15 18:01:22 +01:00
Zoltán Vörös
b6ba1071d1 updated docs with a section on byteswap 2021-02-15 17:11:18 +01:00
Zoltán Vörös
2c69328303
Merge pull request #322 from v923z/frombuffer-fix
removed dangling byte from frombuffer
2021-02-14 09:53:22 +01:00
Zoltán Vörös
d512e716c3
Merge pull request #317 from v923z/byteswap
implemented byteswap
2021-02-14 09:44:54 +01:00
Zoltán Vörös
efcfeff790
Merge branch 'master' into byteswap 2021-02-14 09:36:07 +01:00
Zoltán Vörös
8444b12c90
Merge pull request #321 from v923z/frombuffer-fix
fixes frombuffer implementation glitch
2021-02-14 09:34:51 +01:00
Zoltán Vörös
281b8da84a removed dangling byte from frombuffer 2021-02-14 09:32:37 +01:00
Zoltán Vörös
60d017d3f6 fixes frombuffer implementation glitch 2021-02-14 09:04:42 +01:00
Zoltán Vörös
a0cd09c7c3
Update README.md 2021-02-13 11:16:49 +01:00
Zoltán Vörös
6b9ec26d02
Update README.md 2021-02-13 11:13:59 +01:00
Zoltán Vörös
51a8c12820
Update README.md 2021-02-13 11:12:26 +01:00
Zoltán Vörös
18f3b180b3 fixed faulty dot function 2021-02-13 10:19:46 +01:00
Zoltán Vörös
3486f77a80
Merge pull request #318 from v923z/transform
moved the dot function to numpy
2021-02-13 09:40:09 +01:00
Zoltán Vörös
0e76f9458d updated trace/dot documentation 2021-02-13 09:37:34 +01:00
Zoltán Vörös
317943b970 moved trace to numpy 2021-02-13 09:25:08 +01:00
Zoltán Vörös
701ad767c8 moved the dot function to numpy 2021-02-13 09:06:02 +01:00
Zoltán Vörös
27bc5b2e79 implemented byteswap 2021-02-12 21:10:08 +01:00
Zoltán Vörös
ab964b94be
Merge pull request #314 from ubIQio/ubIQio-patch-1
Add vector 2-norm test
2021-02-12 18:35:43 +01:00
Zoltán Vörös
0ea7871404
Merge pull request #316 from v923z/stats
simplify array contraction
2021-02-12 18:33:23 +01:00
Zoltán Vörös
7de1d0947e fixed small glitch in all/any 2021-02-12 18:23:53 +01:00
Zoltán Vörös
8046888d99 simplified array contraction algorithm 2021-02-12 18:15:42 +01:00
Zoltán Vörös
0b20b30af6 combined macros for std and mean 2021-02-11 08:03:41 +01:00
jetpax
bc7e9bb8df Fix failing test 2021-02-10 19:21:52 -08:00
Zoltán Vörös
6499453d32 removed redundant code from numerical.c 2021-02-10 20:27:48 +01:00
Zoltán Vörös
93f70d16bc rationalised code in sum/mean/std 2021-02-10 20:21:24 +01:00
Zoltán Vörös
fd8a2256a6 improved accuracy of sum on iterables 2021-02-10 17:17:49 +01:00
ubIQio
b4f54900bf
Add vector 2-norm axis = 0 test case 2021-02-10 16:21:57 +01:00
Zoltán Vörös
0acf90433e simplified all/any implementation, fixed tools_reduce_axes function 2021-02-10 07:28:09 +01:00
ubIQio
1754a2eb14
Add vector norm test 2021-02-10 06:08:30 +01:00
Zoltán Vörös
96a944cc38
Merge pull request #313 from v923z/norm-fix
removed redundant exception from linalg.norm
2021-02-09 19:24:46 +01:00
Zoltán Vörös
93822e6e13 removed redundant exception from linalg.norm 2021-02-09 19:23:51 +01:00
Zoltán Vörös
5f716a7883
Merge pull request #312 from v923z/norm
improved linalg.norm
2021-02-09 18:57:11 +01:00
Zoltán Vörös
6867951ab4 fixed indexing glitch in tools_reduce_axes 2021-02-09 18:53:43 +01:00
Zoltán Vörös
cacb1b66bd fixed linalg.norm for a special case 2021-02-09 18:26:57 +01:00
Zoltán Vörös
674220c959 updated change log 2021-02-09 17:34:14 +01:00
Zoltán Vörös
0d1379d62b linalg.norm should not work with the axis keyword argument 2021-02-09 17:32:41 +01:00
Zoltán Vörös
2c71434eab moved the reduce_axes helper function to ulab_tools 2021-02-09 07:00:47 +01:00
Zoltán Vörös
e00ad9ca11
Merge pull request #311 from v923z/norm
improved accuracy of linalg.norm, and extended it to generic iterables
2021-02-08 20:27:12 +01:00
Zoltán Vörös
7c4f4dba48 improved accuracy of linalg.norm, and extended it to generic iterables 2021-02-08 20:24:45 +01:00
Zoltán Vörös
a726c1d00d
Merge pull request #310 from v923z/init-fix
len fix
2021-02-08 19:53:22 +01:00
Zoltán Vörös
dc4ae424ce update change log 2021-02-08 19:50:38 +01:00
Zoltán Vörös
01cddd36a5 fix len unary operator 2021-02-08 19:49:26 +01:00
Zoltán Vörös
0e0956bc3e
Merge pull request #309 from v923z/any
any/all implementation
2021-02-08 18:23:47 +01:00
Zoltán Vörös
4607f8e9f9 added any/all documentation 2021-02-08 17:58:39 +01:00
Zoltán Vörös
f7d86b53e2 completed any/all implementation 2021-02-08 17:47:05 +01:00
Zoltán Vörös
e485b0c5e4 implemented any/all for iterables and scalars 2021-02-01 21:05:43 +01:00
Zoltán Vörös
e4fa4cb851
Merge pull request #303 from v923z/isinf
adds Isinf, isfinite
2021-02-01 19:27:03 +01:00
Zoltán Vörös
66f0851068
Merge pull request #300 from CallumJHays/isinf
Add `np.isinf` tests
2021-02-01 19:22:45 +01:00
CallumJHays
80d8fcc919 test np.isinf 2021-02-01 21:08:12 +10:00
Zoltán Vörös
418e1b846c updated .gitignore file 2021-01-30 22:28:50 +01:00
Zoltán Vörös
c622fe61d8 added isinf/isfinite functions 2021-01-29 22:38:40 +01:00
Zoltán Vörös
9611a0a638 removed circuitpython tests 2021-01-29 17:15:28 +01:00
Zoltán Vörös
eb33480797
Merge pull request #298 from v923z/std-fix
Std fix
2021-01-29 16:40:08 +01:00
Zoltán Vörös
685ec61bbb
Merge branch 'master' into std-fix 2021-01-29 16:34:37 +01:00
Zoltán Vörös
e5961ecd3a
Merge pull request #297 from v923z/init
Adds array initialisation from nested iterables in 3 and 4 dimensions.
2021-01-29 16:32:56 +01:00
Zoltán Vörös
67e0e500c3
Merge branch 'master' into init 2021-01-29 16:31:04 +01:00
Zoltán Vörös
cca906e29b removed unpaired #endif 2021-01-29 16:28:56 +01:00
Zoltán Vörös
ba0e2a5310 fixed the computation of the standard deviation on iterables 2021-01-29 15:24:25 +01:00
Zoltán Vörös
8832683fc6 implemented generic array initialisation from nested iterables 2021-01-29 15:07:24 +01:00
Zoltán Vörös
bd4b9eeaf1
Update README.md 2021-01-24 09:34:44 +01:00
Zoltán Vörös
a1f01dd489
Merge pull request #293 from teuler/new_branch
fix to allow including user.c
2021-01-24 09:13:44 +01:00
teuler
875d2730ce
fix to allow including user.c 2021-01-23 09:30:34 +01:00
Zoltán Vörös
be9033384e
Merge pull request #291 from v923z/openmv
adds option to override pre-processor constants
2021-01-21 22:00:54 +01:00
Zoltán Vörös
cdc1429196 added option to include own config file 2021-01-21 21:55:16 +01:00
Zoltán Vörös
83a92f9b4b added ifndef/endif wrappers in ulab.h 2021-01-21 20:51:55 +01:00
Zoltán Vörös
23de394fc1 backup commit, not functional 2021-01-21 20:20:15 +01:00
Zoltán Vörös
2ed1699fe1
re-worded several sections of readme 2021-01-16 10:35:42 +01:00
Zoltán Vörös
48a3a65998
added build instructions for the unix port 2021-01-16 10:06:03 +01:00
Zoltán Vörös
9f51cd968b Merge branch 'numpy-scipy'
Moving to complete numpy compatibility
2021-01-15 16:29:36 +01:00
Zoltán Vörös
5a491d4c5d commented out circuitpython build instructions 2021-01-15 16:25:06 +01:00
Zoltán Vörös
b26c3754c5 pulled in workflow files from master 2021-01-15 16:20:35 +01:00
Zoltán Vörös
206b3668e4 updated readme with esp32 instructions 2021-01-15 16:09:05 +01:00
Zoltán Vörös
208fa196ab updated docs 2021-01-15 16:03:40 +01:00
Zoltán Vörös
8e16ab25e5 added documentation on frombuffer, and fixed small error in the code 2021-01-15 08:03:16 +01:00
Zoltán Vörös
d6936aa1c4 updated readme 2021-01-15 07:44:29 +01:00
Zoltán Vörös
a093b3a3cb
Merge pull request #287 from v923z/diff-fix
fixes issue #285
2021-01-14 20:17:47 +01:00
Zoltán Vörös
96fe29d9c7 found copy-paster errors in diff macro 2021-01-14 20:15:19 +01:00
Zoltán Vörös
e387404a82 fixes issue #285 2021-01-14 20:08:23 +01:00
Zoltán Vörös
27229be5b7
Merge pull request #286 from jepler/ci-macos
Fix macos build (mp & cp), add mac builds to ci
2021-01-14 19:43:02 +01:00
Zoltán Vörös
06bb8348a0 updated docs, removed circuitpython stuff, and fixed diff code 2021-01-14 19:40:18 +01:00
cbe3e4c2ad continue chasing cp+mac build errors 2021-01-13 16:25:25 -06:00
91076ad408 continue chasing cp+mac build errors 2021-01-13 16:16:07 -06:00
89ecef5a20 get rid of (non-portable?) sed -i 2021-01-13 16:12:46 -06:00
99984d0042 build-cp: Eliminate axtls, work around adafruit/circuitpython#3990 2021-01-13 16:09:21 -06:00
579f6e623c try again to install deps sanely 2021-01-13 15:51:01 -06:00
00e07bfa5b fix clang cast problem
Closes #284.
2021-01-13 15:49:08 -06:00
5a8c09123f try again to install cp deps
.. the doc-related items are removed since the build process does not
currently test doc building
2021-01-13 15:47:30 -06:00
1d4ea95a80 ci: Build circuitpython+macos 2021-01-13 15:45:35 -06:00
8680051d29 ci: invoke build.sh
.. otherwise we have to maintain more stuff in two places
2021-01-13 15:45:25 -06:00
f383766add reduce amount of cloned stuff 2021-01-13 15:44:14 -06:00
eac578603d Get rid of nonportable nproc
.. using a Python incantation instead.  Mac doesn't have
nproc.
2021-01-13 15:31:01 -06:00
793eef4030 Add portable version of readlink -f
from https://github.com/ko1nksm/readlinkf/blob/master/readlinkf.sh
(license: CC0)

Mac readlink does not support the -f flag, which is not
in POSIX.
2021-01-13 15:30:06 -06:00
5d6d7a3248 ci: fix running micropython tests 2021-01-13 15:15:24 -06:00
897c5a528c ci: get rid of need for ffi, btree 2021-01-13 15:14:16 -06:00
2a81310a96 workflows: Also build on macos 2021-01-13 15:03:09 -06:00
Zoltán Vörös
d62d07ea0b
Merge pull request #283 from mdaeron/master
Should close https://github.com/v923z/micropython-ulab/issues/282
2021-01-11 20:31:33 +01:00
mdaeron
734a2898f6
Update ulab-change-log.md
version 1.6.1
2021-01-11 20:29:58 +01:00
mdaeron
4452e59a09
Update ulab.c
increment version number
2021-01-11 20:27:40 +01:00
mdaeron
af5d24f531
Update ndarray.c
Fix bug in ndarray_tobytes()
2021-01-11 20:13:10 +01:00
Zoltán Vörös
18c974798d
added missing clone command 2021-01-09 09:01:41 +01:00
Zoltán Vörös
9f9e006955
Merge pull request #280 from CallumJHays/numpy-scipy
Rename array to ndarray, replaced with constructor fn.
Should address the comment in https://github.com/v923z/micropython-ulab/issues/275.
2021-01-09 08:55:36 +01:00
CallumJHays
8c789fcdcf Fix stubs 2021-01-09 12:07:00 +10:00
CallumJHays
9dfc972180 Rename array to ndarray, replaced with constructor fn 2021-01-09 11:43:55 +10:00
Zoltán Vörös
b8ab59bd84 re-organised code, extended docs 2021-01-08 17:40:44 +01:00
Zoltán Vörös
771b3d837f
Merge pull request #279 from v923z/v923z-readme
updated readme.md with instructions for compiling for EPS32
2021-01-08 12:00:02 +01:00
Zoltán Vörös
7c782ccb6c
Update README.md
Co-authored-by: Cal Hays <callumjhays@gmail.com>
2021-01-08 11:57:39 +01:00
Zoltán Vörös
5749930280
updated readme.md with instruction for compiling for EPS32 2021-01-08 09:17:52 +01:00
Zoltán Vörös
9484e846a3 pulling in PR 272 from master 2020-12-27 12:03:19 +01:00
Zoltán Vörös
b52292919b
Merge pull request #272 from CallumJHays/bug/esp32-v1.13-ambiguous-float
Specify float literals as float
2020-12-27 12:01:22 +01:00
CallumJHays
2ee2be96a3 use MICROPY_FLOAT_CONST macro 2020-12-26 12:34:41 +10:00
Zoltán Vörös
24fc20d31b pulled in stub from master 2020-12-23 21:23:13 +01:00
Zoltán Vörös
2788b19906
Merge pull request #270 from CallumJHays/bug/add-array.dtype-stub
Add `array.dtype` stub
2020-12-23 21:21:06 +01:00
CallumJHays
eea6b1ed6d Add dtype stub 2020-12-21 16:45:31 +10:00
CallumJHays
9cd7ba13f7 Fix ambiguous float compiler issue 2020-12-21 16:42:25 +10:00
Zoltán Vörös
0c66a65358 simplified frombuffer code a bit 2020-11-27 08:01:09 +01:00
Zoltán Vörös
259b929493 simplified frombuffer code a bit 2020-11-27 08:00:47 +01:00
Zoltán Vörös
7419611272 removed TODO warning from ulab_create.c 2020-11-27 07:53:07 +01:00
Zoltán Vörös
e50bdea0c7 moved spectrogram code to scipy/signal/signal.c 2020-11-27 07:51:32 +01:00
Zoltán Vörös
aa649b28bb factored out fft_fft_ifft_spectrogram 2020-11-27 07:42:20 +01:00
Zoltán Vörös
941d36f635 moved frombuffer to ulab_create 2020-11-26 18:53:29 +01:00
Zoltán Vörös
ea2ccdb914 removed OPENMV flags 2020-11-26 17:27:58 +01:00
Zoltán Vörös
42f20bcc67 added the frombuffer function 2020-11-26 17:26:15 +01:00
Zoltán Vörös
815b543c81 moved numpy.c/numpy.h into its own folder 2020-11-26 07:00:23 +01:00
Zoltán Vörös
1f64d99f6d moved scipy files into their own folder 2020-11-26 06:57:44 +01:00
Zoltán Vörös
b35c1568f3 removed numpy_defs.h, scipy_defs.h 2020-11-25 21:03:35 +01:00
Zoltán Vörös
c7f47819e0 moved sosfilt to scipy/signal.c 2020-11-25 19:46:13 +01:00
Zoltán Vörös
532172f1bc moved 4 functions into optimize 2020-11-25 19:26:26 +01:00
Zoltán Vörös
74bc705175 removed unused use_ulab flags 2020-11-25 17:27:55 +01:00
Zoltán Vörös
118d66c771 added convolve test 2020-11-25 17:23:38 +01:00
Zoltán Vörös
ae0276509c fixed tests 2020-11-25 17:22:25 +01:00
Zoltán Vörös
9cb008a869 arrange all functions into numpy/scipy modules 2020-11-24 22:03:21 +01:00
Zoltán Vörös
21f7ffae9a moved more sub-modules to numpy 2020-11-24 18:10:13 +01:00
Zoltán Vörös
a359cccf96 added numpy.c/h to the repository 2020-11-24 17:56:44 +01:00
Zoltán Vörös
f55b69d2ff moved math constants to numpy module 2020-11-24 17:52:53 +01:00
Zoltán Vörös
c71920e186
Merge pull request #266 from v923z/boolean
Boolean
2020-11-24 08:00:36 +01:00
Zoltán Vörös
a30b1c2dc1 removed unused ndarray->dense member 2020-11-24 07:18:17 +01:00
Zoltán Vörös
c4b06e419f added Boolean initialisation option 2020-11-24 07:16:58 +01:00
Zoltán Vörös
1ec22f83e1
Merge pull request #265 from v923z/nan-fix
fixed nan definition
2020-11-23 23:03:59 +01:00
Zoltán Vörös
7eade2b43c fixed nan definition 2020-11-23 23:03:27 +01:00
Zoltán Vörös
b236e65cec
Merge pull request #264 from v923z/inf
added inf/nan class level constants
2020-11-23 22:53:53 +01:00
Zoltán Vörös
a2dc565992 added inf/nan class level constants 2020-11-23 22:50:17 +01:00
Zoltán Vörös
d5853a1374
Merge pull request #262 from v923z/circuitpy-test
added approx tests to circuitpython
2020-11-23 22:11:29 +01:00
Zoltán Vörös
9d34f8a359 added approx tests to circuitpython 2020-11-23 22:04:29 +01:00
Zoltán Vörös
196883f813
Merge pull request #261 from v923z/test-fix
Test fix
2020-11-23 21:49:15 +01:00
Zoltán Vörös
1f6e5ca648 bumped version number to 1.4.10 2020-11-23 21:45:02 +01:00
Zoltán Vörös
2011510871 fixed fmin test 2020-11-23 21:43:34 +01:00
Zoltán Vörös
184b941c4d
Merge pull request #260 from v923z/sort-fix
added in-place sort
2020-11-23 19:02:55 +01:00
Zoltán Vörös
e8fcf2e94b
Merge branch 'master' into sort-fix 2020-11-23 19:02:46 +01:00
Zoltán Vörös
e3704b7e29
Merge pull request #259 from v923z/convolve-fix
Convolve fix
2020-11-23 19:01:30 +01:00
Zoltán Vörös
0284aae8ac
Merge branch 'master' into convolve-fix 2020-11-23 18:58:53 +01:00
Zoltán Vörös
c55e84b265 added in-place sort 2020-11-23 18:22:42 +01:00
Zoltán Vörös
f43fa2bffb implemented convolve fix 2020-11-23 18:13:31 +01:00
Zoltán Vörös
f6f95f39c7
Merge pull request #241 from mouryarahul/fft
Script to test FFT and Filter
2020-11-23 07:45:26 +01:00
Zoltán Vörös
771ac047fe
Merge pull request #257 from mouryarahul/optim
add script to test numerical optimization functions
2020-11-23 07:42:48 +01:00
Zoltán Vörös
b6e90096c5
Merge pull request #253 from mouryarahul/filter
add script to test filter functions
2020-11-23 07:42:14 +01:00
Zoltán Vörös
094302e414
Merge pull request #254 from mouryarahul/approx
remove approx.py
2020-11-23 07:41:32 +01:00
Zoltán Vörös
6dafbef969
Merge pull request #255 from mouryarahul/trapz
add test script for trapz function
2020-11-23 07:40:38 +01:00
Zoltán Vörös
093ab71ea8
Merge pull request #256 from mouryarahul/interp
add test script for interp function
2020-11-23 07:40:07 +01:00
Zoltán Vörös
e748e4aa32
Merge pull request #258 from v923z/norm-fix
fixed linalg.norm
2020-11-23 07:39:32 +01:00
Zoltán Vörös
67b268d1ad fixing convolve 2020-11-23 07:32:01 +01:00
Zoltán Vörös
d384b3b63b fixed linalg.norm 2020-11-23 07:03:17 +01:00
mouryarahul
b35f4ff7d1 add script to test numerical optimization functions 2020-11-23 00:21:07 +00:00
mouryarahul
b8d36719d0 add test script for interp function 2020-11-23 00:14:43 +00:00
mouryarahul
195afc9ac4 add test script for trapz function 2020-11-23 00:08:34 +00:00
mouryarahul
14302180d1 remove approx.py 2020-11-23 00:00:53 +00:00
mouryarahul
9dd719a792 add script to test filter functions 2020-11-22 23:22:17 +00:00
mouryarahul
d43dd21ef1 remove old filter_fft.py 2020-11-22 23:14:25 +00:00
mouryarahul
3552b42cf5 add script to test fft and ifft 2020-11-22 23:12:41 +00:00
Zoltán Vörös
c34570e6c4
Rename operators_extra.py.exp to operators.py.exp 2020-11-22 20:49:26 +01:00
Zoltán Vörös
c3f7519d6a
Rename operators_extra.py to operators.py 2020-11-22 20:49:07 +01:00
Zoltán Vörös
87a3861273
Merge pull request #247 from mouryarahul/poly
add script to test polynomial module
2020-11-22 20:46:06 +01:00
mouryarahul
958c3f12d4 merge poly.py and poly_extra.py 2020-11-21 22:52:31 +00:00
mouryarahul
80059244b9 merged poly.py and poly_extra.py 2020-11-21 22:41:33 +00:00
mouryarahul
25dfdeebcd minor change 2020-11-21 22:39:52 +00:00
Zoltán Vörös
d9af80e7d9
Merge pull request #243 from mouryarahul/linalg
add script to test linalg module
2020-11-21 20:17:56 +01:00
mouryarahul
45ceae68ec rename polyval to poly_extra 2020-11-21 16:58:05 +00:00
Zoltán Vörös
28d8ccf66a
Merge pull request #251 from v923z/build-fix
unpinned micropython v.1.13
2020-11-21 08:47:05 +01:00
Zoltán Vörös
b7f2cf0193 unpinned micropython v.1.13 2020-11-21 08:41:27 +01:00
Zoltán Vörös
5f7092cec0
Merge pull request #242 from mouryarahul/init
add script to test array initialisation
2020-11-21 07:53:54 +01:00
Zoltán Vörös
48b23e05e7
Merge pull request #244 from mouryarahul/methods
add script to test methods on array
2020-11-21 07:43:46 +01:00
Zoltán Vörös
ffcb74e128
Merge pull request #245 from mouryarahul/numericals
add script to test numericals module
2020-11-21 07:41:20 +01:00
Zoltán Vörös
ae6a0791d7
Merge pull request #246 from mouryarahul/operators
add script to test operators module
2020-11-21 07:40:31 +01:00
Zoltán Vörös
3330c16dbc
Merge pull request #248 from mouryarahul/universal
add script to test universal functions
2020-11-21 07:37:41 +01:00
Zoltán Vörös
4b2f10fa00
Merge pull request #240 from mouryarahul/approx
add script for testing approx module
2020-11-21 07:36:18 +01:00
mouryarahul
5aaf4095f3 add script to test universal functions 2020-11-21 00:27:51 +00:00
mouryarahul
63fee69162 add script to test polynomial module 2020-11-21 00:15:59 +00:00
mouryarahul
fc70f61194 add script to test operators module 2020-11-21 00:09:33 +00:00
mouryarahul
8fc18e2468 add script to test numericals module 2020-11-20 23:59:11 +00:00
mouryarahul
b302e8224b add script to test methods on array 2020-11-20 23:50:51 +00:00
mouryarahul
a6692b0d4d add script to test linalg module 2020-11-20 23:41:03 +00:00
mouryarahul
1aa803a79d add script to test array initialisation 2020-11-20 23:31:37 +00:00
mouryarahul
a070f73267 add test script for filter and fft 2020-11-20 23:22:18 +00:00
mouryarahul
1c54e78eb8 minor change 2020-11-20 23:15:14 +00:00
mouryarahul
d2b5180328 add script for testing approx module 2020-11-20 23:02:34 +00:00
Zoltán Vörös
6850079cf3
Merge pull request #239 from v923z/interp-fix
fixed interp
2020-11-20 17:36:10 +01:00
Zoltán Vörös
7262f3a8aa fixed interp 2020-11-20 17:31:58 +01:00
Zoltán Vörös
c0cb577fd7
Merge pull request #238 from v923z/build-fix
unpin micropython v.1.13
2020-11-20 16:12:13 +01:00
Zoltán Vörös
8442948dba unpin micropython v.1.13 2020-11-20 16:08:55 +01:00
Zoltán Vörös
aae4441df0
Merge pull request #237 from v923z/test-fix
split test scripts into numpy-compatible and circuitpy-compatible sets
2020-11-20 15:33:13 +01:00
Zoltán Vörös
dcfee63c96 added circuitpython poly test 2020-11-20 15:09:30 +01:00
Zoltán Vörös
a7decc13bc added circuitpython compare tests 2020-11-20 15:07:26 +01:00
Zoltán Vörös
f08e276977 added circuitpython argmin/argmax test 2020-11-20 15:04:53 +01:00
Zoltán Vörös
5dcc298db2 fixed circuitpython compilation error 2020-11-20 15:04:09 +01:00
Zoltán Vörös
b93c4b95ab split test scripts into numpy-compatible and circuitpy-compatible sets 2020-11-20 08:10:19 +01:00
Zoltán Vörös
31d0bec9fe
Merge pull request #235 from v923z/test-fix
fixed testing error in ndarray
2020-11-19 19:58:31 +01:00
Zoltán Vörös
2eb06ddd30 fixed testing error in ndarray 2020-11-19 19:53:37 +01:00
Zoltán Vörös
2bba03fa34
Merge pull request #234 from v923z/minmax-fix
fixed signedness problem in min/max
2020-11-19 19:39:47 +01:00
Zoltán Vörös
aa77523cad fixed signedness problem in min/max 2020-11-19 19:35:06 +01:00
Zoltán Vörös
ab5567020d
Merge pull request #233 from v923z/full-fix
implemented full/zeros/ones fix
2020-11-19 19:28:43 +01:00
Zoltán Vörös
b8c892f922
Merge branch 'master' into full-fix 2020-11-19 19:24:28 +01:00
Zoltán Vörös
f7cf98650d implemented full/zeros/ones fix 2020-11-19 19:22:47 +01:00
Zoltán Vörös
6422d8c599
Merge pull request #231 from v923z/dtype-fix
fixed dtype creation code
2020-11-19 08:04:06 +01:00
Zoltán Vörös
2851cf7376 fixed dtype creation code 2020-11-19 07:32:07 +01:00
Zoltán Vörös
3766641bc3
Merge pull request #230 from jepler/pin-micropython-1.13
pin micropython 1.13 for tests
2020-11-18 22:30:07 +01:00
9e24593e94 pin micropython 1.13 for tests 2020-11-18 15:28:06 -06:00
Zoltán Vörös
2a1de4e0cd
Merge pull request #229 from jepler/own-run-tests
Own run tests
2020-11-18 22:17:19 +01:00
e3c75d81af ignore circuitpython generated directory 2020-11-18 15:07:42 -06:00
e6113135cc Take our own version of run-tests
This is modified to better suit our needs, and because micropython
changes in the meantime were troublesome.

Expected outputs can be generated:
./run-tests -d tests --write-exp

Remove any incorrect .exp files before running as they won't be updated
otherwise.
2020-11-18 15:07:42 -06:00
Zoltán Vörös
2e42dee0e6
Merge pull request #227 from v923z/std-fix
fixed std
2020-11-18 19:18:06 +01:00
Zoltán Vörös
72935d3f02
Merge branch 'master' into std-fix 2020-11-18 19:13:40 +01:00
Zoltán Vörös
28fd0b8275 fixed std 2020-11-18 19:12:01 +01:00
Zoltán Vörös
5ff332fa27
Merge pull request #226 from v923z/size-fix
Size fix
2020-11-18 17:31:32 +01:00
Zoltán Vörös
4f7dd719b3
Merge branch 'master' into size-fix 2020-11-18 17:31:15 +01:00
Zoltán Vörös
cb23ca8d69 updated conf.py 2020-11-18 17:10:21 +01:00
Zoltán Vörös
73349aee3a removed size function from linalg 2020-11-18 17:07:03 +01:00
Zoltán Vörös
c6eebda626
Merge pull request #225 from v923z/trapz-fix
fixed trapz
2020-11-18 08:35:33 +01:00
Zoltán Vörös
4e06f3614f fixed trapz 2020-11-18 08:03:49 +01:00
Zoltán Vörös
531345446f
Merge pull request #224 from v923z/fixes
Fixes
2020-11-17 18:38:59 +01:00
Zoltán Vörös
9c76fa7450 removed trailing spaces 2020-11-17 18:22:35 +01:00
Zoltán Vörös
629068c576 removed trailing spaces 2020-11-17 18:22:13 +01:00
Zoltán Vörös
c915abc68e removed extra line of code 2020-11-17 18:20:08 +01:00
Zoltán Vörös
18408cdc5e fixed indexing error in roll 2020-11-17 18:16:28 +01:00
Zoltán Vörös
be7f18e888 fixed in-place divide 2020-11-17 07:09:24 +01:00
Zoltán Vörös
424abb0d66 fixed in-place power 2020-11-17 07:06:44 +01:00
Zoltán Vörös
3e742fb964
Merge pull request #223 from v923z/eye-fix
swapped N, M in eye
2020-11-16 22:45:31 +01:00
Zoltán Vörös
b17eb0284f swapped N, M in eye 2020-11-16 22:40:58 +01:00
Zoltán Vörös
9f9abf97ca
Merge pull request #222 from v923z/trace-fix
fixed indexing error in trace
2020-11-16 22:28:34 +01:00
Zoltán Vörös
9d2e60a0e3 fixed indexing error in trace 2020-11-16 22:22:45 +01:00
Zoltán Vörös
0220432e69
Merge pull request #221 from v923z/clip-fix
Clip fix
2020-11-16 22:13:36 +01:00
Zoltán Vörös
cb31b349c3 updated docs 2020-11-16 22:04:46 +01:00
Zoltán Vörös
a291cfe0c7 implemented clip fix 2020-11-16 22:02:28 +01:00
Zoltán Vörös
26173a46f0
Merge pull request #219 from v923z/function-pointer
Function pointer
2020-11-16 20:21:22 +01:00
Zoltán Vörös
ba0751edc8 set NDARRAY_BINARY_USES_FUN_POINTER to 0 2020-11-16 18:33:03 +01:00
Zoltán Vörös
e311773963 added function pointer option to pow and true divide 2020-11-16 07:23:17 +01:00
Zoltán Vörös
91dc3b9bf4 added function pointers in vectorise 2020-11-13 17:06:59 +01:00
Zoltán Vörös
b9d76a3d2f
Merge pull request #217 from v923z/tools
Tools
2020-11-12 19:40:17 +01:00
Zoltán Vörös
259f030750 added linalg_tools 2020-11-12 18:26:37 +01:00
Zoltán Vörös
31c678b6ee factored out matrix inversion and Jacobi rotations 2020-11-12 18:26:01 +01:00
Zoltán Vörös
fdd99fe776 factored out the FFT kernel into a separate file 2020-11-12 08:52:39 +01:00
Zoltán Vörös
d7b64bbf57
Merge pull request #216 from v923z/dtype2
Dtype2
2020-11-11 19:32:06 +01:00
Zoltán Vörös
434ea7b04c
Merge branch 'master' into dtype2 2020-11-11 19:27:25 +01:00
Zoltán Vörös
f82d3947ae
Merge pull request #215 from v923z/sum-fix
improved sum accuracy of float types
2020-11-11 18:17:36 +01:00
Zoltán Vörös
964db3cc57
Merge branch 'master' into sum-fix 2020-11-11 18:13:31 +01:00
Zoltán Vörös
08af994fbd
Merge pull request #214 from v923z/cumtrapz
improved the accuracy of trapz
2020-11-11 18:10:01 +01:00
Zoltán Vörös
821cae1cc8
Merge branch 'master' into cumtrapz 2020-11-11 18:04:22 +01:00
Zoltán Vörös
fd2c1b6f50
Merge pull request #213 from v923z/transpose-fix
transpose fix
2020-11-11 18:02:20 +01:00
Zoltán Vörös
584644ba71 improved sum accuracy of float types 2020-11-11 17:35:38 +01:00
Zoltán Vörös
080244a50a improved the accuracy of trapz 2020-11-11 17:23:47 +01:00
Zoltán Vörös
a5c439b127 transpose fix 2020-11-11 07:35:14 +01:00
Zoltán Vörös
2ec3a422ce dtype is exposed in circuitpython, too 2020-11-11 07:23:59 +01:00
Zoltán Vörös
083f140770
Merge pull request #211 from v923z/view-fix
fixed slicing issues
2020-11-10 19:53:23 +01:00
Zoltán Vörös
1ca41e5d80 fixed slicing issues 2020-11-10 19:43:09 +01:00
Zoltán Vörös
4ae47c84d5
Merge pull request #210 from v923z/core-fix
fixes array casting glitch
2020-11-09 21:08:07 +01:00
Zoltán Vörös
24fe1dcbe1 fixes array casting glitch 2020-11-09 21:03:18 +01:00
Zoltán Vörös
a0634bf1c8
Merge pull request #209 from v923z/sum-flatten
added the axis=None option to sum/mean/std
2020-11-09 20:42:58 +01:00
Zoltán Vörös
091ab1925d added the axis=None option to sum/mean/std 2020-11-09 19:01:51 +01:00
Zoltán Vörös
ac8a1537ef
Merge pull request #207 from v923z/sum-fix
fixed indexing/array in numerical
2020-11-06 19:16:09 +01:00
Zoltán Vörös
c97d05dc4a fixed indexing/array in numerical 2020-11-06 19:08:26 +01:00
Zoltán Vörös
10de13db41
Merge pull request #206 from v923z/tools
added function pointer tools
2020-11-06 17:57:16 +01:00
Zoltán Vörös
89ac466c1d
Merge branch 'master' into tools 2020-11-06 17:50:58 +01:00
Zoltán Vörös
b7c135faf7 added function pointer tools 2020-11-06 17:49:56 +01:00
Zoltán Vörös
0b47ac7bce updated change log 2020-11-04 07:08:09 +01:00
Zoltán Vörös
404342aa54
Merge pull request #204 from v923z/eig-docs
fixed pointer bug in eig
2020-11-03 21:34:02 +01:00
Zoltán Vörös
ecb0a7775b fixed pointer bug in eig 2020-11-03 21:28:32 +01:00
Zoltán Vörös
148c948fa4
added reference to median 2020-11-03 21:08:36 +01:00
Zoltán Vörös
a2dce2e06c
Merge pull request #203 from v923z/median
implementation of the median function
2020-11-03 21:06:21 +01:00
Zoltán Vörös
2f9ca94602
Merge branch 'master' into median 2020-11-03 21:01:49 +01:00
Zoltán Vörös
a772da39a5 updated change log 2020-11-03 20:59:29 +01:00
Zoltán Vörös
ebd91b8ee2 updated docs 2020-11-03 20:58:36 +01:00
Zoltán Vörös
0f634f9ee3 median implemented for higher dimensional arrays 2020-11-03 20:50:41 +01:00
Zoltán Vörös
c84ea225bd median implemented for linear arrays 2020-11-03 19:07:11 +01:00
Zoltán Vörös
ecd24e9702
Merge pull request #201 from v923z/norm-fix
fixed iteration loop in norm, shape
2020-11-03 18:27:52 +01:00
Zoltán Vörös
adfa60fbc9 fixed bad error in numerical.sort 2020-11-03 17:39:18 +01:00
Zoltán Vörös
ca99f2f0e1 fixed iteration loop in norm, shape 2020-11-03 17:04:40 +01:00
Zoltán Vörös
d83d6718b2 Merge branch 'dtype' of github.com:v923z/micropython-ulab into dtype 2020-11-02 23:28:33 +01:00
Zoltán Vörös
050a63c455 updated exp files 2020-11-02 23:27:59 +01:00
Zoltán Vörös
41320fc1b3 updated docs version 2020-11-02 23:13:47 +01:00
Zoltán Vörös
9d4417f66c
Merge pull request #199 from v923z/4d-fix
fixed initialisation error in ndarray_make_new_core
2020-11-02 23:04:16 +01:00
Zoltán Vörös
db1a822c3b fixed initialisation error in ndarray_make_new_core 2020-11-02 23:01:07 +01:00
Zoltán Vörös
bfb4ead10d update index.rst 2020-11-02 22:56:06 +01:00
Zoltán Vörös
8cbb1c164c moved requirements file to root 2020-11-02 22:44:47 +01:00
Zoltán Vörös
c21a54e336
added a reference to the diag function 2020-11-02 22:35:00 +01:00
Zoltán Vörös
e22dd924e6
Merge pull request #198 from v923z/diag-fix
Diag fix
2020-11-02 22:33:09 +01:00
Zoltán Vörös
dc4c9d692a implemented second half of diag 2020-11-02 22:30:15 +01:00
Zoltán Vörös
e15c13feaa fixed diagonal glitch 2020-11-02 22:14:58 +01:00
Zoltán Vörös
8c16c683a6
Merge branch 'master' into dtype 2020-11-02 21:54:35 +01:00
Zoltán Vörös
b7f95c4122 added an implementation of dtype 2020-11-02 21:52:12 +01:00
Zoltán Vörös
984ddf51b4 added dtype implementation 2020-11-02 18:06:29 +01:00
Zoltán Vörös
64a927ee80
Merge pull request #196 from v923z/numerical-fix
fixed small error in argmin_argmax
2020-11-01 20:55:55 +01:00
Zoltán Vörös
ee6e53d65b fixed small error in argmin_argmax 2020-11-01 20:53:32 +01:00
Zoltán Vörös
aa7e741530
Merge pull request #194 from v923z/diag
added diagonal and updated extract_pyi from circuitpython
2020-10-31 21:55:20 +01:00
Zoltán Vörös
475c0ae465 added diagonal and updated extract_pyi from circuitpython 2020-10-31 21:48:02 +01:00
Zoltán Vörös
4dcaaf166d added requirements file 2020-10-30 23:51:07 +01:00
Zoltán Vörös
13ce751255 bumped version number to 1.0.0 2020-10-30 23:15:53 +01:00
Zoltán Vörös
9df0ddf8f2 Merge branch 'tensor'
Updates the master branch with all changes implemented in tensor
2020-10-30 23:14:03 +01:00
Zoltán Vörös
5029565362 removed redundant doc files 2020-10-30 23:13:29 +01:00
Zoltán Vörös
4098558a35 docs can be generated from stubs now 2020-10-30 22:11:02 +01:00
Zoltán Vörös
cad8bcbed5 added new notebooks to version control 2020-10-25 22:43:16 +01:00
Zoltán Vörös
2b26ea39bd removed docs/manual/source/ulab.rst 2020-10-25 22:19:23 +01:00
Zoltán Vörös
1e69c5fc06 preparing docs for 1.0.0 release 2020-10-25 22:15:33 +01:00
Zoltán Vörös
83c20a2a7e added build script for CP 2020-10-25 10:32:33 +01:00
Zoltán Vörös
a5242bb7b3 fixed CP-compatibilty in workflow file 2020-10-25 10:28:50 +01:00
Zoltán Vörös
8242b84753
Merge pull request #186 from jepler/ci-circuitpython
Build circuitpython-ulab as part of CI
2020-10-25 09:58:18 +01:00
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
6daeb09160
Merge pull request #181 from ciscorn/tensor-pyi
Update type hints for new/modified functions
2020-10-23 22:23:53 +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
Taku Fukada
1d18ab0381 Update type hints for the new/modified functions 2020-10-23 06:00:38 +09:00
Zoltán Vörös
ddd553f76f fixed slicing error 2020-10-21 07:12:25 +02:00
Zoltán Vörös
0bfba3bf1b fixed binary operators for empty arrays 2020-10-17 23:35:24 +02:00
Zoltán Vörös
ca61961d22 fixed test scripts 2020-10-17 21:26:59 +02:00
Zoltán Vörös
e2fd36ed7a fixed error in eye 2020-10-17 18:23:31 +02:00
Zoltán Vörös
29f1bad2d8 fixed small error in flatten 2020-10-17 17:03:37 +02:00
Zoltán Vörös
16b973d54c fixed small slicing error 2020-10-17 14:57:57 +02:00
Zoltán Vörös
89801ab12c restored circuitpython compatibility 2020-10-17 13:47:26 +02:00
Zoltán Vörös
5ab7b1cd3d re-organised ndarray_properties.h 2020-10-17 10:28:17 +02:00
Zoltán Vörös
e947baddd7 updated readme 2020-10-17 09:14:22 +02:00
Zoltán Vörös
511bb5f6c9 added in-place operators for 1, 3, and 4 dimensions 2020-10-16 22:14:13 +02:00
Zoltán Vörös
6529a2ef16 fixed error introduced in the last commit 2020-10-16 08:01:51 +02:00
Zoltán Vörös
ca6e707f66 arrays can now be initialised from arrays 2020-10-16 07:54:43 +02:00
Zoltán Vörös
c29df37e7b added concatenate 2020-10-15 18:26:12 +02:00
Zoltán Vörös
6723fdb66e fixed #if/#endif clause 2020-10-14 21:07:54 +02:00
Zoltán Vörös
5389575c07 updated the arctan2 function 2020-10-14 21:03:29 +02:00
Zoltán Vörös
b724c99262 removed debug print 2020-10-14 20:28:39 +02:00
Zoltán Vörös
7af7042572 fixed in-place operators 2020-10-14 20:27:37 +02:00
Zoltán Vörös
c2fac359a5 updated around 2020-10-14 07:20:58 +02:00
Zoltán Vörös
d1c7af3cb5 implemented assignment into Boolean-indexed arrays 2020-10-13 18:15:45 +02:00
Zoltán Vörös
fadc54bd71 implemented Boolean indexing 2020-10-12 20:29:03 +02:00
Zoltán Vörös
a662414ba6 fixed error in numerical sum 2020-10-08 19:44:30 +02:00
Zoltán Vörös
94e4d9e672 fixed some tab <-> spaces oddities 2020-10-07 21:32:50 +02:00
Zoltán Vörös
2e08b2566d added in-place operators 2020-10-07 21:01:11 +02:00
Zoltán Vörös
ff8d260809 fixed the subscript method 2020-10-06 20:06:51 +02:00
Zoltán Vörös
8dec1328ab added copy method to ndarray 2020-10-06 07:12:19 +02:00
Zoltán Vörös
b2465a7c16 simplified ndarray code a bit 2020-10-01 19:39:35 +02:00
Zoltán Vörös
0f336ab65b fixed >=, <= error in binary_op 2020-10-01 07:17:28 +02:00
Zoltán Vörös
0ede57ff42 removed extra if clause from cross product 2020-09-30 19:50:34 +02:00
Zoltán Vörös
6a359e59c8 added cross product to numerical 2020-09-30 19:48:48 +02:00
Zoltán Vörös
3f25cae135 ndarrays can now be unsubscribed 2020-09-30 18:02:04 +02:00
Zoltán Vörös
daed7bf3ce found and fixed small errors in vectorise and poly 2020-09-30 07:20:57 +02:00
Zoltán Vörös
501663e3fe added function iterator to ndarray 2020-09-29 18:48:02 +02:00
Zoltán Vörös
dfd4a6729b added pre-processor switch for unary operators 2020-09-28 21:21:56 +02:00
Zoltán Vörös
7be869fe74 fixed issue with reverse operators in binary_op 2020-09-28 20:53:16 +02:00
Zoltán Vörös
9f917b23c2 added power to binary operators 2020-09-28 19:22:39 +02:00
Zoltán Vörös
2c8faf7680 split binary operators into separate functions 2020-09-26 19:15:43 +02:00
Zoltán Vörös
b7b54cba70 cosmetic changes 2020-09-25 21:12:01 +02:00
Zoltán Vörös
483e9c3dc5 added full 2020-09-24 19:40:58 +02:00
Zoltán Vörös
b7f11232ee added logscale 2020-09-24 19:11:11 +02:00
Zoltán Vörös
86bed99394 added tobytes method 2020-09-23 20:07:42 +02:00
Zoltán Vörös
32369e56fb implement numpy compatibility option 2020-09-22 19:08:39 +02:00
Zoltán Vörös
fd7090ce35 added math constants, degrees/radians and defined relevant constants 2020-09-21 21:37:19 +02:00
Zoltán Vörös
c631443971 fixed poly 2020-09-20 21:35:54 +02:00
Zoltán Vörös
58a205aec6 found and fixed error in argsort 2020-09-16 20:35:35 +02:00
Zoltán Vörös
fdd5920f7b first implementation of argsort (still has small glitch) 2020-09-14 21:50:35 +02:00
Zoltán Vörös
f681f36bc6 fixed sort 2020-08-29 13:19:11 +02:00
Zoltán Vörös
a43e3a45b5 backup commit 2020-08-27 19:24:34 +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
8f9fc87dff implemented roll 2020-08-21 18:27:33 +02:00
Zoltán Vörös
11cd3ac50a fixed indexing error in flip 2020-08-19 16:50:52 +02:00
Zoltán Vörös
f179a1efc1 re-implemented flip 2020-08-19 16:48:08 +02:00
Zoltán Vörös
798f38a76c updated the implementation of diff 2020-08-19 16:12:16 +02:00
Zoltán Vörös
b29bf6c0eb implemented argmin/argmax 2020-08-18 16:42:01 +02:00
Zoltán Vörös
05b2123fbf backup commit, no functional changes 2020-08-17 22:11:17 +02: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
ed4a66938b backup commit, no functional changes 2020-08-17 16:43:52 +02:00
Zoltán Vörös
00f4cdfe53 cleaned up mean/std functions 2020-08-17 00:15:19 +02:00
Zoltán Vörös
43deb6cc3a cleaned up numerical sum a bit 2020-08-15 22:25:18 +02:00
Zoltán Vörös
a0396df9b6 tabs to spaces in all files 2020-08-15 19:31:04 +02:00
Zoltán Vörös
6b33c912de tabs to spaces 2020-08-15 19:24:49 +02:00
Zoltán Vörös
a97d7a7b7e more linting 2020-08-15 19:02:55 +02:00
Zoltán Vörös
1b9c212899 fixed tabs 2020-08-15 18:57:10 +02:00
Zoltán Vörös
4029c6dd86 fixed tabs 2020-08-15 18:56:07 +02:00
Zoltán Vörös
b24886826f fixed tabs 2020-08-15 18:54:08 +02:00
Zoltán Vörös
378bd4ebab fixed tabs 2020-08-15 18:43:52 +02:00
Zoltán Vörös
b95a0816f0 fixed tabs 2020-08-15 18:43:35 +02:00
Zoltán Vörös
1b7dc263e9 fixed tabs 2020-08-15 18:42:55 +02:00
Zoltán Vörös
fe08d5be1b fixed tabs 2020-08-15 18:42:01 +02:00
Zoltán Vörös
71c73f095a fixed tabs 2020-08-15 18:39:03 +02:00
Zoltán Vörös
0caf03bd3e fixed tabs 2020-08-15 18:38:29 +02:00
Zoltán Vörös
eb696d9d91 factored out associative operators 2020-08-15 18:35:28 +02:00
Zoltán Vörös
382d7a613e numerical sum sort of works now 2020-08-15 00:39:12 +02:00
Zoltán Vörös
a81a77a996 found some flash savings 2020-08-13 22:24:40 +02:00
Zoltán Vörös
506986fcb4 found/fixed bug in iterator 2020-08-13 22:08:12 +02:00
Zoltán Vörös
fca36e6d64 fixed iterator 2020-08-13 22:02:16 +02:00
Zoltán Vörös
76c1a3d242 linalg can now be compiled 2020-08-13 20:16:10 +02:00
Zoltán Vörös
621dca8e3b fixed filter module 2020-08-13 15:46:23 +02:00
Zoltán Vörös
3428fb8123 moved user settings to ulab.h 2020-08-13 15:17:22 +02:00
Zoltán Vörös
da1d8c2ac1 shaved off 200 bytes for 1D arrays 2020-08-13 15:13:44 +02:00
Zoltán Vörös
80511c13e4 fixed couple of comments 2020-08-13 15:05:26 +02:00
Zoltán Vörös
1b08404975 fixed flatten for views 2020-08-13 14:52:33 +02:00
Zoltán Vörös
d903b0a7d2 fixed functions in compare module 2020-08-13 13:29:56 +02:00
Zoltán Vörös
5ec64d39ee fft works with views now 2020-08-13 12:45:35 +02:00
Zoltán Vörös
034a8d7b58 fixed unary operators (ndarray_copy_view) 2020-08-13 12:11:12 +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
3be74df891 fixed iteration loops in vectorise 2020-08-12 15:46:59 +02:00
Zoltán Vörös
7e6216a1fc assignment by slices works 2020-08-11 15:12:16 +02:00
Zoltán Vörös
b212bd4cb9 fixed iterator code 2020-08-11 09:28:44 +02:00
Zoltán Vörös
0268f990d7 added ndinfo function 2020-08-08 12:43:57 +02:00
Zoltán Vörös
b1b93994c2 with the exception of pow, binary operators are functional 2020-08-08 11:20:17 +02:00
Zoltán Vörös
b4527192f5 removed unnecessary pointer shifting in the binary loops 2020-08-07 23:23:32 +02:00
Zoltán Vörös
847c49064b tensor dimension is displayed in version number 2020-08-07 23:00:36 +02:00
Zoltán Vörös
20796a10b3 tensor dimension can now be configured 2020-08-07 22:34:24 +02:00
Zoltán Vörös
41c3008fdd removed unused structure from ndarray.h 2020-08-07 20:32:20 +02:00
Zoltán Vörös
63efdbf5a3 proof of concept binary function is working 2020-08-07 20:31:43 +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
d59fc169cd made code in printout tighter 2020-08-07 17:54:26 +02:00
Zoltán Vörös
44f0c46839 fixed printout 2020-08-07 17:52:41 +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
8c4d02f755 not entirely functional, safety backup 2020-08-05 22:35:21 +02: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
bb023e267c pulled in changes from master 2020-08-03 20:58:51 +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
c9b7881add pulled in most recent changes from master 2020-07-31 23:25:36 +02:00
Zoltán Vörös
23ad568b0b Update README.md
fixed links to compiled firmware
2020-07-31 21:45:02 +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
Zoltán Vörös
5cd5aaba35 approx is compiles 2020-07-30 22:07:53 +02:00
Zoltán Vörös
162b39470d fft is working 2020-07-30 21:38:31 +02:00
Zoltán Vörös
b4748dc966 fixed a couple of errors in ndarray.c 2020-07-30 21:16:49 +02:00
Zoltán Vörös
4ddbd8b73f base code can be compiled 2020-07-30 20:49:14 +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
70af1c8a77 fix test 2020-02-18 21:25:03 -06:00
a2962f0fe5 fix printing failure info when tests fail 2020-02-18 21:10:33 -06:00
cf057c4df9 roll back constness corrections 2020-02-18 21:00:30 -06:00
de6b7772e4 fix ci build 2020-02-18 20:50:01 -06:00
43551c9a7a Merge remote-tracking branch 'upstream/master' into 2dim 2020-02-18 20:46:43 -06:00
23adf6e4a7 Mark modules as 'extern const' 2020-02-18 20:32:33 -06:00
f6d123beb8 Don't export modules if CIRCUITPY
.. We are using the "shared bindings" method of circuitpython
2020-02-18 20:32:28 -06:00
27996c9003 Add MP_DELCARE_CONST_FUN declarations to headers 2020-02-18 20:32:22 -06:00
Zoltán Vörös
bec24fe4a4 Merge branch 'master' of github.com:v923z/micropython-ulab 2020-02-17 19:58:27 +01:00
Zoltán Vörös
e71f667114 temporary fix for issue #40 2020-02-17 19:57:55 +01:00
Zoltán Vörös
8300de7f11 backup commit, absolutely nothing essential 2020-02-16 19:53:30 +01:00
Zoltán Vörös
017c1c2c46 added extras.h/extras.c 2020-02-16 19:49:28 +01:00
Zoltán Vörös
6fe015f134 properties are now defined in ndarray_properties.h 2020-02-16 19:49:01 +01:00
Zoltán Vörös
3727c38182
Merge pull request #39 from jepler/github-actions
Perform continuous integration using github actions
2020-02-16 16:22:14 +01:00
a75903efe5 Add github actions
This will build micropython's master branch with ulab support, and
then run the tests in tests/

At this time, there's only one test and it's not very useful.
2020-02-15 19:57:50 -06:00
Zoltán Vörös
d8bfe46bd8 added __name__ to all submodules 2020-02-14 19:51:28 +01:00
Zoltán Vörös
2e3a0b4483 separated sub-modules into proper python sub-modules 2020-02-13 21:49:09 +01:00
Zoltán Vörös
49db707a9f
Merge pull request #37 from jepler/portability
Make portable to CircuitPython within same codebase
2020-02-12 18:27:16 +01:00
Zoltán Vörös
f2aaab84cc
Merge pull request #36 from jepler/convolve-optimize
filter_convolve: fix build error
2020-02-12 18:24:26 +01:00
Jeff Epler
fc80a25685 Increase CircuitPython compatibility
- Adapt to signature of mp_make_new_fun_t for mpy and cpy by ifdef
 - Use MP_OBJ_IS_TYPE instead of mp_obj_is_type
 - Ditto MP_OBJ_IS_INT
 - Use mp_const_none instead of MP_ROM_NONE
 - Ditto mp_const_true, mp_const_false
2020-02-12 10:15:47 -06:00
Jeff Epler
f47abf90ac filter_convolve: fix build error
My earlier change introduced a build error.  I'm not sure why
I didn't find this before making the pull request.
2020-02-12 09:50:17 -06:00
Zoltán Vörös
5bf6e89a0a
Merge pull request #35 from jepler/convolve-optimize
convolve: Optimize and special-case floats
2020-02-12 08:16:18 +01:00
Jeff Epler
7846b0c469 convolve: Optimize and special-case floats
Special casing floats decreases runtime to about 50% (applying a 117-tap
filter to 512 points of data goes from 70ms to 32ms)

The top_n/bot_n calculations already meant that the a/c indices were
never out of range.  This decreases runtime further to about 15% of
original (11ms)

Timings done on an Adafruit Clue (nrf52840 at 64MHz)

It does of course increase code size somewhat.
2020-02-11 17:57:54 -06:00
Zoltán Vörös
9153fd8f8a added a short section to the manual on how to customise ulab 2020-02-11 21:36:12 +01:00
Zoltán Vörös
57cf52838c
Merge pull request #33 from jepler/fix-undef-errors-mpy
Fix some define-guards
2020-02-11 20:19:27 +01:00
Zoltán Vörös
c14eee1bd4 trying to fix ulab.h definitions 2020-02-11 20:15:45 +01:00
Zoltán Vörös
2c71467ced implemented ndarray properties 2020-02-11 20:08:37 +01:00
02d74a4d3e Fix some define-guards
These problems were found building in circuitpython:
../../extmod/ulab/code/numerical.c:671:5: error: "ULAB_NUMERICAL_ARGSORT" is not defined, evaluates to 0 [-Werror=undef]
  671 | #if ULAB_NUMERICAL_ARGSORT
      |     ^~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
../../extmod/ulab/code/ulab.c:150:9: error: "ULAB_VECTORISE_" is not defined, evaluates to 0 [-Werror=undef]
  150 |     #if ULAB_VECTORISE_
      |         ^~~~~~~~~~~~~~~
../../extmod/ulab/code/ulab.c:159:9: error: "ULAB_VECTORISE_TAHN" is not defined, evaluates to 0 [-Werror=undef]
  159 |     #if ULAB_VECTORISE_TAHN
      |         ^~~~~~~~~~~~~~~~~~~
../../extmod/ulab/code/ulab.c:198:9: error: "ULAB_NUMERICAL_ARGSORT" is not defined, evaluates to 0 [-Werror=undef]
  198 |     #if ULAB_NUMERICAL_ARGSORT
      |         ^~~~~~~~~~~~~~~~~~~~~~
2020-02-11 11:08:25 -06:00
Zoltán Vörös
4a0677fd14 removed extra ndarray_get_buffer 2020-02-10 19:54:49 +01:00
Zoltán Vörös
800bb3b872 Merge branch 'master' of github.com:v923z/micropython-ulab 2020-02-10 19:51:22 +01:00
Zoltán Vörös
89170a13a6 fixed error in filter.c, removed asbytearray, and added buffer protocol to ndarray.c 2020-02-10 19:50:49 +01:00
Zoltán Vörös
ca23263655
Merge pull request #31 from jepler/memoryview
ndarray: let memoryview(arr) work
2020-02-10 18:38:11 +01:00
Zoltán Vörös
936bb3bae5 corrected slicing error in issue #32 2020-02-09 19:56:32 +01:00
a90d18caf1 ndarray: let memoryview(arr) work
.. this makes ndarray.rawbytes redundant.  Circuitpython will remove it.
2020-02-08 10:29:45 -06:00
Zoltán Vörös
918075daaf fixed a couple of small issues in filter.c 2020-02-08 10:45:13 +01:00
Zoltán Vörös
fac3eb4099 fixed a couple of small issues in filter.c 2020-02-08 10:44:24 +01:00
Zoltán Vörös
76ccd1a118 the master branch is configurable now 2020-02-07 21:23:24 +01:00
Zoltán Vörös
0315408e05
Merge pull request #29 from jepler/convolve
convolve: implement and document
2020-02-06 19:53:51 +01:00
Jeff Epler
722401c2e4 Clean up and polish based on review comments
no functional change
2020-02-06 12:43:39 -06:00
Jeff Epler
efa4c148c9 convolve: implement and document 2020-02-05 16:33:48 -06:00
Zoltán Vörös
dcd65feb21 fixed indexing error in linalg.dot 2020-01-29 19:27:28 +01:00
Zoltán Vörös
b2c4749962 replaced MP_ROM_PTR macro, wherever it made sense 2020-01-20 19:55:38 +01:00
Zoltán Vörös
941ffceb1b replaced MP_ROM_PTR macro, wherever it made sense 2020-01-20 19:49:57 +01:00
Zoltán Vörös
ac1111c251 fixed glitch in std/mean functions 2020-01-07 21:53:23 +01:00
Zoltán Vörös
992e48b84e switched to version string 2020-01-07 21:02:20 +01:00
Zoltán Vörös
68e38bc6b8 backup commit 2020-01-06 07:43:27 +01:00
Zoltán Vörös
81acfc7da8 ulab_ndarray_type is extern now 2019-12-31 10:57:10 +01:00
Zoltán Vörös
107587de00 found and fixed macro error in vectorise.h 2019-11-29 08:02:48 +01:00
Zoltán Vörös
22f85e0d3b fixed indexing error in linalg.dot 2019-11-28 19:16:59 +01:00
Zoltán Vörös
84d2ce8a52 notebook backup 2019-11-14 20:33:11 +01:00
Zoltán Vörös
1df50af368 replaced fabs by platform-agnostic version 2019-11-14 20:27:32 +01:00
Zoltán Vörös
1b9a80a442 replaced fabs by platform-agnostic version 2019-11-14 20:26:15 +01:00
Zoltán Vörös
97e98c4d31
Merge pull request #12 from nickovs/master
Fixed several issues preventing compilation on macOS
2019-11-12 08:38:26 +01:00
Nicko van Someren
e33720e9c0 Fixed several issues preventing compilation on macOS 2019-11-11 13:39:28 -08:00
Zoltán Vörös
ea588a67df found silly error in manual 2019-11-06 18:37:43 +01:00
Zoltán Vörös
4e507e9805 fixed version number in ulab.c 2019-11-06 18:34:31 +01:00
Zoltán Vörös
1f5d1a9765 fixed version number in ulab.c 2019-11-06 18:34:12 +01:00
Zoltán Vörös
0e9656cc1d added argsort, and fixed a number of smaller bugs 2019-11-06 17:35:01 +01:00
Zoltán Vörös
ec2b0f7084
Update README.md 2019-11-04 22:22:08 +01:00
Zoltán Vörös
d12785af6c honed the explanation on extending ulab 2019-11-04 22:20:45 +01:00
Zoltán Vörös
5f8a5d8dc4 clarified manual section on sort 2019-11-04 22:04:06 +01:00
Zoltán Vörös
b4f4331f26
Merge pull request #7 from mdaeron/master
Small edits to README.md
2019-11-04 21:39:05 +01:00
mdaeron
fd217dfdc9
Update README.md
Specified that the cross-compiler installation command is for Linux.
2019-11-04 20:37:52 +01:00
mdaeron
0f4cf714ac
Update README.md
Added `ulab` at the end of the `git clone https://github.com/v923z/micropython-ulab.git` to be consistent with the `make` command.
2019-11-04 20:35:46 +01:00
Zoltán Vörös
f2deb06db3
Update README.md 2019-11-04 20:21:07 +01:00
Zoltán Vörös
4a5d1e5ee0 updated REAMDE 2019-11-04 19:36:04 +01:00
Zoltán Vörös
08b8fd0ae2 fixed float_t->mp_float_t mistake in ndarray.c 2019-11-04 19:28:06 +01:00
Zoltán Vörös
5c540e0475 changed config.py 2019-11-04 19:22:05 +01:00
Zoltán Vörös
a940870cac merging from testing 2019-11-04 19:20:44 +01:00
Zoltán Vörös
351eafa06c first implementation of sort 2019-11-04 18:54:19 +01:00
Zoltán Vörös
841c4685ff sort was not supposed to be in master... 2019-11-01 15:04:45 +01:00
Zoltán Vörös
3d816ac2c0 sort was not supposed to be in master... 2019-11-01 15:02:47 +01:00
Zoltán Vörös
5022a8b1a2 sort was not supposed to be in master... 2019-11-01 15:01:48 +01:00
334 changed files with 58719 additions and 23181 deletions

25
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,25 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is. Give the `ulab` version
```python
import ulab
print(ulab.__version__)
```
**To Reproduce**
Describe the steps to reproduce the behavior.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context that might help to locate the root of the problem.

View file

@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE REQUEST]"
labels: enhancement
assignees: ''
---
**Describe the solution you'd like**
A clear and concise description of what you want to happen. If possible, link to the `numpy/scipy` function.
**Additional context**
Add any other context about the feature request here.

98
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,98 @@
name: Build CI
on:
push:
pull_request:
paths:
- 'code/**'
- 'tests/**'
- '.github/workflows/**'
- 'build*.sh'
- 'requirements*.txt'
release:
types: [published]
check_suite:
types: [rerequested]
jobs:
micropython:
continue-on-error: true
strategy:
matrix:
os:
- ubuntu-24.04
- macOS-latest
dims: [1, 2, 3, 4]
runs-on: ${{ matrix.os }}
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install requirements
run: |
if type -path apt-get; then
sudo apt update && sudo apt-get install gcc-multilib
fi
- name: Versions
run: |
gcc --version
python3 --version
- name: Checkout ulab
uses: actions/checkout@v4
- name: Checkout micropython repo
uses: actions/checkout@v4
with:
repository: micropython/micropython
path: micropython
- name: Run build.sh
run: ./build.sh ${{ matrix.dims }}
circuitpython:
continue-on-error: true
strategy:
matrix:
os:
- ubuntu-24.04
- macOS-latest
dims: [1, 2, 3, 4]
runs-on: ${{ matrix.os }}
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Versions
run: |
gcc --version
python3 --version
- name: Checkout ulab
uses: actions/checkout@v4
- name: Install requirements
run: |
if type -path apt-get; then
sudo apt update && sudo apt-get install gettext librsvg2-bin
else
brew install gettext librsvg
echo >>$GITHUB_PATH /usr/local/opt/gettext/bin
echo >>$GITHUB_PATH /usr/local/opt/librsvg/bin
fi
python3 -mpip install --upgrade -r requirements_cp_dev.txt
- name: Run build-cp.sh
run: ./build-cp.sh ${{ matrix.dims }}

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
/micropython
/circuitpython
/*.exp
/*.out
/docs/manual/build/
/docs/manual/source/**/*.pyi
/docs/.ipynb_checkpoints/
/docs/ulab-test.ipynb
/code/.atom-build.yml
build/micropython
build/ulab
.idea

24
.readthedocs.yaml Normal file
View file

@ -0,0 +1,24 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.9"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/manual/source/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats: all
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: requirements.txt

17
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,17 @@
Contributions of any kind are always welcome.
# Contributing to the code base
If you feel like adding to the code, you can simply issue a pull request. If you do so, please, try to adhere to `micropython`'s [coding conventions](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md#c-code-conventions).
# Documentation
However, you can also contribute to the documentation (preferably via the [jupyter notebooks](https://github.com/v923z/micropython-ulab/tree/master/docs).
## Testing
If you decide to lend a hand with testing, here are the steps:
1. Write a test script that checks a particular function, or a set of related functions!
1. Drop this script in one of the folders in [ulab tests](https://github.com/v923z/micropython-ulab/tree/master/tests)!
1. Run the [./build.sh](https://github.com/v923z/micropython-ulab/blob/master/build.sh) script in the root directory of `ulab`! This will clone the latest `micropython`, compile the firmware for `unix`, execute all scripts in the `ulab/tests`, and compare the results to those in the expected results files, which are also in `ulab/tests`, and have an extension `.exp`. In case you have a new snippet, i.e., you have no expected results file, or if the results differ from those in the expected file, a new expected file will be generated in the root directory. You should inspect the contents of this file, and if they are satisfactory, then the file can be moved to the `ulab/tests` folder, alongside your snippet.

452
README.md
View file

@ -1,17 +1,447 @@
# 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.
[![Documentation Status](https://readthedocs.org/projects/micropython-ulab/badge/?version=latest)](https://micropython-ulab.readthedocs.io/en/latest/index.html)
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.
`ulab` is a `numpy`-like array manipulation library for [micropython](http://micropython.org/) and [CircuitPython](https://circuitpython.org/).
The module is written in C, defines compact containers (`ndarray`s) for numerical data of one to four
dimensions, and is fast. The library is a software-only standard `micropython` user module,
i.e., it has no hardware dependencies, and can be compiled for any platform. 8-, and 16-bit signed
and unsigned integer `dtypes`, as well as `float`, and, optionally, ` complex` are supported.
The `float` implementation of `micropython` (32-bit `float`, or 64-bit `double`) is automatically
detected and handled.
Firmware for pyboard.v.1.1, as well as for PYBD_SF6 is updated once
in a while, and can be downloaded from https://github.com/v923z/micropython-ulab/releases,
otherwise, it can be compiled from the source by following the steps
https://micropython-usermod.readthedocs.io/en/latest/usermods_05.html#compiling-our-module.
1. [Supported functions and methods](#supported-functions-and-methods)
1. [ndarray methods](#ndarray-methods)
2. [numpy and scipy functions](#numpy-and-scipy-functions)
3. [ulab utilities](#ulab-utilities)
4. [user module](#user-module)
4. [Usage](#usage)
5. [Finding help](#finding-help)
6. [Benchmarks](#benchmarks)
7. [Firmware](#firmware)
1. [Customising the firmware](#customising-the-firmware)
1. [Platforms including ulab](#platforms-including-ulab)
1. [Compiling](#compiling)
1. [UNIX](#unix-port)
1. [STM-based boards](#stm-based-boards)
1. [ESP32-based boards](#esp32-based-boards)
1. [RP2-based boards](#rp2-based-boards)
1. [Compiling for CircuitPython](#compiling-for-circuitpython)
8. [Issues, contributing, and testing](#issues-contributing-and-testing)
1. [Testing](#testing)
# Supported functions and methods
## ndarray methods
`ulab` implements `numpy`'s `ndarray` with the `==`, `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `/`, `*`, `**`,
`%`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that
operate element-wise. Type-aware `ndarray`s can be initialised from any `micropython` iterable, lists of
iterables via the `array` constructor, or by means of the `arange`, `concatenate`, `diag`, `eye`,
`frombuffer`, `full`, `linspace`, `logspace`, `ones`, or `zeros` functions.
`ndarray`s can be sliced, and iterated on, and have a number of their own methods, and properties, such as `flatten()`, `itemsize`, `reshape()`,
`shape`, `size`, `strides`, `tobytes()`, `tolist()`, and `transpose()` and `T`. If the firmware is compiled with `complex` support,
the `imag`, and `real` properties are automatically included.
## `numpy` and `scipy` functions
In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`numpy.random`](https://micropython-ulab.readthedocs.io/en/latest/numpy-random.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest).
## `ulab` utilities
The [`utils`](https://micropython-ulab.readthedocs.io/en/latest/ulab-utils.html) module contains functions for
interfacing with peripheral devices supporting the buffer protocol. These functions do not have an obvious
`numpy` equivalent, but share a similar programming interface, and allow direct data input-output between
numerical arrays and hardware components.
## `user` module
User-defined functions operating on numerical data can easily be added via the `user` module. This allows for transparent extensions, without having to change anything in the core. Hints as to how to work with `ndarray`s at the C level can be found in the [programming manual](https://micropython-ulab.readthedocs.io/en/latest/ulab-programming.html).
# Usage
`ulab` sports a `numpy/scipy`-compatible interface, which makes porting of `CPython` code straightforward. The following
snippet should run equally well in `micropython`, or on a PC.
```python
try:
from ulab import numpy
from ulab import scipy
except ImportError:
import numpy
import scipy.special
x = numpy.array([1, 2, 3])
scipy.special.erf(x)
```
# 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 Jeff Epler's excellent
[circuitpython-ulab](https://learn.adafruit.com/ulab-crunch-numbers-fast-with-circuitpython/overview) overview.
The [tricks](https://micropython-ulab.readthedocs.io/en/latest/ulab-tricks.html) chapter of the user manual discusses
methods by which RAM and speed can be leveraged in particular numerical problems.
# Benchmarks
Representative numbers on performance can be found under [ulab samples](https://github.com/thiagofe/ulab_samples).
# Firmware
Pre-built, and up-to-date firmware files for select platforms can be downloaded
from [micropython-builder](https://github.com/v923z/micropython-builder).
## Customising the firmware
If flash space is a concern, unnecessary functions can be excluded from the compiled firmware with
pre-processor switches. In addition, `ulab` also has options for trading execution speed for firmware size.
A thorough discussion on how the firmware can be customised can be found in the
[corresponding section](https://micropython-ulab.readthedocs.io/en/latest/ulab-intro.html#customising-the-firmware)
of the user manual.
## Platforms including ulab
`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
1. `pimoroni-pico` https://github.com/pimoroni/pimoroni-pico
1. `Tulip Creative Computer` https://github.com/shorepine/tulipcc
## Compiling
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:
### UNIX port
Simply clone the `ulab` repository with
```bash
git clone https://github.com/v923z/micropython-ulab.git ulab
```
and then run
```bash
./build.sh [matrix.dims] # Dimensions is 2 by default
```
This command will clone `micropython`, and build the `unix` port automatically, as well as run the test scripts. If you want an interactive `unix` session, you can launch it in
```bash
ulab/micropython/ports/unix
```
### STM-based boards
First, you have to clone the `micropython` repository by running
```bash
git clone https://github.com/micropython/micropython.git
```
on the command line. This will create a new repository with the name `micropython`. Staying there, clone the `ulab` repository with
```bash
git clone https://github.com/v923z/micropython-ulab.git ulab
```
If you don't have the cross-compiler installed, your might want to do that now, for instance on Linux by executing
```bash
sudo apt-get install gcc-arm-none-eabi
```
If this step was successful, you can try to run the `make` command in the port's directory as
```bash
make BOARD=PYBV11 USER_C_MODULES=../../../ulab all
```
which will prepare the firmware for pyboard.v.11. Similarly,
```bash
make BOARD=PYBD_SF6 USER_C_MODULES=../../../ulab all
```
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
```bash
dfu-util --alt 0 -D firmware.dfu
```
or
```bash
python pydfu.py -u firmware.dfu
```
In case you got stuck somewhere in the process, a bit more detailed instructions can be found under https://github.com/micropython/micropython/wiki/Getting-Started, and https://github.com/micropython/micropython/wiki/Pyboard-Firmware-Update.
### ESP32-based boards
`ulab` can be tested on the ESP32 in [wokwi's micropython emulator](https://wokwi.com/arduino/projects/322114140704342610) without having to compile the C code. This utility also offers the possibility to save and share your `micropython` code.
Firmware for `Espressif` hardware can be built in two different ways, which are discussed in the next two paragraphs. A solution for issues with the firmware size is outlined in the [last paragraph](#what-to-do-if-the-firmware-is-too-large) of this section.
#### Compiling with cmake
Beginning with version 1.15, `micropython` switched to `cmake` on the ESP32 port. If your operating system supports `CMake > 3.12`, you can either simply download, and run the single [build script](https://github.com/v923z/micropython-ulab/blob/master/build/esp32-cmake.sh), or follow the step in this section. Otherwise, you should skip to the [next one](#compiling-with-make), where the old, `make`-based approach is discussed.
In case you encounter difficulties during the build process, you can consult the (general instructions for the ESP32)[https://github.com/micropython/micropython/tree/master/ports/esp32#micropython-port-to-the-esp32].
First, clone the `ulab`, the `micropython`, as well as the `espressif` repositories:
```bash
export BUILD_DIR=$(pwd)
git clone https://github.com/v923z/micropython-ulab.git ulab
git clone https://github.com/micropython/micropython.git
cd $BUILD_DIR/micropython/
git clone -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git
```
Also later releases of `esp-idf` are possible (e.g. `v4.2.1`).
Then install the `ESP-IDF` tools:
```bash
cd esp-idf
./install.sh
. ./export.sh
```
Next, build the `micropython` cross-compiler, and the `ESP` sub-modules:
```bash
cd $BUILD_DIR/micropython/mpy-cross
make
cd $BUILD_DIR/micropython/ports/esp32
make submodules
```
At this point, all requirements are installed and built. We can now compile the firmware with `ulab`. In `$BUILD_DIR/micropython/ports/esp32` create a `makefile` with the following content:
```bash
BOARD = GENERIC
USER_C_MODULES = $(BUILD_DIR)/ulab/code/micropython.cmake
include Makefile
```
You specify with the `BOARD` variable, what you want to compile for, a generic board, or `TINYPICO` (for `micropython` version >1.1.5, use `UM_TINYPICO`), etc. Still in `$BUILD_DIR/micropython/ports/esp32`, you can now run `make`.
#### Compiling with make
If your operating system does not support a recent enough version of `CMake`, you have to stay with `micropython` version 1.14. The firmware can be compiled either by downloading and running the [build script](https://github.com/v923z/micropython-ulab/blob/master/build/esp32.sh), or following the steps below:
First, clone `ulab` with
```bash
git clone https://github.com/v923z/micropython-ulab.git ulab
```
and then, in the same directory, `micropython`
```bash
git clone https://github.com/micropython/micropython.git
```
At this point, you should have `ulab`, and `micropython` side by side.
With version 1.14, `micropython` switched to `cmake` on the `ESP32` port, thus breaking compatibility with user modules. `ulab` can, however, still be compiled with version 1.14. You can check out a particular version by pinning the release tag as
```bash
cd ./micropython/
git checkout tags/v1.14
```
Next, update the submodules,
```bash
git submodule update --init
cd ./mpy-cross && make # build cross-compiler (required)
```
and find the ESP commit hash
```bash
cd ./micropython/ports/esp32
make ESPIDF= # will display supported ESP-IDF commit hashes
# output should look like: """
# ...
# Supported git hash (v3.3): 9e70825d1e1cbf7988cf36981774300066580ea7
# Supported git hash (v4.0) (experimental): 4c81978a3e2220674a432a588292a4c860eef27b
```
Choose an ESPIDF version from one of the options printed by the previous command:
```bash
ESPIDF_VER=9e70825d1e1cbf7988cf36981774300066580ea7
```
In the `micropython` directory, create a new directory with
```bash
mkdir esp32
```
Your `micropython` directory should now look like
```bash
ls
ACKNOWLEDGEMENTS CONTRIBUTING.md esp32 lib mpy-cross README.md
CODECONVENTIONS.md docs examples LICENSE ports tests
CODEOFCONDUCT.md drivers extmod logo py tools
```
In `./micropython/esp32`, download the software development kit with
```bash
git clone https://github.com/espressif/esp-idf.git esp-idf
cd ./esp-idf
git checkout $ESPIDF_VER
git submodule update --init --recursive # get idf submodules
pip install -r ./requirements.txt # install python reqs
```
Next, still staying in `./micropython/eps32/esd-idf/`, install the ESP32 compiler. If using an ESP-IDF version >= 4.x (chosen by `$ESPIDF_VER` above), this can be done by running `. $BUILD_DIR/esp-idf/install.sh`. Otherwise, for version 3.x, run the following commands in in `.micropython/esp32/esp-idf`:
```bash
# for 64 bit linux
curl https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar xvz
# for 32 bit
# curl https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar xvz
# don't worry about adding to path; we'll specify that later
# also, see https://docs.espressif.com/projects/esp-idf/en/v3.3.2/get-started for more info
```
Finally, build the firmware:
```bash
cd ./micropython/ports/esp32
# temporarily add esp32 compiler to path
export PATH=../../esp32/esp-idf/xtensa-esp32-elf/bin:$PATH
export ESPIDF=../../esp32/esp-idf # req'd by Makefile
export BOARD=GENERIC # options are dirs in ./boards
export USER_C_MODULES=../../../ulab # include ulab in firmware
make submodules & make all
```
If it compiles without error, you can plug in your ESP32 via USB and then flash it with:
```bash
make erase && make deploy
```
#### What to do, if the firmware is too large?
When selecting `BOARD=TINYPICO`, the firmware is built but fails to deploy, because it is too large for the standard partitions. We can rectify the problem by creating a new partition table. In order to do so, in `$BUILD_DIR/micropython/ports/esp32/`, copy the following 8 lines to a file named `partitions_ulab.cvs`:
```
# Notes: the offset of the partition table itself is set in
# $ESPIDF/components/partition_table/Kconfig.projbuild and the
# offset of the factory/ota_0 partition is set in makeimg.py
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x200000,
vfs, data, fat, 0x220000, 0x180000,
```
This expands the `factory` partition by 128 kB, and reduces the size of `vfs` by the same amount. Having defined the new partition table, we should extend `sdkconfig.board` by adding the following two lines:
```
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_ulab.csv"
```
This file can be found in `$BUILD_DIR/micropython/ports/esp32/boards/TINYPICO/`. Finally, run `make clean`, and `make`. The new firmware contains the modified partition table, and should fit on the microcontroller.
### RP2-based boards
RP2 firmware can be compiled either by downloading and running the single [build script](https://github.com/v923z/micropython-ulab/blob/master/build/rp2.sh)/[build script for Pico W](https://github.com/v923z/micropython-ulab/blob/master/build/rp2w.sh), or executing the commands below.
First, clone `micropython`:
```bash
git clone https://github.com/micropython/micropython.git
```
Then, setup the required submodules:
```bash
cd micropython
git submodule update --init lib/tinyusb
git submodule update --init lib/pico-sdk
cd lib/pico-sdk
git submodule update --init lib/tinyusb
```
You'll also need to compile `mpy-cross`:
```bash
cd ../../mpy-cross
make
```
That's all you need to do for the `micropython` repository. Now, let us clone `ulab` (in a directory outside the micropython repository):
```bash
cd ../../
git clone https://github.com/v923z/micropython-ulab ulab
```
With this setup, we can now build the firmware. Back in the `micropython` repository, use these commands:
```bash
cd ports/rp2
make USER_C_MODULES=/path/to/ulab/code/micropython.cmake
```
If `micropython` and `ulab` were in the same folder on the computer, you can set `USER_C_MODULES=../../../ulab/code/micropython.cmake`. The compiled firmware will be placed in `micropython/ports/rp2/build`.
# Compiling for CircuitPython
[Adafruit Industries](www.adafruit.com) always include a relatively recent version of `ulab` in their nightly builds. However, if you really need the bleeding edge, you can easily compile the firmware from the source. Simply clone `circuitpython`, and move the commit pointer to the latest version of `ulab` (`ulab` will automatically be cloned with `circuitpython`):
```bash
git clone https://github.com/adafruit/circuitpython.git
cd circuitpyton/extmod/ulab
# update ulab here
git checkout master
git pull
```
You might have to check, whether the `CIRCUITPY_ULAB` variable is set to `1` for the port that you want to compile for. You find this piece of information in the `make` fragment:
```bash
circuitpython/ports/port_of_your_choice/mpconfigport.mk
```
After this, you would run `make` with the single `BOARD` argument, e.g.:
```bash
make BOARD=mini_sam_m4
```
# Issues, contributing, and testing
If you find a problem with the code, please, raise an [issue](https://github.com/v923z/micropython-ulab/issues)! An issue should address a single problem, and should contain a minimal code snippet that demonstrates the difference from the expected behaviour. Reducing a problem to the bare minimum significantly increases the chances of a quick fix.
Feature requests (porting a particular function from `numpy` or `scipy`) should also be posted at [ulab issue](https://github.com/v923z/micropython-ulab/issues).
Contributions of any kind are always welcome. If you feel like adding to the code, you can simply issue a pull request. If you do so, please, try to adhere to `micropython`'s [coding conventions](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md#c-code-conventions).
However, you can also contribute to the documentation (preferably via the [jupyter notebooks](https://github.com/v923z/micropython-ulab/tree/master/docs), or improve the [tests](https://github.com/v923z/micropython-ulab/tree/master/tests).
## Testing
If you decide to lend a hand with testing, here are the steps:
1. Write a test script that checks a particular function, or a set of related functions!
1. Drop this script in one of the folders in [ulab tests](https://github.com/v923z/micropython-ulab/tree/master/tests)!
1. Run the [./build.sh](https://github.com/v923z/micropython-ulab/blob/master/build.sh) script in the root directory of `ulab`! This will clone the latest `micropython`, compile the firmware for `unix`, execute all scripts in the `ulab/tests`, and compare the results to those in the expected results files, which are also in `ulab/tests`, and have an extension `.exp`. In case you have a new snippet, i.e., you have no expected results file, or if the results differ from those in the expected file, a new expected file will be generated in the root directory. You should inspect the contents of this file, and if they are satisfactory, then the file can be moved to the `ulab/tests` folder, alongside your snippet.

52
build-cp.sh Executable file
View file

@ -0,0 +1,52 @@
#!/bin/sh
set -e
# POSIX compliant version
readlinkf_posix() {
[ "${1:-}" ] || return 1
max_symlinks=40
CDPATH='' # to avoid changing to an unexpected directory
target=$1
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
[ -d "${target:-/}" ] && target="$target/"
cd -P . 2>/dev/null || return 1
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
if [ ! "$target" = "${target%/*}" ]; then
case $target in
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
esac
target=${target##*/}
fi
if [ ! -L "$target" ]; then
target="${PWD%/}${target:+/}${target}"
printf '%s\n' "${target:-/}"
return 0
fi
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
# <file mode>, <number of links>, <owner name>, <group name>,
# <size>, <date and time>, <pathname of link>, <contents of link>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
link=$(ls -dl -- "$target" 2>/dev/null) || break
target=${link#*" $target -> "}
done
return 1
}
NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())')
HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )"
[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-all-submodules && git submodule update --init lib/uzlib tools)
rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab
dims=${1-2}
make -C circuitpython/mpy-cross -j$NPROC
make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims
# bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims"
# Docs don't depend on the dimensionality, so only do it once
if [ "$dims" -eq 2 ]; then
(cd circuitpython && sphinx-build -E -W -b html . _build/html)
(cd circuitpython && make check-stubs)
fi

70
build.sh Executable file
View file

@ -0,0 +1,70 @@
#!/bin/sh
GIT_HASH=`git describe --abbrev=8 --always`
# POSIX compliant version
readlinkf_posix() {
[ "${1:-}" ] || return 1
max_symlinks=40
CDPATH='' # to avoid changing to an unexpected directory
target=$1
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
[ -d "${target:-/}" ] && target="$target/"
cd -P . 2>/dev/null || return 1
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
if [ ! "$target" = "${target%/*}" ]; then
case $target in
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
esac
target=${target##*/}
fi
if [ ! -L "$target" ]; then
target="${PWD%/}${target:+/}${target}"
printf '%s\n' "${target:-/}"
return 0
fi
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
# <file mode>, <number of links>, <owner name>, <group name>,
# <size>, <date and time>, <pathname of link>, <contents of link>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
link=$(ls -dl -- "$target" 2>/dev/null) || break
target=${link#*" $target -> "}
done
return 1
}
NPROC=`python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())'`
PLATFORM=`python3 -c 'import sys; print(sys.platform)'`
set -e
HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )"
dims=${1-2}
if [ ! -d "micropython" ] ; then
git clone https://github.com/micropython/micropython
else
git -C micropython pull
fi
make -C micropython/mpy-cross -j${NPROC}
make -C micropython/ports/unix submodules
make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-$dims PROG=micropython-$dims
PROG="micropython/ports/unix/build-$dims/micropython-$dims"
if [ ! -e "$PROG" ]; then
# Older MicroPython revision, executable is still in ports/unix.
PROG="micropython/ports/unix/micropython-$dims"
fi
bash test-common.sh "${dims}" "$PROG"
# Build with single-precision float.
make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT CFLAGS_EXTRA+=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims
# The unix nanbox variant builds as a 32-bit executable and requires gcc-multilib.
# macOS doesn't support i386 builds so only build on linux.
if [ $PLATFORM = linux ]; then
make -C micropython/ports/unix -j${NPROC} VARIANT=nanbox USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims
fi

35
build/esp32-cmake.sh Normal file
View file

@ -0,0 +1,35 @@
#!/bin/bash
export BUILD_DIR=$(pwd)
echo "--- CLONING ULAB ---"
git clone --depth 1 https://github.com/v923z/micropython-ulab.git ulab
echo "--- CLONING MICROPYTHON ---"
git clone --depth 1 https://github.com/micropython/micropython.git
echo "--- CLONING ESP-IDF ---"
cd $BUILD_DIR/micropython/
git clone --depth 1 -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git
echo "--- INSTALL ESP-IDF ---"
cd $BUILD_DIR/micropython/esp-idf
./install.sh
. ./export.sh
echo "--- MPY-CROSS ---"
cd $BUILD_DIR/micropython/mpy-cross
make
echo "--- ESP32 SUBMODULES ---"
cd $BUILD_DIR/micropython/ports/esp32
make submodules
echo "--- PATCH MAKEFILE ---"
cp $BUILD_DIR/micropython/ports/esp32/Makefile $BUILD_DIR/micropython/ports/esp32/MakefileOld
echo "BOARD = GENERIC" > $BUILD_DIR/micropython/ports/esp32/Makefile
echo "USER_C_MODULES = \$(BUILD_DIR)/ulab/code/micropython.cmake" >> $BUILD_DIR/micropython/ports/esp32/Makefile
cat $BUILD_DIR/micropython/ports/esp32/MakefileOld >> $BUILD_DIR/micropython/ports/esp32/Makefile
echo "--- MAKE ---"
make

41
build/esp32.sh Normal file
View file

@ -0,0 +1,41 @@
#!/bin/bash
export BUILD_DIR=$(pwd)
git clone https://github.com/v923z/micropython-ulab.git ulab
git clone https://github.com/micropython/micropython.git
cd $BUILD_DIR/micropython/
git checkout tags/v1.14
git submodule update --init
cd ./mpy-cross && make # build cross-compiler (required)
cd $BUILD_DIR/micropython/ports/esp32
make ESPIDF= # will display supported ESP-IDF commit hashes
# output should look like: """
# ...
# Supported git hash (v3.3): 9e70825d1e1cbf7988cf36981774300066580ea7
# Supported git hash (v4.0) (experimental): 4c81978a3e2220674a432a588292a4c860eef27b
ESPIDF_VER=9e70825d1e1cbf7988cf36981774300066580ea7
mkdir $BUILD_DIR/micropython/esp32
cd $BUILD_DIR/micropython/esp32
git clone https://github.com/espressif/esp-idf.git esp-idf
cd $BUILD_DIR/micropython/esp32/esp-idf
git checkout $ESPIDF_VER
git submodule update --init --recursive # get idf submodules
pip install -r ./requirements.txt # install python reqs
curl https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar xvz
cd $BUILD_DIR/micropython/ports/esp32
# temporarily add esp32 compiler to path
export PATH=$BUILD_DIR/micropython/esp32/esp-idf/xtensa-esp32-elf/bine:$PATH
export ESPIDF=$BUILD_DIR/micropython/esp32/esp-idf
export BOARD=GENERIC # board options are in ./board
export USER_C_MODULES=$BUILD_DIR/ulab # include ulab in firmware
make submodules & make all

24
build/rp2.sh Normal file
View file

@ -0,0 +1,24 @@
#!/bin/bash
export BUILD_DIR=$(pwd)
export MPY_DIR=$BUILD_DIR/micropython
export ULAB_DIR=$BUILD_DIR/../code
if [ ! -d $ULAB_DIR ]; then
printf "Cloning ulab\n"
ULAB_DIR=$BUILD_DIR/ulab/code
git clone https://github.com/v923z/micropython-ulab.git ulab
fi
if [ ! -d $MPY_DIR ]; then
printf "Cloning MicroPython\n"
git clone https://github.com/micropython/micropython.git micropython
fi
cd $MPY_DIR
git submodule update --init
cd ./mpy-cross && make # build cross-compiler (required)
cd $MPY_DIR/ports/rp2
rm -r build
make USER_C_MODULES=$ULAB_DIR/micropython.cmake

25
build/rp2w.sh Normal file
View file

@ -0,0 +1,25 @@
#!/bin/bash
export BOARD=RPI_PICO_W
export BUILD_DIR=$(pwd)
export MPY_DIR=$BUILD_DIR/micropython
export ULAB_DIR=$BUILD_DIR/../code
if [ ! -d $ULAB_DIR ]; then
printf "Cloning ulab\n"
ULAB_DIR=$BUILD_DIR/ulab/code
git clone https://github.com/v923z/micropython-ulab.git ulab
fi
if [ ! -d $MPY_DIR ]; then
printf "Cloning MicroPython\n"
git clone https://github.com/micropython/micropython.git micropython
fi
cd $MPY_DIR
git submodule update --init
cd ./mpy-cross && make # build cross-compiler (required)
cd $MPY_DIR/ports/rp2
make BOARD=$BOARD clean
make USER_C_MODULES=$ULAB_DIR/micropython.cmake BOARD=$BOARD

View file

@ -1,169 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "py/runtime.h"
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "ndarray.h"
#include "fft.h"
enum FFT_TYPE {
FFT_FFT,
FFT_IFFT,
FFT_SPECTRUM,
};
void fft_kernel(mp_float_t *real, mp_float_t *imag, int 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;
mp_float_t tempr, tempi;
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
j = 0;
for(int i = 0; i < n; i++) {
if (j > i) {
SWAP(float, real[i], real[j]);
SWAP(float, imag[i], imag[j]);
}
m = n >> 1;
while (j >= m && m > 0) {
j -= m;
m >>= 1;
}
j += m;
}
mmax = 1;
while (n > mmax) {
istep = mmax << 1;
theta = -1.0*isign*6.28318530717959/istep;
wtemp = MICROPY_FLOAT_C_FUN(sin)(0.5 * theta);
wpr = -2.0 * wtemp * wtemp;
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
wr = 1.0;
wi = 0.0;
for(m = 0; m < mmax; m++) {
for(int i = m; i < n; i += istep) {
j = i + mmax;
tempr = wr * real[j] - wi * imag[j];
tempi = wr * imag[j] + wi * real[j];
real[j] = real[i] - tempr;
imag[j] = imag[i] - tempi;
real[i] += tempr;
imag[i] += tempi;
}
wtemp = wr;
wr = wr*wpr - wi*wpi + wr;
wi = wi*wpr + wtemp*wpi + wi;
}
mmax = istep;
}
}
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("FFT is defined for ndarrays only");
}
if(n_args == 2) {
if(!MP_OBJ_IS_TYPE(arg_im, &ulab_ndarray_type)) {
mp_raise_NotImplementedError("FFT is defined for ndarrays only");
}
}
// 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;
if((len & (len-1)) != 0) {
mp_raise_ValueError("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) {
// 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);
} else {
for(size_t i=0; i < len; i++) {
data_re[i] = ndarray_get_float_value(re->array->items, re->array->typecode, i);
}
}
ndarray_obj_t *out_im = create_new_ndarray(1, len, NDARRAY_FLOAT);
mp_float_t *data_im = (mp_float_t *)out_im->array->items;
if(n_args == 2) {
ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im);
if (re->array->len != im->array->len) {
mp_raise_ValueError("real and imaginary parts must be of equal length");
}
if(im->array->typecode == NDARRAY_FLOAT) {
memcpy((mp_float_t *)out_im->array->items, (mp_float_t *)im->array->items, im->bytes);
} else {
for(size_t i=0; i < len; i++) {
data_im[i] = ndarray_get_float_value(im->array->items, im->array->typecode, i);
}
}
}
if((type == FFT_FFT) || (type == FFT_SPECTRUM)) {
fft_kernel(data_re, data_im, len, 1);
if(type == FFT_SPECTRUM) {
for(size_t i=0; i < len; i++) {
data_re[i] = MICROPY_FLOAT_C_FUN(sqrt)(data_re[i]*data_re[i] + data_im[i]*data_im[i]);
}
}
} else { // inverse transform
fft_kernel(data_re, data_im, len, -1);
// TODO: numpy accepts the norm keyword argument
for(size_t i=0; i < len; i++) {
data_re[i] /= len;
data_im[i] /= len;
}
}
if(type == FFT_SPECTRUM) {
return MP_OBJ_TO_PTR(out_re);
} else {
mp_obj_t tuple[2];
tuple[0] = out_re;
tuple[1] = out_im;
return mp_obj_new_tuple(2, tuple);
}
}
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);
}
}
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 {
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_IFFT);
}
}
mp_obj_t fft_spectrum(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);
} else {
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_SPECTRUM);
}
}

View file

@ -1,23 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#ifndef _FFT_
#define _FFT_
#ifndef MP_PI
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
#endif
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
mp_obj_t fft_fft(size_t , const mp_obj_t *);
mp_obj_t fft_ifft(size_t , const mp_obj_t *);
mp_obj_t fft_spectrum(size_t , const mp_obj_t *);
#endif

View file

@ -1,457 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 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"
mp_obj_t linalg_transpose(mp_obj_t self_in) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
// the size of a single item in the array
uint8_t _sizeof = mp_binary_get_size('@', self->array->typecode, NULL);
// NOTE:
// if the matrices are square, we can simply swap items, but
// generic matrices can't be transposed in place, so we have to
// declare a temporary variable
// NOTE:
// In the old matrix, the coordinate (m, n) is m*self->n + n
// We have to assign this to the coordinate (n, m) in the new
// matrix, i.e., to n*self->m + m (since the new matrix has self->m columns)
// one-dimensional arrays can be transposed by simply swapping the dimensions
if((self->m != 1) && (self->n != 1)) {
uint8_t *c = (uint8_t *)self->array->items;
// self->bytes is the size of the bytearray, irrespective of the typecode
uint8_t *tmp = m_new(uint8_t, self->bytes);
for(size_t m=0; m < self->m; m++) {
for(size_t n=0; n < self->n; n++) {
memcpy(tmp+_sizeof*(n*self->m + m), c+_sizeof*(m*self->n + n), _sizeof);
}
}
memcpy(self->array->items, tmp, self->bytes);
m_del(uint8_t, tmp, self->bytes);
}
SWAP(size_t, self->m, self->n);
return mp_const_none;
}
mp_obj_t linalg_reshape(mp_obj_t self_in, mp_obj_t shape) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
if(!MP_OBJ_IS_TYPE(shape, &mp_type_tuple) || (MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(shape)) != 2)) {
mp_raise_ValueError("shape must be a 2-tuple");
}
mp_obj_iter_buf_t iter_buf;
mp_obj_t item, iterable = mp_getiter(shape, &iter_buf);
uint16_t m, n;
item = mp_iternext(iterable);
m = mp_obj_get_int(item);
item = mp_iternext(iterable);
n = mp_obj_get_int(item);
if(m*n != self->m*self->n) {
// TODO: the proper error message would be "cannot reshape array of size %d into shape (%d, %d)"
mp_raise_ValueError("cannot reshape array (incompatible input/output shape)");
}
self->m = m;
self->n = n;
return MP_OBJ_FROM_PTR(self);
}
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_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
};
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("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("tuple index out of range");
} else {
return mp_obj_new_int(ndarray->n);
}
} else {
mp_raise_ValueError("tuple index out of range");
}
} else {
mp_raise_TypeError("wrong argument type");
}
}
}
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(abs(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("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("only ndarray objects can be inverted");
}
if(o->m != o->n) {
mp_raise_ValueError("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("input matrix is singular");
}
return MP_OBJ_FROM_PTR(inverted);
}
mp_obj_t linalg_dot(mp_obj_t _m1, mp_obj_t _m2) {
// TODO: should the results be upcast?
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("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->n; i++) {
for(size_t j=0; j < m2->m; j++) {
sum = 0.0;
for(size_t k=0; k < m1->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*m1->m+j] = sum;
}
}
return MP_OBJ_FROM_PTR(out);
}
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("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("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_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_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_ROM_PTR(&mp_const_none_obj) } },
{ 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_obj_t linalg_det(mp_obj_t oin) {
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
mp_raise_TypeError("function defined for ndarrays only");
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
if(in->m != in->n) {
mp_raise_ValueError("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(abs(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_obj_t linalg_eig(mp_obj_t oin) {
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
mp_raise_TypeError("function defined for ndarrays only");
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
if(in->m != in->n) {
mp_raise_ValueError("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 < abs(array[m*in->n + n] - array[n*in->n + m])) {
mp_raise_ValueError("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 = 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("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);
}

View file

@ -1,39 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#ifndef _LINALG_
#define _LINALG_
#include "ndarray.h"
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
#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
mp_obj_t linalg_transpose(mp_obj_t );
mp_obj_t linalg_reshape(mp_obj_t , mp_obj_t );
mp_obj_t linalg_size(size_t , const mp_obj_t *, mp_map_t *);
bool linalg_invert_matrix(mp_float_t *, size_t );
mp_obj_t linalg_inv(mp_obj_t );
mp_obj_t linalg_dot(mp_obj_t , mp_obj_t );
mp_obj_t linalg_zeros(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t linalg_ones(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t linalg_eye(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t linalg_det(mp_obj_t );
mp_obj_t linalg_eig(mp_obj_t );
#endif

18
code/micropython.cmake Normal file
View file

@ -0,0 +1,18 @@
add_library(usermod_ulab INTERFACE)
file(GLOB_RECURSE ULAB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.c)
target_sources(usermod_ulab INTERFACE
${ULAB_SOURCES}
)
target_include_directories(usermod_ulab INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_definitions(usermod_ulab INTERFACE
MODULE_ULAB_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_ulab)

View file

@ -2,14 +2,41 @@
USERMODULES_DIR := $(USERMOD_DIR)
# Add all C files to SRC_USERMOD.
SRC_USERMOD += $(USERMODULES_DIR)/scipy/integrate/integrate.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/linalg/linalg.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/optimize/optimize.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/signal/signal.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/special/special.c
SRC_USERMOD += $(USERMODULES_DIR)/ndarray_operators.c
SRC_USERMOD += $(USERMODULES_DIR)/ulab_tools.c
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)/numpy/ndarray/ndarray_iter.c
SRC_USERMOD += $(USERMODULES_DIR)/ndarray_properties.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/approx.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/bitwise.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/compare.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/create.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft_tools.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/filter.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/io/io.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg_tools.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numerical.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/poly.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/random/random.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/stats.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numpy.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/scipy.c
SRC_USERMOD += $(USERMODULES_DIR)/user/user.c
SRC_USERMOD += $(USERMODULES_DIR)/utils/utils.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)
override CFLAGS_EXTRA += -DMODULE_ULAB_ENABLED=1

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
/*
* This file is part of the micropython-ulab project,
*
@ -5,7 +6,8 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
* Copyright (c) 2019-2021 Zoltán Vörös
* 2020 Jeff Epler for Adafruit Industries
*/
#ifndef _NDARRAY_
@ -16,7 +18,14 @@
#include "py/objstr.h"
#include "py/objlist.h"
#define PRINT_MAX 10
#include "ulab.h"
#ifndef MP_PI
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
#endif
#ifndef MP_E
#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536)
#endif
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#define FLOAT_TYPECODE 'f'
@ -24,101 +33,769 @@
#define FLOAT_TYPECODE 'd'
#endif
const mp_obj_type_t ulab_ndarray_type;
#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B
// For object representations A and B a Python float object is allocated as a
// concrete object in a struct, with the first entry pointing to &mp_type_float.
// Constant float objects are a struct in ROM and are referenced via their pointer.
// Use ULAB_DEFINE_FLOAT_CONST to define a constant float object.
// id is the name of the constant, num is its floating point value.
// hex32 is computed as: hex(int.from_bytes(array.array('f', [num]), 'little'))
// hex64 is computed as: hex(int.from_bytes(array.array('d', [num]), 'little'))
// Use ULAB_REFERENCE_FLOAT_CONST to reference a constant float object in code.
#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \
const mp_obj_float_t id##_obj = {{&mp_type_float}, (num)}
#define ULAB_REFERENCE_FLOAT_CONST(id) MP_ROM_PTR(&id##_obj)
// 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;
#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
// For object representation C a Python float object is stored directly in the
// mp_obj_t value.
// See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST.
#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \
enum { \
id = (((((uint32_t)hex32) & ~3) | 2) + 0x80800000) \
}
#define ULAB_REFERENCE_FLOAT_CONST(id) ((mp_obj_t)(id))
#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
// For object representation D (nan-boxing) a Python float object is stored
// directly in the mp_obj_t value.
// See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST.
#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \
const uint64_t id = (((uint64_t)hex64) + 0x8004000000000000ULL)
#define ULAB_REFERENCE_FLOAT_CONST(id) {id}
#endif
#if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 11
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;
#define MP_ERROR_TEXT(x) x
#endif
#if !defined(MP_OBJ_TYPE_GET_SLOT)
#if defined(MP_TYPE_FLAG_EXTENDED)
// Provide MP_OBJ_TYPE_{HAS,GET}_SLOT for CircuitPython.
#define MP_OBJ_TYPE_HAS_SLOT(t, f) (mp_type_get_##f##_slot(t) != NULL)
#define MP_OBJ_TYPE_GET_SLOT(t, f) mp_type_get_##f##_slot(t)
#else
// Provide MP_OBJ_TYPE_{HAS,GET}_SLOT for older revisions of MicroPython.
#define MP_OBJ_TYPE_HAS_SLOT(t, f) ((t)->f != NULL)
#define MP_OBJ_TYPE_GET_SLOT(t, f) (t)->f
// Also allow CiruitPython-style mp_obj_type_t definitions.
#define MP_TYPE_FLAG_EXTENDED (0)
#define MP_TYPE_EXTENDED_FIELDS(...) __VA_ARGS__
#endif
#endif
#define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d)
void ndarray_set_complex_value(void *, size_t , mp_obj_t );
#define NDARRAY_NUMERIC 0
#define NDARRAY_BOOLEAN 1
#define NDARRAY_NDARRAY_TYPE 1
#define NDARRAY_ITERABLE_TYPE 2
extern const mp_obj_type_t ulab_ndarray_type;
enum NDARRAY_TYPE {
NDARRAY_BOOL = '?', // this must never be assigned to the dtype!
NDARRAY_UINT8 = 'B',
NDARRAY_INT8 = 'b',
NDARRAY_UINT16 = 'H',
NDARRAY_INT16 = 'h',
#if ULAB_SUPPORTS_COMPLEX
NDARRAY_COMPLEX = 'c',
#endif
NDARRAY_FLOAT = FLOAT_TYPECODE,
};
typedef struct _ndarray_obj_t {
mp_obj_base_t base;
size_t m, n;
uint8_t dtype;
uint8_t itemsize;
uint8_t boolean;
uint8_t ndim;
size_t len;
mp_obj_array_t *array;
size_t bytes;
size_t shape[ULAB_MAX_DIMS];
int32_t strides[ULAB_MAX_DIMS];
void *array;
void *origin;
} ndarray_obj_t;
mp_obj_t mp_obj_new_ndarray_iterator(mp_obj_t , size_t , mp_obj_iter_buf_t *);
#if ULAB_HAS_DTYPE_OBJECT
extern const mp_obj_type_t ulab_dtype_type;
mp_float_t ndarray_get_float_value(void *, uint8_t , size_t );
typedef struct _dtype_obj_t {
mp_obj_base_t base;
uint8_t dtype;
} dtype_obj_t;
void ndarray_dtype_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
#endif /* ULAB_HAS_DTYPE_OBJECT */
extern const mp_obj_type_t ndarray_flatiter_type;
mp_obj_t ndarray_new_ndarray_iterator(mp_obj_t , mp_obj_iter_buf_t *);
mp_obj_t ndarray_get_item(ndarray_obj_t *, void *);
mp_float_t ndarray_get_float_value(void *, uint8_t );
mp_float_t ndarray_get_float_index(void *, uint8_t , size_t );
bool ndarray_object_is_array_like(mp_obj_t );
void fill_array_iterable(mp_float_t *, mp_obj_t );
size_t *ndarray_shape_vector(size_t , size_t , size_t , size_t );
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 *);
ndarray_obj_t *create_new_ndarray(size_t , size_t , uint8_t );
mp_obj_t ndarray_copy(mp_obj_t );
#if ULAB_HAS_PRINTOPTIONS
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);
#endif
void ndarray_assign_elements(ndarray_obj_t *, mp_obj_t , uint8_t , size_t *);
size_t *ndarray_contract_shape(ndarray_obj_t *, uint8_t );
int32_t *ndarray_contract_strides(ndarray_obj_t *, uint8_t );
ndarray_obj_t *ndarray_from_iterable(mp_obj_t , uint8_t );
ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t , size_t *, uint8_t );
ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *, uint8_t );
ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t , uint8_t *);
ndarray_obj_t *ndarray_new_linear_array(size_t , uint8_t );
ndarray_obj_t *ndarray_new_view(ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t );
bool ndarray_is_dense(ndarray_obj_t *);
ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *);
ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *, uint8_t );
void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *, uint8_t );
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj);
mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
mp_obj_t ndarray_subscr(mp_obj_t , mp_obj_t , mp_obj_t );
mp_obj_t ndarray_getiter(mp_obj_t , mp_obj_iter_buf_t *);
bool ndarray_can_broadcast(ndarray_obj_t *, ndarray_obj_t *, uint8_t *, size_t *, int32_t *, int32_t *);
bool ndarray_can_broadcast_inplace(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
mp_obj_t ndarray_binary_op(mp_binary_op_t , mp_obj_t , mp_obj_t );
mp_obj_t ndarray_unary_op(mp_unary_op_t , mp_obj_t );
mp_obj_t ndarray_shape(mp_obj_t );
mp_obj_t ndarray_rawsize(mp_obj_t );
size_t *ndarray_new_coords(uint8_t );
void ndarray_rewind_array(uint8_t , uint8_t *, size_t *, int32_t *, size_t *);
// various ndarray methods
#if NDARRAY_HAS_BYTESWAP
mp_obj_t ndarray_byteswap(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_byteswap_obj);
#endif
#if NDARRAY_HAS_COPY
mp_obj_t ndarray_copy(mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_copy_obj);
#endif
#if NDARRAY_HAS_FLATTEN
mp_obj_t ndarray_flatten(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t ndarray_asbytearray(mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_flatten_obj);
#endif
#define CREATE_SINGLE_ITEM(outarray, type, typecode, value) do {\
ndarray_obj_t *tmp = create_new_ndarray(1, 1, (typecode));\
type *tmparr = (type *)tmp->array->items;\
tmparr[0] = (type)(value);\
(outarray) = MP_OBJ_FROM_PTR(tmp);\
} while(0)
#if NDARRAY_HAS_DTYPE
mp_obj_t ndarray_dtype(mp_obj_t );
#endif
/*
mp_obj_t row = mp_obj_new_list(n, NULL);
mp_obj_list_t *row_ptr = MP_OBJ_TO_PTR(row);
#if NDARRAY_HAS_ITEMSIZE
mp_obj_t ndarray_itemsize(mp_obj_t );
#endif
should work outside the loop, but it doesn't. Go figure!
*/
#if NDARRAY_HAS_NDIM
mp_obj_t ndarray_ndim(mp_obj_t );
#endif
#define RUN_BINARY_LOOP(typecode, type_out, type_left, type_right, ol, or, op) 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);\
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];}\
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);\
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];\
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)) {\
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) {\
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 NDARRAY_HAS_SIZE
mp_obj_t ndarray_size(mp_obj_t );
#endif
#if NDARRAY_HAS_SHAPE
mp_obj_t ndarray_shape(mp_obj_t );
#endif
#if NDARRAY_HAS_STRIDES
mp_obj_t ndarray_strides(mp_obj_t );
#endif
#if NDARRAY_HAS_RESHAPE
mp_obj_t ndarray_reshape_core(mp_obj_t , mp_obj_t , bool );
mp_obj_t ndarray_reshape(mp_obj_t , mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_2(ndarray_reshape_obj);
#endif
#if NDARRAY_HAS_TOBYTES
mp_obj_t ndarray_tobytes(mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tobytes_obj);
#endif
#if NDARRAY_HAS_TOLIST
mp_obj_t ndarray_tolist(mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tolist_obj);
#endif
#if NDARRAY_HAS_TRANSPOSE
mp_obj_t ndarray_transpose(mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_transpose_obj);
#endif
#if ULAB_NUMPY_HAS_NDINFO
mp_obj_t ndarray_info(mp_obj_t );
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_info_obj);
#endif
mp_int_t ndarray_get_buffer(mp_obj_t , mp_buffer_info_t *, mp_uint_t );
//void ndarray_attributes(mp_obj_t , qstr , mp_obj_t *);
ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t );
#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, lstrides, iarray, istride, varray, vstride)\
type_left *array = (type_left *)(ndarray)->array;\
for(size_t i=0; i < (ndarray)->len; i++) {\
if(*(iarray)) {\
*array = (type_left)(*((type_right *)(varray)));\
(varray) += (vstride);\
}\
}\
if(m == 1) return row;\
mp_obj_list_append(out_list, row);\
}\
return out_list;\
}\
} while(0)
array += (lstrides);\
(iarray) += (istride);\
} while(0)
#if ULAB_HAS_FUNCTION_ITERATOR
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
type_out *array = (type_out *)(results)->array;\
size_t *lcoords = ndarray_new_coords((results)->ndim);\
size_t *rcoords = ndarray_new_coords((results)->ndim);\
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
size_t l = 0;\
do {\
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
} while(0)
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
size_t *lcoords = ndarray_new_coords((results)->ndim);\
size_t *rcoords = ndarray_new_coords((results)->ndim);\
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
size_t l = 0;\
do {\
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (results)->strides, lcoords);\
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
} while(0)
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t *lcoords = ndarray_new_coords((results)->ndim);\
size_t *rcoords = ndarray_new_coords((results)->ndim);\
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
size_t l = 0;\
do {\
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
} while(0)
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
type_out *array = (type_out *)(results)->array;\
size_t *lcoords = ndarray_new_coords((results)->ndim);\
size_t *rcoords = ndarray_new_coords((results)->ndim);\
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
size_t l = 0;\
do {\
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
} while(0)
#else
#if ULAB_MAX_DIMS == 1
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
type_out *array = (type_out *)results->array;\
size_t l = 0;\
do {\
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
size_t l = 0;\
do {\
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t l = 0;\
do {\
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
type_out *array = (type_out *)results->array;\
size_t l = 0;\
do {\
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
type_out *array = (type_out *)results->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
type_out *array = (type_out *)results->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
type_out *array = (type_out *)results->array;\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
type_out *array = (type_out *)results->array;\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#endif /* ULAB_MAX_DIMS == 4 */
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
// iterator macro for traversing arrays over all dimensions
#if ULAB_MAX_DIMS == 1
#define ITERATOR_HEAD()\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define ITERATOR_HEAD()\
size_t _k_ = 0;\
do {\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
_k_++;\
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define ITERATOR_HEAD()\
size_t _j_ = 0;\
do {\
size_t _k_ = 0;\
do {\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
_k_++;\
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
_j_++;\
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define ITERATOR_HEAD()\
size_t _i_ = 0;\
do {\
size_t _j_ = 0;\
do {\
size_t _k_ = 0;\
do {\
size_t _l_ = 0;\
do {
#define ITERATOR_TAIL(_source_, _source_array_)\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
_l_++;\
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
_k_++;\
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
_j_++;\
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);\
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 3] * (_source_)->shape[ULAB_MAX_DIMS - 3];\
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 4];\
_i_++;\
} while(_i_ < (_source_)->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS == 4 */
#endif

1245
code/ndarray_operators.c Normal file

File diff suppressed because it is too large Load diff

714
code/ndarray_operators.h Normal file
View file

@ -0,0 +1,714 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2023 Zoltán Vörös
*/
#include "ndarray.h"
mp_obj_t ndarray_binary_equality(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
mp_obj_t ndarray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_modulo(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t );
mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
#define UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, OPERATOR)\
({\
if((lhs)->dtype == NDARRAY_UINT8) {\
if((rhs)->dtype == NDARRAY_UINT8) {\
INPLACE_LOOP((lhs), uint8_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_INT8) {\
INPLACE_LOOP((lhs), uint8_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_UINT16) {\
INPLACE_LOOP((lhs), uint8_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
} else {\
INPLACE_LOOP((lhs), uint8_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
}\
} else if(lhs->dtype == NDARRAY_INT8) {\
if(rhs->dtype == NDARRAY_UINT8) {\
INPLACE_LOOP((lhs), int8_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_INT8) {\
INPLACE_LOOP((lhs), int8_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_UINT16) {\
INPLACE_LOOP((lhs), int8_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
} else {\
INPLACE_LOOP((lhs), int8_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
}\
} else if(lhs->dtype == NDARRAY_UINT16) {\
if(rhs->dtype == NDARRAY_UINT8) {\
INPLACE_LOOP((lhs), uint16_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_INT8) {\
INPLACE_LOOP((lhs), uint16_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_UINT16) {\
INPLACE_LOOP((lhs), uint16_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
} else {\
INPLACE_LOOP((lhs), uint16_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
}\
} else if(lhs->dtype == NDARRAY_INT16) {\
if(rhs->dtype == NDARRAY_UINT8) {\
INPLACE_LOOP((lhs), int16_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_INT8) {\
INPLACE_LOOP((lhs), int16_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_UINT16) {\
INPLACE_LOOP((lhs), int16_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
} else {\
INPLACE_LOOP((lhs), int16_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
}\
} else if(lhs->dtype == NDARRAY_FLOAT) {\
if(rhs->dtype == NDARRAY_UINT8) {\
INPLACE_LOOP((lhs), mp_float_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_INT8) {\
INPLACE_LOOP((lhs), mp_float_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_UINT16) {\
INPLACE_LOOP((lhs), mp_float_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
} else if(rhs->dtype == NDARRAY_INT16) {\
INPLACE_LOOP((lhs), mp_float_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
} else {\
INPLACE_LOOP((lhs), mp_float_t, mp_float_t, (larray), (rarray), (rstrides), OPERATOR);\
}\
}\
})
#if ULAB_MAX_DIMS == 1
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
({ size_t l = 0;\
do {\
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
({ size_t l = 0;\
do {\
mp_float_t lvalue = (get_lhs)((larray));\
mp_float_t rvalue = (get_rhs)((rarray));\
(set_result)((array), OPERATION);\
(array) += (results)->itemsize;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
({ size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
})
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
({ size_t k = 0;\
do {\
size_t l = 0;\
do {\
mp_float_t lvalue = (get_lhs)((larray));\
mp_float_t rvalue = (get_rhs)((rarray));\
(set_result)((array), OPERATION);\
(array) += (results)->itemsize;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
})
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
({ size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
})
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
({ size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
mp_float_t lvalue = (get_lhs)((larray));\
mp_float_t rvalue = (get_rhs)((rarray));\
(set_result)((array), OPERATION);\
(array) += (results)->itemsize;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
})
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
({ size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
})
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
({ size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
mp_float_t lvalue = (get_lhs)((larray));\
mp_float_t rvalue = (get_rhs)((rarray));\
(set_result)((array), OPERATION);\
(array) += (results)->itemsize;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
})
#endif /* ULAB_MAX_DIMS == 4 */
#define FLOOR_DIVIDE_UINT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
({\
size_t l = 0;\
do {\
*(array)++ = *((type_left *)(larray)) / *((type_right *)(rarray));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#define FLOOR_DIVIDE1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
({\
size_t l = 0;\
int16_t num;\
int16_t denom = (int16_t)*((type_right *)(rarray));\
do {\
num = (int16_t)*((type_left *)(larray));\
if(num >= 0) {\
if(denom < 0) {\
num += -denom - 1;\
}\
} else {\
if(denom >= 0) {\
num += -denom + 1;\
}\
}\
*(array)++ = num / denom;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#define FLOOR_DIVIDE_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
({\
size_t l = 0;\
do {\
*(array)++ = MICROPY_FLOAT_C_FUN(floor)(*((type_left *)(larray)) / *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#if ULAB_MAX_DIMS == 1
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
} while(0)
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
} while(0)
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t l = 0;\
do {\
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t l = 0;\
do {\
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t l = 0;\
do {\
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#define MODULO_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
({\
size_t l = 0;\
do {\
*(array)++ = MICROPY_FLOAT_C_FUN(fmod)(*((type_left *)(larray)), *((type_right *)(rarray)));\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#if ULAB_MAX_DIMS == 1
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t l = 0;\
do {\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
type_out *array = (type_out *)(results)->array;\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#define INPLACE_MODULO_FLOAT1(results, type_right, larray, rarray, rstrides)\
({\
size_t l = 0;\
do {\
*((mp_float_t *)larray) = MICROPY_FLOAT_C_FUN(fmod)(*((mp_float_t *)(larray)), *((type_right *)(rarray)));\
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
})
#if ULAB_MAX_DIMS == 1
#define INPLACE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
size_t l = 0;\
do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */

104
code/ndarray_properties.c Normal file
View file

@ -0,0 +1,104 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2021-2025 Zoltán Vörös
*
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "ulab.h"
#include "ndarray.h"
#include "numpy/ndarray/ndarray_iter.h"
#if ULAB_SUPPORTS_COMPLEX
#include "numpy/carray/carray.h"
#endif
void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
if (dest[0] == MP_OBJ_NULL) {
switch(attr) {
#if NDARRAY_HAS_DTYPE
case MP_QSTR_dtype:
dest[0] = ndarray_dtype(self_in);
break;
#endif
#if NDARRAY_HAS_FLATITER
case MP_QSTR_flat:
dest[0] = ndarray_flatiter_make_new(self_in);
break;
#endif
#if NDARRAY_HAS_ITEMSIZE
case MP_QSTR_itemsize:
dest[0] = ndarray_itemsize(self_in);
break;
#endif
#if NDARRAY_HAS_NDIM
case MP_QSTR_ndim:
dest[0] = ndarray_ndim(self_in);
break;
#endif
#if NDARRAY_HAS_SHAPE
case MP_QSTR_shape:
dest[0] = ndarray_shape(self_in);
break;
#endif
#if NDARRAY_HAS_SIZE
case MP_QSTR_size:
dest[0] = ndarray_size(self_in);
break;
#endif
#if NDARRAY_HAS_STRIDES
case MP_QSTR_strides:
dest[0] = ndarray_strides(self_in);
break;
#endif
#if NDARRAY_HAS_TRANSPOSE
case MP_QSTR_T:
dest[0] = ndarray_transpose(self_in);
break;
#endif
#if ULAB_SUPPORTS_COMPLEX
#if ULAB_NUMPY_HAS_IMAG
case MP_QSTR_imag:
dest[0] = carray_imag(self_in);
break;
#endif
#if ULAB_NUMPY_HAS_IMAG
case MP_QSTR_real:
dest[0] = carray_real(self_in);
break;
#endif
#endif /* ULAB_SUPPORTS_COMPLEX */
default:
// forward to locals dict
dest[1] = MP_OBJ_SENTINEL;
break;
}
} else {
if(dest[1]) {
switch(attr) {
#if ULAB_MAX_DIMS > 1
#if NDARRAY_HAS_RESHAPE
case MP_QSTR_shape:
ndarray_reshape_core(self_in, dest[1], 1);
break;
#endif
#endif
default:
return;
break;
}
dest[0] = MP_OBJ_NULL;
}
}
}

55
code/ndarray_properties.h Normal file
View file

@ -0,0 +1,55 @@
/*
* 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-2025 Zoltán Vörös
*/
#ifndef _NDARRAY_PROPERTIES_
#define _NDARRAY_PROPERTIES_
#include "py/runtime.h"
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "ulab.h"
#include "ndarray.h"
#include "numpy/ndarray/ndarray_iter.h"
void ndarray_properties_attr(mp_obj_t , qstr , mp_obj_t *);
#if NDARRAY_HAS_DTYPE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_dtype_obj, ndarray_dtype);
#endif
#if NDARRAY_HAS_FLATITER
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_new);
#endif
#if NDARRAY_HAS_ITEMSIZE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize);
#endif
#if NDARRAY_HAS_NDIM
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_ndim_obj, ndarray_ndim);
#endif
#if NDARRAY_HAS_SHAPE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
#endif
#if NDARRAY_HAS_SIZE
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size);
#endif
#if NDARRAY_HAS_STRIDES
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_strides_obj, ndarray_strides);
#endif
#endif

View file

@ -1,602 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/misc.h"
#include "numerical.h"
enum NUMERICAL_FUNCTION_TYPE {
NUMERICAL_MIN,
NUMERICAL_MAX,
NUMERICAL_ARGMIN,
NUMERICAL_ARGMAX,
NUMERICAL_SUM,
NUMERICAL_MEAN,
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_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_num, MP_ARG_INT, {.u_int = 50} },
{ MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_true_obj)} },
{ MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
{ 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(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("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_obj_t numerical_sum_mean_std_array(mp_obj_t oin, uint8_t optype) {
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);
mp_int_t len = mp_obj_get_int(mp_obj_len(oin));
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
value = mp_obj_get_float(item);
sum += value;
if(optype == NUMERICAL_STD) {
sq_sum += value*value;
}
}
if(optype == NUMERICAL_SUM) {
return mp_obj_new_float(sum);
} else if(optype == NUMERICAL_MEAN) {
return mp_obj_new_float(sum/len);
} else {
sum /= len; // this is now the mean!
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum));
}
}
STATIC mp_float_t numerical_sum_mean_std_single_line(void *data, size_t start, size_t stop,
size_t stride, uint8_t typecode, uint8_t optype) {
mp_float_t sum = 0.0, sq_sum = 0.0, value;
size_t len = 0;
for(size_t i=start; i < stop; i+=stride, len++) {
value = ndarray_get_float_value(data, typecode, i);
sum += value;
if(optype == NUMERICAL_STD) {
sq_sum += value*value;
}
}
if(len == 0) {
mp_raise_ValueError("data length is 0!");
}
if(optype == NUMERICAL_SUM) {
return sum;
} else if(optype == NUMERICAL_MEAN) {
return sum/len;
} else {
sum /= len; // this is now the mean!
return MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum);
}
}
STATIC mp_obj_t numerical_sum_mean_std_matrix(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {
// return the value for the flattened array
return mp_obj_new_float(numerical_sum_mean_std_single_line(in->array->items, 0,
in->array->len, 1, in->array->typecode, optype));
} else {
uint8_t _axis = mp_obj_get_int(axis);
size_t m = (_axis == 0) ? 1 : in->m;
size_t n = (_axis == 0) ? in->n : 1;
size_t len = in->array->len;
mp_float_t sms;
// TODO: pass in->array->typcode to create_new_ndarray
ndarray_obj_t *out = create_new_ndarray(m, n, NDARRAY_FLOAT);
// TODO: these two cases could probably be combined in a more elegant fashion...
if(_axis == 0) { // vertical
for(size_t i=0; i < n; i++) {
sms = numerical_sum_mean_std_single_line(in->array->items, i, len,
n, in->array->typecode, optype);
((float_t *)out->array->items)[i] = sms;
}
} else { // horizontal
for(size_t i=0; i < m; i++) {
sms = numerical_sum_mean_std_single_line(in->array->items, i*in->n,
(i+1)*in->n, 1, in->array->typecode, optype);
((float_t *)out->array->items)[i] = sms;
}
}
return MP_OBJ_FROM_PTR(out);
}
}
size_t numerical_argmin_argmax_array(ndarray_obj_t *in, size_t start,
size_t stop, size_t stride, uint8_t op) {
size_t best_idx = start;
if(in->array->typecode == NDARRAY_UINT8) {
ARG_MIN_LOOP(in, uint8_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_INT8) {
ARG_MIN_LOOP(in, int8_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_UINT16) {
ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_INT16) {
ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_FLOAT) {
ARG_MIN_LOOP(in, mp_float_t, start, stop, stride, op);
}
return best_idx;
}
void copy_value_into_ndarray(ndarray_obj_t *target, ndarray_obj_t *source, size_t target_idx, size_t source_idx) {
// since we are simply copying, it doesn't matter, whether the arrays are signed or unsigned,
// we can cast them in any way we like
// This could also be done with byte copies. I don't know, whether that would have any benefits
if((target->array->typecode == NDARRAY_UINT8) || (target->array->typecode == NDARRAY_INT8)) {
((uint8_t *)target->array->items)[target_idx] = ((uint8_t *)source->array->items)[source_idx];
} else if((target->array->typecode == NDARRAY_UINT16) || (target->array->typecode == NDARRAY_INT16)) {
((uint16_t *)target->array->items)[target_idx] = ((uint16_t *)source->array->items)[source_idx];
} else {
((float *)target->array->items)[target_idx] = ((float *)source->array->items)[source_idx];
}
}
STATIC mp_obj_t numerical_argmin_argmax(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
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)) {
// This case will work for single iterables only
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++;
}
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
return MP_OBJ_NEW_SMALL_INT(best_idx);
} else {
return best_obj;
}
} else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
size_t best_idx;
if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {
// return the value for the flattened array
best_idx = numerical_argmin_argmax_array(in, 0, in->array->len, 1, optype);
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
return MP_OBJ_NEW_SMALL_INT(best_idx);
} else {
if(in->array->typecode == NDARRAY_FLOAT) {
return mp_obj_new_float(ndarray_get_float_value(in->array->items, in->array->typecode, best_idx));
} else {
return mp_binary_get_val_array(in->array->typecode, in->array->items, best_idx);
}
}
} else { // we have to work with a full matrix here
uint8_t _axis = mp_obj_get_int(axis);
size_t m = (_axis == 0) ? 1 : in->m;
size_t n = (_axis == 0) ? in->n : 1;
size_t len = in->array->len;
ndarray_obj_t *ndarray = NULL;
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
ndarray = create_new_ndarray(m, n, in->array->typecode);
} else { // argmin/argmax
// TODO: one might get away with uint8_t, if both m, and n < 255
ndarray = create_new_ndarray(m, n, NDARRAY_UINT16);
}
// TODO: these two cases could probably be combined in a more elegant fashion...
if(_axis == 0) { // vertical
for(size_t i=0; i < n; i++) {
best_idx = numerical_argmin_argmax_array(in, i, len, n, optype);
if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {
copy_value_into_ndarray(ndarray, in, i, best_idx);
} else {
((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx / n);
}
}
} else { // horizontal
for(size_t i=0; i < m; i++) {
best_idx = numerical_argmin_argmax_array(in, i*in->n, (i+1)*in->n, 1, optype);
if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {
copy_value_into_ndarray(ndarray, in, i, best_idx);
} else {
((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx - i*in->n);
}
}
}
return MP_OBJ_FROM_PTR(ndarray);
}
return mp_const_none;
}
mp_raise_TypeError("input type is not supported");
}
STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t type) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} } ,
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
};
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_obj_t oin = args[0].u_obj;
mp_obj_t axis = args[1].u_obj;
if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
// this seems to pass with False, and True...
mp_raise_ValueError("axis must be None, 0, or 1");
}
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)) {
switch(type) {
case NUMERICAL_MIN:
case NUMERICAL_ARGMIN:
case NUMERICAL_MAX:
case NUMERICAL_ARGMAX:
return numerical_argmin_argmax(oin, axis, type);
case NUMERICAL_SUM:
case NUMERICAL_MEAN:
case NUMERICAL_STD:
return numerical_sum_mean_std_array(oin, type);
default: // we should never reach this point, but whatever
return mp_const_none;
}
} else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
switch(type) {
case NUMERICAL_MIN:
case NUMERICAL_MAX:
case NUMERICAL_ARGMIN:
case NUMERICAL_ARGMAX:
return numerical_argmin_argmax(oin, axis, type);
case NUMERICAL_SUM:
case NUMERICAL_MEAN:
case NUMERICAL_STD:
return numerical_sum_mean_std_matrix(oin, axis, type);
default:
mp_raise_NotImplementedError("operation is not implemented on ndarrays");
}
} else {
mp_raise_TypeError("input must be tuple, list, range, or ndarray");
}
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);
}
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_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_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_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_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_obj_t numerical_std(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_STD);
}
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_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
};
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_obj_t oin = args[0].u_obj;
int16_t shift = mp_obj_get_int(args[1].u_obj);
if((args[2].u_obj != mp_const_none) &&
(mp_obj_get_int(args[2].u_obj) != 0) &&
(mp_obj_get_int(args[2].u_obj) != 1)) {
mp_raise_ValueError("axis must be None, 0, or 1");
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
uint8_t _sizeof = mp_binary_get_size('@', in->array->typecode, NULL);
size_t len;
int16_t _shift;
uint8_t *array = (uint8_t *)in->array->items;
// TODO: transpose the matrix, if axis == 0. Though, that is hard on the RAM...
if(shift < 0) {
_shift = -shift;
} else {
_shift = shift;
}
if((args[2].u_obj == mp_const_none) || (mp_obj_get_int(args[2].u_obj) == 1)) { // shift horizontally
uint16_t M;
if(args[2].u_obj == mp_const_none) {
len = in->array->len;
M = 1;
} else {
len = in->n;
M = in->m;
}
_shift = _shift % len;
if(shift < 0) _shift = len - _shift;
// TODO: if(shift > len/2), we should move in the opposite direction. That would save RAM
_shift *= _sizeof;
uint8_t *tmp = m_new(uint8_t, _shift);
for(size_t m=0; m < M; m++) {
memmove(tmp, &array[m*len*_sizeof], _shift);
memmove(&array[m*len*_sizeof], &array[m*len*_sizeof+_shift], len*_sizeof-_shift);
memmove(&array[(m+1)*len*_sizeof-_shift], tmp, _shift);
}
m_del(uint8_t, tmp, _shift);
return mp_const_none;
} else {
len = in->m;
// temporary buffer
uint8_t *_data = m_new(uint8_t, _sizeof*len);
_shift = _shift % len;
if(shift < 0) _shift = len - _shift;
_shift *= _sizeof;
uint8_t *tmp = m_new(uint8_t, _shift);
for(size_t n=0; n < in->n; n++) {
for(size_t m=0; m < len; m++) {
// this loop should fill up the temporary buffer
memmove(&_data[m*_sizeof], &array[(m*in->n+n)*_sizeof], _sizeof);
}
// now, the actual shift
memmove(tmp, _data, _shift);
memmove(_data, &_data[_shift], len*_sizeof-_shift);
memmove(&_data[len*_sizeof-_shift], tmp, _shift);
for(size_t m=0; m < len; m++) {
// this loop should dump the content of the temporary buffer into data
memmove(&array[(m*in->n+n)*_sizeof], &_data[m*_sizeof], _sizeof);
}
}
m_del(uint8_t, tmp, _shift);
m_del(uint8_t, _data, _sizeof*len);
return mp_const_none;
}
}
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_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
};
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("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("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_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_ROM_PTR(&mp_const_none_obj) } },
{ 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("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("axis must be -1, 0, or 1");
}
if((args[1].u_int < 0) || (args[1].u_int > 9)) {
mp_raise_ValueError("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);
}
/*
void heapsort(ndarray_obj_t *ndarray, size_t n, size_t increment) {
uint8_t type;
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
type = 0;
}
if((ndarray->array->typecode == NDARRAY_UINT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
type = 1;
} else {
type = 2;
}
size_t i, j, k, l;
l = (n >> 1) + 1;
k = n;
uint8_t ui8_tmp, *ui8_array = NULL;
uint16_t ui16_tmp, *ui16_array = NULL;
mp_float_t float_tmp, *float_array = NULL;
for(;;) {
if (l > 1) {
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
ui8_tmp = ui8_array[--l];
}
} else {
tmp = array[k];
array[k] = array[1];
if (--k == 1) {
array[1] = tmp;
break;
}
}
i = l;
j = 2 * l;
while(j <= k) {
if(j < k && array[j] < array[j+1]) {
j++;
}
}
if(tmp < array[j]) {
array[i] = array[j];
i = j;
j <<= 1;
} else break;
}
array[i] = tmp;
}
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_ROM_PTR(&mp_const_none_obj) } },
{ 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("sort argument must be an ndarray");
}
return mp_const_none;
}*/

View file

@ -1,63 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#ifndef _NUMERICAL_
#define _NUMERICAL_
#include "ndarray.h"
mp_obj_t numerical_linspace(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_sum(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_mean(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_std(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_min(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_max(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_argmin(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_argmax(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_roll(size_t , const mp_obj_t *, mp_map_t *);
// 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 );
mp_obj_t numerical_cumsum(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_flip(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_diff(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_sort(size_t , const mp_obj_t *, mp_map_t *);
// this macro could be tighter, if we moved the ifs to the argmin function, assigned <, as well as >
#define ARG_MIN_LOOP(in, type, start, stop, stride, op) do {\
type *array = (type *)(in)->array->items;\
if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\
for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\
if((array)[i] > (array)[best_idx]) {\
best_idx = i;\
}\
}\
} else{\
for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\
if((array)[i] < (array)[best_idx]) best_idx = i;\
}\
}\
} while(0)
#define CALCULATE_DIFF(in, out, type, M, N, inn, increment) do {\
type *source = (type *)(in)->array->items;\
type *target = (type *)(out)->array->items;\
for(size_t i=0; i < (M); i++) {\
for(size_t j=0; j < (N); j++) {\
for(uint8_t k=0; k < n+1; k++) {\
target[i*(N)+j] -= stencil[k]*source[i*(inn)+j+k*(increment)];\
}\
}\
}\
} while(0)
#endif

227
code/numpy/approx.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-2021 Zoltán Vörös
* 2020 Diego Elio Pettenò
* 2020 Taku Fukada
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../ulab.h"
#include "../ulab_tools.h"
#include "carray/carray_tools.h"
#include "approx.h"
//| """Numerical approximation methods"""
//|
ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL);
#if ULAB_NUMPY_HAS_INTERP
//| def interp(
//| x: ulab.numpy.ndarray,
//| xp: ulab.numpy.ndarray,
//| fp: ulab.numpy.ndarray,
//| *,
//| left: Optional[_float] = None,
//| right: Optional[_float] = None
//| ) -> ulab.numpy.ndarray:
//| """
//| :param ulab.numpy.ndarray x: The x-coordinates at which to evaluate the interpolated values.
//| :param ulab.numpy.ndarray xp: The x-coordinates of the data points, must be increasing
//| :param ulab.numpy.ndarray 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_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0);
ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values
ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0);
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(xp->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(fp->dtype)
if((xp->ndim != 1) || (fp->ndim != 1) || (xp->len < 2) || (fp->len < 2) || (xp->len != fp->len)) {
mp_raise_ValueError(MP_ERROR_TEXT("interp is defined for 1D iterables of equal length"));
}
ndarray_obj_t *y = ndarray_new_linear_array(x->len, NDARRAY_FLOAT);
mp_float_t left_value, right_value;
uint8_t *xparray = (uint8_t *)xp->array;
mp_float_t xp_left = ndarray_get_float_value(xparray, xp->dtype);
xparray += (xp->len-1) * xp->strides[ULAB_MAX_DIMS - 1];
mp_float_t xp_right = ndarray_get_float_value(xparray, xp->dtype);
uint8_t *fparray = (uint8_t *)fp->array;
if(args[3].u_obj == mp_const_none) {
left_value = ndarray_get_float_value(fparray, fp->dtype);
} else {
left_value = mp_obj_get_float(args[3].u_obj);
}
if(args[4].u_obj == mp_const_none) {
fparray += (fp->len-1) * fp->strides[ULAB_MAX_DIMS - 1];
right_value = ndarray_get_float_value(fparray, fp->dtype);
} else {
right_value = mp_obj_get_float(args[4].u_obj);
}
xparray = xp->array;
fparray = fp->array;
uint8_t *xarray = (uint8_t *)x->array;
mp_float_t *yarray = (mp_float_t *)y->array;
uint8_t *temp;
for(size_t i=0; i < x->len; i++, yarray++) {
mp_float_t x_value = ndarray_get_float_value(xarray, x->dtype);
xarray += x->strides[ULAB_MAX_DIMS - 1];
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->len - 1, middle_index;
while(right_index - left_index > 1) {
middle_index = left_index + (right_index - left_index) / 2;
temp = xparray + middle_index * xp->strides[ULAB_MAX_DIMS - 1];
mp_float_t xp_middle = ndarray_get_float_value(temp, xp->dtype);
if(x_value <= xp_middle) {
right_index = middle_index;
} else {
left_index = middle_index;
}
}
temp = xparray + left_index * xp->strides[ULAB_MAX_DIMS - 1];
xp_left_ = ndarray_get_float_value(temp, xp->dtype);
temp = xparray + right_index * xp->strides[ULAB_MAX_DIMS - 1];
xp_right_ = ndarray_get_float_value(temp, xp->dtype);
temp = fparray + left_index * fp->strides[ULAB_MAX_DIMS - 1];
fp_left = ndarray_get_float_value(temp, fp->dtype);
temp = fparray + right_index * fp->strides[ULAB_MAX_DIMS - 1];
fp_right = ndarray_get_float_value(temp, fp->dtype);
*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);
#endif
#if ULAB_NUMPY_HAS_TRAPZ
//| def trapz(y: ulab.numpy.ndarray, x: Optional[ulab.numpy.ndarray] = None, dx: _float = 1.0) -> _float:
//| """
//| :param 1D ulab.numpy.ndarray y: the values of the dependent variable
//| :param 1D ulab.numpy.ndarray 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_ROM_NONE } },
{ MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(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, 0);
COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype)
ndarray_obj_t *x;
mp_float_t mean = MICROPY_FLOAT_CONST(0.0);
if(y->len < 2) {
return mp_obj_new_float(mean);
}
if((y->ndim != 1)) {
mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D iterables"));
}
mp_float_t (*funcy)(void *) = ndarray_get_float_function(y->dtype);
uint8_t *yarray = (uint8_t *)y->array;
size_t count = 1;
mp_float_t y1, y2, m;
if(args[1].u_obj != mp_const_none) {
x = ndarray_from_mp_obj(args[1].u_obj, 0); // x must hold an increasing sequence of independent values
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
if((x->ndim != 1) || (y->len != x->len)) {
mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D arrays of equal length"));
}
mp_float_t (*funcx)(void *) = ndarray_get_float_function(x->dtype);
uint8_t *xarray = (uint8_t *)x->array;
mp_float_t x1, x2;
y1 = funcy(yarray);
yarray += y->strides[ULAB_MAX_DIMS - 1];
x1 = funcx(xarray);
xarray += x->strides[ULAB_MAX_DIMS - 1];
for(size_t i=1; i < y->len; i++) {
y2 = funcy(yarray);
yarray += y->strides[ULAB_MAX_DIMS - 1];
x2 = funcx(xarray);
xarray += x->strides[ULAB_MAX_DIMS - 1];
mp_float_t value = (x2 - x1) * (y2 + y1);
m = mean + (value - mean) / (mp_float_t)count;
mean = m;
x1 = x2;
y1 = y2;
count++;
}
} else {
mp_float_t dx = mp_obj_get_float(args[2].u_obj);
y1 = funcy(yarray);
yarray += y->strides[ULAB_MAX_DIMS - 1];
for(size_t i=1; i < y->len; i++) {
y2 = ndarray_get_float_index(y->array, y->dtype, i);
mp_float_t value = (y2 + y1);
m = mean + (value - mean) / (mp_float_t)count;
mean = m;
y1 = y2;
count++;
}
mean *= dx;
}
return mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)*mean*(y->len-1));
}
MP_DEFINE_CONST_FUN_OBJ_KW(approx_trapz_obj, 1, approx_trapz);
#endif

29
code/numpy/approx.h Normal file
View file

@ -0,0 +1,29 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*/
#ifndef _APPROX_
#define _APPROX_
#include "../ulab.h"
#include "../ndarray.h"
#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)
MP_DECLARE_CONST_FUN_OBJ_KW(approx_interp_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(approx_trapz_obj);
#endif /* _APPROX_ */

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

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

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

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

834
code/numpy/carray/carray.c Normal file
View file

@ -0,0 +1,834 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2021-2022 Zoltán Vörös
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/objint.h"
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/misc.h"
#include "../../ulab.h"
#include "../../ndarray.h"
#include "../../ulab_tools.h"
#include "carray.h"
#if ULAB_SUPPORTS_COMPLEX
//| import builtins
//|
//| import ulab.numpy
//| def real(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| Return the real part of the complex argument, which can be
//| either an ndarray, or a scalar."""
//| ...
//|
mp_obj_t carray_real(mp_obj_t _source) {
if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
if(source->dtype != NDARRAY_COMPLEX) {
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
ndarray_copy_array(source, target, 0);
return MP_OBJ_FROM_PTR(target);
} else { // the input is most definitely a complex array
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
ndarray_copy_array(source, target, 0);
return MP_OBJ_FROM_PTR(target);
}
} else {
mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only"));
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real);
//| def imag(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| Return the imaginary part of the complex argument, which can be
//| either an ndarray, or a scalar."""
//| ...
//|
mp_obj_t carray_imag(mp_obj_t _source) {
if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
return MP_OBJ_FROM_PTR(target);
} else { // the input is most definitely a complex array
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
ndarray_copy_array(source, target, source->itemsize / 2);
return MP_OBJ_FROM_PTR(target);
}
} else {
mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only"));
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag);
#if ULAB_NUMPY_HAS_CONJUGATE
//| def conjugate(
//| val: builtins.complex | ulab.numpy.ndarray
//| ) -> builtins.complex | ulab.numpy.ndarray:
//| """
//| Return the conjugate of the complex argument, which can be
//| either an ndarray, or a scalar."""
//| ...
//|
mp_obj_t carray_conjugate(mp_obj_t _source) {
if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
ndarray_copy_array(source, ndarray, 0);
if(source->dtype == NDARRAY_COMPLEX) {
mp_float_t *array = (mp_float_t *)ndarray->array;
array++;
for(size_t i = 0; i < ndarray->len; i++) {
*array *= MICROPY_FLOAT_CONST(-1.0);
array += 2;
}
}
return MP_OBJ_FROM_PTR(ndarray);
} else {
if(mp_obj_is_type(_source, &mp_type_complex)) {
mp_float_t real, imag;
mp_obj_get_complex(_source, &real, &imag);
imag = imag * MICROPY_FLOAT_CONST(-1.0);
return mp_obj_new_complex(real, imag);
} else if(mp_obj_is_int(_source) || mp_obj_is_float(_source)) {
return _source;
} else {
mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray, or a scalar"));
}
}
// this should never happen
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate);
#endif
#if ULAB_NUMPY_HAS_SORT_COMPLEX
//| def sort_complex(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| .. param: a
//| a one-dimensional ndarray
//|
//| Sort a complex array using the real part first, then the imaginary part.
//| Always returns a sorted complex array, even if the input was real."""
//| ...
//|
static void carray_sort_complex_(mp_float_t *array, size_t len) {
// array is assumed to be a floating vector containing the real and imaginary parts
// of a complex array at alternating positions as
// array[0] = real[0]
// array[1] = imag[0]
// array[2] = real[1]
// array[3] = imag[1]
mp_float_t real, imag;
size_t c, q = len, p, r = len >> 1;
for (;;) {
if (r > 0) {
r--;
real = array[2 * r];
imag = array[2 * r + 1];
} else {
q--;
if(q == 0) {
break;
}
real = array[2 * q];
imag = array[2 * q + 1];
array[2 * q] = array[0];
array[2 * q + 1] = array[1];
}
p = r;
c = r + r + 1;
while (c < q) {
if(c + 1 < q) {
if((array[2 * (c+1)] > array[2 * c]) ||
((array[2 * (c+1)] == array[2 * c]) && (array[2 * (c+1) + 1] > array[2 * c + 1]))) {
c++;
}
}
if((array[2 * c] > real) ||
((array[2 * c] == real) && (array[2 * c + 1] > imag))) {
array[2 * p] = array[2 * c]; // real part
array[2 * p + 1] = array[2 * c + 1]; // imag part
p = c;
c = p + p + 1;
} else {
break;
}
}
array[2 * p] = real;
array[2 * p + 1] = imag;
}
}
mp_obj_t carray_sort_complex(mp_obj_t _source) {
if(!mp_obj_is_type(_source, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray"));
}
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
if(source->ndim != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray"));
}
ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(source, NDARRAY_COMPLEX);
if(ndarray->len != 0) {
mp_float_t *array = (mp_float_t *)ndarray->array;
carray_sort_complex_(array, ndarray->len);
}
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_1(carray_sort_complex_obj, carray_sort_complex);
#endif
//| def abs(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| .. param: a
//| a one-dimensional ndarray
//|
//| Return the absolute value of complex ndarray."""
//| ...
//|
mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) {
// calculates the absolute value of a complex array and returns a dense array
uint8_t *sarray = (uint8_t *)source->array;
mp_float_t *tarray = (mp_float_t *)target->array;
uint8_t itemsize = mp_binary_get_size('@', NDARRAY_FLOAT, NULL);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
mp_float_t rvalue = *(mp_float_t *)sarray;
mp_float_t ivalue = *(mp_float_t *)(sarray + itemsize);
*tarray++ = MICROPY_FLOAT_C_FUN(sqrt)(rvalue * rvalue + ivalue * ivalue);
sarray += source->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
sarray += source->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
sarray += source->strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
sarray += source->strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
#endif
return MP_OBJ_FROM_PTR(target);
}
static void carray_copy_part(uint8_t *tarray, uint8_t *sarray, size_t *shape, int32_t *strides) {
// copies the real or imaginary part of an array
// into the respective part of a dense complex array
uint8_t sz = sizeof(mp_float_t);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
memcpy(tarray, sarray, sz);
tarray += 2 * sz;
sarray += strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
sarray -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS-1];
sarray += strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_MAX_DIMS > 2
sarray -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2];
sarray += strides[ULAB_MAX_DIMS - 3];
j++;
} while(j < shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS > 2 */
#if ULAB_MAX_DIMS > 3
sarray -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS-3];
sarray += strides[ULAB_MAX_DIMS - 4];
i++;
} while(i < shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS > 3 */
}
mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) {
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
results->boolean = 1;
uint8_t *array = (uint8_t *)results->array;
if(op == MP_BINARY_OP_NOT_EQUAL) {
memset(array, 1, results->len);
}
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
mp_float_t *larray = (mp_float_t *)lhs->array;
mp_float_t *rarray = (mp_float_t *)rhs->array;
ulab_rescale_float_strides(lstrides);
ulab_rescale_float_strides(rstrides);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
if((larray[0] == rarray[0]) && (larray[1] == rarray[1])) {
*array ^= 0x01;
}
array++;
larray += lstrides[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
larray += lstrides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_MAX_DIMS > 2
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
larray += lstrides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS > 2 */
#if ULAB_MAX_DIMS > 3
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
larray += lstrides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
rarray += rstrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS > 3 */
} else { // only one of the operands is complex
mp_float_t *larray = (mp_float_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
// align the complex array to the left
uint8_t rdtype = rhs->dtype;
int32_t *lstrides_ = lstrides;
int32_t *rstrides_ = rstrides;
if(rhs->dtype == NDARRAY_COMPLEX) {
larray = (mp_float_t *)rhs->array;
rarray = (uint8_t *)lhs->array;
lstrides_ = rstrides;
rstrides_ = lstrides;
rdtype = lhs->dtype;
}
ulab_rescale_float_strides(lstrides_);
if(rdtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX_EQUAL(results, array, uint8_t, larray, lstrides_, rarray, rstrides_);
} else if(rdtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX_EQUAL(results, array, int8_t, larray, lstrides_, rarray, rstrides_);
} else if(rdtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX_EQUAL(results, array, uint16_t, larray, lstrides_, rarray, rstrides_);
} else if(rdtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX_EQUAL(results, array, int16_t, larray, lstrides_, rarray, rstrides_);
} else if(rdtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX_EQUAL(results, array, mp_float_t, larray, lstrides_, rarray, rstrides_);
}
}
return MP_OBJ_FROM_PTR(results);
}
mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
mp_float_t *resarray = (mp_float_t *)results->array;
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
mp_float_t *larray = (mp_float_t *)lhs->array;
mp_float_t *rarray = (mp_float_t *)rhs->array;
ulab_rescale_float_strides(lstrides);
ulab_rescale_float_strides(rstrides);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
// real part
*resarray++ = larray[0] + rarray[0];
// imaginary part
*resarray++ = larray[1] + rarray[1];
larray += lstrides[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
larray += lstrides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_MAX_DIMS > 2
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
larray += lstrides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS > 2 */
#if ULAB_MAX_DIMS > 3
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
larray += lstrides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
rarray += rstrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS > 3 */
} else { // only one of the operands is complex
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
// align the complex array to the left
uint8_t rdtype = rhs->dtype;
int32_t *lstrides_ = lstrides;
int32_t *rstrides_ = rstrides;
if(rhs->dtype == NDARRAY_COMPLEX) {
larray = (uint8_t *)rhs->array;
rarray = (uint8_t *)lhs->array;
lstrides_ = rstrides;
rstrides_ = lstrides;
rdtype = lhs->dtype;
}
if(rdtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides_, rarray, rstrides_, +);
} else if(rdtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides_, rarray, rstrides_, +);
} else if(rdtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides_, rarray, rstrides_, +);
} else if(rdtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides_, rarray, rstrides_, +);
} else if(rdtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides_, rarray, rstrides_, +);
}
// simply copy the imaginary part
uint8_t *tarray = (uint8_t *)results->array;
tarray += sizeof(mp_float_t);
if(lhs->dtype == NDARRAY_COMPLEX) {
rarray = (uint8_t *)lhs->array;
rstrides = lstrides;
} else {
rarray = (uint8_t *)rhs->array;
}
rarray += sizeof(mp_float_t);
carray_copy_part(tarray, rarray, results->shape, rstrides);
}
return MP_OBJ_FROM_PTR(results);
}
static void carray_binary_multiply_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray,
int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) {
if(rdtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, *);
} else if(rdtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, *);
} else if(rdtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, *);
} else if(rdtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, *);
} else if(rdtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, *);
}
}
mp_obj_t carray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
mp_float_t *resarray = (mp_float_t *)results->array;
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
mp_float_t *larray = (mp_float_t *)lhs->array;
mp_float_t *rarray = (mp_float_t *)rhs->array;
ulab_rescale_float_strides(lstrides);
ulab_rescale_float_strides(rstrides);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
// real part
*resarray++ = larray[0] * rarray[0] - larray[1] * rarray[1];
// imaginary part
*resarray++ = larray[0] * rarray[1] + larray[1] * rarray[0];
larray += lstrides[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
larray += lstrides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_MAX_DIMS > 2
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
larray += lstrides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS > 2 */
#if ULAB_MAX_DIMS > 3
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
larray += lstrides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
rarray += rstrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS > 3 */
} else { // only one of the operands is complex
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
uint8_t *lo = larray, *ro = rarray;
int32_t *left_strides = lstrides;
int32_t *right_strides = rstrides;
uint8_t rdtype = rhs->dtype;
// align the complex array to the left
if(rhs->dtype == NDARRAY_COMPLEX) {
lo = (uint8_t *)rhs->array;
ro = (uint8_t *)lhs->array;
rdtype = lhs->dtype;
left_strides = rstrides;
right_strides = lstrides;
}
larray = lo;
rarray = ro;
// real part
carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype);
larray = lo + sizeof(mp_float_t);
rarray = ro;
resarray = (mp_float_t *)results->array;
resarray++;
// imaginary part
carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype);
}
return MP_OBJ_FROM_PTR(results);
}
mp_obj_t carray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
mp_float_t *resarray = (mp_float_t *)results->array;
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
mp_float_t *larray = (mp_float_t *)lhs->array;
mp_float_t *rarray = (mp_float_t *)rhs->array;
ulab_rescale_float_strides(lstrides);
ulab_rescale_float_strides(rstrides);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
// real part
*resarray++ = larray[0] - rarray[0];
// imaginary part
*resarray++ = larray[1] - rarray[1];
larray += lstrides[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
larray += lstrides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_MAX_DIMS > 2
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
larray += lstrides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS > 2 */
#if ULAB_MAX_DIMS > 3
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
larray += lstrides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
rarray += rstrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS > 3 */
} else {
uint8_t *larray = (uint8_t *)lhs->array;
if(lhs->dtype == NDARRAY_COMPLEX) {
uint8_t *rarray = (uint8_t *)rhs->array;
if(rhs->dtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, -);
} else if(rhs->dtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, -);
} else if(rhs->dtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, -);
} else if(rhs->dtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, -);
} else if(rhs->dtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, -);
}
// copy the imaginary part
uint8_t *tarray = (uint8_t *)results->array;
tarray += sizeof(mp_float_t);
larray = (uint8_t *)lhs->array;
larray += sizeof(mp_float_t);
carray_copy_part(tarray, larray, results->shape, lstrides);
} else if(rhs->dtype == NDARRAY_COMPLEX) {
mp_float_t *rarray = (mp_float_t *)rhs->array;
ulab_rescale_float_strides(rstrides);
if(lhs->dtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint8_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int8_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint16_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int16_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides);
}
}
}
return MP_OBJ_FROM_PTR(results);
}
static void carray_binary_left_divide_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray,
int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) {
if(rdtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, /);
} else if(rdtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, /);
} else if(rdtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, /);
} else if(rdtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, /);
} else if(rdtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, /);
}
}
mp_obj_t carray_binary_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
mp_float_t *resarray = (mp_float_t *)results->array;
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
mp_float_t *larray = (mp_float_t *)lhs->array;
mp_float_t *rarray = (mp_float_t *)rhs->array;
ulab_rescale_float_strides(lstrides);
ulab_rescale_float_strides(rstrides);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
// (a + bi) / (c + di) =
// (ac + bd) / (c^2 + d^2) + i (bc - ad) / (c^2 + d^2)
// denominator
mp_float_t denom = rarray[0] * rarray[0] + rarray[1] * rarray[1];
// real part
*resarray++ = (larray[0] * rarray[0] + larray[1] * rarray[1]) / denom;
// imaginary part
*resarray++ = (larray[1] * rarray[0] - larray[0] * rarray[1]) / denom;
larray += lstrides[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
larray += lstrides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_MAX_DIMS > 2
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
larray += lstrides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
#endif /* ULAB_MAX_DIMS > 2 */
#if ULAB_MAX_DIMS > 3
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
larray += lstrides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
rarray += rstrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
#endif /* ULAB_MAX_DIMS > 3 */
} else {
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(lhs->dtype == NDARRAY_COMPLEX) {
// real part
carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype);
// imaginary part
resarray = (mp_float_t *)results->array;
resarray++;
larray = (uint8_t *)lhs->array;
larray += sizeof(mp_float_t);
rarray = (uint8_t *)rhs->array;
carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype);
} else {
if(lhs->dtype == NDARRAY_UINT8) {
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint8_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_INT8) {
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int8_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_UINT16) {
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint16_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_INT16) {
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int16_t, larray, lstrides, rarray, rstrides);
} else if(lhs->dtype == NDARRAY_FLOAT) {
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides);
}
}
}
return MP_OBJ_FROM_PTR(results);
}
#endif

237
code/numpy/carray/carray.h Normal file
View file

@ -0,0 +1,237 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2021-2022 Zoltán Vörös
*/
#ifndef _CARRAY_
#define _CARRAY_
MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj);
MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj);
MP_DECLARE_CONST_FUN_OBJ_1(carray_conjugate_obj);
MP_DECLARE_CONST_FUN_OBJ_1(carray_sort_complex_obj);
mp_obj_t carray_imag(mp_obj_t );
mp_obj_t carray_real(mp_obj_t );
mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *);
mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
#define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t l = 0;\
do {\
*(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\
(resarray) += 2;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define BINARY_LOOP_COMPLEX2(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t k = 0;\
do {\
BINARY_LOOP_COMPLEX1((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define BINARY_LOOP_COMPLEX3(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t j = 0;\
do {\
BINARY_LOOP_COMPLEX2((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define BINARY_LOOP_COMPLEX4(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t i = 0;\
do {\
BINARY_LOOP_COMPLEX3((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t l = 0;\
do {\
*(resarray)++ = *((type_left *)(larray)) - (rarray)[0];\
*(resarray)++ = -(rarray)[1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t k = 0;\
do {\
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t j = 0;\
do {\
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t i = 0;\
do {\
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t l = 0;\
do {\
mp_float_t *c = (mp_float_t *)(rarray);\
mp_float_t denom = c[0] * c[0] + c[1] * c[1];\
mp_float_t a = *((type_left *)(larray)) / denom;\
*(resarray)++ = a * c[0];\
*(resarray)++ = -a * c[1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t k = 0;\
do {\
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t j = 0;\
do {\
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
size_t i = 0;\
do {\
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#define BINARY_LOOP_COMPLEX_EQUAL1(results, array, type_right, larray, lstrides, rarray, rstrides)\
size_t l = 0;\
do {\
if((*(larray) == *((type_right *)(rarray))) && ((larray)[1] == MICROPY_FLOAT_CONST(0.0))) {\
*(array) ^= 0x01;\
}\
(array)++;\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
#define BINARY_LOOP_COMPLEX_EQUAL2(results, array, type_right, larray, lstrides, rarray, rstrides)\
size_t k = 0;\
do {\
BINARY_LOOP_COMPLEX_EQUAL1((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
#define BINARY_LOOP_COMPLEX_EQUAL3(results, array, type_right, larray, lstrides, rarray, rstrides)\
size_t j = 0;\
do {\
BINARY_LOOP_COMPLEX_EQUAL2((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
#define BINARY_LOOP_COMPLEX_EQUAL4(results, array, type_right, larray, lstrides, rarray, rstrides)\
size_t i = 0;\
do {\
BINARY_LOOP_COMPLEX_EQUAL3((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
#if ULAB_MAX_DIMS == 1
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL1
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL2
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL3
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL4
#endif /* ULAB_MAX_DIMS == 4 */
#endif

View file

@ -0,0 +1,28 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2022 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 "../../ulab.h"
#include "../../ndarray.h"
#if ULAB_SUPPORTS_COMPLEX
void raise_complex_NotImplementedError(void) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("not implemented for complex dtype"));
}
#endif

View file

@ -0,0 +1,25 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2022 Zoltán Vörös
*/
#ifndef _CARRAY_TOOLS_
#define _CARRAY_TOOLS_
void raise_complex_NotImplementedError(void);
#if ULAB_SUPPORTS_COMPLEX
#define NOT_IMPLEMENTED_FOR_COMPLEX() raise_complex_NotImplementedError();
#define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) if((dtype) == NDARRAY_COMPLEX) raise_complex_NotImplementedError();
#else
#define NOT_IMPLEMENTED_FOR_COMPLEX() // do nothing
#define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) // do nothing
#endif
#endif

643
code/numpy/compare.c Normal file
View file

@ -0,0 +1,643 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 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 "../ulab.h"
#include "../ndarray_operators.h"
#include "../ulab_tools.h"
#include "carray/carray_tools.h"
#include "compare.h"
static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) {
ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0);
ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0);
#if ULAB_SUPPORTS_COMPLEX
if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) {
NOT_IMPLEMENTED_FOR_COMPLEX()
}
#endif
uint8_t ndim = 0;
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS);
int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS);
if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) {
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
}
uint8_t *larray = (uint8_t *)lhs->array;
uint8_t *rarray = (uint8_t *)rhs->array;
if(op == COMPARE_EQUAL) {
return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_EQUAL);
} else if(op == COMPARE_NOT_EQUAL) {
return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_NOT_EQUAL);
}
// 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_COMPARE_LOOP are
// typecode of result, type_out, type_left, type_right, lhs operand, rhs operand, operator
if(lhs->dtype == NDARRAY_UINT8) {
if(rhs->dtype == NDARRAY_UINT8) {
RUN_COMPARE_LOOP(NDARRAY_UINT8, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT8) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_UINT16) {
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT16) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_FLOAT) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
}
} else if(lhs->dtype == NDARRAY_INT8) {
if(rhs->dtype == NDARRAY_UINT8) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT8) {
RUN_COMPARE_LOOP(NDARRAY_INT8, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_UINT16) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT16) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_FLOAT) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
}
} else if(lhs->dtype == NDARRAY_UINT16) {
if(rhs->dtype == NDARRAY_UINT8) {
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT8) {
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_UINT16) {
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT16) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_FLOAT) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
}
} else if(lhs->dtype == NDARRAY_INT16) {
if(rhs->dtype == NDARRAY_UINT8) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT8) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_UINT16) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT16) {
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_FLOAT) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
}
} else if(lhs->dtype == NDARRAY_FLOAT) {
if(rhs->dtype == NDARRAY_UINT8) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT8) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_UINT16) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_INT16) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
} else if(rhs->dtype == NDARRAY_FLOAT) {
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
}
}
return mp_const_none; // we should never reach this point
}
#if ULAB_NUMPY_HAS_EQUAL | ULAB_NUMPY_HAS_NOTEQUAL
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;
}
#endif
#if ULAB_NUMPY_HAS_CLIP
//| def clip(
//| a: _ScalarOrArrayLike,
//| a_min: _ScalarOrArrayLike,
//| a_max: _ScalarOrArrayLike,
//| ) -> _ScalarOrNdArray:
//| """
//| Clips (limits) the values in an array.
//|
//| :param a: Scalar or array containing elements to clip.
//| :param a_min: Minimum value, it will be broadcast against ``a``.
//| :param a_max: Maximum value, it will be broadcast against ``a``.
//| :return:
//| A scalar or array with the elements of ``a``, but where
//| values < ``a_min`` are replaced with ``a_min``, and those
//| > ``a_max`` with ``a_max``.
//| """
//| ...
mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) {
// Note: this function could be made faster by implementing a single-loop comparison in
// RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we
// 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
if(mp_obj_is_int(x1) || mp_obj_is_float(x1)) {
mp_float_t v1 = mp_obj_get_float(x1);
mp_float_t v2 = mp_obj_get_float(x2);
mp_float_t v3 = mp_obj_get_float(x3);
if(v1 < v2) {
return x2;
} else if(v1 > v3) {
return x3;
} else {
return x1;
}
} else { // assume ndarrays
return compare_function(x2, compare_function(x1, x3, COMPARE_MINIMUM), COMPARE_MAXIMUM);
}
}
MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip);
#endif
#if ULAB_NUMPY_HAS_EQUAL
//| def equal(x: _ScalarOrArrayLike, y: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """
//| Returns ``x == y`` element-wise.
//|
//| :param x, y:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A boolean scalar or array with the element-wise result of ``x == y``.
//| """
//| ...
mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) {
return compare_equal_helper(x1, x2, COMPARE_EQUAL);
}
MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal);
#endif
#if ULAB_NUMPY_HAS_NOTEQUAL
//| def not_equal(
//| x: _ScalarOrArrayLike,
//| y: _ScalarOrArrayLike,
//| ) -> Union[_bool, ulab.numpy.ndarray]:
//| """
//| Returns ``x != y`` element-wise.
//|
//| :param x, y:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A boolean scalar or array with the element-wise result of ``x != y``.
//| """
//| ...
mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL);
}
MP_DEFINE_CONST_FUN_OBJ_2(compare_not_equal_obj, compare_not_equal);
#endif
#if ULAB_NUMPY_HAS_ISFINITE | ULAB_NUMPY_HAS_ISINF
static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
// mask should signify, whether the function is called from isinf (mask = 1),
// or from isfinite (mask = 0)
if(mp_obj_is_int(_x)) {
if(mask) {
return mp_const_false;
} else {
return mp_const_true;
}
} else if(mp_obj_is_float(_x)) {
mp_float_t x = mp_obj_get_float(_x);
if(isnan(x)) {
return mp_const_false;
}
if(mask) { // called from isinf
return isinf(x) ? mp_const_true : mp_const_false;
} else { // called from isfinite
return isinf(x) ? mp_const_false : mp_const_true;
}
} else if(mp_obj_is_type(_x, &ulab_ndarray_type)) {
ndarray_obj_t *x = MP_OBJ_TO_PTR(_x);
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL);
// At this point, results is all False
uint8_t *rarray = (uint8_t *)results->array;
if(x->dtype != NDARRAY_FLOAT) {
// int types can never be infinite...
if(!mask) {
// ...so flip all values in the array, if the function was called from isfinite
memset(rarray, 1, results->len);
}
return MP_OBJ_FROM_PTR(results);
}
uint8_t *xarray = (uint8_t *)x->array;
ITERATOR_HEAD();
mp_float_t value = *(mp_float_t *)xarray;
if(isnan(value)) {
*rarray++ = 0;
} else {
*rarray++ = isinf(value) ? mask : 1 - mask;
}
ITERATOR_TAIL(x, xarray);
return MP_OBJ_FROM_PTR(results);
} else {
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
return mp_const_none;
}
#endif
#if ULAB_NUMPY_HAS_ISFINITE
//| def isfinite(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
//| """
//| Tests element-wise for finiteness (i.e., it should not be infinity or a NaN).
//|
//| :param x: Input scalar or ndarray.
//| :return:
//| A boolean scalar or array with True where ``x`` is finite, and
//| False otherwise.
//| """
//| ...
mp_obj_t compare_isfinite(mp_obj_t _x) {
return compare_isinf_isfinite(_x, 0);
}
MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite);
#endif
#if ULAB_NUMPY_HAS_ISINF
//| def isinf(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
//| """
//| Tests element-wise for positive or negative infinity.
//|
//| :param x: Input scalar or ndarray.
//| :return:
//| A boolean scalar or array with True where ``x`` is positive or
//| negative infinity, and False otherwise.
//| """
//| ...
mp_obj_t compare_isinf(mp_obj_t _x) {
return compare_isinf_isfinite(_x, 1);
}
MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf);
#endif
#if ULAB_NUMPY_HAS_MAXIMUM
//| def maximum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """
//| Returns the element-wise maximum.
//|
//| :param x1, x2:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A scalar or array with the element-wise maximum of ``x1`` and ``x2``.
//| """
//| ...
mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) {
// extra round, so that we can return maximum(3, 4) properly
mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM);
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->dtype, ndarray->array, 0);
}
return result;
}
MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum);
#endif
#if ULAB_NUMPY_HAS_MINIMUM
//| def minimum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """
//| Returns the element-wise minimum.
//|
//| :param x1, x2:
//| Input scalar or array. If ``x.shape != y.shape`` they must
//| be broadcastable to a common shape (which becomes the
//| shape of the output.)
//| :return:
//| A scalar or array with the element-wise minimum of ``x1`` and ``x2``.
//| """
//| ...
mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) {
// extra round, so that we can return minimum(3, 4) properly
mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM);
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->dtype, ndarray->array, 0);
}
return result;
}
MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum);
#endif
#if ULAB_NUMPY_HAS_NONZERO
//| def nonzero(x: _ScalarOrArrayLike) -> ulab.numpy.ndarray:
//| """
//| Returns the indices of elements that are non-zero.
//|
//| :param x:
//| Input scalar or array. If ``x`` is a scalar, it is treated
//| as a single-element 1-d array.
//| :return:
//| An array of indices that are non-zero.
//| """
//| ...
mp_obj_t compare_nonzero(mp_obj_t x) {
ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
// since ndarray_new_linear_array calls m_new0, the content of zero is a single zero
ndarray_obj_t *zero = ndarray_new_linear_array(1, NDARRAY_UINT8);
uint8_t ndim = 0;
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
int32_t *x_strides = m_new(int32_t, ULAB_MAX_DIMS);
int32_t *zero_strides = m_new(int32_t, ULAB_MAX_DIMS);
// we don't actually have to inspect the outcome of ndarray_can_broadcast,
// because the right hand side is a linear array with a single element
ndarray_can_broadcast(ndarray_x, zero, &ndim, shape, x_strides, zero_strides);
// equal_obj is a Boolean ndarray
mp_obj_t equal_obj = ndarray_binary_equality(ndarray_x, zero, ndim, shape, x_strides, zero_strides, MP_BINARY_OP_NOT_EQUAL);
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(equal_obj);
// these are no longer needed, get rid of them
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, x_strides, ULAB_MAX_DIMS);
m_del(int32_t, zero_strides, ULAB_MAX_DIMS);
uint8_t *array = (uint8_t *)ndarray->array;
uint8_t *origin = (uint8_t *)ndarray->array;
// First, count the number of Trues:
uint16_t count = 0;
size_t indices[ULAB_MAX_DIMS];
#if ULAB_MAX_DIMS > 3
indices[3] = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
indices[2] = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
indices[1] = 0;
do {
#endif
indices[0] = 0;
do {
if(*array != 0) {
count++;
}
array += ndarray->strides[ULAB_MAX_DIMS - 1];
indices[0]++;
} while(indices[0] < ndarray->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
array += ndarray->strides[ULAB_MAX_DIMS - 2];
indices[1]++;
} while(indices[1] < ndarray->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
array += ndarray->strides[ULAB_MAX_DIMS - 3];
indices[2]++;
} while(indices[2] < ndarray->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
array += ndarray->strides[ULAB_MAX_DIMS - 4];
indices[3]++;
} while(indices[3] < ndarray->shape[ULAB_MAX_DIMS - 4]);
#endif
mp_obj_t *items = m_new(mp_obj_t, ndarray->ndim);
uint16_t *arrays[ULAB_MAX_DIMS];
for(uint8_t i = 0; i < ndarray->ndim; i++) {
ndarray_obj_t *item_array = ndarray_new_linear_array(count, NDARRAY_UINT16);
uint16_t *iarray = (uint16_t *)item_array->array;
arrays[ULAB_MAX_DIMS - 1 - i] = iarray;
items[ndarray->ndim - 1 - i] = MP_OBJ_FROM_PTR(item_array);
}
array = origin;
count = 0;
#if ULAB_MAX_DIMS > 3
indices[3] = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
indices[2] = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
indices[1] = 0;
do {
#endif
indices[0] = 0;
do {
if(*array != 0) {
for(uint8_t d = 0; d < ndarray->ndim; d++) {
arrays[ULAB_MAX_DIMS - 1 - d][count] = indices[d];
}
count++;
}
array += ndarray->strides[ULAB_MAX_DIMS - 1];
indices[0]++;
} while(indices[0] < ndarray->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
array += ndarray->strides[ULAB_MAX_DIMS - 2];
indices[1]++;
} while(indices[1] < ndarray->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
array += ndarray->strides[ULAB_MAX_DIMS - 3];
indices[2]++;
} while(indices[2] < ndarray->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
array += ndarray->strides[ULAB_MAX_DIMS - 4];
indices[3]++;
} while(indices[3] < ndarray->shape[ULAB_MAX_DIMS - 4]);
#endif
return mp_obj_new_tuple(ndarray->ndim, items);
}
MP_DEFINE_CONST_FUN_OBJ_1(compare_nonzero_obj, compare_nonzero);
#endif /* ULAB_NUMPY_HAS_NONZERO */
#if ULAB_NUMPY_HAS_WHERE
//| def where(
//| condition: _ScalarOrArrayLike,
//| x: _ScalarOrArrayLike,
//| y: _ScalarOrArrayLike,
//| ) -> ulab.numpy.ndarray:
//| """
//| Returns elements from ``x`` or ``y`` depending on ``condition``.
//|
//| :param condition:
//| Input scalar or array. If an element (or scalar) is truthy,
//| the corresponding element from ``x`` is chosen, otherwise
//| ``y`` is used. ``condition``, ``x`` and ``y`` must also be
//| broadcastable to the same shape (which becomes the output
//| shape.)
//| :param x, y:
//| Input scalar or array.
//| :return:
//| An array with elements from ``x`` when ``condition`` is
//| truthy, and ``y`` elsewhere.
//| """
//| ...
mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) {
// this implementation will work with ndarrays, and scalars only
ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0);
ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0);
ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0);
COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype)
int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS);
int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS);
int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS);
size_t *oshape = m_new(size_t, ULAB_MAX_DIMS);
uint8_t ndim;
// establish the broadcasting conditions first
// if any two of the arrays can be broadcast together, then
// the three arrays can also be broadcast together
if(!ndarray_can_broadcast(c, x, &ndim, oshape, cstrides, ystrides) ||
!ndarray_can_broadcast(c, y, &ndim, oshape, cstrides, ystrides) ||
!ndarray_can_broadcast(x, y, &ndim, oshape, xstrides, ystrides)) {
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
}
ndim = MAX(MAX(c->ndim, x->ndim), y->ndim);
for(uint8_t i = 1; i <= ndim; i++) {
cstrides[ULAB_MAX_DIMS - i] = c->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : c->strides[ULAB_MAX_DIMS - i];
xstrides[ULAB_MAX_DIMS - i] = x->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : x->strides[ULAB_MAX_DIMS - i];
ystrides[ULAB_MAX_DIMS - i] = y->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : y->strides[ULAB_MAX_DIMS - i];
oshape[ULAB_MAX_DIMS - i] = MAX(MAX(c->shape[ULAB_MAX_DIMS - i], x->shape[ULAB_MAX_DIMS - i]), y->shape[ULAB_MAX_DIMS - i]);
}
uint8_t out_dtype = ndarray_upcast_dtype(x->dtype, y->dtype);
ndarray_obj_t *out = ndarray_new_dense_ndarray(ndim, oshape, out_dtype);
mp_float_t (*cfunc)(void *) = ndarray_get_float_function(c->dtype);
mp_float_t (*xfunc)(void *) = ndarray_get_float_function(x->dtype);
mp_float_t (*yfunc)(void *) = ndarray_get_float_function(y->dtype);
mp_float_t (*ofunc)(void *, mp_float_t ) = ndarray_set_float_function(out->dtype);
uint8_t *oarray = (uint8_t *)out->array;
uint8_t *carray = (uint8_t *)c->array;
uint8_t *xarray = (uint8_t *)x->array;
uint8_t *yarray = (uint8_t *)y->array;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
mp_float_t value;
mp_float_t cvalue = cfunc(carray);
if(cvalue != MICROPY_FLOAT_CONST(0.0)) {
value = xfunc(xarray);
} else {
value = yfunc(yarray);
}
ofunc(oarray, value);
oarray += out->itemsize;
carray += cstrides[ULAB_MAX_DIMS - 1];
xarray += xstrides[ULAB_MAX_DIMS - 1];
yarray += ystrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < out->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
carray -= cstrides[ULAB_MAX_DIMS - 1] * c->shape[ULAB_MAX_DIMS-1];
carray += cstrides[ULAB_MAX_DIMS - 2];
xarray -= xstrides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
xarray += xstrides[ULAB_MAX_DIMS - 2];
yarray -= ystrides[ULAB_MAX_DIMS - 1] * y->shape[ULAB_MAX_DIMS-1];
yarray += ystrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < out->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
carray -= cstrides[ULAB_MAX_DIMS - 2] * c->shape[ULAB_MAX_DIMS-2];
carray += cstrides[ULAB_MAX_DIMS - 3];
xarray -= xstrides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
xarray += xstrides[ULAB_MAX_DIMS - 3];
yarray -= ystrides[ULAB_MAX_DIMS - 2] * y->shape[ULAB_MAX_DIMS-2];
yarray += ystrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < out->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
carray -= cstrides[ULAB_MAX_DIMS - 3] * c->shape[ULAB_MAX_DIMS-3];
carray += cstrides[ULAB_MAX_DIMS - 4];
xarray -= xstrides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
xarray += xstrides[ULAB_MAX_DIMS - 4];
yarray -= ystrides[ULAB_MAX_DIMS - 3] * y->shape[ULAB_MAX_DIMS-3];
yarray += ystrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < out->shape[ULAB_MAX_DIMS - 4]);
#endif
return MP_OBJ_FROM_PTR(out);
}
MP_DEFINE_CONST_FUN_OBJ_3(compare_where_obj, compare_where);
#endif

151
code/numpy/compare.h Normal file
View file

@ -0,0 +1,151 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*/
#ifndef _COMPARE_
#define _COMPARE_
#include "../ulab.h"
#include "../ndarray.h"
enum COMPARE_FUNCTION_TYPE {
COMPARE_EQUAL,
COMPARE_NOT_EQUAL,
COMPARE_MINIMUM,
COMPARE_MAXIMUM,
COMPARE_CLIP,
};
MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj);
MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj);
MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj);
MP_DECLARE_CONST_FUN_OBJ_2(compare_isinf_obj);
MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj);
MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj);
MP_DECLARE_CONST_FUN_OBJ_1(compare_nonzero_obj);
MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj);
MP_DECLARE_CONST_FUN_OBJ_3(compare_where_obj);
#if ULAB_MAX_DIMS == 1
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t l = 0;\
do {\
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
return MP_OBJ_FROM_PTR(results);\
#endif // ULAB_MAX_DIMS == 1
#if ULAB_MAX_DIMS == 2
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
return MP_OBJ_FROM_PTR(results);\
#endif // ULAB_MAX_DIMS == 2
#if ULAB_MAX_DIMS == 3
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < results->shape[ULAB_MAX_DIMS - 3]);\
return MP_OBJ_FROM_PTR(results);\
#endif // ULAB_MAX_DIMS == 3
#if ULAB_MAX_DIMS == 4
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < results->shape[ULAB_MAX_DIMS - 3]);\
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < results->shape[ULAB_MAX_DIMS - 4]);\
return MP_OBJ_FROM_PTR(results);\
#endif // ULAB_MAX_DIMS == 4
#define RUN_COMPARE_LOOP(dtype, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, ndim, shape, op) do {\
ndarray_obj_t *results = ndarray_new_dense_ndarray((ndim), (shape), (dtype));\
uint8_t *array = (uint8_t *)results->array;\
if((op) == COMPARE_MINIMUM) {\
COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, <);\
}\
if((op) == COMPARE_MAXIMUM) {\
COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, >);\
}\
} while(0)
#endif

1079
code/numpy/create.c Normal file

File diff suppressed because it is too large Load diff

89
code/numpy/create.h Normal file
View file

@ -0,0 +1,89 @@
/*
* 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-2021 Zoltán Vörös
*/
#ifndef _CREATE_
#define _CREATE_
#include "../ulab.h"
#include "../ndarray.h"
#if ULAB_NUMPY_HAS_ARANGE
mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_arange_obj);
#endif
#if ULAB_NUMPY_HAS_ASARRAY
mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_asarray_obj);
#endif
#if ULAB_NUMPY_HAS_CONCATENATE
mp_obj_t create_concatenate(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_concatenate_obj);
#endif
#if ULAB_NUMPY_HAS_DIAG
mp_obj_t create_diag(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_diag_obj);
#endif
#if ULAB_MAX_DIMS > 1
#if ULAB_NUMPY_HAS_EYE
mp_obj_t create_eye(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_eye_obj);
#endif
#endif
#if ULAB_NUMPY_HAS_FULL
mp_obj_t create_full(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_full_obj);
#endif
#if ULAB_NUMPY_HAS_LINSPACE
mp_obj_t create_linspace(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_linspace_obj);
#endif
#if ULAB_NUMPY_HAS_LOGSPACE
mp_obj_t create_logspace(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_logspace_obj);
#endif
#if ULAB_NUMPY_HAS_ONES
mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj);
#endif
#if ULAB_NUMPY_HAS_TAKE
mp_obj_t create_take(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_take_obj);
#endif
#if ULAB_NUMPY_HAS_ZEROS
mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj);
#endif
#if ULAB_NUMPY_HAS_FROMBUFFER
mp_obj_t create_frombuffer(size_t , const mp_obj_t *, mp_map_t *);
MP_DECLARE_CONST_FUN_OBJ_KW(create_frombuffer_obj);
#endif
#define ARANGE_LOOP(type_, ndarray, len, step, stop) \
({\
type_ *array = (type_ *)(ndarray)->array;\
for (size_t i = 0; i < (len) - 1; i++, (value) += (step)) {\
*array++ = (type_)(value);\
}\
*array = (type_)(stop);\
})
#endif

105
code/numpy/fft/fft.c Normal file
View file

@ -0,0 +1,105 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2024 Zoltán Vörös
* 2020 Scott Shawcroft for Adafruit Industries
* 2020 Taku Fukada
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "../carray/carray_tools.h"
#include "fft.h"
//| """Frequency-domain functions"""
//|
//| import ulab.numpy
//| import ulab.utils
//| def fft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
//| """
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
//| :param ulab.numpy.ndarray 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.utils.spectrogram`, which computes the magnitude of the fft,
//| rather than separately returning its real and imaginary parts."""
//| ...
//|
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
static mp_obj_t fft_fft(mp_obj_t arg) {
return fft_fft_ifft(arg, FFT_FFT);
}
MP_DEFINE_CONST_FUN_OBJ_1(fft_fft_obj, fft_fft);
#else
static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
if(n_args == 2) {
return fft_fft_ifft(n_args, args[0], args[1], FFT_FFT);
} else {
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_FFT);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
#endif
//| def ifft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
//| """
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
//| :param ulab.numpy.ndarray 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"""
//| ...
//|
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
static mp_obj_t fft_ifft(mp_obj_t arg) {
return fft_fft_ifft(arg, FFT_IFFT);
}
MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
#else
static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
NOT_IMPLEMENTED_FOR_COMPLEX()
if(n_args == 2) {
return fft_fft_ifft(n_args, args[0], args[1], FFT_IFFT);
} else {
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_IFFT);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
#endif
static const mp_rom_map_elem_t ulab_fft_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fft) },
{ MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&fft_fft_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifft), MP_ROM_PTR(&fft_ifft_obj) },
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table);
const mp_obj_module_t ulab_fft_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module);
#endif

30
code/numpy/fft/fft.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) 2019-2021 Zoltán Vörös
*/
#ifndef _FFT_
#define _FFT_
#include "../../ulab.h"
#include "../../ulab_tools.h"
#include "../../ndarray.h"
#include "fft_tools.h"
extern const mp_obj_module_t ulab_fft_module;
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
MP_DECLARE_CONST_FUN_OBJ_3(fft_fft_obj);
MP_DECLARE_CONST_FUN_OBJ_3(fft_ifft_obj);
#else
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj);
#endif
#endif

266
code/numpy/fft/fft_tools.c Normal file
View file

@ -0,0 +1,266 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2024 Zoltán Vörös
*/
#include <math.h>
#include <string.h>
#include "py/runtime.h"
#include "../../ndarray.h"
#include "../../ulab_tools.h"
#include "../carray/carray_tools.h"
#include "fft_tools.h"
#ifndef MP_PI
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
#endif
#ifndef MP_E
#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536)
#endif
/* Kernel implementation for the case, when ulab has no complex support
* The following function takes two arrays, namely, the real and imaginary
* parts of a complex array, and calculates the Fourier transform in place.
*
* The function is basically a modification of four1 from Numerical Recipes,
* has no dependencies beyond micropython itself (for the definition of mp_float_t),
* and can be used independent of ulab.
*/
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
/* Kernel implementation for the complex case. Data are contained in data as
data[0], data[1], data[2], data[3], .... , data[2n - 2], data[2n-1]
real[0], imag[0], real[1], imag[1], .... , real[n-1], imag[n-1]
In general
real[i] = data[2i]
imag[i] = data[2i+1]
*/
void fft_kernel(mp_float_t *data, size_t n, int isign) {
size_t j, m, mmax, istep;
mp_float_t tempr, tempi;
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
j = 0;
for(size_t i = 0; i < n; i++) {
if (j > i) {
SWAP(mp_float_t, data[2*i], data[2*j]);
SWAP(mp_float_t, data[2*i+1], data[2*j+1]);
}
m = n >> 1;
while (j >= m && m > 0) {
j -= m;
m >>= 1;
}
j += m;
}
mmax = 1;
while (n > mmax) {
istep = mmax << 1;
theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
wr = MICROPY_FLOAT_CONST(1.0);
wi = MICROPY_FLOAT_CONST(0.0);
for(m = 0; m < mmax; m++) {
for(size_t i = m; i < n; i += istep) {
j = i + mmax;
tempr = wr * data[2*j] - wi * data[2*j+1];
tempi = wr * data[2*j+1] + wi * data[2*j];
data[2*j] = data[2*i] - tempr;
data[2*j+1] = data[2*i+1] - tempi;
data[2*i] += tempr;
data[2*i+1] += tempi;
}
wtemp = wr;
wr = wr*wpr - wi*wpi + wr;
wi = wi*wpr + wtemp*wpi + wi;
}
mmax = istep;
}
}
/*
* The following function is a helper interface to the python side.
* It has been factored out from fft.c, so that the same argument parsing
* routine can be called from utils.spectrogram.
*/
mp_obj_t fft_fft_ifft(mp_obj_t data_in, uint8_t type) {
if(!mp_obj_is_type(data_in, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(data_in);
#if ULAB_MAX_DIMS > 1
if(in->ndim != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
}
#endif
size_t len = in->len;
// Check if input is of length of power of 2
if((len & (len-1)) != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
}
ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_COMPLEX);
mp_float_t *data = (mp_float_t *)out->array;
uint8_t *array = (uint8_t *)in->array;
if(in->dtype == NDARRAY_COMPLEX) {
uint8_t sz = 2 * sizeof(mp_float_t);
for(size_t i = 0; i < len; i++) {
memcpy(data, array, sz);
data += 2;
array += in->strides[ULAB_MAX_DIMS - 1];
}
} else {
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
for(size_t i = 0; i < len; i++) {
// real part; the imaginary part is 0, no need to assign
*data = func(array);
data += 2;
array += in->strides[ULAB_MAX_DIMS - 1];
}
}
data -= 2 * len;
if(type == FFT_FFT) {
fft_kernel(data, len, 1);
} else { // inverse transform
fft_kernel(data, len, -1);
// TODO: numpy accepts the norm keyword argument
for(size_t i = 0; i < 2 * len; i++) {
*data++ /= len;
}
}
return MP_OBJ_FROM_PTR(out);
}
#else /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) {
size_t j, m, mmax, istep;
mp_float_t tempr, tempi;
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
j = 0;
for(size_t i = 0; i < n; i++) {
if (j > i) {
SWAP(mp_float_t, real[i], real[j]);
SWAP(mp_float_t, imag[i], imag[j]);
}
m = n >> 1;
while (j >= m && m > 0) {
j -= m;
m >>= 1;
}
j += m;
}
mmax = 1;
while (n > mmax) {
istep = mmax << 1;
theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
wr = MICROPY_FLOAT_CONST(1.0);
wi = MICROPY_FLOAT_CONST(0.0);
for(m = 0; m < mmax; m++) {
for(size_t i = m; i < n; i += istep) {
j = i + mmax;
tempr = wr * real[j] - wi * imag[j];
tempi = wr * imag[j] + wi * real[j];
real[j] = real[i] - tempr;
imag[j] = imag[i] - tempi;
real[i] += tempr;
imag[i] += tempi;
}
wtemp = wr;
wr = wr*wpr - wi*wpi + wr;
wi = wi*wpr + wtemp*wpi + wi;
}
mmax = istep;
}
}
mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
}
if(n_args == 2) {
if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
}
}
ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re);
#if ULAB_MAX_DIMS > 1
if(re->ndim != 1) {
COMPLEX_DTYPE_NOT_IMPLEMENTED(re->dtype)
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
}
#endif
size_t len = re->len;
// Check if input is of length of power of 2
if((len & (len-1)) != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
}
ndarray_obj_t *out_re = ndarray_new_linear_array(len, NDARRAY_FLOAT);
mp_float_t *data_re = (mp_float_t *)out_re->array;
uint8_t *array = (uint8_t *)re->array;
mp_float_t (*func)(void *) = ndarray_get_float_function(re->dtype);
for(size_t i=0; i < len; i++) {
*data_re++ = func(array);
array += re->strides[ULAB_MAX_DIMS - 1];
}
data_re -= len;
ndarray_obj_t *out_im = ndarray_new_linear_array(len, NDARRAY_FLOAT);
mp_float_t *data_im = (mp_float_t *)out_im->array;
if(n_args == 2) {
ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im);
#if ULAB_MAX_DIMS > 1
if(im->ndim != 1) {
COMPLEX_DTYPE_NOT_IMPLEMENTED(im->dtype)
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
}
#endif
if (re->len != im->len) {
mp_raise_ValueError(MP_ERROR_TEXT("real and imaginary parts must be of equal length"));
}
array = (uint8_t *)im->array;
func = ndarray_get_float_function(im->dtype);
for(size_t i=0; i < len; i++) {
*data_im++ = func(array);
array += im->strides[ULAB_MAX_DIMS - 1];
}
data_im -= len;
}
if(type == FFT_FFT) {
fft_kernel(data_re, data_im, len, 1);
} else { // inverse transform
fft_kernel(data_re, data_im, len, -1);
// TODO: numpy accepts the norm keyword argument
for(size_t i=0; i < len; i++) {
*data_re++ /= len;
*data_im++ /= len;
}
}
mp_obj_t tuple[2];
tuple[0] = MP_OBJ_FROM_PTR(out_re);
tuple[1] = MP_OBJ_FROM_PTR(out_im);
return mp_obj_new_tuple(2, tuple);
}
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */

View file

@ -0,0 +1,27 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _FFT_TOOLS_
#define _FFT_TOOLS_
enum FFT_TYPE {
FFT_FFT,
FFT_IFFT,
};
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
void fft_kernel(mp_float_t *, size_t , int );
mp_obj_t fft_fft_ifft(mp_obj_t , uint8_t );
#else
void fft_kernel(mp_float_t *, mp_float_t *, size_t , int );
mp_obj_t fft_fft_ifft(size_t , mp_obj_t , mp_obj_t , uint8_t );
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
#endif /* _FFT_TOOLS_ */

132
code/numpy/filter.c Normal file
View file

@ -0,0 +1,132 @@
/*
* 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-2021 Zoltán Vörös
* 2020 Taku Fukada
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../ulab.h"
#include "../scipy/signal/signal.h"
#include "carray/carray_tools.h"
#include "filter.h"
#if ULAB_NUMPY_HAS_CONVOLVE
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_ROM_NONE } },
{ MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("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);
// deal with linear arrays only
#if ULAB_MAX_DIMS > 1
if((a->ndim != 1) || (c->ndim != 1)) {
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be linear arrays"));
}
#endif
size_t len_a = a->len;
size_t len_c = c->len;
if(len_a == 0 || len_c == 0) {
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must not be empty"));
}
int len = len_a + len_c - 1; // convolve mode "full"
int32_t off = len_c - 1;
uint8_t dtype = NDARRAY_FLOAT;
#if ULAB_SUPPORTS_COMPLEX
if((a->dtype == NDARRAY_COMPLEX) || (c->dtype == NDARRAY_COMPLEX)) {
dtype = NDARRAY_COMPLEX;
}
#endif
ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype);
mp_float_t *array = (mp_float_t *)ndarray->array;
uint8_t *aarray = (uint8_t *)a->array;
uint8_t *carray = (uint8_t *)c->array;
int32_t as = a->strides[ULAB_MAX_DIMS - 1] / a->itemsize;
int32_t cs = c->strides[ULAB_MAX_DIMS - 1] / c->itemsize;
#if ULAB_SUPPORTS_COMPLEX
if(dtype == NDARRAY_COMPLEX) {
mp_float_t a_real, a_imag;
mp_float_t c_real, c_imag = MICROPY_FLOAT_CONST(0.0);
for(int32_t k = -off; k < len-off; k++) {
mp_float_t accum_real = MICROPY_FLOAT_CONST(0.0);
mp_float_t accum_imag = MICROPY_FLOAT_CONST(0.0);
int32_t top_n = MIN(len_c, len_a - k);
int32_t bot_n = MAX(-k, 0);
for(int32_t n = bot_n; n < top_n; n++) {
int32_t idx_c = (len_c - n - 1) * cs;
int32_t idx_a = (n + k) * as;
if(a->dtype != NDARRAY_COMPLEX) {
a_real = ndarray_get_float_index(aarray, a->dtype, idx_a);
a_imag = MICROPY_FLOAT_CONST(0.0);
} else {
a_real = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a);
a_imag = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a + 1);
}
if(c->dtype != NDARRAY_COMPLEX) {
c_real = ndarray_get_float_index(carray, c->dtype, idx_c);
c_imag = MICROPY_FLOAT_CONST(0.0);
} else {
c_real = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c);
c_imag = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c + 1);
}
accum_real += a_real * c_real - a_imag * c_imag;
accum_imag += a_real * c_imag + a_imag * c_real;
}
*array++ = accum_real;
*array++ = accum_imag;
}
return MP_OBJ_FROM_PTR(ndarray);
}
#endif
for(int32_t k = -off; k < len-off; k++) {
mp_float_t accum = MICROPY_FLOAT_CONST(0.0);
int32_t top_n = MIN(len_c, len_a - k);
int32_t bot_n = MAX(-k, 0);
for(int32_t n = bot_n; n < top_n; n++) {
int32_t idx_c = (len_c - n - 1) * cs;
int32_t idx_a = (n + k) * as;
mp_float_t ai = ndarray_get_float_index(aarray, a->dtype, idx_a);
mp_float_t ci = ndarray_get_float_index(carray, c->dtype, idx_c);
accum += ai * ci;
}
*array++ = accum;
}
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve);
#endif

20
code/numpy/filter.h Normal file
View file

@ -0,0 +1,20 @@
/*
* 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-2021 Zoltán Vörös
*/
#ifndef _FILTER_
#define _FILTER_
#include "../ulab.h"
#include "../ndarray.h"
MP_DECLARE_CONST_FUN_OBJ_KW(filter_convolve_obj);
#endif

806
code/numpy/io/io.c Normal file
View file

@ -0,0 +1,806 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2022 Zoltán Vörös
*/
#include <math.h>
#include <string.h>
#include "py/builtin.h"
#include "py/formatfloat.h"
#include "py/obj.h"
#include "py/parsenum.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/vfs.h"
#include "../../ndarray.h"
#include "../../ulab_tools.h"
#include "io.h"
#define ULAB_IO_BUFFER_SIZE 128
#define ULAB_IO_CLIPBOARD_SIZE 32
#define ULAB_IO_MAX_ROWS 65535
#define ULAB_IO_NULL_ENDIAN 0
#define ULAB_IO_LITTLE_ENDIAN 1
#define ULAB_IO_BIG_ENDIAN 2
#if ULAB_NUMPY_HAS_LOAD
static void io_read_(mp_obj_t stream, const mp_stream_p_t *stream_p, char *buffer, const char *string, uint16_t len, int *error) {
size_t read = stream_p->read(stream, buffer, len, error);
bool fail = false;
if(read == len) {
if(string != NULL) {
if(memcmp(buffer, string, len) != 0) {
fail = true;
}
}
} else {
fail = true;
}
if(fail) {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, error);
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
}
}
static mp_obj_t io_load(mp_obj_t file) {
if(!mp_obj_is_str(file)) {
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
int error;
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
// test for endianness
uint16_t x = 1;
int8_t native_endianness = (x >> 8) == 1 ? ULAB_IO_BIG_ENDIAN : ULAB_IO_LITTLE_ENDIAN;
mp_obj_t open_args[2] = {
file,
MP_OBJ_NEW_QSTR(MP_QSTR_rb)
};
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
const mp_stream_p_t *stream_p = mp_get_stream(stream);
// read header
// magic string
io_read_(stream, stream_p, buffer, "\x93NUMPY", 6, &error);
// simply discard the version number
io_read_(stream, stream_p, buffer, NULL, 2, &error);
// header length, represented as a little endian uint16 (0x76, 0x00)
io_read_(stream, stream_p, buffer, NULL, 2, &error);
uint16_t header_length = buffer[1];
header_length <<= 8;
header_length += buffer[0];
// beginning of the dictionary describing the array
io_read_(stream, stream_p, buffer, "{'descr': '", 11, &error);
uint8_t dtype;
io_read_(stream, stream_p, buffer, NULL, 1, &error);
uint8_t endianness = ULAB_IO_NULL_ENDIAN;
if(*buffer == '<') {
endianness = ULAB_IO_LITTLE_ENDIAN;
} else if(*buffer == '>') {
endianness = ULAB_IO_BIG_ENDIAN;
}
io_read_(stream, stream_p, buffer, NULL, 2, &error);
if(memcmp(buffer, "u1", 2) == 0) {
dtype = NDARRAY_UINT8;
} else if(memcmp(buffer, "i1", 2) == 0) {
dtype = NDARRAY_INT8;
} else if(memcmp(buffer, "u2", 2) == 0) {
dtype = NDARRAY_UINT16;
} else if(memcmp(buffer, "i2", 2) == 0) {
dtype = NDARRAY_INT16;
}
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
else if(memcmp(buffer, "f4", 2) == 0) {
dtype = NDARRAY_FLOAT;
}
#else
else if(memcmp(buffer, "f8", 2) == 0) {
dtype = NDARRAY_FLOAT;
}
#endif
#if ULAB_SUPPORTS_COMPLEX
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
else if(memcmp(buffer, "c8", 2) == 0) {
dtype = NDARRAY_COMPLEX;
}
#else
else if(memcmp(buffer, "c16", 3) == 0) {
dtype = NDARRAY_COMPLEX;
}
#endif
#endif /* ULAB_SUPPORT_COPMLEX */
else {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
mp_raise_TypeError(MP_ERROR_TEXT("wrong dtype"));
}
io_read_(stream, stream_p, buffer, "', 'fortran_order': False, 'shape': (", 37, &error);
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
uint16_t bytes_to_read = MIN(ULAB_IO_BUFFER_SIZE, header_length - 51);
// bytes_to_read is 128 at most. This should be enough to contain a
// maximum of 4 size_t numbers plus the delimiters
io_read_(stream, stream_p, buffer, NULL, bytes_to_read, &error);
char *needle = buffer;
uint8_t ndim = 0;
// find out the number of dimensions by counting the commas in the string
while(1) {
if(*needle == ',') {
ndim++;
if(needle[1] == ')') {
break;
}
} else if((*needle == ')') && (ndim > 0)) {
ndim++;
break;
}
needle++;
}
needle = buffer;
for(uint8_t i = 0; i < ndim; i++) {
size_t number = 0;
// trivial number parsing here
while(1) {
if((*needle == ' ') || (*needle == '\t')) {
needle++;
}
if((*needle > 47) && (*needle < 58)) {
number = number * 10 + (*needle - 48);
} else if((*needle == ',') || (*needle == ')')) {
break;
}
else {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
}
needle++;
}
needle++;
shape[ULAB_MAX_DIMS - ndim + i] = number;
}
// strip the rest of the header
if((bytes_to_read + 51) < header_length) {
io_read_(stream, stream_p, buffer, NULL, header_length - (bytes_to_read + 51), &error);
}
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(ndim, shape, dtype);
char *array = (char *)ndarray->array;
size_t read = stream_p->read(stream, array, ndarray->len * ndarray->itemsize, &error);
if(read != ndarray->len * ndarray->itemsize) {
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
}
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
m_del(char, buffer, ULAB_IO_BUFFER_SIZE);
// swap the bytes, if necessary
if((native_endianness != endianness) && (dtype != NDARRAY_UINT8) && (dtype != NDARRAY_INT8)) {
uint8_t sz = ndarray->itemsize;
char *tmpbuff = NULL;
#if ULAB_SUPPORTS_COMPLEX
if(dtype == NDARRAY_COMPLEX) {
// work with the floating point real and imaginary parts
sz /= 2;
tmpbuff = m_new(char, sz);
for(size_t i = 0; i < ndarray->len; i++) {
for(uint8_t k = 0; k < 2; k++) {
tmpbuff += sz;
for(uint8_t j = 0; j < sz; j++) {
memcpy(--tmpbuff, array++, 1);
}
memcpy(array-sz, tmpbuff, sz);
}
}
} else {
#endif
tmpbuff = m_new(char, sz);
for(size_t i = 0; i < ndarray->len; i++) {
tmpbuff += sz;
for(uint8_t j = 0; j < sz; j++) {
memcpy(--tmpbuff, array++, 1);
}
memcpy(array-sz, tmpbuff, sz);
}
#if ULAB_SUPPORTS_COMPLEX
}
#endif
m_del(char, tmpbuff, sz);
}
m_del(size_t, shape, ULAB_MAX_DIMS);
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_1(io_load_obj, io_load);
#endif /* ULAB_NUMPY_HAS_LOAD */
#if ULAB_NUMPY_HAS_LOADTXT
static void io_assign_value(const char *clipboard, uint8_t len, ndarray_obj_t *ndarray, size_t *idx, uint8_t dtype) {
#if MICROPY_PY_BUILTINS_COMPLEX
mp_obj_t value = mp_parse_num_decimal(clipboard, len, false, false, NULL);
#else
mp_obj_t value = mp_parse_num_float(clipboard, len, false, NULL);
#endif
if(dtype != NDARRAY_FLOAT) {
mp_float_t _value = mp_obj_get_float(value);
value = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(_value));
}
ndarray_set_value(dtype, ndarray->array, (*idx)++, value);
}
static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_max_rows, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = -1 } },
{ MP_QSTR_usecols, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
{ MP_QSTR_skiprows, 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);
mp_obj_t open_args[2] = {
args[0].u_obj,
MP_OBJ_NEW_QSTR(MP_QSTR_r)
};
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
const mp_stream_p_t *stream_p = mp_get_stream(stream);
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
int error;
char delimiter = ' ';
if(args[1].u_obj != mp_const_none) {
size_t _len;
char *_delimiter = m_new(char, 8);
_delimiter = (char *)mp_obj_str_get_data(args[1].u_obj, &_len);
delimiter = _delimiter[0];
}
char comment_char = '#';
if(args[2].u_obj != mp_const_none) {
size_t _len;
char *_comment_char = m_new(char, 8);
_comment_char = (char *)mp_obj_str_get_data(args[2].u_obj, &_len);
comment_char = _comment_char[0];
}
uint16_t skiprows = args[6].u_int;
uint16_t max_rows = ULAB_IO_MAX_ROWS;
if((args[3].u_int > 0) && (args[3].u_int < ULAB_IO_MAX_ROWS)) {
max_rows = args[3].u_int + skiprows;
}
uint16_t *cols = NULL;
uint8_t used_columns = 0;
if(args[4].u_obj != mp_const_none) {
if(mp_obj_is_int(args[4].u_obj)) {
used_columns = 1;
cols = m_new(uint16_t, used_columns);
cols[0] = (uint16_t)mp_obj_get_int(args[4].u_obj);
} else {
#if ULAB_MAX_DIMS == 1
mp_raise_ValueError(MP_ERROR_TEXT("usecols keyword must be specified"));
#else
// assume that the argument is an iterable
used_columns = (uint16_t)mp_obj_get_int(mp_obj_len(args[4].u_obj));
cols = m_new(uint16_t, used_columns);
mp_obj_iter_buf_t iter_buf;
mp_obj_t item, iterable = mp_getiter(args[4].u_obj, &iter_buf);
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
*cols++ = (uint16_t)mp_obj_get_int(item);
}
cols -= used_columns;
#endif
}
}
uint8_t dtype = args[5].u_int;
// count the columns and rows
// we actually count only the rows and the items, and assume that
// the number of columns can be gotten by means of a simple division,
// i.e., that each row has the same number of columns
char *offset;
uint16_t rows = 0, items = 0, all_rows = 0;
uint8_t read;
uint8_t len = 0;
do {
read = (uint8_t)stream_p->read(stream, buffer, ULAB_IO_BUFFER_SIZE - 1, &error);
buffer[read] = '\0';
offset = buffer;
while(*offset != '\0') {
while(*offset == comment_char) {
// clear the line till the end, or the buffer's end
while((*offset != '\0')) {
offset++;
if(*offset == '\n') {
offset++;
all_rows++;
break;
}
}
}
// catch whitespaces here: if these are not on a comment line, then they delimit a number
if(*offset == '\n') {
all_rows++;
if(all_rows > skiprows) {
rows++;
items++;
len = 0;
}
if(all_rows == max_rows) {
break;
}
}
if((*offset == ' ') || (*offset == '\t') || (*offset == '\v') ||
(*offset == '\f') || (*offset == '\r') || (*offset == delimiter)) {
offset++;
while((*offset == ' ') || (*offset == '\t') || (*offset == '\v') || (*offset == '\f') || (*offset == '\r')) {
offset++;
}
if(len > 0) {
if(all_rows >= skiprows) {
items++;
}
len = 0;
}
} else {
offset++;
len++;
}
}
} while((read > 0) && (all_rows < max_rows));
if(rows == 0) {
mp_raise_ValueError(MP_ERROR_TEXT("empty file"));
}
uint16_t columns = items / rows;
if(columns < used_columns) {
mp_raise_ValueError(MP_ERROR_TEXT("usecols is too high"));
}
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
#if ULAB_MAX_DIMS == 1
shape[0] = rows;
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(1, shape, dtype);
#else
if(args[4].u_obj == mp_const_none) {
shape[ULAB_MAX_DIMS - 1] = columns;
} else {
shape[ULAB_MAX_DIMS - 1] = used_columns;
}
shape[ULAB_MAX_DIMS - 2] = rows;
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(2, shape, dtype);
#endif
struct mp_stream_seek_t seek_s;
seek_s.offset = 0;
seek_s.whence = MP_SEEK_SET;
stream_p->ioctl(stream, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
char *clipboard = m_new(char, ULAB_IO_CLIPBOARD_SIZE);
char *clipboard_origin = clipboard;
rows = 0;
columns = 0;
len = 0;
size_t idx = 0;
do {
read = stream_p->read(stream, buffer, ULAB_IO_BUFFER_SIZE - 1, &error);
buffer[read] = '\0';
offset = buffer;
while(*offset != '\0') {
while(*offset == comment_char) {
// clear the line till the end, or the buffer's end
while((*offset != '\0')) {
offset++;
if(*offset == '\n') {
rows++;
offset++;
break;
}
}
}
if(rows == max_rows) {
break;
}
if((*offset == ' ') || (*offset == '\t') || (*offset == '\v') ||
(*offset == '\f') || (*offset == '\r') || (*offset == '\n') || (*offset == delimiter)) {
offset++;
while((*offset == ' ') || (*offset == '\t') || (*offset == '\v') ||
(*offset == '\f') || (*offset == '\r') || (*offset == '\n')) {
offset++;
}
if(len > 0) {
clipboard = clipboard_origin;
if(rows >= skiprows) {
#if ULAB_MAX_DIMS == 1
if(columns == cols[0]) {
io_assign_value(clipboard, len, ndarray, &idx, dtype);
}
#else
if(args[4].u_obj == mp_const_none) {
io_assign_value(clipboard, len, ndarray, &idx, dtype);
} else {
for(uint8_t c = 0; c < used_columns; c++) {
if(columns == cols[c]) {
io_assign_value(clipboard, len, ndarray, &idx, dtype);
break;
}
}
}
#endif
}
columns++;
len = 0;
if(offset[-1] == '\n') {
columns = 0;
rows++;
}
}
} else {
*clipboard++ = *offset++;
len++;
}
}
} while((read > 0) && (rows < max_rows));
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(char, buffer, ULAB_IO_BUFFER_SIZE);
m_del(char, clipboard, ULAB_IO_CLIPBOARD_SIZE);
m_del(uint16_t, cols, used_columns);
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_KW(io_loadtxt_obj, 1, io_loadtxt);
#endif /* ULAB_NUMPY_HAS_LOADTXT */
#if ULAB_NUMPY_HAS_SAVE
static uint8_t io_sprintf(char *buffer, const char *comma, size_t x) {
uint8_t offset = 1;
char *buf = buffer;
// our own minimal implementation of sprintf for size_t types
// this is required on systems, where sprintf is not available
// find out, how many characters are required
// we could call log10 here...
for(size_t i = 10; i < 100000000; i *= 10) {
if(x < i) {
break;
}
buf++;
}
while(x > 0) {
uint8_t rem = x % 10;
*buf-- = '0' + rem;
x /= 10;
offset++;
}
buf += offset;
while(*comma != '\0') {
*buf++ = *comma++;
offset++;
}
return offset - 1;
}
static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) {
if(!mp_obj_is_str(file) || !mp_obj_is_type(ndarray_, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(ndarray_);
int error;
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
uint8_t offset = 0;
// test for endianness
uint16_t x = 1;
int8_t native_endianness = (x >> 8) == 1 ? '>' : '<';
mp_obj_t open_args[2] = {
file,
MP_OBJ_NEW_QSTR(MP_QSTR_wb)
};
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
const mp_stream_p_t *stream_p = mp_get_stream(stream);
// write header;
// magic string + header length, which is always 128 - 10 = 118, represented as a little endian uint16 (0x76, 0x00)
// + beginning of the dictionary describing the array
memcpy(buffer, "\x93NUMPY\x01\x00\x76\x00{'descr': '", 21);
offset += 21;
buffer[offset] = native_endianness;
if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) {
// for single-byte data, the endianness doesn't matter
buffer[offset] = '|';
}
offset++;
switch(ndarray->dtype) {
case NDARRAY_UINT8:
memcpy(buffer+offset, "u1", 2);
break;
case NDARRAY_INT8:
memcpy(buffer+offset, "i1", 2);
break;
case NDARRAY_UINT16:
memcpy(buffer+offset, "u2", 2);
break;
case NDARRAY_INT16:
memcpy(buffer+offset, "i2", 2);
break;
case NDARRAY_FLOAT:
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
memcpy(buffer+offset, "f4", 2);
#else
memcpy(buffer+offset, "f8", 2);
#endif
break;
#if ULAB_SUPPORTS_COMPLEX
case NDARRAY_COMPLEX:
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
memcpy(buffer+offset, "c8", 2);
#else
memcpy(buffer+offset, "c16", 3);
offset++;
#endif
break;
#endif
}
offset += 2;
memcpy(buffer+offset, "', 'fortran_order': False, 'shape': (", 37);
offset += 37;
if(ndarray->ndim == 1) {
offset += io_sprintf(buffer+offset, ",\0", ndarray->shape[ULAB_MAX_DIMS - 1]);
} else {
for(uint8_t i = ndarray->ndim; i > 1; i--) {
offset += io_sprintf(buffer+offset, ", \0", ndarray->shape[ULAB_MAX_DIMS - i]);
}
offset += io_sprintf(buffer+offset, "\0", ndarray->shape[ULAB_MAX_DIMS - 1]);
}
memcpy(buffer+offset, "), }", 4);
offset += 4;
// pad with space till the very end
memset(buffer+offset, 32, ULAB_IO_BUFFER_SIZE - offset - 1);
buffer[ULAB_IO_BUFFER_SIZE - 1] = '\n';
stream_p->write(stream, buffer, ULAB_IO_BUFFER_SIZE, &error);
// write the array data
uint8_t sz = ndarray->itemsize;
offset = 0;
uint8_t *array = (uint8_t *)ndarray->array;
ITERATOR_HEAD();
memcpy(buffer+offset, array, sz);
offset += sz;
if(offset == ULAB_IO_BUFFER_SIZE) {
stream_p->write(stream, buffer, offset, &error);
offset = 0;
}
ITERATOR_TAIL(ndarray, array);
stream_p->write(stream, buffer, offset, &error);
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
m_del(char, buffer, ULAB_IO_BUFFER_SIZE);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(io_save_obj, io_save);
#endif /* ULAB_NUMPY_HAS_SAVE */
#if ULAB_NUMPY_HAS_SAVETXT
static int8_t io_format_float(ndarray_obj_t *ndarray, mp_float_t (*func)(void *), uint8_t *array, char *buffer, const char *delimiter) {
// own implementation of float formatting for platforms that don't have sprintf
int8_t offset = 0;
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
const int precision = 6;
#else
const int precision = 7;
#endif
#else
const int precision = 16;
#endif
#if ULAB_SUPPORTS_COMPLEX
if(ndarray->dtype == NDARRAY_COMPLEX) {
mp_float_t real = func(array);
mp_float_t imag = func(array + ndarray->itemsize / 2);
offset = mp_format_float(real, buffer, ULAB_IO_BUFFER_SIZE, 'f', precision, 'j');
if(imag >= MICROPY_FLOAT_CONST(0.0)) {
buffer[offset++] = '+';
} else {
buffer[offset++] = '-';
}
offset += mp_format_float(-imag, &buffer[offset], ULAB_IO_BUFFER_SIZE, 'f', precision, 'j');
}
#endif
offset = (uint8_t)mp_format_float(func(array), buffer, ULAB_IO_BUFFER_SIZE, 'f', precision, '\0');
#if ULAB_SUPPORTS_COMPLEX
if(ndarray->dtype != NDARRAY_COMPLEX) {
// complexes end with a 'j', floats with a '\0', so we have to wind back by one character
offset--;
}
#endif
while(*delimiter != '\0') {
buffer[offset++] = *delimiter++;
}
return offset;
}
static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_header, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_footer, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_str(args[0].u_obj) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
#if ULAB_MAX_DIMS > 2
if(ndarray->ndim > 2) {
mp_raise_ValueError(MP_ERROR_TEXT("array has too many dimensions"));
}
#endif
mp_obj_t open_args[2] = {
args[0].u_obj,
MP_OBJ_NEW_QSTR(MP_QSTR_w)
};
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
const mp_stream_p_t *stream_p = mp_get_stream(stream);
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
int error;
size_t len_comment;
char *comments;
if(mp_obj_is_str(args[5].u_obj)) {
const char *_comments = mp_obj_str_get_data(args[5].u_obj, &len_comment);
comments = (char *)_comments;
} else {
len_comment = 2;
comments = m_new(char, len_comment);
comments[0] = '#';
comments[1] = ' ';
}
if(mp_obj_is_str(args[3].u_obj)) {
size_t _len;
const char *header = mp_obj_str_get_data(args[3].u_obj, &_len);
stream_p->write(stream, comments, len_comment, &error);
// We can't write the header in the single chunk, for it might contain line breaks
for(size_t i = 0; i < _len; header++, i++) {
stream_p->write(stream, header, 1, &error);
if((*header == '\n') && (i < _len)) {
stream_p->write(stream, comments, len_comment, &error);
}
}
stream_p->write(stream, "\n", 1, &error);
}
uint8_t *array = (uint8_t *)ndarray->array;
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
char *delimiter = m_new(char, 8);
if(ndarray->ndim == 1) {
delimiter[0] = '\n';
delimiter[1] = '\0';
} else if(args[2].u_obj == mp_const_none) {
delimiter[0] = ' ';
delimiter[1] = '\0';
} else {
size_t delimiter_len;
delimiter = (char *)mp_obj_str_get_data(args[2].u_obj, &delimiter_len);
}
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
int8_t chars = io_format_float(ndarray, func, array, buffer, l == ndarray->shape[ULAB_MAX_DIMS - 1] - 1 ? "\n" : delimiter);
if(chars > 0) {
stream_p->write(stream, buffer, chars, &error);
}
array += ndarray->strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
array += ndarray->strides[ULAB_MAX_DIMS - 2];
k++;
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
#endif
if(mp_obj_is_str(args[4].u_obj)) { // footer string
size_t _len;
const char *footer = mp_obj_str_get_data(args[4].u_obj, &_len);
stream_p->write(stream, comments, len_comment, &error);
// We can't write the header in the single chunk, for it might contain line breaks
for(size_t i = 0; i < _len; footer++, i++) {
stream_p->write(stream, footer, 1, &error);
if((*footer == '\n') && (i < _len)) {
stream_p->write(stream, comments, len_comment, &error);
}
}
stream_p->write(stream, "\n", 1, &error);
}
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(io_savetxt_obj, 2, io_savetxt);
#endif /* ULAB_NUMPY_HAS_SAVETXT */

19
code/numpy/io/io.h Normal file
View file

@ -0,0 +1,19 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2022 Zoltán Vörös
*/
#ifndef _ULAB_IO_
#define _ULAB_IO_
MP_DECLARE_CONST_FUN_OBJ_1(io_load_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(io_loadtxt_obj);
MP_DECLARE_CONST_FUN_OBJ_2(io_save_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(io_savetxt_obj);
#endif

542
code/numpy/linalg/linalg.c Normal file
View file

@ -0,0 +1,542 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
* 2020 Scott Shawcroft for Adafruit Industries
* 2020 Roberto Colistete Jr.
* 2020 Taku Fukada
*
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../../ulab.h"
#include "../../ulab_tools.h"
#include "../carray/carray_tools.h"
#include "linalg.h"
#if ULAB_NUMPY_HAS_LINALG_MODULE
//|
//| import ulab.numpy
//|
//| """Linear algebra functions"""
//|
#if ULAB_MAX_DIMS > 1
//| def cholesky(A: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| :param ~ulab.numpy.ndarray A: a positive definite, symmetric square matrix
//| :return ~ulab.numpy.ndarray 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 *ndarray = tools_object_is_square(oin);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT);
mp_float_t *Larray = (mp_float_t *)L->array;
size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
uint8_t *array = (uint8_t *)ndarray->array;
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
for(size_t m=0; m < N; m++) { // rows
for(size_t n=0; n < N; n++) { // columns
*Larray++ = func(array);
array += ndarray->strides[ULAB_MAX_DIMS - 1];
}
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
array += ndarray->strides[ULAB_MAX_DIMS - 2];
}
Larray -= N*N;
// make sure the matrix is symmetric
for(size_t m=0; m < N; m++) { // rows
for(size_t n=m+1; n < N; n++) { // columns
// compare entry (m, n) to (n, m)
if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(Larray[m * N + n] - Larray[n * N + m])) {
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric"));
}
}
}
// this is actually not needed, but Cholesky in numpy returns the lower triangular matrix
for(size_t i=0; i < N; i++) { // rows
for(size_t j=i+1; j < N; j++) { // columns
Larray[i*N + j] = MICROPY_FLOAT_CONST(0.0);
}
}
mp_float_t sum = 0.0;
for(size_t i=0; i < N; i++) { // rows
for(size_t j=0; j <= i; j++) { // columns
sum = Larray[i * N + j];
for(size_t k=0; k < j; k++) {
sum -= Larray[i * N + k] * Larray[j * N + k];
}
if(i == j) {
if(sum <= MICROPY_FLOAT_CONST(0.0)) {
mp_raise_ValueError(MP_ERROR_TEXT("matrix is not positive definite"));
} else {
Larray[i * N + i] = MICROPY_FLOAT_C_FUN(sqrt)(sum);
}
} else {
Larray[i * N + j] = sum / Larray[j * N + j];
}
}
}
return MP_OBJ_FROM_PTR(L);
}
MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky);
//| def det(m: ulab.numpy.ndarray) -> 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 *ndarray = tools_object_is_square(oin);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
uint8_t *array = (uint8_t *)ndarray->array;
size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
mp_float_t *tmp = m_new(mp_float_t, N * N);
for(size_t m=0; m < N; m++) { // rows
for(size_t n=0; n < N; n++) { // columns
*tmp++ = ndarray_get_float_value(array, ndarray->dtype);
array += ndarray->strides[ULAB_MAX_DIMS - 1];
}
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
array += ndarray->strides[ULAB_MAX_DIMS - 2];
}
// re-wind the pointer
tmp -= N*N;
mp_float_t c;
mp_float_t det_sign = 1.0;
for(size_t m=0; m < N-1; m++){
if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m * (N+1)]) < LINALG_EPSILON) {
size_t m1 = m + 1;
for(; m1 < N; m1++) {
if(!(MICROPY_FLOAT_C_FUN(fabs)(tmp[m1*N+m]) < LINALG_EPSILON)) {
//look for a line to swap
for(size_t m2=0; m2 < N; m2++) {
mp_float_t swapVal = tmp[m*N+m2];
tmp[m*N+m2] = tmp[m1*N+m2];
tmp[m1*N+m2] = swapVal;
}
det_sign = -det_sign;
break;
}
}
if (m1 >= N) {
m_del(mp_float_t, tmp, N * N);
return mp_obj_new_float(0.0);
}
}
for(size_t n=0; n < N; n++) {
if(m != n) {
c = tmp[N * n + m] / tmp[m * (N+1)];
for(size_t k=0; k < N; k++){
tmp[N * n + k] -= c * tmp[N * m + k];
}
}
}
}
mp_float_t det = det_sign;
for(size_t m=0; m < N; m++){
det *= tmp[m * (N+1)];
}
m_del(mp_float_t, tmp, N * N);
return mp_obj_new_float(det);
}
MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
#endif
#if ULAB_MAX_DIMS > 1
//| def eig(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
//| """
//| :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 = tools_object_is_square(oin);
COMPLEX_DTYPE_NOT_IMPLEMENTED(in->dtype)
uint8_t *iarray = (uint8_t *)in->array;
size_t S = in->shape[ULAB_MAX_DIMS - 1];
mp_float_t *array = m_new(mp_float_t, S*S);
for(size_t i=0; i < S; i++) { // rows
for(size_t j=0; j < S; j++) { // columns
*array++ = ndarray_get_float_value(iarray, in->dtype);
iarray += in->strides[ULAB_MAX_DIMS - 1];
}
iarray -= in->strides[ULAB_MAX_DIMS - 1] * S;
iarray += in->strides[ULAB_MAX_DIMS - 2];
}
array -= S * S;
// make sure the matrix is symmetric
for(size_t m=0; m < S; m++) {
for(size_t n=m+1; n < S; n++) {
// compare entry (m, n) to (n, m)
// TODO: this must probably be scaled!
if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n] - array[n * S + m])) {
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric"));
}
}
}
// if we got this far, then the matrix will be symmetric
ndarray_obj_t *eigenvectors = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, S, S), NDARRAY_FLOAT);
mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array;
size_t iterations = linalg_jacobi_rotations(array, eigvectors, S);
if(iterations == 0) {
// the computation did not converge; numpy raises LinAlgError
m_del(mp_float_t, array, in->len);
mp_raise_ValueError(MP_ERROR_TEXT("iterations did not converge"));
}
ndarray_obj_t *eigenvalues = ndarray_new_linear_array(S, NDARRAY_FLOAT);
mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array;
for(size_t i=0; i < S; i++) {
eigvalues[i] = array[i * (S + 1)];
}
m_del(mp_float_t, array, in->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 MP_OBJ_FROM_PTR(tuple);
}
MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
//| def inv(m: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| :param ~ulab.numpy.ndarray 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 *ndarray = tools_object_is_square(o_in);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
uint8_t *array = (uint8_t *)ndarray->array;
size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT);
mp_float_t *iarray = (mp_float_t *)inverted->array;
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
for(size_t i=0; i < N; i++) { // rows
for(size_t j=0; j < N; j++) { // columns
*iarray++ = func(array);
array += ndarray->strides[ULAB_MAX_DIMS - 1];
}
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
array += ndarray->strides[ULAB_MAX_DIMS - 2];
}
// re-wind the pointer
iarray -= N*N;
if(!linalg_invert_matrix(iarray, N)) {
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular"));
}
return MP_OBJ_FROM_PTR(inverted);
}
MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
#endif
//| def norm(x: ulab.numpy.ndarray) -> float:
//| """
//| :param ~ulab.numpy.ndarray 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(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
{ MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t x = args[0].u_obj;
mp_obj_t axis = args[1].u_obj;
mp_float_t dot = 0.0, value;
size_t count = 1;
if(mp_obj_is_type(x, &mp_type_tuple) || mp_obj_is_type(x, &mp_type_list) || mp_obj_is_type(x, &mp_type_range)) {
mp_obj_iter_buf_t iter_buf;
mp_obj_t item, iterable = mp_getiter(x, &iter_buf);
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
value = mp_obj_get_float(item);
// we could simply take the sum of value ** 2,
// but this method is numerically stable
dot = dot + (value * value - dot) / count++;
}
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1)));
} else if(mp_obj_is_type(x, &ulab_ndarray_type)) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
uint8_t *array = (uint8_t *)ndarray->array;
// always get a float, so that we don't have to resolve the dtype later
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
ndarray_obj_t *results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT);
mp_float_t *rarray = (mp_float_t *)results->array;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
if(axis != mp_const_none) {
count = 1;
dot = 0.0;
}
do {
value = func(array);
dot = dot + (value * value - dot) / count++;
array += _shape_strides.strides[0];
l++;
} while(l < _shape_strides.shape[0]);
*rarray = MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1));
#if ULAB_MAX_DIMS > 1
rarray += _shape_strides.increment;
array -= _shape_strides.strides[0] * _shape_strides.shape[0];
array += _shape_strides.strides[ULAB_MAX_DIMS - 1];
k++;
} while(k < _shape_strides.shape[ULAB_MAX_DIMS - 1]);
#endif
#if ULAB_MAX_DIMS > 2
array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1];
array += _shape_strides.strides[ULAB_MAX_DIMS - 2];
j++;
} while(j < _shape_strides.shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 3
array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2];
array += _shape_strides.strides[ULAB_MAX_DIMS - 3];
i++;
} while(i < _shape_strides.shape[ULAB_MAX_DIMS - 3]);
#endif
if(results->ndim == 0) {
return mp_obj_new_float(*rarray);
}
return MP_OBJ_FROM_PTR(results);
}
return mp_const_none; // we should never reach this point
}
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_norm_obj, 1, linalg_norm);
#if ULAB_MAX_DIMS > 1
//| def qr(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
//| """
//| :param m: a matrix
//| :return tuple (Q, R):
//|
//| Factor the matrix a as QR, where Q is orthonormal and R is upper-triangular.
//| """
//| ...
//|
static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_mode, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_QSTR(MP_QSTR_reduced) } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("operation is defined for ndarrays only"));
}
ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
if(source->ndim != 2) {
mp_raise_ValueError(MP_ERROR_TEXT("operation is defined for 2D arrays only"));
}
size_t m = source->shape[ULAB_MAX_DIMS - 2]; // rows
size_t n = source->shape[ULAB_MAX_DIMS - 1]; // columns
ndarray_obj_t *Q = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m, m), NDARRAY_FLOAT);
ndarray_obj_t *R = ndarray_new_dense_ndarray(2, source->shape, NDARRAY_FLOAT);
mp_float_t *qarray = (mp_float_t *)Q->array;
mp_float_t *rarray = (mp_float_t *)R->array;
// simply copy the entries of source to a float array
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
uint8_t *sarray = (uint8_t *)source->array;
for(size_t i = 0; i < m; i++) {
for(size_t j = 0; j < n; j++) {
*rarray++ = func(sarray);
sarray += source->strides[ULAB_MAX_DIMS - 1];
}
sarray -= n * source->strides[ULAB_MAX_DIMS - 1];
sarray += source->strides[ULAB_MAX_DIMS - 2];
}
rarray -= m * n;
// start with the unit matrix
for(size_t i = 0; i < m; i++) {
qarray[i * (m + 1)] = 1.0;
}
for(size_t j = 0; j < n; j++) { // columns
for(size_t i = m - 1; i > j; i--) { // rows
mp_float_t c, s;
// Givens matrix: note that numpy uses a strange form of the rotation
// [[c s],
// [s -c]]
if(MICROPY_FLOAT_C_FUN(fabs)(rarray[i * n + j]) < LINALG_EPSILON) { // r[i, j]
c = (rarray[(i - 1) * n + j] >= MICROPY_FLOAT_CONST(0.0)) ? MICROPY_FLOAT_CONST(1.0) : MICROPY_FLOAT_CONST(-1.0); // r[i-1, j]
s = 0.0;
} else if(MICROPY_FLOAT_C_FUN(fabs)(rarray[(i - 1) * n + j]) < LINALG_EPSILON) { // r[i-1, j]
c = 0.0;
s = (rarray[i * n + j] >= MICROPY_FLOAT_CONST(0.0)) ? MICROPY_FLOAT_CONST(-1.0) : MICROPY_FLOAT_CONST(1.0); // r[i, j]
} else {
mp_float_t t, u;
if(MICROPY_FLOAT_C_FUN(fabs)(rarray[(i - 1) * n + j]) > MICROPY_FLOAT_C_FUN(fabs)(rarray[i * n + j])) { // r[i-1, j], r[i, j]
t = rarray[i * n + j] / rarray[(i - 1) * n + j]; // r[i, j]/r[i-1, j]
u = MICROPY_FLOAT_C_FUN(sqrt)(1 + t * t);
c = MICROPY_FLOAT_CONST(-1.0) / u;
s = c * t;
} else {
t = rarray[(i - 1) * n + j] / rarray[i * n + j]; // r[i-1, j]/r[i, j]
u = MICROPY_FLOAT_C_FUN(sqrt)(1 + t * t);
s = MICROPY_FLOAT_CONST(-1.0) / u;
c = s * t;
}
}
mp_float_t r1, r2;
// update R: multiply with the rotation matrix from the left
for(size_t k = 0; k < n; k++) {
r1 = rarray[(i - 1) * n + k]; // r[i-1, k]
r2 = rarray[i * n + k]; // r[i, k]
rarray[(i - 1) * n + k] = c * r1 + s * r2; // r[i-1, k]
rarray[i * n + k] = s * r1 - c * r2; // r[i, k]
}
// update Q: multiply with the transpose of the rotation matrix from the right
for(size_t k = 0; k < m; k++) {
r1 = qarray[k * m + (i - 1)];
r2 = qarray[k * m + i];
qarray[k * m + (i - 1)] = c * r1 + s * r2;
qarray[k * m + i] = s * r1 - c * r2;
}
}
}
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
GET_STR_DATA_LEN(args[1].u_obj, mode, len);
if(memcmp(mode, "complete", 8) == 0) {
tuple->items[0] = MP_OBJ_FROM_PTR(Q);
tuple->items[1] = MP_OBJ_FROM_PTR(R);
} else if(memcmp(mode, "reduced", 7) == 0) {
size_t k = MAX(m, n) - MIN(m, n);
ndarray_obj_t *q = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m, m - k), NDARRAY_FLOAT);
ndarray_obj_t *r = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m - k, n), NDARRAY_FLOAT);
mp_float_t *qa = (mp_float_t *)q->array;
mp_float_t *ra = (mp_float_t *)r->array;
for(size_t i = 0; i < m; i++) {
memcpy(qa, qarray, (m - k) * q->itemsize);
qa += (m - k);
qarray += m;
}
for(size_t i = 0; i < m - k; i++) {
memcpy(ra, rarray, n * r->itemsize);
ra += n;
rarray += n;
}
tuple->items[0] = MP_OBJ_FROM_PTR(q);
tuple->items[1] = MP_OBJ_FROM_PTR(r);
} else {
mp_raise_ValueError(MP_ERROR_TEXT("mode must be complete, or reduced"));
}
return MP_OBJ_FROM_PTR(tuple);
}
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_qr_obj, 1, linalg_qr);
#endif
static const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) },
#if ULAB_MAX_DIMS > 1
#if ULAB_LINALG_HAS_CHOLESKY
{ MP_ROM_QSTR(MP_QSTR_cholesky), MP_ROM_PTR(&linalg_cholesky_obj) },
#endif
#if ULAB_LINALG_HAS_DET
{ MP_ROM_QSTR(MP_QSTR_det), MP_ROM_PTR(&linalg_det_obj) },
#endif
#if ULAB_LINALG_HAS_EIG
{ MP_ROM_QSTR(MP_QSTR_eig), MP_ROM_PTR(&linalg_eig_obj) },
#endif
#if ULAB_LINALG_HAS_INV
{ MP_ROM_QSTR(MP_QSTR_inv), MP_ROM_PTR(&linalg_inv_obj) },
#endif
#if ULAB_LINALG_HAS_QR
{ MP_ROM_QSTR(MP_QSTR_qr), MP_ROM_PTR(&linalg_qr_obj) },
#endif
#endif
#if ULAB_LINALG_HAS_NORM
{ MP_ROM_QSTR(MP_QSTR_norm), MP_ROM_PTR(&linalg_norm_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
const mp_obj_module_t ulab_linalg_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module);
#endif
#endif

View file

@ -0,0 +1,27 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _LINALG_
#define _LINALG_
#include "../../ulab.h"
#include "../../ndarray.h"
#include "linalg_tools.h"
extern const mp_obj_module_t ulab_linalg_module;
MP_DECLARE_CONST_FUN_OBJ_1(linalg_cholesky_obj);
MP_DECLARE_CONST_FUN_OBJ_1(linalg_det_obj);
MP_DECLARE_CONST_FUN_OBJ_1(linalg_eig_obj);
MP_DECLARE_CONST_FUN_OBJ_1(linalg_inv_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_norm_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_qr_obj);
#endif

View file

@ -0,0 +1,170 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2010 Zoltán Vörös
*/
#include <math.h>
#include <string.h>
#include "py/runtime.h"
#include "linalg_tools.h"
/*
* The following function inverts a matrix, whose entries are given in the input array
* The function has no dependencies beyond micropython itself (for the definition of mp_float_t),
* and can be used independent of ulab.
*/
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_new0(mp_float_t, N*N);
mp_float_t elem = 1.0;
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)]) < LINALG_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]) < LINALG_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;
}
/*
* The following function calculates the eigenvalues and eigenvectors of a symmetric
* real matrix, whose entries are given in the input array.
* The function has no dependencies beyond micropython itself (for the definition of mp_float_t),
* and can be used independent of ulab.
*/
size_t linalg_jacobi_rotations(mp_float_t *array, mp_float_t *eigvectors, size_t S) {
// eigvectors should be a 0-array; start out with the unit matrix
for(size_t m=0; m < S; m++) {
eigvectors[m * (S+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 * S * S;
do {
iterations--;
// find the pivot here
M = 0;
N = 0;
largest = 0.0;
for(size_t m=0; m < S-1; m++) { // -1: no need to inspect last row
for(size_t n=m+1; n < S; n++) {
w = MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n]);
if((largest < w) && (LINALG_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 * S + N] - array[M * S + M]) / (MICROPY_FLOAT_CONST(2.0)*array[M * S + 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 * S + M] = array[M * S + M] - t * array[M * S + N];
// a(N, N) = a(N, N) + t*a(M, N)
array[N * S + N] = array[N * S + N] + t * array[M * S + N];
// after the rotation, the a(M, N), and a(N, M) entries should become zero
array[M * S + N] = array[N * S + M] = MICROPY_FLOAT_CONST(0.0);
// then all other elements in the column
for(size_t k=0; k < S; k++) {
if((k == M) || (k == N)) {
continue;
}
aMk = array[M * S + k];
aNk = array[N * S + k];
// a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k))
array[M * S + k] -= s * (aNk + tau * aMk);
// a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k))
array[N * S + k] += s * (aMk - tau * aNk);
// a(k, M) = a(M, k)
array[k * S + M] = array[M * S + k];
// a(k, N) = a(N, k)
array[k * S + N] = array[N * S + 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 < S; m++) {
vm = eigvectors[m * S + M];
vn = eigvectors[m * S + N];
// the new value of eigvectors(m, M)
eigvectors[m * S + M] = c * vm - s * vn;
// the new value of eigvectors(m, N)
eigvectors[m * S + N] = s * vm + c * vn;
}
} while(iterations > 0);
return iterations;
}

View file

@ -0,0 +1,28 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _TOOLS_TOOLS_
#define _TOOLS_TOOLS_
#ifndef LINALG_EPSILON
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#define LINALG_EPSILON MICROPY_FLOAT_CONST(1.2e-7)
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
#define LINALG_EPSILON MICROPY_FLOAT_CONST(2.3e-16)
#endif
#endif /* LINALG_EPSILON */
#define JACOBI_MAX 20
bool linalg_invert_matrix(mp_float_t *, size_t );
size_t linalg_jacobi_rotations(mp_float_t *, mp_float_t *, size_t );
#endif /* _TOOLS_TOOLS_ */

View file

@ -0,0 +1,66 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Zoltán Vörös
*
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "ndarray_iter.h"
#ifdef NDARRAY_HAS_FLATITER
mp_obj_t ndarray_flatiter_make_new(mp_obj_t self_in) {
ndarray_flatiter_t *flatiter = m_new_obj(ndarray_flatiter_t);
flatiter->base.type = &ndarray_flatiter_type;
flatiter->iternext = ndarray_flatiter_next;
flatiter->ndarray = self_in;
flatiter->cur = 0;
return MP_OBJ_FROM_PTR(flatiter);
}
mp_obj_t ndarray_flatiter_next(mp_obj_t self_in) {
ndarray_flatiter_t *self = MP_OBJ_TO_PTR(self_in);
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(self->ndarray);
uint8_t *array = (uint8_t *)ndarray->array;
if(self->cur < ndarray->len) {
uint32_t remainder = self->cur;
uint8_t i = ULAB_MAX_DIMS - 1;
do {
size_t div = (remainder / ndarray->shape[i]);
array += remainder * ndarray->strides[i];
remainder -= div * ndarray->shape[i];
i--;
} while(i > ULAB_MAX_DIMS - ndarray->ndim);
self->cur++;
return ndarray_get_item(ndarray, array);
}
return MP_OBJ_STOP_ITERATION;
}
mp_obj_t ndarray_new_flatiterator(mp_obj_t flatiter_in, mp_obj_iter_buf_t *iter_buf) {
assert(sizeof(ndarray_flatiter_t) <= sizeof(mp_obj_iter_buf_t));
ndarray_flatiter_t *iter = (ndarray_flatiter_t *)iter_buf;
ndarray_flatiter_t *flatiter = MP_OBJ_TO_PTR(flatiter_in);
iter->base.type = &mp_type_polymorph_iter;
iter->iternext = ndarray_flatiter_next;
iter->ndarray = flatiter->ndarray;
iter->cur = 0;
return MP_OBJ_FROM_PTR(iter);
}
mp_obj_t ndarray_get_flatiterator(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
return ndarray_new_flatiterator(o_in, iter_buf);
}
#endif /* NDARRAY_HAS_FLATITER */

View file

@ -0,0 +1,36 @@
/*
* 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-2021 Zoltán Vörös
*/
#ifndef _NDARRAY_ITER_
#define _NDARRAY_ITER_
#include "py/runtime.h"
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "../../ulab.h"
#include "../../ndarray.h"
// TODO: take simply mp_obj_ndarray_it_t from ndarray.c
typedef struct _mp_obj_ndarray_flatiter_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
mp_obj_t ndarray;
size_t cur;
} ndarray_flatiter_t;
mp_obj_t ndarray_get_flatiterator(mp_obj_t , mp_obj_iter_buf_t *);
mp_obj_t ndarray_flatiter_make_new(mp_obj_t );
mp_obj_t ndarray_flatiter_next(mp_obj_t );
#endif

1429
code/numpy/numerical.c Normal file

File diff suppressed because it is too large Load diff

523
code/numpy/numerical.h Normal file
View file

@ -0,0 +1,523 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _NUMERICAL_
#define _NUMERICAL_
#include "../ulab.h"
#include "../ndarray.h"
// TODO: implement cumsum
#define RUN_ARGMIN1(ndarray, type, array, results, rarray, index, op)\
({\
uint16_t best_index = 0;\
type best_value = *((type *)(array));\
if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\
for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\
if(*((type *)(array)) > best_value) {\
best_index = i;\
best_value = *((type *)(array));\
}\
(array) += (ndarray)->strides[(index)];\
}\
} else {\
for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\
if(*((type *)(array)) < best_value) {\
best_index = i;\
best_value = *((type *)(array));\
}\
(array) += (ndarray)->strides[(index)];\
}\
}\
if(((op) == NUMERICAL_ARGMAX) || ((op) == NUMERICAL_ARGMIN)) {\
memcpy((rarray), &best_index, (results)->itemsize);\
} else {\
memcpy((rarray), &best_value, (results)->itemsize);\
}\
(rarray) += (results)->itemsize;\
})
#define RUN_SUM1(type, array, results, rarray, ss)\
({\
type sum = 0;\
for(size_t i=0; i < (ss).shape[0]; i++) {\
sum += *((type *)(array));\
(array) += (ss).strides[0];\
}\
memcpy((rarray), &sum, (results)->itemsize);\
(rarray) += (results)->itemsize;\
})
// Instead of the straightforward implementation of the definition,
// we take the numerically stable Welford algorithm here
// https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
#define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\
({\
mp_float_t M = 0.0, m = 0.0, S = 0.0;\
for(size_t i=0; i < (ss).shape[0]; i++) {\
mp_float_t value = (mp_float_t)(*(type *)(array));\
m = M + (value - M) / (mp_float_t)(i+1);\
if(isStd) {\
S += (value - M) * (value - m);\
}\
M = m;\
(array) += (ss).strides[0];\
}\
*(rarray)++ = isStd ? MICROPY_FLOAT_C_FUN(sqrt)(S / (div)) : M;\
})
#define RUN_DIFF1(ndarray, type, array, results, rarray, index, stencil, N)\
({\
for(size_t i=0; i < (results)->shape[ULAB_MAX_DIMS - 1]; i++) {\
type sum = 0;\
uint8_t *source = (array);\
for(uint8_t d=0; d < (N)+1; d++) {\
sum -= (stencil)[d] * *((type *)source);\
source += (ndarray)->strides[(index)];\
}\
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 1];\
*(type *)(rarray) = sum;\
(rarray) += (results)->itemsize;\
}\
})
#define HEAPSORT1(type, array, increment, N)\
({\
type *_array = (type *)array;\
type tmp;\
size_t c, q = (N), p, r = (N) >> 1;\
for (;;) {\
if (r > 0) {\
tmp = _array[(--r)*(increment)];\
} else {\
q--;\
if(q == 0) {\
break;\
}\
tmp = _array[q*(increment)];\
_array[q*(increment)] = _array[0];\
}\
p = r;\
c = r + r + 1;\
while (c < q) {\
if((c + 1 < q) && (_array[(c+1)*(increment)] > _array[c*(increment)])) {\
c++;\
}\
if(_array[c*(increment)] > tmp) {\
_array[p*(increment)] = _array[c*(increment)];\
p = c;\
c = p + p + 1;\
} else {\
break;\
}\
}\
_array[p*(increment)] = tmp;\
}\
})
#define HEAP_ARGSORT1(type, array, increment, N, iarray, iincrement)\
({\
type *_array = (type *)array;\
type tmp;\
uint16_t itmp, c, q = (N), p, r = (N) >> 1;\
assert(N);\
for (;;) {\
if (r > 0) {\
r--;\
itmp = (iarray)[r*(iincrement)];\
tmp = _array[itmp*(increment)];\
} else {\
q--;\
if(q == 0) {\
break;\
}\
itmp = (iarray)[q*(iincrement)];\
tmp = _array[itmp*(increment)];\
(iarray)[q*(iincrement)] = (iarray)[0];\
}\
p = r;\
c = r + r + 1;\
while (c < q) {\
if((c + 1 < q) && (_array[(iarray)[(c+1)*(iincrement)]*(increment)] > _array[(iarray)[c*(iincrement)]*(increment)])) {\
c++;\
}\
if(_array[(iarray)[c*(iincrement)]*(increment)] > tmp) {\
(iarray)[p*(iincrement)] = (iarray)[c*(iincrement)];\
p = c;\
c = p + p + 1;\
} else {\
break;\
}\
}\
(iarray)[p*(iincrement)] = itmp;\
}\
})
#if ULAB_MAX_DIMS == 1
#define RUN_SUM(type, array, results, rarray, ss) do {\
RUN_SUM1(type, (array), (results), (rarray), (ss));\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
} while(0)
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
} while(0)
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
} while(0)
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
HEAPSORT1(type, (array), (increment), (N));\
} while(0)
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
} while(0)
#endif
#if ULAB_MAX_DIMS == 2
#define RUN_SUM(type, array, results, rarray, ss) do {\
size_t l = 0;\
do {\
RUN_SUM1(type, (array), (results), (rarray), (ss));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
size_t l = 0;\
do {\
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
size_t l = 0;\
do {\
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
(array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
} while(0)
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
size_t l = 0;\
do {\
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
size_t l = 0;\
do {\
HEAPSORT1(type, (array), (increment), (N));\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
} while(0)
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
size_t l = 0;\
do {\
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
(iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
} while(0)
#endif
#if ULAB_MAX_DIMS == 3
#define RUN_SUM(type, array, results, rarray, ss) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_SUM1(type, (array), (results), (rarray), (ss));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
(array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(array) += (strides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
} while(0)
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 2]);\
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 3];\
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 3]);\
} while(0)
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
HEAPSORT1(type, (array), (increment), (N));\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(array) += (strides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
} while(0)
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
(iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
(iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(iarray) += (istrides)[ULAB_MAX_DIMS - 2];\
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(array) += (strides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif
#if ULAB_MAX_DIMS == 4
#define RUN_SUM(type, array, results, rarray, ss) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_SUM1(type, (array), (results), (rarray), (ss));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
(array) -= (ss).strides[0] * (ss).shape[0];\
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
(array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(array) += (strides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
(array) += (strides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (shape)[ULAB_MAX_DIMS - 3]);\
} while(0)
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
(rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 2]);\
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
(array) += (strides)[ULAB_MAX_DIMS - 3];\
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
(rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 3]);\
(array) -= (strides)[ULAB_MAX_DIMS - 3] * (shape)[ULAB_MAX_DIMS-3];\
(array) += (strides)[ULAB_MAX_DIMS - 4];\
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
(rarray) += (results)->strides[ULAB_MAX_DIMS - 4];\
j++;\
} while(j < (shape)[ULAB_MAX_DIMS - 4]);\
} while(0)
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
HEAPSORT1(type, (array), (increment), (N));\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(array) += (strides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
(array) += (strides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (shape)[ULAB_MAX_DIMS - 3]);\
} while(0)
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
(array) += (strides)[ULAB_MAX_DIMS - 1];\
(iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
(iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(iarray) += (istrides)[ULAB_MAX_DIMS - 2];\
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
(array) += (strides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
(iarray) -= (istrides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
(iarray) += (istrides)[ULAB_MAX_DIMS - 3];\
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
(array) += (strides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (shape)[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_all_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_any_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmax_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmin_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argsort_obj);
MP_DECLARE_CONST_FUN_OBJ_2(numerical_cross_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_diff_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_flip_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_max_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_mean_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_median_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_min_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_roll_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_std_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sum_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj);
#endif

408
code/numpy/numpy.c Normal file
View file

@ -0,0 +1,408 @@
/*
* 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-2022 Zoltán Vörös
* 2020 Taku Fukada
*/
#include <math.h>
#include <string.h>
#include "py/runtime.h"
#include "numpy.h"
#include "approx.h"
#include "bitwise.h"
#include "carray/carray.h"
#include "compare.h"
#include "create.h"
#include "fft/fft.h"
#include "filter.h"
#include "io/io.h"
#include "linalg/linalg.h"
#include "numerical.h"
#include "random/random.h"
#include "stats.h"
#include "transform.h"
#include "poly.h"
#include "vector.h"
//| """Compatibility layer for numpy"""
//|
//| class ndarray: ...
//| def get_printoptions() -> Dict[str, int]:
//| """Get printing options"""
//| ...
//|
//| def set_printoptions(threshold: Optional[int] = None, edgeitems: Optional[int] = None) -> None:
//| """Set printing options"""
//| ...
//|
//| def ndinfo(array: ulab.numpy.ndarray) -> None:
//| ...
//|
//| def array(
//| values: Union[ndarray, Iterable[Union[_float, _bool, Iterable[Any]]]],
//| *,
//| dtype: _DType = ulab.numpy.float
//| ) -> ulab.numpy.ndarray:
//| """alternate constructor function for `ulab.numpy.ndarray`. Mirrors numpy.array"""
//| ...
// math constants
#if ULAB_NUMPY_HAS_E
ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E, 0x402df854UL, 0x4005bf0a8b145769ULL);
#endif
#if ULAB_NUMPY_HAS_INF
ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY, 0x7f800000UL, 0x7ff0000000000000ULL);
#endif
#if ULAB_NUMPY_HAS_NAN
ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN, 0x7fc00000UL, 0x7ff8000000000000ULL);
#endif
#if ULAB_NUMPY_HAS_PI
ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, MP_PI, 0x40490fdbUL, 0x400921fb54442d18ULL);
#endif
static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_numpy) },
{ MP_ROM_QSTR(MP_QSTR_ndarray), MP_ROM_PTR(&ulab_ndarray_type) },
{ MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&ndarray_array_constructor_obj) },
#if ULAB_NUMPY_HAS_FROMBUFFER
{ MP_ROM_QSTR(MP_QSTR_frombuffer), MP_ROM_PTR(&create_frombuffer_obj) },
#endif
// math constants
#if ULAB_NUMPY_HAS_E
{ MP_ROM_QSTR(MP_QSTR_e), ULAB_REFERENCE_FLOAT_CONST(ulab_const_float_e) },
#endif
#if ULAB_NUMPY_HAS_INF
{ MP_ROM_QSTR(MP_QSTR_inf), ULAB_REFERENCE_FLOAT_CONST(numpy_const_float_inf) },
#endif
#if ULAB_NUMPY_HAS_NAN
{ MP_ROM_QSTR(MP_QSTR_nan), ULAB_REFERENCE_FLOAT_CONST(numpy_const_float_nan) },
#endif
#if ULAB_NUMPY_HAS_PI
{ MP_ROM_QSTR(MP_QSTR_pi), ULAB_REFERENCE_FLOAT_CONST(ulab_const_float_pi) },
#endif
// class constants, always included
{ MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_INT(NDARRAY_BOOL) },
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
{ MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) },
{ MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) },
{ MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) },
{ MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) },
#if ULAB_SUPPORTS_COMPLEX
{ MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_INT(NDARRAY_COMPLEX) },
#endif
// modules of numpy
#if ULAB_NUMPY_HAS_FFT_MODULE
{ MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) },
#endif
#if ULAB_NUMPY_HAS_LINALG_MODULE
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) },
#endif
#if ULAB_NUMPY_HAS_RANDOM_MODULE
{ MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&ulab_numpy_random_module) },
#endif
#if ULAB_HAS_PRINTOPTIONS
{ MP_ROM_QSTR(MP_QSTR_set_printoptions), MP_ROM_PTR(&ndarray_set_printoptions_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_printoptions), MP_ROM_PTR(&ndarray_get_printoptions_obj) },
#endif
#if ULAB_NUMPY_HAS_NDINFO
{ MP_ROM_QSTR(MP_QSTR_ndinfo), MP_ROM_PTR(&ndarray_info_obj) },
#endif
#if ULAB_NUMPY_HAS_ARANGE
{ MP_ROM_QSTR(MP_QSTR_arange), MP_ROM_PTR(&create_arange_obj) },
#endif
#if ULAB_NUMPY_HAS_COMPRESS
{ MP_ROM_QSTR(MP_QSTR_compress), MP_ROM_PTR(&transform_compress_obj) },
#endif
#if ULAB_NUMPY_HAS_CONCATENATE
{ MP_ROM_QSTR(MP_QSTR_concatenate), MP_ROM_PTR(&create_concatenate_obj) },
#endif
#if ULAB_NUMPY_HAS_DELETE
{ MP_ROM_QSTR(MP_QSTR_delete), MP_ROM_PTR(&transform_delete_obj) },
#endif
#if ULAB_NUMPY_HAS_DIAG
#if ULAB_MAX_DIMS > 1
{ MP_ROM_QSTR(MP_QSTR_diag), MP_ROM_PTR(&create_diag_obj) },
#endif
#endif
#if ULAB_NUMPY_HAS_EMPTY
{ MP_ROM_QSTR(MP_QSTR_empty), MP_ROM_PTR(&create_zeros_obj) },
#endif
#if ULAB_MAX_DIMS > 1
#if ULAB_NUMPY_HAS_EYE
{ MP_ROM_QSTR(MP_QSTR_eye), MP_ROM_PTR(&create_eye_obj) },
#endif
#endif /* ULAB_MAX_DIMS */
// functions of the approx sub-module
#if ULAB_NUMPY_HAS_INTERP
{ MP_ROM_QSTR(MP_QSTR_interp), MP_ROM_PTR(&approx_interp_obj) },
#endif
#if ULAB_NUMPY_HAS_TRAPZ
{ MP_ROM_QSTR(MP_QSTR_trapz), MP_ROM_PTR(&approx_trapz_obj) },
#endif
// functions of the create sub-module
#if ULAB_NUMPY_HAS_FULL
{ MP_ROM_QSTR(MP_QSTR_full), MP_ROM_PTR(&create_full_obj) },
#endif
#if ULAB_NUMPY_HAS_LINSPACE
{ MP_ROM_QSTR(MP_QSTR_linspace), MP_ROM_PTR(&create_linspace_obj) },
#endif
#if ULAB_NUMPY_HAS_LOGSPACE
{ MP_ROM_QSTR(MP_QSTR_logspace), MP_ROM_PTR(&create_logspace_obj) },
#endif
#if ULAB_NUMPY_HAS_ONES
{ MP_ROM_QSTR(MP_QSTR_ones), MP_ROM_PTR(&create_ones_obj) },
#endif
#if ULAB_NUMPY_HAS_ZEROS
{ MP_ROM_QSTR(MP_QSTR_zeros), MP_ROM_PTR(&create_zeros_obj) },
#endif
#if ULAB_NUMPY_HAS_CLIP
{ MP_ROM_QSTR(MP_QSTR_clip), MP_ROM_PTR(&compare_clip_obj) },
#endif
#if ULAB_NUMPY_HAS_EQUAL
{ MP_ROM_QSTR(MP_QSTR_equal), MP_ROM_PTR(&compare_equal_obj) },
#endif
#if ULAB_NUMPY_HAS_NOTEQUAL
{ MP_ROM_QSTR(MP_QSTR_not_equal), MP_ROM_PTR(&compare_not_equal_obj) },
#endif
#if ULAB_NUMPY_HAS_ISFINITE
{ MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&compare_isfinite_obj) },
#endif
#if ULAB_NUMPY_HAS_ISINF
{ MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&compare_isinf_obj) },
#endif
#if ULAB_NUMPY_HAS_MAXIMUM
{ MP_ROM_QSTR(MP_QSTR_maximum), MP_ROM_PTR(&compare_maximum_obj) },
#endif
#if ULAB_NUMPY_HAS_MINIMUM
{ MP_ROM_QSTR(MP_QSTR_minimum), MP_ROM_PTR(&compare_minimum_obj) },
#endif
#if ULAB_NUMPY_HAS_NONZERO
{ MP_ROM_QSTR(MP_QSTR_nonzero), MP_ROM_PTR(&compare_nonzero_obj) },
#endif
#if ULAB_NUMPY_HAS_WHERE
{ MP_ROM_QSTR(MP_QSTR_where), MP_ROM_PTR(&compare_where_obj) },
#endif
// bitwise operators
#if ULAB_NUMPY_HAS_BITWISE_AND
{ MP_ROM_QSTR(MP_QSTR_bitwise_and), MP_ROM_PTR(&bitwise_bitwise_and_obj) },
#endif
#if ULAB_NUMPY_HAS_BITWISE_OR
{ MP_ROM_QSTR(MP_QSTR_bitwise_or), MP_ROM_PTR(&bitwise_bitwise_or_obj) },
#endif
#if ULAB_NUMPY_HAS_BITWISE_XOR
{ MP_ROM_QSTR(MP_QSTR_bitwise_xor), MP_ROM_PTR(&bitwise_bitwise_xor_obj) },
#endif
#if ULAB_NUMPY_HAS_LEFT_SHIFT
{ MP_ROM_QSTR(MP_QSTR_left_shift), MP_ROM_PTR(&left_shift_obj) },
#endif
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
{ MP_ROM_QSTR(MP_QSTR_right_shift), MP_ROM_PTR(&right_shift_obj) },
#endif
// functions of the filter sub-module
#if ULAB_NUMPY_HAS_CONVOLVE
{ MP_ROM_QSTR(MP_QSTR_convolve), MP_ROM_PTR(&filter_convolve_obj) },
#endif
// functions of the numerical sub-module
#if ULAB_NUMPY_HAS_ALL
{ MP_ROM_QSTR(MP_QSTR_all), MP_ROM_PTR(&numerical_all_obj) },
#endif
#if ULAB_NUMPY_HAS_ANY
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&numerical_any_obj) },
#endif
#if ULAB_NUMPY_HAS_ARGMINMAX
{ MP_ROM_QSTR(MP_QSTR_argmax), MP_ROM_PTR(&numerical_argmax_obj) },
{ MP_ROM_QSTR(MP_QSTR_argmin), MP_ROM_PTR(&numerical_argmin_obj) },
#endif
#if ULAB_NUMPY_HAS_ARGSORT
{ MP_ROM_QSTR(MP_QSTR_argsort), MP_ROM_PTR(&numerical_argsort_obj) },
#endif
#if ULAB_NUMPY_HAS_ASARRAY
{ MP_ROM_QSTR(MP_QSTR_asarray), MP_ROM_PTR(&create_asarray_obj) },
#endif
#if ULAB_NUMPY_HAS_CROSS
{ MP_ROM_QSTR(MP_QSTR_cross), MP_ROM_PTR(&numerical_cross_obj) },
#endif
#if ULAB_NUMPY_HAS_DIFF
{ MP_ROM_QSTR(MP_QSTR_diff), MP_ROM_PTR(&numerical_diff_obj) },
#endif
#if ULAB_NUMPY_HAS_DOT
#if ULAB_MAX_DIMS > 1
{ MP_ROM_QSTR(MP_QSTR_dot), MP_ROM_PTR(&transform_dot_obj) },
#endif
#endif
#if ULAB_NUMPY_HAS_TRACE
#if ULAB_MAX_DIMS > 1
{ MP_ROM_QSTR(MP_QSTR_trace), MP_ROM_PTR(&stats_trace_obj) },
#endif
#endif
#if ULAB_NUMPY_HAS_FLIP
{ MP_ROM_QSTR(MP_QSTR_flip), MP_ROM_PTR(&numerical_flip_obj) },
#endif
#if ULAB_NUMPY_HAS_LOAD
{ MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&io_load_obj) },
#endif
#if ULAB_NUMPY_HAS_LOADTXT
{ MP_ROM_QSTR(MP_QSTR_loadtxt), MP_ROM_PTR(&io_loadtxt_obj) },
#endif
#if ULAB_NUMPY_HAS_MINMAX
{ MP_ROM_QSTR(MP_QSTR_max), MP_ROM_PTR(&numerical_max_obj) },
#endif
#if ULAB_NUMPY_HAS_MEAN
{ MP_ROM_QSTR(MP_QSTR_mean), MP_ROM_PTR(&numerical_mean_obj) },
#endif
#if ULAB_NUMPY_HAS_MEDIAN
{ MP_ROM_QSTR(MP_QSTR_median), MP_ROM_PTR(&numerical_median_obj) },
#endif
#if ULAB_NUMPY_HAS_MINMAX
{ MP_ROM_QSTR(MP_QSTR_min), MP_ROM_PTR(&numerical_min_obj) },
#endif
#if ULAB_NUMPY_HAS_ROLL
{ MP_ROM_QSTR(MP_QSTR_roll), MP_ROM_PTR(&numerical_roll_obj) },
#endif
#if ULAB_NUMPY_HAS_SAVE
{ MP_ROM_QSTR(MP_QSTR_save), MP_ROM_PTR(&io_save_obj) },
#endif
#if ULAB_NUMPY_HAS_SAVETXT
{ MP_ROM_QSTR(MP_QSTR_savetxt), MP_ROM_PTR(&io_savetxt_obj) },
#endif
#if ULAB_NUMPY_HAS_SIZE
{ MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&transform_size_obj) },
#endif
#if ULAB_NUMPY_HAS_SORT
{ MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_obj) },
#endif
#if ULAB_NUMPY_HAS_STD
{ MP_ROM_QSTR(MP_QSTR_std), MP_ROM_PTR(&numerical_std_obj) },
#endif
#if ULAB_NUMPY_HAS_SUM
{ MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&numerical_sum_obj) },
#endif
#if ULAB_NUMPY_HAS_TAKE
{ MP_ROM_QSTR(MP_QSTR_take), MP_ROM_PTR(&create_take_obj) },
#endif
// functions of the poly sub-module
#if ULAB_NUMPY_HAS_POLYFIT
{ MP_ROM_QSTR(MP_QSTR_polyfit), MP_ROM_PTR(&poly_polyfit_obj) },
#endif
#if ULAB_NUMPY_HAS_POLYVAL
{ MP_ROM_QSTR(MP_QSTR_polyval), MP_ROM_PTR(&poly_polyval_obj) },
#endif
// functions of the vector sub-module
#if ULAB_NUMPY_HAS_ACOS
{ MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&vector_acos_obj) },
#endif
#if ULAB_NUMPY_HAS_ACOSH
{ MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&vector_acosh_obj) },
#endif
#if ULAB_NUMPY_HAS_ARCTAN2
{ MP_ROM_QSTR(MP_QSTR_arctan2), MP_ROM_PTR(&vector_arctan2_obj) },
#endif
#if ULAB_NUMPY_HAS_AROUND
{ MP_ROM_QSTR(MP_QSTR_around), MP_ROM_PTR(&vector_around_obj) },
#endif
#if ULAB_NUMPY_HAS_ASIN
{ MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&vector_asin_obj) },
#endif
#if ULAB_NUMPY_HAS_ASINH
{ MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&vector_asinh_obj) },
#endif
#if ULAB_NUMPY_HAS_ATAN
{ MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&vector_atan_obj) },
#endif
#if ULAB_NUMPY_HAS_ATANH
{ MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&vector_atanh_obj) },
#endif
#if ULAB_NUMPY_HAS_CEIL
{ MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&vector_ceil_obj) },
#endif
#if ULAB_NUMPY_HAS_COS
{ MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&vector_cos_obj) },
#endif
#if ULAB_NUMPY_HAS_COSH
{ MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&vector_cosh_obj) },
#endif
#if ULAB_NUMPY_HAS_DEGREES
{ MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&vector_degrees_obj) },
#endif
#if ULAB_NUMPY_HAS_EXP
{ MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&vector_exp_obj) },
#endif
#if ULAB_NUMPY_HAS_EXPM1
{ MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&vector_expm1_obj) },
#endif
#if ULAB_NUMPY_HAS_FLOOR
{ MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&vector_floor_obj) },
#endif
#if ULAB_NUMPY_HAS_LOG
{ MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&vector_log_obj) },
#endif
#if ULAB_NUMPY_HAS_LOG10
{ MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&vector_log10_obj) },
#endif
#if ULAB_NUMPY_HAS_LOG2
{ MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&vector_log2_obj) },
#endif
#if ULAB_NUMPY_HAS_RADIANS
{ MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&vector_radians_obj) },
#endif
#if ULAB_NUMPY_HAS_SIN
{ MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&vector_sin_obj) },
#endif
#if ULAB_NUMPY_HAS_SINC
{ MP_ROM_QSTR(MP_QSTR_sinc), MP_ROM_PTR(&vector_sinc_obj) },
#endif
#if ULAB_NUMPY_HAS_SINH
{ MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&vector_sinh_obj) },
#endif
#if ULAB_NUMPY_HAS_SQRT
{ MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&vector_sqrt_obj) },
#endif
#if ULAB_NUMPY_HAS_TAN
{ MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&vector_tan_obj) },
#endif
#if ULAB_NUMPY_HAS_TANH
{ MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&vector_tanh_obj) },
#endif
#if ULAB_NUMPY_HAS_VECTORIZE
{ MP_ROM_QSTR(MP_QSTR_vectorize), MP_ROM_PTR(&vector_vectorize_obj) },
#endif
#if ULAB_SUPPORTS_COMPLEX
#if ULAB_NUMPY_HAS_REAL
{ MP_ROM_QSTR(MP_QSTR_real), MP_ROM_PTR(&carray_real_obj) },
#endif
#if ULAB_NUMPY_HAS_IMAG
{ MP_ROM_QSTR(MP_QSTR_imag), MP_ROM_PTR(&carray_imag_obj) },
#endif
#if ULAB_NUMPY_HAS_CONJUGATE
{ MP_ROM_QSTR(MP_QSTR_conjugate), MP_ROM_PTR(&carray_conjugate_obj) },
#endif
#if ULAB_NUMPY_HAS_SORT_COMPLEX
{ MP_ROM_QSTR(MP_QSTR_sort_complex), MP_ROM_PTR(&carray_sort_complex_obj) },
#endif
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table);
const mp_obj_module_t ulab_numpy_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module);
#endif

21
code/numpy/numpy.h Normal file
View file

@ -0,0 +1,21 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*
*/
#ifndef _NUMPY_
#define _NUMPY_
#include "../ulab.h"
#include "../ndarray.h"
extern const mp_obj_module_t ulab_numpy_module;
#endif /* _NUMPY_ */

218
code/numpy/poly.c Normal file
View file

@ -0,0 +1,218 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
* 2020 Jeff Epler for Adafruit Industries
* 2020 Scott Shawcroft for Adafruit Industries
* 2020 Taku Fukada
*/
#include "py/obj.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include "../ulab.h"
#include "linalg/linalg_tools.h"
#include "../ulab_tools.h"
#include "carray/carray_tools.h"
#include "poly.h"
#if ULAB_NUMPY_HAS_POLYFIT
mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
if(!ndarray_object_is_array_like(args[0])) {
mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable"));
}
#if ULAB_SUPPORTS_COMPLEX
if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
}
#endif
size_t lenx = 0, leny = 0;
uint8_t deg = 0;
mp_float_t *x, *XT, *y, *prod;
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(MP_ERROR_TEXT("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_array_like(args[1])) {
mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable"));
}
lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1]));
if(lenx != leny) {
mp_raise_ValueError(MP_ERROR_TEXT("input vectors must be of equal length"));
}
deg = (uint8_t)mp_obj_get_int(args[2]);
if(leny < deg) {
mp_raise_ValueError(MP_ERROR_TEXT("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(MP_ERROR_TEXT("could not invert Vandermonde matrix"));
}
// at this point, we have the inverse of X^T * X
// y is a column vector; x is free now, we can use it for storing intermediate values
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 = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT);
mp_float_t *betav = (mp_float_t *)beta->array;
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
m_del(mp_float_t, y, leny);
// 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);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
#endif
#if ULAB_NUMPY_HAS_POLYVAL
static mp_float_t poly_eval(mp_float_t x, mp_float_t *p, uint8_t plen) {
mp_float_t y = p[0];
for(uint8_t j=0; j < plen-1; j++) {
y *= x;
y += p[j+1];
}
return y;
}
mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
if(!ndarray_object_is_array_like(o_p)) {
mp_raise_TypeError(MP_ERROR_TEXT("input is not iterable"));
}
#if ULAB_SUPPORTS_COMPLEX
ndarray_obj_t *input;
if(mp_obj_is_type(o_p, &ulab_ndarray_type)) {
input = MP_OBJ_TO_PTR(o_p);
COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype)
}
if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
input = MP_OBJ_TO_PTR(o_x);
COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype)
}
#endif
// p had better be a one-dimensional standard iterable
size_t plen = (size_t)mp_obj_get_int(mp_obj_len_maybe(o_p));
mp_float_t *p = m_new(mp_float_t, plen);
mp_obj_iter_buf_t p_buf;
mp_obj_t p_item, p_iterable = mp_getiter(o_p, &p_buf);
uint8_t i = 0;
while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) {
p[i] = mp_obj_get_float(p_item);
i++;
}
if(!ndarray_object_is_array_like(o_x)) {
return mp_obj_new_float(poly_eval(mp_obj_get_float(o_x), p, plen));
}
// polynomials are going to be of type float, except, when both
// the coefficients and the independent variable are integers
ndarray_obj_t *ndarray;
if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_x);
uint8_t *sarray = (uint8_t *)source->array;
ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
mp_float_t *array = (mp_float_t *)ndarray->array;
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
// TODO: these loops are really nothing, but the re-impplementation of
// ITERATE_VECTOR from vectorise.c. We could pass a function pointer here
ITERATOR_HEAD();
*array++ = poly_eval(func(sarray), p, plen);
ITERATOR_TAIL(source, sarray);
} else {
// o_x had better be a one-dimensional standard iterable
ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT);
mp_float_t *array = (mp_float_t *)ndarray->array;
mp_obj_iter_buf_t x_buf;
mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf);
while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) {
*array++ = poly_eval(mp_obj_get_float(x_item), p, plen);
}
}
m_del(mp_float_t, p, plen);
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
#endif

21
code/numpy/poly.h Normal file
View file

@ -0,0 +1,21 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _POLY_
#define _POLY_
#include "../ulab.h"
#include "../ndarray.h"
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj);
MP_DECLARE_CONST_FUN_OBJ_2(poly_polyval_obj);
#endif

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

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

View file

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

54
code/numpy/stats.c Normal file
View file

@ -0,0 +1,54 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
* 2020 Scott Shawcroft for Adafruit Industries
* 2020 Roberto Colistete Jr.
* 2020 Taku Fukada
*
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../ulab.h"
#include "../ulab_tools.h"
#include "carray/carray_tools.h"
#include "stats.h"
#if ULAB_MAX_DIMS > 1
#if ULAB_NUMPY_HAS_TRACE
//| def trace(m: ulab.numpy.ndarray) -> _float:
//| """
//| :param m: a square matrix
//|
//| Compute the trace of the matrix, the sum of its diagonal elements."""
//| ...
//|
static mp_obj_t stats_trace(mp_obj_t oin) {
ndarray_obj_t *ndarray = tools_object_is_square(oin);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
mp_float_t trace = 0.0;
for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) {
int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]);
trace += ndarray_get_float_index(ndarray->array, ndarray->dtype, pos/ndarray->itemsize);
}
if(ndarray->dtype == NDARRAY_FLOAT) {
return mp_obj_new_float(trace);
}
return mp_obj_new_int_from_float(trace);
}
MP_DEFINE_CONST_FUN_OBJ_1(stats_trace_obj, stats_trace);
#endif
#endif

20
code/numpy/stats.h Normal file
View file

@ -0,0 +1,20 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _STATS_
#define _STATS_
#include "../ulab.h"
#include "../ndarray.h"
MP_DECLARE_CONST_FUN_OBJ_1(stats_trace_obj);
#endif

456
code/numpy/transform.c Normal file
View file

@ -0,0 +1,456 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*
*/
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../ulab.h"
#include "../ulab_tools.h"
#include "carray/carray_tools.h"
#include "numerical.h"
#include "transform.h"
#if ULAB_NUMPY_HAS_COMPRESS
static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t condition = args[0].u_obj;
if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
uint8_t *array = (uint8_t *)ndarray->array;
mp_obj_t axis = args[2].u_obj;
size_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(condition));
int8_t ax, shift_ax = 0;
if(axis != mp_const_none) {
ax = tools_get_axis(axis, ndarray->ndim);
shift_ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
}
if(((axis == mp_const_none) && (len != ndarray->len)) ||
((axis != mp_const_none) && (len != ndarray->shape[shift_ax]))) {
mp_raise_ValueError(MP_ERROR_TEXT("wrong length of condition array"));
}
size_t true_count = 0;
mp_obj_iter_buf_t iter_buf;
mp_obj_t item, iterable = mp_getiter(condition, &iter_buf);
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if(mp_obj_is_true(item)) {
true_count++;
}
}
iterable = mp_getiter(condition, &iter_buf);
ndarray_obj_t *result = NULL;
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
memcpy(shape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
size_t *rshape = m_new(size_t, ULAB_MAX_DIMS);
memcpy(rshape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t));
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
if(axis == mp_const_none) {
result = ndarray_new_linear_array(true_count, ndarray->dtype);
rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
rshape[ULAB_MAX_DIMS - 1] = 0;
} else {
rshape[shift_ax] = true_count;
result = ndarray_new_dense_ndarray(ndarray->ndim, rshape, ndarray->dtype);
SWAP(size_t, shape[shift_ax], shape[ULAB_MAX_DIMS - 1]);
SWAP(size_t, rshape[shift_ax], rshape[ULAB_MAX_DIMS - 1]);
SWAP(int32_t, strides[shift_ax], strides[ULAB_MAX_DIMS - 1]);
memcpy(rstrides, result->strides, ULAB_MAX_DIMS * sizeof(int32_t));
SWAP(int32_t, rstrides[shift_ax], rstrides[ULAB_MAX_DIMS - 1]);
}
uint8_t *rarray = (uint8_t *)result->array;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
if(axis != mp_const_none) {
iterable = mp_getiter(condition, &iter_buf);
}
do {
item = mp_iternext(iterable);
if(mp_obj_is_true(item)) {
memcpy(rarray, array, ndarray->itemsize);
rarray += rstrides[ULAB_MAX_DIMS - 1];
}
array += strides[ULAB_MAX_DIMS - 1];
l++;
} while(l < shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
array += strides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2];
array += strides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
array -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS - 3];
array += strides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
i++;
} while(i < shape[ULAB_MAX_DIMS - 4]);
#endif
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(size_t, rshape, ULAB_MAX_DIMS);
m_del(int32_t, strides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
return MP_OBJ_FROM_PTR(result);
}
MP_DEFINE_CONST_FUN_OBJ_KW(transform_compress_obj, 2, transform_compress);
#endif /* ULAB_NUMPY_HAS_COMPRESS */
#if ULAB_NUMPY_HAS_DELETE
static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
uint8_t *array = (uint8_t *)ndarray->array;
mp_obj_t indices = args[1].u_obj;
mp_obj_t axis = args[2].u_obj;
int8_t shift_ax;
size_t axis_len;
if(axis != mp_const_none) {
int8_t ax = tools_get_axis(axis, ndarray->ndim);
shift_ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
axis_len = ndarray->shape[shift_ax];
} else {
axis_len = ndarray->len;
}
size_t index_len;
if(mp_obj_is_int(indices)) {
index_len = 1;
} else {
if(mp_obj_len_maybe(indices) == MP_OBJ_NULL) {
mp_raise_TypeError(MP_ERROR_TEXT("wrong index type"));
}
index_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(indices));
if (index_len == 0){
// if the second positional argument is empty
// return the original array
return MP_OBJ_FROM_PTR(ndarray);
}
}
if(index_len > axis_len) {
mp_raise_ValueError(MP_ERROR_TEXT("wrong length of index array"));
}
size_t *index_array = m_new(size_t, index_len);
if(mp_obj_is_int(indices)) {
ssize_t value = (ssize_t)mp_obj_get_int(indices);
if(value < 0) {
value += axis_len;
}
if((value < 0) || (value > (ssize_t)axis_len)) {
mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds"));
} else {
*index_array++ = (size_t)value;
}
} else {
mp_obj_iter_buf_t iter_buf;
mp_obj_t item, iterable = mp_getiter(indices, &iter_buf);
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
ssize_t value = (ssize_t)mp_obj_get_int(item);
if(value < 0) {
value += axis_len;
}
if((value < 0) || (value > (ssize_t)axis_len)) {
mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds"));
} else {
*index_array++ = (size_t)value;
}
}
}
// sort the array, since it is not guaranteed that the input is sorted
HEAPSORT1(size_t, index_array, 1, index_len);
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
memcpy(shape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
size_t *rshape = m_new(size_t, ULAB_MAX_DIMS);
memcpy(rshape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t));
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
ndarray_obj_t *result = NULL;
if(axis == mp_const_none) {
result = ndarray_new_linear_array(ndarray->len - index_len, ndarray->dtype);
rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
memset(rshape, 0, sizeof(size_t) * ULAB_MAX_DIMS);
} else {
rshape[shift_ax] = shape[shift_ax] - index_len;
result = ndarray_new_dense_ndarray(ndarray->ndim, rshape, ndarray->dtype);
SWAP(size_t, shape[shift_ax], shape[ULAB_MAX_DIMS - 1]);
SWAP(size_t, rshape[shift_ax], rshape[ULAB_MAX_DIMS - 1]);
SWAP(int32_t, strides[shift_ax], strides[ULAB_MAX_DIMS - 1]);
memcpy(rstrides, result->strides, ULAB_MAX_DIMS * sizeof(int32_t));
SWAP(int32_t, rstrides[shift_ax], rstrides[ULAB_MAX_DIMS - 1]);
}
uint8_t *rarray = (uint8_t *)result->array;
index_array -= index_len;
size_t count = 0;
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
if(count == *index_array) {
index_array++;
} else {
memcpy(rarray, array, ndarray->itemsize);
rarray += rstrides[ULAB_MAX_DIMS - 1];
}
array += strides[ULAB_MAX_DIMS - 1];
l++;
count++;
} while(l < shape[ULAB_MAX_DIMS - 1]);
if(axis != mp_const_none) {
index_array -= index_len;
count = 0;
}
#if ULAB_MAX_DIMS > 1
array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
array += strides[ULAB_MAX_DIMS - 2];
rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS - 1];
rarray += rstrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2];
array += strides[ULAB_MAX_DIMS - 3];
rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
rarray += rstrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
array -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS - 3];
array += strides[ULAB_MAX_DIMS - 4];
rarray -= rstrides[ULAB_MAX_DIMS - 3] * rshape[ULAB_MAX_DIMS - 3];
rarray += rstrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < shape[ULAB_MAX_DIMS - 4]);
#endif
// TODO: deleting shape generates a seg fault
// m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(size_t, rshape, ULAB_MAX_DIMS);
m_del(int32_t, strides, ULAB_MAX_DIMS);
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
return MP_OBJ_FROM_PTR(result);
}
MP_DEFINE_CONST_FUN_OBJ_KW(transform_delete_obj, 2, transform_delete);
#endif /* ULAB_NUMPY_HAS_DELETE */
#if ULAB_MAX_DIMS > 1
#if ULAB_NUMPY_HAS_DOT
//| def dot(m1: ulab.numpy.ndarray, m2: ulab.numpy.ndarray) -> Union[ulab.numpy.ndarray, _float]:
//| """
//| :param ~ulab.numpy.ndarray m1: a matrix, or a vector
//| :param ~ulab.numpy.ndarray m2: a matrix, or a vector
//|
//| Computes the product of two matrices, or two vectors. In the letter case, the inner product is returned."""
//| ...
//|
mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) {
// TODO: should the results be upcast?
// This implements 2D operations only!
if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("arguments must be ndarrays"));
}
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
COMPLEX_DTYPE_NOT_IMPLEMENTED(m1->dtype)
COMPLEX_DTYPE_NOT_IMPLEMENTED(m2->dtype)
uint8_t *array1 = (uint8_t *)m1->array;
uint8_t *array2 = (uint8_t *)m2->array;
mp_float_t (*func1)(void *) = ndarray_get_float_function(m1->dtype);
mp_float_t (*func2)(void *) = ndarray_get_float_function(m2->dtype);
if(m1->shape[ULAB_MAX_DIMS - 1] != m2->shape[ULAB_MAX_DIMS - m2->ndim]) {
mp_raise_ValueError(MP_ERROR_TEXT("dimensions do not match"));
}
uint8_t ndim = MIN(m1->ndim, m2->ndim);
size_t shape1 = m1->ndim == 2 ? m1->shape[ULAB_MAX_DIMS - m1->ndim] : 1;
size_t shape2 = m2->ndim == 2 ? m2->shape[ULAB_MAX_DIMS - 1] : 1;
size_t *shape = NULL;
if(ndim == 2) { // matrix times matrix -> matrix
shape = ndarray_shape_vector(0, 0, shape1, shape2);
} else { // matrix times vector -> vector, vector times vector -> vector (size 1)
shape = ndarray_shape_vector(0, 0, 0, shape1 * shape2);
}
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
mp_float_t *rarray = (mp_float_t *)results->array;
for(size_t i=0; i < shape1; i++) { // rows of m1
for(size_t j=0; j < shape2; j++) { // columns of m2
mp_float_t dot = 0.0;
for(size_t k=0; k < m1->shape[ULAB_MAX_DIMS - 1]; k++) {
// (i, k) * (k, j)
dot += func1(array1) * func2(array2);
array1 += m1->strides[ULAB_MAX_DIMS - 1];
array2 += m2->strides[ULAB_MAX_DIMS - m2->ndim];
}
*rarray++ = dot;
array1 -= m1->strides[ULAB_MAX_DIMS - 1] * m1->shape[ULAB_MAX_DIMS - 1];
array2 -= m2->strides[ULAB_MAX_DIMS - m2->ndim] * m2->shape[ULAB_MAX_DIMS - m2->ndim];
array2 += m2->strides[ULAB_MAX_DIMS - 1];
}
array1 += m1->strides[ULAB_MAX_DIMS - m1->ndim];
array2 = m2->array;
}
if((m1->ndim * m2->ndim) == 1) { // return a scalar, if product of two vectors
return mp_obj_new_float(*(--rarray));
} else {
return MP_OBJ_FROM_PTR(results);
}
}
MP_DEFINE_CONST_FUN_OBJ_2(transform_dot_obj, transform_dot);
#endif /* ULAB_NUMPY_HAS_DOT */
#endif /* ULAB_MAX_DIMS > 1 */
#if ULAB_NUMPY_HAS_SIZE
static mp_obj_t transform_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_ROM_NONE } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(ulab_tools_mp_obj_is_scalar(args[0].u_obj)) {
return mp_obj_new_int(1);
}
if(!ndarray_object_is_array_like(args[0].u_obj)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
}
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
return mp_obj_len_maybe(args[0].u_obj);
}
// at this point, the args[0] is most certainly an ndarray
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
mp_obj_t axis = args[1].u_obj;
size_t len;
if(axis != mp_const_none) {
int8_t ax = tools_get_axis(axis, ndarray->ndim);
len = ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + ax];
} else {
len = ndarray->len;
}
return mp_obj_new_int(len);
}
MP_DEFINE_CONST_FUN_OBJ_KW(transform_size_obj, 1, transform_size);
#endif

30
code/numpy/transform.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) 2019-2021 Zoltán Vörös
*
*/
#ifndef _TRANSFORM_
#define _TRANSFORM_
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../ulab.h"
#include "../ulab_tools.h"
MP_DECLARE_CONST_FUN_OBJ_KW(transform_compress_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(transform_delete_obj);
MP_DECLARE_CONST_FUN_OBJ_2(transform_dot_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(transform_size_obj);
#endif

978
code/numpy/vector.c Normal file
View file

@ -0,0 +1,978 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2023 Zoltán Vörös
* 2020-2023 Jeff Epler for Adafruit Industries
* 2020 Scott Shawcroft for Adafruit Industries
* 2020 Taku Fukada
*/
#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 "../ulab.h"
#include "../ulab_tools.h"
#include "carray/carray_tools.h"
#include "vector.h"
//| """Element-by-element functions
//|
//| These functions can operate on numbers, 1-D iterables, and arrays of 1 to 4 dimensions 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."""
//|
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, mp_float_t (*f)(mp_float_t)) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
// this keyword argument is not used; it's only here, so that functions that
// support the complex dtype can call vector_generic_vector directly
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t o_in = args[0].u_obj;
// 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_obj_t out = args[1].u_obj;
ndarray_obj_t *target = NULL;
ndarray_obj_t *source = NULL;
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
source = MP_OBJ_TO_PTR(o_in);
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
if(out == mp_const_none) {
target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
} else {
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
mp_raise_ValueError(MP_ERROR_TEXT("out must be an ndarray"));
}
target = MP_OBJ_TO_PTR(out);
if(target->dtype != NDARRAY_FLOAT) {
mp_raise_ValueError(MP_ERROR_TEXT("out must be of float dtype"));
}
if(target->ndim != source->ndim) {
mp_raise_ValueError(MP_ERROR_TEXT("input and output dimensions differ"));
}
for(uint8_t d = 0; d < target->ndim; d++) {
if(target->shape[ULAB_MAX_DIMS - 1 - d] != source->shape[ULAB_MAX_DIMS - 1 - d]) {
mp_raise_ValueError(MP_ERROR_TEXT("input and output shapes differ"));
}
}
}
mp_float_t *tarray = (mp_float_t *)target->array;
int32_t *tstrides = m_new(int32_t, ULAB_MAX_DIMS);
for(uint8_t d = 0; d < target->ndim; d++) {
tstrides[ULAB_MAX_DIMS - 1 - d] = target->strides[ULAB_MAX_DIMS - 1 - d] / target->itemsize;
}
uint8_t *sarray = (uint8_t *)source->array;
#if ULAB_VECTORISE_USES_FUN_POINTER
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
ITERATOR_HEAD();
mp_float_t value = func(sarray);
*tarray++ = f(value);
ITERATOR_TAIL(source, sarray);
#else
if(source->dtype == NDARRAY_UINT8) {
ITERATE_VECTOR(uint8_t, target, tarray, tstrides, source, sarray);
} else if(source->dtype == NDARRAY_INT8) {
ITERATE_VECTOR(int8_t, target, tarray, tstrides, source, sarray);
} else if(source->dtype == NDARRAY_UINT16) {
ITERATE_VECTOR(uint16_t, target, tarray, tstrides, source, sarray);
} else if(source->dtype == NDARRAY_INT16) {
ITERATE_VECTOR(int16_t, target, tarray, tstrides, source, sarray);
} else {
ITERATE_VECTOR(mp_float_t, target, tarray, tstrides, source, sarray);
}
#endif /* ULAB_VECTORISE_USES_FUN_POINTER */
} else {
target = ndarray_from_mp_obj(o_in, 0);
mp_float_t *tarray = (mp_float_t *)target->array;
for(size_t i = 0; i < target->len; i++) {
*tarray = f(*tarray);
tarray++;
}
}
return MP_OBJ_FROM_PTR(target);
}
#else
static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
// Return a single value, if o_in is not iterable
if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) {
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
}
ndarray_obj_t *ndarray = NULL;
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
uint8_t *sarray = (uint8_t *)source->array;
ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
mp_float_t *array = (mp_float_t *)ndarray->array;
#if ULAB_VECTORISE_USES_FUN_POINTER
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
ITERATOR_HEAD();
mp_float_t value = func(sarray);
*array++ = f(value);
ITERATOR_TAIL(source, sarray);
#else
if(source->dtype == NDARRAY_UINT8) {
ITERATE_VECTOR(uint8_t, array, source, sarray);
} else if(source->dtype == NDARRAY_INT8) {
ITERATE_VECTOR(int8_t, array, source, sarray);
} else if(source->dtype == NDARRAY_UINT16) {
ITERATE_VECTOR(uint16_t, array, source, sarray);
} else if(source->dtype == NDARRAY_INT16) {
ITERATE_VECTOR(int16_t, array, source, sarray);
} else {
ITERATE_VECTOR(mp_float_t, array, source, sarray);
}
#endif /* ULAB_VECTORISE_USES_FUN_POINTER */
} else {
ndarray = ndarray_from_mp_obj(o_in, 0);
mp_float_t *narray = (mp_float_t *)ndarray->array;
for(size_t i = 0; i < ndarray->len; i++) {
*narray = f(*narray);
narray++;
}
}
return MP_OBJ_FROM_PTR(ndarray);
}
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#if ULAB_NUMPY_HAS_ACOS
//| def acos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse cosine function"""
//| ...
//|
MATH_FUN_1(acos, acos);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_acos_obj, 1, vector_acos);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_ACOS */
#if ULAB_NUMPY_HAS_ACOSH
//| def acosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse hyperbolic cosine function"""
//| ...
//|
MATH_FUN_1(acosh, acosh);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_acosh_obj, 1, vector_acosh);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_ACOSH */
#if ULAB_NUMPY_HAS_ASIN
//| def asin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse sine function"""
//| ...
//|
MATH_FUN_1(asin, asin);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_asin_obj, 1, vector_asin);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_ASIN */
#if ULAB_NUMPY_HAS_ASINH
//| def asinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse hyperbolic sine function"""
//| ...
//|
MATH_FUN_1(asinh, asinh);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_asinh_obj, 1, vector_asinh);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_ASINH */
#if ULAB_NUMPY_HAS_AROUND
//| def around(a: ulab.numpy.ndarray, *, decimals: int = 0) -> ulab.numpy.ndarray:
//| """Returns a new float array in which each element is rounded to
//| ``decimals`` places."""
//| ...
//|
mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } },
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }
#endif
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("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 *source = MP_OBJ_TO_PTR(args[0].u_obj);
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
mp_obj_t out = args[2].u_obj;
if(out != mp_const_none) {
mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for function"));
}
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
mp_float_t *narray = (mp_float_t *)ndarray->array;
uint8_t *sarray = (uint8_t *)source->array;
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
ITERATOR_HEAD();
mp_float_t f = func(sarray);
*narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul;
ITERATOR_TAIL(source, sarray);
return MP_OBJ_FROM_PTR(ndarray);
}
MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around);
#endif /* ULAB_NUMPY_HAS_AROUND */
#if ULAB_NUMPY_HAS_ATAN
//| def atan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse tangent function; the return values are in the
//| range [-pi/2,pi/2]."""
//| ...
//|
MATH_FUN_1(atan, atan);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_atan_obj, 1, vector_atan);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_ATAN */
#if ULAB_NUMPY_HAS_ATANH
//| def atanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse hyperbolic tangent function"""
//| ...
//|
MATH_FUN_1(atanh, atanh);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_atanh_obj, 1, vector_atanh);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_ATANH */
#if ULAB_NUMPY_HAS_ARCTAN2
//| def arctan2(ya: _ScalarOrArrayLike, xa: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the inverse tangent function of y/x; the return values are in
//| the range [-pi, pi]."""
//| ...
//|
mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) {
if((mp_obj_is_float(y) || mp_obj_is_int(y)) &&
(mp_obj_is_float(x) || mp_obj_is_int(x))) {
mp_float_t _y = mp_obj_get_float(y);
mp_float_t _x = mp_obj_get_float(x);
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(_y, _x));
}
ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype)
ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_y->dtype)
uint8_t ndim = 0;
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS);
int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS);
if(!ndarray_can_broadcast(ndarray_x, ndarray_y, &ndim, shape, xstrides, ystrides)) {
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
m_del(size_t, shape, ULAB_MAX_DIMS);
m_del(int32_t, xstrides, ULAB_MAX_DIMS);
m_del(int32_t, ystrides, ULAB_MAX_DIMS);
}
uint8_t *xarray = (uint8_t *)ndarray_x->array;
uint8_t *yarray = (uint8_t *)ndarray_y->array;
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
mp_float_t *rarray = (mp_float_t *)results->array;
mp_float_t (*funcx)(void *) = ndarray_get_float_function(ndarray_x->dtype);
mp_float_t (*funcy)(void *) = ndarray_get_float_function(ndarray_y->dtype);
#if ULAB_MAX_DIMS > 3
size_t i = 0;
do {
#endif
#if ULAB_MAX_DIMS > 2
size_t j = 0;
do {
#endif
#if ULAB_MAX_DIMS > 1
size_t k = 0;
do {
#endif
size_t l = 0;
do {
mp_float_t _x = funcx(xarray);
mp_float_t _y = funcy(yarray);
*rarray++ = MICROPY_FLOAT_C_FUN(atan2)(_y, _x);
xarray += xstrides[ULAB_MAX_DIMS - 1];
yarray += ystrides[ULAB_MAX_DIMS - 1];
l++;
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
#if ULAB_MAX_DIMS > 1
xarray -= xstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
xarray += xstrides[ULAB_MAX_DIMS - 2];
yarray -= ystrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
yarray += ystrides[ULAB_MAX_DIMS - 2];
k++;
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
#endif
#if ULAB_MAX_DIMS > 2
xarray -= xstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
xarray += xstrides[ULAB_MAX_DIMS - 3];
yarray -= ystrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
yarray += ystrides[ULAB_MAX_DIMS - 3];
j++;
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
#endif
#if ULAB_MAX_DIMS > 3
xarray -= xstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
xarray += xstrides[ULAB_MAX_DIMS - 4];
yarray -= ystrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
yarray += ystrides[ULAB_MAX_DIMS - 4];
i++;
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
#endif
return MP_OBJ_FROM_PTR(results);
}
MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2);
#endif /* ULAB_VECTORISE_HAS_ARCTAN2 */
#if ULAB_NUMPY_HAS_CEIL
//| def ceil(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Rounds numbers up to the next whole number"""
//| ...
//|
MATH_FUN_1(ceil, ceil);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_ceil_obj, 1, vector_ceil);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_CEIL */
#if ULAB_NUMPY_HAS_COS
//| def cos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the cosine function"""
//| ...
//|
MATH_FUN_1(cos, cos);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_cos_obj, 1, vector_cos);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_COS */
#if ULAB_NUMPY_HAS_COSH
//| def cosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the hyperbolic cosine function"""
//| ...
//|
MATH_FUN_1(cosh, cosh);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_cosh_obj, 1, vector_cosh);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_COSH */
#if ULAB_NUMPY_HAS_DEGREES
//| def degrees(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Converts angles from radians to degrees"""
//| ...
//|
static mp_float_t vector_degrees_(mp_float_t value) {
return value * MICROPY_FLOAT_CONST(180.0) / MP_PI;
}
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
static mp_obj_t vector_degrees(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return vector_generic_vector(n_args, pos_args, kw_args, vector_degrees_);
}
MP_DEFINE_CONST_FUN_OBJ_KW(vector_degrees_obj, 1, vector_degrees);
#else
static mp_obj_t vector_degrees(mp_obj_t x_obj) {
return vector_generic_vector(x_obj, vector_degrees_);
}
MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_DEGREES */
#if ULAB_SCIPY_SPECIAL_HAS_ERF
//| def erf(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the error function, which has applications in statistics"""
//| ...
//|
MATH_FUN_1(erf, erf);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_erf_obj, 1, vector_erf);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_SCIPY_SPECIAL_HAS_ERF */
#if ULAB_SCIPY_SPECIAL_HAS_ERFC
//| def erfc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the complementary error function, which has applications in statistics"""
//| ...
//|
MATH_FUN_1(erfc, erfc);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_erfc_obj, 1, vector_erfc);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_SCIPY_SPECIAL_HAS_ERFC */
#if ULAB_NUMPY_HAS_EXP
//| def exp(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the exponent function."""
//| ...
//|
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
static mp_obj_t vector_exp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
#else
static mp_obj_t vector_exp(mp_obj_t o_in) {
#endif
#if ULAB_SUPPORTS_COMPLEX
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
// since the complex case is dissimilar to the rest, we've got to do the parsing of the keywords here
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t o_in = args[0].u_obj;
mp_obj_t out = args[1].u_obj;
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
if(mp_obj_is_type(o_in, &mp_type_complex)) {
mp_float_t real, imag;
mp_obj_get_complex(o_in, &real, &imag);
mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag));
} else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
if((out != mp_const_none) && (source->dtype == NDARRAY_COMPLEX)){
mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype"));
}
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
if(source->dtype == NDARRAY_COMPLEX) {
uint8_t *sarray = (uint8_t *)source->array;
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
mp_float_t *array = (mp_float_t *)ndarray->array;
uint8_t itemsize = sizeof(mp_float_t);
ITERATOR_HEAD();
mp_float_t real = *(mp_float_t *)sarray;
mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
*array++ = exp_real * MICROPY_FLOAT_C_FUN(cos)(imag);
*array++ = exp_real * MICROPY_FLOAT_C_FUN(sin)(imag);
ITERATOR_TAIL(source, sarray);
return MP_OBJ_FROM_PTR(ndarray);
}
}
#endif /* ULAB_SUPPORTS_COMPLEX */
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(exp));
#else
return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp));
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
}
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_exp_obj, 1, vector_exp);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_EXP */
#if ULAB_NUMPY_HAS_EXPM1
//| def expm1(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function."""
//| ...
//|
MATH_FUN_1(expm1, expm1);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_expm1_obj, 1, vector_expm1);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_EXPM1 */
#if ULAB_NUMPY_HAS_FLOOR
//| def floor(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Rounds numbers up to the next whole number"""
//| ...
//|
MATH_FUN_1(floor, floor);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_floor_obj, 1, vector_floor);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_FLOOR */
#if ULAB_SCIPY_SPECIAL_HAS_GAMMA
//| def gamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the gamma function"""
//| ...
//|
MATH_FUN_1(gamma, tgamma);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_gamma_obj, 1, vector_gamma);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_SCIPY_SPECIAL_HAS_GAMMA */
#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN
//| def lgamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the natural log of the gamma function"""
//| ...
//|
MATH_FUN_1(lgamma, lgamma);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_lgamma_obj, 1, vector_lgamma);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_SCIPY_SEPCIAL_HAS_GAMMALN */
#if ULAB_NUMPY_HAS_LOG
//| def log(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the natural log"""
//| ...
//|
MATH_FUN_1(log, log);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_log_obj, 1, vector_log);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_LOG */
#if ULAB_NUMPY_HAS_LOG10
//| def log10(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the log base 10"""
//| ...
//|
MATH_FUN_1(log10, log10);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_log10_obj, 1, vector_log10);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_LOG10 */
#if ULAB_NUMPY_HAS_LOG2
//| def log2(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the log base 2"""
//| ...
//|
MATH_FUN_1(log2, log2);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_log2_obj, 1, vector_log2);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_LOG2 */
#if ULAB_NUMPY_HAS_RADIANS
//| def radians(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Converts angles from degrees to radians"""
//| ...
//|
static mp_float_t vector_radians_(mp_float_t value) {
return value * MP_PI / MICROPY_FLOAT_CONST(180.0);
}
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
static mp_obj_t vector_radians(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return vector_generic_vector(n_args, pos_args, kw_args, vector_radians_);
}
MP_DEFINE_CONST_FUN_OBJ_KW(vector_radians_obj, 1, vector_radians);
#else
static mp_obj_t vector_radians(mp_obj_t x_obj) {
return vector_generic_vector(x_obj, vector_radians_);
}
MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_RADIANS */
#if ULAB_NUMPY_HAS_SIN
//| def sin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the sine function"""
//| ...
//|
MATH_FUN_1(sin, sin);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sin_obj, 1, vector_sin);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_SIN */
#if ULAB_NUMPY_HAS_SINC
//| def sinc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the normalized sinc function"""
//| ...
//|
static mp_float_t vector_sinc1(mp_float_t x) {
if (x == MICROPY_FLOAT_CONST(0.)) {
return MICROPY_FLOAT_CONST(1.);
}
x *= MP_PI;
return MICROPY_FLOAT_C_FUN(sin)(x) / x;
}
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
static mp_obj_t vector_sinc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return vector_generic_vector(n_args, pos_args, kw_args, vector_sinc1);
}
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sinc_obj, 1, vector_sinc);
#else
static mp_obj_t vector_sinc(mp_obj_t x_obj) {
return vector_generic_vector(x_obj, vector_sinc1);
}
MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_SINC */
#if ULAB_NUMPY_HAS_SINH
//| def sinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the hyperbolic sine"""
//| ...
//|
MATH_FUN_1(sinh, sinh);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sinh_obj, 1, vector_sinh);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_SINH */
#if ULAB_NUMPY_HAS_SQRT
//| def sqrt(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the square root"""
//| ...
//|
#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD
mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
#endif
#if ULAB_SUPPORTS_COMPLEX
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } },
#endif
};
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 ULAB_SUPPORTS_COMPLEX
mp_obj_t o_in = args[0].u_obj;
uint8_t dtype = mp_obj_get_int(args[2].u_obj);
if((dtype != NDARRAY_FLOAT) && (dtype != NDARRAY_COMPLEX)) {
mp_raise_TypeError(MP_ERROR_TEXT("dtype must be float, or complex"));
}
if(mp_obj_is_type(o_in, &mp_type_complex)) {
mp_float_t real, imag;
mp_obj_get_complex(o_in, &real, &imag);
mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta));
} else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
if((source->dtype == NDARRAY_COMPLEX) && (dtype == NDARRAY_FLOAT)) {
mp_raise_TypeError(MP_ERROR_TEXT("can't convert complex to float"));
}
if(dtype == NDARRAY_COMPLEX) {
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
mp_obj_t out = args[1].u_obj;
if(out != mp_const_none) {
mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype"));
}
#endif
if(source->dtype == NDARRAY_COMPLEX) {
uint8_t *sarray = (uint8_t *)source->array;
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
mp_float_t *array = (mp_float_t *)ndarray->array;
uint8_t itemsize = sizeof(mp_float_t);
ITERATOR_HEAD();
mp_float_t real = *(mp_float_t *)sarray;
mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
*array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta);
*array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta);
ITERATOR_TAIL(source, sarray);
return MP_OBJ_FROM_PTR(ndarray);
} else if(source->dtype == NDARRAY_FLOAT) {
uint8_t *sarray = (uint8_t *)source->array;
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
mp_float_t *array = (mp_float_t *)ndarray->array;
ITERATOR_HEAD();
mp_float_t value = *(mp_float_t *)sarray;
if(value >= MICROPY_FLOAT_CONST(0.0)) {
*array++ = MICROPY_FLOAT_C_FUN(sqrt)(value);
array++;
} else {
array++;
*array++ = MICROPY_FLOAT_C_FUN(sqrt)(-value);
}
ITERATOR_TAIL(source, sarray);
return MP_OBJ_FROM_PTR(ndarray);
} else {
mp_raise_TypeError(MP_ERROR_TEXT("input dtype must be float or complex"));
}
}
}
#endif /* ULAB_SUPPORTS_COMPLEX */
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(sqrt));
}
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sqrt_obj, 1, vector_sqrt);
#else
MATH_FUN_1(sqrt, sqrt);
MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD | ULAB_SUPPORTS_COMPLEX */
#endif /* ULAB_NUMPY_HAS_SQRT */
#if ULAB_NUMPY_HAS_TAN
//| def tan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the tangent"""
//| ...
//|
MATH_FUN_1(tan, tan);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_tan_obj, 1, vector_tan);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_TAN */
#if ULAB_NUMPY_HAS_TANH
//| def tanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
//| """Computes the hyperbolic tangent"""
//| ...
MATH_FUN_1(tanh, tanh);
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DEFINE_CONST_FUN_OBJ_KW(vector_tanh_obj, 1, vector_tanh);
#else
MP_DEFINE_CONST_FUN_OBJ_1(vector_tanh_obj, vector_tanh);
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* ULAB_NUMPY_HAS_TANH */
#if ULAB_NUMPY_HAS_VECTORIZE
static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void) n_args;
(void) n_kw;
vectorized_function_obj_t *self = MP_OBJ_TO_PTR(self_in);
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]);
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, self->otypes);
uint8_t *sarray = (uint8_t *)source->array;
uint8_t *narray = (uint8_t *)ndarray->array;
ITERATOR_HEAD();
avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0);
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue);
ndarray_set_value(self->otypes, narray, 0, fvalue);
narray += ndarray->itemsize;
ITERATOR_TAIL(source, sarray);
return MP_OBJ_FROM_PTR(ndarray);
} else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) ||
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 *ndarray = ndarray_new_linear_array(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 = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue);
ndarray_set_value(self->otypes, ndarray->array, i, fvalue);
i++;
}
return MP_OBJ_FROM_PTR(ndarray);
} else if(mp_obj_is_int(args[0]) || mp_obj_is_float(args[0])) {
ndarray_obj_t *ndarray = ndarray_new_linear_array(1, self->otypes);
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, args);
ndarray_set_value(self->otypes, ndarray->array, 0, fvalue);
return MP_OBJ_FROM_PTR(ndarray);
} else {
mp_raise_ValueError(MP_ERROR_TEXT("wrong input type"));
}
return mp_const_none;
}
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
MP_DEFINE_CONST_OBJ_TYPE(
vector_function_type,
MP_QSTR_,
MP_TYPE_FLAG_NONE,
call, vector_vectorized_function_call
);
#else
const mp_obj_type_t vector_function_type = {
{ &mp_type_type },
.flags = MP_TYPE_FLAG_EXTENDED,
.name = MP_QSTR_,
MP_TYPE_EXTENDED_FIELDS(
.call = vector_vectorized_function_call,
)
};
#endif
//| def vectorize(
//| f: Union[Callable[[int], _float], Callable[[_float], _float]],
//| *,
//| otypes: Optional[_DType] = None
//| ) -> Callable[[_ScalarOrArrayLike], ulab.numpy.ndarray]:
//| """
//| :param callable f: The function to wrap
//| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float.
//|
//| Wrap a Python function ``f`` so that it can be applied to arrays or scalars. A scalar passed to the wrapped function is treated as a single-element 1-D array.
//| The callable must return only values of the types specified by ``otypes``, or the result is undefined."""
//| ...
//|
static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("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(MP_ERROR_TEXT("wrong output type"));
}
}
else {
mp_raise_ValueError(MP_ERROR_TEXT("wrong output type"));
}
vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t);
function->base.type = &vector_function_type;
function->otypes = otypes;
function->fun = args[0].u_obj;
function->type = type;
return MP_OBJ_FROM_PTR(function);
}
MP_DEFINE_CONST_FUN_OBJ_KW(vector_vectorize_obj, 1, vector_vectorize);
#endif

319
code/numpy/vector.h Normal file
View file

@ -0,0 +1,319 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Zoltán Vörös
*/
#ifndef _VECTOR_
#define _VECTOR_
#include "../ulab.h"
#include "../ndarray.h"
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DECLARE_CONST_FUN_OBJ_KW(vector_acos_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_acosh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_asin_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_asinh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_atan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_atanh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_ceil_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_cos_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_cosh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_degrees_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_erf_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_erfc_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_exp_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_expm1_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_floor_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_gamma_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_lgamma_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log10_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log2_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_radians_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sin_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinc_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinh_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_tan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_tanh_obj);
#else
MP_DECLARE_CONST_FUN_OBJ_1(vector_acos_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_acosh_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_asin_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_asinh_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_atan_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_atanh_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_ceil_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_cos_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_cosh_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_degrees_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_erf_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_erfc_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_exp_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_expm1_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_floor_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_gamma_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_lgamma_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_log_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_log10_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_log2_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_radians_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_sin_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_sinc_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_sinh_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj);
MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj);
#endif
MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj);
#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sqrt_obj);
#else
MP_DECLARE_CONST_FUN_OBJ_1(vector_sqrt_obj);
#endif
MP_DECLARE_CONST_FUN_OBJ_KW(vector_vectorize_obj);
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;
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
#if ULAB_HAS_FUNCTION_ITERATOR
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray)\
({\
size_t *scoords = ndarray_new_coords((source)->ndim);\
for(size_t i = 0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\
for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
*(tarray) = f(*((type *)(sarray)));\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
}\
ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
}\
})
#else
#if ULAB_MAX_DIMS == 1
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define ITERATE_VECTOR(type, target, tshape, tstrides, source, sarray) do {\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(tarray) = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 3] * (target)->shape[ULAB_MAX_DIMS - 3];\
(tarray) += (tstrides)[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
#define MATH_FUN_1(py_name, c_name) \
static mp_obj_t vector_ ## py_name(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { \
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(c_name)); \
}
#else /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#if ULAB_HAS_FUNCTION_ITERATOR
#define ITERATE_VECTOR(type, array, source, sarray, shift)\
({\
size_t *scoords = ndarray_new_coords((source)->ndim);\
for(size_t i=0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\
for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
*(array) = f(*((type *)(sarray)));\
(array)++;\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
}\
ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
}\
})
#else
#if ULAB_MAX_DIMS == 1
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 1 */
#if ULAB_MAX_DIMS == 2
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 2 */
#if ULAB_MAX_DIMS == 3
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 3 */
#if ULAB_MAX_DIMS == 4
#define ITERATE_VECTOR(type, array, source, sarray) do {\
size_t i = 0;\
do {\
size_t j = 0;\
do {\
size_t k = 0;\
do {\
size_t l = 0;\
do {\
*(array)++ = f(*((type *)(sarray)));\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
l++;\
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
k++;\
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
j++;\
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\
(sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
i++;\
} while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\
} while(0)
#endif /* ULAB_MAX_DIMS == 4 */
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
#define MATH_FUN_1(py_name, c_name) \
static mp_obj_t vector_ ## py_name(mp_obj_t x_obj) { \
return vector_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
}
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
#endif /* _VECTOR_ */

View file

@ -1,193 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#include "py/obj.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include "ndarray.h"
#include "linalg.h"
#include "poly.h"
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;
}
return false;
}
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_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
size_t m, n;
if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) {
ndarray_obj_t *ndx = MP_OBJ_TO_PTR(o_x);
m = ndx->m;
n = ndx->n;
} else {
mp_obj_array_t *ix = MP_OBJ_TO_PTR(o_x);
m = 1;
n = ix->len;
}
// polynomials are going to be of type float, except, when both
// the coefficients and the independent variable are integers
ndarray_obj_t *out = create_new_ndarray(m, n, NDARRAY_FLOAT);
mp_obj_iter_buf_t x_buf;
mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf);
mp_obj_iter_buf_t p_buf;
mp_obj_t p_item, p_iterable;
mp_float_t x, y;
mp_float_t *outf = (mp_float_t *)out->array->items;
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;
while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) {
p[i] = mp_obj_get_float(p_item);
i++;
}
i = 0;
while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) {
x = mp_obj_get_float(x_item);
y = p[0];
for(uint8_t j=0; j < plen-1; j++) {
y *= x;
y += p[j+1];
}
outf[i++] = y;
}
m_del(mp_float_t, p, plen);
return MP_OBJ_FROM_PTR(out);
}
mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
if((n_args != 2) && (n_args != 3)) {
mp_raise_ValueError("number of arguments must be 2, or 3");
}
if(!object_is_nditerable(args[0])) {
mp_raise_ValueError("input data must be an iterable");
}
uint16_t lenx, leny;
uint8_t deg;
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("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("input vectors must be of equal length");
}
deg = (uint8_t)mp_obj_get_int(args[2]);
if(leny < deg) {
mp_raise_ValueError("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("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);
}

View file

@ -1,17 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#ifndef _POLY_
#define _POLY_
mp_obj_t poly_polyval(mp_obj_t , mp_obj_t );
mp_obj_t poly_polyfit(size_t , const mp_obj_t *);
#endif

View file

@ -0,0 +1,701 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Harald Milz <hm@seneca.muc.de>
*
* References:
* - Dr. Robert van Engelen, Improving the mp_float_t Exponential Quadrature Tanh-Sinh, Sinh-Sinh and Exp-Sinh Formulas,
* 2021, https://www.genivia.com/qthsh.html
* - Borwein, Bailey & Girgensohn, "Experimentation in Mathematics - Computational Paths to Discovery", A K Peters,
* 2003, pages 312-313
* - Joren Vanherck, Bart Sorée, Wim Magnus, Tanh-sinh quadrature for single and multiple integration using
* floating-point arithmetic, 2020, https://arxiv.org/abs/2007.15057
* - Tanh-Sinh quadrature, Wikipedia, https://en.wikipedia.org/wiki/Tanh-sinh_quadrature
* - Romberg's method, Wikipedia, https://en.wikipedia.org/wiki/Romberg%27s_method
* - Adaptive Simpson's method, Wikipedia, https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method
* - GaussKronrod quadrature formula, Wikipedia, https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula
*
* This module provides four integration methods, and thus deviates from scipy.integrate a bit.
* As for the pros and cons of the different methods please consult the literature above.
* The code was ported to Micropython from Dr. Engelen's paper and used with his written kind permission
* - quad - Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature
* - romberg - Romberg quadrature
* - simpson - Adaptive Simpson quadrature
* - quadgk - Adaptive Gauss-Kronrod (G10,K21) quadrature
*/
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "py/objtuple.h"
#include "../../ndarray.h"
#include "../../ulab.h"
#include "../../ulab_tools.h"
#include "integrate.h"
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-14), 0x283424dcUL, 0x3e901b2b29a4692bULL);
#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-17)
#else
ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-8), 0x358637cfUL, 0x3e7010c6f7d42d18ULL);
#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-8)
#endif
#define ULAB_ZERO MICROPY_FLOAT_CONST(0.0)
#define ULAB_POINT_TWO_FIVE MICROPY_FLOAT_CONST(0.25)
#define ULAB_ONE MICROPY_FLOAT_CONST(1.0)
#define ULAB_TWO MICROPY_FLOAT_CONST(2.0)
#define ULAB_FOUR MICROPY_FLOAT_CONST(4.0)
#define ULAB_SIX MICROPY_FLOAT_CONST(6.0)
#define ULAB_TEN MICROPY_FLOAT_CONST(10.0)
#define ULAB_FIFTEEN MICROPY_FLOAT_CONST(15.0)
#define ULAB_EPSILON_5 MICROPY_FLOAT_CONST(1e-5)
static mp_float_t integrate_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) {
// Helper function for calculating the value of f(x, a, b, c, ...),
// where f is defined in python. Takes a float, returns a float.
// The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams
fargs[0] = mp_obj_new_float(x);
return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs));
}
// sign helper function
int sign(mp_float_t x) {
if (x >= ULAB_ZERO)
return 1;
else
return -1;
}
#if ULAB_INTEGRATE_HAS_TANHSINH
// Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature
// https://www.genivia.com/qthsh.html
// return optimized Exp-Sinh integral split point d
mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t eps, mp_float_t d) {
const mp_obj_type_t *type = mp_obj_get_type(fun);
mp_obj_t fargs[1];
mp_float_t h2 = integrate_python_call(type, fun, a + d/2, fargs, 0) - integrate_python_call(type, fun, (a + d*2)*4, fargs, 0);
int i = 1, j = 32; // j=32 is optimal to find r
if (isfinite(h2) && MICROPY_FLOAT_C_FUN(fabs)(h2) > ULAB_EPSILON_5) { // if |h2| > 2^-16
mp_float_t r, fl, fr, h, s = 0, lfl, lfr, lr = 2;
do { // find max j such that fl and fr are finite
j /= 2;
r = 1 << (i + j);
fl = integrate_python_call(type, fun, a + d/r, fargs, 0);
fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0);
h = fl - fr;
} while (j > 1 && !isfinite(h));
if (j > 1 && isfinite(h) && sign(h) != sign(h2)) {
lfl = fl; // last fl=f(a+d/r)
lfr = fr; // last fr=f(a+d*r)*r*r
do { // bisect in 4 iterations
j /= 2;
r = 1 << (i + j);
fl = integrate_python_call(type, fun, a + d/r, fargs, 0);
fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0);
h = fl - fr;
if (isfinite(h)) {
s += MICROPY_FLOAT_C_FUN(fabs)(h); // sum |h| to remove noisy cases
if (sign(h) == sign(h2)) {
i += j; // search right half
}
else { // search left half
lfl = fl; // record last fl=f(a+d/r)
lfr = fr; // record last fl=f(a+d*r)*r*r
lr = r; // record last r
}
}
} while (j > 1);
if (s > eps) { // if sum of |h| > eps
h = lfl - lfr; // use last fl and fr before the sign change
r = lr; // use last r before the sign change
if (h != ULAB_ZERO) // if last diff != 0, back up r by one step
r /= ULAB_TWO;
if (MICROPY_FLOAT_C_FUN(fabs)(lfl) < MICROPY_FLOAT_C_FUN(fabs)(lfr))
d /= r; // move d closer to the finite endpoint
else
d *= r; // move d closer to the infinite endpoint
}
}
}
return d;
}
// integrate function f, range a..b, max levels n, error tolerance eps
mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps, mp_float_t *e) {
const mp_obj_type_t *type = mp_obj_get_type(fun);
mp_obj_t fargs[1];
const mp_float_t tol = ULAB_TEN * eps;
mp_float_t c = ULAB_ZERO, d = ULAB_ONE, s, sign = ULAB_ONE, v, h = ULAB_TWO;
int k = 0, mode = 0; // Tanh-Sinh = 0, Exp-Sinh = 1, Sinh-Sinh = 2
if (b < a) { // swap bounds
v = b;
b = a;
a = v;
sign = -1;
}
if (isfinite(a) && isfinite(b)) {
c = (a+b) / ULAB_TWO;
d = (b-a) / ULAB_TWO;
v = c;
}
else if (isfinite(a)) {
mode = 1; // Exp-Sinh
d = exp_sinh_opt_d(fun, a, eps, d);
c = a;
v = a+d;
}
else if (isfinite(b)) {
mode = 1; // Exp-Sinh
// d = -d;
d = exp_sinh_opt_d(fun, b, eps, -d);
sign = -sign;
c = b;
v = b+d;
}
else {
mode = 2; // Sinh-Sinh
v = ULAB_ZERO;
}
s = integrate_python_call(type, fun, v, fargs, 0);
do {
mp_float_t p = ULAB_ZERO, q, fp = ULAB_ZERO, fm = ULAB_ZERO, t, eh;
h /= ULAB_TWO;
t = eh = MICROPY_FLOAT_C_FUN(exp)(h);
if (k > ULAB_ZERO)
eh *= eh;
if (mode == 0) { // Tanh-Sinh
do {
mp_float_t u = MICROPY_FLOAT_C_FUN(exp)(ULAB_ONE / t - t); // = exp(-2*sinh(j*h)) = 1/exp(sinh(j*h))^2
mp_float_t r = ULAB_TWO * u / (ULAB_ONE + u); // = 1 - tanh(sinh(j*h))
mp_float_t w = (t + ULAB_ONE / t) * r / (ULAB_ONE + u); // = cosh(j*h)/cosh(sinh(j*h))^2
mp_float_t x = d*r;
if (a+x > a) { // if too close to a then reuse previous fp
mp_float_t y = integrate_python_call(type, fun, a+x, fargs, 0);
if (isfinite(y))
fp = y; // if f(x) is finite, add to local sum
}
if (b-x < b) { // if too close to a then reuse previous fp
mp_float_t y = integrate_python_call(type, fun, b-x, fargs, 0);
if (isfinite(y))
fm = y; // if f(x) is finite, add to local sum
}
q = w*(fp+fm);
p += q;
t *= eh;
} while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p));
}
else {
t /= ULAB_TWO;
do {
mp_float_t r = MICROPY_FLOAT_C_FUN(exp)(t - ULAB_POINT_TWO_FIVE / t); // = exp(sinh(j*h))
mp_float_t x, y, w = r;
q = ULAB_ZERO;
if (mode == 1) { // Exp-Sinh
x = c + d/r;
if (x == c) // if x hit the finite endpoint then break
break;
y = integrate_python_call(type, fun, x, fargs, 0);
if (isfinite(y)) // if f(x) is finite, add to local sum
q += y/w;
}
else { // Sinh-Sinh
r = (r - ULAB_ONE / r) / ULAB_TWO; // = sinh(sinh(j*h))
w = (w + ULAB_ONE / w) / ULAB_TWO; // = cosh(sinh(j*h))
x = c - d*r;
y = integrate_python_call(type, fun, x, fargs, 0);
if (isfinite(y)) // if f(x) is finite, add to local sum
q += y*w;
}
x = c + d*r;
y = integrate_python_call(type, fun, x, fargs, 0);
if (isfinite(y)) // if f(x) is finite, add to local sum
q += y*w;
q *= t + ULAB_POINT_TWO_FIVE / t; // q *= cosh(j*h)
p += q;
t *= eh;
} while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p));
}
v = s-p;
s += p;
++k;
} while (MICROPY_FLOAT_C_FUN(fabs)(v) > tol*MICROPY_FLOAT_C_FUN(fabs)(s) && k <= n);
// return the error estimate by reference
*e = MICROPY_FLOAT_C_FUN(fabs)(v)/(MICROPY_FLOAT_C_FUN(fabs)(s)+eps);
return sign*d*s*h; // result with estimated relative error e
}
//| def tanhsinh(
//| fun: Callable[[float], float],
//| a: float,
//| b: float,
//| *,
//| levels: int = 6
//| eps: float = etolerance
//| ) -> float:
//| """
//| :param callable f: The function to integrate
//| :param float a: The lower integration limit
//| :param float b: The upper integration limit
//| :param float levels: The number of levels to perform (6..7 is optimal)
//| :param float eps: The error tolerance value
//|
//| Find a quadrature of the function ``f(x)`` on the interval
//| (``a``..``b``) using an optimized double exponential. The result is accurate to within
//| ``eps`` unless more than ``levels`` levels are required."""
//|
static mp_obj_t integrate_tanhsinh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_levels, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 6} },
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
}
// iterate over args 1, 2, and 4
// arg 3 will be handled by MP_ARG_INT above.
for (int i=1; i<=4; i*=2) {
type = mp_obj_get_type(args[i].u_obj);
if (type != &mp_type_float && type != &mp_type_int) {
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
}
}
mp_float_t a = mp_obj_get_float(args[1].u_obj);
mp_float_t b = mp_obj_get_float(args[2].u_obj);
uint16_t n = (uint16_t)args[3].u_int;
if (n < 1) {
mp_raise_ValueError(MP_ERROR_TEXT("levels needs to be a positive integer"));
}
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
mp_obj_t res[2];
mp_float_t e;
res[0] = mp_obj_new_float(tanhsinh(fun, a, b, n, eps, &e));
res[1] = mp_obj_new_float(e);
return mp_obj_new_tuple(2, res);
}
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_tanhsinh_obj, 2, integrate_tanhsinh);
#endif /* ULAB_INTEGRATE_HAS_TANHSINH */
#if ULAB_INTEGRATE_HAS_ROMBERG
// Romberg quadrature
// This function is deprecated as of SciPy 1.12.0 and will be removed in SciPy 1.15.0. Please use scipy.integrate.quad instead.
// https://en.wikipedia.org/wiki/Romberg%27s_method, https://www.genivia.com/qthsh.html,
// https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html (which is different
// insofar as the latter expects an array of function values).
mp_float_t qromb(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps) {
const mp_obj_type_t *type = mp_obj_get_type(fun);
mp_obj_t fargs[1];
mp_float_t R1[n], R2[n];
mp_float_t *Ro = &R1[0], *Ru = &R2[0];
mp_float_t h = b-a;
uint16_t i, j;
Ro[0] = (integrate_python_call(type, fun, a, fargs, 0) + integrate_python_call(type, fun, b, fargs, 0)) * h/2;
for (i = 1; i < n; ++i) {
unsigned long long k = 1UL << i;
unsigned long long s = 1;
mp_float_t sum = ULAB_ZERO;
mp_float_t *Rt;
h /= ULAB_TWO;
for (j = 1; j < k; j += 2)
sum += integrate_python_call(type, fun, a+j*h, fargs, 0);
Ru[0] = h*sum + Ro[0] / ULAB_TWO;
for (j = 1; j <= i; ++j) {
s <<= 2;
Ru[j] = (s*Ru[j-1] - Ro[j-1])/(s-1);
}
if (i > 2 && MICROPY_FLOAT_C_FUN(fabs)(Ro[i-1]-Ru[i]) <= eps*MICROPY_FLOAT_C_FUN(fabs)(Ru[i])+eps)
return Ru[i];
Rt = Ro;
Ro = Ru;
Ru = Rt;
}
return Ro[n-1];
}
//| def romberg(
//| fun: Callable[[float], float],
//| a: float,
//| b: float,
//| *,
//| steps: int = 100
//| eps: float = etolerance
//| ) -> float:
//| """
//| :param callable f: The function to integrate
//| :param float a: The lower integration limit
//| :param float b: The upper integration limit
//| :param float steps: The number of equidistant steps
//| :param float eps: The tolerance value
//|
//| Find a quadrature of the function ``f(x)`` on the interval
//| (``a``..``b``) using the Romberg method. The result is accurate to within
//| ``eps`` unless more than ``steps`` steps are required."""
//| ...
//|
static mp_obj_t integrate_romberg(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
}
// iterate over args 1, 2, and 4
// arg 3 will be handled by MP_ARG_INT above.
for (int i=1; i<=4; i*=2) {
type = mp_obj_get_type(args[i].u_obj);
if (type != &mp_type_float && type != &mp_type_int) {
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
}
}
mp_float_t a = mp_obj_get_float(args[1].u_obj);
mp_float_t b = mp_obj_get_float(args[2].u_obj);
uint16_t steps = (uint16_t)args[3].u_int;
if (steps < 1) {
mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer"));
}
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
return mp_obj_new_float(qromb(fun, a, b, steps, eps));
}
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_romberg_obj, 2, integrate_romberg);
#endif /* ULAB_INTEGRATE_HAS_ROMBERG */
#if ULAB_INTEGRATE_HAS_SIMPSON
// Adaptive Simpson quadrature
// https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method, https://www.genivia.com/qthsh.html
mp_float_t as(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, mp_float_t fa, mp_float_t fm,
mp_float_t fb, mp_float_t v, mp_float_t eps, int n, mp_float_t t) {
const mp_obj_type_t *type = mp_obj_get_type(fun);
mp_obj_t fargs[1];
mp_float_t h = (b-a) / ULAB_TWO;
mp_float_t f1 = integrate_python_call(type, fun, a + h / ULAB_TWO, fargs, 0);
mp_float_t f2 = integrate_python_call(type, fun, b - h / ULAB_TWO, fargs, 0);
mp_float_t sl = h*(fa + ULAB_FOUR * f1 + fm) / ULAB_SIX;
mp_float_t sr = h*(fm + ULAB_FOUR * f2 + fb) / ULAB_SIX;
mp_float_t s = sl+sr;
mp_float_t d = (s-v) / ULAB_FIFTEEN;
mp_float_t m = a+h;
if (n <= 0 || MICROPY_FLOAT_C_FUN(fabs)(d) < eps)
return t + s + d; // note: fabs(d) can be used as error estimate
eps /= ULAB_TWO;
--n;
t = as(fun, a, m, fa, f1, fm, sl, eps, n, t);
return as(fun, m, b, fm, f2, fb, sr, eps, n, t);
}
mp_float_t qasi(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t eps) {
const mp_obj_type_t *type = mp_obj_get_type(fun);
mp_obj_t fargs[1];
mp_float_t fa = integrate_python_call(type, fun, a, fargs, 0);
mp_float_t fm = integrate_python_call(type, fun, (a+b)/2, fargs, 0);
mp_float_t fb = integrate_python_call(type, fun, b, fargs, 0);
mp_float_t v = (fa + ULAB_FOUR * fm + fb) * (b-a) / ULAB_SIX;
return as(fun, a, b, fa, fm, fb, v, eps, n, 0);
}
//| def simpson(
//| fun: Callable[[float], float],
//| a: float,
//| b: float,
//| *,
//| steps: int = 100
//| eps: float = etolerance
//| ) -> float:
//| """
//| :param callable f: The function to integrate
//| :param float a: The lower integration limit
//| :param float b: The upper integration limit
//| :param float steps: The number of equidistant steps
//| :param float eps: The tolerance value
//|
//| Find a quadrature of the function ``f(x)`` on the interval
//| (``a``..``b``) using the Adaptive Simpson's method. The result is accurate to within
//| ``eps`` unless more than ``steps`` steps are required."""
//| ...
//|
static mp_obj_t integrate_simpson(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
// iterate over args 1, 2, and 4
// arg 3 will be handled by MP_ARG_INT above.
for (int i=1; i<=4; i*=2) {
type = mp_obj_get_type(args[i].u_obj);
if (type != &mp_type_float && type != &mp_type_int) {
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
}
}
mp_float_t a = mp_obj_get_float(args[1].u_obj);
mp_float_t b = mp_obj_get_float(args[2].u_obj);
uint16_t steps = (uint16_t)args[3].u_int;
if (steps < 1) {
mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer"));
}
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
return mp_obj_new_float(qasi(fun, a, b, steps, eps));
}
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_simpson_obj, 2, integrate_simpson);
#endif /* ULAB_INTEGRATE_HAS_SIMPSON */
#if ULAB_INTEGRATE_HAS_QUAD
// Adaptive Gauss-Kronrod (G10,K21) quadrature
// https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula, https://www.genivia.com/qthsh.html
mp_float_t gk(mp_float_t (*fun)(mp_float_t), mp_float_t c, mp_float_t d, mp_float_t *err) {
// abscissas and weights pre-calculated with Legendre Stieltjes polynomials
static const mp_float_t abscissas[21] = {
MICROPY_FLOAT_CONST(0.00000000000000000e+00),
MICROPY_FLOAT_CONST(7.65265211334973338e-02),
MICROPY_FLOAT_CONST(1.52605465240922676e-01),
MICROPY_FLOAT_CONST(2.27785851141645078e-01),
MICROPY_FLOAT_CONST(3.01627868114913004e-01),
MICROPY_FLOAT_CONST(3.73706088715419561e-01),
MICROPY_FLOAT_CONST(4.43593175238725103e-01),
MICROPY_FLOAT_CONST(5.10867001950827098e-01),
MICROPY_FLOAT_CONST(5.75140446819710315e-01),
MICROPY_FLOAT_CONST(6.36053680726515025e-01),
MICROPY_FLOAT_CONST(6.93237656334751385e-01),
MICROPY_FLOAT_CONST(7.46331906460150793e-01),
MICROPY_FLOAT_CONST(7.95041428837551198e-01),
MICROPY_FLOAT_CONST(8.39116971822218823e-01),
MICROPY_FLOAT_CONST(8.78276811252281976e-01),
MICROPY_FLOAT_CONST(9.12234428251325906e-01),
MICROPY_FLOAT_CONST(9.40822633831754754e-01),
MICROPY_FLOAT_CONST(9.63971927277913791e-01),
MICROPY_FLOAT_CONST(9.81507877450250259e-01),
MICROPY_FLOAT_CONST(9.93128599185094925e-01),
MICROPY_FLOAT_CONST(9.98859031588277664e-01),
};
static const mp_float_t weights[21] = {
MICROPY_FLOAT_CONST(7.66007119179996564e-02),
MICROPY_FLOAT_CONST(7.63778676720807367e-02),
MICROPY_FLOAT_CONST(7.57044976845566747e-02),
MICROPY_FLOAT_CONST(7.45828754004991890e-02),
MICROPY_FLOAT_CONST(7.30306903327866675e-02),
MICROPY_FLOAT_CONST(7.10544235534440683e-02),
MICROPY_FLOAT_CONST(6.86486729285216193e-02),
MICROPY_FLOAT_CONST(6.58345971336184221e-02),
MICROPY_FLOAT_CONST(6.26532375547811680e-02),
MICROPY_FLOAT_CONST(5.91114008806395724e-02),
MICROPY_FLOAT_CONST(5.51951053482859947e-02),
MICROPY_FLOAT_CONST(5.09445739237286919e-02),
MICROPY_FLOAT_CONST(4.64348218674976747e-02),
MICROPY_FLOAT_CONST(4.16688733279736863e-02),
MICROPY_FLOAT_CONST(3.66001697582007980e-02),
MICROPY_FLOAT_CONST(3.12873067770327990e-02),
MICROPY_FLOAT_CONST(2.58821336049511588e-02),
MICROPY_FLOAT_CONST(2.03883734612665236e-02),
MICROPY_FLOAT_CONST(1.46261692569712530e-02),
MICROPY_FLOAT_CONST(8.60026985564294220e-03),
MICROPY_FLOAT_CONST(3.07358371852053150e-03),
};
static const mp_float_t gauss_weights[10] = {
MICROPY_FLOAT_CONST(1.52753387130725851e-01),
MICROPY_FLOAT_CONST(1.49172986472603747e-01),
MICROPY_FLOAT_CONST(1.42096109318382051e-01),
MICROPY_FLOAT_CONST(1.31688638449176627e-01),
MICROPY_FLOAT_CONST(1.18194531961518417e-01),
MICROPY_FLOAT_CONST(1.01930119817240435e-01),
MICROPY_FLOAT_CONST(8.32767415767047487e-02),
MICROPY_FLOAT_CONST(6.26720483341090636e-02),
MICROPY_FLOAT_CONST(4.06014298003869413e-02),
MICROPY_FLOAT_CONST(1.76140071391521183e-02),
};
const mp_obj_type_t *type = mp_obj_get_type(fun);
mp_obj_t fargs[1];
mp_float_t p = ULAB_ZERO; // kronrod quadrature sum
mp_float_t q = ULAB_ZERO; // gauss quadrature sum
mp_float_t fp, fm;
mp_float_t e;
int i;
fp = integrate_python_call(type, fun, c, fargs, 0);
p = fp * weights[0];
for (i = 1; i < 21; i += 2) {
fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0);
fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0);
p += (fp + fm) * weights[i];
q += (fp + fm) * gauss_weights[i/2];
}
for (i = 2; i < 21; i += 2) {
fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0);
fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0);
p += (fp + fm) * weights[i];
}
*err = MICROPY_FLOAT_C_FUN(fabs)(p - q);
e = MICROPY_FLOAT_C_FUN(fabs)(2 * p * ULAB_MACHEPS); // optional, to take 1e-17 MachEps prec. into account
if (*err < e)
*err = e;
return p;
}
mp_float_t qakro(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t tol, mp_float_t eps, mp_float_t *err) {
mp_float_t c = (a+b) / ULAB_TWO;
mp_float_t d = (b-a) / ULAB_TWO;
mp_float_t e;
mp_float_t r = gk(fun, c, d, &e);
mp_float_t s = d*r;
mp_float_t t = MICROPY_FLOAT_C_FUN(fabs)(s*tol);
if (tol == ULAB_ZERO)
tol = t;
if (n > 0 && t < e && tol < e) {
s = qakro(fun, a, c, n-1, t / ULAB_TWO, eps, err);
s += qakro(fun, c, b, n-1, t / ULAB_TWO, eps, &e);
*err += e;
return s;
}
*err = e;
return s;
}
//| def quad(
//| fun: Callable[[float], float],
//| a: float,
//| b: float,
//| *,
//| order: int = 5
//| eps: float = etolerance
//| ) -> float:
//| """
//| :param callable f: The function to integrate
//| :param float a: The lower integration limit
//| :param float b: The upper integration limit
//| :param float order: Order of quadrature integration. Default is 5.
//| :param float eps: The tolerance value
//|
//| Find a quadrature of the function ``f(x)`` on the interval
//| (``a``..``b``) using the Adaptive Gauss-Kronrod method. The result is accurate to within
//| ``eps`` unless a higher order than ``order`` is required."""
//| ...
//|
static mp_obj_t integrate_quad(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_order, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5} },
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
}
// iterate over args 1, 2, and 4
// arg 3 will be handled by MP_ARG_INT above.
for (int i=1; i<=4; i*=2) {
type = mp_obj_get_type(args[i].u_obj);
if (type != &mp_type_float && type != &mp_type_int) {
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
}
}
mp_float_t a = mp_obj_get_float(args[1].u_obj);
mp_float_t b = mp_obj_get_float(args[2].u_obj);
uint16_t order = (uint16_t)args[3].u_int;
if (order < 1) {
mp_raise_ValueError(MP_ERROR_TEXT("order needs to be a positive integer"));
}
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
mp_obj_t res[2];
mp_float_t e;
res[0] = mp_obj_new_float(qakro(fun, a, b, order, 0, eps, &e));
res[1] = mp_obj_new_float(e);
return mp_obj_new_tuple(2, res);
}
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_quad_obj, 2, integrate_quad);
#endif /* ULAB_INTEGRATE_HAS_QUAD */
static const mp_rom_map_elem_t ulab_scipy_integrate_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_integrate) },
#if ULAB_INTEGRATE_HAS_TANHSINH
{ MP_ROM_QSTR(MP_QSTR_tanhsinh), MP_ROM_PTR(&integrate_tanhsinh_obj) },
#endif
#if ULAB_INTEGRATE_HAS_ROMBERG
{ MP_ROM_QSTR(MP_QSTR_romberg), MP_ROM_PTR(&integrate_romberg_obj) },
#endif
#if ULAB_INTEGRATE_HAS_SIMPSON
{ MP_ROM_QSTR(MP_QSTR_simpson), MP_ROM_PTR(&integrate_simpson_obj) },
#endif
#if ULAB_INTEGRATE_HAS_QUAD
{ MP_ROM_QSTR(MP_QSTR_quad), MP_ROM_PTR(&integrate_quad_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_integrate_globals, ulab_scipy_integrate_globals_table);
const mp_obj_module_t ulab_scipy_integrate_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_integrate_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_integrate, ulab_scipy_integrate_module);
#endif

View file

@ -0,0 +1,34 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Harald Milz <hm@seneca.muc.de>
*
*/
#ifndef _SCIPY_INTEGRATE_
#define _SCIPY_INTEGRATE_
#include "../../ulab_tools.h"
extern const mp_obj_module_t ulab_scipy_integrate_module;
#if ULAB_INTEGRATE_HAS_TANHSINH
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_tanhsinh_obj);
#endif
#if ULAB_INTEGRATE_HAS_ROMBERG
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_romberg_obj);
#endif
#if ULAB_INTEGRATE_HAS_SIMPSON
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_simpson_obj);
#endif
#if ULAB_INTEGRATE_HAS_QUAD
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_quad_obj);
#endif
#endif /* _SCIPY_INTEGRATE_ */

281
code/scipy/linalg/linalg.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) 2021 Vikas Udupa
*
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../../ulab.h"
#include "../../ulab_tools.h"
#include "../../numpy/linalg/linalg_tools.h"
#include "linalg.h"
#if ULAB_SCIPY_HAS_LINALG_MODULE
//|
//| import ulab.scipy
//| import ulab.numpy
//|
//| """Linear algebra functions"""
//|
#if ULAB_MAX_DIMS > 1
//| def solve_triangular(A: ulab.numpy.ndarray, b: ulab.numpy.ndarray, lower: bool) -> ulab.numpy.ndarray:
//| """
//| :param ~ulab.numpy.ndarray A: a matrix
//| :param ~ulab.numpy.ndarray b: a vector
//| :param ~bool lower: if true, use only data contained in lower triangle of A, else use upper triangle of A
//| :return: solution to the system A x = b. Shape of return matches b
//| :raises TypeError: if A and b are not of type ndarray and are not dense
//| :raises ValueError: if A is a singular matrix
//|
//| Solve the equation A x = b for x, assuming A is a triangular matrix"""
//| ...
//|
static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
size_t i, j;
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
{ MP_QSTR_lower, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_TRUE } },
};
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(MP_ERROR_TEXT("first two arguments must be ndarrays"));
}
ndarray_obj_t *A = MP_OBJ_TO_PTR(args[0].u_obj);
ndarray_obj_t *b = MP_OBJ_TO_PTR(args[1].u_obj);
if(!ndarray_is_dense(A) || !ndarray_is_dense(b)) {
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
}
size_t A_rows = A->shape[ULAB_MAX_DIMS - 2];
size_t A_cols = A->shape[ULAB_MAX_DIMS - 1];
uint8_t *A_arr = (uint8_t *)A->array;
uint8_t *b_arr = (uint8_t *)b->array;
mp_float_t (*get_A_ele)(void *) = ndarray_get_float_function(A->dtype);
mp_float_t (*get_b_ele)(void *) = ndarray_get_float_function(b->dtype);
uint8_t *temp_A = A_arr;
// check if input matrix A is singular
for (i = 0; i < A_rows; i++) {
if (MICROPY_FLOAT_C_FUN(fabs)(get_A_ele(A_arr)) < LINALG_EPSILON)
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular"));
A_arr += A->strides[ULAB_MAX_DIMS - 2];
A_arr += A->strides[ULAB_MAX_DIMS - 1];
}
A_arr = temp_A;
ndarray_obj_t *x = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT);
mp_float_t *x_arr = (mp_float_t *)x->array;
if (mp_obj_is_true(args[2].u_obj)) {
// Solve the lower triangular matrix by iterating each row of A.
// Start by finding the first unknown using the first row.
// On finding this unknown, find the second unknown using the second row.
// Continue the same till the last unknown is found using the last row.
for (i = 0; i < A_rows; i++) {
mp_float_t sum = 0.0;
for (j = 0; j < i; j++) {
sum += (get_A_ele(A_arr) * (*x_arr++));
A_arr += A->strides[ULAB_MAX_DIMS - 1];
}
sum = (get_b_ele(b_arr) - sum) / (get_A_ele(A_arr));
*x_arr = sum;
x_arr -= j;
A_arr -= A->strides[ULAB_MAX_DIMS - 1] * j;
A_arr += A->strides[ULAB_MAX_DIMS - 2];
b_arr += b->strides[ULAB_MAX_DIMS - 1];
}
} else {
// Solve the upper triangular matrix by iterating each row of A.
// Start by finding the last unknown using the last row.
// On finding this unknown, find the last-but-one unknown using the last-but-one row.
// Continue the same till the first unknown is found using the first row.
A_arr += (A->strides[ULAB_MAX_DIMS - 2] * A_rows);
b_arr += (b->strides[ULAB_MAX_DIMS - 1] * A_cols);
x_arr += A_cols;
for (i = A_rows - 1; i < A_rows; i--) {
mp_float_t sum = 0.0;
for (j = i + 1; j < A_cols; j++) {
sum += (get_A_ele(A_arr) * (*x_arr++));
A_arr += A->strides[ULAB_MAX_DIMS - 1];
}
x_arr -= (j - i);
A_arr -= (A->strides[ULAB_MAX_DIMS - 1] * (j - i));
b_arr -= b->strides[ULAB_MAX_DIMS - 1];
sum = (get_b_ele(b_arr) - sum) / get_A_ele(A_arr);
*x_arr = sum;
A_arr -= A->strides[ULAB_MAX_DIMS - 2];
}
}
return MP_OBJ_FROM_PTR(x);
}
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj, 2, solve_triangular);
//| def cho_solve(L: ulab.numpy.ndarray, b: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| :param ~ulab.numpy.ndarray L: the lower triangular, Cholesky factorization of A
//| :param ~ulab.numpy.ndarray b: right-hand-side vector b
//| :return: solution to the system A x = b. Shape of return matches b
//| :raises TypeError: if L and b are not of type ndarray and are not dense
//|
//| Solve the linear equations A x = b, given the Cholesky factorization of A as input"""
//| ...
//|
static mp_obj_t cho_solve(mp_obj_t _L, mp_obj_t _b) {
if(!mp_obj_is_type(_L, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays"));
}
ndarray_obj_t *L = MP_OBJ_TO_PTR(_L);
ndarray_obj_t *b = MP_OBJ_TO_PTR(_b);
if(!ndarray_is_dense(L) || !ndarray_is_dense(b)) {
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
}
mp_float_t (*get_L_ele)(void *) = ndarray_get_float_function(L->dtype);
mp_float_t (*get_b_ele)(void *) = ndarray_get_float_function(b->dtype);
void (*set_L_ele)(void *, mp_float_t) = ndarray_set_float_function(L->dtype);
size_t L_rows = L->shape[ULAB_MAX_DIMS - 2];
size_t L_cols = L->shape[ULAB_MAX_DIMS - 1];
// Obtain transpose of the input matrix L in L_t
size_t L_t_shape[ULAB_MAX_DIMS];
size_t L_t_rows = L_t_shape[ULAB_MAX_DIMS - 2] = L_cols;
size_t L_t_cols = L_t_shape[ULAB_MAX_DIMS - 1] = L_rows;
ndarray_obj_t *L_t = ndarray_new_dense_ndarray(L->ndim, L_t_shape, L->dtype);
uint8_t *L_arr = (uint8_t *)L->array;
uint8_t *L_t_arr = (uint8_t *)L_t->array;
uint8_t *b_arr = (uint8_t *)b->array;
size_t i, j;
uint8_t *L_ptr = L_arr;
uint8_t *L_t_ptr = L_t_arr;
for (i = 0; i < L_rows; i++) {
for (j = 0; j < L_cols; j++) {
set_L_ele(L_t_ptr, get_L_ele(L_ptr));
L_t_ptr += L_t->strides[ULAB_MAX_DIMS - 2];
L_ptr += L->strides[ULAB_MAX_DIMS - 1];
}
L_t_ptr -= j * L_t->strides[ULAB_MAX_DIMS - 2];
L_t_ptr += L_t->strides[ULAB_MAX_DIMS - 1];
L_ptr -= j * L->strides[ULAB_MAX_DIMS - 1];
L_ptr += L->strides[ULAB_MAX_DIMS - 2];
}
ndarray_obj_t *x = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT);
mp_float_t *x_arr = (mp_float_t *)x->array;
ndarray_obj_t *y = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT);
mp_float_t *y_arr = (mp_float_t *)y->array;
// solve L y = b to obtain y, where L_t x = y
for (i = 0; i < L_rows; i++) {
mp_float_t sum = 0.0;
for (j = 0; j < i; j++) {
sum += (get_L_ele(L_arr) * (*y_arr++));
L_arr += L->strides[ULAB_MAX_DIMS - 1];
}
sum = (get_b_ele(b_arr) - sum) / (get_L_ele(L_arr));
*y_arr = sum;
y_arr -= j;
L_arr -= L->strides[ULAB_MAX_DIMS - 1] * j;
L_arr += L->strides[ULAB_MAX_DIMS - 2];
b_arr += b->strides[ULAB_MAX_DIMS - 1];
}
// using y, solve L_t x = y to obtain x
L_t_arr += (L_t->strides[ULAB_MAX_DIMS - 2] * L_t_rows);
y_arr += L_t_cols;
x_arr += L_t_cols;
for (i = L_t_rows - 1; i < L_t_rows; i--) {
mp_float_t sum = 0.0;
for (j = i + 1; j < L_t_cols; j++) {
sum += (get_L_ele(L_t_arr) * (*x_arr++));
L_t_arr += L_t->strides[ULAB_MAX_DIMS - 1];
}
x_arr -= (j - i);
L_t_arr -= (L_t->strides[ULAB_MAX_DIMS - 1] * (j - i));
y_arr--;
sum = ((*y_arr) - sum) / get_L_ele(L_t_arr);
*x_arr = sum;
L_t_arr -= L_t->strides[ULAB_MAX_DIMS - 2];
}
return MP_OBJ_FROM_PTR(x);
}
MP_DEFINE_CONST_FUN_OBJ_2(linalg_cho_solve_obj, cho_solve);
#endif
static const mp_rom_map_elem_t ulab_scipy_linalg_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) },
#if ULAB_MAX_DIMS > 1
#if ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR
{ MP_ROM_QSTR(MP_QSTR_solve_triangular), MP_ROM_PTR(&linalg_solve_triangular_obj) },
#endif
#if ULAB_SCIPY_LINALG_HAS_CHO_SOLVE
{ MP_ROM_QSTR(MP_QSTR_cho_solve), MP_ROM_PTR(&linalg_cho_solve_obj) },
#endif
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_linalg_globals, ulab_scipy_linalg_globals_table);
const mp_obj_module_t ulab_scipy_linalg_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_linalg_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module);
#endif
#endif

View file

@ -0,0 +1,21 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Vikas Udupa
*
*/
#ifndef _SCIPY_LINALG_
#define _SCIPY_LINALG_
extern const mp_obj_module_t ulab_scipy_linalg_module;
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj);
MP_DECLARE_CONST_FUN_OBJ_2(linalg_cho_solve_obj);
#endif /* _SCIPY_LINALG_ */

View file

@ -0,0 +1,417 @@
/*
* 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-2021 Zoltán Vörös
* 2020 Taku Fukada
*/
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "../../ndarray.h"
#include "../../ulab.h"
#include "../../ulab_tools.h"
#include "optimize.h"
ULAB_DEFINE_FLOAT_CONST(xtolerance, MICROPY_FLOAT_CONST(2.4e-7), 0x3480d959UL, 0x3e901b2b29a4692bULL);
ULAB_DEFINE_FLOAT_CONST(rtolerance, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL);
static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) {
// Helper function for calculating the value of f(x, a, b, c, ...),
// where f is defined in python. Takes a float, returns a float.
// The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams
fargs[0] = mp_obj_new_float(x);
return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs));
}
#if ULAB_SCIPY_OPTIMIZE_HAS_BISECT
//| 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 optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// Simple bisection routine
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_xtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(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(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
mp_float_t xtol = mp_obj_get_float(args[3].u_obj);
mp_obj_t fargs[1];
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 = optimize_python_call(type, fun, a, fargs, 0);
right = optimize_python_call(type, fun, b, fargs, 0);
if(left * right > 0) {
mp_raise_ValueError(MP_ERROR_TEXT("function has the same sign at the ends of interval"));
}
mp_float_t rtb = left < MICROPY_FLOAT_CONST(0.0) ? a : b;
mp_float_t dx = left < MICROPY_FLOAT_CONST(0.0) ? b - a : a - b;
if(args[4].u_int < 0) {
mp_raise_ValueError(MP_ERROR_TEXT("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(optimize_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(optimize_bisect_obj, 3, optimize_bisect);
#endif
#if ULAB_SCIPY_OPTIMIZE_HAS_FMIN
//| 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 optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// downhill simplex method in 1D
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} },
{ MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} },
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 200} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
// parameters controlling convergence conditions
mp_float_t xatol = mp_obj_get_float(args[2].u_obj);
mp_float_t fatol = mp_obj_get_float(args[3].u_obj);
if(args[4].u_int <= 0) {
mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0"));
}
uint16_t maxiter = (uint16_t)args[4].u_int;
mp_float_t x0 = mp_obj_get_float(args[1].u_obj);
mp_float_t x1 = MICROPY_FLOAT_C_FUN(fabs)(x0) > OPTIMIZE_EPSILON ? (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_NONZDELTA) * x0 : OPTIMIZE_ZDELTA;
mp_obj_t fargs[1];
mp_float_t f0 = optimize_python_call(type, fun, x0, fargs, 0);
mp_float_t f1 = optimize_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 = optimize_python_call(type, fun, x0, fargs, 0);
f1 = optimize_python_call(type, fun, x1, fargs, 0);
// reflection
mp_float_t xr = (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_ALPHA) * x0 - OPTIMIZE_ALPHA * x1;
mp_float_t fr = optimize_python_call(type, fun, xr, fargs, 0);
if(fr < f0) { // expansion
mp_float_t xe = (1 + OPTIMIZE_ALPHA * OPTIMIZE_BETA) * x0 - OPTIMIZE_ALPHA * OPTIMIZE_BETA * x1;
mp_float_t fe = optimize_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 + OPTIMIZE_GAMMA * OPTIMIZE_ALPHA) * x0 - OPTIMIZE_GAMMA * OPTIMIZE_ALPHA * x1;
mp_float_t fc = optimize_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) - OPTIMIZE_GAMMA) * x0 + OPTIMIZE_GAMMA * x1;
mp_float_t fc = optimize_python_call(type, fun, xc, fargs, 0);
if(fc < f1) {
x1 = xc;
f1 = fc;
} else {
shrink = 1;
}
}
if(shrink == 1) {
x1 = x0 + OPTIMIZE_DELTA * (x1 - x0);
f1 = optimize_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(optimize_fmin_obj, 2, optimize_fmin);
#endif
#if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT
static void optimize_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 = optimize_python_call(type, fun, x[i], fargs0, nparams);
mp_float_t f1 = optimize_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 optimize_delta(mp_float_t *jacobi, mp_float_t *grad, uint16_t len, uint8_t nparams, mp_float_t lambda) {
//
}
mp_obj_t optimize_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_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_p0, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_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_ROM_NONE} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t fun = args[0].u_obj;
const mp_obj_type_t *type = mp_obj_get_type(fun);
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
mp_obj_t x_obj = args[1].u_obj;
mp_obj_t y_obj = args[2].u_obj;
mp_obj_t p0_obj = args[3].u_obj;
if(!ndarray_object_is_array_like(x_obj) || !ndarray_object_is_array_like(y_obj)) {
mp_raise_TypeError(MP_ERROR_TEXT("data must be iterable"));
}
if(!ndarray_object_is_nditerable(p0_obj)) {
mp_raise_TypeError(MP_ERROR_TEXT("initial values must be iterable"));
}
size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(x_obj));
uint8_t lenp = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(p0_obj));
if(len != (uint16_t)mp_obj_get_int(mp_obj_len_maybe(y_obj))) {
mp_raise_ValueError(MP_ERROR_TEXT("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(optimize_curve_fit_obj, 2, optimize_curve_fit);
#endif
#if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON
//| 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 optimize_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_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance) } },
{ MP_QSTR_rtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(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(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
}
mp_float_t x = mp_obj_get_float(args[1].u_obj);
mp_float_t tol = mp_obj_get_float(args[2].u_obj);
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) ? OPTIMIZE_EPS * x : -OPTIMIZE_EPS * x;
mp_obj_t fargs[1];
if(args[4].u_int <= 0) {
mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0"));
}
for(uint16_t i=0; i < args[4].u_int; i++) {
fx = optimize_python_call(type, fun, x, fargs, 0);
df = (optimize_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(optimize_newton_obj, 2, optimize_newton);
#endif
static const mp_rom_map_elem_t ulab_scipy_optimize_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_optimize) },
#if ULAB_SCIPY_OPTIMIZE_HAS_BISECT
{ MP_ROM_QSTR(MP_QSTR_bisect), MP_ROM_PTR(&optimize_bisect_obj) },
#endif
#if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT
{ MP_ROM_QSTR(MP_QSTR_curve_fit), MP_ROM_PTR(&optimize_curve_fit_obj) },
#endif
#if ULAB_SCIPY_OPTIMIZE_HAS_FMIN
{ MP_ROM_QSTR(MP_QSTR_fmin), MP_ROM_PTR(&optimize_fmin_obj) },
#endif
#if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON
{ MP_ROM_QSTR(MP_QSTR_newton), MP_ROM_PTR(&optimize_newton_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_optimize_globals, ulab_scipy_optimize_globals_table);
const mp_obj_module_t ulab_scipy_optimize_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_optimize_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module);
#endif

View file

@ -0,0 +1,41 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*
*/
#ifndef _SCIPY_OPTIMIZE_
#define _SCIPY_OPTIMIZE_
#include "../../ulab_tools.h"
#ifndef OPTIMIZE_EPSILON
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#define OPTIMIZE_EPSILON MICROPY_FLOAT_CONST(1.2e-7)
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
#define OPTIMIZE_EPSILON MICROPY_FLOAT_CONST(2.3e-16)
#endif
#endif
#define OPTIMIZE_EPS MICROPY_FLOAT_CONST(1.0e-4)
#define OPTIMIZE_NONZDELTA MICROPY_FLOAT_CONST(0.05)
#define OPTIMIZE_ZDELTA MICROPY_FLOAT_CONST(0.00025)
#define OPTIMIZE_ALPHA MICROPY_FLOAT_CONST(1.0)
#define OPTIMIZE_BETA MICROPY_FLOAT_CONST(2.0)
#define OPTIMIZE_GAMMA MICROPY_FLOAT_CONST(0.5)
#define OPTIMIZE_DELTA MICROPY_FLOAT_CONST(0.5)
extern const mp_obj_module_t ulab_scipy_optimize_module;
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_bisect_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_curve_fit_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_fmin_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_newton_obj);
#endif /* _SCIPY_OPTIMIZE_ */

58
code/scipy/scipy.c Normal file
View file

@ -0,0 +1,58 @@
/*
* 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-2021 Zoltán Vörös
* 2020 Taku Fukada
*/
#include <math.h>
#include "py/runtime.h"
#include "../ulab.h"
#include "optimize/optimize.h"
#include "signal/signal.h"
#include "special/special.h"
#include "linalg/linalg.h"
#include "integrate/integrate.h"
#if ULAB_HAS_SCIPY
//| """Compatibility layer for scipy"""
//|
static const mp_rom_map_elem_t ulab_scipy_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_scipy) },
#if ULAB_SCIPY_HAS_INTEGRATE_MODULE
{ MP_ROM_QSTR(MP_QSTR_integrate), MP_ROM_PTR(&ulab_scipy_integrate_module) },
#endif
#if ULAB_SCIPY_HAS_LINALG_MODULE
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) },
#endif
#if ULAB_SCIPY_HAS_OPTIMIZE_MODULE
{ MP_ROM_QSTR(MP_QSTR_optimize), MP_ROM_PTR(&ulab_scipy_optimize_module) },
#endif
#if ULAB_SCIPY_HAS_SIGNAL_MODULE
{ MP_ROM_QSTR(MP_QSTR_signal), MP_ROM_PTR(&ulab_scipy_signal_module) },
#endif
#if ULAB_SCIPY_HAS_SPECIAL_MODULE
{ MP_ROM_QSTR(MP_QSTR_special), MP_ROM_PTR(&ulab_scipy_special_module) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_globals, ulab_scipy_globals_table);
const mp_obj_module_t ulab_scipy_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module);
#endif
#endif /* ULAB_HAS_SCIPY */

21
code/scipy/scipy.h Normal file
View file

@ -0,0 +1,21 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*
*/
#ifndef _SCIPY_
#define _SCIPY_
#include "../ulab.h"
#include "../ndarray.h"
extern const mp_obj_module_t ulab_scipy_module;
#endif /* _SCIPY_ */

138
code/scipy/signal/signal.c Normal file
View file

@ -0,0 +1,138 @@
/*
* 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-2021 Zoltán Vörös
* 2020 Taku Fukada
*/
#include <math.h>
#include <string.h>
#include "py/runtime.h"
#include "../../ulab.h"
#include "../../ndarray.h"
#include "../../numpy/carray/carray_tools.h"
#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1
static void signal_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;
}
mp_obj_t signal_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_ROM_NONE } },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_zi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!ndarray_object_is_array_like(args[0].u_obj) || !ndarray_object_is_array_like(args[1].u_obj)) {
mp_raise_TypeError(MP_ERROR_TEXT("sosfilt requires iterable arguments"));
}
#if ULAB_SUPPORTS_COMPLEX
if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
}
#endif
size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj));
ndarray_obj_t *y = ndarray_new_linear_array(lenx, NDARRAY_FLOAT);
mp_float_t *yarray = (mp_float_t *)y->array;
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);
#if ULAB_MAX_DIMS > 1
if(inarray->ndim > 1) {
mp_raise_ValueError(MP_ERROR_TEXT("input must be one-dimensional"));
}
#endif
uint8_t *iarray = (uint8_t *)inarray->array;
for(size_t i=0; i < lenx; i++) {
*yarray++ = ndarray_get_float_value(iarray, inarray->dtype);
iarray += inarray->strides[ULAB_MAX_DIMS - 1];
}
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));
size_t *shape = ndarray_shape_vector(0, 0, lensos, 2);
ndarray_obj_t *zf = ndarray_new_dense_ndarray(2, shape, NDARRAY_FLOAT);
mp_float_t *zf_array = (mp_float_t *)zf->array;
if(args[2].u_obj != mp_const_none) {
if(!mp_obj_is_type(args[2].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("zi must be an ndarray"));
} else {
ndarray_obj_t *zi = MP_OBJ_TO_PTR(args[2].u_obj);
if((zi->shape[ULAB_MAX_DIMS - 2] != lensos) || (zi->shape[ULAB_MAX_DIMS - 1] != 2)) {
mp_raise_ValueError(MP_ERROR_TEXT("zi must be of shape (n_section, 2)"));
}
if(zi->dtype != NDARRAY_FLOAT) {
mp_raise_ValueError(MP_ERROR_TEXT("zi must be of float type"));
}
// TODO: this won't work with sparse arrays
memcpy(zf_array, zi->array, 2*lensos*sizeof(mp_float_t));
}
}
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if(mp_obj_get_int(mp_obj_len_maybe(item)) != 6) {
mp_raise_ValueError(MP_ERROR_TEXT("sos array must be of shape (n_section, 6)"));
} else {
fill_array_iterable(coeffs, item);
if(coeffs[3] != MICROPY_FLOAT_CONST(1.0)) {
mp_raise_ValueError(MP_ERROR_TEXT("sos[:, 3] should be all ones"));
}
signal_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 MP_OBJ_FROM_PTR(tuple);
}
}
MP_DEFINE_CONST_FUN_OBJ_KW(signal_sosfilt_obj, 2, signal_sosfilt);
#endif /* ULAB_SCIPY_SIGNAL_HAS_SOSFILT */
static const mp_rom_map_elem_t ulab_scipy_signal_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_signal) },
#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1
{ MP_ROM_QSTR(MP_QSTR_sosfilt), MP_ROM_PTR(&signal_sosfilt_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_signal_globals, ulab_scipy_signal_globals_table);
const mp_obj_module_t ulab_scipy_signal_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_signal_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module);
#endif

View file

@ -0,0 +1,23 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*
*/
#ifndef _SCIPY_SIGNAL_
#define _SCIPY_SIGNAL_
#include "../../ulab.h"
#include "../../ndarray.h"
extern const mp_obj_module_t ulab_scipy_signal_module;
MP_DECLARE_CONST_FUN_OBJ_KW(signal_sosfilt_obj);
#endif /* _SCIPY_SIGNAL_ */

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 Jeff Epler for Adafruit Industries
* 2020 Scott Shawcroft for Adafruit Industries
* 2020-2021 Zoltán Vörös
* 2020 Taku Fukada
*/
#include <math.h>
#include "py/runtime.h"
#include "../../ulab.h"
#include "../../numpy/vector.h"
static const mp_rom_map_elem_t ulab_scipy_special_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_special) },
#if ULAB_SCIPY_SPECIAL_HAS_ERF
{ MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&vector_erf_obj) },
#endif
#if ULAB_SCIPY_SPECIAL_HAS_ERFC
{ MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&vector_erfc_obj) },
#endif
#if ULAB_SCIPY_SPECIAL_HAS_GAMMA
{ MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&vector_gamma_obj) },
#endif
#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN
{ MP_ROM_QSTR(MP_QSTR_gammaln), MP_ROM_PTR(&vector_lgamma_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_special_globals, ulab_scipy_special_globals_table);
const mp_obj_module_t ulab_scipy_special_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_special_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module);
#endif

View file

@ -0,0 +1,21 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*
*/
#ifndef _SCIPY_SPECIAL_
#define _SCIPY_SPECIAL_
#include "../../ulab.h"
#include "../../ndarray.h"
extern const mp_obj_module_t ulab_scipy_special_module;
#endif /* _SCIPY_SPECIAL_ */

View file

@ -1,3 +1,4 @@
/*
* This file is part of the micropython-ulab project,
*
@ -5,7 +6,8 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
* Copyright (c) 2019-2021 Zoltán Vörös
* 2020 Jeff Epler for Adafruit Industries
*/
#include <math.h>
@ -15,173 +17,218 @@
#include "py/runtime.h"
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h" // this can in the future be dropped
#include "py/objarray.h"
#include "ulab.h"
#include "ndarray.h"
#include "linalg.h"
#include "vectorise.h"
#include "poly.h"
#include "fft.h"
#include "numerical.h"
#include "ndarray_properties.h"
#include "numpy/create.h"
#include "numpy/ndarray/ndarray_iter.h"
#define ULAB_VERSION 0.24
#include "numpy/numpy.h"
#include "scipy/scipy.h"
// TODO: we should get rid of this; array.sort depends on it
#include "numpy/numerical.h"
typedef struct _mp_obj_float_t {
mp_obj_base_t base;
mp_float_t value;
} mp_obj_float_t;
#include "user/user.h"
#include "utils/utils.h"
mp_obj_float_t ulab_version = {{&mp_type_float}, ULAB_VERSION};
#define ULAB_VERSION 6.9.0
#define xstr(s) str(s)
#define str(s) #s
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_rawsize_obj, ndarray_rawsize);
MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten);
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_asbytearray_obj, ndarray_asbytearray);
#if ULAB_SUPPORTS_COMPLEX
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D-c)
#else
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D)
#endif
MP_DEFINE_CONST_FUN_OBJ_1(linalg_transpose_obj, linalg_transpose);
MP_DEFINE_CONST_FUN_OBJ_2(linalg_reshape_obj, linalg_reshape);
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_size_obj, 1, linalg_size);
MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
MP_DEFINE_CONST_FUN_OBJ_2(linalg_dot_obj, linalg_dot);
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_zeros_obj, 0, linalg_zeros);
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_ones_obj, 0, linalg_ones);
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_eye_obj, 0, linalg_eye);
MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
static MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sinh_obj, vectorise_sinh);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sqrt_obj, vectorise_sqrt);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan);
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tanh_obj, vectorise_tanh);
#ifdef ULAB_HASH
static MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH));
#endif
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_linspace_obj, 2, numerical_linspace);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sum_obj, 1, numerical_sum);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_max_obj, 1, numerical_max);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmax_obj, 1, numerical_argmax);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_spectrum_obj, 1, 2, fft_spectrum);
STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) },
{ MP_ROM_QSTR(MP_QSTR_rawsize), MP_ROM_PTR(&ndarray_rawsize_obj) },
static const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
#if ULAB_MAX_DIMS > 1
#if NDARRAY_HAS_RESHAPE
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) },
#endif
#if NDARRAY_HAS_TRANSPOSE
{ MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&ndarray_transpose_obj) },
#endif
#endif
#if NDARRAY_HAS_BYTESWAP
{ MP_ROM_QSTR(MP_QSTR_byteswap), MP_ROM_PTR(&ndarray_byteswap_obj) },
#endif
#if NDARRAY_HAS_COPY
{ MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&ndarray_copy_obj) },
#endif
#if NDARRAY_HAS_FLATTEN
{ MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },
{ MP_ROM_QSTR(MP_QSTR_asbytearray), MP_ROM_PTR(&ndarray_asbytearray_obj) },
{ MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&linalg_transpose_obj) },
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&linalg_reshape_obj) },
#endif
#if NDARRAY_HAS_TOBYTES
{ MP_ROM_QSTR(MP_QSTR_tobytes), MP_ROM_PTR(&ndarray_tobytes_obj) },
#endif
#if NDARRAY_HAS_TOLIST
{ MP_ROM_QSTR(MP_QSTR_tolist), MP_ROM_PTR(&ndarray_tolist_obj) },
#endif
#if NDARRAY_HAS_SORT
{ MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
static MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
// MicroPython after-b41aaaa (Sept 19 2022).
#if NDARRAY_IS_SLICEABLE
#define NDARRAY_TYPE_SUBSCR subscr, ndarray_subscr,
#else
#define NDARRAY_TYPE_SUBSCR
#endif
#if NDARRAY_IS_ITERABLE
#define NDARRAY_TYPE_ITER iter, ndarray_getiter,
#define NDARRAY_TYPE_ITER_FLAGS MP_TYPE_FLAG_ITER_IS_GETITER
#else
#define NDARRAY_TYPE_ITER
#define NDARRAY_TYPE_ITER_FLAGS 0
#endif
#if NDARRAY_HAS_UNARY_OPS
#define NDARRAY_TYPE_UNARY_OP unary_op, ndarray_unary_op,
#else
#define NDARRAY_TYPE_UNARY_OP
#endif
#if NDARRAY_HAS_BINARY_OPS
#define NDARRAY_TYPE_BINARY_OP binary_op, ndarray_binary_op,
#else
#define NDARRAY_TYPE_BINARY_OP
#endif
MP_DEFINE_CONST_OBJ_TYPE(
ulab_ndarray_type,
MP_QSTR_ndarray,
MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST | NDARRAY_TYPE_ITER_FLAGS,
print, ndarray_print,
make_new, ndarray_make_new,
locals_dict, &ulab_ndarray_locals_dict,
NDARRAY_TYPE_SUBSCR
NDARRAY_TYPE_ITER
NDARRAY_TYPE_UNARY_OP
NDARRAY_TYPE_BINARY_OP
attr, ndarray_properties_attr,
buffer, ndarray_get_buffer
);
#else
// CircuitPython and earlier MicroPython revisions.
const mp_obj_type_t ulab_ndarray_type = {
{ &mp_type_type },
.flags = MP_TYPE_FLAG_EXTENDED
#if defined(MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) && defined(MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)
| 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,
.subscr = ndarray_subscr,
.getiter = ndarray_getiter,
.unary_op = ndarray_unary_op,
.binary_op = ndarray_binary_op,
.locals_dict = (mp_obj_dict_t*)&ulab_ndarray_locals_dict,
MP_TYPE_EXTENDED_FIELDS(
#if NDARRAY_IS_SLICEABLE
.subscr = ndarray_subscr,
#endif
#if NDARRAY_IS_ITERABLE
.getiter = ndarray_getiter,
#endif
#if NDARRAY_HAS_UNARY_OPS
.unary_op = ndarray_unary_op,
#endif
#if NDARRAY_HAS_BINARY_OPS
.binary_op = ndarray_binary_op,
#endif
.attr = ndarray_properties_attr,
.buffer_p = { .get_buffer = ndarray_get_buffer, },
)
};
#endif
#if ULAB_HAS_DTYPE_OBJECT
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
MP_DEFINE_CONST_OBJ_TYPE(
ulab_dtype_type,
MP_QSTR_dtype,
MP_TYPE_FLAG_NONE,
print, ndarray_dtype_print,
make_new, ndarray_dtype_make_new
);
#else
const mp_obj_type_t ulab_dtype_type = {
{ &mp_type_type },
.name = MP_QSTR_dtype,
.print = ndarray_dtype_print,
.make_new = ndarray_dtype_make_new,
};
#endif
#endif
#if NDARRAY_HAS_FLATITER
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
MP_DEFINE_CONST_OBJ_TYPE(
ndarray_flatiter_type,
MP_QSTR_flatiter,
MP_TYPE_FLAG_ITER_IS_GETITER,
iter, ndarray_get_flatiterator
);
#else
const mp_obj_type_t ndarray_flatiter_type = {
{ &mp_type_type },
.name = MP_QSTR_flatiter,
MP_TYPE_EXTENDED_FIELDS(
.getiter = ndarray_get_flatiterator,
)
};
#endif
#endif
static const mp_rom_map_elem_t ulab_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) },
{ MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) },
#ifdef ULAB_HASH
{ MP_ROM_QSTR(MP_QSTR___sha__), MP_ROM_PTR(&ulab_sha_obj) },
#endif
#if ULAB_HAS_DTYPE_OBJECT
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ulab_dtype_type) },
#else
#if NDARRAY_HAS_DTYPE
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) },
#endif /* NDARRAY_HAS_DTYPE */
#endif /* ULAB_HAS_DTYPE_OBJECT */
{ MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) },
#if ULAB_HAS_SCIPY
{ MP_ROM_QSTR(MP_QSTR_scipy), MP_ROM_PTR(&ulab_scipy_module) },
#endif
#if ULAB_HAS_USER_MODULE
{ MP_ROM_QSTR(MP_QSTR_user), MP_ROM_PTR(&ulab_user_module) },
#endif
#if ULAB_HAS_UTILS_MODULE
{ MP_ROM_QSTR(MP_QSTR_utils), MP_ROM_PTR(&ulab_utils_module) },
#endif
};
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) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_array), (mp_obj_t)&ulab_ndarray_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_size), (mp_obj_t)&linalg_size_obj },
{ MP_OBJ_NEW_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 },
{ 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 },
{ 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_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_diff), (mp_obj_t)&numerical_diff_obj },
{ 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_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 },
// class constants
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
{ MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) },
{ MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) },
{ MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) },
{ MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) },
};
STATIC MP_DEFINE_CONST_DICT (
static MP_DEFINE_CONST_DICT (
mp_module_ulab_globals,
ulab_globals_table
);
#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);
MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule);

848
code/ulab.h Normal file
View file

@ -0,0 +1,848 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2022 Zoltán Vörös
*/
#ifndef __ULAB__
#define __ULAB__
// The pre-processor constants in this file determine how ulab behaves:
//
// - how many dimensions ulab can handle
// - which functions are included in the compiled firmware
// - whether arrays can be sliced and iterated over
// - which binary/unary operators are supported
// - whether ulab can deal with complex numbers
//
// A considerable amount of flash space can be saved by removing (setting
// the corresponding constants to 0) the unnecessary functions and features.
// Values defined here can be overridden by your own config file as
// make -DULAB_CONFIG_FILE="my_ulab_config.h"
#if defined(ULAB_CONFIG_FILE)
#include ULAB_CONFIG_FILE
#endif
// Adds support for complex ndarrays
#ifndef ULAB_SUPPORTS_COMPLEX
#define ULAB_SUPPORTS_COMPLEX (1)
#endif
// Determines, whether scipy is defined in ulab. The sub-modules and functions
// of scipy have to be defined separately
#ifndef ULAB_HAS_SCIPY
#define ULAB_HAS_SCIPY (1)
#endif
// The maximum number of dimensions the firmware should be able to support
// Possible values lie between 1, and 4, inclusive
#ifndef ULAB_MAX_DIMS
#define ULAB_MAX_DIMS 2
#endif
// By setting this constant to 1, iteration over array dimensions will be implemented
// as a function (ndarray_rewind_array), instead of writing out the loops in macros
// This reduces firmware size at the expense of speed
#ifndef ULAB_HAS_FUNCTION_ITERATOR
#define ULAB_HAS_FUNCTION_ITERATOR (0)
#endif
// If NDARRAY_IS_ITERABLE is 1, the ndarray object defines its own iterator function
// This option saves approx. 250 bytes of flash space
#ifndef NDARRAY_IS_ITERABLE
#define NDARRAY_IS_ITERABLE (1)
#endif
// Slicing can be switched off by setting this variable to 0
#ifndef NDARRAY_IS_SLICEABLE
#define NDARRAY_IS_SLICEABLE (1)
#endif
// The default threshold for pretty printing. These variables can be overwritten
// at run-time via the set_printoptions() function
#ifndef ULAB_HAS_PRINTOPTIONS
#define ULAB_HAS_PRINTOPTIONS (1)
#endif
#define NDARRAY_PRINT_THRESHOLD 10
#define NDARRAY_PRINT_EDGEITEMS 3
// determines, whether the dtype is an object, or simply a character
// the object implementation is numpythonic, but requires more space
#ifndef ULAB_HAS_DTYPE_OBJECT
#define ULAB_HAS_DTYPE_OBJECT (0)
#endif
// the ndarray binary operators
#ifndef NDARRAY_HAS_BINARY_OPS
#define NDARRAY_HAS_BINARY_OPS (1)
#endif
// Firmware size can be reduced at the expense of speed by using function
// pointers in iterations. For each operator, he function pointer saves around
// 2 kB in the two-dimensional case, and around 4 kB in the four-dimensional case.
#ifndef NDARRAY_BINARY_USES_FUN_POINTER
#define NDARRAY_BINARY_USES_FUN_POINTER (0)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_ADD
#define NDARRAY_HAS_BINARY_OP_ADD (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_AND
#define NDARRAY_HAS_BINARY_OP_AND (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_EQUAL
#define NDARRAY_HAS_BINARY_OP_EQUAL (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE
#define NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_LESS
#define NDARRAY_HAS_BINARY_OP_LESS (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_LESS_EQUAL
#define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_MODULO
#define NDARRAY_HAS_BINARY_OP_MODULO (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_MORE
#define NDARRAY_HAS_BINARY_OP_MORE (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_MORE_EQUAL
#define NDARRAY_HAS_BINARY_OP_MORE_EQUAL (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_MULTIPLY
#define NDARRAY_HAS_BINARY_OP_MULTIPLY (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_NOT_EQUAL
#define NDARRAY_HAS_BINARY_OP_NOT_EQUAL (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_OR
#define NDARRAY_HAS_BINARY_OP_OR (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_POWER
#define NDARRAY_HAS_BINARY_OP_POWER (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_SUBTRACT
#define NDARRAY_HAS_BINARY_OP_SUBTRACT (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE
#define NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE (1)
#endif
#ifndef NDARRAY_HAS_BINARY_OP_XOR
#define NDARRAY_HAS_BINARY_OP_XOR (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_OPS
#define NDARRAY_HAS_INPLACE_OPS (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_ADD
#define NDARRAY_HAS_INPLACE_ADD (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_MODULO
#define NDARRAY_HAS_INPLACE_MODU (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_MULTIPLY
#define NDARRAY_HAS_INPLACE_MULTIPLY (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_POWER
#define NDARRAY_HAS_INPLACE_POWER (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_SUBTRACT
#define NDARRAY_HAS_INPLACE_SUBTRACT (1)
#endif
#ifndef NDARRAY_HAS_INPLACE_TRUE_DIVIDE
#define NDARRAY_HAS_INPLACE_TRUE_DIVIDE (1)
#endif
// bitwise operators
#ifndef ULAB_NUMPY_HAS_BITWISE_AND
#define ULAB_NUMPY_HAS_BITWISE_AND (1)
#endif
#ifndef ULAB_NUMPY_HAS_BITWISE_OR
#define ULAB_NUMPY_HAS_BITWISE_OR (1)
#endif
#ifndef ULAB_NUMPY_HAS_BITWISE_XOR
#define ULAB_NUMPY_HAS_BITWISE_XOR (1)
#endif
#ifndef ULAB_NUMPY_HAS_LEFT_SHIFT
#define ULAB_NUMPY_HAS_LEFT_SHIFT (1)
#endif
#ifndef ULAB_NUMPY_HAS_RIGHT_SHIFT
#define ULAB_NUMPY_HAS_RIGHT_SHIFT (1)
#endif
// the ndarray unary operators
#ifndef NDARRAY_HAS_UNARY_OPS
#define NDARRAY_HAS_UNARY_OPS (1)
#endif
#ifndef NDARRAY_HAS_UNARY_OP_ABS
#define NDARRAY_HAS_UNARY_OP_ABS (1)
#endif
#ifndef NDARRAY_HAS_UNARY_OP_INVERT
#define NDARRAY_HAS_UNARY_OP_INVERT (1)
#endif
#ifndef NDARRAY_HAS_UNARY_OP_LEN
#define NDARRAY_HAS_UNARY_OP_LEN (1)
#endif
#ifndef NDARRAY_HAS_UNARY_OP_NEGATIVE
#define NDARRAY_HAS_UNARY_OP_NEGATIVE (1)
#endif
#ifndef NDARRAY_HAS_UNARY_OP_POSITIVE
#define NDARRAY_HAS_UNARY_OP_POSITIVE (1)
#endif
// determines, which ndarray methods are available
#ifndef NDARRAY_HAS_BYTESWAP
#define NDARRAY_HAS_BYTESWAP (1)
#endif
#ifndef NDARRAY_HAS_COPY
#define NDARRAY_HAS_COPY (1)
#endif
#ifndef NDARRAY_HAS_DTYPE
#define NDARRAY_HAS_DTYPE (1)
#endif
#ifndef NDARRAY_HAS_FLATTEN
#define NDARRAY_HAS_FLATTEN (1)
#endif
#ifndef NDARRAY_HAS_ITEMSIZE
#define NDARRAY_HAS_ITEMSIZE (1)
#endif
#ifndef NDARRAY_HAS_NDIM
#define NDARRAY_HAS_NDIM (1)
#endif
#ifndef NDARRAY_HAS_RESHAPE
#define NDARRAY_HAS_RESHAPE (1)
#endif
#ifndef NDARRAY_HAS_SHAPE
#define NDARRAY_HAS_SHAPE (1)
#endif
#ifndef NDARRAY_HAS_SIZE
#define NDARRAY_HAS_SIZE (1)
#endif
#ifndef NDARRAY_HAS_SORT
#define NDARRAY_HAS_SORT (1)
#endif
#ifndef NDARRAY_HAS_STRIDES
#define NDARRAY_HAS_STRIDES (1)
#endif
#ifndef NDARRAY_HAS_TOBYTES
#define NDARRAY_HAS_TOBYTES (1)
#endif
#ifndef NDARRAY_HAS_TOLIST
#define NDARRAY_HAS_TOLIST (1)
#endif
#ifndef NDARRAY_HAS_TRANSPOSE
#define NDARRAY_HAS_TRANSPOSE (1)
#endif
// Firmware size can be reduced at the expense of speed by using a function
// pointer in iterations. Setting ULAB_VECTORISE_USES_FUNCPOINTER to 1 saves
// around 800 bytes in the four-dimensional case, and around 200 in two dimensions.
#ifndef ULAB_VECTORISE_USES_FUN_POINTER
#define ULAB_VECTORISE_USES_FUN_POINTER (1)
#endif
// determines, whether e is defined in ulab.numpy itself
#ifndef ULAB_NUMPY_HAS_E
#define ULAB_NUMPY_HAS_E (1)
#endif
// ulab defines infinite as a class constant in ulab.numpy
#ifndef ULAB_NUMPY_HAS_INF
#define ULAB_NUMPY_HAS_INF (1)
#endif
// ulab defines NaN as a class constant in ulab.numpy
#ifndef ULAB_NUMPY_HAS_NAN
#define ULAB_NUMPY_HAS_NAN (1)
#endif
// determines, whether pi is defined in ulab.numpy itself
#ifndef ULAB_NUMPY_HAS_PI
#define ULAB_NUMPY_HAS_PI (1)
#endif
// determines, whether the ndinfo function is available
#ifndef ULAB_NUMPY_HAS_NDINFO
#define ULAB_NUMPY_HAS_NDINFO (1)
#endif
// if this constant is set to 1, the interpreter can iterate
// over the flat array without copying any data
#ifndef NDARRAY_HAS_FLATITER
#define NDARRAY_HAS_FLATITER (1)
#endif
// frombuffer adds 600 bytes to the firmware
#ifndef ULAB_NUMPY_HAS_FROMBUFFER
#define ULAB_NUMPY_HAS_FROMBUFFER (1)
#endif
// functions that create an array
#ifndef ULAB_NUMPY_HAS_ARANGE
#define ULAB_NUMPY_HAS_ARANGE (1)
#endif
#ifndef ULAB_NUMPY_HAS_CONCATENATE
#define ULAB_NUMPY_HAS_CONCATENATE (1)
#endif
#ifndef ULAB_NUMPY_HAS_DIAG
#define ULAB_NUMPY_HAS_DIAG (1)
#endif
#ifndef ULAB_NUMPY_HAS_EMPTY
#define ULAB_NUMPY_HAS_EMPTY (1)
#endif
#ifndef ULAB_NUMPY_HAS_EYE
#define ULAB_NUMPY_HAS_EYE (1)
#endif
#ifndef ULAB_NUMPY_HAS_FULL
#define ULAB_NUMPY_HAS_FULL (1)
#endif
#ifndef ULAB_NUMPY_HAS_LINSPACE
#define ULAB_NUMPY_HAS_LINSPACE (1)
#endif
#ifndef ULAB_NUMPY_HAS_LOGSPACE
#define ULAB_NUMPY_HAS_LOGSPACE (1)
#endif
#ifndef ULAB_NUMPY_HAS_ONES
#define ULAB_NUMPY_HAS_ONES (1)
#endif
#ifndef ULAB_NUMPY_HAS_ZEROS
#define ULAB_NUMPY_HAS_ZEROS (1)
#endif
// functions that compare arrays
#ifndef ULAB_NUMPY_HAS_CLIP
#define ULAB_NUMPY_HAS_CLIP (1)
#endif
#ifndef ULAB_NUMPY_HAS_EQUAL
#define ULAB_NUMPY_HAS_EQUAL (1)
#endif
#ifndef ULAB_NUMPY_HAS_ISFINITE
#define ULAB_NUMPY_HAS_ISFINITE (1)
#endif
#ifndef ULAB_NUMPY_HAS_ISINF
#define ULAB_NUMPY_HAS_ISINF (1)
#endif
#ifndef ULAB_NUMPY_HAS_MAXIMUM
#define ULAB_NUMPY_HAS_MAXIMUM (1)
#endif
#ifndef ULAB_NUMPY_HAS_MINIMUM
#define ULAB_NUMPY_HAS_MINIMUM (1)
#endif
#ifndef ULAB_NUMPY_HAS_NONZERO
#define ULAB_NUMPY_HAS_NONZERO (1)
#endif
#ifndef ULAB_NUMPY_HAS_NOTEQUAL
#define ULAB_NUMPY_HAS_NOTEQUAL (1)
#endif
#ifndef ULAB_NUMPY_HAS_WHERE
#define ULAB_NUMPY_HAS_WHERE (1)
#endif
// the integrate module; functions of the integrate module still have
// to be defined separately
#ifndef ULAB_SCIPY_HAS_INTEGRATE_MODULE
#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_TANHSINH
#define ULAB_INTEGRATE_HAS_TANHSINH (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_ROMBERG
#define ULAB_INTEGRATE_HAS_ROMBERG (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_SIMPSON
#define ULAB_INTEGRATE_HAS_SIMPSON (1)
#endif
#ifndef ULAB_INTEGRATE_HAS_QUAD
#define ULAB_INTEGRATE_HAS_QUAD (1)
#endif
// the linalg module; functions of the linalg module still have
// to be defined separately
#ifndef ULAB_NUMPY_HAS_LINALG_MODULE
#define ULAB_NUMPY_HAS_LINALG_MODULE (1)
#endif
#ifndef ULAB_LINALG_HAS_CHOLESKY
#define ULAB_LINALG_HAS_CHOLESKY (1)
#endif
#ifndef ULAB_LINALG_HAS_DET
#define ULAB_LINALG_HAS_DET (1)
#endif
#ifndef ULAB_LINALG_HAS_EIG
#define ULAB_LINALG_HAS_EIG (1)
#endif
#ifndef ULAB_LINALG_HAS_INV
#define ULAB_LINALG_HAS_INV (1)
#endif
#ifndef ULAB_LINALG_HAS_NORM
#define ULAB_LINALG_HAS_NORM (1)
#endif
#ifndef ULAB_LINALG_HAS_QR
#define ULAB_LINALG_HAS_QR (1)
#endif
// the FFT module; functions of the fft module still have
// to be defined separately
#ifndef ULAB_NUMPY_HAS_FFT_MODULE
#define ULAB_NUMPY_HAS_FFT_MODULE (1)
#endif
// By setting this constant to 1, the FFT routine will behave in a
// numpy-compatible way, i.e., it will output a complex array
// This setting has no effect, if ULAB_SUPPORTS_COMPLEX is 0
// Note that in this case, the input also must be numpythonic,
// i.e., the real an imaginary parts cannot be passed as two arguments
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
#endif
#ifndef ULAB_FFT_HAS_FFT
#define ULAB_FFT_HAS_FFT (1)
#endif
#ifndef ULAB_FFT_HAS_IFFT
#define ULAB_FFT_HAS_IFFT (1)
#endif
#ifndef ULAB_NUMPY_HAS_ALL
#define ULAB_NUMPY_HAS_ALL (1)
#endif
#ifndef ULAB_NUMPY_HAS_ANY
#define ULAB_NUMPY_HAS_ANY (1)
#endif
#ifndef ULAB_NUMPY_HAS_ARGMINMAX
#define ULAB_NUMPY_HAS_ARGMINMAX (1)
#endif
#ifndef ULAB_NUMPY_HAS_ARGSORT
#define ULAB_NUMPY_HAS_ARGSORT (1)
#endif
#ifndef ULAB_NUMPY_HAS_ASARRAY
#define ULAB_NUMPY_HAS_ASARRAY (1)
#endif
#ifndef ULAB_NUMPY_HAS_COMPRESS
#define ULAB_NUMPY_HAS_COMPRESS (1)
#endif
#ifndef ULAB_NUMPY_HAS_CONVOLVE
#define ULAB_NUMPY_HAS_CONVOLVE (1)
#endif
#ifndef ULAB_NUMPY_HAS_CROSS
#define ULAB_NUMPY_HAS_CROSS (1)
#endif
#ifndef ULAB_NUMPY_HAS_DELETE
#define ULAB_NUMPY_HAS_DELETE (1)
#endif
#ifndef ULAB_NUMPY_HAS_DIFF
#define ULAB_NUMPY_HAS_DIFF (1)
#endif
#ifndef ULAB_NUMPY_HAS_DOT
#define ULAB_NUMPY_HAS_DOT (1)
#endif
#ifndef ULAB_NUMPY_HAS_FLIP
#define ULAB_NUMPY_HAS_FLIP (1)
#endif
#ifndef ULAB_NUMPY_HAS_INTERP
#define ULAB_NUMPY_HAS_INTERP (1)
#endif
#ifndef ULAB_NUMPY_HAS_LOAD
#define ULAB_NUMPY_HAS_LOAD (1)
#endif
#ifndef ULAB_NUMPY_HAS_LOADTXT
#define ULAB_NUMPY_HAS_LOADTXT (1)
#endif
#ifndef ULAB_NUMPY_HAS_MEAN
#define ULAB_NUMPY_HAS_MEAN (1)
#endif
#ifndef ULAB_NUMPY_HAS_MEDIAN
#define ULAB_NUMPY_HAS_MEDIAN (1)
#endif
#ifndef ULAB_NUMPY_HAS_MINMAX
#define ULAB_NUMPY_HAS_MINMAX (1)
#endif
#ifndef ULAB_NUMPY_HAS_POLYFIT
#define ULAB_NUMPY_HAS_POLYFIT (1)
#endif
#ifndef ULAB_NUMPY_HAS_POLYVAL
#define ULAB_NUMPY_HAS_POLYVAL (1)
#endif
#ifndef ULAB_NUMPY_HAS_ROLL
#define ULAB_NUMPY_HAS_ROLL (1)
#endif
#ifndef ULAB_NUMPY_HAS_SAVE
#define ULAB_NUMPY_HAS_SAVE (1)
#endif
#ifndef ULAB_NUMPY_HAS_SAVETXT
#define ULAB_NUMPY_HAS_SAVETXT (1)
#endif
#ifndef ULAB_NUMPY_HAS_SIZE
#define ULAB_NUMPY_HAS_SIZE (1)
#endif
#ifndef ULAB_NUMPY_HAS_SORT
#define ULAB_NUMPY_HAS_SORT (1)
#endif
#ifndef ULAB_NUMPY_HAS_STD
#define ULAB_NUMPY_HAS_STD (1)
#endif
#ifndef ULAB_NUMPY_HAS_SUM
#define ULAB_NUMPY_HAS_SUM (1)
#endif
#ifndef ULAB_NUMPY_HAS_TAKE
#define ULAB_NUMPY_HAS_TAKE (1)
#endif
#ifndef ULAB_NUMPY_HAS_TRACE
#define ULAB_NUMPY_HAS_TRACE (1)
#endif
#ifndef ULAB_NUMPY_HAS_TRAPZ
#define ULAB_NUMPY_HAS_TRAPZ (1)
#endif
// vectorised versions of the functions of the math python module, with
// the exception of the functions listed in scipy.special
// if this constant is set, math functions support the out keyword argument
#ifndef ULAB_MATH_FUNCTIONS_OUT_KEYWORD
#define ULAB_MATH_FUNCTIONS_OUT_KEYWORD (1)
#endif
#ifndef ULAB_NUMPY_HAS_ACOS
#define ULAB_NUMPY_HAS_ACOS (1)
#endif
#ifndef ULAB_NUMPY_HAS_ACOSH
#define ULAB_NUMPY_HAS_ACOSH (1)
#endif
#ifndef ULAB_NUMPY_HAS_ARCTAN2
#define ULAB_NUMPY_HAS_ARCTAN2 (1)
#endif
#ifndef ULAB_NUMPY_HAS_AROUND
#define ULAB_NUMPY_HAS_AROUND (1)
#endif
#ifndef ULAB_NUMPY_HAS_ASIN
#define ULAB_NUMPY_HAS_ASIN (1)
#endif
#ifndef ULAB_NUMPY_HAS_ASINH
#define ULAB_NUMPY_HAS_ASINH (1)
#endif
#ifndef ULAB_NUMPY_HAS_ATAN
#define ULAB_NUMPY_HAS_ATAN (1)
#endif
#ifndef ULAB_NUMPY_HAS_ATANH
#define ULAB_NUMPY_HAS_ATANH (1)
#endif
#ifndef ULAB_NUMPY_HAS_CEIL
#define ULAB_NUMPY_HAS_CEIL (1)
#endif
#ifndef ULAB_NUMPY_HAS_COS
#define ULAB_NUMPY_HAS_COS (1)
#endif
#ifndef ULAB_NUMPY_HAS_COSH
#define ULAB_NUMPY_HAS_COSH (1)
#endif
#ifndef ULAB_NUMPY_HAS_DEGREES
#define ULAB_NUMPY_HAS_DEGREES (1)
#endif
#ifndef ULAB_NUMPY_HAS_EXP
#define ULAB_NUMPY_HAS_EXP (1)
#endif
#ifndef ULAB_NUMPY_HAS_EXPM1
#define ULAB_NUMPY_HAS_EXPM1 (1)
#endif
#ifndef ULAB_NUMPY_HAS_FLOOR
#define ULAB_NUMPY_HAS_FLOOR (1)
#endif
#ifndef ULAB_NUMPY_HAS_LOG
#define ULAB_NUMPY_HAS_LOG (1)
#endif
#ifndef ULAB_NUMPY_HAS_LOG10
#define ULAB_NUMPY_HAS_LOG10 (1)
#endif
#ifndef ULAB_NUMPY_HAS_LOG2
#define ULAB_NUMPY_HAS_LOG2 (1)
#endif
#ifndef ULAB_NUMPY_HAS_RADIANS
#define ULAB_NUMPY_HAS_RADIANS (1)
#endif
#ifndef ULAB_NUMPY_HAS_SIN
#define ULAB_NUMPY_HAS_SIN (1)
#endif
#ifndef ULAB_NUMPY_HAS_SINC
#define ULAB_NUMPY_HAS_SINC (1)
#endif
#ifndef ULAB_NUMPY_HAS_SINH
#define ULAB_NUMPY_HAS_SINH (1)
#endif
#ifndef ULAB_NUMPY_HAS_SQRT
#define ULAB_NUMPY_HAS_SQRT (1)
#endif
#ifndef ULAB_NUMPY_HAS_TAN
#define ULAB_NUMPY_HAS_TAN (1)
#endif
#ifndef ULAB_NUMPY_HAS_TANH
#define ULAB_NUMPY_HAS_TANH (1)
#endif
#ifndef ULAB_NUMPY_HAS_VECTORIZE
#define ULAB_NUMPY_HAS_VECTORIZE (1)
#endif
// Complex functions. The implementations are compiled into
// the firmware, only if ULAB_SUPPORTS_COMPLEX is set to 1
#ifndef ULAB_NUMPY_HAS_CONJUGATE
#define ULAB_NUMPY_HAS_CONJUGATE (1)
#endif
#ifndef ULAB_NUMPY_HAS_IMAG
#define ULAB_NUMPY_HAS_IMAG (1)
#endif
#ifndef ULAB_NUMPY_HAS_REAL
#define ULAB_NUMPY_HAS_REAL (1)
#endif
#ifndef ULAB_NUMPY_HAS_SORT_COMPLEX
#define ULAB_NUMPY_HAS_SORT_COMPLEX (1)
#endif
// random module
#ifndef ULAB_NUMPY_HAS_RANDOM_MODULE
#define ULAB_NUMPY_HAS_RANDOM_MODULE (1)
#endif
#ifndef ULAB_NUMPY_RANDOM_HAS_NORMAL
#define ULAB_NUMPY_RANDOM_HAS_NORMAL (1)
#endif
#ifndef ULAB_NUMPY_RANDOM_HAS_RANDOM
#define ULAB_NUMPY_RANDOM_HAS_RANDOM (1)
#endif
#ifndef ULAB_NUMPY_RANDOM_HAS_UNIFORM
#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1)
#endif
// scipy modules
#ifndef ULAB_SCIPY_HAS_LINALG_MODULE
#define ULAB_SCIPY_HAS_LINALG_MODULE (1)
#endif
#ifndef ULAB_SCIPY_LINALG_HAS_CHO_SOLVE
#define ULAB_SCIPY_LINALG_HAS_CHO_SOLVE (1)
#endif
#ifndef ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR
#define ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR (1)
#endif
#ifndef ULAB_SCIPY_HAS_SIGNAL_MODULE
#define ULAB_SCIPY_HAS_SIGNAL_MODULE (1)
#endif
#ifndef ULAB_SCIPY_SIGNAL_HAS_SOSFILT
#define ULAB_SCIPY_SIGNAL_HAS_SOSFILT (1)
#endif
#ifndef ULAB_SCIPY_HAS_OPTIMIZE_MODULE
#define ULAB_SCIPY_HAS_OPTIMIZE_MODULE (1)
#endif
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_BISECT
#define ULAB_SCIPY_OPTIMIZE_HAS_BISECT (1)
#endif
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT
#define ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT (0) // not fully implemented
#endif
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_FMIN
#define ULAB_SCIPY_OPTIMIZE_HAS_FMIN (1)
#endif
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_NEWTON
#define ULAB_SCIPY_OPTIMIZE_HAS_NEWTON (1)
#endif
#ifndef ULAB_SCIPY_HAS_SPECIAL_MODULE
#define ULAB_SCIPY_HAS_SPECIAL_MODULE (1)
#endif
#ifndef ULAB_SCIPY_SPECIAL_HAS_ERF
#define ULAB_SCIPY_SPECIAL_HAS_ERF (1)
#endif
#ifndef ULAB_SCIPY_SPECIAL_HAS_ERFC
#define ULAB_SCIPY_SPECIAL_HAS_ERFC (1)
#endif
#ifndef ULAB_SCIPY_SPECIAL_HAS_GAMMA
#define ULAB_SCIPY_SPECIAL_HAS_GAMMA (1)
#endif
#ifndef ULAB_SCIPY_SPECIAL_HAS_GAMMALN
#define ULAB_SCIPY_SPECIAL_HAS_GAMMALN (1)
#endif
// functions of the utils module
#ifndef ULAB_HAS_UTILS_MODULE
#define ULAB_HAS_UTILS_MODULE (1)
#endif
#ifndef ULAB_UTILS_HAS_FROM_INT16_BUFFER
#define ULAB_UTILS_HAS_FROM_INT16_BUFFER (1)
#endif
#ifndef ULAB_UTILS_HAS_FROM_UINT16_BUFFER
#define ULAB_UTILS_HAS_FROM_UINT16_BUFFER (1)
#endif
#ifndef ULAB_UTILS_HAS_FROM_INT32_BUFFER
#define ULAB_UTILS_HAS_FROM_INT32_BUFFER (1)
#endif
#ifndef ULAB_UTILS_HAS_FROM_UINT32_BUFFER
#define ULAB_UTILS_HAS_FROM_UINT32_BUFFER (1)
#endif
#ifndef ULAB_UTILS_HAS_SPECTROGRAM
#define ULAB_UTILS_HAS_SPECTROGRAM (1)
#endif
// user-defined module; source of the module and
// its sub-modules should be placed in code/user/
#ifndef ULAB_HAS_USER_MODULE
#define ULAB_HAS_USER_MODULE (0)
#endif
#endif

332
code/ulab_tools.c Normal file
View file

@ -0,0 +1,332 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2022 Zoltán Vörös
*/
#include <string.h>
#include "py/runtime.h"
#include "ulab.h"
#include "ndarray.h"
#include "ulab_tools.h"
// The following five functions return a float from a void type
// The value in question is supposed to be located at the head of the pointer
mp_float_t ndarray_get_float_uint8(void *data) {
// Returns a float value from an uint8_t type
return (mp_float_t)(*(uint8_t *)data);
}
mp_float_t ndarray_get_float_int8(void *data) {
// Returns a float value from an int8_t type
return (mp_float_t)(*(int8_t *)data);
}
mp_float_t ndarray_get_float_uint16(void *data) {
// Returns a float value from an uint16_t type
return (mp_float_t)(*(uint16_t *)data);
}
mp_float_t ndarray_get_float_int16(void *data) {
// Returns a float value from an int16_t type
return (mp_float_t)(*(int16_t *)data);
}
mp_float_t ndarray_get_float_float(void *data) {
// Returns a float value from an mp_float_t type
return *((mp_float_t *)data);
}
// returns a single function pointer, depending on the dtype
void *ndarray_get_float_function(uint8_t dtype) {
if(dtype == NDARRAY_UINT8) {
return ndarray_get_float_uint8;
} else if(dtype == NDARRAY_INT8) {
return ndarray_get_float_int8;
} else if(dtype == NDARRAY_UINT16) {
return ndarray_get_float_uint16;
} else if(dtype == NDARRAY_INT16) {
return ndarray_get_float_int16;
} else {
return ndarray_get_float_float;
}
}
mp_float_t ndarray_get_float_index(void *data, uint8_t dtype, size_t index) {
// returns a single float value from an array located at index
if(dtype == NDARRAY_UINT8) {
return (mp_float_t)((uint8_t *)data)[index];
} else if(dtype == NDARRAY_INT8) {
return (mp_float_t)((int8_t *)data)[index];
} else if(dtype == NDARRAY_UINT16) {
return (mp_float_t)((uint16_t *)data)[index];
} else if(dtype == NDARRAY_INT16) {
return (mp_float_t)((int16_t *)data)[index];
} else {
return (mp_float_t)((mp_float_t *)data)[index];
}
}
mp_float_t ndarray_get_float_value(void *data, uint8_t dtype) {
// Returns a float value from an arbitrary data type
// The value in question is supposed to be located at the head of the pointer
if(dtype == NDARRAY_UINT8) {
return (mp_float_t)(*(uint8_t *)data);
} else if(dtype == NDARRAY_INT8) {
return (mp_float_t)(*(int8_t *)data);
} else if(dtype == NDARRAY_UINT16) {
return (mp_float_t)(*(uint16_t *)data);
} else if(dtype == NDARRAY_INT16) {
return (mp_float_t)(*(int16_t *)data);
} else {
return *((mp_float_t *)data);
}
}
#if NDARRAY_BINARY_USES_FUN_POINTER | ULAB_NUMPY_HAS_WHERE
uint8_t ndarray_upcast_dtype(uint8_t ldtype, uint8_t rdtype) {
// returns a single character that corresponds to the broadcasting rules
// - if one of the operarands is a float, the result is always 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
if(ldtype == rdtype) {
// if the two dtypes are equal, the result is also of that type
return ldtype;
} else if(((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_INT8)) ||
((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_UINT8)) ||
((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_INT16)) ||
((ldtype == NDARRAY_INT16) && (rdtype == NDARRAY_UINT8)) ||
((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_INT16)) ||
((ldtype == NDARRAY_INT16) && (rdtype == NDARRAY_INT8))) {
return NDARRAY_INT16;
} else if(((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_UINT16)) ||
((ldtype == NDARRAY_UINT16) && (rdtype == NDARRAY_UINT8)) ||
((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_UINT16)) ||
((ldtype == NDARRAY_UINT16) && (rdtype == NDARRAY_INT8))) {
return NDARRAY_UINT16;
}
return NDARRAY_FLOAT;
}
// The following five functions are the inverse of the ndarray_get_... functions,
// and write a floating point datum into a void pointer
void ndarray_set_float_uint8(void *data, mp_float_t datum) {
*((uint8_t *)data) = (uint8_t)datum;
}
void ndarray_set_float_int8(void *data, mp_float_t datum) {
*((int8_t *)data) = (int8_t)datum;
}
void ndarray_set_float_uint16(void *data, mp_float_t datum) {
*((uint16_t *)data) = (uint16_t)datum;
}
void ndarray_set_float_int16(void *data, mp_float_t datum) {
*((int16_t *)data) = (int16_t)datum;
}
void ndarray_set_float_float(void *data, mp_float_t datum) {
*((mp_float_t *)data) = datum;
}
// returns a single function pointer, depending on the dtype
void *ndarray_set_float_function(uint8_t dtype) {
if(dtype == NDARRAY_UINT8) {
return ndarray_set_float_uint8;
} else if(dtype == NDARRAY_INT8) {
return ndarray_set_float_int8;
} else if(dtype == NDARRAY_UINT16) {
return ndarray_set_float_uint16;
} else if(dtype == NDARRAY_INT16) {
return ndarray_set_float_int16;
} else {
return ndarray_set_float_float;
}
}
#endif /* NDARRAY_BINARY_USES_FUN_POINTER */
int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) {
int8_t ax = mp_obj_get_int(axis);
if(ax < 0) ax += ndim;
if((ax < 0) || (ax > ndim - 1)) {
mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds"));
}
return ax;
}
shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) {
// TODO: replace numerical_reduce_axes with this function, wherever applicable
// This function should be used, whenever a tensor is contracted;
// The shape and strides at `axis` are moved to the zeroth position,
// everything else is aligned to the right
if(!mp_obj_is_int(axis) & (axis != mp_const_none)) {
mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer"));
}
shape_strides _shape_strides;
_shape_strides.increment = 0;
// this is the contracted dimension (won't be overwritten for axis == None)
_shape_strides.ndim = 0;
if(axis == mp_const_none) {
_shape_strides.shape = ndarray->shape;
_shape_strides.strides = ndarray->strides;
return _shape_strides;
}
size_t *shape = m_new(size_t, ULAB_MAX_DIMS + 1);
_shape_strides.shape = shape;
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS + 1);
_shape_strides.strides = strides;
memcpy(_shape_strides.shape, ndarray->shape, sizeof(size_t) * ULAB_MAX_DIMS);
memcpy(_shape_strides.strides, ndarray->strides, sizeof(int32_t) * ULAB_MAX_DIMS);
_shape_strides.axis = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten)
if(axis != mp_const_none) { // i.e., axis is an integer
int8_t ax = tools_get_axis(axis, ndarray->ndim);
_shape_strides.axis = ULAB_MAX_DIMS - ndarray->ndim + ax;
_shape_strides.ndim = ndarray->ndim - 1;
}
// move the value stored at index to the leftmost position, and align everything else to the right
_shape_strides.shape[0] = ndarray->shape[_shape_strides.axis];
_shape_strides.strides[0] = ndarray->strides[_shape_strides.axis];
for(uint8_t i = 0; i < _shape_strides.axis; i++) {
// entries to the right of index must be shifted by one position to the left
_shape_strides.shape[i + 1] = ndarray->shape[i];
_shape_strides.strides[i + 1] = ndarray->strides[i];
}
if(_shape_strides.ndim != 0) {
_shape_strides.increment = 1;
}
if(_shape_strides.ndim == 0) {
_shape_strides.ndim = 1;
_shape_strides.shape[ULAB_MAX_DIMS - 1] = 1;
_shape_strides.strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
}
return _shape_strides;
}
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t *ndarray, ndarray_obj_t *results, mp_obj_t keepdims, shape_strides _shape_strides) {
// restores the contracted dimension, if keepdims is True
if((ndarray->ndim == 1) && (keepdims != mp_const_true)) {
// since the original array has already been contracted and
// we don't want to keep the dimensions here, we have to return a scalar
return mp_binary_get_val_array(results->dtype, results->array, 0);
}
if(keepdims == mp_const_true) {
results->ndim += 1;
for(int8_t i = 0; i < ULAB_MAX_DIMS; i++) {
results->shape[i] = ndarray->shape[i];
}
results->shape[_shape_strides.axis] = 1;
results->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
for(uint8_t i = ULAB_MAX_DIMS; i > 1; i--) {
results->strides[i - 2] = results->strides[i - 1] * results->shape[i - 1];
}
}
return MP_OBJ_FROM_PTR(results);
}
#if ULAB_MAX_DIMS > 1
ndarray_obj_t *tools_object_is_square(mp_obj_t obj) {
// Returns an ndarray, if the object is a square ndarray,
// raises the appropriate exception otherwise
if(!mp_obj_is_type(obj, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("size is defined for ndarrays only"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj);
if((ndarray->shape[ULAB_MAX_DIMS - 1] != ndarray->shape[ULAB_MAX_DIMS - 2]) || (ndarray->ndim != 2)) {
mp_raise_ValueError(MP_ERROR_TEXT("input must be square matrix"));
}
return ndarray;
}
#endif
uint8_t ulab_binary_get_size(uint8_t dtype) {
#if ULAB_SUPPORTS_COMPLEX
if(dtype == NDARRAY_COMPLEX) {
return 2 * (uint8_t)sizeof(mp_float_t);
}
#endif
return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL);
}
#if ULAB_SUPPORTS_COMPLEX
void ulab_rescale_float_strides(int32_t *strides) {
// re-scale the strides, so that we can work with floats, when iterating
uint8_t sz = sizeof(mp_float_t);
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
strides[i] /= sz;
}
}
#endif
bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) {
#if ULAB_SUPPORTS_COMPLEX
if(mp_obj_is_int(obj) || mp_obj_is_float(obj) || mp_obj_is_type(obj, &mp_type_complex)) {
return true;
} else {
return false;
}
#else
if(mp_obj_is_int(obj) || mp_obj_is_float(obj)) {
return true;
} else {
return false;
}
#endif
}
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t out, uint8_t dtype, uint8_t ndim, size_t *shape, bool dense_only) {
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out);
if(ndarray->dtype != dtype) {
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dtype"));
}
if(ndarray->ndim != ndim) {
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dimension"));
}
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
if(ndarray->shape[i] != shape[i]) {
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong shape"));
}
}
if(dense_only) {
if(!ndarray_is_dense(ndarray)) {
mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous"));
}
}
return ndarray;
}

51
code/ulab_tools.h Normal file
View file

@ -0,0 +1,51 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2022 Zoltán Vörös
*/
#ifndef _TOOLS_
#define _TOOLS_
#include "ndarray.h"
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
typedef struct _shape_strides_t {
uint8_t increment;
uint8_t axis;
uint8_t ndim;
size_t *shape;
int32_t *strides;
} shape_strides;
mp_float_t ndarray_get_float_uint8(void *);
mp_float_t ndarray_get_float_int8(void *);
mp_float_t ndarray_get_float_uint16(void *);
mp_float_t ndarray_get_float_int16(void *);
mp_float_t ndarray_get_float_float(void *);
void *ndarray_get_float_function(uint8_t );
uint8_t ndarray_upcast_dtype(uint8_t , uint8_t );
void *ndarray_set_float_function(uint8_t );
shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t );
int8_t tools_get_axis(mp_obj_t , uint8_t );
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t * , ndarray_obj_t * , mp_obj_t , shape_strides );
ndarray_obj_t *tools_object_is_square(mp_obj_t );
uint8_t ulab_binary_get_size(uint8_t );
#if ULAB_SUPPORTS_COMPLEX
void ulab_rescale_float_strides(int32_t *);
#endif
bool ulab_tools_mp_obj_is_scalar(mp_obj_t );
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t , uint8_t , uint8_t , size_t *, bool );
#endif

98
code/user/user.c Normal file
View file

@ -0,0 +1,98 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 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_HAS_USER_MODULE
//| """This module should hold arbitrary user-defined functions."""
//|
static mp_obj_t user_square(mp_obj_t arg) {
// the function takes a single dense ndarray, and calculates the
// element-wise square of its entries
// raise a TypeError exception, if the input is not an ndarray
if(!mp_obj_is_type(arg, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray"));
}
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(arg);
// make sure that the input is a dense array
if(!ndarray_is_dense(ndarray)) {
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
}
// if the input is a dense array, create `results` with the same number of
// dimensions, shape, and dtype
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype);
// since in a dense array the iteration over the elements is trivial, we
// can cast the data arrays ndarray->array and results->array to the actual type
if(ndarray->dtype == NDARRAY_UINT8) {
uint8_t *array = (uint8_t *)ndarray->array;
uint8_t *rarray = (uint8_t *)results->array;
for(size_t i=0; i < ndarray->len; i++, array++) {
*rarray++ = (*array) * (*array);
}
} else if(ndarray->dtype == NDARRAY_INT8) {
int8_t *array = (int8_t *)ndarray->array;
int8_t *rarray = (int8_t *)results->array;
for(size_t i=0; i < ndarray->len; i++, array++) {
*rarray++ = (*array) * (*array);
}
} else if(ndarray->dtype == NDARRAY_UINT16) {
uint16_t *array = (uint16_t *)ndarray->array;
uint16_t *rarray = (uint16_t *)results->array;
for(size_t i=0; i < ndarray->len; i++, array++) {
*rarray++ = (*array) * (*array);
}
} else if(ndarray->dtype == NDARRAY_INT16) {
int16_t *array = (int16_t *)ndarray->array;
int16_t *rarray = (int16_t *)results->array;
for(size_t i=0; i < ndarray->len; i++, array++) {
*rarray++ = (*array) * (*array);
}
} else { // if we end up here, the dtype is NDARRAY_FLOAT
mp_float_t *array = (mp_float_t *)ndarray->array;
mp_float_t *rarray = (mp_float_t *)results->array;
for(size_t i=0; i < ndarray->len; i++, array++) {
*rarray++ = (*array) * (*array);
}
}
// at the end, return a micrppython object
return MP_OBJ_FROM_PTR(results);
}
MP_DEFINE_CONST_FUN_OBJ_1(user_square_obj, user_square);
static const mp_rom_map_elem_t ulab_user_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj },
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_user_globals, ulab_user_globals_table);
const mp_obj_module_t ulab_user_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module);
#endif
#endif

20
code/user/user.h Normal file
View file

@ -0,0 +1,20 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*/
#ifndef _USER_
#define _USER_
#include "../ulab.h"
#include "../ndarray.h"
extern const mp_obj_module_t ulab_user_module;
#endif

414
code/utils/utils.c Normal file
View file

@ -0,0 +1,414 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2024 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 "utils.h"
#include "../ulab_tools.h"
#include "../numpy/fft/fft_tools.h"
#if ULAB_HAS_UTILS_MODULE
enum UTILS_BUFFER_TYPE {
UTILS_INT16_BUFFER,
UTILS_UINT16_BUFFER,
UTILS_INT32_BUFFER,
UTILS_UINT32_BUFFER,
};
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t buffer_type) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } } ,
{ MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } },
{ MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } },
{ MP_QSTR_out, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_byteswap, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
ndarray_obj_t *ndarray = NULL;
if(args[3].u_obj != mp_const_none) {
ndarray = MP_OBJ_TO_PTR(args[3].u_obj);
if((ndarray->dtype != NDARRAY_FLOAT) || !ndarray_is_dense(ndarray)) {
mp_raise_TypeError(MP_ERROR_TEXT("out must be a float dense array"));
}
}
size_t offset = mp_obj_get_int(args[2].u_obj);
mp_buffer_info_t bufinfo;
if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) {
if(bufinfo.len < offset) {
mp_raise_ValueError(MP_ERROR_TEXT("offset is too large"));
}
uint8_t sz = sizeof(int16_t);
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
if((buffer_type == UTILS_INT32_BUFFER) || (buffer_type == UTILS_UINT32_BUFFER)) {
sz = sizeof(int32_t);
}
#endif
size_t len = (bufinfo.len - offset) / sz;
if((len * sz) != (bufinfo.len - offset)) {
mp_raise_ValueError(MP_ERROR_TEXT("buffer size must be a multiple of element size"));
}
if(mp_obj_get_int(args[1].u_obj) > 0) {
size_t count = mp_obj_get_int(args[1].u_obj);
if(len < count) {
mp_raise_ValueError(MP_ERROR_TEXT("buffer is smaller than requested size"));
} else {
len = count;
}
}
if(args[3].u_obj == mp_const_none) {
ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT);
} else {
if(ndarray->len < len) {
mp_raise_ValueError(MP_ERROR_TEXT("out array is too small"));
}
}
uint8_t *buffer = bufinfo.buf;
mp_float_t *array = (mp_float_t *)ndarray->array;
if(args[4].u_obj == mp_const_true) {
// swap the bytes before conversion
uint8_t *tmpbuff = m_new(uint8_t, sz);
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER
if((buffer_type == UTILS_INT16_BUFFER) || (buffer_type == UTILS_UINT16_BUFFER)) {
for(size_t i = 0; i < len; i++) {
tmpbuff += sz;
for(uint8_t j = 0; j < sz; j++) {
memcpy(--tmpbuff, buffer++, 1);
}
if(buffer_type == UTILS_INT16_BUFFER) {
*array++ = (mp_float_t)(*(int16_t *)tmpbuff);
} else {
*array++ = (mp_float_t)(*(uint16_t *)tmpbuff);
}
}
}
#endif
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
if((buffer_type == UTILS_INT32_BUFFER) || (buffer_type == UTILS_UINT32_BUFFER)) {
for(size_t i = 0; i < len; i++) {
tmpbuff += sz;
for(uint8_t j = 0; j < sz; j++) {
memcpy(--tmpbuff, buffer++, 1);
}
if(buffer_type == UTILS_INT32_BUFFER) {
*array++ = (mp_float_t)(*(int32_t *)tmpbuff);
} else {
*array++ = (mp_float_t)(*(uint32_t *)tmpbuff);
}
}
}
#endif
} else {
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER
if(buffer_type == UTILS_INT16_BUFFER) {
for(size_t i = 0; i < len; i++) {
*array++ = (mp_float_t)(*(int16_t *)buffer);
buffer += sz;
}
}
#endif
#if ULAB_UTILS_HAS_FROM_UINT16_BUFFER
if(buffer_type == UTILS_UINT16_BUFFER) {
for(size_t i = 0; i < len; i++) {
*array++ = (mp_float_t)(*(uint16_t *)buffer);
buffer += sz;
}
}
#endif
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER
if(buffer_type == UTILS_INT32_BUFFER) {
for(size_t i = 0; i < len; i++) {
*array++ = (mp_float_t)(*(int32_t *)buffer);
buffer += sz;
}
}
#endif
#if ULAB_UTILS_HAS_FROM_UINT32_BUFFER
if(buffer_type == UTILS_UINT32_BUFFER) {
for(size_t i = 0; i < len; i++) {
*array++ = (mp_float_t)(*(uint32_t *)buffer);
buffer += sz;
}
}
#endif
}
return MP_OBJ_FROM_PTR(ndarray);
}
return mp_const_none;
}
#ifdef ULAB_UTILS_HAS_FROM_INT16_BUFFER
static mp_obj_t utils_from_int16_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_INT16_BUFFER);
}
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_int16_buffer_obj, 1, utils_from_int16_buffer);
#endif
#ifdef ULAB_UTILS_HAS_FROM_UINT16_BUFFER
static mp_obj_t utils_from_uint16_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_UINT16_BUFFER);
}
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint16_buffer_obj, 1, utils_from_uint16_buffer);
#endif
#ifdef ULAB_UTILS_HAS_FROM_INT32_BUFFER
static mp_obj_t utils_from_int32_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_INT32_BUFFER);
}
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_int32_buffer_obj, 1, utils_from_int32_buffer);
#endif
#ifdef ULAB_UTILS_HAS_FROM_UINT32_BUFFER
static mp_obj_t utils_from_uint32_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_UINT32_BUFFER);
}
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_buffer);
#endif
#endif /* ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER */
#if ULAB_UTILS_HAS_SPECTROGRAM
//| import ulab.numpy
//|
//| def spectrogram(r: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
//| """
//| :param ulab.numpy.ndarray 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.welch`` https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html."""
//| ...
//|
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE }} ,
#if !ULAB_FFT_IS_NUMPY_COMPATIBLE
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
#endif
{ MP_QSTR_scratchpad, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_log, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
#if ULAB_MAX_DIMS > 1
if(in->ndim != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
}
#endif
size_t len = in->len;
// Check if input is of length of power of 2
if((len & (len-1)) != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
}
ndarray_obj_t *out = NULL;
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
mp_obj_t scratchpad_object = args[1].u_obj;
mp_obj_t out_object = args[2].u_obj;
mp_obj_t log_object = args[3].u_obj;
#else
mp_obj_t scratchpad_object = args[2].u_obj;
mp_obj_t out_object = args[3].u_obj;
mp_obj_t log_object = args[4].u_obj;
#endif
if(out_object != mp_const_none) {
if(!mp_obj_is_type(out_object, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("out must be an ndarray"));
}
out = MP_OBJ_TO_PTR(out_object);
if((out->dtype != NDARRAY_FLOAT) || (out->ndim != 1)){
mp_raise_TypeError(MP_ERROR_TEXT("out array must be a 1D array of float type"));
}
if(len != out->len) {
mp_raise_ValueError(MP_ERROR_TEXT("input and out arrays must have same length"));
}
} else {
out = ndarray_new_linear_array(len, NDARRAY_FLOAT);
}
ndarray_obj_t *scratchpad = NULL;
mp_float_t *tmp = NULL;
if(scratchpad_object != mp_const_none) {
if(!mp_obj_is_type(scratchpad_object, &ulab_ndarray_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("scratchpad must be an ndarray"));
}
scratchpad = MP_OBJ_TO_PTR(scratchpad_object);
if(!ndarray_is_dense(scratchpad) || (scratchpad->ndim != 1) || (scratchpad->dtype != NDARRAY_FLOAT)) {
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be a 1D dense float array"));
}
if(scratchpad->len != 2 * len) {
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be twice as long as input"));
}
tmp = (mp_float_t *)scratchpad->array;
} else {
tmp = m_new0(mp_float_t, 2 * len);
}
uint8_t *array = (uint8_t *)in->array;
#if ULAB_FFT_IS_NUMPY_COMPATIBLE & ULAB_SUPPORTS_COMPLEX
if(in->dtype == NDARRAY_COMPLEX) {
uint8_t sz = 2 * sizeof(mp_float_t);
for(size_t i = 0; i < len; i++) {
memcpy(tmp, array, sz);
tmp += 2;
array += in->strides[ULAB_MAX_DIMS - 1];
}
} else {
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
for(size_t i = 0; i < len; i++) {
*tmp++ = func(array); // real part
*tmp++ = 0; // imaginary part, clear
array += in->strides[ULAB_MAX_DIMS - 1];
}
}
tmp -= 2 * len;
fft_kernel(tmp, len, 1);
#else // we might have two real input vectors
ndarray_obj_t *in2 = NULL;
if(n_args == 2) {
if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
}
in2 = MP_OBJ_TO_PTR(args[1].u_obj);
#if ULAB_MAX_DIMS > 1
if(in2->ndim != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
}
#endif
if(len != in2->len) {
mp_raise_TypeError(MP_ERROR_TEXT("input arrays are not compatible"));
}
}
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
for(size_t i = 0; i < len; i++) {
*tmp++ = func(array); // real part; imageinary will be cleared later
array += in->strides[ULAB_MAX_DIMS - 1];
}
if(n_args == 2) {
mp_float_t (*func2)(void *) = ndarray_get_float_function(in2->dtype);
array = (uint8_t *)in2->array;
for(size_t i = 0; i < len; i++) {
*tmp++ = func2(array);
array += in2->strides[ULAB_MAX_DIMS - 1];
}
tmp -= len;
} else {
// if there is only one input argument, clear the imaginary part
memset(tmp, 0, len * sizeof(mp_float_t));
}
tmp -= len;
fft_kernel(tmp, tmp + len, len, 1);
#endif /* ULAB_FFT_IS_NUMPY_COMPATIBLE */
mp_float_t *spectrum = (mp_float_t *)out->array;
uint8_t spectrum_sz = out->strides[ULAB_MAX_DIMS - 1] / sizeof(mp_float_t);
for(size_t i = 0; i < len; i++) {
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
*spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + 1) * *(tmp + 1));
tmp += 2;
#else
*spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + len) * *(tmp + len));
tmp++;
#endif
if(log_object == mp_const_true) {
*spectrum = MICROPY_FLOAT_C_FUN(log)(*spectrum);
}
spectrum += spectrum_sz;
}
if(scratchpad_object == mp_const_none) {
tmp -= len;
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
tmp -= len;
#endif
m_del(mp_float_t, tmp, 2 * len);
}
return MP_OBJ_FROM_PTR(out);
}
MP_DEFINE_CONST_FUN_OBJ_KW(utils_spectrogram_obj, 1, utils_spectrogram);
#endif /* ULAB_UTILS_HAS_SPECTROGRAM */
static const mp_rom_map_elem_t ulab_utils_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utils) },
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER
{ MP_ROM_QSTR(MP_QSTR_from_int16_buffer), MP_ROM_PTR(&utils_from_int16_buffer_obj) },
#endif
#if ULAB_UTILS_HAS_FROM_UINT16_BUFFER
{ MP_ROM_QSTR(MP_QSTR_from_uint16_buffer), MP_ROM_PTR(&utils_from_uint16_buffer_obj) },
#endif
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER
{ MP_ROM_QSTR(MP_QSTR_from_int32_buffer), MP_ROM_PTR(&utils_from_int32_buffer_obj) },
#endif
#if ULAB_UTILS_HAS_FROM_UINT32_BUFFER
{ MP_ROM_QSTR(MP_QSTR_from_uint32_buffer), MP_ROM_PTR(&utils_from_uint32_buffer_obj) },
#endif
#if ULAB_UTILS_HAS_SPECTROGRAM
{ MP_ROM_QSTR(MP_QSTR_spectrogram), MP_ROM_PTR(&utils_spectrogram_obj) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_utils_globals, ulab_utils_globals_table);
const mp_obj_module_t ulab_utils_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals,
};
#if CIRCUITPY_ULAB
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module);
#endif
#endif /* ULAB_HAS_UTILS_MODULE */

19
code/utils/utils.h Normal file
View file

@ -0,0 +1,19 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Zoltán Vörös
*/
#ifndef _UTILS_
#define _UTILS_
#include "../ulab.h"
#include "../ndarray.h"
extern const mp_obj_module_t ulab_utils_module;
#endif

View file

@ -1,106 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 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
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_integer(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;
}
// _degrees won't compile for the unix port
/*
mp_float_t _degreesf(mp_float_t x) {
return(180*x/MP_PI);
}
MATH_FUN_1(degrees, _degrees);
// _radians won't compile for the unix port
mp_float_t _radiansf(mp_float_t x) {
return(MP_PI*x/180.0);
}
MATH_FUN_1(radians, _radians);
STATIC mp_float_t _fabsf(mp_float_t x) {
return fabsf(x);
}
MATH_FUN_1(fabs, _fabs);
*/
MATH_FUN_1(acos, acos);
MATH_FUN_1(acosh, acosh);
MATH_FUN_1(asin, asin);
MATH_FUN_1(asinh, asinh);
MATH_FUN_1(atan, atan);
MATH_FUN_1(atanh, atanh);
MATH_FUN_1(ceil, ceil);
MATH_FUN_1(cos, cos);
MATH_FUN_1(erf, erf);
MATH_FUN_1(erfc, erfc);
MATH_FUN_1(exp, exp);
MATH_FUN_1(expm1, expm1);
MATH_FUN_1(floor, floor);
MATH_FUN_1(gamma, tgamma);
MATH_FUN_1(lgamma, lgamma);
MATH_FUN_1(log, log);
MATH_FUN_1(log10, log10);
MATH_FUN_1(log2, log2);
MATH_FUN_1(sin, sin);
MATH_FUN_1(sinh, sinh);
MATH_FUN_1(sqrt, sqrt);
MATH_FUN_1(tan, tan);
MATH_FUN_1(tanh, tanh);

View file

@ -1,52 +0,0 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Zoltán Vörös
*/
#ifndef _VECTORISE_
#define _VECTORISE_
#include "ndarray.h"
mp_obj_t vectorise_acos(mp_obj_t );
mp_obj_t vectorise_acosh(mp_obj_t );
mp_obj_t vectorise_asin(mp_obj_t );
mp_obj_t vectorise_asinh(mp_obj_t );
mp_obj_t vectorise_atan(mp_obj_t );
mp_obj_t vectorise_atanh(mp_obj_t );
mp_obj_t vectorise_ceil(mp_obj_t );
mp_obj_t vectorise_cos(mp_obj_t );
mp_obj_t vectorise_erf(mp_obj_t );
mp_obj_t vectorise_erfc(mp_obj_t );
mp_obj_t vectorise_exp(mp_obj_t );
mp_obj_t vectorise_expm1(mp_obj_t );
mp_obj_t vectorise_floor(mp_obj_t );
mp_obj_t vectorise_gamma(mp_obj_t );
mp_obj_t vectorise_lgamma(mp_obj_t );
mp_obj_t vectorise_log(mp_obj_t );
mp_obj_t vectorise_log10(mp_obj_t );
mp_obj_t vectorise_log2(mp_obj_t );
mp_obj_t vectorise_sin(mp_obj_t );
mp_obj_t vectorise_sinh(mp_obj_t );
mp_obj_t vectorise_sqrt(mp_obj_t );
mp_obj_t vectorise_tan(mp_obj_t );
mp_obj_t vectorise_tanh(mp_obj_t );
#define ITERATE_VECTOR(type, souce, out) do {\
type *input = (type *)(source)->array->items;\
for(size_t i=0; i < (source)->array->len; i++) {\
(out)[i] = f(input[i]);\
}\
} while(0)
#define MATH_FUN_1(py_name, c_name) \
mp_obj_t vectorise_ ## py_name(mp_obj_t x_obj) { \
return vectorise_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
}
#endif

View file

@ -14,6 +14,10 @@ help:
.PHONY: help Makefile
clean:
rm -rf "$(BUILDDIR)"
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile

35
docs/manual/make.bat Normal file
View file

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View file

@ -10,19 +10,24 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
#import sphinx_rtd_theme
from sphinx.transforms import SphinxTransform
from docutils import nodes
from sphinx import addnodes
# -- Project information -----------------------------------------------------
project = 'micropython-ulab'
copyright = '2019, Zoltán Vörös'
project = 'The ulab book'
copyright = '2019-2025, Zoltán Vörös and contributors'
author = 'Zoltán Vörös'
# The full version, including alpha/beta/rc tags
release = '0.24'
release = '6.9.0'
# -- General configuration ---------------------------------------------------
@ -42,18 +47,46 @@ templates_path = ['_templates']
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
latex_maketitle = r'''
\begin{titlepage}
\begin{flushright}
\Huge\textbf{The $\mu$lab book}
\vskip 0.5em
\LARGE
\textbf{Release %s}
\vskip 5em
\huge\textbf{Zoltán Vörös}
\end{flushright}
\begin{flushright}
\LARGE
\vskip 2em
with contributions by
\vskip 2em
\textbf{Roberto Colistete Jr.}
\vskip 0.2em
\textbf{Jeff Epler}
\vskip 0.2em
\textbf{Taku Fukada}
\vskip 0.2em
\textbf{Diego Elio Pettenò}
\vskip 0.2em
\textbf{Scott Shawcroft}
\vskip 5em
\today
\end{flushright}
\end{titlepage}
'''%release
latex_elements = {
'maketitle': latex_maketitle
}
master_doc = 'index'
author=u'Zoltán Vörös'
@ -61,7 +94,19 @@ copyright=author
language='en'
latex_documents = [
(master_doc, 'ulab-manual.tex', 'Micropython ulab documentation',
(master_doc, 'the-ulab-book.tex', 'The $\mu$lab book',
'Zoltán Vörös', 'manual'),
]
# Read the docs theme
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd:
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.']
except ImportError:
html_theme = 'default'
html_theme_path = ['.']
else:
html_theme_path = ['.']

View file

@ -1,16 +1,36 @@
.. ulab-manual documentation master file, created by
sphinx-quickstart on Sat Oct 19 12:48:00 2019.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to micropython-ulab's documentation!
Welcome to the ulab book!
=======================================
.. toctree::
:maxdepth: 2
:caption: Contents:
:caption: Introduction
ulab
ulab-intro
.. toctree::
:maxdepth: 2
:caption: User's guide:
ulab-ndarray
numpy-functions
numpy-universal
numpy-fft
numpy-linalg
numpy-random
scipy-integrate
scipy-linalg
scipy-optimize
scipy-signal
scipy-special
ulab-utils
ulab-tricks
ulab-programming
Indices and tables
==================

View file

@ -0,0 +1,197 @@
numpy.fft
=========
Functions related to Fourier transforms can be called by prepending them
with ``numpy.fft.``. The module defines the following two functions:
1. `numpy.fft.fft <#fft>`__
2. `numpy.fft.ifft <#ifft>`__
``numpy``:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.fft.ifft.html
fft
---
Since ``ulab``\ s ``ndarray`` does not support complex numbers, the
invocation of the Fourier transform differs from that in ``numpy``. In
``numpy``, you can simply pass an array or iterable to the function, and
it will be treated as a complex array:
.. code::
# code to be run in CPython
fft.fft([1, 2, 3, 4, 1, 2, 3, 4])
.. parsed-literal::
array([20.+0.j, 0.+0.j, -4.+4.j, 0.+0.j, -4.+0.j, 0.+0.j, -4.-4.j,
0.+0.j])
**WARNING:** The array returned is also complex, i.e., the real and
imaginary components are cast together. In ``ulab``, the real and
imaginary parts are treated separately: you have to pass two
``ndarray``\ s to the function, although, the second argument is
optional, in which case the imaginary part is assumed to be zero.
**WARNING:** The function, as opposed to ``numpy``, returns a 2-tuple,
whose elements are two ``ndarray``\ s, holding the real and imaginary
parts of the transform separately.
.. code::
# code to be run in micropython
from ulab import numpy as np
x = np.linspace(0, 10, num=1024)
y = np.sin(x)
z = np.zeros(len(x))
a, b = np.fft.fft(x)
print('real part:\t', a)
print('\nimaginary part:\t', b)
c, d = np.fft.fft(x, z)
print('\nreal part:\t', c)
print('\nimaginary part:\t', d)
.. parsed-literal::
real part: array([5119.996, -5.004663, -5.004798, ..., -5.005482, -5.005643, -5.006577], dtype=float)
imaginary part: array([0.0, 1631.333, 815.659, ..., -543.764, -815.6588, -1631.333], dtype=float)
real part: array([5119.996, -5.004663, -5.004798, ..., -5.005482, -5.005643, -5.006577], dtype=float)
imaginary part: array([0.0, 1631.333, 815.659, ..., -543.764, -815.6588, -1631.333], dtype=float)
ulab with complex support
~~~~~~~~~~~~~~~~~~~~~~~~~
If the ``ULAB_SUPPORTS_COMPLEX``, and ``ULAB_FFT_IS_NUMPY_COMPATIBLE``
pre-processor constants are set to 1 in
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__
as
.. code:: c
// Adds support for complex ndarrays
#ifndef ULAB_SUPPORTS_COMPLEX
#define ULAB_SUPPORTS_COMPLEX (1)
#endif
.. code:: c
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
#endif
then the FFT routine will behave in a ``numpy``-compatible way: the
single input array can either be real, in which case the imaginary part
is assumed to be zero, or complex. The output is also complex.
While ``numpy``-compatibility might be a desired feature, it has one
side effect, namely, the FFT routine consumes approx. 50% more RAM. The
reason for this lies in the implementation details.
ifft
----
The above-mentioned rules apply to the inverse Fourier transform. The
inverse is also normalised by ``N``, the number of elements, as is
customary in ``numpy``. With the normalisation, we can ascertain that
the inverse of the transform is equal to the original array.
.. code::
# code to be run in micropython
from ulab import numpy as np
x = np.linspace(0, 10, num=1024)
y = np.sin(x)
a, b = np.fft.fft(y)
print('original vector:\t', y)
y, z = np.fft.ifft(a, b)
# the real part should be equal to y
print('\nreal part of inverse:\t', y)
# the imaginary part should be equal to zero
print('\nimaginary part of inverse:\t', z)
.. parsed-literal::
original vector: array([0.0, 0.009775016, 0.0195491, ..., -0.5275068, -0.5357859, -0.5440139], dtype=float)
real part of inverse: array([-2.980232e-08, 0.0097754, 0.0195494, ..., -0.5275064, -0.5357857, -0.5440133], dtype=float)
imaginary part of inverse: array([-2.980232e-08, -1.451171e-07, 3.693752e-08, ..., 6.44871e-08, 9.34986e-08, 2.18336e-07], dtype=float)
Note that unlike in ``numpy``, the length of the array on which the
Fourier transform is carried out must be a power of 2. If this is not
the case, the function raises a ``ValueError`` exception.
ulab with complex support
~~~~~~~~~~~~~~~~~~~~~~~~~
The ``fft.ifft`` function can also be made ``numpy``-compatible by
setting the ``ULAB_SUPPORTS_COMPLEX``, and
``ULAB_FFT_IS_NUMPY_COMPATIBLE`` pre-processor constants to 1.
Computation and storage costs
-----------------------------
RAM
~~~
The FFT routine of ``ulab`` calculates the transform in place. This
means that beyond reserving space for the two ``ndarray``\ s that will
be returned (the computation uses these two as intermediate storage
space), only a handful of temporary variables, all floats or 32-bit
integers, are required.
Speed of FFTs
~~~~~~~~~~~~~
A comment on the speed: a 1024-point transform implemented in python
would cost around 90 ms, and 13 ms in assembly, if the code runs on the
pyboard, v.1.1. You can gain a factor of four by moving to the D series
https://github.com/peterhinch/micropython-fourier/blob/master/README.md#8-performance.
.. code::
# code to be run in micropython
from ulab import numpy as np
x = np.linspace(0, 10, num=1024)
y = np.sin(x)
@timeit
def np_fft(y):
return np.fft.fft(y)
a, b = np_fft(y)
.. parsed-literal::
execution time: 1985 us
The C implementation runs in less than 2 ms on the pyboard (we have just
measured that), and has been reported to run in under 0.8 ms on the D
series board. That is an improvement of at least a factor of four.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,386 @@
numpy.linalg
============
Functions in the ``linalg`` module can be called by prepending them by
``numpy.linalg.``. The module defines the following seven functions:
1. `numpy.linalg.cholesky <#cholesky>`__
2. `numpy.linalg.det <#det>`__
3. `numpy.linalg.eig <#eig>`__
4. `numpy.linalg.inv <#inv>`__
5. `numpy.linalg.norm <#norm>`__
6. `numpy.linalg.qr <#qr>`__
cholesky
--------
``numpy``:
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.linalg.cholesky.html
The function of the Cholesky decomposition takes a positive definite,
symmetric square matrix as its single argument, and returns the *square
root matrix* in the lower triangular form. If the input argument does
not fulfill the positivity or symmetry condition, a ``ValueError`` is
raised.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([[25, 15, -5], [15, 18, 0], [-5, 0, 11]])
print('a: ', a)
print('\n' + '='*20 + '\nCholesky decomposition\n', np.linalg.cholesky(a))
.. parsed-literal::
a: array([[25.0, 15.0, -5.0],
[15.0, 18.0, 0.0],
[-5.0, 0.0, 11.0]], dtype=float)
====================
Cholesky decomposition
array([[5.0, 0.0, 0.0],
[3.0, 3.0, 0.0],
[-1.0, 1.0, 3.0]], dtype=float)
det
---
``numpy``:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.det.html
The ``det`` function takes a square matrix as its single argument, and
calculates the determinant. The calculation is based on successive
elimination of the matrix elements, and the return value is a float,
even if the input array was of integer type.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
print(np.linalg.det(a))
.. parsed-literal::
-2.0
Benchmark
~~~~~~~~~
Since the routine for calculating the determinant is pretty much the
same as for finding the `inverse of a matrix <#inv>`__, the execution
times are similar:
.. code::
# code to be run in micropython
from ulab import numpy as np
@timeit
def matrix_det(m):
return np.linalg.inv(m)
m = np.array([[1, 2, 3, 4, 5, 6, 7, 8], [0, 5, 6, 4, 5, 6, 4, 5],
[0, 0, 9, 7, 8, 9, 7, 8], [0, 0, 0, 10, 11, 12, 11, 12],
[0, 0, 0, 0, 4, 6, 7, 8], [0, 0, 0, 0, 0, 5, 6, 7],
[0, 0, 0, 0, 0, 0, 7, 6], [0, 0, 0, 0, 0, 0, 0, 2]])
matrix_det(m)
.. parsed-literal::
execution time: 294 us
eig
---
``numpy``:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.eig.html
The ``eig`` function calculates the eigenvalues and the eigenvectors of
a real, symmetric square matrix. If the matrix is not symmetric, a
``ValueError`` will be raised. The function takes a single argument, and
returns a tuple with the eigenvalues, and eigenvectors. With the help of
the eigenvectors, amongst other things, you can implement sophisticated
stabilisation routines for robots.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
x, y = np.linalg.eig(a)
print('eigenvectors of a:\n', y)
print('\neigenvalues of a:\n', x)
.. parsed-literal::
eigenvectors of a:
array([[0.8151560042509081, -0.4499411232970823, -0.1644660242574522, 0.3256141906686505],
[0.2211334179893007, 0.7846992598235538, 0.08372081379922657, 0.5730077734355189],
[-0.1340114162071679, -0.3100776411558949, 0.8742786816656, 0.3486109343758527],
[-0.5183258053659028, -0.292663481927148, -0.4489749870391468, 0.6664142156731531]], dtype=float)
eigenvalues of a:
array([-1.165288365404889, 0.8029365530314914, 5.585625756072663, 13.77672605630074], dtype=float)
The same matrix diagonalised with ``numpy`` yields:
.. code::
# code to be run in CPython
a = array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
x, y = eig(a)
print('eigenvectors of a:\n', y)
print('\neigenvalues of a:\n', x)
.. parsed-literal::
eigenvectors of a:
[[ 0.32561419 0.815156 0.44994112 -0.16446602]
[ 0.57300777 0.22113342 -0.78469926 0.08372081]
[ 0.34861093 -0.13401142 0.31007764 0.87427868]
[ 0.66641421 -0.51832581 0.29266348 -0.44897499]]
eigenvalues of a:
[13.77672606 -1.16528837 0.80293655 5.58562576]
When comparing results, we should keep two things in mind:
1. the eigenvalues and eigenvectors are not necessarily sorted in the
same way
2. an eigenvector can be multiplied by an arbitrary non-zero scalar, and
it is still an eigenvector with the same eigenvalue. This is why all
signs of the eigenvector belonging to 5.58, and 0.80 are flipped in
``ulab`` with respect to ``numpy``. This difference, however, is of
absolutely no consequence.
Computation expenses
~~~~~~~~~~~~~~~~~~~~
Since the function is based on `Givens
rotations <https://en.wikipedia.org/wiki/Givens_rotation>`__ and runs
till convergence is achieved, or till the maximum number of allowed
rotations is exhausted, there is no universal estimate for the time
required to find the eigenvalues. However, an order of magnitude can, at
least, be guessed based on the measurement below:
.. code::
# code to be run in micropython
from ulab import numpy as np
@timeit
def matrix_eig(a):
return np.linalg.eig(a)
a = np.array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
matrix_eig(a)
.. parsed-literal::
execution time: 111 us
inv
---
``numpy``:
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.linalg.inv.html
A square matrix, provided that it is not singular, can be inverted by
calling the ``inv`` function that takes a single argument. The inversion
is based on successive elimination of elements in the lower left
triangle, and raises a ``ValueError`` exception, if the matrix turns out
to be singular (i.e., one of the diagonal entries is zero).
.. code::
# code to be run in micropython
from ulab import numpy as np
m = np.array([[1, 2, 3, 4], [4, 5, 6, 4], [7, 8.6, 9, 4], [3, 4, 5, 6]])
print(np.linalg.inv(m))
.. parsed-literal::
array([[-2.166666666666667, 1.500000000000001, -0.8333333333333337, 1.0],
[1.666666666666667, -3.333333333333335, 1.666666666666668, -0.0],
[0.1666666666666666, 2.166666666666668, -0.8333333333333337, -1.0],
[-0.1666666666666667, -0.3333333333333333, 0.0, 0.5]], dtype=float64)
Computation expenses
~~~~~~~~~~~~~~~~~~~~
Note that the cost of inverting a matrix is approximately twice as many
floats (RAM), as the number of entries in the original matrix, and
approximately as many operations, as the number of entries. Here are a
couple of numbers:
.. code::
# code to be run in micropython
from ulab import numpy as np
@timeit
def invert_matrix(m):
return np.linalg.inv(m)
m = np.array([[1, 2,], [4, 5]])
print('2 by 2 matrix:')
invert_matrix(m)
m = np.array([[1, 2, 3, 4], [4, 5, 6, 4], [7, 8.6, 9, 4], [3, 4, 5, 6]])
print('\n4 by 4 matrix:')
invert_matrix(m)
m = np.array([[1, 2, 3, 4, 5, 6, 7, 8], [0, 5, 6, 4, 5, 6, 4, 5],
[0, 0, 9, 7, 8, 9, 7, 8], [0, 0, 0, 10, 11, 12, 11, 12],
[0, 0, 0, 0, 4, 6, 7, 8], [0, 0, 0, 0, 0, 5, 6, 7],
[0, 0, 0, 0, 0, 0, 7, 6], [0, 0, 0, 0, 0, 0, 0, 2]])
print('\n8 by 8 matrix:')
invert_matrix(m)
.. parsed-literal::
2 by 2 matrix:
execution time: 65 us
4 by 4 matrix:
execution time: 105 us
8 by 8 matrix:
execution time: 299 us
The above-mentioned scaling is not obeyed strictly. The reason for the
discrepancy is that the function call is still the same for all three
cases: the input must be inspected, the output array must be created,
and so on.
norm
----
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html
The function takes a vector or matrix without options, and returns its
2-norm, i.e., the square root of the sum of the square of the elements.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print('norm of a:', np.linalg.norm(a))
print('norm of b:', np.linalg.norm(b))
.. parsed-literal::
norm of a: 7.416198487095663
norm of b: 16.88194301613414
qr
--
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.linalg.qr.html
The function computes the QR decomposition of a matrix ``m`` of
dimensions ``(M, N)``, i.e., it returns two such matrices, ``q``\ , and
``r``, that ``m = qr``, where ``q`` is orthonormal, and ``r`` is upper
triangular. In addition to the input matrix, which is the first
positional argument, the function accepts the ``mode`` keyword argument
with a default value of ``reduced``. If ``mode`` is ``reduced``, ``q``,
and ``r`` are returned in the reduced representation. Otherwise, the
outputs will have dimensions ``(M, M)``, and ``(M, N)``, respectively.
.. code::
# code to be run in micropython
from ulab import numpy as np
A = np.arange(6).reshape((3, 2))
print('A: \n', A)
print('complete decomposition')
q, r = np.linalg.qr(A, mode='complete')
print('q: \n', q)
print()
print('r: \n', r)
print('\n\nreduced decomposition')
q, r = np.linalg.qr(A, mode='reduced')
print('q: \n', q)
print()
print('r: \n', r)
.. parsed-literal::
A:
array([[0, 1],
[2, 3],
[4, 5]], dtype=int16)
complete decomposition
q:
array([[0.0, -0.9128709291752768, 0.408248290463863],
[-0.447213595499958, -0.3651483716701107, -0.8164965809277261],
[-0.8944271909999159, 0.1825741858350553, 0.408248290463863]], dtype=float64)
r:
array([[-4.47213595499958, -5.813776741499454],
[0.0, -1.095445115010332],
[0.0, 0.0]], dtype=float64)
reduced decomposition
q:
array([[0.0, -0.9128709291752768],
[-0.447213595499958, -0.3651483716701107],
[-0.8944271909999159, 0.1825741858350553]], dtype=float64)
r:
array([[-4.47213595499958, -5.813776741499454],
[0.0, -1.095445115010332]], dtype=float64)

View file

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

View file

@ -0,0 +1,510 @@
Universal functions
===================
Standard mathematical functions can be calculated on any scalar,
scalar-valued iterable (ranges, lists, tuples containing numbers), and
on ``ndarray``\ s without having to change the call signature. In all
cases the functions return a new ``ndarray`` of typecode ``float``
(since these functions usually generate float values, anyway). The only
exceptions to this rule are the ``exp``, and ``sqrt`` functions, which,
if ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__,
can return complex arrays, depending on the argument. All functions
execute faster with ``ndarray`` arguments than with iterables, because
the values of the input vector can be extracted faster.
At present, the following functions are supported (starred functions can
operate on, or can return complex arrays):
``acos``, ``acosh``, ``arctan2``, ``around``, ``asin``, ``asinh``,
``atan``, ``arctan2``, ``atanh``, ``ceil``, ``cos``, ``degrees``,
``exp*``, ``expm1``, ``floor``, ``log``, ``log10``, ``log2``,
``radians``, ``sin``, ``sinc``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``.
These functions are applied element-wise to the arguments, thus, e.g.,
the exponential of a matrix cannot be calculated in this way, only the
exponential of the matrix entries.
In order to avoid repeated memory allocations, functions can take the
``out=None`` optional argument, which must be a floating point
``ndarray`` of the same size as the input ``array``. If these conditions
are not fulfilled, and exception will be raised. If ``out=None``, a new
array will be created upon each invocation of the function.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = range(9)
b = np.array(a)
# works with ranges, lists, tuples etc.
print('a:\t', a)
print('exp(a):\t', np.exp(a))
# with 1D arrays
print('\n=============\nb:\n', b)
print('exp(b):\n', np.exp(b))
# as well as with matrices
c = np.array(range(9)).reshape((3, 3))
print('\n=============\nc:\n', c)
print('exp(c):\n', np.exp(c))
# using the `out` argument
d = np.array(range(9)).reshape((3, 3))
print('\nd before invoking the function:\n', d)
np.exp(c, out=d)
print('\nd afteri nvoking the function:\n', d)
.. parsed-literal::
a: range(0, 9)
exp(a): array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767, 54.59815003314424, 148.4131591025766, 403.4287934927351, 1096.633158428459, 2980.957987041728], dtype=float64)
=============
b:
array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float64)
exp(b):
array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767, 54.59815003314424, 148.4131591025766, 403.4287934927351, 1096.633158428459, 2980.957987041728], dtype=float64)
=============
c:
array([[0.0, 1.0, 2.0],
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]], dtype=float64)
exp(c):
array([[1.0, 2.718281828459045, 7.38905609893065],
[20.08553692318767, 54.59815003314424, 148.4131591025766],
[403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)
d before invoking the function:
array([[0.0, 1.0, 2.0],
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]], dtype=float64)
d afteri nvoking the function:
array([[1.0, 2.718281828459045, 7.38905609893065],
[20.08553692318767, 54.59815003314424, 148.4131591025766],
[403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)
Computation expenses
--------------------
The overhead for calculating with micropython iterables is quite
significant: for the 1000 samples below, the difference is more than 800
microseconds, because internally the function has to create the
``ndarray`` for the output, has to fetch the iterables items of unknown
type, and then convert them to floats. All these steps are skipped for
``ndarray``\ s, because these pieces of information are already known.
Doing the same with ``list`` comprehension requires 30 times more time
than with the ``ndarray``, which would become even more, if we converted
the resulting list to an ``ndarray``.
.. code::
# code to be run in micropython
from ulab import numpy as np
import math
a = [0]*1000
b = np.array(a)
@timeit
def timed_vector(iterable):
return np.exp(iterable)
@timeit
def timed_list(iterable):
return [math.exp(i) for i in iterable]
print('iterating over ndarray in ulab')
timed_vector(b)
print('\niterating over list in ulab')
timed_vector(a)
print('\niterating over list in python')
timed_list(a)
.. parsed-literal::
iterating over ndarray in ulab
execution time: 441 us
iterating over list in ulab
execution time: 1266 us
iterating over list in python
execution time: 11379 us
arctan2
-------
``numpy``:
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.arctan2.html
The two-argument inverse tangent function is also part of the ``vector``
sub-module. The function implements broadcasting as discussed in the
section on ``ndarray``\ s. Scalars (``micropython`` integers or floats)
are also allowed.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([1, 2.2, 33.33, 444.444])
print('a:\n', a)
print('\narctan2(a, 1.0)\n', np.arctan2(a, 1.0))
print('\narctan2(1.0, a)\n', np.arctan2(1.0, a))
print('\narctan2(a, a): \n', np.arctan2(a, a))
.. parsed-literal::
a:
array([1.0, 2.2, 33.33, 444.444], dtype=float64)
arctan2(a, 1.0)
array([0.7853981633974483, 1.14416883366802, 1.5408023243361, 1.568546328341769], dtype=float64)
arctan2(1.0, a)
array([0.7853981633974483, 0.426627493126876, 0.02999400245879636, 0.002249998453127392], dtype=float64)
arctan2(a, a):
array([0.7853981633974483, 0.7853981633974483, 0.7853981633974483, 0.7853981633974483], dtype=float64)
around
------
``numpy``:
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.around.html
``numpy``\ s ``around`` function can also be found in the ``vector``
sub-module. The function implements the ``decimals`` keyword argument
with default value ``0``. The first argument must be an ``ndarray``. If
this is not the case, the function raises a ``TypeError`` exception.
Note that ``numpy`` accepts general iterables. The ``out`` keyword
argument known from ``numpy`` is not accepted. The function always
returns an ndarray of type ``mp_float_t``.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([1, 2.2, 33.33, 444.444])
print('a:\t\t', a)
print('\ndecimals = 0\t', np.around(a, decimals=0))
print('\ndecimals = 1\t', np.around(a, decimals=1))
print('\ndecimals = -1\t', np.around(a, decimals=-1))
.. parsed-literal::
a: array([1.0, 2.2, 33.33, 444.444], dtype=float64)
decimals = 0 array([1.0, 2.0, 33.0, 444.0], dtype=float64)
decimals = 1 array([1.0, 2.2, 33.3, 444.4], dtype=float64)
decimals = -1 array([0.0, 0.0, 30.0, 440.0], dtype=float64)
exp
---
If ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__,
the exponential function can also take complex arrays as its argument,
in which case the return value is also complex.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([1, 2, 3])
print('a:\t\t', a)
print('exp(a):\t\t', np.exp(a))
print()
b = np.array([1+1j, 2+2j, 3+3j], dtype=np.complex)
print('b:\t\t', b)
print('exp(b):\t\t', np.exp(b))
.. parsed-literal::
a: array([1.0, 2.0, 3.0], dtype=float64)
exp(a): array([2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64)
b: array([1.0+1.0j, 2.0+2.0j, 3.0+3.0j], dtype=complex)
exp(b): array([1.468693939915885+2.287355287178842j, -3.074932320639359+6.71884969742825j, -19.88453084414699+2.834471132487004j], dtype=complex)
sqrt
----
If ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__,
the exponential function can also take complex arrays as its argument,
in which case the return value is also complex. If the input is real,
but the results might be complex, the user is supposed to specify the
output ``dtype`` in the function call. Otherwise, the square roots of
negative numbers will result in ``NaN``.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([1, -1])
print('a:\t\t', a)
print('sqrt(a):\t\t', np.sqrt(a))
print('sqrt(a):\t\t', np.sqrt(a, dtype=np.complex))
.. parsed-literal::
a: array([1.0, -1.0], dtype=float64)
sqrt(a): array([1.0, nan], dtype=float64)
sqrt(a): array([1.0+0.0j, 0.0+1.0j], dtype=complex)
Vectorising generic python functions
------------------------------------
``numpy``:
https://numpy.org/doc/stable/reference/generated/numpy.vectorize.html
The examples above use factory functions. In fact, they are nothing but
the vectorised versions of the standard mathematical functions.
User-defined ``python`` functions can also be vectorised by help of
``vectorize``. This function takes a positional argument, namely, the
``python`` function that you want to vectorise, and a non-mandatory
keyword argument, ``otypes``, which determines the ``dtype`` of the
output array. The ``otypes`` must be ``None`` (default), or any of the
``dtypes`` defined in ``ulab``. With ``None``, the output is
automatically turned into a float array.
The return value of ``vectorize`` is a ``micropython`` object that can
be called as a standard function, but which now accepts either a scalar,
an ``ndarray``, or a generic ``micropython`` iterable as its sole
argument. Note that the function that is to be vectorised must have a
single argument.
.. code::
# code to be run in micropython
from ulab import numpy as np
def f(x):
return x*x
vf = np.vectorize(f)
# calling with a scalar
print('{:20}'.format('f on a scalar: '), vf(44.0))
# calling with an ndarray
a = np.array([1, 2, 3, 4])
print('{:20}'.format('f on an ndarray: '), vf(a))
# calling with a list
print('{:20}'.format('f on a list: '), vf([2, 3, 4]))
.. parsed-literal::
f on a scalar: array([1936.0], dtype=float64)
f on an ndarray: array([1.0, 4.0, 9.0, 16.0], dtype=float64)
f on a list: array([4.0, 9.0, 16.0], dtype=float64)
As mentioned, the ``dtype`` of the resulting ``ndarray`` can be
specified via the ``otypes`` keyword. The value is bound to the function
object that ``vectorize`` returns, therefore, if the same function is to
be vectorised with different output types, then for each type a new
function object must be created.
.. code::
# code to be run in micropython
from ulab import numpy as np
l = [1, 2, 3, 4]
def f(x):
return x*x
vf1 = np.vectorize(f, otypes=np.uint8)
vf2 = np.vectorize(f, otypes=np.float)
print('{:20}'.format('output is uint8: '), vf1(l))
print('{:20}'.format('output is float: '), vf2(l))
.. parsed-literal::
output is uint8: array([1, 4, 9, 16], dtype=uint8)
output is float: array([1.0, 4.0, 9.0, 16.0], dtype=float64)
The ``otypes`` keyword argument cannot be used for type coercion: if the
function evaluates to a float, but ``otypes`` would dictate an integer
type, an exception will be raised:
.. code::
# code to be run in micropython
from ulab import numpy as np
int_list = [1, 2, 3, 4]
float_list = [1.0, 2.0, 3.0, 4.0]
def f(x):
return x*x
vf = np.vectorize(f, otypes=np.uint8)
print('{:20}'.format('integer list: '), vf(int_list))
# this will raise a TypeError exception
print(vf(float_list))
.. parsed-literal::
integer list: array([1, 4, 9, 16], dtype=uint8)
Traceback (most recent call last):
File "/dev/shm/micropython.py", line 14, in <module>
TypeError: can't convert float to int
Benchmarks
~~~~~~~~~~
It should be pointed out that the ``vectorize`` function produces the
pseudo-vectorised version of the ``python`` function that is fed into
it, i.e., on the C level, the same ``python`` function is called, with
the all-encompassing ``mp_obj_t`` type arguments, and all that happens
is that the ``for`` loop in ``[f(i) for i in iterable]`` runs purely in
C. Since type checking and type conversion in ``f()`` is expensive, the
speed-up is not so spectacular as when iterating over an ``ndarray``
with a factory function: a gain of approximately 30% can be expected,
when a native ``python`` type (e.g., ``list``) is returned by the
function, and this becomes around 50% (a factor of 2), if conversion to
an ``ndarray`` is also counted.
The following code snippet calculates the square of a 1000 numbers with
the vectorised function (which returns an ``ndarray``), with ``list``
comprehension, and with ``list`` comprehension followed by conversion to
an ``ndarray``. For comparison, the execution time is measured also for
the case, when the square is calculated entirely in ``ulab``.
.. code::
# code to be run in micropython
from ulab import numpy as np
def f(x):
return x*x
vf = np.vectorize(f)
@timeit
def timed_vectorised_square(iterable):
return vf(iterable)
@timeit
def timed_python_square(iterable):
return [f(i) for i in iterable]
@timeit
def timed_ndarray_square(iterable):
return np.array([f(i) for i in iterable])
@timeit
def timed_ulab_square(ndarray):
return ndarray**2
print('vectorised function')
squares = timed_vectorised_square(range(1000))
print('\nlist comprehension')
squares = timed_python_square(range(1000))
print('\nlist comprehension + ndarray conversion')
squares = timed_ndarray_square(range(1000))
print('\nsquaring an ndarray entirely in ulab')
a = np.array(range(1000))
squares = timed_ulab_square(a)
.. parsed-literal::
vectorised function
execution time: 7237 us
list comprehension
execution time: 10248 us
list comprehension + ndarray conversion
execution time: 12562 us
squaring an ndarray entirely in ulab
execution time: 560 us
From the comparisons above, it is obvious that ``python`` functions
should only be vectorised, when the same effect cannot be gotten in
``ulab`` only. However, although the time savings are not significant,
there is still a good reason for caring about vectorised functions.
Namely, user-defined ``python`` functions become universal, i.e., they
can accept generic iterables as well as ``ndarray``\ s as their
arguments. A vectorised function is still a one-liner, resulting in
transparent and elegant code.
A final comment on this subject: the ``f(x)`` that we defined is a
*generic* ``python`` function. This means that it is not required that
it just crunches some numbers. It has to return a number object, but it
can still access the hardware in the meantime. So, e.g.,
.. code:: python
led = pyb.LED(2)
def f(x):
if x < 100:
led.toggle()
return x*x
is perfectly valid code.

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