Skip to content

Instantly share code, notes, and snippets.

@chhenning
Created December 4, 2017 20:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chhenning/27e080c7528a12fdf751a63571bcb4cb to your computer and use it in GitHub Desktop.
Save chhenning/27e080c7528a12fdf751a63571bcb4cb to your computer and use it in GitHub Desktop.
Exchange data to and from cpp and numpy using buffer protocol
#include <numeric>
#include <vector>
#include <pybind11/pybind11.h>
namespace py = pybind11;
/*
A very generic matrix class
*/
class Matrix {
public:
Matrix(size_t rows, size_t cols)
: m_rows(rows), m_cols(cols)
{
m_vec = std::vector<float>(rows * cols, 12.f);
}
float *data() { return &m_vec.front(); }
size_t rows() const { return m_rows; }
size_t cols() const { return m_cols; }
float sum() const
{
return std::accumulate(m_vec.begin(), m_vec.end(), 0.f);
}
private:
size_t m_rows, m_cols;
std::vector<float> m_vec;
};
void init_matrix(py::module& m)
{
py::class_<Matrix> PyMatrix(m, "Matrix", py::buffer_protocol());
PyMatrix.def(py::init<size_t,size_t>());
// create matrix from numpy array using buffer protocol
PyMatrix.def(py::init([](py::buffer b)
{
// request a buffer descriptor from python
py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format())
{
throw std::runtime_error("Incompatible format: expected a float array!");
}
if (info.ndim != 2)
{
throw std::runtime_error("Incompatible buffer dimension!");
}
std::unique_ptr<Matrix> M = std::make_unique<Matrix>(info.shape[0], info.shape[1]);
std::memcpy(M->data(), static_cast<float*>(info.ptr), info.shape[0] * info.shape[1] * sizeof(float));
return M;
}));
// according to Python's buffer protocol specification
PyMatrix.def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info(
m.data(), /* Pointer to buffer */
sizeof(float), /* Size of one scalar */
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
2, /* Number of dimensions */
{ m.rows(), m.cols() }, /* Buffer dimensions */
{ sizeof(float) * m.cols(), /* Strides (in bytes) for each index */
sizeof(float) }
);
});
PyMatrix.def("sum", &Matrix::sum);
}
@chhenning
Copy link
Author

In python you can do the following to create a numpy array without copying from cpp Matrix. Please note for demonstration purposes the Matrix is initialized with 12.f

>>> import chh
>>> import numpy as np
>>> m = chh.Matrix(4,4)
>>> m.sum()
192.0
>>> a = np.array(m, copy = False)
>>> np.sum(a)
192.0
>>> b = np.arange(16, dtype=np.float)
>>> b
array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
		11.,  12.,  13.,  14.,  15.])
>>> b = b.reshape(4,4)
>>> m2 = chh.Matrix(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: Incompatible format: expected a float array!
>>> b = b.astype(np.float32)
>>> m2 = chh.Matrix(b)
>>> m2.sum()
120.0
>>>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment