Использование 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++.

Другие вопросы по тегам