6.17. Solvers en willekeurige getallen

Wanneer de te bestuderen functie wordt gedefinieerd door Python-code in plaats van door een buffer met samples, is een andere familie van hulpmiddelen de juiste keuze: waar ligt de wortel van de functie, het minimum, de integraal over een gegeven interval? De submodules scipy.integrate en scipy.optimize dekken dat werk. Elk algoritme roept terug in de door de gebruiker geleverde Python-functie, dus de kosten per iteratie zijn hoger dan bij een bufferreductie; het gemak zit in het niet zelf hoeven schrijven van de solver.

De submodule scipy.special dekt de statistische speciale functies (errorfunctie, gamma) die opduiken bij het berekenen van de cumulatieve verdelingsfunctie (CDF, de kans dat een sample hoogstens een bepaalde waarde heeft) of de kansdichtheidsfunctie (PDF, de relatieve waarschijnlijkheid bij een bepaalde waarde) van een kansverdeling. numpy.random dekt de pseudo-willekeurige generator voor dithering, simulatie en synthetische testdata.

6.17.1. Numerieke integratie van een callable

Wanneer de integrand een Python-functie is in plaats van een buffer met samples, biedt scipy.integrate vier kwadratuuralgoritmen:

  • quad() – adaptieve Gauss-Kronrod. De juiste standaard voor gladde integranden. Geeft (value, error) terug.

  • romberg() – klassieke Romberg / Newton-Cotes. Geeft één float terug. Upstream verouderd; opgenomen voor compatibiliteit.

  • simpson() – adaptieve regel van Simpson. Geeft één float terug.

  • tanhsinh() – dubbel-exponentiële kwadratuur. Gebruik dit wanneer de integrand eindpuntsingulariteiten of een oneindige limiet heeft. Geeft (value, error) terug.

De Gauss-integraal geëvalueerd met de dubbel-exponentiële regel (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))

Uitvoer:

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

6.17.2. Wortels vinden en minimaliseren

scipy.optimize dekt drie klassieke solvers voor één variabele. Elke iteratie roept terug in de door de gebruiker geleverde Python-functie, dus de versnelling ten opzichte van een pure-Python-solver is bescheiden (ongeveer 2x); het gemak zit in het niet zelf hoeven schrijven van de solver.

  • bisect() – vind een wortel van f op [a, b] door het interval te halveren. f(a) en f(b) moeten tegengestelde tekens hebben:

    def f(x):
        return x * x - 1
    
    sp.optimize.bisect(f, 0, 4)        # ~1.0
    
  • newton() – vind een wortel met behulp van secans- / Newton-Raphson-iteratie:

    def f(x):
        return x * x * x - 2.0
    
    sp.optimize.newton(f, 3., tol=0.001, rtol=0.01)
    # ~1.260
    
  • fmin() – vind een lokaal minimum met behulp van de downhill-simplex- (Nelder-Mead-)methode:

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

De reikwijdte voor één variabele is voldoende voor de meeste optimalisaties aan de camerakant – de kalibratieconstante van een sensor, de versterking die een contrastmeting maximaliseert, de drempelwaarde waar een histogram het scherpst bimodaal is. Voor problemen met meerdere variabelen is het juiste antwoord meestal om het probleem te herformuleren als een kleine lineaire-algebra-oplossing in plaats van naar een algemene niet-lineaire optimaliseerder te grijpen.

6.17.3. Speciale functies

scipy.special biedt een handvol statistische en kansfuncties die zich gedragen als universele functies – ze accepteren een scalair, een iterable of een ndarray en geven een float ndarray terug:

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

De errorfunctie en haar complement komen voor in de CDF van een Gaussverdeling – de toepassing bij uitstek voor het omzetten tussen een gemeten z-score en een kans of voor het berekenen van de staartintegraal van een normale verdeling. De gamma- en log-gammafuncties komen op bij beta- / chi-kwadraat- / student-t-berekeningen; gammaln is de numeriek stabiele vorm voor grote argumenten waarbij gamma zelf zou overlopen.

6.17.4. Willekeurige getallen

numpy.random biedt een Generator-klasse die samples trekt uit gangbare verdelingen. De generator is toestandsgebonden: elke aanroep verzet de interne toestand, zodat opeenvolgende aanroepen onafhankelijke samples teruggeven:

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

Het uitvoer-dtype is altijd float. size= accepteert een integer (eendimensionale uitvoer) of een tuple (n-dimensionale uitvoer); bij weglating wordt een enkele Python-float teruggegeven.

De generator is geschikt voor simulatie, dithering, synthetische testdata en elke andere toepassing waar cryptografische sterkte niet vereist is. Hij is niet geschikt voor sleutels of tokens; gebruik daarvoor de systeembron voor willekeur via os.

6.17.5. Beschikbaarheid bij het bouwen

Of elke submodule daadwerkelijk aanwezig is, hangt af van hoe de camera is gebouwd. scipy.optimize en scipy.special zijn niet op elke camera ingeschakeld; het aanroepen van een functie die de camera niet bevat geeft een AttributeError. dir(sp), dir(sp.optimize), dir(np.random) en consorten rapporteren wat beschikbaar is op de camera waarop wordt gericht.