Sto scrivendo un'estensione C al mio programma Python per scopi di velocità, e in esecuzione in un comportamento molto strano cercando di passare in un array numpy 3-dimensionale. Funziona con un array bidimensionale, ma sono sicuro che sto mettendo a punto qualcosa con i puntatori che cercano di farlo funzionare con la 3a dimensione. Ma ecco la parte strana. Se passo in un array 3-D, si blocca con un errore bus . Se (in Python) creo prima la mia variabile come array 2D, quindi la sovrascrivo con un array 3D, funziona perfettamente con. Se la variabile è prima un array vuoto e poi un array 3D, si blocca con un errore di segmento . Com'è possibile che ciò accada?Passaggio di array numpy tridimensionale a C
Inoltre, qualcuno può aiutarmi a far funzionare un array 3D? O dovrei semplicemente rinunciare e passare in un array 2D e rimodellarlo da solo?
Ecco il mio codice C:
static PyObject* func(PyObject* self, PyObject* args) {
PyObject *list2_obj;
PyObject *list3_obj;
if (!PyArg_ParseTuple(args, "OO", &list2_obj, &list3_obj))
return NULL;
double **list2;
double ***list3;
//Create C arrays from numpy objects:
int typenum = NPY_DOUBLE;
PyArray_Descr *descr;
descr = PyArray_DescrFromType(typenum);
npy_intp dims[3];
if (PyArray_AsCArray(&list2_obj, (void **)&list2, dims, 2, descr) < 0 || PyArray_AsCArray(&list3_obj, (void ***)&list3, dims, 3, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "error converting to c array");
return NULL;
}
printf("2D: %f, 3D: %f.\n", list2[3][1], list3[1][0][2]);
}
Ed ecco il mio codice Python che chiama la funzione di cui sopra:
import cmod, numpy
l2 = numpy.array([[1.0,2.0,3.0], [4.0,5.0,6.0], [7.0,8.0,9.0], [3.0, 5.0, 0.0]])
l3 = numpy.array([[2,7, 1], [6, 3, 9], [1, 10, 13], [4, 2, 6]]) # Line A
l3 = numpy.array([]) # Line B
l3 = numpy.array([[[2,7, 1, 11], [6, 3, 9, 12]],
[[1, 10, 13, 15], [4, 2, 6, 2]]])
cmod.func(l2, l3)
Quindi, se io commento sia la linea A e B, si blocca con un Errore del bus. Se la linea A è presente, ma la riga B è commentata, viene eseguita correttamente senza errori. Se la Linea B è presente ma la riga A è commentata, stampa i numeri corretti ma Segue errori. Infine, se entrambe le linee sono presenti, stampa anche i numeri corretti e quindi i difetti di Seg. Che diavolo sta succedendo qui?
MODIFICA: Ok. Wow. Quindi stavo usando int
in Python ma li chiamavo double
in C. E funzionava bene con gli array 1D e 2D. Ma non in 3D. Così ho cambiato la definizione Python di l3 per essere float, e ora funziona tutto in modo fantastico (Grazie mille Bi Rico).
Ma ora, più strano comportamento con Linee A & B! Ora se entrambe le linee sono commentate, il programma funziona. Se la linea B è presente ma A è commentata, funziona, e idem se entrambi sono non commentati. Ma se la linea A è presente e B è commentato, ottengo di nuovo il fantastico errore Bus. Mi piacerebbe davvero evitarli in futuro, quindi qualcuno ha la minima idea del perché la dichiarazione di una variabile Python possa avere questo tipo di impatto?
EDIT 2:. Beh, come folle come questi errori sono, sono tutti dovuti alla matrice NumPy 3-dimensionale passo in Se mi passa solo in 1 o 2-D array, si comporta come previsto, e la manipolazione delle altre variabili Python non fa nulla. Questo mi porta a credere che il problema si trovi da qualche parte nel conteggio dei riferimenti di Python. Nel codice C il conteggio dei riferimenti è diminuito più di quanto dovrebbe per gli array 3-D, e quando quella funzione restituisce Python prova a ripulire gli oggetti e tenta di eliminare un puntatore NULL. Questa è solo la mia ipotesi, e ho provato a Py_INCREF();
tutto quello che potevo pensare senza successo. Credo che mi limiterò a utilizzare una matrice 2D e rimodellare in C.
Sei sicuro che '(void **)' è corretta, non si dovrebbe solo passare in a '(void *)'? – seberg
My C fa schifo ma ... La tua espressione in 'if' non è in cortocircuito se la prima chiamata a' PyArray_AsRaray 'supera? Potrebbe benissimo essere che la seconda chiamata, cioè quella per 'list3', non viene mai eseguita. – Jaime
@seberg Non sono sicuro che '(void **)' sia corretto, ma '(void *)' provoca un errore di Bus. @Jaime No, quella funzione restituisce valori negativi solo se fallisce, molto probabilmente se il malloc che chiama fallisce. – DaveTheScientist