2011-03-24 15 views
5

Estoy usando SWIG para unir código C++ a Python (2.6), y parte de ese pegamento incluye un código que convierte grandes campos de datos (millones de valores) del lado C++ a una matriz Numpy. El mejor método que se me ocurre con los aperos de un iterador para la clase y luego se proporciona un método de Python:Conversión rápida del vector C/C++ al array Numpy

def __array__(self, dtype=float): 
    return np.fromiter(self, dtype, self.size()) 

El problema es que cada llamada iterador next es muy costoso, ya que tiene que pasar por unos tres o cuatro Envoltorios SWIG. Toma demasiado tiempo. Puedo garantizar que los datos de C++ se almacenan de forma contigua (ya que viven en un std :: vector), y parece que Numpy debería poder llevar un puntero al principio de esos datos junto con la cantidad de valores que contiene, y léelo directamente.

¿Hay alguna manera de pasar un puntero a internal_data_[0] y el valor internal_data_.size() a numpy para que pueda acceder directamente o copiar los datos sin toda la sobrecarga de Python?

Respuesta

0

Parece que la única solución real es basar algo de pybuffer.i que puede copiar desde C++ en un búfer existente. Si se agrega esto a un trago archivo de inclusión:

%insert("python") %{ 
import numpy as np 
%} 

/*! Templated function to copy contents of a container to an allocated memory 
* buffer 
*/ 
%inline %{ 
//==== ADDED BY numpy.i 
#include <algorithm> 

template < typename Container_T > 
void copy_to_buffer(
     const Container_T& field, 
     typename Container_T::value_type* buffer, 
     typename Container_T::size_type length 
     ) 
{ 
// ValidateUserInput(length == field.size(), 
//   "Destination buffer is the wrong size"); 
    // put your own assertion here or BAD THINGS CAN HAPPEN 

    if (length == field.size()) { 
     std::copy(field.begin(), field.end(), buffer); 
    } 
} 
//==== 

%} 

%define TYPEMAP_COPY_TO_BUFFER(CLASS...) 
%typemap(in) (CLASS::value_type* buffer, CLASS::size_type length) 
(int res = 0, Py_ssize_t size_ = 0, void *buffer_ = 0) { 

    res = PyObject_AsWriteBuffer($input, &buffer_, &size_); 
    if (res < 0) { 
     PyErr_Clear(); 
     %argument_fail(res, "(CLASS::value_type*, CLASS::size_type length)", 
       $symname, $argnum); 
    } 
    $1 = ($1_ltype) buffer_; 
    $2 = ($2_ltype) (size_/sizeof($*1_type)); 
} 
%enddef 


%define ADD_NUMPY_ARRAY_INTERFACE(PYVALUE, PYCLASS, CLASS...) 

TYPEMAP_COPY_TO_BUFFER(CLASS) 

%template(_copy_to_buffer_ ## PYCLASS) copy_to_buffer<CLASS>; 

%extend CLASS { 
%insert("python") %{ 
def __array__(self): 
    """Enable access to this data as a numpy array""" 
    a = np.ndarray(shape=(len(self),), dtype=PYVALUE) 
    _copy_to_buffer_ ## PYCLASS(self, a) 
    return a 
%} 
} 

%enddef 

entonces usted puede hacer un recipiente "Numpy" -able con

%template(DumbVectorFloat) DumbVector<double>; 
ADD_NUMPY_ARRAY_INTERFACE(float, DumbVectorFloat, DumbVector<double>); 

Luego, en Python, acaba de hacer:

# dvf is an instance of DumbVectorFloat 
import numpy as np 
my_numpy_array = np.asarray(dvf) 

Esto solo tiene la sobrecarga de una única llamada de traducción Python < -> C++, no la N que resultaría de una matriz de longitud N típica.

Una versión un poco más completa de este código es parte de mi PyTRT project at github.

2

Querrá definir __array_interface__() instead. Esto le permitirá pasar el puntero y la información de forma directamente.

+0

¿Puede proporcionar un poco más de detalle para una implementación práctica? ¿Existe también una forma de hacerlo sin tener que compilar mi proyecto con los archivos de encabezado de Numpy? Gracias. –

+0

también dice que es una interfaz heredada. –

+0

'__array_interface__' es simplemente un dict simple con tipos simples dentro de él. No es necesario compilar con ningún encabezado Numpy. Ignora la nota que lo llama "legado". Pensé que ya había eliminado eso. Si lo desea, puede implementar la interfaz de búfer PEP 3118, pero esto es más fácil. –

0

Si envuelve su vector en un objeto que implementa Pythons Buffer Interface, puede pasarlo a la matriz numpy para la inicialización (consulte docs, tercer argumento). Apuesto a que esta inicialización es mucho más rápido, ya que solo puede usar memcpy para copiar los datos.

+0

Gracias por el consejo.¿Tiene algún ejemplo del uso de 'pybuffer_mutable_binary' u otra interfaz en SWIG para implementar la interfaz' __buffer__' para, por ejemplo, flotadores? –

+0

@Seth: Lo siento, no puedo ayudarte allí. –

+0

Parece que tendría que implementar toda la interfaz del búfer para esta clase a mano desde cero. SWIG solo proporciona la capacidad de * leer otros almacenamientos intermedios *, no de exportar funciones de almacenamiento intermedio. –

1

Tal vez sea posible usar f2py en lugar de swig. A pesar de su nombre, es capaz de conectar python con C y con Fortran. Ver http://www.scipy.org/Cookbook/f2py_and_NumPy

La ventaja es que maneja la conversión a matrices numpy automáticamente.

Dos advertencias: si aún no conoce Fortran, puede encontrar f2py un poco extraño; y no sé qué tan bien funciona con C++.

+0

Gracias por la respuesta. Conozco un poco de FORTRAN, pero estoy usando muchas características 'C++' -y en mi código: templates, typedefs, etc. Prefiero no introducir otra dependencia. –

+0

Bastante re re C++. Probablemente tendrías que escribir envoltorios C intermedios intermedios, lo que podría ser un problema. Por otro lado, no es realmente otra dependencia ya que f2py es parte de numpy, que ya estás usando. No necesitarías un compilador fortran. – deprecated