retorno de puntero de matriz c malloc en cython

5 minutos de lectura

avatar de usuario
W9DKI

¿Cómo se devuelve un puntero de matriz malloc (o puntero de matriz numpy) en cython a python3, de manera eficiente?

El código de cython funciona perfectamente siempre que no devuelva el puntero de matriz

Me gustaría:

def double complex* randn_zig(int n):
  ...
  r = malloc(n*n*sizeof(double complex))
  ...
  return r

El equivalente c11 (gcc 11) es:

double complex* randn_zig(int n){

    r = malloc(n*n*sizeof(double complex))

    return r
}

Yo he tratado
<double complex*> randn_zig(int n):

y randn_zig(<double complex*> r, int n):

y otras permutaciones sin éxito hasta el momento. La versión del código c y cython es 5 veces más rápida que la versión Numby/pylab randn si puedo encontrar una manera de devolver un puntero a la gran matriz compleja doble de 10 ^ 6 a 10 ^ 10.

  • ¿Puede proporcionar más código para la parte cuando intenta llamar a esta función? ¿Y qué errores te dan?

    – Ashalynd

    3 de agosto de 2014 a las 7:16


  • No hay un equivalente de python para un puntero, por lo que no es realmente el puntero que desea devolver, es una referencia a (es decir, algún objeto de python que tiene un puntero) al bloque de memoria que se ha asignado. El tipo de objeto de Python que elija dependerá de lo que pretenda hacer con el puntero/bloque de memoria.

    – Bi Rico

    04/08/2014 a las 22:47

avatar de usuario
Syrtis mayor

API de Numpy C

Tu pregunta es similar a esta publicación.

Puede usar la siguiente función para pasar un puntero C a la matriz Numpy. La memoria se liberará automáticamente cuando se recicle la matriz Numpy. Si desea liberar el puntero de forma masiva, no debe configurar el indicador NPY_OWNDATA.

import numpy as np
cimport numpy as np

cdef pointer_to_numpy_array_complex128(void * ptr, np.npy_intp size):
    '''Convert c pointer to numpy array.
    The memory will be freed as soon as the ndarray is deallocated.
    '''
    cdef extern from "numpy/arrayobject.h":
        void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)
    cdef np.ndarray[np.complex128, ndim=1] arr = \
            np.PyArray_SimpleNewFromData(1, &size, np.NPY_COMPLEX128, ptr)
    PyArray_ENABLEFLAGS(arr, np.NPY_OWNDATA)
    return arr

Para referencia:

Vistas de memoria con tipo Cython

Por supuesto, también puedes usar vista de memoria cython.

import numpy as np
cimport numpy as np

cdef np.complex128_t[:,:] view = <np.complex128_t[:n,:n]> c_pointer
numpy_arr = np.asarray(view)

El código anterior transferirá el puntero C a una matriz numpy. Sin embargo, esto sería no ¡Libere memoria automáticamente, debe liberar la memoria usted mismo o provocaría una pérdida de memoria!

  • 1. Gracias Syrtis lo intentaré en breve. Realmente necesito resolver esto ahora para un proyecto. Por favor, vea el comentario a continuación.

    – W9DKI

    31 de enero de 2016 a las 22:41


Otra opción (además de las dos opciones de la respuesta superior: PyArray_SimpleNewFromData y simplemente devolver la vista de memoria escrita sin manejar la memoria) es usar la cython.view.array clase.

Esta es una clase de nivel bastante bajo que se puede usar para envolver la memoria existente. tiene un atributo callback_free_data donde puede configurar una función para que se llame a la destrucción para que libere la memoria (el código de ejemplo aquí se copia de la documentación):

cdef view.array my_array = view.array(..., mode="fortran", allocate_buffer=False)
my_array.data = <char *> my_data_pointer

# define a function that can deallocate the data (if needed)
my_array.callback_free_data = free

Expone el protocolo de búfer para que pueda indexarlo, usarlo con vistas de memoria escritas o envolverlo con una matriz Numpy (sin copiar) con np.asarray. La última característica puede ser más fácil de usar que PyArray_SimpleNewFromData.

Creo que el mejor enfoque es pasar el puntero de una matriz existente creada en Python a través de NumPy a Cython; de lo contrario, parece que debe copiar el contenido de la matriz creada por malloc a otra matriz, como se demuestra en este ejemplo de juguete:

import numpy as np
cimport numpy as np

from libc.stdlib cimport malloc, free

def main():
  cdef int i, n=40
  cdef double complex *r
  cdef np.ndarray[np.complex128_t, ndim=1] a
  a = np.zeros(n*n, dtype=np.complex128)
  r = <double complex *>malloc(n*n*sizeof(double complex))
  for i in range(n*n):
      r[i] = 1.
  for i in range(n*n):
      a[i] = r[i]
  free(r)
  return a

  • He estado ocupado en otros proyectos y ahora estoy de vuelta resolviendo este. Gracias. En cualquier caso, python 3.4 y numpy parecen incluir el generador principal de Meresenne, que supongo que es dsfmt(), predeterminado en Ubuntu 15.10. No he mirado la fuente de Python para python3.4, pero no estaba allí para 3.2. No estoy seguro si tienen el algoritmo Ziggurat o Leva para randn(). necesito mirar Implementaré uno si lo desea. 73

    – W9DKI

    31 de enero de 2016 a las 22:38


avatar de usuario
W9DKI

Para gcc 5+ que usa el estándar C-11 ( gcc -std=gnu11 . . . ), la sintaxis para las matrices multidimensionales malloc y calloc ha cambiado significativamente.

Un procedimiento main(), para crear una matriz calloc compleja, doble y 2-D r[n][n] para n = 1024 es ahora:

long n = 1024;
complex double (*r)[n] = calloc(n, sizeof *r);

Un ejemplo de un generador de números aleatorios gaussiano randn_box_muller() usando un puntero a esta matriz calloc r[n][n] es:

inline static void randn_box_muller(long n, complex double r[][n])
{
    long i, j; 
    register double x, y;

    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){  
            x = 2.*M_PI*dsfmt_genrand_close_open(&dsfmt);
            y = sqrt(-2.*log(dsfmt_genrand_close_open(&dsfmt)));
            r[i][j] = (cos(x) + I*sin(x))*y;
        }
     }
     return;
}

Esta sintaxis de asignación de calloc relativamente nueva es un poco extraña. Funciona bien para matrices calloc y malloc de 1, 2 e incluso n dimensiones. Con suerte, esto también funcionará junto con Python3. Espero estar probando esto en breve.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad