Embed python / numpy in C++

 ̄綄美尐妖づ 提交于 2021-01-24 10:55:52

问题


I am trying to use python 3 (with numpy) in my C++ application. This entails sending a C++ array to python, performing calculations and then retrieving the result in C++. To do this I based myself on the code that was discussed here: https://codereview.stackexchange.com/questions/92266/sending-a-c-array-to-python-numpy-and-back/92353#92353 and also here: Sending a C++ array to Python and back (Extending C++ with Numpy).

While the example from the code review post basically works I am having troubles with the return values when I modified the python and C++ script: when I am trying to return a variable that was created in python the result is a vector of nan instead of the intended computations. My guess is that the object somehow goes out of scope but I can't fix this problem.

I use the following python script in a file called mymodule.py:

import numpy

def array_tutorial(a):
    print("array_tutorial - python")
    print(a)
    print("")
    firstRow = a[0,:]
    #beta = numpy.array([[10,20,30],[10,20,30],[10,20,30]])
    #firstRow = beta[0,:]
    return firstRow

def myfunction():
    beta = numpy.array([[1,2,3],[1,2,3],[1,2,3]])
    print("myfunction - python")
    print(beta)
    print("")
    firstRow = beta[0,:]
    return firstRow

My C++ code is in the file numpy_cpp.cpp which is a slightly changed and simplified version of the accepted answer to the code review post.

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <stdio.h>
#include <iostream>
#include <stdlib.h> 

#include <Python.h>
#include "numpy/arrayobject.h"

int main(int argc, char* argv[])
{
    setenv("PYTHONPATH", ".", 0);

    Py_Initialize();
    import_array();

    // Build the 2D array in C++
    const int SIZE = 3;
    npy_intp dims[2]{SIZE, SIZE};
    const int ND = 2;
    long double(*c_arr)[SIZE]{ new long double[SIZE][SIZE] };

    for (int i = 0; i < SIZE; i++){
        for (int j = 0; j < SIZE; j++){
            c_arr[i][j] = i + j;}
    }

    // Convert it to a NumPy array.
    PyObject *pArray = PyArray_SimpleNewFromData(ND, dims, NPY_LONGDOUBLE, reinterpret_cast<void*>(c_arr));

    // import mymodule
    const char *module_name = "mymodule";
    PyObject *pName = PyUnicode_FromString(module_name);
    PyObject *pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    // import function
    const char *func_name = "array_tutorial";
    PyObject *pFunc = PyObject_GetAttrString(pModule, func_name);
    PyObject *pReturn = PyObject_CallFunctionObjArgs(pFunc, pArray, NULL);
    PyArrayObject *np_ret = reinterpret_cast<PyArrayObject*>(pReturn);

    // Convert back to C++ array and print.
    int len = PyArray_SHAPE(np_ret)[0];
    long double* c_out;
    c_out = reinterpret_cast<long double*>(PyArray_DATA(np_ret));
    std::cout << "Printing output array - C++" << std::endl;
    for (int i = 0; i < len; i++){
        std::cout << c_out[i] << ' ';
    }
    std::cout << std::endl << std::endl;


    // import function without arguments
    const char *func_name2 = "myfunction";
    PyObject *pFunc2 = PyObject_GetAttrString(pModule, func_name2);
    PyObject *pReturn2 = PyObject_CallFunctionObjArgs(pFunc2, NULL);
    PyArrayObject *np_ret2 = reinterpret_cast<PyArrayObject*>(pReturn2);

    // convert back to C++ array and print
    int len2 = PyArray_SHAPE(np_ret2)[0];
    long double* c_out2;
    c_out2 = reinterpret_cast<long double*>(PyArray_DATA(np_ret2));
    std::cout << "Printing output array 2 - C++" << std::endl;
    for (int i = 0; i < len2; i++){
        std::cout << c_out2[i] << ' ';
    }
    std::cout << std::endl << std::endl;

    Py_Finalize();
    return 0;
}

Compared to the accepted answer I had to add

setenv("PYTHONPATH", ".", 0);

to make sure the python script is found, I added the second function call for the function "myfunction" that has no input arguments and I removed some of the error handling.

I am on Ubuntu 16.10 and and use

g++ -Wall numpy_cpp.cpp -I/usr/include/python3.5m/ -lpython3.5m  

to compile and link (which goes fine except for one warning by import_array()). I am targeting python 3.

Running the program however gives the following console output:

array_tutorial - python
[[ 0.0  1.0  2.0]
 [ 1.0  2.0  3.0]
 [ 2.0  3.0  4.0]]

Printing output array - C++
0 1 2 

myfunction - python
[[1 2 3]
 [1 2 3]
 [1 2 3]]

Printing output array 2 - C++
nan nan nan 

It is the last output that gives me trouble, where python returns the first row of an numpy array that was set up in the python script. From the python print statements it seems that the numpy array is fine (not nan), but once it is referred to C++ things go sideways.

If I uncomment the two lines above the return statement in the array_tutorial function I get the same (disappointing) result for the first function call.

My question is therefore how to get the correct values in C++ without the objects going (presumably) out of scope?

I apologize for the lengthy post, and thank you in advance for any help!


EDIT: As pointed out by lomereiter below the numpy arrays in python should be set up keeping the data types in mind. This solves the problem. A better python script that outputs the data type of the received array and specifies the data type of the declared array would be:

import numpy

def array_tutorial(a):
    print("array_tutorial - python")
    print(a)
    print(numpy.dtype(a[0,0]))
    print("")
    firstRow = a[0,:]
    #beta = numpy.array([[10,20,30],[10,20,30],[10,20,30]],dtype=numpy.float128)
    #firstRow = beta[0,:]
    return firstRow

def myfunction():
    beta = numpy.array([[1,2,3],[1,2,3],[1,2,3]],dtype=numpy.float128)
    print("myfunction - python")
    print(beta)
    print("")
    firstRow = beta[0,:]
    return firstRow

回答1:


You should specify dtype when you create the array in Python code. You are casting to long double in C++ code while the dtype is deduced to be int64 (on 64-bit platforms)



来源:https://stackoverflow.com/questions/46493307/embed-python-numpy-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!