Использование scipy из C++ через pybind11
Я хотел бы иметь возможность использовать модули Python, такие как Numpy, Scipy и т. Д. Из C++. Следующий код пытается вызвать scipy.optimize.curve_fit, чтобы соответствовать параболической функции. Возникает проблема при вызове curve_fit. Здесь исключение брошено.
#include <iostream>
#include <pybind11/embed.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h> // mandatory for myPyObject.cast<std::vector<T>>()
#include <pybind11/functional.h> // mandatory for py::cast( std::function )
namespace py = pybind11;
int main()
{
try {
py::scoped_interpreter guard{};
py::module np = py::module::import("numpy");
py::object random = np.attr("random");
py::module scipy = py::module::import("scipy.optimize");
// create some data for fitting
std::vector<double> xValues(11, 0);
std::vector<double> yValues(11, 0);
for (int i = -5; i < 6; ++i) {
xValues[i + 5] = i;
yValues[i + 5] = i*i;
}
// cast it to numpy arrays
py::array_t<double> pyXValues = py::cast(xValues);
py::array_t<double> pyYValues = py::cast(yValues);
// add some noise to the yValues using numpy -> Works!
py::array_t<double> pyYValuesNoise = np.attr("add")(pyYValues, random.attr("randn")(11));
// create a function f_a(x) = a*x^2
std::function<std::vector<double>(std::vector<double>, double)> squared = [](std::vector<double> x, double a) {
std::vector<double> retvals(x);
std::transform(x.begin(), x.end(), retvals.begin(), [a](double val) { return a*val*val; });
return retvals;
};
// cast it to a python function
py::function pySquared = py::cast(squared);
// get scipy.optimize.curve_fit
py::function curve_fit = scipy.attr("curve_fit");
// call curve_fit -> throws exception
/* py::object = */ curve_fit(pySquared, pyXValues, pyYValues);
}
catch (std::exception error) {
std::cout << error.what() << std::endl;
}
system("pause");
return 0;
}
Исключение дает информация:
ValueError: подпись не найдена для встроенного <встроенного метода объекта PyCapsule в 0x00000204FFE9C630>
В:
D: \ Programs \ python36_6_x64 \ Lib \ inspect.py (2090): _signature_from_builtin D: \ Programs \ python36_6_x64 \ Lib \ inspect.py (2266): _signature_from_callable D: \ Programs \ python36_6_x64 \ Lib \ inspect.py (2802): from_callable D: \ Programs \ python36_6_x64 \ Lib \ inspect.py (3052): подпись D: \ Programs \ python36_6_x64 \ lib \ site-packages \ scipy_lib_util.py (290): getargspec_no_self
D: \ Programs \ python36_6_x64 \ lib \ site-packages \ scipy \ optimize \ minpack.py (685): curve_fit
Как правильно вызвать Curve_fit из C++?
1 ответ
Основываясь на комментарии Йенса Мунка, я создал модуль Python "MyPythonModule", который содержит файл "MyFunctionality.py", с функцией
def python_square_function(x, a):
return a*x**2
Я добавил путь к этому модулю в переменную окружения PYTHONPATH. Код C++ был изменен на:
#include <iostream>
#include <pybind11/embed.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h> // for myPyObject.cast<std::vector<T>>()
namespace py = pybind11;
int main()
{
py::scoped_interpreter guard{};
py::module np = py::module::import("numpy");
py::object random = np.attr("random");
py::module scipy = py::module::import("scipy.optimize");
// Load created module containing f_a(x) = a*x^2
py::module myModule = py::module::import("MyPythonModule.MyFunctionality");
// Create some data for fitting
std::vector<double> xValues(11, 0);
std::vector<double> yValues(11, 0);
for (int i = -5; i < 6; ++i) {
xValues[i + 5] = i;
yValues[i + 5] = i*i;
}
// Cast data to numpy arrays
py::array_t<double> pyXValues = py::cast(xValues);
py::array_t<double> pyYValues = py::cast(yValues);
// Add some noise to the yValues using numpy
py::array_t<double> pyYValuesNoise = np.attr("add")(pyYValues, random.attr("randn")(11));
// Get the function f_a(x) = a*x^2 we want to fit
py::function pySquareFunction = myModule.attr("python_square_function");
// Load scipy.optimize.curve_fit
py::function curve_fit = scipy.attr("curve_fit");
// Call curve_fit
py::object retVals = curve_fit(pySquareFunction, pyXValues, pyYValuesNoise);
// The return value contains the optimal values and the covariance matrix.
// Get the optimal values
py::object optVals = retVals.attr("__getitem__")(0);
// Cast return value back to std::vector and show the result
std::vector<double> retValsStd = optVals.cast<std::vector<double>>();
std::cout << "Fitted parameter a = " << retValsStd[0] << std::endl;
return 0;
}
Этот код приводит к ожидаемому поведению: подогнанный параметр a = 0.978144
,
К сожалению, это все еще обходной путь, который использует немного внешнего кода Python. Было бы здорово иметь возможность определять все внутри исходного кода C++.