Interfacing with C

Ctypes and CFFI

Numba supports jitting ctypes and CFFI function calls. Numba will automatically figure out the signatures of the functions and data. Below is a gibbs sampling code that accesses ctypes (or CFFI) functions defined in another module ( rk_seed, rk_gamma and rk_normal), and that passes in a pointer to a struct also allocated with ctypes (state_p):

def gibbs(N, thin):
    rng.rk_seed(0, rng.state_p)

    x = 0
    y = 0
    samples = np.empty((N,2))
    for i in range(N):
        for j in range(thin):
            x = rng.rk_gamma(rng.state_p, 3.0, 1.0/(y**2+4))
            y = rng.rk_normal(rng.state_p, 1.0/(x+1), 1.0/math.sqrt(2+2*x))

        samples[i, 0] = x
        samples[i, 1] = y

    return samples

Note

Passing in ctypes or CFFI libraries to autojit functions does not yet work. However, passing in individual functions does.

Writing Low-level Code

Numba allows users to deal with high-level as well as low-level C-like code. Users can access and define new or external stucts, deal with pointers, and call C functions natively.

Note

Type declarations are covered in in Types and Variables.

Structs

Structs can be declared on the stack, passed in as arguments, returned from external or Numba functions, or originate from record arrays. Structs currently have copy-by-value semantics, but this is likely to change.

Struct fields can accessed as follows:

  • struct.attr
  • struct['attr']
  • struct[field_index]

An example is shown below:

from numba import struct, jit, double
import numpy as np

record_type = struct([('x', double), ('y', double)])
record_dtype = record_type.get_dtype()
a = np.array([(1.0, 2.0), (3.0, 4.0)], dtype=record_dtype)

@jit(argtypes=[record_type[:]])
def hypot(data):
    # return types of numpy functions are inferred
    result = np.empty_like(data, dtype=np.float64) 
    # notice access to structure elements 'x' and 'y' via attribute access
    # You can also index by field name or field index:
    #       data[i].x == data[i]['x'] == data[i][0]
    for i in range(data.shape[0]):
        result[i] = np.sqrt(data[i].x * data[i].x + data[i].y * data[i].y)

    return result

print hypot(a)

# Notice inferred return type
print hypot.signature
# Notice native sqrt calls and for.body direct access to memory...
#print hypot.lfunc

Pointers

Pointers in Numba can be used in a similar way to C. They can be cast, indexed and operated on with pointer arithmetic. Currently it is however not possible to obtain the address of an lvalue.

An example is shown below:

import numba
from numba import *
from numba.tests.test_support import autojit_py3doc
import numpy as np

int32p = int32.pointer()
voidp = void.pointer()

@autojit_py3doc
def test_pointer_arithmetic():
    """
    >>> test_pointer_arithmetic()
    48L
    """
    p = int32p(Py_uintptr_t(0))
    p = p + 10
    p += 2
    return Py_uintptr_t(p) # 0 + 4 * 12

@autojit_py3doc(locals={"pointer_value": Py_uintptr_t})
def test_pointer_indexing(pointer_value, type_p):
    """
    >>> a = np.array([1, 2, 3, 4], dtype=np.float32)
    >>> test_pointer_indexing(a.ctypes.data, float32.pointer())
    (1.0, 2.0, 3.0, 4.0)

    >>> a = np.array([1, 2, 3, 4], dtype=np.int64)
    >>> test_pointer_indexing(a.ctypes.data, int64.pointer())
    (1L, 2L, 3L, 4L)
    """
    p = type_p(pointer_value)
    return p[0], p[1], p[2], p[3]

@autojit
def test_compare_null():
    """
    >>> test_compare_null()
    True
    """
    return voidp(Py_uintptr_t(0)) == numba.NULL

numba.testmod()

Table Of Contents

Previous topic

Arrays

Next topic

Static Compilation (pycc)

This Page