2015-08-31 17 views
8

muszę obliczyć całkę następującej funkcji w zakresach, które zaczyna się już od -150:Oszukiwanie numpy/Python do reprezentującą bardzo dużych i bardzo małych ilościach

import numpy as np 
from scipy.special import ndtr 

def my_func(x): 
    return np.exp(x ** 2) * 2 * ndtr(x * np.sqrt(2)) 

Problemem jest to, że ta część funkcji

np.exp(x ** 2) 

dąży do nieskończoności - mam inf dla wartości x mniej niż około -26.

I ta część funkcji

2 * ndtr(x * np.sqrt(2)) 

co jest równoważne

from scipy.special import erf 

1 + erf(x) 

dąży do 0.

tak, to bardzo, bardzo dużych ilość razy bardzo, bardzo małe numer powinien podać mi odpowiednią liczbę - ale zamiast tego python daje mi nan.

Co mogę zrobić, aby obejść ten problem?

+0

Czy na pewno nie ma rozwiązań analitycznych dla swojej integralnej części? –

+0

@ReblochonMasque nie, nie jestem. czy wiesz, gdzie mogę znaleźć? na pewno nie mam kotów matematycznych, żeby to rozwiązać na własną rękę. – dbliss

+0

mathstackexchange maybe - or wolframalpha - or sympy –

Odpowiedz

4

Istnieje już taka funkcja: erfcx. Myślę, że erfcx(-x) powinien dać ci integrand, którego potrzebujesz (zauważ, że 1+erf(x)=erfc(-x)).

+0

'erf (x)' jest funkcją nieparzystą: 'erf (-x) = -erf (x)'. Tak więc 'erfc (-x) = 1 - erf (-x) = 1 + erf (x)' (pierwsza równość to definicja 'erfc', druga używa nieparzystej symetrii). –

+0

Zauważ, że odwracając znaki argumentów, możesz zastąpić na przykład 'quad (my_func, -14, -4)' z 'quad (erfcx, 4, 14)'. –

+0

@WarrenWeckesser thanks. jedno ostatnie pytanie: 'erfcx' jest zdefiniowane jako' exp (x ** 2) * erfc (x) '. jeśli robię 'erfcx (-x)', to nie daje mi 'exp (-x ** 2) * erfc (-x)', kiedy to, czego chcę, to 'exp (x ** 2) * erfc (-x) '? lub, czekaj, myślę, że daje mi 'exp ((- x) ** 2) * erf ((- x))', który * jest * to, co chcę. – dbliss

5

myślę kombinacja rozwiązania @ askewchan i scipy.special.log_ndtr rade:

from scipy.special import log_ndtr 

_log2 = np.log(2) 
_sqrt2 = np.sqrt(2) 

def my_func(x): 
    return np.exp(x ** 2) * 2 * ndtr(x * np.sqrt(2)) 

def my_func2(x): 
    return np.exp(x * x + _log2 + log_ndtr(x * _sqrt2)) 

print(my_func(-150)) 
# nan 

print(my_func2(-150) 
# 0.0037611803122451198 

Dla x <= -20, log_ndtr(x)uses a Taylor series expansion of the error function to iteratively compute the log CDF directly, która jest o wiele bardziej stabilne numerycznie niż po prostu biorąc log(ndtr(x)).


Aktualizacja

Jak wspomniano w uwag exp może również przepełnienia jeśli x jest wystarczająco duża. Chociaż można to obejść stosując mpmath.exp, prostsza i szybsza metoda jest rzucić się do np.longdouble które na moim komputerze, może reprezentować wartości do 1.189731495357231765e + 4932:

import mpmath 

def my_func3(x): 
    return mpmath.exp(x * x + _log2 + log_ndtr(x * _sqrt2)) 

def my_func4(x): 
    return np.exp(np.float128(x * x + _log2 + log_ndtr(x * _sqrt2))) 

print(my_func2(50)) 
# inf 

print(my_func3(50)) 
# mpf('1.0895188633566085e+1086') 

print(my_func4(50)) 
# 1.0895188633566084842e+1086 

%timeit my_func3(50) 
# The slowest run took 8.01 times longer than the fastest. This could mean that 
# an intermediate result is being cached 100000 loops, best of 3: 15.5 µs per 
# loop 

%timeit my_func4(50) 
# The slowest run took 11.11 times longer than the fastest. This could mean 
# that an intermediate result is being cached 100000 loops, best of 3: 2.9 µs 
# per loop 
+2

Prawdopodobnie mała uwaga dla tego przypadku użycia, ale dla skalarów, 'math.log' i' math.sqrt' są około dziesięć razy szybsze niż 'np.log' i' np.sqrt'. Lub jeszcze szybciej, 'log2 = math.log (2)' poza definicją funkcji. Dla mnie daje to około dwa razy przyspieszenie dla wywołania 'quad' – askewchan

+0

@askewchan dobry punkt - tak naprawdę nie myślałem o wydajności –

+0

to jest świetne. Wielkie dzięki. testuję to teraz. 'my_func2 (50)' podnosi 'RunTimeWarning':" przepełnienie napotkane w exp. " wydaje się, że 'np.exp' nie może obsłużyć danych wejściowych większych niż' 709'. (samo dla 'math.exp'.) jakikolwiek pomysł, jak mogę to obejść? – dbliss

2

Nie wiem, jak to będzie pomocne być, ale oto kilka myśli, które są zbyt długie na komentarz.

Musisz obliczyć całkę z 2 \cdot e^{x^2} \cdot f(\sqrt{2}x), która będzie correctly identified będzie e^{x^2}*(1 + erf(x)). Otwierając nawiasy, możesz zintegrować obie części sumy.

enter image description here

scipy ma ten imaginary error function implemented

Druga część jest trudniejsze:

enter image description here

To generalized hypergeometric function. Niestety wygląda to na scipy does not have an implementation of it, ale twierdzi, że tak jest.

Tutaj użyłem nieoznaczonych całek bez stałych, znając wartości, które są jasne, jak używać określonych.

+0

To, co wydaje mi się, że wkręca mnie w tę strategię, polega na tym, że 'scipy.special.erfi (50)' odnosi się do 'inf'. – dbliss

+0

derp. 'mpmath' ma swój własny' erfi' który działa. – dbliss

+0

Pytanie matematyczne dla ciebie: jak poradziłbym sobie z przypadkiem, w którym moja całka jest "w połowie określona" - tj. Mam górny limit, ale mój dolny limit to '-inf'. nie wiem, jak ocenić funkcje w pytaniu * w * '-inf'. – dbliss

Powiązane problemy