Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mp.asin is inaccurate for small complex inputs #787

Open
pearu opened this issue Apr 16, 2024 · 3 comments
Open

mp.asin is inaccurate for small complex inputs #787

pearu opened this issue Apr 16, 2024 · 3 comments
Labels
bug an unexpected problem or unintended behavior
Milestone

Comments

@pearu
Copy link
Contributor

pearu commented Apr 16, 2024

As in the title.

Reproducer:

>>> mpmath.mp.asin(mpmath.mpc(0, 1e-22))
mpc(real='0.0', imag='0.0')
>>> mpmath.fp.asin(mpmath.mpc(0, 1e-22))  # expected result
1e-22j

A workaround is to increase precision:

>>> mpmath.mp.prec=80
>>> mpmath.mp.asin(mpmath.mpc(0, 1e-22))
mpc(real='0.0', imag='9.9999996826552253889673883e-23')

Points z=x+yj in the following (approximate) region

|x| < |y| < 1e-19

of the complex plane suffer from the defect reported here.

@skirpichev skirpichev added the bug an unexpected problem or unintended behavior label Apr 17, 2024
@skirpichev
Copy link
Collaborator

Perhaps, we could just increase working precision:

diff --git a/mpmath/libmp/libmpc.py b/mpmath/libmp/libmpc.py
index 2e22b22..b0893d4 100644
--- a/mpmath/libmp/libmpc.py
+++ b/mpmath/libmp/libmpc.py
@@ -609,7 +609,7 @@ def acos_asin(z, prec, rnd, n):
     and by abs(a) <= 1, in order to improve the numerical accuracy.
     """
     a, b = z
-    wp = prec + 10
+    wp = prec + 25
     # infinite real component
     if a in (finf, fninf):
         if b in (finf, fninf):

@skirpichev skirpichev added this to the 1.4 milestone Apr 25, 2024
@pearu
Copy link
Contributor Author

pearu commented Apr 25, 2024

Yes, but notice that the increase by 25 is likely insufficient or it must depend on the input. For instance,

>>> mpmath.mp.prec=700
>>> mpmath.mp.asin(mpmath.mpc(0, 1e-220))
mpc(real='0.0', imag='0.0')
>>> mpmath.mp.prec=800
>>> mpmath.mp.asin(mpmath.mpc(0, 1e-220))
mpc(real='0.0', imag='9.9999999999999999239355998681967174227375122814278010784257107926662288959827321849441348805654645781222578141680149580443831699278821999049728767619913188664117273184328232545396962216463347269815350517239219609176864224305536231460588661161e-221')

So, I think the algorithm in mpmath itself should be fixed because the same Hull et al algorithm is used elsewhere with double FP without having the defect reported here.

@skirpichev skirpichev removed this from the 1.4 milestone Apr 25, 2024
@pearu
Copy link
Contributor Author

pearu commented Apr 25, 2024

Looking at the code, I see that mpmath implements the Hull et al algorithm partially on the so-called "safe" region (see p 307) but it does not handle the non-safe regions (see p 315). While some parts of the non-safe regions are ok to dismiss because of MP math, then handling the non-safe region 2 in the paper corresponds to resolving this issue. In short, asin(z) in region 2 should read:

# assume z.imag >= 0, z.real => 0
if z.imag < eps * abs(z.real - 1):
    if z.real < 1:
        abs_real = arcsin(z.real)
        abs_imag = z.imag / sqrt((1 - z.real) * (z.real + 1))
    else:
        abs_real = pi / 2
        abs_imag = log1p(z.real - 1 + sqrt((z.real - 1) * (z.real + 1)))

where eps = 2 ** (-prec).
HTH

@skirpichev skirpichev added this to the 1.4 milestone May 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

2 participants