将大型复杂数组从Python传递到C++;-什么';我最好的选择是什么?

将大型复杂数组从Python传递到C++;-什么';我最好的选择是什么?,python,c++,arrays,visual-c++,Python,C++,Arrays,Visual C++,2017/06/13编辑: 我试着按照建议使用boost,但在花了3天多的时间试图让它编译和链接之后,失败了,我决定愚蠢而痛苦的方式可能是最快、最不痛苦的。。。。所以现在我的代码只是保存了大量的文本文件(分裂数组和文件中复杂的/虚构部分),然后C++读取。优雅的不有效的。。。对 我有一些科学代码,目前是用Python编写的,由于循环中的数字3d集成步骤,这些代码的速度正在减慢。为了克服这个问题,我正在重新编写C++中的这个特殊步骤。(Cython等不是一个选项) 长篇短文:我希望尽可能方便地、

2017/06/13编辑: 我试着按照建议使用boost,但在花了3天多的时间试图让它编译和链接之后,失败了,我决定愚蠢而痛苦的方式可能是最快、最不痛苦的。。。。所以现在我的代码只是保存了大量的文本文件(分裂数组和文件中复杂的/虚构部分),然后C++读取。优雅的不有效的。。。对


我有一些科学代码,目前是用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)