13

I am building an array with cython element by element. I'd like to store the constant np.inf (or -1 * np.inf) in some entries. However, this will require the overhead of going back into Python to look up inf. Is there a libc.math equivalent of this constant? Or some other value that could easily be used that's equivalent to (-1*np.inf) and can be used from Cython without overhead?

EDIT example, you have:

cdef double value = 0
for k in xrange(...):
  # use -inf here -- how to avoid referring to np.inf and calling back to python?
  value = -1 * np.inf

3 Answers3

18

The recommended way of doing this in Cython is:

from numpy.math cimport INFINITY

Note, that this is a "cimport" rather than a regular import. This is Cython's official wrapper around NumPy's npymath.

Tom
  • 379
  • 4
  • 7
  • 2
    I get the error `fatal error: 'numpy/npy_math.h' file not found`. Anyone else get this? – hlin117 Aug 27 '15 at 06:46
  • @hlin117, nope. Probably something wrong with your setup. This answer is still the recommended way to get infinity. – Tom Oct 01 '16 at 22:59
  • 2
    Can you provide a cite for "the recommended way..."? – The Unfun Cat Jun 20 '18 at 10:22
  • 1
    Are you being overly pedantic? If you want to use Numpy's stuff, then this a cleaner way than using a cdef extern. If you want to use math.h, then it looks like `from libc.math cimport INFINITY` is now available, which could also work. Both of these options are better than the other two answers given, especially if you are working on any modern platform. – Tom Jun 27 '18 at 01:56
  • 1
    Also, see the example of using `cimport` here: https://cython.readthedocs.io/en/latest/src/tutorial/external.html#calling-c-functions – Tom Jun 27 '18 at 02:51
17

There's no literal for it, but float can parse it from a string:

>>> float('inf')
inf
>>> np.inf == float('inf')
True

Alternatively, math.h may (almost certainly will) declare a macro that evaluates to inf, in which case you can just use that:

cdef extern from "math.h":
    float INFINITY

(There's no clean way to check if INFINITY is defined in pure Cython, so if you want to cover all your bases you'll need to get hacky. One way of doing it is to create a small C header, say fallbackinf.h:

#ifndef INFINITY
#define INFINITY 0
#endif

And then in your .pyx file:

cdef extern from "math.h":
    float INFINITY

cdef extern from "fallbackinf.h":
    pass

inf = INFINITY if INFINITY != 0 else float('inf')

(You can't assign to INFINITY, because it's an rvalue. You could do away with the ternary operator if you #defined INFINITY as 1.0/0.0 in your header, but that might raise SIGFPE, depending on your compiler.)

This is definitely in the realm of cargo cult optimisation, though.)

Cairnarvon
  • 25,981
  • 9
  • 51
  • 65
  • but if I use `float` within a Cython loop, will that involve calling back to Python? I edited my answer to give an example. –  Apr 17 '13 at 03:00
  • 1
    Ultimately that calls `PyFloat_AsDouble` every time, which isn't *that* slow, but not something you want to do more often than necessary; assign it to a constant once, outside the loop. But I've updated my answer with a different, compile-time method. – Cairnarvon Apr 17 '13 at 03:48
  • Thanks! your `cdef extern...` works great. Are there systems/conditions where it might not work, or should it work on any python distribution / OS? –  Apr 17 '13 at 04:16
  • 1
    The INFINITY macro was added in C99; if your math.h is older, it's not guaranteed to be there (though it probably still will be). In practice, it should be fine. – Cairnarvon Apr 17 '13 at 04:29
  • Last question: is it possible to programmatically check for INFINITY (if it exists) through Cython and if not, default to `float('inf')`? –  Apr 17 '13 at 04:40
  • 1
    Not cleanly. I've added what I believe to be the easiest way to my answer. – Cairnarvon Apr 17 '13 at 05:17
4

You can use Numpy's math library, see here for what's available:

cdef extern from "numpy/npy_math.h":
    double inf "NPY_INFINITY"

When building the Cython extension module, you need to specify the correct include directory and library to link:

>>> from numpy.distutils.misc_util import get_info
>>> get_info('npymath')
{'define_macros': [], 
 'libraries': ['npymath', 'm'], 
 'library_dirs': ['/usr/lib/python2.7/dist-packages/numpy/core/lib'], 
 'include_dirs': ['/usr/lib/python2.7/dist-packages/numpy/core/include']}

The information obtained from that function can be passed onto Python distutils, or whatever build system you use.

pv.
  • 33,875
  • 8
  • 55
  • 49