Defining more complex static arrays

Quite often in numerical methods there are many coefficients that are static, since they are fixed for a particular method. I was wondering what is the best way in Cython / C to set such arrays or variables.

In my case, Runge-Kutta. The integration methods are basically the same, with the exception of the coefficients and the number of stages. Now I am doing something like (simplified)

# 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

Some moments:

  • I know that malloc is not for static arrays, but I do not know how to declare "numStages" or "RKHelper" as static in Cython, so I can not use a static array ... Or am I something like "double [4] "in RKHelper, which does not allow the use of the same structure definition for all RK methods.
  • , , . (, array = [0., 0., 0., 0., 1.3,... ]).
  • , Cython "" ?

, ?

+4
2

, , - - , . ,

:

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

:

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

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]

. , "", " ", , C. - / Python.

0

Source: https://habr.com/ru/post/1627420/


All Articles