Tengo un pequeño proyecto que funciona muy bien con SWIG. En particular, algunas de mis funciones devuelven std::vector
s, que se traducen a tuplas en Python. Ahora hago muchos numerics, por lo que solo hago que SWIG los convierta en numpy arrays una vez que se devuelven desde el código de C++. Para hacer esto, uso algo como lo siguiente en SWIG.¿Hay alguna forma de utilizar pythonappend con la nueva función incorporada de SWIG?
%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}
(En realidad, hay varias funciones llamadas de datos, algunos de los cuales flotadores de retorno, por lo que puedo comprobar que val
es en realidad una tupla.) Esto funciona muy bien.
Pero, también me gustaría usar la bandera -builtin
que ya está disponible. Las llamadas a estas funciones de Datos son raras y en su mayoría interactivas, por lo que su lentitud no es un problema, pero existen otros bucles lentos que se aceleran significativamente con la opción incorporada.
El problema es que cuando uso ese indicador, la característica pythonappend se ignora silenciosamente. Ahora, Data simplemente devuelve una tupla nuevamente. ¿Hay alguna manera de que todavía pueda devolver matrices numpy? Intenté usar mapas de tipos, pero se convirtió en un desastre gigante.
Editar:
Borealid ha respondido a la pregunta muy bien. Solo para completar, incluyo un par de tipos de mapas relacionados pero sutilmente diferentes que necesito porque vuelvo por referencia constante y uso vectores de vectores (¡no empieces!). Estos son lo suficientemente diferentes como para que no quisiera que nadie más tropezara tratando de descubrir las pequeñas diferencias.
%typemap(out) std::vector<int>& {
npy_intp result_size = $1->size();
npy_intp dims[1] = { result_size };
PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
int* dat = (int*) PyArray_DATA(npy_arr);
for (size_t i = 0; i < result_size; ++i) { dat[i] = (*$1)[i]; }
$result = PyArray_Return(npy_arr);
}
%typemap(out) std::vector<std::vector<int> >& {
npy_intp result_size = $1->size();
npy_intp result_size2 = (result_size>0 ? (*$1)[0].size() : 0);
npy_intp dims[2] = { result_size, result_size2 };
PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_INT);
int* dat = (int*) PyArray_DATA(npy_arr);
for (size_t i = 0; i < result_size; ++i) { for (size_t j = 0; j < result_size2; ++j) { dat[i*result_size2+j] = (*$1)[i][j]; } }
$result = PyArray_Return(npy_arr);
}
Edición 2:
Aunque no es exactamente lo que estaba buscando, problemas similares también pueden ser resueltos usando @ enfoque de Monk (explained here).
No creo que pueda hacer esto sin escribir un mapa de tipos y hacerlo en el lado C, precisamente porque -builtin elimina el código donde normalmente se coloca pythonappend. ¿Está seguro de que la capacidad de compilación es mucho más rápida (es decir, si los perfiles lo llevaron a usarlo?) Me sentiría tentado de utilizar dos módulos, uno con y otro sin -builtin. – Flexo
Me sorprende que no haya ninguna advertencia de que '-builtin' ignora pythonappend. No estoy a la altura del desafío del mapeo tipográfico 'std :: vector's en matrices numpy. Hice un perfil, y aceleró significativamente el ciclo más molesto en mi interfaz (no lo suficiente como para tomar un descanso, demasiado tiempo para esperar con frecuencia). Pero también me di cuenta de que puedo mover este bucle en mi código de C++, aunque de forma algo torpe. Así que así es como iré. Aún así, su sugerencia de "dos módulos" es interesante y podría ser útil en otros casos. – Mike
¿Ha llamado a SWIG con -Wall? Supuse que advertiría en ese caso. – Flexo