Python 定义更复杂的静态数组
通常在数值方法中,有很多系数是静态的,因为它们对于特定的方法是固定的。我想知道Cython/C中设置这样的数组或变量的最佳方法是什么 在我的例子中,除了系数和级数外,龙格-库塔积分方法基本相同。现在我正在做一些类似(简化)Python 定义更复杂的静态数组,python,c,cython,Python,C,Cython,通常在数值方法中,有很多系数是静态的,因为它们对于特定的方法是固定的。我想知道Cython/C中设置这样的数组或变量的最佳方法是什么 在我的例子中,除了系数和级数外,龙格-库塔积分方法基本相同。现在我正在做一些类似(简化) #定义一些结构,使其可用于所有不同的Runge-Kutta方法 ctypedef结构RKHelper: 整数 双*系数 cdef: RK方法 #后来的第二种方法、第三种方法等。 firstRKMethod.numStages=3 firstRKMethod.coeffs=ma
#定义一些结构,使其可用于所有不同的Runge-Kutta方法
ctypedef结构RKHelper:
整数
双*系数
cdef:
RK方法
#后来的第二种方法、第三种方法等。
firstRKMethod.numStages=3
firstRKMethod.coeffs=malloc(firstRKMethod.numStages*sizeof(双精度))
#数组可以很大,并且大多数条目为零
对于范围内的ii(firstRKMethod.numStages):
firstRKMethod.coefs[ii]=0。
#设置非零元素
firstRKMethod.系数[2]=1.3
有几点:
- 我知道malloc不适用于静态数组,但我不知道如何在Cython中将“numStages”或“RKHelper”声明为静态,所以我不能使用静态数组。。。或者我在RKHelper中执行类似于“double[4]”的操作,这不允许对所有RK方法使用相同的结构定义
- 我想知道是否有比循环更好的方法。我不想手动设置整个数组(例如,数组=[0,0,0,0,0,1.3,…很多数字大多为零])
- 就我所见,Cython中没有“真正的”静态变量,是吗
干杯实现您想要实现的一种方法是将Runge-Kutta方案的系数设置为全局变量,这样您就可以使用静态数组。这会很快,但肯定很难看 丑陋的解决方案:
cdef int numStages = 3
# Using the pointer notation you can set a static array
# as well as its elements in one go
cdef double* coeffs = [0.,0.,1.3]
# You can always change the coefficients further as you wish
def RungeKutta_StaticArrayGlobal():
# Do stuff
# Just to check
return numStages
from libc.stdlib cimport calloc, free
ctypedef struct RKHelper:
int numStages
double* coeffs
def RungeKutta_DynamicArray():
cdef:
RKHelper firstRKMethod
firstRKMethod.numStages = 3
# Use calloc instead, it zero initialises the buffer, so you don't
# need to set the elements to zero within a loop
firstRKMethod.coeffs = <double*> calloc(firstRKMethod.numStages,sizeof(double))
# Set non-zero elements
firstRKMethod.coeffs[2] = 1.3
free(firstRKMethod.coeffs)
# Just to check
return firstRKMethod.numStages
更好的解决方案是定义一个cython
类,其成员为Runge-Kutta系数
优雅的解决方案:
cdef class RungeKutta_StaticArrayClass:
cdef double* coeffs
cdef int numStages
def __cinit__(self):
# Note that due to the static nature of self.coeffs, its elements
# expire beyond the scope of this function
self.coeffs = [0.,0.,1.3]
self.numStages = 3
def GetnumStages(self):
return self.numStages
def Integrate(self):
# Reset self.coeffs
self.coeffs = [0.,0.,0.,0.,0.8,2.1]
# Perform integration
关于设置元素的问题,让我们使用calloc
而不是malloc
使用动态分配的数组修改您自己的代码
动态分配的版本:
cdef int numStages = 3
# Using the pointer notation you can set a static array
# as well as its elements in one go
cdef double* coeffs = [0.,0.,1.3]
# You can always change the coefficients further as you wish
def RungeKutta_StaticArrayGlobal():
# Do stuff
# Just to check
return numStages
from libc.stdlib cimport calloc, free
ctypedef struct RKHelper:
int numStages
double* coeffs
def RungeKutta_DynamicArray():
cdef:
RKHelper firstRKMethod
firstRKMethod.numStages = 3
# Use calloc instead, it zero initialises the buffer, so you don't
# need to set the elements to zero within a loop
firstRKMethod.coeffs = <double*> calloc(firstRKMethod.numStages,sizeof(double))
# Set non-zero elements
firstRKMethod.coeffs[2] = 1.3
free(firstRKMethod.coeffs)
# Just to check
return firstRKMethod.numStages
RungeKutta_StaticArray
基本上具有接近no op
的成本,这意味着对数组分配没有运行时惩罚。您可以选择在此函数中声明coefs
,并且计时仍然相同。RungeKutta_StaticArrayClass
尽管设置带有成员和构造函数的类的开销仍然比动态分配的版本快。为什么不使用numpy数组呢?实际上,它不是静态的(请参见最后的注释),但您可以在全局范围内分配它,以便在模块启动时创建它。您还可以访问下面的原始C阵列,因此没有实际的效率成本
import numpy as np
# at module global scope
cdef double[::1] rk_coeffs = np.zeros((50,)) # avoid having to manually fill with 0s
# illustratively fill the non-zero elements
rk_coeffs[1] = 2.0
rk_coeffs[3] = 5.0
# if you need to convert to a C array
cdef double* rk_coeffs_ptr = &rk_coeffs[0]
注意,我对这个问题的理解是,您使用的“static”是指“编译到模块中”,而不是任何Python静态方法/类变量 谢谢你的回答。关于您的建议:如果许多条目相同(例如零),那么在代码中编写StaticArray()有点烦人。我不想在我的代码中有成千上万的带零的行。基本上,我希望有一种方法可以告诉Cython或编译器在编译时编写一个静态数组,即“Cython:在编译时使用此循环生成一个静态数组”。integrator类是我的第一个实现,但是考虑到GIL中cdef类的局限性,我试图在非常基本的块(比如integrator)中避开它。