upiterbarg / mpmath

Automatically exported from code.google.com/p/mpmath
Other
0 stars 0 forks source link

Should poles return inf? #108

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Should poles of functions more consistently return inf instead of raising
ValueError or ZeroDivisionError? I'm thinking particularly of csc(0),
cot(0), gamma(0), zeta(1).

On one hand, an exception sometimes allows one to detect errors earlier. On
the other hand, inf is correct in many situations: 1/f(x) correctly gives
zero (very useful for e.g. 1/gamma(x)), and 0*f(x) still gives nan rather
than some arbitrary wrong limit.

Original issue reported on code.google.com by fredrik....@gmail.com on 11 Dec 2008 at 4:16

GoogleCodeExporter commented 9 years ago
Let the user choose, like in Python's decimal module (if I recall it correctly)?

Original comment by Vinzent.Steinberg@gmail.com on 11 Dec 2008 at 4:49

GoogleCodeExporter commented 9 years ago
Yes, it can be handled via the same mechanism as propagation of complex results:

>>> log(-1)
mpc(real='0.0', imag='3.1415926535897931')
>>> mp.trap_complex = True
>>> log(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\source\mp\trunk\mpmath\functions.py", line 1161, in log
    return ln(x)
  File "c:\source\mp\trunk\mpmath\functions.py", line 339, in f
    return make_mpf(real_f(x._mpf_, prec, rounding))
  File "c:\source\mp\trunk\mpmath\libelefun.py", line 977, in mpf_log
    raise ComplexResult("logarithm of a negative number")
mpmath.libmpf.ComplexResult: logarithm of a negative number

So, is it worth adding a new exception and option for this?

Original comment by fredrik....@gmail.com on 11 Dec 2008 at 5:01

GoogleCodeExporter commented 9 years ago
Also see 
http://svn.python.org/view/python/trunk/Modules/mathmodule.c?view=markup

"These are the "spirit of 754" rules:

1. If the mathematical result is a real number, but of magnitude too
large to approximate by a machine float, overflow is signaled and the
result is an infinity (with the appropriate sign).

2. If the mathematical result is a real number, but of magnitude too
small to approximate by a machine float, underflow is signaled and the
result is a zero (with the appropriate sign).

3. At a singularity (a value x such that the limit of f(y) as y
approaches x exists and is an infinity), "divide by zero" is signaled
and the result is an infinity (with the appropriate sign).  This is
complicated a little by that the left-side and right-side limits may
not be the same; e.g., 1/x approaches +inf or -inf as x approaches 0
from the positive or negative directions.  In that specific case, the
sign of the zero determines the result of 1/0.

4. At a point where a function has no defined result in the extended
reals (i.e., the reals plus an infinity or two), invalid operation is
signaled and a NaN is returned.

And these are what Python has historically /tried/ to do (but not
always successfully, as platform libm behavior varies a lot):

For #1, raise OverflowError.

For #2, return a zero (with the appropriate sign if that happens by
accident ;-)).

For #3 and #4, raise ValueError.  It may have made sense to raise
Python's ZeroDivisionError in #3, but historically that's only been
raised for division by zero and mod by zero."

Original comment by Vinzent.Steinberg@gmail.com on 16 Jan 2009 at 8:19

GoogleCodeExporter commented 9 years ago
Well, hardware floats (and Decimals for that matter) have the benefit of being 
able
to return a value (nan) *and* signaling an error by setting a flag in a 
register. I
don't want to do this at the mpf_ level, because I prefer the interface to be 
purely
functional (stateless). It could be done at a higher level though.

For functions like mpf_div, one could perhaps implement an optional argument 
flag
that determines whether division by zero should raise an exception. 
Alternatively,
there could be two functions for division, where one raises and the other 
doesn't.

Further a possibility is to allow optionally passing a mutable dict as a keyword
argument to mpf_ functions. Then flags like d['divide by zero'] = True, 
d['inexact']
= True, ... could be set in-place. Just an idea; possibly needlessly complex 
without
much benefit.

Original comment by fredrik....@gmail.com on 3 Feb 2009 at 8:02

GoogleCodeExporter commented 9 years ago
Currently the interface is not stateless, see mp.dps for instance (or is this 
"high
level"?). Are you planning to change this? Why do you prefer a stateless 
interface?

The mutable dict could be **kwargs.

Original comment by Vinzent.Steinberg@gmail.com on 4 Feb 2009 at 2:07

GoogleCodeExporter commented 9 years ago
Functions like mpf_add are stateless. mp.dps is the high level interface, and 
it is
stateful because when doing manual calculations, it tends to be more convenient 
to
just set precision once.

Statelessness and separation into is a "low level" and a "high level" mpmath is 
good
because convenience aspects of the interface (like mp.dps) can be changed 
without
worrying about the correctness of the underlying algorithms.

> The mutable dict could be **kwargs.

I don't see how that would work. Here is the idea:

def mpf_exp(x, prec, rounding, flags=None):
    if x == fzero:
        return fone
    # exp(x) is transcendental so rounding will occur
    if flags:
        flags['inexact'] = True
    return _calculate_exp(x, prec, rounding)

But yeah, I'm not sure this is a good idea.

Original comment by fredrik....@gmail.com on 4 Feb 2009 at 3:05

GoogleCodeExporter commented 9 years ago
Something like "inexact=True" could be stored inside the result. This would be a
better place IMHO. The high-level functions could check the result and raise
exceptions if wanted, while the low-level functions prefer to return inf, nan, 
etc.

Original comment by Vinzent.Steinberg@gmail.com on 4 Feb 2009 at 5:17