定义更复杂的静态数组

时间:2016-02-05 10:37:12

标签: python c cython

通常在数值方法中,一个具有很多系数是静态的,因为它们是针对特定方法固定的。我想知道在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不适用于静态数组,但我不知道如何声明&#34; numStages&#34;或者&#34; RKHelper&#34;在Cython中是静态的,所以我不能使用静态数组...或者我做类似&#34; double [4]&#34;在RKHelper中,它不允许对所有RK方法使用相同的结构定义。
  • 我想知道是否有比循环更好的方法。我不想手动设置整个数组(例如array = [0.,0.,0.,0.,1.3,... 许多数字大部分为零])。< / LI>
  • 据我所知,没有&#34;真实&#34; Cython中的静态变量,有吗?

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

干杯

2 个答案:

答案 0 :(得分:1)

实现您想要实现的目标的一种方法是将 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

更好的解决方案是使用 Runge Kutta 系数作为其成员来定义cython

优雅的解决方案

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尽管使用其成员和构造函数设置类的开销仍然比动态分配的版本更快。

答案 1 :(得分:0)

为什么不使用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]

注意我对这个问题的解读是你正在使用&#34;静态&#34;意思是&#34;编译成模块&#34;而不是任何numerous C-related definitions或任何与Python静态方法/类变量有关的事情。