通过pybind11使用C ++中的scipy

问题描述 投票:1回答:1

我想有可能使用C ++中的numpy,scipy等python模块。以下代码尝试调用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):_ signign_from_builtin D:\ Programs \ python36_6_x64 \ Lib \ inspect.py(2266):_ signign_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

如何正确地从C ++调用curve_fit?

c++ visual-studio-2015 scipy pybind11
1个回答
1
投票

基于Jens Munk的comment,我创建了一个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 ++源代码中定义所有内容会很棒。

© www.soinside.com 2019 - 2024. All rights reserved.