定义更复杂的静态数组
Defining more complicated static arrays
在数值方法中经常有很多系数是静态的,因为它们对于特定方法是固定的。我想知道在 Cython/C 中设置此类数组或变量的最佳方法是什么。
在我的案例中,龙格-库塔积分法除了系数和级数不同外,大部分都是相同的。现在我正在做类似(简化)
# Define some struct such that it can be used for all different Runge-Kutta methods
ctypedef struct RKHelper:
int numStages
double* coeffs
cdef:
RKHelper firstRKMethod
# Later secondRKMethod, thirdRKMethod, etc.
firstRKMethod.numStages = 3
firstRKMethod.coeffs = <double*> malloc(firstRKMethod.numStages*sizeof(double))
# Arrays can be large and most entries are zero
for ii in range(firstRKMethod.numStages):
firstRKMethod.coeffs[ii] = 0.
# Set non-zero elements
firstRKMethod.coeffs[2] = 1.3
几点:
- 我知道 malloc 不适用于静态数组,但我不知道如何在 Cython 中将 "numStages" 或 "RKHelper" 声明为静态,所以我不能使用静态数组... 或者我在 RKHelper 中做了类似 "double[4]" 的操作,它不允许对所有 RK 方法使用相同的结构定义。
- 我想知道是否有比循环更好的方法。我不想手动设置整个数组(例如数组 = [0., 0., 0., 0., 1.3, ... 很多数字大多为零 ])。
- 据我所知,Cython 中没有 "real" 静态变量,是吗?
有没有更好的方法来做我想做的事?
干杯
实现你想要实现的一种方法是将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
更好的解决方案是定义一个 cython
class 以 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
来使用动态分配的数组修改您自己的代码
动态分配版本:
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
让我们做一个有点荒谬的基准测试,以验证前两个示例中的数组是真正静态的(即没有运行时成本)
In[1]: print(RungeKutta_DynamicArray())
3
In[2]: print(RungeKutta_StaticArray())
3
In[3]: obj = RungeKutta_StaticArrayClass()
In[4]: print(obj.GetnumStages())
3
In[5]: %timeit RungeKutta_DynamicArray()
10000000 loops, best of 3: 65.2 ns per loop
In[6]: %timeit RungeKutta_StaticArray()
10000000 loops, best of 3: 25.2 ns per loop
In[6]: %timeit RungeKutta_StaticArrayClass()
10000000 loops, best of 3: 49.6 ns per loop
RungeKutta_StaticArray
本质上具有接近 no-op
的成本,这意味着数组分配没有运行时惩罚。您可以选择在此函数内声明 coeffs
,时间仍然相同。 RungeKutta_StaticArrayClass
尽管使用其成员和构造函数设置 class 的开销仍然比动态分配的版本更快。
为什么不直接使用 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" 来表示 "compiled into the module" 而不是任何 numerous C-related definitions 或与 Python 静态 methods/class 变量有关的任何事情。
在数值方法中经常有很多系数是静态的,因为它们对于特定方法是固定的。我想知道在 Cython/C 中设置此类数组或变量的最佳方法是什么。
在我的案例中,龙格-库塔积分法除了系数和级数不同外,大部分都是相同的。现在我正在做类似(简化)
# Define some struct such that it can be used for all different Runge-Kutta methods
ctypedef struct RKHelper:
int numStages
double* coeffs
cdef:
RKHelper firstRKMethod
# Later secondRKMethod, thirdRKMethod, etc.
firstRKMethod.numStages = 3
firstRKMethod.coeffs = <double*> malloc(firstRKMethod.numStages*sizeof(double))
# Arrays can be large and most entries are zero
for ii in range(firstRKMethod.numStages):
firstRKMethod.coeffs[ii] = 0.
# Set non-zero elements
firstRKMethod.coeffs[2] = 1.3
几点:
- 我知道 malloc 不适用于静态数组,但我不知道如何在 Cython 中将 "numStages" 或 "RKHelper" 声明为静态,所以我不能使用静态数组... 或者我在 RKHelper 中做了类似 "double[4]" 的操作,它不允许对所有 RK 方法使用相同的结构定义。
- 我想知道是否有比循环更好的方法。我不想手动设置整个数组(例如数组 = [0., 0., 0., 0., 1.3, ... 很多数字大多为零 ])。
- 据我所知,Cython 中没有 "real" 静态变量,是吗?
有没有更好的方法来做我想做的事?
干杯
实现你想要实现的一种方法是将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
更好的解决方案是定义一个 cython
class 以 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
动态分配版本:
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
让我们做一个有点荒谬的基准测试,以验证前两个示例中的数组是真正静态的(即没有运行时成本)
In[1]: print(RungeKutta_DynamicArray())
3
In[2]: print(RungeKutta_StaticArray())
3
In[3]: obj = RungeKutta_StaticArrayClass()
In[4]: print(obj.GetnumStages())
3
In[5]: %timeit RungeKutta_DynamicArray()
10000000 loops, best of 3: 65.2 ns per loop
In[6]: %timeit RungeKutta_StaticArray()
10000000 loops, best of 3: 25.2 ns per loop
In[6]: %timeit RungeKutta_StaticArrayClass()
10000000 loops, best of 3: 49.6 ns per loop
RungeKutta_StaticArray
本质上具有接近 no-op
的成本,这意味着数组分配没有运行时惩罚。您可以选择在此函数内声明 coeffs
,时间仍然相同。 RungeKutta_StaticArrayClass
尽管使用其成员和构造函数设置 class 的开销仍然比动态分配的版本更快。
为什么不直接使用 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" 来表示 "compiled into the module" 而不是任何 numerous C-related definitions 或与 Python 静态 methods/class 变量有关的任何事情。