Skip to content

Correct evaluation for inverse trig/hyperbolic functions at infinity#1396

Closed
skirpichev wants to merge 5 commits intodiofant:masterfrom
skirpichev:fix-trig-eval-oo
Closed

Correct evaluation for inverse trig/hyperbolic functions at infinity#1396
skirpichev wants to merge 5 commits intodiofant:masterfrom
skirpichev:fix-trig-eval-oo

Conversation

@skirpichev
Copy link
Copy Markdown
Member

@skirpichev skirpichev commented Apr 26, 2024

Without this evalf() produces different results for evaluated and not evaluated expressions. Also, eval() results differ from numeric libraries:

    In [2]: asin(+oo, evaluate=False).evalf()
    Out[2]: 1.5707963267949 - inf⋅ⅈ

    In [3]: asin(+oo, evaluate=True).evalf()
    Out[3]: -inf⋅ⅈ

    In [4]: cmath.asin(complex('(inf-0j)'))
    Out[4]: (1.5707963267948966-infj)

Rewrite rules also break evaluation:

    In [5]: asin(oo, evaluate=False).rewrite(acos)
    Out[5]:
    π
    ─ - ∞⋅ⅈ
    2

Old evaluation rules are fine for Mathematica, which has either DirectedInfinity or ComplexInfinity:

    In[1]:= Infinity + I
    Out[1]= Infinity

But in the Diofant:

    In [6]: oo + I
    Out[6]: ∞ + ⅈ

@skirpichev skirpichev added wrong answer if mathematically wrong result was obtained functions labels Apr 26, 2024
@skirpichev skirpichev added this to the 0.15 milestone Apr 26, 2024
@skirpichev skirpichev marked this pull request as ready for review April 27, 2024 03:23
@skirpichev
Copy link
Copy Markdown
Member Author

skirpichev commented Apr 27, 2024

Now for inverse functions with infinite components:

$ python a.py 
Bad cases: 0
script a.py
from diofant import *

cases = [oo, -oo, I*oo, -I*oo, oo - I*oo, oo + I*oo,
         -oo - I*oo, -oo + I*oo]

bad = 0
for f in [asin, acos, asec, acsc, asinh, acosh, atan, acot, atanh, acoth]:
    for a in cases:
        if f(a, evaluate=False).evalf() != f(a, evaluate=True).evalf():
            bad += 1
            print(f, a)
print(f"Bad cases: {bad}")

@skirpichev skirpichev force-pushed the fix-trig-eval-oo branch 2 times, most recently from 9de3cfe to 7c97faf Compare April 29, 2024 05:41
Without this evalf() produces different results for evaluated and not
evaluated expressions.  Also, eval() results differ from numeric
libraries:

    In [2]: asin(+oo, evaluate=False).evalf()
    Out[2]: 1.5707963267949 - inf⋅ⅈ

    In [3]: asin(+oo, evaluate=True).evalf()
    Out[3]: -inf⋅ⅈ

    In [4]: cmath.asin(complex('(inf-0j)'))
    Out[4]: (1.5707963267948966-infj)

Rewrite rules also break evaluation:

    In [5]: asin(oo, evaluate=False).rewrite(acos)
    Out[5]:
    π
    ─ - ∞⋅ⅈ
    2

Old evaluation rules are fine for Mathematica, which has either
DirectedInfinity or ComplexInfinity:

    In[1]:= Infinity + I
    Out[1]= Infinity

But in the Diofant:

    In [6]: oo + I
    Out[6]: ∞ + ⅈ
@skirpichev skirpichev changed the title Correct evaluation rules for inverse trig/hyperbolic functions at ±oo Correct evaluation for inverse trig/hyperbolic functions at infinity May 10, 2024
@skirpichev
Copy link
Copy Markdown
Member Author

With #1404:

$ python a.py
0 bad rewrites out of 484
script a.py
from diofant import *

x = symbols('x')

funcs = [cos, sin, tan, cot, csc, sec, acos, asin, atan, acot, acsc, asec,
         cosh, sinh, tanh, coth, csch, sech, acosh, asinh, atanh, acoth]
#        acsch, asech]

points = [oo, -oo, I*oo, -I*oo]

bad = 0
total = 0
for f1 in funcs:
    for f2 in funcs:
        total += 1
        expr1 = f1(x)
        expr2 = expr1.rewrite(f2)
        fine = True
        for p in points:
            val1 = expr1.subs({x: p}).evalf()
            try:
                val2 = expr2.subs({x: p}).evalf()
                test = val1 != val2
            except:
                test = True
            if test:
                if expr1.limit(x, p).evalf() == expr2.limit(x, p).evalf():
                    continue
                print()
                pprint((f1, f2, p))
                pprint((expr1, ' --> ', expr2))
                fine = False
        if not fine:
            bad += 1

print(bad, 'bad rewrites out of', total)

@skirpichev
Copy link
Copy Markdown
Member Author

skirpichev commented May 10, 2024

Another series of bulk tests:

$ python c.py 
0 bad rewrites out of 484
script c.py
import numpy as np
from diofant import *

x = symbols('x')

funcs = [cos, sin, tan, cot , csc , sec , acos, asin, atan, acot , acsc , asec,
         cosh, sinh, tanh, coth, csch, sech, acosh, asinh, atanh, acoth]

numtests = 10
std = 10
normal = np.random.randn
points_real = std*normal(numtests)
points_complex = std*(normal(numtests) + normal(numtests)*1j)
points = ([Float(_, dps=60) for _ in points_real] +
          [Float(_.real, dps=60) + I*Float(_.imag, dps=60) for _ in points_complex])

def isclose(n1, n2):
    try:
        return bool(abs(n1 - n2)/(abs(n1) + abs(n2)) < 1e-5)
    except TypeError:
        return False

bad = 0
total = 0
for f1 in funcs:
    for f2 in funcs:
        total += 1
        expr1 = f1(x)
        expr2 = expr1.rewrite(f2)
        fine = True
        for p in points:
            val1 = expr1.subs({x:p}).evalf(strict=False)
            val2 = expr2.subs({x:p}).evalf(strict=False)
            if not isclose(val1, val2):
                print()
                print(f1, f2, p)
                print(expr1, ' --> ', expr2)
                print(val1, val2)
                fine = False
        if not fine:
            bad += 1

print(bad, 'bad rewrites out of', total)

@skirpichev skirpichev closed this Aug 5, 2024
@skirpichev skirpichev deleted the fix-trig-eval-oo branch August 5, 2024 02:04
@skirpichev skirpichev removed this from the 0.15 milestone Aug 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions wrong answer if mathematically wrong result was obtained

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant