0

Is it possible to pass boost::python::numpy::ndarray as (default or not) argument of a boost::python function ?

dummy has no ndarray. stupid as one ndarray argument but with no default value. silly has one ndarray as default value.

>> more dummy.cpp stupid.cpp silly.cpp 
::::::::::::::
dummy.cpp
::::::::::::::
#include <boost/python.hpp>
namespace bp = boost::python;

int f(double x, double y=1.0) {return (int)(x+y);};

BOOST_PYTHON_MODULE(dummy)
{
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=1.0 ) );
}
::::::::::::::
stupid.cpp
::::::::::::::
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, double y=1.0) {return (int)(x.shape(0)+y);};

BOOST_PYTHON_MODULE(stupid)
{
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=1.0 ) );
}
::::::::::::::
silly.cpp
::::::::::::::
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, np::ndarray * y=nullptr) {return (int)(y ? x.shape(0)+y->shape(0) : x.shape(0));};

BOOST_PYTHON_MODULE(silly)
{
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=nullptr ) );
}

>> make
g++ -I /usr/include/python2.7 -o dummy.so  -fPIC -shared dummy.cpp  -lboost_python -lboost_numpy -lpython2.7
g++ -I /usr/include/python2.7 -o stupid.so -fPIC -shared stupid.cpp -lboost_python -lboost_numpy -lpython2.7
g++ -I /usr/include/python2.7 -o silly.so  -fPIC -shared silly.cpp  -lboost_python -lboost_numpy -lpython2.7

>> python
Python 2.7.17 (default, Oct 19 2019, 23:36:22) 
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dummy; dummy.f(1)
2
>>> import numpy; import stupid; stupid.f(numpy.array([1, 2, 3])) 
Segmentation fault

UPDATE

Tried to add Py_Initialize(); np::initialize(); in f without success

>> more stupid.cpp silly.cpp 
::::::::::::::
stupid.cpp
::::::::::::::
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, double y=1.0) {
  Py_Initialize();
  np::initialize();
  return (int)(x.shape(0)+y);
};

BOOST_PYTHON_MODULE(stupid)
{
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=1.0 ) );
}
::::::::::::::
silly.cpp
::::::::::::::
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, np::ndarray * y=nullptr) {
  Py_Initialize();
  np::initialize();
  return (int)(y ? x.shape(0)+y->shape(0) : x.shape(0));
};

BOOST_PYTHON_MODULE(silly)
{
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=nullptr ) );
}

>> make
g++ -I /usr/include/python2.7 -o stupid.so -fPIC -shared stupid.cpp -lboost_python -lboost_numpy -lpython2.7
g++ -I /usr/include/python2.7 -o silly.so  -fPIC -shared silly.cpp  -lboost_python -lboost_numpy -lpython2.7

>> python
Python 2.7.17 (default, Oct 19 2019, 23:36:22) 
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy; import stupid; stupid.f(numpy.array([1, 2, 3]))
Segmentation fault

UPDATE

OK, got it to work with calls in BOOST_PYTHON_MODULE. Still KO with default argument (silly example).

>> more stupid.cpp silly.cpp 
::::::::::::::
stupid.cpp
::::::::::::::
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, double y=1.0) {
  return (int)(x.shape(0)+y);
};

BOOST_PYTHON_MODULE(stupid)
{
  Py_Initialize();
  np::initialize();
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=1.0 ) );
}
::::::::::::::
silly.cpp
::::::::::::::
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, np::ndarray * y=nullptr) {
  return (int)(y ? x.shape(0)+y->shape(0) : x.shape(0));
};

BOOST_PYTHON_MODULE(silly)
{
  Py_Initialize();
  np::initialize();
  bp::def("f", f, ( bp::arg("x"), bp::arg("y")=nullptr ) );
}

>> make
g++ -I /usr/include/python2.7 -o stupid.so -fPIC -shared stupid.cpp -lboost_python -lboost_numpy -lpython2.7
g++ -I /usr/include/python2.7 -o silly.so  -fPIC -shared silly.cpp  -lboost_python -lboost_numpy -lpython2.7

 >> python
Python 2.7.17 (default, Oct 19 2019, 23:36:22) 
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy; import stupid; stupid.f(numpy.array([1, 2, 3]))
4
>>> import numpy; import silly; silly.f(numpy.array([1, 2, 3]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: No to_python (by-value) converter found for C++ type: decltype(nullptr)

WORKAROUND

>> more silly.cpp 
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, bp::object y) {
  np::ndarray yy = np::array(bp::list());
  if (!y.is_none()) yy = bp::extract<np::ndarray>(y);
  return (int)(x.shape(0)+yy.shape(0));
};

BOOST_PYTHON_MODULE(silly)
{
  Py_Initialize();
  np::initialize();
  bp::def("f", f, ( bp::arg("x"), bp::arg("y") ) );
}

>> make
g++ -I /usr/include/python2.7 -o silly.so  -fPIC -shared silly.cpp  -lboost_python -lboost_numpy -lpython2.7

>> python
Python 2.7.17 (default, Oct 19 2019, 23:36:22) 
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy; import silly; silly.f(numpy.array([1, 2, 3]), numpy.array([1, 2]))
5
>>> import numpy; import silly; silly.f(numpy.array([1, 2, 3]), None)
3

1 Answer 1

2

To be able to use numpy first initialise the Python runtime and the numpy module:

Py_Initialize();
np::initialize();

Failure to call these results in segmentation errors.

=======================================================================

For the silly.cpp the workaround is not needed. Here is the implementation:

#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;

int f(np::ndarray x, np::ndarray y = np::array(bp::list()) ) {
  return (int)(x.shape(0)+y.shape(0));
};

BOOST_PYTHON_MODULE(silly)
{
  Py_Initialize();
  np::initialize();
  bp::def("f", f, ( bp::arg("x"), bp::arg("y") = np::array(bp::list()) ) );
}

And the test results:

>>> import numpy, silly; silly.f(numpy.array([1, 2, 3]), numpy.array([1, 2]))
5
>>> import numpy, silly; silly.f(numpy.array([1, 2, 3]))
3
Sign up to request clarification or add additional context in comments.

3 Comments

Sure, missed that : tried to add theses calls in f without success...
OK, got it to work with calls in BOOST_PYTHON_MODULE. Still KO with default argument (silly example)
@FGH I've removed the workaround in silly.cpp - see my updated answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.