6.17. Resolvedores 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, o seu mínimo, o seu integral num dado intervalo? Os submódulos scipy.integrate e scipy.optimize cobrem esse trabalho. Cada algoritmo chama de volta a função Python fornecida pelo utilizador, pelo que o custo por iteração é superior ao de uma redução de buffer; a conveniência está em não ter de escrever o resolvedor.
O submódulo scipy.special cobre as funções especiais estatísticas (função de erro, gama) que surgem ao calcular a função de distribuição cumulativa (CDF, a probabilidade de uma amostra ser no máximo um dado valor) ou a função de densidade de probabilidade (PDF, a probabilidade relativa num dado valor) de uma distribuição. numpy.random cobre o gerador pseudo-aleatório para dithering, simulação e dados de teste sintéticos.
6.17.1. Integração numérica de um chamável¶
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. A predefinição certa para integrandos suaves. Devolve(value, error).romberg()– Romberg / Newton-Cotes clássico. Devolve um único float. Descontinuado a montante; incluído por compatibilidade.simpson()– regra de Simpson adaptativa. Devolve um único float.tanhsinh()– quadratura dupla-exponencial. Use quando o integrando tem singularidades nos extremos ou um limite infinito. Devolve(value, error).
O integral gaussiano avaliado 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. Pesquisa de raízes e minimização¶
scipy.optimize cobre três resolvedores clássicos de uma variável. Cada iteração chama de volta a função Python fornecida pelo utilizador, pelo que a aceleração relativamente a um resolvedor puramente Python é modesta (cerca de 2x); a conveniência está em não ter de escrever o resolvedor.
bisect()– encontrar uma raiz defem[a, b]por bissecção do intervalo.f(a)ef(b)devem ter sinais opostos:def f(x): return x * x - 1 sp.optimize.bisect(f, 0, 4) # ~1.0
newton()– encontrar uma raiz usando iteração 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()– encontrar um mínimo local usando o método simplex descendente (Nelder-Mead):def f(x): return (x - 1) ** 2 - 1 sp.optimize.fmin(f, 3.0) # ~1.0
O âmbito de uma variável é suficiente para a maioria das otimizações do lado da câmara – a constante de calibração de um sensor, o ganho que maximiza uma medição de contraste, o limiar onde a bimodalidade de um histograma é mais nítida. Para problemas de múltiplas variáveis, a resposta certa é geralmente reformular o problema como uma resolução de álgebra linear de pequena dimensão, em vez de recorrer a um otimizador não linear genérico.
6.17.3. Funções especiais¶
scipy.special expõe um conjunto de funções estatísticas e de probabilidade que se comportam como funções universais – aceitam um escalar, um iterável ou um ndarray e devolvem um ndarray de floats:
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 o seu complemento aparecem na CDF de uma gaussiana – a aplicação de eleição para converter entre um z-score medido e uma probabilidade, ou para calcular o integral da cauda de uma distribuição normal. As funções gama e log-gama surgem em cálculos beta / qui-quadrado / student-t; gammaln é a forma numericamente estável para argumentos grandes onde o próprio gamma transbordaria.
6.17.4. Números aleatórios¶
numpy.random fornece uma classe Generator que extrai amostras de distribuições comuns. O gerador é estável: cada chamada avança o seu estado interno, pelo que chamadas consecutivas devolvem 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 a 1-D) ou um tuple (saída a n-D); quando omitido, é devolvido um único float Python.
O gerador é adequado para simulação, dithering, dados de teste sintéticos e qualquer outra aplicação onde a robustez criptográfica não é necessária. Não é adequado para chaves ou tokens; use a fonte aleatória do sistema através de os para esses efeitos.
6.17.5. Disponibilidade em tempo de compilação¶
Se cada submódulo está realmente presente depende de como a câmara foi compilada. scipy.optimize e scipy.special não estão ativados em todas as câmaras; chamar uma função que a câmara não inclui levanta AttributeError. dir(sp), dir(sp.optimize), dir(np.random) e similares reportam o que está disponível na câmara em uso.