包装一个函数,在 Cython 中返回复杂类型的向量

Wrapping a function returning a Vector of Complex types in Cython

本文关键字:返回 Cython 复杂 类型 向量 函数 一个 包装      更新时间:2023-10-16

我目前正在尝试包装我创建的C++类。其中一个函数返回包含复杂浮点数的向量向量:

std::vector<std::vector<std::complex<float>>>

我的 DataBridge.pxd 文件如下所示:

# distutils: language = c++
from libcpp cimport bool
from libcpp cimport float
from libcpp.vector cimport vector
from libcpp.complex cimport complex
from libc.float cimport float
cimport numpy as np
import numpy as np
cdef extern from "projectxcpp/bridge.cpp":
pass
# Declare the class with cdef
cdef extern from "projectxcpp/bridge.hpp":
cdef cppclass DataBridge:
DataBridge() except +
void start()
void stop()
bool isDataReady()
vector[vector[complex[float]]] getData()

我的bridge.hpp文件如下所示:

#ifndef BRIDGE_HPP
#define BRIDGE_HPP
#include <vector>
#include <complex>
...
class DataBridge {
public:
int start(void);
int stop(void);
bool isDataReady(void);
std::vector<std::vector<std::complex<float>>> getData(void);
private:
...
};
#endif

然后,尝试运行 setup.py,给出以下消息:

[1/1] Cythonizing snowconecpp.pyx
Error compiling Cython file:
------------------------------------------------------------
...
DataBridge() except +
void start()
void stop()
bool isDataReady()
vector[vector[complex[float]]] getData()
^
------------------------------------------------------------
DataBridge.pxd:24:29: Array size must be a compile time constant
Error compiling Cython file:
------------------------------------------------------------
...
DataBridge() except +
void start()
void stop()
bool isDataReady()
vector[vector[complex[float]]] getData()
^
------------------------------------------------------------
DataBridge.pxd:24:29: unknown type in template argument
Traceback (most recent call last):
File "setup.py", line 15, in <module>
setup(ext_modules=cythonize("projectxcpp.pyx", language_level="3"))
File ".../lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1096, in cythonize
cythonize_one(*args)
File ".../lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1219, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: projectxcpp.pyx

我的 setup.py 文件仅包含:

from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize("projectxcpp.pyx", language_level="3"))

当我的包装器中将类型声明为complex[float] evector[vector[float]] e时,这些类型将解析并移至下一步编译。

只是想知道在 Cython 的向量中放置复杂类型是否有特殊要求 - 似乎无法为涉及C++包装的这种情况或类似情况找到答案。

我的最终目标是能够将这些数据读出到 numpy 数组中。如果有更好的方法,也请让我意识到这一点 - 谢谢!

我认为问题是complex已经是 Python/Cython 中的一种类型(并且由于与 C 重叠或C++复杂,在某些地方处理得也有些糟糕......如果您重命名它,或者按其完整路径寻址,它可以工作

from libcpp.complex cimport complex as cpp_complex
# later
vector[vector[cpp_complex[float]]]

cimport libcpp.complex
# later
vector[vector[libcpp.complex.complex[float]]]

对于问题的更一般部分:我倾向于不为这样的 Numpy 数组提供数据。部分原因是我真的不喜欢将 2D 数组定义为指针到指针或矢量的矢量。您最好使用与形状信息一起存储的 1D 数组/矢量,以便对其进行索引。

与 Numpy 接口有几个选项:

  1. 如果您事先知道大小,则使用 Numpy 分配内存并将该内存传递到C++(您可以获得指向 Numpy 数组的第一个元素的指针(。这样,C++代码会修改 Numpy 的内存,您不必复制数据。

  2. 如果要使用 C++ 分配内存,则可以使用实现缓冲区协议的 Cython 类包装它。内存由C++管理,但 Numpy 无需复制即可直接访问内存。(您可能需要实现移动构造函数以避免复制,但这相当简单(。

  3. 您可以使用 Numpy
  4. C API 函数PyArray_SimpleNewFromData让 Numpy 包装内存。这需要小心以确保在正确的时间释放它。

  5. 您可以执行当前正在执行的操作并复制数据。这实现起来非常简单,但每次要将数据从C++移动到 Python 时都会添加一个复制操作。