6.17. Solveurs et nombres aléatoires#

Lorsque la fonction étudiée est définie par du code Python plutôt que par un tampon d’échantillons, une autre famille d’outils est le bon choix : où se trouve la racine de la fonction, son minimum, son intégrale sur un intervalle donné ? Les sous-modules scipy.integrate et scipy.optimize couvrent ce travail. Chaque algorithme rappelle la fonction Python fournie par l’utilisateur, de sorte que le coût par itération est plus élevé qu’une réduction sur tampon ; la commodité réside dans le fait de ne pas avoir à écrire le solveur.

Le sous-module scipy.special couvre les fonctions spéciales statistiques (fonction d’erreur, gamma) qui interviennent lors du calcul de la fonction de répartition (CDF, la probabilité qu’un échantillon soit au plus égal à une valeur donnée) ou de la fonction de densité de probabilité (PDF, la vraisemblance relative à une valeur donnée) d’une distribution de probabilité. numpy.random couvre le générateur pseudo-aléatoire pour le tramage, la simulation et les données de test synthétiques.

6.17.1. Intégration numérique d’une fonction appelable#

Lorsque l’intégrande est une fonction Python plutôt qu’un tampon d’échantillons, scipy.integrate expose quatre algorithmes de quadrature :

  • quad() – Gauss-Kronrod adaptatif. Le bon choix par défaut pour les intégrandes lisses. Renvoie (value, error).

  • romberg() – Romberg / Newton-Cotes classique. Renvoie un unique flottant. Déprécié en amont ; inclus pour compatibilité.

  • simpson() – règle de Simpson adaptative. Renvoie un unique flottant.

  • tanhsinh() – quadrature double-exponentielle. À utiliser lorsque l’intégrande présente des singularités aux bornes ou une limite infinie. Renvoie (value, error).

L’intégrale gaussienne évaluée avec la règle double-exponentielle (tanhsinh)

from math import exp
from math import pi
from math import sqrt
from ulab import numpy as np
from ulab import scipy as sp

f = lambda x: exp(-x * x)
value, err = sp.integrate.tanhsinh(f, -np.inf, np.inf)
print("approx:", value, "   exact:", sqrt(pi))

Sortie

approx: 1.7724538...   exact: 1.7724538...

6.17.2. Recherche de racine et minimisation#

scipy.optimize couvre trois solveurs classiques à une seule variable. Chaque itération rappelle la fonction Python fournie par l’utilisateur, de sorte que le gain de vitesse par rapport à un solveur en Python pur est modeste (environ 2x) ; la commodité réside dans le fait de ne pas avoir à écrire le solveur.

  • bisect() – trouve une racine de f sur [a, b] en divisant l’intervalle par deux. f(a) et f(b) doivent avoir des signes opposés

    def f(x):
        return x * x - 1
    
    sp.optimize.bisect(f, 0, 4)        # ~1.0
    
  • newton() – trouve une racine par itération de la sécante / Newton-Raphson

    def f(x):
        return x * x * x - 2.0
    
    sp.optimize.newton(f, 3., tol=0.001, rtol=0.01)
    # ~1.260
    
  • fmin() – trouve un minimum local par la méthode du simplexe descendant (Nelder-Mead)

    def f(x):
        return (x - 1) ** 2 - 1
    
    sp.optimize.fmin(f, 3.0)           # ~1.0
    

La portée à une seule variable suffit pour la plupart des optimisations côté caméra – une constante de calibration d’un capteur, le gain qui maximise une mesure de contraste, le seuil où la bimodalité d’un histogramme est la plus marquée. Pour les problèmes à plusieurs variables, la bonne réponse consiste généralement à reformuler le problème comme une petite résolution d’algèbre linéaire plutôt que de recourir à un optimiseur non linéaire général.

6.17.3. Fonctions spéciales#

scipy.special expose une poignée de fonctions statistiques et de probabilité qui se comportent comme des fonctions universelles – elles acceptent un scalaire, un itérable ou un ndarray et renvoient un ndarray de flottants

x = np.linspace(0, 4, num=8)

sp.special.erf(x)         # error function
sp.special.erfc(x)        # complementary error function
sp.special.gamma(x + 1)   # gamma function
sp.special.gammaln(x + 1) # log-gamma function

La fonction d’erreur et son complément apparaissent dans la CDF d’une gaussienne – l’application de prédilection pour convertir entre un score z mesuré et une probabilité, ou pour calculer l’intégrale de queue d’une distribution normale. Les fonctions gamma et log-gamma apparaissent dans les calculs bêta / khi-deux / student-t ; gammaln est la forme numériquement stable pour les grands arguments où gamma lui-même déborderait.

6.17.4. Nombres aléatoires#

numpy.random fournit une classe Generator qui tire des échantillons de distributions courantes. Le générateur est à état : chaque appel fait avancer son état interne, de sorte que des appels consécutifs renvoient des échantillons indépendants

from ulab import numpy as np

rng = np.random.Generator(seed=42)

rng.random(size=5)             # 5 uniform [0.0, 1.0) samples
rng.uniform(low=-1.0, high=1.0, size=10)
rng.normal(loc=0.0, scale=1.0, size=(2, 4))

Le dtype de sortie est toujours float. size= accepte un entier (sortie à une dimension) ou un uplet (sortie à n dimensions) ; lorsqu’il est omis, un unique flottant Python est renvoyé.

Le générateur convient à la simulation, au tramage, aux données de test synthétiques et à toute autre application où une robustesse cryptographique n’est pas requise. Il n’est pas adapté aux clés ou aux jetons ; utilisez pour cela la source aléatoire du système via os.

6.17.5. Disponibilité à la compilation#

La présence effective de chaque sous-module dépend de la façon dont la caméra a été compilée. scipy.optimize et scipy.special ne sont pas activés sur toutes les caméras ; appeler une fonction que la caméra n’inclut pas lève AttributeError. dir(sp), dir(sp.optimize), dir(np.random) et consorts indiquent ce qui est disponible sur la caméra ciblée.