Numpy sous micropython !

La réflexion

Les tableaux numpy sont un "must" du langage Python pour manipuler des valeurs numériques. C'est de facto une sorte de "standard" en CPython. Pour dire les choses simplement, un tableau Numpy, çà reprend les concepts de l'objet list mais en l'étendant à des tableaux multidimensionnels et en permettant des opérations sur tous les éléments simultanément à l'aide d'opérations ou fonctions comme on le ferait sur des variables. Bref, c'est un "must".

Par contre, la librairie Numpy est beaucoup trop volumineuse pour être disponible en Micropython. On va explorer ici les possibilités en Micropython.

Pourquoi faire çà ?

Comme le dit la doc du numpy ulab (voir ci-après) https://micropython-ulab.readthedocs.io/en/latest/ulab-intro.html, "Of course, the first question that one has to answer is, why on Earth one would need a fast math library on a microcontroller. "... "Bien sûr, la première question à laquelle il faut répondre est de savoir pourquoi on a besoin d'une bibliothèque mathématique rapide sur un microcontrôleur."

Pourquoi on voudrait faire çà ? à priori, çà va "blinder" des ressources du microcontrôleur... à priori... Mais en fait, il y a plusieurs raisons potentiellement valables :

  • pour disposer d'un traitement rapide de données, avec des fonctions avancées. AInsi, le créateur de numpy-ulab avait besoin d'une transformée de Fourier (extraction des fréquences) sur un signal mesuré par une borche analogique de la carte utilisée (la pyboard en l'occurrence). Le tout de façon assez rapide, d'où la nécessité de compiler nativement la librairie...

  • parce que "numpy", c'est un peu un "Saint Graal" quand on code en Python : çà sert tout le temps... et pour pleins de domaines utilisant des tableaux de valeurs. Probablement pas indispensables voire même utile sur une carte à micro-contrôleur, mais çà permet d'apprendre, de se familiariser avec un tableau numpy... qui seront disponibles en CPython. Donc dans une approche didactique, c'est très intéressant.

  • parce que si on peut écrire des codes utilisant numpy sur microcontrôleur... et bien çà veut dire qu'on va pouvoir réutiliser des codes qui fonctionnent en CPython "as is" sur le microcontrôleur et inversement. Dans la série "je potentialise les codes que j'écris", ben c'est pas mal... !

  • parce que Micropython ne cesse pas de nous surprendre. A la base, c'est presque finalement une expérimentation : qu'est-ce que çà donnerait un Python sur microcontrôleur... Beaucoup n'aurait pas miser grand chose là-dessus au départ... mais quand on voit où on est actuellement, et bien on se dit qu'on peut bien continuer l'expérimentation... Et si on pouvait avoir numpy, çà donnerait quoi ? est-ce possible ? Juste pour voir...

  • et parce que les plateformes à microcontrôleur vont être de plus en plus puissante avec le temps. On a déjà des STM à 600Mhz... donc même si actuellement c'est peut-être encore un peu "limite"... les choses vont probablement s'améliorer. ...

  • just for fun ! Au moins pour voir ce qu'il est possible, qui à laisser de côté si on en n'a pas besoin, mais au moins voir ce que çà donne. Les "esprits pythoniques" comprendront...

  • et parce que si on a Numpy sur un micro-contrôleur... et bien çà va faire "lever les oreilles" de tous ceux qui codent déjà en Python... "Ah ouais, tu fais çà sur une carte à 4€... ah ouais, quand même... c'est dingue ce truc... !" suivi d'un innocent "Et c'est quoi la manip' pour tester ?... juste pour voir... "

un numpy sous micropython existe !

Le projet ulab propose une version de micropython intégrant un module numpy-like :

Cette solution nécessite un micropython compilé avec numpy. La doc est complète.

Clé en main

On trouvera des version de Micropython incluant numpy de ulab ici pour la plupart des plateformes (ESP 32, Pyboard, et même le Pi Pico ! ) :

A noter que le fichier binaire faire 800Ko, contre 500Ko environ pour Micropython seul.

Si on veut le faire soi-même

Si on veut le compiler soi-même, on le trouvera ici : https://github.com/v923z/micropython-ulab#compiling

Et aussi

Petit test

Simplement flasher sa carte Pi Pico avec la version pimoroni de micropython (la 1.15 à l'heure de mon test) :

  • carte débranchée, appui sur "BOOTSEL",
  • brancher la carte
  • copier le UF2 voulu

Une fois fait, ouvrir Thonny ou autre et dans l'interpréteur, on peut tester :

>>>from ulab import numpy as np
>>>np.ones(100)
>>>x=np.arange(100)
>>>print(x)
array([0, 1, 2, ..., 97, 98, 99], dtype=int16)
>>>y=np.sin(x)
>>>print(y)
array([0.0, 0.8414709, 0.9092974, ..., 0.3796077, -0.5733819, -0.9992067], dtype=float32)

Bah... bah... bah... ! C'est tout simplement énorme : çà fonctionne ! J'ai limpression d'être dans un notebook ou l'interpréteur Jupyter... mais non, je suis sur le microcontrôleur !

Là je suis sur le Pi Pico (à moins de 5€) et j'ai juste flashé avec la version pimoroni... Bref, simple !

Note

J'en reste là pour le petit test de débrouillage, mais clairement, la doc de la lib' est ENORME, quasiment un remake de celle de numpy en CPython (qui est une très très grosse doc). Mais le truc le plus incroyable, c'est qu'on dispose non seulement des tableaux Numpy... mais aussi de fonctions qu'on a dans Scipy, telle que la tranformée de Fourier, etc... Probablement, on a un nombre limité de fonctions Scipy, cette librairie étant énorme en CPython, mais quand même, on a quelques-unes qui peuvent s'avérer très intéressantes

La suite

La première chose que l'on a envie de faire, c'est de "timer" un peu les exécutions de fonctions, sur une taille de tableau "standard", disons 128 valeurs, ce qui est la largeur d'un petit écran OLED par exemple.

Avec TinyNUmpy

TinyNumpy est une version allégée de micropython, en pure Python d'où l'idée de tester çà.

Pour l'avoir fait :

  • pas mal de dépendances nécessaires
  • et au final, un gros problème avec les ctypes.... donc çà ne fonctionne pas pour moi.
  • et le projet date de 7 ou 8 ans, sans nouveau commit.... donc bof, bof...

Avec des list

On peut aussi tout simplement partir sur des list et se faire quelques fonctions qui donne un comportement "numpy-like"... mais çà ne vaut pas le numpy.