6.17. Lösare och slumptal

När funktionen som studeras definieras av Python-kod snarare än en buffert med sampel är en annan familj av verktyg det rätta valet: var är funktionens rot, dess minimum, dess integral över ett givet intervall? Undermodulerna scipy.integrate och scipy.optimize täcker det arbetet. Varje algoritm anropar tillbaka in i den användarlevererade Python-funktionen, så kostnaden per iteration är högre än en buffertreduktion; bekvämligheten ligger i att slippa skriva lösaren själv.

Undermodulen scipy.special täcker de statistiska specialfunktionerna (felfunktionen, gamma) som dyker upp vid beräkning av en sannolikhetsfördelnings kumulativa fördelningsfunktion (CDF, sannolikheten att ett sampel är högst ett givet värde) eller sannolikhetstäthetsfunktion (PDF, den relativa sannolikheten vid ett givet värde). numpy.random täcker den pseudoslumpmässiga generatorn för dithering, simulering och syntetisk testdata.

6.17.1. Numerisk integration av en anropbar funktion

När integranden är en Python-funktion snarare än en buffert med sampel exponerar scipy.integrate fyra kvadraturalgoritmer:

  • quad() – adaptiv Gauss-Kronrod. Det rätta standardvalet för släta integrander. Returnerar (value, error).

  • romberg() – klassisk Romberg / Newton-Cotes. Returnerar ett enda flyttal. Föråldrad uppströms; inkluderad för kompatibilitet.

  • simpson() – adaptiv Simpsons regel. Returnerar ett enda flyttal.

  • tanhsinh() – dubbelexponentiell kvadratur. Använd när integranden har singulariteter vid ändpunkterna eller en oändlig gräns. Returnerar (value, error).

Den gaussiska integralen utvärderad med den dubbelexponentiella regeln (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))

Utdata:

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

6.17.2. Rotsökning och minimering

scipy.optimize täcker tre klassiska envariabellösare. Varje iteration anropar tillbaka in i den användarlevererade Python-funktionen, så uppsnabbningen jämfört med en ren Python-lösare är blygsam (ungefär 2x); bekvämligheten ligger i att slippa skriva lösaren själv.

  • bisect() – hitta en rot till f[a, b] genom att halvera intervallet. f(a) och f(b) måste ha motsatta tecken:

    def f(x):
        return x * x - 1
    
    sp.optimize.bisect(f, 0, 4)        # ~1.0
    
  • newton() – hitta en rot med hjälp av sekant- / Newton-Raphson-iteration:

    def f(x):
        return x * x * x - 2.0
    
    sp.optimize.newton(f, 3., tol=0.001, rtol=0.01)
    # ~1.260
    
  • fmin() – hitta ett lokalt minimum med hjälp av nedåtgående-simplex-metoden (Nelder-Mead):

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

Envariabelomfånget räcker för de flesta optimeringar på kamerasidan – en sensors kalibreringskonstant, den förstärkning som maximerar en kontrastmätning, det tröskelvärde där ett histograms bimodalitet är skarpast. För flervariabelproblem är det rätta svaret oftast att omformulera problemet som en liten linjäralgebralösning snarare än att ta till en generell ickelinjär optimerare.

6.17.3. Specialfunktioner

scipy.special exponerar en handfull statistik- och sannolikhetsfunktioner som beter sig som universella funktioner – de accepterar en skalär, en iterabel eller en ndarray och returnerar en flyttals-ndarray

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

Felfunktionen och dess komplement förekommer i CDF:n för en gaussian – den självklara tillämpningen för att konvertera mellan ett uppmätt z-värde och en sannolikhet eller för att beräkna svansintegralen av en normalfördelning. Gamma- och log-gamma-funktionerna dyker upp i beta- / chi-tvåa- / student-t-beräkningar; gammaln är den numeriskt stabila formen för stora argument där gamma självt skulle överflöda.

6.17.4. Slumptal

numpy.random tillhandahåller en Generator-klass som drar sampel från vanliga fördelningar. Generatorn är tillståndsbärande: varje anrop avancerar dess interna tillstånd, så på varandra följande anrop returnerar oberoende sampel:

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

Utdatans dtype är alltid float. size= accepterar ett heltal (1-dimensionell utdata) eller en tupel (n-dimensionell utdata); när det utelämnas returneras ett enstaka Python-flyttal.

Generatorn är lämplig för simulering, dithering, syntetisk testdata och varje annan tillämpning där kryptografisk styrka inte krävs. Den är inte lämplig för nycklar eller token; använd systemets slumpkälla via os för sådant.

6.17.5. Tillgänglighet vid bygge

Huruvida varje undermodul faktiskt finns beror på hur kameran byggdes. scipy.optimize och scipy.special är inte aktiverade på varje kamera; att anropa en funktion som kameran inte inkluderar utlöser AttributeError. dir(sp), dir(sp.optimize), dir(np.random) med flera rapporterar vad som är tillgängligt på den kamera som åsyftas.