Отправка массива C ++ в Python и обратно (расширение C ++ с помощью Numpy)

Я собираюсь отправить c++ массив функции Python как numpy array и верни другого numpy array, После консультации с numpy Документация и некоторые другие потоки и настройка кода, наконец, код работает, но я хотел бы знать, написан ли этот код оптимально с учетом:

  • Ненужное копирование массива между c++ а также numpy (python),
  • Правильное разыменование переменных.
  • Легкий прямой подход.

Код C ++:

// python_embed.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "Python.h"#include "numpy/arrayobject.h"#include<iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
Py_SetProgramName(argv[0]);
Py_Initialize();
import_array()

// Build the 2D array
PyObject *pArgs, *pReturn, *pModule, *pFunc;
PyArrayObject *np_ret, *np_arg;
const int SIZE{ 10 };
npy_intp dims[2]{SIZE, SIZE};
const int ND{ 2 };
long double(*c_arr)[SIZE]{ new long double[SIZE][SIZE] };
long double* c_out;
for (int i{}; i < SIZE; i++)
for (int j{}; j < SIZE; j++)
c_arr[i][j] = i * SIZE + j;

np_arg = reinterpret_cast<PyArrayObject*>(PyArray_SimpleNewFromData(ND, dims, NPY_LONGDOUBLE,
reinterpret_cast<void*>(c_arr)));

// Calling array_tutorial from mymodule
PyObject *pName = PyUnicode_FromString("mymodule");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (!pModule){
cout << "mymodule can not be imported" << endl;
Py_DECREF(np_arg);
delete[] c_arr;
return 1;
}
pFunc = PyObject_GetAttrString(pModule, "array_tutorial");
if (!pFunc || !PyCallable_Check(pFunc)){
Py_DECREF(pModule);
Py_XDECREF(pFunc);
Py_DECREF(np_arg);
delete[] c_arr;
cout << "array_tutorial is null or not callable" << endl;
return 1;
}
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, reinterpret_cast<PyObject*>(np_arg));
pReturn = PyObject_CallObject(pFunc, pArgs);
np_ret = reinterpret_cast<PyArrayObject*>(pReturn);
if (PyArray_NDIM(np_ret) != ND - 1){ // row[0] is returned
cout << "Function returned with wrong dimension" << endl;
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(np_arg);
Py_DECREF(np_ret);
delete[] c_arr;
return 1;
}
int len{ PyArray_SHAPE(np_ret)[0] };
c_out = reinterpret_cast<long double*>(PyArray_DATA(np_ret));
cout << "Printing output array" << endl;
for (int i{}; i < len; i++)
cout << c_out[i] << ' ';
cout << endl;

// Finalizing
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(np_arg);
Py_DECREF(np_ret);
delete[] c_arr;
Py_Finalize();
return 0;
}

В CodeReview есть фантастический ответ: Ссылка на сайт…

27

Решение

Проверять xtensor и xtensor-питон привязки Python.

xtensor — это библиотека C ++, предназначенная для численного анализа с выражениями многомерных массивов.

Xtensor обеспечивает

  • расширяемая система выражений, обеспечивающая вещание в стиле numpy (см. шпаргалку numpy to xtensor).
  • API, соответствующий идиомам стандартной библиотеки C ++.
  • инструменты для манипулирования выражениями массива и построения на основе xtensor.
  • привязки для Python, но также R и Джулия.

Пример использования

Инициализируйте двумерный массив и вычислите сумму одной из его строк и одномерного массива.

#include <iostream>
#include "xtensor/xarray.hpp"#include "xtensor/xio.hpp"
xt::xarray<double> arr1
{{1.0, 2.0, 3.0},
{2.0, 5.0, 7.0},
{2.0, 5.0, 7.0}};

xt::xarray<double> arr2
{5.0, 6.0, 7.0};

xt::xarray<double> res = xt::view(arr1, 1) + arr2;

std::cout << res;

Выходы

{7, 11, 14}

Создание универсальной функции в стиле Numpy в C ++.

#include "pybind11/pybind11.h"#include "xtensor-python/pyvectorize.hpp"#include <numeric>
#include <cmath>

namespace py = pybind11;

double scalar_func(double i, double j)
{
return std::sin(i) - std::cos(j);
}

PYBIND11_PLUGIN(xtensor_python_test)
{
py::module m("xtensor_python_test", "Test module for xtensor python bindings");

m.def("vectorized_func", xt::pyvectorize(scalar_func), "");

return m.ptr();
}

Код Python:

import numpy as np
import xtensor_python_test as xt

x = np.arange(15).reshape(3, 5)
y = [1, 2, 3, 4, 5]
z = xt.vectorized_func(x, y)
z

Выходы

[[-0.540302,  1.257618,  1.89929 ,  0.794764, -1.040465],
[-1.499227,  0.136731,  1.646979,  1.643002,  0.128456],
[-1.084323, -0.583843,  0.45342 ,  1.073811,  0.706945]]
4

Другие решения

По моему опыту, это кажется довольно эффективным.
Чтобы получить еще большую эффективность, попробуйте это:
http://ubuntuforums.org/showthread.php?t=1266059

Используя weave, вы можете встроить код C / C ++ в Python, чтобы это было полезно.

http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.weave.inline.html

Вот ссылка на то, как Python может использоваться для взаимодействия между различными языками, а также примеры.

http://docs.scipy.org/doc/numpy/user/c-info.python-as-glue.html

Это быстрый и простой пример того, как передавать массивы с пустым массивом в c ++ с помощью Cython:

http://www.birving.com/blog/2014/05/13/passing-numpy-arrays-between-python-and/

3

В качестве дополнительного способа, не касаясь непосредственно Python C API, можно использовать pybind11 (библиотека только для заголовков):

CPP:

#include <pybind11/embed.h> // everything needed for embedding
#include <iostream>
#include <Eigen/Dense>
#include<pybind11/eigen.h>
using Eigen::MatrixXd;
namespace py = pybind11;

int main()
{
try
{
Py_SetProgramName("PYTHON");
py::scoped_interpreter guard{};

py::module py_test = py::module::import("py_test");

MatrixXd m(2,2);
m(0,0) = 1;
m(1,0) = 2;
m(0,1) = 3;
m(1,1) = 4;

py::object result = py_test.attr("test_mat")(m);

MatrixXd res = result.cast<MatrixXd>();
std::cout << "In c++ \n" << res << std::endl;
}
catch (std::exception ex)
{
std::cout << "ERROR   : " << ex.what() << std::endl;
}
return 1;
}

В py_test.py :

def test_mat(m):
print ("Inside python m = \n ",m )
m[0,0] = 10
m[1,1] = 99
return m

Выход :

Inside python m =
[[ 1.  3.]
[ 2.  4.]]
In c++
10  3
2 99

Увидеть официальная документация.

PS: я использую собственный для матрицы C ++.

3