Python is considered to behave as an excellent adhesive between different programming languages. Most of what we have seen along those lines thus far involves invoking shell programs indirectly, perhaps via IPython's !
notation or by using sys
or os.popen
. However, many performance-critical modules in Python, such as NumPy, have substantial components written in C or other high-performance languages. We'll examine several ways to interface or incorporate other languages—particularly C and Fortran—into your Python program in this lesson.
from __future__ import division
import numpy as np
import scipy as sp
import matplotlib as mpl
import matplotlib.pyplot as plt
import sys
print(sys.prefix)
!echo $PYTHONPATH
/Users/lrao/Library/Enthought/Canopy_64bit/User
Motivation
As is probably now clear to you, Python is a flexible and easy-to-use language. The programmer time required to program a new model or calculation is typically quite short.
However, this reduction in human time, comes at the expense of increasing computation time, especially when we are talking about calculations which involve iterating over large arrays. Interpreted languages (MATLAB, Python, Perl, Ruby, etc.) are thus categorically slower than low-level compiled languages (C, C++, Fortran, etc.). On the other hand, writing lengthy programs in C and FORTRAN is a cumbersome task. This tradeoff is one motivation to code the expensive chunks in C or FORTRAN and glue the bigger picture with Python.
In real terms, the range in relative performance on a set of standard benchmarks can be quantified thus (where 1.0 is the performance of C):
Python doesn't comport itself poorly among these peers, but it's still not in the same performance league as the heavy hitters.
Another major reason you may need to use Fortran or C with Python is access to legacy code: code that is already performing to specifications that would be too expensive, difficult, or loss-incurring to port to Python.
Fortran (Formula Translation) was introduced in 1954 for numeric and scientific computing. Fortran is compiled to machine code for the purpose of speed, and it serves as the benchmark for high-performance computing even today. Despite ongoing criticisms, it has managed to survive and adapt for nearly sixty years. Fortran still exists and dominates HPC due to both its computational performance and software inertia—the enormously large body of legacy code written in Fortran.
Let's look at an example FORTRAN function to get a better understanding of how it works!
Note: This lesson's aim is not to verse you well in Fortran, so we'll neglect nitty-gritty details. We will introduce just enough to address common needs and demonstrates interfacing with Python.
%%file circle.f
PROGRAM CIRCLE
REAL R, AREA
! This program reads a real number r and prints
! the area of a circle with radius r.
WRITE (*,*) 'Enter radius, r = '
READ (*,*) R
AREA = 3.14159*R*R
WRITE (*,*) 'Area, A = ', AREA
STOP
END
Writing circle.f
The resulting compilation and execution:
$ gfortran -ffree-form -o circle circle.f
$ ./circle
Enter radius, r =
1
Area, A = 3.14159012
Anyway, that's a little ugly, isn't it? Originally, all Fortran programs had to be written in upper-case letters. Most people now write lower-case. You are free to mix letter case, but note that Fortran is not case-sensitive: X
and x
are the same variable.
If you wish to use $\pi$ as a constant, you can do it using the parameter
statement:
%%file circle.f
program circle
real r, area, pi
parameter (pi = 3.14159)
! This program reads a real number r and prints
! the area of a circle with radius r.
write (*,*) 'Enter radius, r = '
read (*,*) R
area = 3.14159*r*r
write (*,*) 'Area, A = ', area
stop
end
Overwriting circle.f
Every variable should be defined in a declaration. This establishes the type of the variable. The most common declarations are:
integer list of variables
real list of variables
double precision list of variables
complex list of variables
logical list of variables
character list of variables
Implicit typing can be turned on or off using implicit [real]
or implicit none
, and the latter in particular is often seen in the wild.
FORTRAN uses one dimensional arrays to correspond to vectors and 2D arrays to represent matrices.
real a(20)
declares an array of size 20. By convention, Fortran arrays are indexed from 1 (MATLAB follows this convention as well). Thus the first number in the array is denoted by a(1) and the last by a(20).
integer i(10)
logical aa(0:1)
double precision x(100)
This code segment stores the 10 first square numbers in the array sq:
integer i, sq(10)
do 100 i = 1, 10
sq(i) = i**2
100 continue
A common bug in Fortran is that the program tries to access array elements that are out of bounds or undefined. This is the responsibility of the programmer, and the Fortran compiler will not detect any such bugs!
Note: One tricky thing about reading Fortran code is that constructs may not mean what you think. For instance:
real*8 a
does not mean a vector a
with eight components, but an eight-byte (C double
) floating-point number a
..le.
and other operators of this type mean less than or equal to, <=
, etc.real A(3,5)
declares a 2D array of size 3x5. It is common to declare arrays of a larger size than what we use because in FORTRAN we cannot dynamically change the size of an array. In other words, arrays are static.
real A(3,5)
integer i,j
! We will only use the upper 3 by 3 part of this array.
do 20 j = 1, 3
do 10 i = 1, 3
a(i,j) = real(i)/real(j)
10 continue
20 continue
Note also that, contrary to C and Python, Fortran arrays are column-major. The first index refers to the row, as is conventional in linear algebraic notation, but loops would need to be reversed relative to C (thus loop over rows before columns in Fortran for efficiency (src)). This becomes critical when passing data to and from Fortran.
%%file hellofortran.f
! File hellofortran.f
subroutine hellofortran (n)
integer n
do 100 i=0, n
print *, "Fortran says hello"
100 continue
end
Overwriting hellofortran.f
!f2py -c -m hellofortran hellofortran.f
running build running config_cc unifing config_cc, config, build_clib, build_ext, build commands --compiler options running config_fc unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options running build_src build_src building extension "hellofortran" sources f2py options: [] f2py:> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/hellofortranmodule.c creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7 Reading fortran codes... Reading file 'hellofortran.f' (format:fix,strict) Post-processing... Block: hellofortran Block: hellofortran Post-processing (stage 2)... Building modules... Building module "hellofortran"... Constructing wrapper function "hellofortran"... hellofortran(n) Wrote C/API module "hellofortran" to file "/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/hellofortranmodule.c" adding '/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.c' to sources. adding '/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7' to include_dirs. copying /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/f2py/src/fortranobject.c -> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7 copying /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/f2py/src/fortranobject.h -> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7 build_src: building npy-pkg config files running build_ext customize UnixCCompiler customize UnixCCompiler using build_ext customize Gnu95FCompiler Found executable /usr/local/bin/gfortran customize Gnu95FCompiler customize Gnu95FCompiler using build_ext building 'hellofortran' extension compiling C sources C compiler: gcc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -DNDEBUG -g -O3 -arch x86_64 creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7 compile options: '-I/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7 -I/Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 -c' gcc: /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.c In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.c:2: In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.h:13: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:4: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1761: /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings] #warning "Using deprecated NumPy API, disable it by " \ ^ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: warning: equality comparison with extraneous parentheses [-Wparentheses-equality] if ((fp->defs[i].func==NULL)) { ~~~~~~~~~~~~~~~~^~~~~~ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: note: remove extraneous parentheses around the comparison to silence this warning if ((fp->defs[i].func==NULL)) { ~ ^ ~ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: note: use '=' to turn this equality comparison into an assignment if ((fp->defs[i].func==NULL)) { ^~ = 2 warnings generated. gcc: /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/hellofortranmodule.c In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/hellofortranmodule.c:17: In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.h:13: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:4: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1761: /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings] #warning "Using deprecated NumPy API, disable it by " \ ^ 1 warning generated. compiling Fortran sources Fortran f77 compiler: /usr/local/bin/gfortran -Wall -ffixed-form -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops Fortran f90 compiler: /usr/local/bin/gfortran -Wall -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops Fortran fix compiler: /usr/local/bin/gfortran -Wall -ffixed-form -fno-second-underscore -Wall -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops compile options: '-I/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7 -I/Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 -c' gfortran:f77: hellofortran.f /usr/local/bin/gfortran -Wall -arch x86_64 -Wall -undefined dynamic_lookup -bundle /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/hellofortranmodule.o /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/src.macosx-10.6-x86_64-2.7/fortranobject.o /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X/hellofortran.o -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin13/4.9.0 -lgfortran -o ./hellofortran.so Removing build directory /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpknMN5X
Now we write a Python script which uses the module
%%file hello.py
import hellofortran
hellofortran.hellofortran(5)
Overwriting hello.py
# run the script
!python hello.py
Fortran says hello Fortran says hello Fortran says hello Fortran says hello Fortran says hello Fortran says hello
Vector input , scalar output
%%file dprod.f
subroutine dprod(x, y, n)
double precision x(n), y
y = 1.0
do 100 i=1, n
y = y * x(i)
100 continue
end
Overwriting dprod.f
!rm -f dprod.pyf
!f2py -m dprod -h dprod.pyf dprod.f
Reading fortran codes... Reading file 'dprod.f' (format:fix,strict) Post-processing... Block: dprod {} In: :dprod:dprod.f:dprod vars2fortran: No typespec for argument "n". Block: dprod Post-processing (stage 2)... Saving signatures to file "./dprod.pyf"
The f2py program generates a declaration file dprod.pyf
!cat dprod.pyf
! -*- f90 -*- ! Note: the context of this file is case sensitive. python module dprod ! in interface ! in :dprod subroutine dprod(x,y,n) ! in :dprod:dprod.f double precision dimension(n) :: x double precision :: y integer, optional,check(len(x)>=n),depend(x) :: n=len(x) end subroutine dprod end interface end python module dprod ! This file was auto-generated with f2py (version:2). ! See http://cens.ioc.ee/projects/f2py2e/
The module does not know what Fortran subroutine arguments is input and output, so we need to manually edit the module declaration files and mark output variables with intent(out) and input variable with intent(in):
%%file dprod.pyf
python module dprod ! in
interface ! in :dprod
subroutine dprod(x,y,n) ! in :dprod:dprod.f
double precision dimension(n), intent(in) :: x
double precision, intent(out) :: y
integer, optional,check(len(x)>=n),depend(x),intent(in) :: n=len(x)
end subroutine dprod
end interface
end python module dprod
Overwriting dprod.pyf
!f2py -c dprod.pyf dprod.f #Compile the code which can be included in Python
running build running config_cc unifing config_cc, config, build_clib, build_ext, build commands --compiler options running config_fc unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options running build_src build_src building extension "dprod" sources creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7 f2py options: [] f2py: dprod.pyf Reading fortran codes... Reading file 'dprod.pyf' (format:free) Post-processing... Block: dprod Block: dprod Post-processing (stage 2)... Building modules... Building module "dprod"... Constructing wrapper function "dprod"... y = dprod(x,[n]) Wrote C/API module "dprod" to file "/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/dprodmodule.c" adding '/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.c' to sources. adding '/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7' to include_dirs. copying /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/f2py/src/fortranobject.c -> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7 copying /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/f2py/src/fortranobject.h -> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7 build_src: building npy-pkg config files running build_ext customize UnixCCompiler customize UnixCCompiler using build_ext customize Gnu95FCompiler Found executable /usr/local/bin/gfortran customize Gnu95FCompiler customize Gnu95FCompiler using build_ext building 'dprod' extension compiling C sources C compiler: gcc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -DNDEBUG -g -O3 -arch x86_64 creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7 compile options: '-I/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7 -I/Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 -c' gcc: /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/dprodmodule.c In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/dprodmodule.c:18: In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.h:13: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:4: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1761: /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings] #warning "Using deprecated NumPy API, disable it by " \ ^ 1 warning generated. gcc: /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.c In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.c:2: In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.h:13: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:4: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1761: /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings] #warning "Using deprecated NumPy API, disable it by " \ ^ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: warning: equality comparison with extraneous parentheses [-Wparentheses-equality] if ((fp->defs[i].func==NULL)) { ~~~~~~~~~~~~~~~~^~~~~~ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: note: remove extraneous parentheses around the comparison to silence this warning if ((fp->defs[i].func==NULL)) { ~ ^ ~ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: note: use '=' to turn this equality comparison into an assignment if ((fp->defs[i].func==NULL)) { ^~ = 2 warnings generated. compiling Fortran sources Fortran f77 compiler: /usr/local/bin/gfortran -Wall -ffixed-form -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops Fortran f90 compiler: /usr/local/bin/gfortran -Wall -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops Fortran fix compiler: /usr/local/bin/gfortran -Wall -ffixed-form -fno-second-underscore -Wall -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops compile options: '-I/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7 -I/Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 -c' gfortran:f77: dprod.f /usr/local/bin/gfortran -Wall -arch x86_64 -Wall -undefined dynamic_lookup -bundle /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/dprodmodule.o /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/src.macosx-10.6-x86_64-2.7/fortranobject.o /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk/dprod.o -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin13/4.9.0 -lgfortran -o ./dprod.so Removing build directory /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpALjlpk
import dprod #Using the module in Python
help (dprod)
Help on module dprod: NAME dprod FILE /Users/lrao/Documents/PythonTA/dprod.so DESCRIPTION This module 'dprod' is auto-generated with f2py (version:2). Functions: y = dprod(x,n=len(x)) . DATA __version__ = '$Revision: $' dprod = <fortran object> VERSION
dprod.dprod(np.arange(1,50))
6.082818640342675e+62
np.prod(np.arange(1.0,50.0))
6.0828186403426752e+62
Comparing the performance
xvec = np.random.rand(1000)
timeit dprod.dprod(xvec)
100000 loops, best of 3: 3.41 µs per loop
timeit xvec.prod()
100000 loops, best of 3: 6.9 µs per loop
The cumulative sum over data in an array is a loop-intensive algorithm and hence a good example to exploit the performance advantage in Fortran.
# pure Python
def py_dcumsum(a):
b = np.empty_like(a)
b[0] = a[0]
for n in range(1,len(a)):
b[n] = b[n-1]+a[n]
return b
%%file dcumsum.f
c File dcumsum.f
subroutine dcumsum(a, b, n)
double precision a(n)
double precision b(n)
integer n
cf2py intent(in) :: a
cf2py intent(out) :: b
cf2py intent(hide) :: n
b(1) = a(1)
do 100 i=2, n
b(i) = b(i-1) + a(i)
100 continue
end
Overwriting dcumsum.f
!f2py -c dcumsum.f -m dcumsum
running build running config_cc unifing config_cc, config, build_clib, build_ext, build commands --compiler options running config_fc unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options running build_src build_src building extension "dcumsum" sources f2py options: [] f2py:> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/dcumsummodule.c creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7 Reading fortran codes... Reading file 'dcumsum.f' (format:fix,strict) Post-processing... Block: dcumsum Block: dcumsum Post-processing (stage 2)... Building modules... Building module "dcumsum"... Constructing wrapper function "dcumsum"... b = dcumsum(a) Wrote C/API module "dcumsum" to file "/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/dcumsummodule.c" adding '/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.c' to sources. adding '/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7' to include_dirs. copying /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/f2py/src/fortranobject.c -> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7 copying /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/f2py/src/fortranobject.h -> /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7 build_src: building npy-pkg config files running build_ext customize UnixCCompiler customize UnixCCompiler using build_ext customize Gnu95FCompiler Found executable /usr/local/bin/gfortran customize Gnu95FCompiler customize Gnu95FCompiler using build_ext building 'dcumsum' extension compiling C sources C compiler: gcc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -DNDEBUG -g -O3 -arch x86_64 creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP creating /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7 compile options: '-I/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7 -I/Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 -c' gcc: /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.c In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.c:2: In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.h:13: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:4: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1761: /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings] #warning "Using deprecated NumPy API, disable it by " \ ^ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: warning: equality comparison with extraneous parentheses [-Wparentheses-equality] if ((fp->defs[i].func==NULL)) { ~~~~~~~~~~~~~~~~^~~~~~ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: note: remove extraneous parentheses around the comparison to silence this warning if ((fp->defs[i].func==NULL)) { ~ ^ ~ /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.c:338:30: note: use '=' to turn this equality comparison into an assignment if ((fp->defs[i].func==NULL)) { ^~ = 2 warnings generated. gcc: /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/dcumsummodule.c In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/dcumsummodule.c:18: In file included from /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.h:13: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:4: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17: In file included from /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1761: /Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings] #warning "Using deprecated NumPy API, disable it by " \ ^ 1 warning generated. compiling Fortran sources Fortran f77 compiler: /usr/local/bin/gfortran -Wall -ffixed-form -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops Fortran f90 compiler: /usr/local/bin/gfortran -Wall -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops Fortran fix compiler: /usr/local/bin/gfortran -Wall -ffixed-form -fno-second-underscore -Wall -fno-second-underscore -arch x86_64 -fPIC -O3 -funroll-loops compile options: '-I/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7 -I/Users/lrao/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 -c' gfortran:f77: dcumsum.f /usr/local/bin/gfortran -Wall -arch x86_64 -Wall -undefined dynamic_lookup -bundle /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/dcumsummodule.o /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/src.macosx-10.6-x86_64-2.7/fortranobject.o /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP/dcumsum.o -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin13/4.9.0 -lgfortran -o ./dcumsum.so Removing build directory /var/folders/sj/lkcv2fm52491sv_04hq72cb80000gn/T/tmpl5K8vP
import dcumsum
a = np.array([1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0])
py_dcumsum(a)
array([ 1., 3., 6., 10., 15., 21., 28., 36.])
dcumsum.dcumsum(a)
array([ 1., 3., 6., 10., 15., 21., 28., 36.])
a = np.random.rand(10000)
timeit py_dcumsum(a)
100 loops, best of 3: 7.63 ms per loop
timeit dcumsum.dcumsum(a)
100000 loops, best of 3: 13.8 µs per loop
Now we move on to a more intuitive—or at least familiar—language C. C is often called a mid-level or systems programming language. This is not a reflection on its lack of programming power but more a reflection on its capability to access the system's low level functions. It wins on the performance aspect and also on the not-as-ludicrously-obscure-as-Fortran aspect.
Despite its increased ease of use as opposed to FORTRAN, it still requires quite a bit of detail with respect to data types, functions, data structures etc. Python still involves less human time than C does. So, we benefit from gluing C and Python together.
int
—integer.float
—floating point value: i.e., a number with a fractional part.double
—a double-precision floating point value.char
—a single character.void
—a valueless special-purpose type.printf
—Output formatted string.scanf
—Input stream (until Return pressed).getchar
—Input single character.(This program should be run at the command line; getchar
-based input is not currently supported in the IPython notebook interface.)
Salient features of this program include the #include
statement, the int
return type of the main
function, and the use of {
and }
to delimit a block of code.
%%file test.c
#include <stdio.h>
int main() {
printf( "Happy Halloween! \n" );
getchar();
return 0;
}
!gcc -o test test.c
!./test
int sum_values_of_array(int all_nums[]) {
int i, sum=0;
for(i = 0; i<5; i++)
sum = sum + all_nums[i];
return sum;
}
import ctypes
ctypes.cdll.LoadLibrary("libc.so.6")
libc = ctypes.CDLL("libc.so.6")
libc.printf
--------------------------------------------------------------------------- OSError Traceback (most recent call last) <ipython-input-30-1380e5270232> in <module>() 1 import ctypes ----> 2 ctypes.cdll.LoadLibrary("libc.so.6") 3 libc = ctypes.CDLL("libc.so.6") 4 libc.printf /Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/lib/python2.7/ctypes/__init__.pyc in LoadLibrary(self, name) 441 442 def LoadLibrary(self, name): --> 443 return self._dlltype(name) 444 445 cdll = LibraryLoader(CDLL) /Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/lib/python2.7/ctypes/__init__.pyc in __init__(self, name, mode, handle, use_errno, use_last_error) 363 364 if handle is None: --> 365 self._handle = _dlopen(self._name, mode) 366 else: 367 self._handle = handle OSError: dlopen(libc.so.6, 6): image not found
print(libc.time(None))
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-31-83b3c7cc197a> in <module>() ----> 1 print(libc.time(None)) NameError: name 'libc' is not defined
%%file functions.c
#include <stdio.h>
void hello(int n);
double dprod(double *x, int n);
void dcumsum(double *a, double *b, int n);
void
hello(int n)
{
int i;
for (i = 0; i < n; i++)
{
printf("C says hello\n");
}
}
double
dprod(double *x, int n)
{
int i;
double y = 1.0;
for (i = 0; i < n; i++)
{
y *= x[i];
}
return y;
}
void
dcumsum(double *a, double *b, int n)
{
int i;
b[0] = a[0];
for (i = 1; i < n; i++)
{
b[i] = a[i] + b[i-1];
}
}
Overwriting functions.c
Compiling the C code
!gcc -c -Wall -O2 -Wall -ansi -pedantic -fPIC -o functions.o functions.c
!gcc -o libfunctions.so -shared functions.o
functions.c:46:2: warning: no newline at end of file [-Wnewline-eof] } ^ 1 warning generated.
!file libfunctions.so
libfunctions.so: Mach-O 64-bit dynamically linked shared library x86_64
Now we need to write wrapper functions to access the C library: To load the library we use the ctypes package, which included in the Python standard library (with extensions from numpy for passing arrays to C). Then we manually set the types of the argument and return values (no automatic code inspection here!).
%%file functions.py
import numpy
import ctypes
_libfunctions = numpy.ctypeslib.load_library('libfunctions', '.')
_libfunctions.hello.argtypes = [ctypes.c_int]
_libfunctions.hello.restype = ctypes.c_void_p
_libfunctions.dprod.argtypes = [numpy.ctypeslib.ndpointer(dtype=numpy.float), ctypes.c_int]
_libfunctions.dprod.restype = ctypes.c_double
_libfunctions.dcumsum.argtypes = [numpy.ctypeslib.ndpointer(dtype=numpy.float), numpy.ctypeslib.ndpointer(dtype=numpy.float), ctypes.c_int]
_libfunctions.dcumsum.restype = ctypes.c_void_p
def hello(n):
return _libfunctions.hello(int(n))
def dprod(x, n=None):
if n is None:
n = len(x)
x = numpy.asarray(x, dtype=numpy.float)
return _libfunctions.dprod(x, int(n))
def dcumsum(a, n):
a = numpy.asarray(a, dtype=numpy.float)
b = numpy.empty(len(a), dtype=numpy.float)
_libfunctions.dcumsum(a, b, int(n))
return b
Overwriting functions.py
%%file run_hello_c.py
import functions
functions.hello(3)
Overwriting run_hello_c.py
!python run_hello_c.py
C says hello C says hello C says hello
import functions
functions.dprod([1,2,3,4,5]) #product
120.0
a = np.random.rand(100000)
res_c = functions.dcumsum(a, len(a))
res_fortran = dcumsum.dcumsum(a)
res_c - res_fortran
array([ 0., 0., 0., ..., 0., 0., 0.])
timeit functions.dcumsum(a, len(a))
1000 loops, best of 3: 143 µs per loop
timeit dcumsum.dcumsum(a)
1000 loops, best of 3: 147 µs per loop
timeit a.cumsum()
1000 loops, best of 3: 326 µs per loop
from numpy import zeros
from scipy import weave
dx = 0.1
dy = 0.1
dx2 = dx*dx
dy2 = dy*dy
def py_update(u):
nx, ny = u.shape
for i in xrange(1,nx-1):
for j in xrange(1, ny-1):
u[i,j] = ((u[i+1, j] + u[i-1, j]) * dy2 +
(u[i, j+1] + u[i, j-1]) * dx2) / (2*(dx2+dy2))
def calc(N, Niter=100, func=py_update, args=()):
u = zeros([N, N])
u[0] = 1
for i in range(Niter):
func(u,*args)
return u
%timeit calc(100, 200, func=py_update)
1 loops, best of 3: 6.63 s per loop
Using numpy
def num_update(u):
u[1:-1,1:-1] = ((u[2:,1:-1]+u[:-2,1:-1])*dy2 +
(u[1:-1,2:] + u[1:-1,:-2])*dx2) / (2*(dx2+dy2))
%timeit calc(100, 200, func=num_update)
10 loops, best of 3: 20.4 ms per loop
Using the weave
module to implement C.
def weave_update(u):
code = """
int i, j;
for (i=1; i<Nu[0]-1; i++) {
for (j=1; j<Nu[1]-1; j++) {
U2(i,j) = ((U2(i+1, j) + U2(i-1, j))*dy2 + \
(U2(i, j+1) + U2(i, j-1))*dx2) / (2*(dx2+dy2));
}
}
"""
weave.inline(code, ['u', 'dx2', 'dy2'])
%timeit calc(100, 200, func=weave_update)
10 loops, best of 3: 24 ms per loop
Cython serves as another interface between Python and C. Strictly speaking, Cython is a static compiler for a superset of the Python language. From the documentation: "The Cython language is a superset of the Python language that additionally supports calling C functions and declaring C types on variables and class attributes. This allows the compiler to generate very efficient C code from Cython code."
Basically any Python code is valid Cython code, but Cython additionally imposes C data types (and manages them intelligently behind the scenes), as well as provides a basic superset of the language to manage this extensions (such as the keywords cdef
and cimport
). So you lose the flexibility of a dynamic interpreted language, but gain some performance and interoperability.
Using Cython consists of the following steps:
.pyx
source file.import
ing the module.%%file factorial.pyx
def factorial(n):
return 1 if (n < 1) else n * factorial(n-1)
Writing factorial.pyx
Hop over to a shell and take a look at the generated code in factorial.c
. I dare you to untangle it.
Given a pyx
file, there are a few ways we can convert this to a C module using Cython. The first is to carry out the basic steps manually.
!cython factorial.pyx
#!gcc -shared -fPIC -o factorial.so factorial.c
!gcc -I /software/canopy-1.4.1/installer/appdata/canopy-1.4.1.1975.rh5-x86_64/include/python2.7/ -shared -fPIC -o factorial.so factorial.c
factorial.c:8:10: fatal error: 'pyconfig.h' file not found #include "pyconfig.h" ^ 1 error generated.
import factorial
print(factorial.factorial(6))
--------------------------------------------------------------------------- ImportError Traceback (most recent call last) <ipython-input-56-4d6d17025505> in <module>() ----> 1 import factorial 2 print(factorial.factorial(6)) ImportError: No module named factorial
Another path to Cython is to use the built-in cythonize
function. (This is preferred for large codes.) I had trouble getting this to work on the EWS installation of Python, however.
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("factorial.pyx"),
)
# problem is clang ñ gcc here (-fopenmp)
An exception has occurred, use %tb to see the full traceback. SystemExit: usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: -c --help [cmd1 cmd2 ...] or: -c --help-commands or: -c cmd --help error: option -f not recognized
To exit: use 'exit', 'quit', or Ctrl-D.
%tb
--------------------------------------------------------------------------- SystemExit Traceback (most recent call last) <ipython-input-57-7e9601081d40> in <module>() 3 4 setup( ----> 5 ext_modules=cythonize("factorial.pyx"), 6 ) 7 # problem is clang ñ gcc here (-fopenmp) /Applications/Canopy.app/appdata/updates/ready/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/lib/python2.7/distutils/core.pyc in setup(**attrs) 138 ok = dist.parse_command_line() 139 except DistutilsArgError, msg: --> 140 raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg 141 142 if DEBUG: SystemExit: usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: -c --help [cmd1 cmd2 ...] or: -c --help-commands or: -c cmd --help error: option -f not recognized
!python setup.py build_ext --inplace
python: can't open file 'setup.py': [Errno 2] No such file or directory
When working with the IPython notebook environment, there is a convenient way of compiling and loading Cython code. Using the %%cython
IPython magic, we can simply type the Cython code in a code cell and let IPython take care of the conversion to C code, compilation and loading of the function. To be able to use the %%cython
magic in IPython, we first need to load the extension cythonmagic
:
%load_ext cythonmagic
%%cython
cimport numpy
def cy_dcumsum2(numpy.ndarray[numpy.float64_t, ndim=1] a, numpy.ndarray[numpy.float64_t, ndim=1] b):
cdef int i, n = len(a)
b[0] = a[0]
for i from 1 <= i < n:
b[i] = b[i-1] + a[i]
return b
import numpy as np
x = np.ones((32))
y = np.zeros(x.shape)
cy_dcumsum2(x,y)
print(y)
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32.]
Okay, so as a refresher, Cython usage boils down to:
$ cython mymodule.pyx
$ gcc mymodule.c -o mymodule.so
$ python
>>> import mymodule
Lakshmi Rao and Neal Davis developed these materials for Computational Science and Engineering at the University of Illinois at Urbana–Champaign.
This content is available under a [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/).