在Cython

时间:2017-04-28 20:42:24

标签: python c++ cython

我编写了一个小型C ++库来模拟一个ODE系统,并使用Cython包装一个初始化和解决系统的函数。 C ++函数接受一些参数,并返回一个包含两个矩阵的结构:一组用于构造系统的模式,以及系统中已解决的状态每一步。

这些矩阵是使用Eigen库实现的,所以我使用Eigency接口将这些数据结构的底层内容传输到NumPy数组。

如果在Python中我将视图返回到其中一个矩阵,那么一切都很好。但是,我想返回一个包含这两个矩阵的元组,这需要临时存储这个结构,然后返回每个矩阵成员:

## kernel.pyx

from eigency.core cimport *

cdef extern from "kernel_cpp.h":

  cdef cppclass network:
    MatrixXf state
    MatrixXf patterns

  cdef network _run "run" (int N, int K, int P, double g, double T, double tau, double dt)

def run(N=10000, K=100, P=30, g=1.5, T=0.5, tau=1e-3, dt=1e-3):
  return ndarray_view(_run(N,K,P,g,T,tau,dt).state) # <--- This works
  # This does not:
  # cdef network net = _run(N,K,P,g,T,tau,dt):
  # return (ndarray_view(net.state), ndarray_view(net.patterns))

使用python setup.py build_ext --inplace构建注释代码时出现的错误是

kernel.cpp:1552:11: error: no matching constructor for initialization of 'network'
  network __pyx_v_net;
          ^`

这对我来说很有意义--Cython正在尝试为网络调用构造函数。但我希望施工能够在&#34;跑步中进行。&#34;这里的正确类型声明是什么?

如果有帮助,代码的精简版本为here

谢谢!

1 个答案:

答案 0 :(得分:3)

值得了解Cython如何转换代码堆栈分配C ++变量的代码:

def run(N=10000, K=100, P=30, g=1.5, T=0.5, tau=1e-3, dt=1e-3):
   cdef network net = _run(N,K,P,g,T,tau,dt)

函数内部转换为:

{
    // start of function
    network net; // actually some mangled variable name (see your error message)

    // body of function
    net = _run(N,K,P,g,T,tau,dt);
}

即。它需要默认构造函数和复制/移动赋值运算符。这与C ++代码的编写方式不符,因此偶尔会导致问题。 Cython通常假设存在这些(除非你告诉它有关其他构造函数),所以你不需要在cdef cppclass块中明确地写出这些。

我认为在编译C ++代码时看起来会出现错误消息,并且它没有定义默认构造函数。 (之后的复制赋值应该不是问题,因为C ++会自动生成它)。您有两种选择:

  1. 如果您对修改C ++代码感到满意,请定义默认构造函数。这将允许Cython代码按写入方式工作。如果您使用的是&gt; = C ++ 11,那么就可以这么简单:

    network() = default;
    

    这可能不会起作用,这取决于内部Eigen类型是否可以默认构建。

  2. 如果您不想修改C ++代码,或者事实证明您无法轻松定义默认构造函数,那么您必须修改Cython代码以分配networknew。这也意味着你必须自己处理释放:

      # earlier
       cdef cppclass network:
         # need to tell Cython about copy constructor
         network(const network&)
         # everything else as before 
    
    def run(N=10000, K=100, P=30, g=1.5, T=0.5, tau=1e-3, dt=1e-3):
        cdef network* net
        try:
            net = new network(_run(N,K,P,g,T,tau,dt))
            return (ndarray_view(net.state), ndarray_view(net.patterns))
        finally:
            del net
    

    在这种情况下,不需要默认构造函数,只需要复制/移动构造函数(无论如何都必须能够从函数返回network)。注意使用finally来确保我们释放内存。