Cython con matrices de longitud variable
Las matrices de crecimiento dinámico son un tipo de matriz. Son muy útiles cuando no conoce el tamaño exacto de la matriz en el momento del diseño. Primero necesitas definir un número inicial de elementos. ( Wikipedia )
Escribí una solución de Python y la convertí a Cython . Cython se puede utilizar para mejorar la velocidad de los bucles for anidados en Python. Donde mi código Cython es un poco más rápido. Mi solución Cython obviamente no es la más rápida. Estoy tratando de realizar un bucle for anidado similar al de mi código Python lo más rápido posible en Cython.
Sería útil tener algo de experiencia en C, que no tengo. El principal problema con el que me encontré es que Cython tiene reglas de alcance diferentes a las de Python. Dado que C y Python tienen diferentes reglas de alcance. En otras palabras, no podemos crear un nuevo vector en el ciclo y asignarle el mismo nombre.
Mi solución funciona pero es demasiado lenta. ¿Alguien puede mejorar el código Cython anterior utilizando un enfoque más similar a C?
Pitón
import numpy as np
my_list = [1,2,3]
n = 10
a = 0.5
Estimate_1_list = []
Estimate_2_list = []
for l in my_list:
# Resizable matrices
a_mat = np.zeros((l,n+1),float)
b_mat = np.zeros((l,n+1),float)
for i in range(n):
t = i*a
for j in range(l):
# Fill matrices
a_mat[j,i+1] = a_mat[j,i+1] + np.random.random()
b_mat[j,i+1] = a_mat[j,i+1]/(2*t+3)
# Append values of interest to use at different values of matrix size
Estimate_1_list.append(np.mean(a_mat[:,n]))
Estimate_2_list.append(np.std(a_mat[:,n]))
results = [Estimate_1_list,Estimate_2_list]
Cython
import cython
# Load cython extension
%load_ext Cython
%%cython
import numpy as np
def my_function(list my_list, int n, int a ):
cdef list Estimate_1_list = []
cdef list Estimate_2_list = []
cdef int l,i,t,j
for l in my_list:
# Resizable matrices (could I use memory view?)
a_mat = np.zeros((l,n+1),float)
b_mat = np.zeros((l,n+1),float)
for i in range(n):
t = i*a
for j in range(l):
# Fill matrices
a_mat[j,i+1] = a_mat[j,i+1] + np.random.random()
b_mat[j,i+1] = a_mat[j,i+1]/(2*t+3)
# Append values of interest to use at different values of matrix size
Estimate_1_list.append(np.mean(a_mat[:,n]))
Estimate_2_list.append(np.std(a_mat[:,n]))
# Return results
results = [Estimate_1_list,Estimate_2_list]
return results
Pruebas
# Test cython to show that the function is running
my_list = [1,2,3]
n = 10
a = 0.5
my_function(my_list, n, a)
[[0.13545224609230933, 0.6603542545719762, 0.6632002117071227],
[0.0, 0.19967544614685195, 0.22125180486616808]]
Respuestas
Ceros
Esta:
a_mat = np.zeros
no es la llamada correcta para su propósito. En su np.emptylugar, desea , porque en realidad no le importa cuáles son los valores iniciales, ya que hace un ciclo de inicialización completo inmediatamente después.
Además de eso: dado que está agregando random()a cada elemento de a_mat, simplemente inicialice a_mata una sola llamada de random()con la forma correcta, en lugar de tener que hacer una adición de elementos.
Vectorización
Tiene una dimensión exterior (a ltravés my_list), una segunda dimensión ( l) y una tercera dimensión ( n + 1). La segunda dimensión es variable; el primero y el tercero son constantes. Esto significa que puede representar esto de manera más eficiente si reorganiza sus dimensiones de modo que las fijas estén en el interior. En otras palabras, si tuvieras
len(my_list) = 3
l = 1, 2, 3
n = 10
entonces puede representar esto como una única matriz tridimensional de dimensiones 6 * 3 * 10, donde 6 == len(my_list) * (len(my_list) - 1). Creo que es posible hacer todo esto sin un solo forbucle, que es ideal para el rendimiento vectorizado.