Skip to content

Conversation

skirpichev
Copy link
Collaborator

This will leave handling of special numbers to fewer functions, e.g. to hypercomb().

Closes #520
Closes #522

This will leave handling of special numbers to fewer
functions, e.g. to hypercomb().

Closes mpmath#520
Closes mpmath#522
@skirpichev
Copy link
Collaborator Author

Not sure if this worth it, looks like a hack. And, clearly, it's a compatibility break.

On another hand this allows pass through to low-level functions (like hypercomb()) to handle special inputs, instead of blowing up on statements like ctx.prec+=ctx.mag(z).

Comment on lines 777 to 780
def _set_prec(ctx, n):
if not ctx.isfinite(n):
return
ctx._prec = ctx._prec_rounding[0] = max(1, int(n))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an odd fix: just ignore the precision if it is mpmath.inf?

Is it valid to set the ctx.prec = mpmath.inf?

If it is valid then surely it should be used for something rather than ignored. If it is not valid then surely it should raise an error rather than being ignored. Either way ignoring does not seem like the right thing to do.

I would have thought that the right way to fix this is here by checking if M is infinite before doing ctx.prec += M:

M = ctx.mag(z)
if M < 1:
# Represent as limit definition
def h(n):
r = (z/2)**2
T1 = [z, 2], [-n, n-1], [n], [], [], [1-n], r
T2 = [z, 2], [n, -n-1], [-n], [], [], [1+n], r
return T1, T2
# We could use the limit definition always, but it leads
# to very bad cancellation (of exponentially large terms)
# for large real z
# Instead represent in terms of 2F0
else:
ctx.prec += M

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an odd fix: just ignore the precision if it is mpmath.inf?

Yes, but see: #792 (comment)

Is it valid to set the ctx.prec = mpmath.inf?

Yes, that was legal before.

I would have thought that the right way to fix this is here by checking if M is infinite before doing ctx.prec += M:

That was my original fix. The problem is that this will be required for many functions, that rely on hypercomb(). While with this approach we just fall deeper and rely on this function to handle special values for both mp and fp context.

BTW, to workaround SymPy failure we could use ctx.isinf instead.

@oscarbenjamin
Copy link

Is it valid to set the ctx.prec = mpmath.inf?

Yes, that was legal before.

In mpmath 1.3.0 it gives an error:

>>> import mpmath
>>> mpmath.mp.prec = mpmath.inf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/enojb/work/dev/mpmath/mpmath/ctx_mp_python.py", line 613, in _set_prec
    ctx._prec = ctx._prec_rounding[0] = max(1, int(n))
                                               ^^^^^^
  File "/Users/enojb/work/dev/mpmath/mpmath/ctx_mp_python.py", line 143, in __int__
    def __int__(s): return int(to_int(s._mpf_))
                               ^^^^^^^^^^^^^^^
  File "/Users/enojb/work/dev/mpmath/mpmath/libmp/libmpf.py", line 351, in to_int
    raise ValueError("cannot convert inf or nan to int")
ValueError: cannot convert inf or nan to int

After the change here it silently does nothing:

>>> import mpmath
>>> mpmath.mp.prec = mpmath.inf
>>> mpmath.mp.prec
53

@skirpichev
Copy link
Collaborator Author

In mpmath 1.3.0 it gives an error:

Oops, indeed. That was the reason why infinities were handled incorrectly.

Either way ignoring does not seem like the right thing to do.

Probably you are right. But I dislike approach "lets add workarounds to every hypercomb-based function". Maybe there are other options?

What if we make this legal? The prec=0 is a special case for "exact" computations (without rounding). See the mp._parse_prec() helper, it supports also ctx.inf. But now this is available only for kwargs of some mp functions.

skirpichev added a commit to skirpichev/mpmath that referenced this pull request Apr 30, 2024
@oscarbenjamin
Copy link

Maybe there could be come sort of helper function for calling hypercomb with precision increased by mag(z):

def hypercomb_prec_magz(ctx, ..., z):
    magz = ctx.mag(z)
    if ctx.isfinite(magz):
        # for finite z we need magz extra precision in hypercomb
        with ctx.extraprec(magz):
            return ctx.hypercomb(..., z)
    else:
        # z is infinite so let hypercomb handle special values.
        # increased precision is not needed in this case.
        return ctx.hypercomb(..., z)

I don't know how many cases there are like that.

Alternatively:

@contextmanager
def extraprec_inf(ctx, extra):
    """Increase precision if extra is finite."""
    if ctx.isfinite(extra):
        with ctx.extraprec(extra):
             yield
    else:
        # This branch is for handling special values where something is infinite
        # No extra precision is needed for that case.
        yield

skirpichev added a commit that referenced this pull request Apr 30, 2024
Temporary fix for SymPy, regression from #792
@skirpichev
Copy link
Collaborator Author

I don't know how many cases there are like that.

$ git grep ctx.hypercomb mpmath/functions/|wc -l
57

But I think same story is valid for other functions (hyper/hyp2f1/etc).

What if we make this legal? The prec=0 is a special case for "exact" computations (without rounding). See the mp._parse_prec() helper, it supports also ctx.inf.

Tests pass with

diff --git a/mpmath/ctx_mp_python.py b/mpmath/ctx_mp_python.py
index 335cd58..9d4f120 100644
--- a/mpmath/ctx_mp_python.py
+++ b/mpmath/ctx_mp_python.py
@@ -774,8 +774,8 @@ def default(ctx):
         ctx.trap_complex = False
 
     def _set_prec(ctx, n):
-        if not ctx.isfinite(n):
-            return
+        if ctx.isinf(n):
+            n = 0
         ctx._prec = ctx._prec_rounding[0] = max(1, int(n))
         ctx._dps = prec_to_dps(n)
 

@skirpichev
Copy link
Collaborator Author

See #801

@skirpichev skirpichev added this to the 1.4 milestone May 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unexpected return from hyp1f1 when evaluating limit at negative infinity besselk function doesn't support limit approaching infinity
2 participants