Domanda

Ho un certo numero di funzioni C, e vorrei chiamarli in pitone. Cython sembra essere la strada da percorrere, ma non posso davvero trovare un esempio di come esattamente questo è fatto. Le mie C sguardi funzione come questa:

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 ) ;

Tutto quello che voglio fare è quello di indicare i primi tre parametri (una stringa e due numeri interi), e recuperare 8 array NumPy (o le liste di pitone. Tutte le doppie hanno array N elementi). Il mio codice si presuppone che i puntatori siano rivolte a un pezzo già allocata della memoria. Inoltre, il codice C prodotta dovrebbe link per alcune librerie esterne.

È stato utile?

Soluzione

Ecco un piccolo ma completo esempio di passaggio di array numpy a una funzione C esterna, logicamente

fc( int N, double* a, double* b, double* z )  # z = a + b

utilizzando Cython. (Questo è sicuramente ben noto a chi lo conosce bene. I commenti sono benvenuti. Ultima modifica:. 23 Feb 2011, per Cython 0,14)

Prima leggi o scremato Cython accumulo e Cython con NumPy .

2 passi:

  • python f-setup.py build_ext --inplace
    giri f.pyx e fc.cpp -> f.so, una libreria dinamica
  • python test-f.py
    carichi import f f.so; f.fpy( ... ) chiama il fc( ... ) C.

usi python f-setup.py distutils per eseguire Cython, compilazione e link:
cython f.pyx -> f.cpp
compilazione f.cpp e fc.cpp
Link f.o fc.o -> f.so, una dinamica lib che import f pitone verrà caricato.

Per gli studenti, io suggerirei: fare un diagramma di questi passaggi, Guardare attraverso il file qui sotto, quindi scaricare ed eseguire.

(distutils è un enorme, pacchetto contorto utilizzato per creare pacchetti di Python per la distribuzione, e installarli. Qui stiamo usando solo una piccola parte di esso per compilare e linkare f.so. Questo passaggio non ha nulla a che fare con Cython, ma può essere fonte di confusione; semplici errori in un .pyx possono causare pagine di messaggi di errore oscuri da g ++ di compilazione e di collegamento. Guarda anche distutils doc e / o SO domande sul distutils .)

Come make, setup.py sarà rieseguire cython f.pyx e g++ -c ... f.cpp se f.pyx è più recente f.cpp.
Per la pulizia, rm -r build/.

Un'alternativa al setup.py sarebbe quella di eseguire i passi a parte, in uno script o Makefile:
cython --cplus f.pyx -> f.cpp # see cython -h
g++ -c ... f.cpp -> f.o
g++ -c ... fc.cpp -> fc.o
cc-lib f.o fc.o -> dynamic library f.so.
Modificare l'involucro cc-lib-mac qui di seguito per la piattaforma e l'installazione: non è bello, ma piccolo

.

Per esempi reali di Cython involucro C, guardare i file .pyx in quasi tutte le SciKit .

Vedere anche: Cython per gli utenti numpy e SO domande / tag / Cython .


Per decomprimere i seguenti file, cut-incollare il molto da un unico grande file, dicono cython-numpy-c-demo, poi in Unix (in un ambiente pulito directory nuovo) sh cython-numpy-c-demo run.

#--------------------------------------------------------------------------------
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

Altri suggerimenti

Il seguente codice Cython da http://article.gmane.org/gmane.comp.python.cython.user/5625 non richiede cast espliciti e gestisce anche le matrici non continui:

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])

In sostanza è possibile scrivere la funzione Cython tale che assegna gli array (assicuratevi di cimport numpy as np):

cdef np.ndarray[np.double_t, ndim=1] rr = np.zeros((N,), dtype=np.double)

poi passare nel puntatore .data di ciascuna alla funzione C. Che dovrebbe funzionare. Se non è necessario iniziare con zeri si potrebbe usare np.empty per un piccolo aumento di velocità.

Vedere la Cython per NumPy utenti esercitazione nella documentazione (riparato per il link corretto).

Si dovrebbe verificare ctypes è probabilmente la cosa più facile da usare se tutto si vuole è una funzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top