In matlab there is a special function which is not available in any of the collections for the Python I know (numpy, scipy, mpmath, ...).
Probably there are other places where functions like this one may be found?
UPD For all who think that the question is trivial, please try to compute this function for argument ~30 first.
UPD2 Arbitrary precision is a nice workaround, but if possible I would prefer to avoid it. I need a "standard" machine precision (no more no less) and maximum speed possible.
UPD3 It turns out, mpmath
gives surprisingly inaccurate result. Even where standard python math
works, mpmath
results are worse. Which makes it absolutely worthless.
UPD4 The code to compare different ways to compute erfcx.
import numpy as np
def int_erfcx(x):
"Integral which gives erfcx"
from scipy import integrate
def f(xi):
return np.exp(-x*xi)*np.exp(-0.5*xi*xi)
return 0.79788456080286535595*integrate.quad(f,
0.0,min(2.0,50.0/(1.0+x))+100.0,limit=500)[0]
def my_erfcx(x):
"""M. M. Shepherd and J. G. Laframboise,
MATHEMATICS OF COMPUTATION 36, 249 (1981)
Note that it is reasonable to compute it in long double
(or whatever python has)
"""
ch_coef=[np.float128(0.1177578934567401754080e+01),
np.float128( -0.4590054580646477331e-02),
np.float128( -0.84249133366517915584e-01),
np.float128( 0.59209939998191890498e-01),
np.float128( -0.26658668435305752277e-01),
np.float128( 0.9074997670705265094e-02),
np.float128( -0.2413163540417608191e-02),
np.float128( 0.490775836525808632e-03),
np.float128( -0.69169733025012064e-04),
np.float128( 0.4139027986073010e-05),
np.float128( 0.774038306619849e-06),
np.float128( -0.218864010492344e-06),
np.float128( 0.10764999465671e-07),
np.float128( 0.4521959811218e-08),
np.float128( -0.775440020883e-09),
np.float128( -0.63180883409e-10),
np.float128( 0.28687950109e-10),
np.float128( 0.194558685e-12),
np.float128( -0.965469675e-12),
np.float128( 0.32525481e-13),
np.float128( 0.33478119e-13),
np.float128( -0.1864563e-14),
np.float128( -0.1250795e-14),
np.float128( 0.74182e-16),
np.float128( 0.50681e-16),
np.float128( -0.2237e-17),
np.float128( -0.2187e-17),
np.float128( 0.27e-19),
np.float128( 0.97e-19),
np.float128( 0.3e-20),
np.float128( -0.4e-20)]
K=np.float128(3.75)
y = (x-K) / (x+K)
y2 = np.float128(2.0)*y
(d, dd) = (ch_coef[-1], np.float128(0.0))
for cj in ch_coef[-2:0:-1]:
(d, dd) = (y2 * d - dd + cj, d)
d = y * d - dd + ch_coef[0]
return d/(np.float128(1)+np.float128(2)*x)
def math_erfcx(x):
import scipy.special as spec
return spec.erfc(x) * np.exp(x*x)
def mpmath_erfcx(x):
import mpmath
return mpmath.exp(x**2) * mpmath.erfc(x)
if __name__ == "__main__":
x=np.linspace(1.0,26.0,200)
X=np.linspace(1.0,100.0,200)
intY = np.array([int_erfcx(xx*np.sqrt(2)) for xx in X])
myY = np.array([my_erfcx(xx) for xx in X])
myy = np.array([my_erfcx(xx) for xx in x])
mathy = np.array([math_erfcx(xx) for xx in x])
mpmathy = np.array([mpmath_erfcx(xx) for xx in x])
mpmathY = np.array([mpmath_erfcx(xx) for xx in X])
print ("Integral vs exact: %g"%max(np.abs(intY-myY)/myY))
print ("math vs exact: %g"%max(np.abs(mathy-myy)/myy))
print ("mpmath vs math: %g"%max(np.abs(mpmathy-mathy)/mathy))
print ("mpmath vs integral:%g"%max(np.abs(mpmathY-intY)/intY))
exit()
For me, it gives
Integral vs exact: 6.81236e-16
math vs exact: 7.1137e-16
mpmath vs math: 4.90899e-14
mpmath vs integral:8.85422e-13
Obviously, math
gives best possible precision where it works while mpmath
gives error couple orders of magnitude larger where math
works and even more for larger arguments.
The gmpy2 library provides access to the MPFR multiple-precision library. For normal precision, it is almost 5x faster than mpmath.
Both libraries return the same result for 30.
Disclaimer: I maintain gmpy2. I'm actively working towards a new release but there shouldn't be any issues with the current release for this calculation.
Edit: I was curious about the overhead of making two functions calls instead of just one so I implemented a gmpy2.erfcx() entirely in C but still using MPFR to perform the calculations. The improvement was less than I expected. If you think erfcx() would be useful, I can add it to the next release.
A highly optimized C++ implementation of erfcx (for both real and complex arguments) was recently merged into SciPy and should be in SciPy version 0.12.
Here is a simple and fast implementation giving 12-13 digit accuracy globally:
I don't know that any of the standard sources include that function, but you can implement it in straightforward fashion, at least if you use mpmath and aren't too worried about performance:
produces
And it behaves as it should even when the math.* routines overflow. mpmath's interval support isn't quite up to the task (without some hackery I'm too lazy to do), but for this I'm pretty sure mpfs will suffice, as erfcx is simply the product of two things mpmath can compute nicely.
This function is now available in the newest Version of scipy, see http://docs.scipy.org/doc/scipy/reference/special.html.