gpt4 book ai didi

python - C++ Boost Multiprecision 与 Python 的 mpmath 之间的互操作性

转载 作者:太空宇宙 更新时间:2023-11-04 12:39:06 24 4
gpt4 key购买 nike

我有分别使用 Boost Multiprecision 和 Python 的 mpmath 的经验。

当要使两者通信时(例如在 C++ 中创建 Python 扩展),我的尝试总是涉及某种浪费的浮点到字符串和字符串到浮点的转换。

我的问题是:是否可以让两者以更高效(更优雅)的方式进行通信?我的意思是,有没有办法以同样的方式直接让 C++ Boost Multiprecision 从 Python mpmath.mpf 对象加载和导出到对象 as C's mpp does via pybind11

我一直在寻找这个。 only other similar question I found只是使用 pybind11 从 Boost Multiprecision 导出到 Python(通常),而不是直接导出到 mpmath 对象。在那个问题中,OP 最终使用了我试图避免的相同方法(即,在与 C++ 和 Python 通信时从/向字符串转换)。

最佳答案

这仅部分回答了您的问题。因为直接的答案是:不,如果不浪费地转换为字符串,就不可能以干净的方式进行,因为 mpmath是一个纯 python 库,没有任何部分写入 CC++ ,因此,即使您试图通过寻求使用某种二进制兼容性来跳过“浪费的转换”,您的代码也将非常脆弱:它每次都会在某些 python 时中断。或 mpmath内部结构发生了微小的变化。

但是我需要完全相同的东西。所以我决定通过 boost::python 注册一个自动转换它使用字符串检查和转换。实际上在 python 中你也创建了 mpmath.mpf来自字符串的对象,所以它非常相似,只是在下面的代码中它更快,因为它写在 C++ 中.

所以这对我有用:

#include <boost/python.hpp>
#include <iostream>
#include <limits>
#include <sstream>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
namespace py = ::boost::python;
using Prec80 = boost::multiprecision::number<boost::multiprecision::cpp_bin_float<80>>;

template<typename ArbitraryReal>
struct ArbitraryReal_to_python {
static PyObject* convert(const ArbitraryReal& val){
std::stringstream ss{};
ss << std::setprecision(std::numeric_limits<ArbitraryReal>::digits10+1) << val;
py::object mpmath = py::import("mpmath");
mpmath.attr("mp").attr("dps")=int(std::numeric_limits<ArbitraryReal>::digits10+1);
py::object result = mpmath.attr("mpf")(ss.str());
return boost::python::incref( result.ptr() );
}
};

template<typename ArbitraryReal>
struct ArbitraryReal_from_python {
ArbitraryReal_from_python(){
boost::python::converter::registry::push_back(&convertible,&construct,boost::python::type_id<ArbitraryReal>());
}
static void* convertible(PyObject* obj_ptr){
// Accept whatever python is able to convert into float
// This works with mpmath numbers. However if you want to accept strings as numbers this checking code can be a little longer to verify if string is a valid number.
double check = PyFloat_AsDouble(obj_ptr);
return (PyErr_Occurred()==nullptr) ? obj_ptr : nullptr;
}
static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data){
std::istringstream ss{ py::call_method<std::string>(obj_ptr, "__str__") };
void* storage=((boost::python::converter::rvalue_from_python_storage<ArbitraryReal>*)(data))->storage.bytes;
new (storage) ArbitraryReal;
ArbitraryReal* val=(ArbitraryReal*)storage;
ss >> *val;
data->convertible=storage;
}
};


struct Var
{
Prec80 value{"-71.23"};
Prec80 get() const { return value; };
void set(Prec80 val) { value = val; };
};

BOOST_PYTHON_MODULE(pysmall)
{
ArbitraryReal_from_python<Prec80>();
py::to_python_converter<Prec80,ArbitraryReal_to_python<Prec80>>();

py::class_<Var>("Var" )
.add_property("val", &Var::get, &Var::set);
}

现在你用这个命令编译这段代码:

g++ -O1 -g pysmall.cpp -o pysmall.so -std=gnu++17 -fPIC -shared -I/usr/include/python3.7m/ -lboost_python37 -lpython3.7m -Wl,-soname,"pysmall.so"

这是一个例子python session :

In [1]: import pysmall
In [2]: a=pysmall.Var()
In [3]: a.val
Out[3]: mpf('-71.2299999999999999999999999999999999999999999999999999999999999999999999999999997072')
In [4]: a.val=123.12
In [5]: a.val
Out[5]: mpf('123.120000000000000000000000000000000000000000000000000000000000000000000000000000003')

C++代码不关心 mpmath 是否已经在 python 中导入。如果是,则获取现有的库句柄,如果不是,则将其导入。如果您发现此代码段有任何改进空间,请告诉我!

在我写这篇文章时,这里有一些有用的引用资料:

  1. https://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
  2. https://github.com/bluescarni/mppp/blob/master/include/mp%2B%2B/extra/pybind11.hpp (但我不想使用 pybind11,只是 boost::python )

编辑:我现在已经在 YADE 中完成了这个实现,它适用于 EIGEN 和 CGAL 库。关于这个问题的部分在文件 ToFromPythonConverter.hpp

关于python - C++ Boost Multiprecision 与 Python 的 mpmath 之间的互操作性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55132860/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com