C++ コードから一部のデータをオブジェクトとして返したいと考えていnumpy.array
ます。を見てみましたboost::python::numeric
が、そのドキュメントは非常に簡潔です。vector<double>
たとえば、(それほど大きくない)をpythonに返す例を取得できますか?データのコピーをしてもかまいません。
5 に答える
更新:私の元の回答(https://github.com/ndarray/Boost.NumPy)で説明されているライブラリは、Boost 1.63の時点でBoost.Pythonに直接統合されているため、スタンドアロンバージョンは非推奨になりました。以下のテキストは、新しい統合バージョンに対応しています(名前空間のみが変更されています)。
Boost.Pythonには、NumPyC-APIの適度に完全なラッパーがBoost.Pythonインターフェイスに含まれるようになりました。これはかなり低レベルであり、コピーせずにC ++データをNumPyとの間でやり取りする方法という、より難しい問題に対処する方法に主に焦点を当てていますが、コピーされたstd ::vectorreturnを次のように実行する方法は次のとおりです。
#include "boost/python/numpy.hpp"
namespace bp = boost::python;
namespace bn = boost::python::numpy;
std::vector<double> myfunc(...);
bn::ndarray mywrapper(...) {
std::vector<double> v = myfunc(...);
Py_intptr_t shape[1] = { v.size() };
bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>());
std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data()));
return result;
}
BOOST_PYTHON_MODULE(example) {
bn::initialize();
bp::def("myfunc", mywrapper);
}
特別なサード パーティの C++ ライブラリをダウンロードする必要がないソリューション (ただし、numpy が必要です)。
#include <numpy/ndarrayobject.h> // ensure you include this header
boost::python::object stdVecToNumpyArray( std::vector<double> const& vec )
{
npy_intp size = vec.size();
/* const_cast is rather horrible but we need a writable pointer
in C++11, vec.data() will do the trick
but you will still need to const_cast
*/
double * data = size ? const_cast<double *>(&vec[0])
: static_cast<double *>(NULL);
// create a PyObject * from pointer and data
PyObject * pyObj = PyArray_SimpleNewFromData( 1, &size, NPY_DOUBLE, data );
boost::python::handle<> handle( pyObj );
boost::python::numeric::array arr( handle );
/* The problem of returning arr is twofold: firstly the user can modify
the data which will betray the const-correctness
Secondly the lifetime of the data is managed by the C++ API and not the
lifetime of the numpy array whatsoever. But we have a simple solution..
*/
return arr.copy(); // copy the object. numpy owns the copy now.
}
もちろん、double * と size から汎用的な関数を作成し、この情報を抽出して vector から関数を呼び出すこともできます。NPY_TYPES
テンプレートを作成することもできますが、データ型から列挙型への何らかのマッピングが必要になります。
numpy API を直接使用することは必ずしも難しいことではありませんが、自分のプロジェクトでは定期的に boost::multiarray を使用しており、配列の形状を C++/Python 境界間で自動的に転送すると便利です。だから、ここに私のレシピがあります。http://code.google.com/p/numpy-boost/を使用するか、このバージョンの numpy_boost.hpp ヘッダーを使用してください。これは複数ファイルの boost::python プロジェクトにより適していますが、一部の C++11 を使用しています。次に、boost::python コードから、次のようなものを使用します。
PyObject* myfunc(/*....*/)
{
// If your data is already in a boost::multiarray object:
// numpy_boost< double, 1 > to_python( numpy_from_boost_array(result_cm) );
// otherwise:
numpy_boost< double, 1> to_python( boost::extents[n] );
std::copy( my_vector.begin(), my_vector.end(), to_python.begin() );
PyObject* result = to_python.py_ptr();
Py_INCREF( result );
return result;
}
利用可能な回答を見て、「これは簡単だろう」と思いました。私は何時間も費やして、答えの些細な例/適応のように見えるものを試みました.
次に、@ maxの回答を正確に実装し(Eigenをインストールする必要がありました)、うまく機能しましたが、それでも適応に問題がありました。私の問題は、ほとんどが (数による) ばかげた構文ミスでしたが、さらに、ベクターがスタックから削除されたように見えた後、コピーされた std::vector のデータへのポインターを使用していました。
この例では、std::vector へのポインターが返されますが、サイズと data() ポインターを返すか、基になるデータへの安定した方法で numpy 配列にアクセスできる他の実装を使用することもできます (つまり、存在):
class_<test_wrap>("test_wrap")
.add_property("values", +[](test_wrap& self) -> object {
return wrap(self.pvalues()->data(),self.pvalues()->size());
})
;
を使用した test_wrap の場合std::vector<double>
(通常、pvalues() はベクトルを設定せずにポインターを返すだけです):
class test_wrap {
public:
std::vector<double> mValues;
std::vector<double>* pvalues() {
mValues.clear();
for(double d_ = 0.0; d_ < 4; d_+=0.3)
{
mValues.push_back(d_);
}
return &mValues;
}
};
完全な例は Github にあるため、面倒な書き起こしの手順をスキップして、ビルドやライブラリなどについて心配する必要がなくなります。以下を実行するだけで、機能する例を取得できるはずです (必要な機能がインストールされ、パスが設定されている場合)。すでに):
git clone https://github.com/ransage/boost_numpy_example.git
cd boost_numpy_example
# Install virtualenv, numpy if necessary; update path (see below*)
cd build && cmake .. && make && ./test_np.py
これにより、次の出力が得られます。
# cmake/make output
values has type <type 'numpy.ndarray'>
values has len 14
values is [ 0. 0.3 0.6 0.9 1.2 1.5 1.8 2.1 2.4 2.7 3. 3.3 3.6 3.9]
*私の場合、次のようにnumpyをvirtualenvに入れました-@maxの提案に従って実行できる場合、これは不要ですpython -c "import numpy; print numpy.get_include()"
。
# virtualenv, pip, path unnecessary if your Python has numpy
virtualenv venv
./venv/bin/pip install -r requirements.txt
export PATH="$(pwd)/venv/bin:$PATH"
楽しむ!:-)