2

我希望有可能使用C++ 中的numpyscipy等 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: no signature found for builtin < 0x00000204FFE9C630 处 PyCapsule 对象的内置方法>

在:
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\站点包\scipy\optimize\minpack.py(685):curve_fit

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

4

1 回答 1

3

根据 Jens Munk 的评论,我创建了一个 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++ 源代码中定义所有内容,那就太好了。

于 2018-08-28T14:31:38.047 回答