6.17. Solucionadores e números aleatórios¶
Quando a função em estudo é definida por código Python em vez de um buffer de amostras, uma família diferente de ferramentas é a escolha certa: onde está a raiz da função, seu mínimo, sua integral em um dado intervalo? Os submódulos scipy.integrate e scipy.optimize cobrem esse trabalho. Cada algoritmo faz chamadas de volta para a função Python fornecida pelo usuário, então o custo por iteração é maior que o de uma redução de buffer; a conveniência está em não precisar escrever o solucionador.
O submódulo scipy.special cobre as funções especiais estatísticas (função de erro, gama) que surgem ao computar a função de distribuição cumulativa (CDF, a probabilidade de uma amostra ser no máximo um dado valor) ou a função densidade de probabilidade (PDF, a verossimilhança relativa em um dado valor) de uma distribuição de probabilidade. numpy.random cobre o gerador pseudoaleatório para dithering, simulação e dados de teste sintéticos.
6.17.1. Integração numérica de um callable¶
Quando o integrando é uma função Python em vez de um buffer de amostras, scipy.integrate expõe quatro algoritmos de quadratura:
quad()– Gauss-Kronrod adaptativo. O padrão certo para integrandos suaves. Retorna(value, error).romberg()– Romberg / Newton-Cotes clássico. Retorna um único float. Descontinuado no projeto de origem; incluído por compatibilidade.simpson()– regra de Simpson adaptativa. Retorna um único float.tanhsinh()– quadratura dupla-exponencial. Use quando o integrando tem singularidades nos extremos ou um limite infinito. Retorna(value, error).
A integral gaussiana avaliada com a regra dupla-exponencial (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))
Saída:
approx: 1.7724538... exact: 1.7724538...
6.17.2. Busca de raízes e minimização¶
scipy.optimize cobre três solucionadores clássicos de variável única. Cada iteração faz uma chamada de volta para a função Python fornecida pelo usuário, então o ganho de velocidade em relação a um solucionador em Python puro é modesto (cerca de 2x); a conveniência está em não precisar escrever o solucionador.
bisect()– encontra uma raiz defem[a, b]dividindo o intervalo pela metade.f(a)ef(b)devem ter sinais opostos:def f(x): return x * x - 1 sp.optimize.bisect(f, 0, 4) # ~1.0
newton()– encontra uma raiz usando iteração da secante / 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()– encontra um mínimo local usando o método do simplex descendente (Nelder-Mead):def f(x): return (x - 1) ** 2 - 1 sp.optimize.fmin(f, 3.0) # ~1.0
O escopo de variável única é suficiente para a maioria das otimizações do lado da câmera – a constante de calibração de um sensor, o ganho que maximiza uma medida de contraste, o limiar onde a bimodalidade de um histograma é mais acentuada. Para problemas multivariáveis, a resposta certa costuma ser reformular o problema como uma pequena resolução de álgebra linear em vez de recorrer a um otimizador não linear genérico.
6.17.3. Funções especiais¶
scipy.special expõe um punhado de funções estatísticas e de probabilidade que se comportam como funções universais – elas aceitam um escalar, um iterável ou um ndarray e retornam um ndarray de ponto flutuante:
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
A função de erro e seu complemento aparecem na CDF de uma gaussiana – a aplicação de escolha para converter entre um z-score medido e uma probabilidade, ou para computar a integral da cauda de uma distribuição normal. As funções gama e log-gama surgem em cálculos de beta / qui-quadrado / t de Student; gammaln é a forma numericamente estável para argumentos grandes em que gamma por si só estouraria.
6.17.4. Números aleatórios¶
numpy.random fornece uma classe Generator que sorteia amostras de distribuições comuns. O gerador é stateful: cada chamada avança seu estado interno, então chamadas consecutivas retornam amostras independentes:
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))
O dtype de saída é sempre float. size= aceita um inteiro (saída unidimensional) ou uma tupla (saída n-dimensional); quando omitido, um único float Python é retornado.
O gerador é adequado para simulação, dithering, dados de teste sintéticos e qualquer outra aplicação em que força criptográfica não seja necessária. Ele não é adequado para chaves ou tokens; use a fonte aleatória do sistema por meio de os para esses casos.
6.17.5. Disponibilidade em tempo de compilação¶
Se cada submódulo está de fato presente depende de como a câmera foi compilada. scipy.optimize e scipy.special não estão habilitados em toda câmera; chamar uma função que a câmera não inclui lança AttributeError. dir(sp), dir(sp.optimize), dir(np.random) e similares relatam o que está disponível na câmera alvo.