python - Envoltura simple del código C con cython
numpy (4)
Aquí hay un ejemplo pequeño pero completo de pasar matrices numpy a una función C externa, lógicamente
fc( int N, double* a, double* b, double* z ) # z = a + b
usando Cython. (Esto seguramente es bien conocido por aquellos que lo conocen bien. Los comentarios son bienvenidos. Último cambio: 23 de febrero de 2011, para Cython 0.14).
Primero lee o desglosa la construcción de Cython y Cython con NumPy .
2 pasos:
-
python f-setup.py build_ext --inplace
giraf.pyx
yfc.cpp
->f.so
, una biblioteca dinámica -
python test-f.py
import f
cargasf.so
;f.fpy( ... )
llama al Cfc( ... )
.
python f-setup.py
usa distutils
para ejecutar cython, compilar y vincular:
cython f.pyx -> f.cpp
compilar f.cpp
y fc.cpp
link fo fc.o
-> f.so
, una lib dinámica que python import f
cargará.
Para los estudiantes, sugeriría: haga un diagrama de estos pasos, revise los archivos a continuación, luego descárguelos y ejecútelos.
( distutils
es un paquete enorme e intrincado que se usa para hacer paquetes de Python para su distribución e instalarlos. Aquí estamos usando solo una pequeña parte para compilar y vincular a f.so
Este paso no tiene nada que ver con Cython, pero puede ser confuso, los errores simples en .pyx pueden causar páginas de mensajes de error oscuros de compilación y enlace de g ++. Consulte también las preguntas de distutils doc y / o SO en distutils ).
Al igual que make
, setup.py
volverá a ejecutar cython f.pyx
y g++ -c ... f.cpp
si f.pyx
es más reciente que f.cpp
.
Para limpiar, rm -r build/
.
Una alternativa a setup.py
sería ejecutar los pasos por separado, en un script o Makefile:
cython --cplus f.pyx -> f.cpp # see cython -h
g++ -c ... f.cpp -> fo
g++ -c ... fc.cpp -> fc.o
cc-lib fo fc.o -> dynamic library f.so
Modifique el contenedor cc-lib-mac
continuación para su plataforma e instalación: no es bonito, pero es pequeño.
Para ver ejemplos reales de Cython wrapping C, mire los archivos .pyx en cualquier SciKit .
Ver también: Cython para usuarios de NumPy y cython .
Para descomprimir los siguientes archivos, corte y pegue el lote en un archivo grande, digamos cython-numpy-c-demo
, luego en Unix (en un nuevo directorio limpio) ejecute sh cython-numpy-c-demo
.
#--------------------------------------------------------------------------------
cat >f.pyx <</!
# f.pyx: numpy arrays -> extern from "fc.h"
# 3 steps:
# cython f.pyx -> f.c
# link: python f-setup.py build_ext --inplace -> f.so, a dynamic library
# py test-f.py: import f gets f.so, f.fpy below calls fc()
import numpy as np
cimport numpy as np
cdef extern from "fc.h":
int fc( int N, double* a, double* b, double* z ) # z = a + b
def fpy( N,
np.ndarray[np.double_t,ndim=1] A,
np.ndarray[np.double_t,ndim=1] B,
np.ndarray[np.double_t,ndim=1] Z ):
""" wrap np arrays to fc( a.data ... ) """
assert N <= len(A) == len(B) == len(Z)
fcret = fc( N, <double*> A.data, <double*> B.data, <double*> Z.data )
# fcret = fc( N, A.data, B.data, Z.data ) grr char*
return fcret
!
#--------------------------------------------------------------------------------
cat >fc.h <</!
// fc.h: numpy arrays from cython , double*
int fc( int N, const double a[], const double b[], double z[] );
!
#--------------------------------------------------------------------------------
cat >fc.cpp <</!
// fc.cpp: z = a + b, numpy arrays from cython
#include "fc.h"
#include <stdio.h>
int fc( int N, const double a[], const double b[], double z[] )
{
printf( "fc: N=%d a[0]=%f b[0]=%f /n", N, a[0], b[0] );
for( int j = 0; j < N; j ++ ){
z[j] = a[j] + b[j];
}
return N;
}
!
#--------------------------------------------------------------------------------
cat >f-setup.py <</!
# python f-setup.py build_ext --inplace
# cython f.pyx -> f.cpp
# g++ -c f.cpp -> f.o
# g++ -c fc.cpp -> fc.o
# link f.o fc.o -> f.so
# distutils uses the Makefile distutils.sysconfig.get_makefile_filename()
# for compiling and linking: a sea of options.
# http://docs.python.org/distutils/introduction.html
# http://docs.python.org/distutils/apiref.html 20 pages ...
# https://stackoverflow.com/questions/tagged/distutils+python
import numpy
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
# from Cython.Build import cythonize
ext_modules = [Extension(
name="f",
sources=["f.pyx", "fc.cpp"],
# extra_objects=["fc.o"], # if you compile fc.cpp separately
include_dirs = [numpy.get_include()], # .../site-packages/numpy/core/include
language="c++",
# libraries=
# extra_compile_args = "...".split(),
# extra_link_args = "...".split()
)]
setup(
name = ''f'',
cmdclass = {''build_ext'': build_ext},
ext_modules = ext_modules,
# ext_modules = cythonize(ext_modules) ? not in 0.14.1
# version=
# description=
# author=
# author_email=
)
# test: import f
!
#--------------------------------------------------------------------------------
cat >test-f.py <</!
#!/usr/bin/env python
# test-f.py
import numpy as np
import f # loads f.so from cc-lib: f.pyx -> f.c + fc.o -> f.so
N = 3
a = np.arange( N, dtype=np.float64 )
b = np.arange( N, dtype=np.float64 )
z = np.ones( N, dtype=np.float64 ) * np.NaN
fret = f.fpy( N, a, b, z )
print "fpy -> fc z:", z
!
#--------------------------------------------------------------------------------
cat >cc-lib-mac <</!
#!/bin/sh
me=${0##*/}
case $1 in
"" )
set -- f.cpp fc.cpp ;; # default: g++ these
-h* | --h* )
echo "
$me [g++ flags] xx.c yy.cpp zz.o ...
compiles .c .cpp .o files to a dynamic lib xx.so
"
exit 1
esac
# Logically this is simple, compile and link,
# but platform-dependent, layers upon layers, gloom, doom
base=${1%.c*}
base=${base%.o}
set -x
g++ -dynamic -arch ppc /
-bundle -undefined dynamic_lookup /
-fno-strict-aliasing -fPIC -fno-common -DNDEBUG `# -g` -fwrapv /
-isysroot /Developer/SDKs/MacOSX10.4u.sdk /
-I/Library/Frameworks/Python.framework/Versions/2.6/include/python2.6 /
-I${Pysite?}/numpy/core/include /
-O2 -Wall /
"$@" /
-o $base.so
# undefs: nm -gpv $base.so | egrep ''^ *U _+[^P]''
!
# 23 Feb 2011 13:38
Tengo varias funciones C y me gustaría llamarlas desde python. Cython parece ser el camino a seguir, pero realmente no puedo encontrar un ejemplo de cómo se hace esto exactamente. Mi función C se ve así:
void calculate_daily ( char *db_name, int grid_id, int year,
double *dtmp, double *dtmn, double *dtmx,
double *dprec, double *ddtr, double *dayl,
double *dpet, double *dpar ) ;
Todo lo que quiero hacer es especificar los primeros tres parámetros (una cadena y dos enteros) y recuperar 8 matrices numpy (o listas de Python. Todas las matrices dobles tienen N elementos). Mi código asume que los punteros apuntan a un trozo de memoria ya asignado. Además, el código C producido debe vincularse a algunas bibliotecas externas.
Básicamente puedes escribir tu función de Cython de modo que asigne las matrices (asegúrate de que cimport numpy as np
):
cdef np.ndarray[np.double_t, ndim=1] rr = np.zeros((N,), dtype=np.double)
luego pase el puntero .data
de cada uno a su función C. Eso debería funcionar. Si no necesita comenzar con ceros, puede usar np.empty
para aumentar la velocidad.
Consulte el tutorial de Cython for NumPy Users en los documentos (lo corrigió en el enlace correcto).
El siguiente código de Cython de http://article.gmane.org/gmane.comp.python.cython.user/5625 no requiere moldes explícitos y también maneja matrices no continuas:
def fpy(A):
cdef np.ndarray[np.double_t, ndim=2, mode="c"] A_c
A_c = np.ascontiguousarray(A, dtype=np.double)
fc(&A_c[0,0])