将大型复杂数组从Python传递到C++;-什么';我最好的选择是什么?
2017/06/13编辑: 我试着按照建议使用boost,但在花了3天多的时间试图让它编译和链接之后,失败了,我决定愚蠢而痛苦的方式可能是最快、最不痛苦的。。。。所以现在我的代码只是保存了大量的文本文件(分裂数组和文件中复杂的/虚构部分),然后C++读取。优雅的不有效的。。。对将大型复杂数组从Python传递到C++;-什么';我最好的选择是什么?,python,c++,arrays,visual-c++,Python,C++,Arrays,Visual C++,2017/06/13编辑: 我试着按照建议使用boost,但在花了3天多的时间试图让它编译和链接之后,失败了,我决定愚蠢而痛苦的方式可能是最快、最不痛苦的。。。。所以现在我的代码只是保存了大量的文本文件(分裂数组和文件中复杂的/虚构部分),然后C++读取。优雅的不有效的。。。对 我有一些科学代码,目前是用Python编写的,由于循环中的数字3d集成步骤,这些代码的速度正在减慢。为了克服这个问题,我正在重新编写C++中的这个特殊步骤。(Cython等不是一个选项) 长篇短文:我希望尽可能方便地、
我有一些科学代码,目前是用Python编写的,由于循环中的数字3d集成步骤,这些代码的速度正在减慢。为了克服这个问题,我正在重新编写C++中的这个特殊步骤。(Cython等不是一个选项)
长篇短文:我希望尽可能方便地、无痛地将几个非常复杂的复数数组从Python代码转换到C++积分器。我可以用文本或二进制文件手动完成这项工作,但在开始之前,我想知道是否有更好的选择
我使用VisualStudio C++和Acon DA作为Python(不是我的选择)
有没有文件格式或方法,可以快速、方便地从Python中保存复数数组,然后在C++?< /P>中重新创建它? 非常感谢,,
Ben在编辑中添加注释。 正如评论中提到的,
python
本身作为一种解释语言,在计算效率方面几乎没有潜力。因此,为了使python脚本高效,必须使用并非全部解释的模块,而是在后台调用用C/C++编写的编译(和优化)代码。这正是numpy
为您所做的,尤其是对整个阵列的操作
因此,高效python脚本的第一步是使用numpy
。第二步是尝试使用自己编译(和优化)的代码。因此,我在下面的示例中假设您使用numpy
存储复数数组。其他一切都是不明智的
有多种方法可以从C/C++程序中访问python的原始数据。我个人已经完成了这项工作,但必须警告您,文档和支持充其量都是糟糕的:您几乎是靠自己(当然还有堆栈溢出)
例如,你的C++文件可能看起来像这个< /P>
// file.cc
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace p = boost::python;
namespace n = p::numpy;
n::ndarray func(const n::ndarray&input, double control_variable)
{
/*
your code here, see documentation for boost python
you pass almost any python variable, doesn't have to be numpy stuff
*/
}
BOOST_PYTHON_MODULE(module_name)
{
Py_Initialize();
n::initialize(); // only needed if you use numpy in the interface
p::def("function", func, "doc-string");
}
并将其作为
python setup.py build
运行,这将创建一个合适的。因此,可以从python导入build
的子目录中的文件。我多次使用的一个简单解决方案是将“C++端”构建为dll(=Linux/OS X上的共享对象),提供一个简单、类似C的入口点(直接整数,指针&有限公司,无STL内容)并通过ctypes
传递数据
这避免了boost/SIP/Swig/…构建噩梦,可以保持零拷贝(使用ctypes,您可以直接向numpy数据传递指针),并允许您做任何您想做的事情(特别是在构建端-没有该死的distutils,没有boost,什么都没有-使用任何可以构建类似C的dll的东西构建它)在C++方面,它也具有让C++语言从其他语言调用的良好的副作用(实际上任何语言都有某种方式与C库接口)。
是一个快速的人工例子。C++的边是:
extern "C" {
double sum_it(double *array, int size) {
double ret = 0.;
for(int i=0; i<size; ++i) {
ret += array[i];
}
return ret;
}
}
这确保了数据被正确封送;然后调用函数就像调用
import summer
import numpy as np
# from a list (with copy)
print summer.sum_it([1, 2, 3, 4.5])
# from a numpy array of the right type - zero-copy
print summer.sum_it(np.array([3., 4., 5.]))
有关如何使用它的详细信息,请参阅。另请参阅
对于复数,情况稍微复杂一些,因为在cType中没有它的构建;如果我们想在C++侧使用<代码> STD::复杂< /COD>(它是用NUPY复杂布局工作的,即一个两个双倍的序列),我们可以将C++的边写成:
extern "C" {
std::complex<double> sum_it_cplx(std::complex<double> *array, int size) {
std::complex<double> ret(0., 0.);
for(int i=0; i<size; ++i) {
ret += array[i];
}
return ret;
}
}
继承自ctypes.Structure
启用ctypes编组魔术,这是根据\u fields\u
成员执行的;构造函数和额外方法只是为了在Python端易于使用
然后,我们必须告诉ctypes返回类型
_sum_it_cplx = cdll.sum_it_cplx
_sum_it_cplx.restype = c_complex
最后,以与前一个类似的方式编写我们的包装:
def sum_it_cplx(l):
if isinstance(l, np.ndarray) and l.dtype == np.complex and len(l.shape)==1:
# the numpy array layout for complexes (sequence of two double) is already
# compatible with std::complex (see https://stackoverflow.com/a/5020268/214671)
a = l.ctypes.data
else:
# otherwise, try to build our c_complex
arr_t = c_complex * len(l)
a = arr_t(*(c_complex(r) for r in l))
ret = _sum_it_cplx(a, len(l))
return ret.to_complex()
如上所述进行测试
# from a complex list (with copy)
print summer.sum_it_cplx([1. + 0.j, 0 + 1.j, 2 + 2.j])
# from a numpy array of the right type - zero-copy
print summer.sum_it_cplx(np.array([1. + 0.j, 0 + 1.j, 2 + 2.j]))
产生预期结果:
(3+3j)
(3+3j)
我看到OP现在已经有一年多的历史了,但我最近使用原生Python-C/C++API及其Numpy-C/C++扩展解决了一个类似的问题,并且由于各种原因(例如,复杂的数字解决方法、凌乱的代码)和Boost,我个人不喜欢使用CType,所以我想将我的答案发布给未来的搜索者
Python-capi和Numpy-capi的文档都非常广泛(虽然一开始有点过于庞大),但是在一两次成功之后,编写本机C/C++扩展变得非常容易
这是一个可以从Python调用的C++函数示例,它集成了一个真实或复杂的3D NUMPY数组(<代码> NoMpy.Bux/Cube >或Nupy.Cudio >”类型。该函数将通过DLL( So/<代码>)通过模块<代码> CySimult.S/<代码> ./P>导入。
#include "Python.h"
#include "numpy/arrayobject.h"
#include <math.h>
static PyObject * integrate3(PyObject * module, PyObject * args)
{
PyObject * argy=NULL; // Regular Python/C API
PyArrayObject * yarr=NULL; // Extended Numpy/C API
double dx,dy,dz;
// "O" format -> read argument as a PyObject type into argy (Python/C API)
if (!PyArg_ParseTuple(args, "Oddd", &argy,&dx,&dy,&dz)
{
PyErr_SetString(PyExc_ValueError, "Error parsing arguments.");
return NULL;
}
// Determine if it's a complex number array (Numpy/C API)
int DTYPE = PyArray_ObjectType(argy, NPY_FLOAT);
int iscomplex = PyTypeNum_ISCOMPLEX(DTYPE);
// parse python object into numpy array (Numpy/C API)
yarr = (PyArrayObject *)PyArray_FROM_OTF(argy, DTYPE, NPY_ARRAY_IN_ARRAY);
if (yarr==NULL) {
Py_INCREF(Py_None);
return Py_None;
}
//just assume this for 3 dimensional array...you can generalize to N dims
if (PyArray_NDIM(yarr) != 3) {
Py_CLEAR(yarr);
PyErr_SetString(PyExc_ValueError, "Expected 3 dimensional integrand");
return NULL;
}
npy_intp * dims = PyArray_DIMS(yarr);
npy_intp i,j,k,m;
double * p;
//initialize variable to hold result
Py_complex result = {.real = 0, .imag = 0};
if (iscomplex) {
for (i=0;i<dims[0];i++)
for (j=0;j<dims[1];j++)
for (k=0;k<dims[1];k++) {
p = (double*)PyArray_GETPTR3(yarr, i,j,k);
result.real += *p;
result.imag += *(p+1);
}
} else {
for (i=0;i<dims[0];i++)
for (j=0;j<dims[1];j++)
for (k=0;k<dims[1];k++) {
p = (double*)PyArray_GETPTR3(yarr, i,j,k);
result.real += *p;
}
}
//multiply by step size
result.real *= (dx*dy*dz);
result.imag *= (dx*dy*dz);
Py_CLEAR(yarr);
//copy result into returnable type with new reference
if (iscomplex) {
return Py_BuildValue("D", &result);
} else {
return Py_BuildValue("d", result.real);
}
};
然后通过setup.py
进行编译,就像Walter的boost示例一样,只做了几处明显的更改-将那里的file.cc
替换为我们的文件cintegrate.cxx
,删除boost依赖项,并确保包含指向“numpy/arrayobject.h”
的路径
在python中,您可以像这样使用它:
import cintegrate
import numpy as np
arr = np.random.randn(4,8,16) + 1j*np.random.randn(4,8,16)
# arbitrary step size dx = 1., y=0.5, dz = 0.25
ans = cintegrate.integrate3(arr, 1.0, 0.5, .25)
此特定代码尚未被测试,但大部分是从工作代码中复制的。
为什么不尝试使算法更高效?如果Python效率低下,它仍然将在C++中效率低下(理论上)在Python中用NyPy做集成吗?它在处理数组方面很快,因为它使用编译的代码而不是Python解释器。我已经尝试了Numpy,并得出结论,对我来说,在C++中写下这一步。
(3+3j)
(3+3j)
#include "Python.h"
#include "numpy/arrayobject.h"
#include <math.h>
static PyObject * integrate3(PyObject * module, PyObject * args)
{
PyObject * argy=NULL; // Regular Python/C API
PyArrayObject * yarr=NULL; // Extended Numpy/C API
double dx,dy,dz;
// "O" format -> read argument as a PyObject type into argy (Python/C API)
if (!PyArg_ParseTuple(args, "Oddd", &argy,&dx,&dy,&dz)
{
PyErr_SetString(PyExc_ValueError, "Error parsing arguments.");
return NULL;
}
// Determine if it's a complex number array (Numpy/C API)
int DTYPE = PyArray_ObjectType(argy, NPY_FLOAT);
int iscomplex = PyTypeNum_ISCOMPLEX(DTYPE);
// parse python object into numpy array (Numpy/C API)
yarr = (PyArrayObject *)PyArray_FROM_OTF(argy, DTYPE, NPY_ARRAY_IN_ARRAY);
if (yarr==NULL) {
Py_INCREF(Py_None);
return Py_None;
}
//just assume this for 3 dimensional array...you can generalize to N dims
if (PyArray_NDIM(yarr) != 3) {
Py_CLEAR(yarr);
PyErr_SetString(PyExc_ValueError, "Expected 3 dimensional integrand");
return NULL;
}
npy_intp * dims = PyArray_DIMS(yarr);
npy_intp i,j,k,m;
double * p;
//initialize variable to hold result
Py_complex result = {.real = 0, .imag = 0};
if (iscomplex) {
for (i=0;i<dims[0];i++)
for (j=0;j<dims[1];j++)
for (k=0;k<dims[1];k++) {
p = (double*)PyArray_GETPTR3(yarr, i,j,k);
result.real += *p;
result.imag += *(p+1);
}
} else {
for (i=0;i<dims[0];i++)
for (j=0;j<dims[1];j++)
for (k=0;k<dims[1];k++) {
p = (double*)PyArray_GETPTR3(yarr, i,j,k);
result.real += *p;
}
}
//multiply by step size
result.real *= (dx*dy*dz);
result.imag *= (dx*dy*dz);
Py_CLEAR(yarr);
//copy result into returnable type with new reference
if (iscomplex) {
return Py_BuildValue("D", &result);
} else {
return Py_BuildValue("d", result.real);
}
};
static PyMethodDef cintegrate_Methods[] = {
{"integrate3", integrate3, METH_VARARGS,
"Pass 3D numpy array (double or complex) and dx,dy,dz step size. Returns Reimman integral"},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"cintegrate", /* name of module */
NULL, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
cintegrate_Methods
};
import cintegrate
import numpy as np
arr = np.random.randn(4,8,16) + 1j*np.random.randn(4,8,16)
# arbitrary step size dx = 1., y=0.5, dz = 0.25
ans = cintegrate.integrate3(arr, 1.0, 0.5, .25)