Provide glue to organise complex tasks
Combine performance of compiled codes with flexibility of Python
Reuse code that you already have
Basic approach is to compile extension modules
Requires
gfortran, cc, ...)Will produce
.so) loaded at run timeimport extension module as usualFortran
f2py is part of Numpy will handle external subroutinesf90wrapC (more choice)
f2py can be used (a kludge)Various other approaches
f2py¶You need to provide f2py with:
f2py can help you generate a signature file)f2py can:
optional) that define the Fortran interfacef2py <source> -m <extension_module> -h <signature>.pyf
dependf2py -c <signature_file>.pyf <source_file>.f90import extension_module_name
extension_module_name.function(args)
farray_sqrt.f90¶array_sqrt() is an external subroutine
subroutine array_sqrt(n, a_in, a_out)
implicit none
integer, intent(in) :: n
real, dimension(n), intent(in) :: a_in
real, dimension(n), intent(out) :: a_out
integer :: i
do i = 1, n
a_out(i) = sqrt(a_in(i))
end do
return
end subroutine array_sqrt
f2py can try to create the signature file (farray_sqrt.pyf) automatically
f2py farray_sqrt.f90 -m farray -h farray_sqrt.pyf
The Python extension module will be called: farray
-m optionSignature in text file called: farray_sqrt.pyf
use the -h option
Note: will not overwrite an existing signature file:
Use --overwrite-signature to overwrite.
# Call from within Python to save exiting notebook...
!f2py ./code/farray_sqrt.f90 -m farray -h farray_sqrt.pyf
Attributes such as optional, intent and depend specify the visibility, purpose and dependencies of the arguments.
!cat farray_sqrt.pyf
Note: exact result can depend on version of numpy, so it is worth checking
Once you have verified that the signature file is correct
f2py to compile a module file that can be imported into Pythonf2py -c farray_sqrt.pyf farray_sqrt.f90
This should produce a shared library file called: farray.so
# Run f2py command from within notebook
# If you don't want to see the output, try "msg = !f2py ..."
!f2py -c farray_sqrt.pyf ./code/farray_sqrt.f90
# Check we have the farray.so
!ls
!cat farray_sqrt.pyf
# import the extension module
import numpy as np
from farray import array_sqrt
# view docsting of function (automatically produced)
array_sqrt?
# let's try to use the function
ain = np.array([1.0, 4.0, 9.0, 16.0, 2.0], np.double)
print(ain)
aout = array_sqrt(ain)
print(aout)
ctypes¶Uses only python (no additional interface file or mixed-language intermediate code)
import ctypes
Must load the library (.so) file explicitly
lib = ctypes.cdll.LoadLibrary("./my_library.so")
lib.my_c_function.restype = ctypes.c_int
lib.my_c_function.argtypes = [ctypes.c_double]
Consider the simple function:
int my_c_function(double val) {
return (int) (val + 1.0);
}
We need to compile an extension module:
!gcc -c -fPIC ./code/my_library.c
!gcc -shared -o my_library.so my_library.o
!ls
Now:
import ctypes
lib = ctypes.cdll.LoadLibrary("./my_library.so")
lib.my_c_function.restype = ctypes.c_int
lib.my_c_function.argtypes = [ctypes.c_double]
x = float(23)
result = lib.my_c_function(x)
print(result, type(result))
ctypes and numpy.ndarray¶Consider again the square root example, this time in C:
#include <math.h>
void array_sqrt(int n, double * a_in, double * a_out) {
int i;
for (i = 0; i < n; i++) {
a_out[i] = sqrt(a_in[i]);
}
return;
}
!gcc -c -fPIC ./code/c_sqrt.c
!gcc -shared -o c_sqrt.so c_sqrt.o
numpy.ctypeslib¶The corresponding ctypes code must address the two C pointers.
import ctypes
import numpy
from numpy.ctypeslib import ndpointer
lib = ctypes.cdll.LoadLibrary("./c_sqrt.so")
lib.array_sqrt.restype = None
lib.array_sqrt.argtypes = [ctypes.c_int, \
ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), \
ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]
a_in = numpy.array([16.0, 25.0, 36.0, 49.0])
a_out = numpy.empty(4, np.double)
lib.array_sqrt(4, a_in, a_out)
print(a_out)
Native Python interface
Cython : converts Python-like code into a C library which can call other C libraries
SWIG (or Simplified Wrapper and Interface Generator) : reads header files and generates a library Python can load
ctypes, cffi (C Foreign Function Interface for Python) : both provide "foreign function interfaces", or lightweight APIs, for calling C libraries from within Python
The goal is to provide a convenient and reliable way to call compiled C code from Python using interface declarations written in C
Weave : includes C/C++ code within Python code and compiles it transparently
Boost.python : helps write C++ libraries that Python can load and use easily
PyCUDA : allows you to include NVIDIA CUDA code within Python. You can also write C code by hand, that can be called by Python.
f2py is a simple way to call Fortran code from Pythonf90wraphelp(ctypes)
fibonacci.f90¶Use f2py to create an extension module for function fibonacci() at ./code/fibonacci.f90 and test it in Python.
fibonacci() computes the first n Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13,...
and stores the results in the array provided.
subroutine fibonacci(n, a_out)
implicit none
integer, intent(in) :: n
real*8, dimension(n) :: a_out
integer :: i
do i = 1, n
if (i.eq.1) then
a_out(i) = 0.0
else if (i.eq.2) then
a_out(i) = 1.0
else
a_out(i) = a_out(i-1) + a_out(i-2)
end if
end do
end subroutine fibonacci
!ls
An equivalent C function is available to compute a Fibonacci series: ./code/fibonacci.c