ulab, or what you will - numpy on bare metal

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
OutoftheBOTS_
Posts: 678
Joined: Mon Nov 20, 2017 10:18 am

Re: ulab, or what you will - numpy on bare metal

Post by OutoftheBOTS_ » Wed Oct 30, 2019 8:38 pm

I see this ;libiary has featured on Hackster.io https://www.hackster.io/news/zoltan-vor ... 9oOo5q-u1I

v923z
Posts: 100
Joined: Mon Dec 28, 2015 6:19 pm

Re: ulab, or what you will - numpy on bare metal

Post by v923z » Wed Oct 30, 2019 8:54 pm

OutoftheBOTS_ wrote:
Wed Oct 30, 2019 8:38 pm
I see this ;libiary has featured on Hackster.io https://www.hackster.io/news/zoltan-vor ... 9oOo5q-u1I
It must have leaked from hackaday.com, or perhaps the blog of adafruit.com. Though, the reaction on hackaday was really bashing :( But the hackster.io article is nice. Thanks!

User avatar
jimmo
Posts: 883
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: ulab, or what you will - numpy on bare metal

Post by jimmo » Wed Oct 30, 2019 9:39 pm

v923z wrote:
Wed Oct 30, 2019 6:16 pm
Am I correct in saying that for the reversed case, you simply swap the operands, and call the same function again?
Yes, runtime.c will do this when none of the previous options have worked and it would otherwise raise TypeError. It changes the op from FOO to REVERSE_FOO and calls your binary op method with the args reversed.

This means for commutative operators, your binary op can treat FOO and REVERSE_FOO the same but obviously things like subtraction or division need to be handled differently.

User avatar
mathieu
Posts: 40
Joined: Fri Nov 10, 2017 9:57 pm

Re: ulab, or what you will - numpy on bare metal

Post by mathieu » Thu Oct 31, 2019 10:21 pm

I am trying to compile ulab for the PYBD_SF6 on macOS following instructions found at https://github.com/v923z/micropython-ul ... e/ulab.rst. When I run

Code: Select all

make BOARD=PYBD_SF6 CROSS_COMPILE=/usr/local/bin/arm-none-eabi-
I get the following error:

Code: Select all

make: *** No rule to make target `lib/mbedtls/library/aes.c', needed by `build-PYBD_SF6/genhdr/qstr.i.last'.  Stop.
Am I missing something obvious? This is my first attempt at building my own firmware and I'm obviously over my head.

If I may suggest: I suspect many "regular" numpy users out there would find it very useful to have access to precompiled firmwares for all three pyboard-D models.

Many thanks to v923z for this impressive piece of work!

v923z
Posts: 100
Joined: Mon Dec 28, 2015 6:19 pm

Re: ulab, or what you will - numpy on bare metal

Post by v923z » Thu Oct 31, 2019 10:31 pm

Jim,
jimmo wrote:
Wed Oct 30, 2019 9:39 pm
v923z wrote:
Wed Oct 30, 2019 6:16 pm
Am I correct in saying that for the reversed case, you simply swap the operands, and call the same function again?

Yes, runtime.c will do this when none of the previous options have worked and it would otherwise raise TypeError. It changes the op from FOO to REVERSE_FOO and calls your binary op method with the args reversed.

This means for commutative operators, your binary op can treat FOO and REVERSE_FOO the same but obviously things like subtraction or division need to be handled differently.
Subtractions and divisions will make life a bit difficult, that is true, but I think they are not a serious predicament. What bugs me a bit more is this comment from runtime0.h

Code: Select all

    // MP_BINARY_OP_REVERSE_* must follow immediately after MP_BINARY_OP_*
The problem is that upcasting can be done on a case-by-case basis, i.e., you have to inspect all possible typecode combinations. This happens here: https://github.com/v923z/micropython-ul ... ray.c#L758. In order to simplify the procedure, I do this inspection once, and for 8 operators. Otherwise, the ugly decision tree would have to be repeated 8 times. Now, does the above-mentioned comment mean that the switch statement at https://github.com/v923z/micropython-ul ... ray.c#L719 has to be written as

Code: Select all

switch(op) {
            case MP_BINARY_OP_EQUAL:
            ...
                break;
                ...
           case MP_BINARY_OP_ADD:
           	ugly decision tree...
           ...
           	break;
           case MP_BINARY_OP_REVERSE_ADD:
           	ugly decision tree...
           ...
           	break;
           	...
(this would add a lot to the code) or can I expand as

Code: Select all

switch(op) {
            case MP_BINARY_OP_EQUAL:
            ...
                break;
                ...
           case MP_BINARY_OP_ADD:
           case MP_BINARY_OP_REVERSE_ADD:
           case MP_BINARY_OP_MULTIPLY:
           case MP_BINARY_OP_REVERSE_MULTIPLY:
           case MP_BINARY_OP_TRUE_DIVIDE:
           case MP_BINARY_OP_REVERSE_TRUE_DIVIDE:
           	ugly decision tree...
           ...
           	break;
I think, the simplest solution would be, if I could swap the two operands at the very beginning, like so

Code: Select all

mp_obj_t ndarray_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
	if(op == MP_BINARY_OP_REVERSE_ADD) {
		printf("reversed");
		return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs);
	}
	...
but that does not seem to work. Even if

Code: Select all

import ulab as np
a = np.array([1, 2, 3])
a + 5
returns [6, 7, 8],

Code: Select all

import ulab as np
a = np.array([1, 2, 3])
5+a
throws a TypeError exception, meaning that the type of 5 (or a) was not preserved. (I had the prinout, so I know that flow control entered the very first if statement.) Is there a way of overcoming this? What is the trick in your implementation?

And what about relational operators? I don't find reversion for those.

Code: Select all

import ulab as np
a = np.array([1, 2, 3])
a < 5
works, but

Code: Select all

import ulab as np
a = np.array([1, 2, 3])
5 > a
doesn't, and it seems, this can't even be made to work. Is that correct?

v923z
Posts: 100
Joined: Mon Dec 28, 2015 6:19 pm

Re: ulab, or what you will - numpy on bare metal

Post by v923z » Thu Oct 31, 2019 10:43 pm

mathieu wrote:
Thu Oct 31, 2019 10:21 pm
I am trying to compile ulab for the PYBD_SF6 on macOS following instructions found at https://github.com/v923z/micropython-ul ... e/ulab.rst. When I run

Code: Select all

make BOARD=PYBD_SF6 CROSS_COMPILE=/usr/local/bin/arm-none-eabi-
I get the following error:

Code: Select all

make: *** No rule to make target `lib/mbedtls/library/aes.c', needed by `build-PYBD_SF6/genhdr/qstr.i.last'.  Stop.
Am I missing something obvious? This is my first attempt at building my own firmware and I'm obviously over my head.
Oh, so you are trying to compile without ulab, i.e., the standard firmware, i.e., the error has nothing to do with the module. Is that right?
mathieu wrote:
Thu Oct 31, 2019 10:21 pm
If I may suggest: I suspect many "regular" numpy users out there would find it very useful to have access to precompiled firmwares for all three pyboard-D models.
I agree. If I had time, I would do that, though, there are at least 20 different boards supporting micropython now, and if you want to compile for all of them, then the process has to be automatised. By that I mean compilation, and uploading to storage. You have to persuade @Damien. But I am with you. He could automatically pull the ulab repository, whenever something changes there, like the webhook of readthedocs.

v923z
Posts: 100
Joined: Mon Dec 28, 2015 6:19 pm

Re: ulab, or what you will - numpy on bare metal

Post by v923z » Thu Oct 31, 2019 11:09 pm

mathieu wrote:
Thu Oct 31, 2019 10:21 pm

Code: Select all

make BOARD=PYBD_SF6 CROSS_COMPILE=/usr/local/bin/arm-none-eabi-
I get the following error:

Code: Select all

make: *** No rule to make target `lib/mbedtls/library/aes.c', needed by `build-PYBD_SF6/genhdr/qstr.i.last'.  Stop.
I have just tried to compile for the PYBD board (though, I have none), and while I get the standard firmware without issues, with ulab, I get missing references to sqrtf, and sinf in the link step.

Code: Select all

Including User C Module from ../../../ulab/code
LINK build-PYBD_SF6/firmware.elf
build-PYBD_SF6/code/linalg.o: In function `linalg_eig':
linalg.c:(.text.linalg_eig+0x100): undefined reference to `sqrtf'
linalg.c:(.text.linalg_eig+0x118): undefined reference to `sqrtf'
linalg.c:(.text.linalg_eig+0x124): undefined reference to `sqrtf'
linalg.c:(.text.linalg_eig+0x298): undefined reference to `sqrtf'
build-PYBD_SF6/code/fft.o: In function `fft_kernel':
fft.c:(.text.fft_kernel+0x9a): undefined reference to `sinf'
fft.c:(.text.fft_kernel+0xb2): undefined reference to `sinf'
build-PYBD_SF6/code/fft.o: In function `fft_fft_ifft_spectrum':
fft.c:(.text.fft_fft_ifft_spectrum+0x108): undefined reference to `sqrtf'
build-PYBD_SF6/code/numerical.o: In function `numerical_sum_mean_std_single_line':
numerical.c:(.text.numerical_sum_mean_std_single_line+0x70): undefined reference to `sqrtf'
build-PYBD_SF6/code/numerical.o: In function `numerical_sum_mean_std_array':
numerical.c:(.text.numerical_sum_mean_std_array+0x70): undefined reference to `sqrtf'
make: *** [Makefile:514: build-PYBD_SF6/firmware.elf] Error 1

If someone knows the remedy, please, let me know, so that I can update the manual.

v923z
Posts: 100
Joined: Mon Dec 28, 2015 6:19 pm

Re: ulab, or what you will - numpy on bare metal

Post by v923z » Thu Oct 31, 2019 11:29 pm

mathieu wrote:
Thu Oct 31, 2019 10:21 pm
If I may suggest: I suspect many "regular" numpy users out there would find it very useful to have access to precompiled firmwares for all three pyboard-D models.
By changing all occurrences of sinf, and sqrtf to sin, and sqrt, I managed to compile the firmware for PYBD:
https://github.com/v923z/micropython-ul ... s/tag/0.23. I can't test the results, so I can only hope that it works for you.

But then the compilation fails for the pyboard.v.1.1. There must be a better way...

Damien
Site Admin
Posts: 623
Joined: Mon Dec 09, 2013 5:02 pm

Re: ulab, or what you will - numpy on bare metal

Post by Damien » Fri Nov 01, 2019 10:51 am

The PYBD_SF6 uses double precision float, and only provides the double precision math functions (eg sin, cos, not sinf, cosf).

I see in your ulab code that you use "float" exclusively as the FP data type (at the C level). Is this a restriction or can it also work with double?

I would suggest changing all occurrences of "float" to "mp_float_t", that will allow it to compile for all targets with the FP precision supported/configured by the target.

v923z
Posts: 100
Joined: Mon Dec 28, 2015 6:19 pm

Re: ulab, or what you will - numpy on bare metal

Post by v923z » Fri Nov 01, 2019 11:48 am

Damien wrote:
Fri Nov 01, 2019 10:51 am
The PYBD_SF6 uses double precision float, and only provides the double precision math functions (eg sin, cos, not sinf, cosf).

I see in your ulab code that you use "float" exclusively as the FP data type (at the C level). Is this a restriction or can it also work with double?
I think one must consider various things: if you use doubles, you need twice as much RAM, and I don't know, whether that precision is really necessary in an embedded environment. But I am not dead against doubles.

On the other hand, if you have both double, and float, then upcasting becomes more involving, especially, that not all platforms support doubles. You would then need #ifdef compiler directives all over the code. Can we define a reasonable common denominator for all platforms? (u)int8_t, (u)int16_t, and mp_float_t, or does that already fail on some (ESP8266, e.g.)?
Damien wrote:
Fri Nov 01, 2019 10:51 am
I would suggest changing all occurrences of "float" to "mp_float_t", that will allow it to compile for all targets with the FP precision supported/configured by the target.
OK, thanks for the hint. But at the end of the day, one still has to cast to float, I believe: when you create the binary array, you have to set the typecode, or you would have different binary arrays for different platforms (see above). Is it generally true that, where doubles are implemented, you also have more RAM? I work mainly with the STM32F405, and that is why I can't really comment on this. But I absolutely see the problem, and I also know that a decision must be made at one point.

One more comment on this: if the float type is going to be different, depending on the platform, how should one create an array with a specific type?

Code: Select all

a = np.array([1, 2, 3], dtype=float)
will work on the pyboard, but not on the PYBD. Or should have float different meanings in the two environments?

Post Reply