Passing big complex arrays from Python to C++ - what's my best option?

前端 未结 3 1495
说谎
说谎 2021-01-03 05:02

2017/06/13 EDIT: I tried using boost as was suggested, but after spending more than 3 days trying to get it to compile and link, and failing, I decided that the stupid painf

3条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-03 05:52

    I see the OP is over a year old now, but I recently addressed a similar problem using the native Python-C/C++ API and its Numpy-C/C++ extension, and since I personally don't enjoy using ctypes for various reasons (e.g., complex number workarounds, messy code), nor Boost, wanted to post my answer for future searchers.

    Documentation for the Python-C API and Numpy-C API are both quite extensive (albeit a little overwhelming at first). But after one or two successes, writing native C/C++ extensions becomes very easy.

    Here is an example C++ function that can be called from Python. It integrates a 3D numpy array of either real or complex (numpy.double or numpy.cdouble) type. The function will be imported through a DLL (.so) via the module cintegrate.so.

    #include "Python.h"
    #include "numpy/arrayobject.h"
    #include 
    
    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, "Offffd", &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

    Simply put that into a source file (we'll call it cintegrate.cxx along with the module definition stuff, inserted at the bottom:

    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
    };
    

    Then compile that via setup.py much like Walter's boost example with just a couple obvious changes- replacing file.cc there with our file cintegrate.cxx, removing boost dependencies, and making sure the path to "numpy/arrayobject.h" is included.

    In python then you can use it like:

    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)
    

    This specific code hasn't been tested but is mostly copied from working code.

提交回复
热议问题