Search code examples
pythonnumpyruntime-errorwarningsinfinity

Why does float64 produce a RuntimeWarning where float produces a nan?


I'm using the warnings module to raise warnings as errors.

When I call my function plot_fig_4, I get the following error:

In [5]: plot_amit.plot_fig_4()
g: 1 of 3
theta_E: 1 of 1000
---------------------------------------------------------------------------
RuntimeWarning                            Traceback (most recent call last)
<ipython-input-5-5a631d2493d7> in <module>()
----> 1 plot_amit.plot_fig_4()

/home/dan/Science/dopa_net/plot_amit.pyc in plot_fig_4()
    130                                                                     tau_0, tau,
    131                                                                     theta_E_,
--> 132                                                                     H)
    133 
    134             # Iterate through theta_I, starting at the lowest value.

/home/dan/Science/dopa_net/plot_support.py in _get_d_phi(mu, sigma, tau_0, tau_i, theta_i, H)
   2059     for (i, mu_), (j, sigma_) in itertools.product(enumerate(mu),
   2060                                                    enumerate(sigma)):
-> 2061         phi[i, j] = _get_phi(tau_0, tau_i, theta_i, mu_, sigma_, H)
   2062     import pdb
   2063     pdb.set_trace()

/home/dan/Science/dopa_net/plot_support.py in _get_phi(tau_0, tau, theta_over_J, mu_over_J, sigma_over_J, H)
   1835 
   1836     # Compute the integral.
-> 1837     integral = _integrate_little_phi(lower, alpha)
   1838 
   1839     # Compute phi.

/home/dan/Science/dopa_net/plot_support.py in _integrate_little_phi(lower, upper)
   1869     upper_int = _integrate(upper)
   1870     lower_int = _integrate(lower)
-> 1871     return upper_int - lower_int
   1872 
   1873 

RuntimeWarning: invalid value encountered in double_scalars

OK. So I stick a pdb.set_trace inside _integrate_little_phi, just before the line at which the error is raised, re-run, and inspect the values of the relevant variables:

In [7]: plot_amit.plot_fig_4()
g: 1 of 3
theta_E: 1 of 1000
> /home/dan/Science/dopa_net/plot_support.py(1873)_integrate_little_phi()
-> return upper_int - lower_int
(Pdb) upper_int
inf
(Pdb) lower_int
inf
(Pdb) type(upper_int)
<type 'numpy.float64'>
(Pdb) type(lower_int)
<type 'numpy.float64'>

Huh. So the error was raised because I was subtracting infinity from infinity. Can I replicate that?

(Pdb) upper_int - lower_int
*** RuntimeWarning: invalid value encountered in double_scalars

Yes. But wait a minute. Let's try one more thing:

(Pdb) np.inf
inf
(Pdb) type(np.inf)
<type 'float'>
(Pdb) np.inf - np.inf
nan

What the what? When I directly subtract infinity from infinity using np.inf (where np is numpy), I get nan, not the RuntimeWarning.

Why do I get nan in this instance and RuntimeWarning in the other? I have posted the difference in type (float versus float64) deliberately. The question is, why do these (trivially) different types produce different results?


Solution

  • Because in the case of np.inf , the type is float (the basic datatype) , whereas in case of upper_int / lower_int the data type is numpy.float64 . Similar issue can be reproduced by -

    In [7]: a = np.float64('inf')
    
    In [8]: type(a)
    Out[8]: numpy.float64
    
    In [9]: a - a
    RuntimeWarning: invalid value encountered in double_scalars
      if __name__ == '__main__':
    Out[9]: nan
    

    For the case of np.inf / float -

    In [3]: float('inf') - float('inf')
    Out[3]: nan
    
    In [11]: np.inf
    Out[11]: inf
    
    In [12]: type(np.inf)
    Out[12]: float
    

    I think this may be because in case of normal inf , you cannot get it from calculation. Example -

    >>> 123123123123. ** 2
    1.5159303447561418e+22
    >>> _ ** 2
    2.298044810152475e+44
    >>> _ ** 2
    5.281009949468725e+88
    >>> _ ** 2
    2.788906608638767e+177
    >>> _ ** 2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OverflowError: (34, 'Result too large')
    

    Instead you always get an overflow error.

    Whereas when using np.float64 , you can get the infinity value from calculation (though even that time it would throw a warning) -

    In [63]: n = np.float64('123123123123123123123')
    
    In [64]: n
    Out[64]: 1.2312312312312313e+20
    
    In [65]: n = n ** 2
    
    In [66]: n = n ** 2
    
    In [67]: n = n ** 2
    
    In [68]: n = n ** 2
    C:\Anaconda3\Scripts\ipython-script.py:1: RuntimeWarning: overflow encountered in double_scalars
      if __name__ == '__main__':
    
    In [69]: n
    Out[69]: inf
    

    Hence, since you can get np.float64 infinity through calculation, they are throwing more warnings, when you try to do more calculations on it that may try to reduce the number from infinity to a much smaller value , that is subtracting/dividing by infinity (multiplication or addition of infinity is fine, since adding infinity to infinity would only give back infinity). Example -

    In [71]: n - n
    C:\Anaconda3\Scripts\ipython-script.py:1: RuntimeWarning: invalid value encountered in double_scalars
      if __name__ == '__main__':
    Out[71]: nan
    
    In [72]: n/n
    C:\Anaconda3\Scripts\ipython-script.py:1: RuntimeWarning: invalid value encountered in double_scalars
      if __name__ == '__main__':
    Out[72]: nan
    
    In [73]: n*n
    Out[73]: inf
    

    Though in your case , I believe you may have gotten a direct infinite value from source.