定义更复杂的静态数组

2024-06-16 11:51:38 发布

您现在位置:Python中文网/ 问答频道 /正文

数值方法中的系数往往是固定的。我想知道Cython/C中设置这样的数组或变量的最佳方法是什么。在

在我的例子中,除了系数和级数外,Runge-Kutta积分方法基本相同。现在我正在做类似(简化)

# 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方法使用相同的结构定义。在
  • 我想知道有没有比循环更好的方法。我不想手动设置整个数组(例如array=[0,0,0,0,1.3。。。很多数字大多为零。在
  • 据我所知,Cython中没有“真正的”静态变量,是吗?在

有没有更好的方式来做我想做的事?在

干杯


Tags: 方法静态数组becanstructcythondouble
2条回答

为什么不直接使用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静态方法/类变量有关的内容。在

一种实现您想要实现的方法是将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类,其成员是Runge-Kutta系数

优雅的解决方案

^{pr2}$

关于设置元素的问题,让我们使用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仍然比动态分配的版本快。在

相关问题 更多 >