From b2519a25a948a36eafb545d166cd7b2df607ea35 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 3 Jul 2011 16:40:30 +0000 Subject: [PATCH] Rename (and move) boost.python.numpy to boost.numpy. --- SConscript | 34 +- boost/numpy.hpp | 33 ++ boost/numpy/dtype.hpp | 65 ++++ boost/{python => }/numpy/internal.hpp | 16 +- boost/numpy/invoke_matching.hpp | 186 +++++++++++ boost/numpy/matrix.hpp | 67 ++++ boost/numpy/ndarray.hpp | 296 ++++++++++++++++++ boost/numpy/numpy_object_mgr_traits.hpp | 31 ++ boost/numpy/scalars.hpp | 60 ++++ boost/numpy/ufunc.hpp | 209 +++++++++++++ boost/python/numpy.hpp | 33 -- boost/python/numpy/dtype.hpp | 59 ---- boost/python/numpy/invoke_matching.hpp | 184 ----------- boost/python/numpy/matrix.hpp | 62 ---- boost/python/numpy/ndarray.hpp | 287 ----------------- .../python/numpy/numpy_object_mgr_traits.hpp | 27 -- boost/python/numpy/scalars.hpp | 55 ---- boost/python/numpy/ufunc.hpp | 193 ------------ libs/{python => }/numpy/src/Jamfile | 0 libs/numpy/src/SConscript | 4 + libs/numpy/src/dtype.cpp | 102 ++++++ libs/numpy/src/matrix.cpp | 63 ++++ libs/numpy/src/ndarray.cpp | 269 ++++++++++++++++ libs/numpy/src/numpy.cpp | 16 + libs/numpy/src/scalars.cpp | 35 +++ libs/numpy/src/ufunc.cpp | 64 ++++ libs/{python => }/numpy/test/Jamfile | 0 libs/numpy/test/SConscript | 12 + libs/{python => }/numpy/test/indexing.py | 12 +- libs/numpy/test/indexing_mod.cpp | 22 ++ libs/{python => }/numpy/test/ndarray.py | 0 libs/numpy/test/ndarray_mod.cpp | 38 +++ libs/{python => }/numpy/test/shapes.py | 0 libs/numpy/test/shapes_mod.cpp | 16 + libs/{python => }/numpy/test/templates.py | 0 libs/numpy/test/templates_mod.cpp | 57 ++++ libs/{python => }/numpy/test/ufunc.py | 0 libs/numpy/test/ufunc_mod.cpp | 30 ++ libs/python/numpy/src/SConscript | 4 - libs/python/numpy/src/dtype.cpp | 92 ------ libs/python/numpy/src/matrix.cpp | 51 --- libs/python/numpy/src/ndarray.cpp | 285 ----------------- libs/python/numpy/src/numpy.cpp | 13 - libs/python/numpy/src/scalars.cpp | 35 --- libs/python/numpy/src/ufunc.cpp | 48 --- libs/python/numpy/test/SConscript | 12 - libs/python/numpy/test/indexing_mod.cpp | 21 -- libs/python/numpy/test/ndarray_mod.cpp | 54 ---- libs/python/numpy/test/shapes_mod.cpp | 13 - libs/python/numpy/test/templates_mod.cpp | 51 --- libs/python/numpy/test/ufunc_mod.cpp | 31 -- 51 files changed, 1706 insertions(+), 1641 deletions(-) create mode 100644 boost/numpy.hpp create mode 100644 boost/numpy/dtype.hpp rename boost/{python => }/numpy/internal.hpp (58%) create mode 100644 boost/numpy/invoke_matching.hpp create mode 100644 boost/numpy/matrix.hpp create mode 100644 boost/numpy/ndarray.hpp create mode 100644 boost/numpy/numpy_object_mgr_traits.hpp create mode 100644 boost/numpy/scalars.hpp create mode 100644 boost/numpy/ufunc.hpp delete mode 100644 boost/python/numpy.hpp delete mode 100644 boost/python/numpy/dtype.hpp delete mode 100644 boost/python/numpy/invoke_matching.hpp delete mode 100644 boost/python/numpy/matrix.hpp delete mode 100644 boost/python/numpy/ndarray.hpp delete mode 100644 boost/python/numpy/numpy_object_mgr_traits.hpp delete mode 100644 boost/python/numpy/scalars.hpp delete mode 100644 boost/python/numpy/ufunc.hpp rename libs/{python => }/numpy/src/Jamfile (100%) create mode 100644 libs/numpy/src/SConscript create mode 100644 libs/numpy/src/dtype.cpp create mode 100644 libs/numpy/src/matrix.cpp create mode 100644 libs/numpy/src/ndarray.cpp create mode 100644 libs/numpy/src/numpy.cpp create mode 100644 libs/numpy/src/scalars.cpp create mode 100644 libs/numpy/src/ufunc.cpp rename libs/{python => }/numpy/test/Jamfile (100%) create mode 100644 libs/numpy/test/SConscript rename libs/{python => }/numpy/test/indexing.py (82%) create mode 100644 libs/numpy/test/indexing_mod.cpp rename libs/{python => }/numpy/test/ndarray.py (100%) create mode 100644 libs/numpy/test/ndarray_mod.cpp rename libs/{python => }/numpy/test/shapes.py (100%) create mode 100644 libs/numpy/test/shapes_mod.cpp rename libs/{python => }/numpy/test/templates.py (100%) create mode 100644 libs/numpy/test/templates_mod.cpp rename libs/{python => }/numpy/test/ufunc.py (100%) create mode 100644 libs/numpy/test/ufunc_mod.cpp delete mode 100644 libs/python/numpy/src/SConscript delete mode 100644 libs/python/numpy/src/dtype.cpp delete mode 100644 libs/python/numpy/src/matrix.cpp delete mode 100644 libs/python/numpy/src/ndarray.cpp delete mode 100644 libs/python/numpy/src/numpy.cpp delete mode 100644 libs/python/numpy/src/scalars.cpp delete mode 100644 libs/python/numpy/src/ufunc.cpp delete mode 100644 libs/python/numpy/test/SConscript delete mode 100644 libs/python/numpy/test/indexing_mod.cpp delete mode 100644 libs/python/numpy/test/ndarray_mod.cpp delete mode 100644 libs/python/numpy/test/shapes_mod.cpp delete mode 100644 libs/python/numpy/test/templates_mod.cpp delete mode 100644 libs/python/numpy/test/ufunc_mod.cpp diff --git a/SConscript b/SConscript index 3ffd5a57..9a43d32b 100644 --- a/SConscript +++ b/SConscript @@ -1,33 +1,33 @@ import scons_tools import os -targets = {"boost.python.numpy":{}} +targets = {"boost.numpy":{}} scons_tools.LocalConfiguration( - name="boost.python.numpy", - libraries=["boost_python_numpy"], + name="boost.numpy", + libraries=["boost_numpy"], dependencies=("boost.python", "numpy") ) -bp_numpy_env = scons_tools.GetEnvironment().Clone() -bp_numpy_env.Append(CPPPATH=[os.path.abspath(os.curdir)]) -libpath = os.path.abspath("%s/python/numpy/src" % scons_tools.GetBuildDir()) +boost_numpy_env = scons_tools.GetEnvironment().Clone() +boost_numpy_env.Append(CPPPATH=[os.path.abspath(os.curdir)]) +libpath = os.path.abspath("%s/numpy/src" % scons_tools.GetBuildDir()) if os.environ.has_key("LD_LIBRARY_PATH"): - bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) + boost_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) else: - bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = libpath -bp_numpy_env.Append(LIBPATH=[libpath]) -bp_numpy_env.SetupPackages(["boost.python", "numpy"]) -Export("bp_numpy_env") + boost_numpy_env["ENV"]["LD_LIBRARY_PATH"] = libpath +boost_numpy_env.Append(LIBPATH=[libpath]) +boost_numpy_env.SetupPackages(["boost.python", "numpy"]) +Export("boost_numpy_env") -targets["boost.python.numpy"]["lib"] = SConscript("libs/python/numpy/src/SConscript") -targets["boost.python.numpy"]["install"] = ( - bp_numpy_env.RecursiveInstall( - os.path.join(bp_numpy_env["INSTALL_HEADERS"], "boost"), +targets["boost.numpy"]["lib"] = SConscript("libs/numpy/src/SConscript") +targets["boost.numpy"]["install"] = ( + boost_numpy_env.RecursiveInstall( + os.path.join(boost_numpy_env["INSTALL_HEADERS"], "boost"), "boost", regex="(.*\.hpp)") - + bp_numpy_env.Install(bp_numpy_env["INSTALL_LIB"], targets["boost.python.numpy"]["lib"]) + + boost_numpy_env.Install(boost_numpy_env["INSTALL_LIB"], targets["boost.numpy"]["lib"]) ) -targets["boost.python.numpy"]["test"] = SConscript("libs/python/numpy/test/SConscript") +targets["boost.numpy"]["test"] = SConscript("libs/numpy/test/SConscript") Return("targets") diff --git a/boost/numpy.hpp b/boost/numpy.hpp new file mode 100644 index 00000000..1efd4ce8 --- /dev/null +++ b/boost/numpy.hpp @@ -0,0 +1,33 @@ +#ifndef BOOST_NUMPY_HPP_INCLUDED +#define BOOST_NUMPY_HPP_INCLUDED + +/** + * @file boost/numpy.hpp + * @brief Main public header file for boost.numpy. + */ + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace numpy { + +/** + * @brief Initialize the Numpy C-API + * + * This must be called before using anything in boost.numpy; + * It should probably be the first line inside BOOST_PYTHON_MODULE. + * + * @internal This just calls the Numpy C-API functions "import_array()" + * and "import_ufunc()". + */ +void initialize(); + +} // namespace boost::numpy +} // namespace boost + +#endif // !BOOST_NUMPY_HPP_INCLUDED diff --git a/boost/numpy/dtype.hpp b/boost/numpy/dtype.hpp new file mode 100644 index 00000000..173802e3 --- /dev/null +++ b/boost/numpy/dtype.hpp @@ -0,0 +1,65 @@ +#ifndef BOOST_NUMPY_DTYPE_HPP_INCLUDED +#define BOOST_NUMPY_DTYPE_HPP_INCLUDED + +/** + * @file boost/numpy/dtype.hpp + * @brief Object manager for Python's numpy.dtype class. + */ + +#include +#include + +#include +#include + +namespace boost +{ +namespace numpy +{ + +/** + * @brief A boost.python "object manager" (subclass of object) for numpy.dtype. + * + * @todo This could have a lot more interesting accessors. + */ +class dtype : public python::object +{ + static python::detail::new_reference convert(python::object::object_cref arg, bool align); +public: + + /// @brief Convert an arbitrary Python object to a data-type descriptor object. + template + explicit dtype(T arg, bool align=false) : python::object(convert(arg, align)) {} + + /** + * @brief Get the built-in numpy dtype associated with the given scalar template type. + * + * This is perhaps the most useful part of the numpy API: it returns the dtype object + * corresponding to a built-in C++ type. This should work for any integer or floating point + * type supported by numpy, and will also work for std::complex if + * sizeof(std::complex) == 2*sizeof(T). + * + * It can also be useful for users to add explicit specializations for POD structs + * that return field-based dtypes. + */ + template static dtype get_builtin(); + + /// @brief Return the size of the data type in bytes. + int get_itemsize() const; + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, python::object); + +}; + +} // namespace boost::numpy + +namespace python +{ +namespace converter +{ +NUMPY_OBJECT_MANAGER_TRAITS(numpy::dtype); +} // namespace boost::python::converter +} // namespace boost::python +} // namespace boost + +#endif // !BOOST_NUMPY_DTYPE_HPP_INCLUDED diff --git a/boost/python/numpy/internal.hpp b/boost/numpy/internal.hpp similarity index 58% rename from boost/python/numpy/internal.hpp rename to boost/numpy/internal.hpp index 55d1c70c..0970319c 100644 --- a/boost/python/numpy/internal.hpp +++ b/boost/numpy/internal.hpp @@ -1,19 +1,19 @@ -#ifndef BOOST_PYTHON_NUMPY_INTERNAL_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_INTERNAL_HPP_INCLUDED +#ifndef BOOST_NUMPY_INTERNAL_HPP_INCLUDED +#define BOOST_NUMPY_INTERNAL_HPP_INCLUDED /** - * @file boost/python/numpy/internal.hpp + * @file boost/numpy/internal.hpp * @brief Internal header file to include the Numpy C-API headers. * - * This should only be included by source files in the boost.python.numpy library itself. + * This should only be included by source files in the boost.numpy library itself. */ #include -#ifdef BOOST_PYTHON_NUMPY_INTERNAL +#ifdef BOOST_NUMPY_INTERNAL #define NO_IMPORT_ARRAY #define NO_IMPORT_UFUNC #else -#ifndef BOOST_PYTHON_NUMPY_INTERNAL_MAIN +#ifndef BOOST_NUMPY_INTERNAL_MAIN ERROR_internal_hpp_is_for_internal_use_only #endif #endif @@ -21,9 +21,9 @@ ERROR_internal_hpp_is_for_internal_use_only #define PY_UFUNC_UNIQUE_SYMBOL BOOST_UFUNC_ARRAY_API #include #include -#include +#include #define NUMPY_OBJECT_MANAGER_TRAITS_IMPL(pytype,manager) \ PyTypeObject const * object_manager_traits::get_pytype() { return &pytype; } -#endif // !BOOST_PYTHON_NUMPY_INTERNAL_HPP_INCLUDED +#endif // !BOOST_NUMPY_INTERNAL_HPP_INCLUDED diff --git a/boost/numpy/invoke_matching.hpp b/boost/numpy/invoke_matching.hpp new file mode 100644 index 00000000..17f629fb --- /dev/null +++ b/boost/numpy/invoke_matching.hpp @@ -0,0 +1,186 @@ +#ifndef BOOST_NUMPY_INVOKE_MATCHING_HPP_INCLUDED +#define BOOST_NUMPY_INVOKE_MATCHING_HPP_INCLUDED + +/** + * @file boost/numpy/invoke_matching.hpp + * @brief Template invocation based on dtype matching. + */ + +#include +#include + +#include + +namespace boost +{ +namespace numpy +{ +namespace detail +{ + +struct add_pointer_meta +{ + template + struct apply + { + typedef typename boost::add_pointer::type type; + }; + +}; + +struct dtype_template_match_found {}; +struct nd_template_match_found {}; + +template +struct dtype_template_invoker +{ + + template + void operator()(T *) const + { + if (dtype::get_builtin() == m_dtype) + { + m_func.template apply(); + throw dtype_template_match_found(); + } + } + + dtype_template_invoker(dtype const & dtype_, Function func) + : m_dtype(dtype_), m_func(func) {} + +private: + dtype const & m_dtype; + Function m_func; +}; + +template +struct dtype_template_invoker< boost::reference_wrapper > +{ + + template + void operator()(T *) const + { + if (dtype::get_builtin() == m_dtype) + { + m_func.template apply(); + throw dtype_template_match_found(); + } + } + + dtype_template_invoker(dtype const & dtype_, Function & func) + : m_dtype(dtype_), m_func(func) {} + +private: + dtype const & m_dtype; + Function & m_func; +}; + +template +struct nd_template_invoker +{ + template + void operator()(boost::mpl::integral_c *) const + { + if (m_nd == N) + { + m_func.template apply(); + throw nd_template_match_found(); + } + } + + nd_template_invoker(int nd, Function func) : m_nd(nd), m_func(func) {} + +private: + int m_nd; + Function m_func; +}; + +template +struct nd_template_invoker< boost::reference_wrapper > +{ + template + void operator()(boost::mpl::integral_c *) const + { + if (m_nd == N) + { + m_func.template apply(); + throw nd_template_match_found(); + } + } + + nd_template_invoker(int nd, Function & func) : m_nd(nd), m_func(func) {} + +private: + int m_nd; + Function & m_func; +}; + +} // namespace boost::numpy::detail + +template +void invoke_matching_nd(int nd, Function f) +{ + detail::nd_template_invoker invoker(nd, f); + try { boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker);} + catch (detail::nd_template_match_found &) { return;} + PyErr_SetString(PyExc_TypeError, "number of dimensions not found in template list."); + python::throw_error_already_set(); +} + +template +void invoke_matching_dtype(dtype const & dtype_, Function f) +{ + detail::dtype_template_invoker invoker(dtype_, f); + try { boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker);} + catch (detail::dtype_template_match_found &) { return;} + PyErr_SetString(PyExc_TypeError, "dtype not found in template list."); + python::throw_error_already_set(); +} + +namespace detail +{ + +template +struct array_template_invoker_wrapper_2 +{ + template + void apply() const { m_func.template apply();} + array_template_invoker_wrapper_2(Function & func) : m_func(func) {} + +private: + Function & m_func; +}; + +template +struct array_template_invoker_wrapper_1 +{ + template + void apply() const { invoke_matching_nd(m_nd, array_template_invoker_wrapper_2(m_func));} + array_template_invoker_wrapper_1(int nd, Function & func) : m_nd(nd), m_func(func) {} + +private: + int m_nd; + Function & m_func; +}; + +template +struct array_template_invoker_wrapper_1< DimSequence, boost::reference_wrapper > + : public array_template_invoker_wrapper_1< DimSequence, Function > +{ + array_template_invoker_wrapper_1(int nd, Function & func) + : array_template_invoker_wrapper_1< DimSequence, Function >(nd, func) {} +}; + +} // namespace boost::numpy::detail + +template +void invoke_matching_array(ndarray const & array_, Function f) +{ + detail::array_template_invoker_wrapper_1 wrapper(array_.get_nd(), f); + invoke_matching_dtype(array_.get_dtype(), wrapper); +} + +} // namespace boost::numpy +} // namespace boost + +#endif // !BOOST_NUMPY_INVOKE_MATCHING_HPP_INCLUDED diff --git a/boost/numpy/matrix.hpp b/boost/numpy/matrix.hpp new file mode 100644 index 00000000..db5c8d24 --- /dev/null +++ b/boost/numpy/matrix.hpp @@ -0,0 +1,67 @@ +#ifndef BOOST_NUMPY_MATRIX_HPP_INCLUDED +#define BOOST_NUMPY_MATRIX_HPP_INCLUDED + +/** + * @file boost/numpy/matrix.hpp + * @brief Object manager for numpy.matrix. + */ + +#include +#include +#include + +namespace boost +{ +namespace numpy +{ + +/** + * @brief A boost.python "object manager" (subclass of object) for numpy.matrix. + * + * @internal numpy.matrix is defined in Python, so object_manager_traits::get_pytype() + * is implemented by importing numpy and getting the "matrix" attribute of the module. + * We then just hope that doesn't get destroyed while we need it, because if we put + * a dynamic python object in a static-allocated boost::python::object or handle<>, + * bad things happen when Python shuts down. I think this solution is safe, but I'd + * love to get that confirmed. + */ +class matrix : public ndarray +{ + static python::object construct(object_cref obj, dtype const & dt, bool copy); + static python::object construct(object_cref obj, bool copy); +public: + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(matrix, ndarray); + + /// @brief Equivalent to "numpy.matrix(obj,dt,copy)" in Python. + explicit matrix(python::object const & obj, dtype const & dt, bool copy=true) + : ndarray(python::extract(construct(obj, dt, copy))) {} + + /// @brief Equivalent to "numpy.matrix(obj,copy=copy)" in Python. + explicit matrix(python::object const & obj, bool copy=true) + : ndarray(python::extract(construct(obj, copy))) {} + + /// \brief Return a view of the matrix with the given dtype. + matrix view(dtype const & dt) const; + + /// \brief Copy the scalar (deep for all non-object fields). + matrix copy() const; + + /// \brief Transpose the matrix. + matrix transpose() const; + +}; + +} // namespace boost::numpy +namespace python +{ +namespace converter +{ + +NUMPY_OBJECT_MANAGER_TRAITS(numpy::matrix); + +} // namespace boost::python::converter +} // namespace boost::python +} // namespace boost + +#endif // !BOOST_NUMPY_MATRIX_HPP_INCLUDED diff --git a/boost/numpy/ndarray.hpp b/boost/numpy/ndarray.hpp new file mode 100644 index 00000000..f570b3eb --- /dev/null +++ b/boost/numpy/ndarray.hpp @@ -0,0 +1,296 @@ +#ifndef BOOST_NUMPY_NDARRAY_HPP_INCLUDED +#define BOOST_NUMPY_NDARRAY_HPP_INCLUDED + +/** + * @file boost/numpy/ndarray.hpp + * @brief Object manager and various utilities for numpy.ndarray. + */ + +#include +#include +#include +#include +#include + +#include + +namespace boost +{ +namespace numpy +{ + +/** + * @brief A boost.python "object manager" (subclass of object) for numpy.ndarray. + * + * @todo This could have a lot more functionality (like boost::python::numeric::array). + * Right now all that exists is what was needed to move raw data between C++ and Python. + */ +class ndarray : public python::object +{ + + /** + * @brief An internal struct that's byte-compatible with PyArrayObject. + * + * This is just a hack to allow inline access to this stuff while hiding numpy/arrayobject.h + * from the user. + */ + struct array_struct + { + PyObject_HEAD + char * data; + int nd; + Py_intptr_t * shape; + Py_intptr_t * strides; + PyObject * base; + PyObject * descr; + int flags; + PyObject * weakreflist; + }; + + /// @brief Return the held Python object as an array_struct. + array_struct * get_struct() const { return reinterpret_cast(this->ptr()); } + +public: + + /** + * @brief Enum to represent (some) of Numpy's internal flags. + * + * These don't match the actual Numpy flag values; we can't get those without including + * numpy/arrayobject.h or copying them directly. That's very unfortunate. + * + * @todo I'm torn about whether this should be an enum. It's very convenient to not + * make these simple integer values for overloading purposes, but the need to + * define every possible combination and custom bitwise operators is ugly. + */ + enum bitflag + { + NONE=0x0, C_CONTIGUOUS=0x1, F_CONTIGUOUS=0x2, V_CONTIGUOUS=0x1|0x2, + ALIGNED=0x4, WRITEABLE=0x8, BEHAVED=0x4|0x8, + CARRAY_RO=0x1|0x4, CARRAY=0x1|0x4|0x8, CARRAY_MIS=0x1|0x8, + FARRAY_RO=0x2|0x4, FARRAY=0x2|0x4|0x8, FARRAY_MIS=0x2|0x8, + UPDATE_ALL=0x1|0x2|0x4, VARRAY=0x1|0x2|0x8, ALL=0x1|0x2|0x4|0x8 + }; + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(ndarray, object); + + /// @brief Return a view of the scalar with the given dtype. + ndarray view(dtype const & dt) const; + + /// @brief Copy the scalar (deep for all non-object fields). + ndarray copy() const; + + /// @brief Return the size of the nth dimension. + int const shape(int n) const { return get_shape()[n]; } + + /// @brief Return the stride of the nth dimension. + int const strides(int n) const { return get_strides()[n]; } + + /** + * @brief Return the array's raw data pointer. + * + * This returns char so stride math works properly on it. It's pretty much + * expected that the user will have to reinterpret_cast it. + */ + char * get_data() const { return get_struct()->data; } + + /// @brief Return the array's data-type descriptor object. + dtype get_dtype() const; + + /// @brief Return the object that owns the array's data, or None if the array owns its own data. + python::object get_base() const; + + /// @brief Set the object that owns the array's data. Use with care. + void set_base(object const & base); + + /// @brief Return the shape of the array as an array of integers (length == get_nd()). + Py_intptr_t const * get_shape() const { return get_struct()->shape; } + + /// @brief Return the stride of the array as an array of integers (length == get_nd()). + Py_intptr_t const * get_strides() const { return get_struct()->strides; } + + /// @brief Return the number of array dimensions. + int const get_nd() const { return get_struct()->nd; } + + /// @brief Return the array flags. + bitflag const get_flags() const; + + /// @brief Reverse the dimensions of the array. + ndarray transpose() const; + + /// @brief Eliminate any unit-sized dimensions. + ndarray squeeze() const; + + /// @brief Equivalent to self.reshape(*shape) in Python. + ndarray reshape(python::tuple const & shape) const; + + /** + * @brief If the array contains only a single element, return it as an array scalar; otherwise return + * the array. + * + * @internal This is simply a call to PyArray_Return(); + */ + python::object scalarize() const; +}; + +/** + * @brief Construct a new array with the given shape and data type, with data initialized to zero. + */ +ndarray zeros(python::tuple const & shape, dtype const & dt); +ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); + +/** + * @brief Construct a new array with the given shape and data type, with data left uninitialized. + */ +ndarray empty(python::tuple const & shape, dtype const & dt); +ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); + +/** + * @brief Construct a new array from an arbitrary Python sequence. + * + * @todo This does't seem to handle ndarray subtypes the same way that "numpy.array" does in Python. + */ +ndarray array(python::object const & obj); +ndarray array(python::object const & obj, dtype const & dt); + +namespace detail +{ + +ndarray from_data_impl(void * data, + dtype const & dt, + std::vector const & shape, + std::vector const & strides, + python::object const & owner, + bool writeable); + +template +ndarray from_data_impl(void * data, + dtype const & dt, + Container shape, + Container strides, + python::object const & owner, + bool writeable, + typename boost::enable_if< boost::is_integral >::type * enabled = NULL) +{ + std::vector shape_(shape.begin(),shape.end()); + std::vector strides_(strides.begin(), strides.end()); + return from_data_impl(data, dt, shape_, strides_, owner, writeable); +} + +ndarray from_data_impl(void * data, + dtype const & dt, + python::object const & shape, + python::object const & strides, + python::object const & owner, + bool writeable); + +} // namespace boost::numpy::detail + +/** + * @brief Construct a new ndarray object from a raw pointer. + * + * @param[in] data Raw pointer to the first element of the array. + * @param[in] dt Data type descriptor. Often retrieved with dtype::get_builtin(). + * @param[in] shape Shape of the array as STL container of integers; must have begin() and end(). + * @param[in] strides Shape of the array as STL container of integers; must have begin() and end(). + * @param[in] owner An arbitray Python object that owns that data pointer. The array object will + * keep a reference to the object, and decrement it's reference count when the + * array goes out of scope. Pass None at your own peril. + * + * @todo Should probably take ranges of iterators rather than actual container objects. + */ +template +inline ndarray from_data(void * data, + dtype const & dt, + Container shape, + Container strides, + python::object const & owner) +{ + return numpy::detail::from_data_impl(data, dt, shape, strides, owner, true); +} + +/** + * @brief Construct a new ndarray object from a raw pointer. + * + * @param[in] data Raw pointer to the first element of the array. + * @param[in] dt Data type descriptor. Often retrieved with dtype::get_builtin(). + * @param[in] shape Shape of the array as STL container of integers; must have begin() and end(). + * @param[in] strides Shape of the array as STL container of integers; must have begin() and end(). + * @param[in] owner An arbitray Python object that owns that data pointer. The array object will + * keep a reference to the object, and decrement it's reference count when the + * array goes out of scope. Pass None at your own peril. + * + * This overload takes a const void pointer and sets the "writeable" flag of the array to false. + * + * @todo Should probably take ranges of iterators rather than actual container objects. + */ +template +inline ndarray from_data(void const * data, + dtype const & dt, + Container shape, + Container strides, + python::object const & owner) +{ + return numpy::detail::from_data_impl(const_cast(data), dt, shape, strides, owner, false); +} + +/** + * @brief Transform an arbitrary object into a numpy array with the given requirements. + * + * @param[in] obj An arbitrary python object to convert. Arrays that meet the requirements + * will be passed through directly. + * @param[in] dt Data type descriptor. Often retrieved with dtype::get_builtin(). + * @param[in] nd_min Minimum number of dimensions. + * @param[in] nd_max Maximum number of dimensions. + * @param[in] flags Bitwise OR of flags specifying additional requirements. + */ +ndarray from_object(python::object const & obj, dtype const & dt, + int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); + +inline ndarray from_object(python::object const & obj, dtype const & dt, + int nd, ndarray::bitflag flags=ndarray::NONE) +{ + return from_object(obj, dt, nd, nd, flags); +} + +inline ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE) +{ + return from_object(obj, dt, 0, 0, flags); +} + +ndarray from_object(python::object const & obj, int nd_min, int nd_max, + ndarray::bitflag flags=ndarray::NONE); + +inline ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE) +{ + return from_object(obj, nd, nd, flags); +} + +inline ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) +{ + return from_object(obj, 0, 0, flags); +} + +inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) +{ + return ndarray::bitflag(int(a) | int(b)); +} + +inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) +{ + return ndarray::bitflag(int(a) & int(b)); +} + +} // namespace boost::numpy + +namespace python +{ +namespace converter +{ + +NUMPY_OBJECT_MANAGER_TRAITS(numpy::ndarray); + +} // namespace boost::python::converter +} // namespace boost::python +} // namespace boost + +#endif // !BOOST_NUMPY_NDARRAY_HPP_INCLUDED diff --git a/boost/numpy/numpy_object_mgr_traits.hpp b/boost/numpy/numpy_object_mgr_traits.hpp new file mode 100644 index 00000000..784954a4 --- /dev/null +++ b/boost/numpy/numpy_object_mgr_traits.hpp @@ -0,0 +1,31 @@ +#ifndef BOOST_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED +#define BOOST_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED + +/** + * @file boost/numpy/numpy_object_mgr_traits.hpp + * @brief Macro that specializes object_manager_traits by requiring a + * source-file implementation of get_pytype(). + */ + +#define NUMPY_OBJECT_MANAGER_TRAITS(manager) \ +template <> \ +struct object_manager_traits \ +{ \ + BOOST_STATIC_CONSTANT(bool, is_specialized = true); \ + static inline python::detail::new_reference adopt(PyObject* x) \ + { \ + return python::detail::new_reference(python::pytype_check((PyTypeObject*)get_pytype(), x)); \ + } \ + static bool check(PyObject* x) \ + { \ + return ::PyObject_IsInstance(x, (PyObject*)get_pytype()); \ + } \ + static manager* checked_downcast(PyObject* x) \ + { \ + return python::downcast((checked_downcast_impl)(x, (PyTypeObject*)get_pytype())); \ + } \ + static PyTypeObject const * get_pytype(); \ +} + +#endif // !BOOST_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED + diff --git a/boost/numpy/scalars.hpp b/boost/numpy/scalars.hpp new file mode 100644 index 00000000..8c778597 --- /dev/null +++ b/boost/numpy/scalars.hpp @@ -0,0 +1,60 @@ +#ifndef BOOST_NUMPY_SCALARS_HPP_INCLUDED +#define BOOST_NUMPY_SCALARS_HPP_INCLUDED + +/** + * @file boost/numpy/scalars.hpp + * @brief Object managers for array scalars (currently only numpy.void is implemented). + */ + +#include +#include +#include + +namespace boost +{ +namespace numpy +{ + +/** + * @brief A boost.python "object manager" (subclass of object) for numpy.void. + * + * @todo This could have a lot more functionality. + */ +class void_ : public python::object +{ + static python::detail::new_reference convert(object_cref arg, bool align); +public: + + /** + * @brief Construct a new array scalar with the given size and void dtype. + * + * Data is initialized to zero. One can create a standalone scalar object + * with a certain dtype "dt" with: + * @code + * void_ scalar = void_(dt.get_itemsize()).view(dt); + * @endcode + */ + explicit void_(Py_ssize_t size); + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(void_, object); + + /// @brief Return a view of the scalar with the given dtype. + void_ view(dtype const & dt) const; + + /// @brief Copy the scalar (deep for all non-object fields). + void_ copy() const; + +}; + +} // namespace boost::numpy + +namespace python +{ +namespace converter +{ +NUMPY_OBJECT_MANAGER_TRAITS(numpy::void_); +} // namespace boost::python::converter +} // namespace boost::python +} // namespace boost + +#endif // !BOOST_NUMPY_SCALARS_HPP_INCLUDED diff --git a/boost/numpy/ufunc.hpp b/boost/numpy/ufunc.hpp new file mode 100644 index 00000000..6effdadb --- /dev/null +++ b/boost/numpy/ufunc.hpp @@ -0,0 +1,209 @@ +#ifndef BOOST_NUMPY_UFUNC_HPP_INCLUDED +#define BOOST_NUMPY_UFUNC_HPP_INCLUDED + +/** + * @file boost/numpy/ufunc.hpp + * @brief Utilities to create ufunc-like broadcasting functions out of C++ functors. + */ + +#include +#include +#include +#include + +namespace boost +{ +namespace numpy +{ + +/** + * @brief A boost.python "object manager" (subclass of object) for PyArray_MultiIter. + * + * multi_iter is a Python object, but a very low-level one. It should generally only be used + * in loops of the form: + * @code + * while (iter.not_done()) { + * ... + * iter.next(); + * } + * @endcode + * + * @todo I can't tell if this type is exposed in Python anywhere; if it is, we should use that name. + * It's more dangerous than most object managers, however - maybe it actually belongs in + * a detail namespace? + */ +class multi_iter : public python::object +{ +public: + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, python::object); + + /// @brief Increment the iterator. + void next(); + + /// @brief Check if the iterator is at its end. + bool not_done() const; + + /// @brief Return a pointer to the element of the nth broadcasted array. + char * get_data(int n) const; + + /// @brief Return the number of dimensions of the broadcasted array expression. + int const get_nd() const; + + /// @brief Return the shape of the broadcasted array expression as an array of integers. + Py_intptr_t const * get_shape() const; + + /// @brief Return the shape of the broadcasted array expression in the nth dimension. + Py_intptr_t const shape(int n) const; + +}; + +/// @brief Construct a multi_iter over a single sequence or scalar object. +multi_iter make_multi_iter(python::object const & a1); + +/// @brief Construct a multi_iter by broadcasting two objects. +multi_iter make_multi_iter(python::object const & a1, python::object const & a2); + +/// @brief Construct a multi_iter by broadcasting three objects. +multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); + +/** + * @brief Helps wrap a C++ functor taking a single scalar argument as a broadcasting ufunc-like + * Python object. + * + * Typical usage looks like this: + * @code + * struct TimesPI + * { + * typedef double argument_type; + * typedef double result_type; + * double operator()(double input) const { return input * M_PI; } + * }; + * + * BOOST_PYTHON_MODULE(example) + * { + * class_< TimesPI >("TimesPI") + * .def("__call__", unary_ufunc::make()); + * } + * @endcode + * + */ +template +struct unary_ufunc +{ + + /** + * @brief A C++ function with object arguments that broadcasts its arguments before + * passing them to the underlying C++ functor. + */ + static python::object call(TUnaryFunctor & self, python::object const & input, python::object const & output) + { + dtype in_dtype = dtype::get_builtin(); + dtype out_dtype = dtype::get_builtin(); + ndarray in_array = from_object(input, in_dtype, ndarray::ALIGNED); + ndarray out_array = (output != python::object()) ? + from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE) + : zeros(in_array.get_nd(), in_array.get_shape(), out_dtype); + multi_iter iter = make_multi_iter(in_array, out_array); + while (iter.not_done()) + { + TArgument * argument = reinterpret_cast(iter.get_data(0)); + TResult * result = reinterpret_cast(iter.get_data(1)); + *result = self(*argument); + iter.next(); + } + return out_array.scalarize(); + } + + /** + * @brief Construct a boost.python function object from call() with reasonable keyword names. + * + * Users will often want to specify their own keyword names with the same signature, but this + * is a convenient shortcut. + */ + static python::object make() + { + namespace p = python; + return p::make_function(call, p::default_call_policies(), (p::arg("input"), p::arg("output")=p::object())); + } +}; + +/** + * @brief Helps wrap a C++ functor taking a pair of scalar arguments as a broadcasting ufunc-like + * Python object. + * + * Typical usage looks like this: + * @code + * struct CosSum + * { + * typedef double first_argument_type; + * typedef double second_argument_type; + * typedef double result_type; + * double operator()(double input1, double input2) const { return std::cos(input1 + input2); } + * }; + * + * BOOST_PYTHON_MODULE(example) + * { + * class_< CosSum >("CosSum") + * .def("__call__", binary_ufunc::make()); + * } + * @endcode + * + */ +template +struct binary_ufunc +{ + + static python::object + call(TBinaryFunctor & self, python::object const & input1, python::object const & input2, + python::object const & output) + { + dtype in1_dtype = dtype::get_builtin(); + dtype in2_dtype = dtype::get_builtin(); + dtype out_dtype = dtype::get_builtin(); + ndarray in1_array = from_object(input1, in1_dtype, ndarray::ALIGNED); + ndarray in2_array = from_object(input2, in2_dtype, ndarray::ALIGNED); + multi_iter iter = make_multi_iter(in1_array, in2_array); + ndarray out_array = (output != python::object()) + ? from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE) + : zeros(iter.get_nd(), iter.get_shape(), out_dtype); + iter = make_multi_iter(in1_array, in2_array, out_array); + while (iter.not_done()) + { + TArgument1 * argument1 = reinterpret_cast(iter.get_data(0)); + TArgument2 * argument2 = reinterpret_cast(iter.get_data(1)); + TResult * result = reinterpret_cast(iter.get_data(2)); + *result = self(*argument1, *argument2); + iter.next(); + } + return out_array.scalarize(); + } + + static python::object make() + { + namespace p = python; + return p::make_function(call, p::default_call_policies(), + (p::arg("input1"), p::arg("input2"), p::arg("output")=p::object())); + } + +}; + +} // namespace boost::numpy + +namespace python +{ +namespace converter +{ + +NUMPY_OBJECT_MANAGER_TRAITS(numpy::multi_iter); + +} // namespace boost::python::converter +} // namespace boost::python +} // namespace boost + +#endif // !BOOST_NUMPY_UFUNC_HPP_INCLUDED diff --git a/boost/python/numpy.hpp b/boost/python/numpy.hpp deleted file mode 100644 index c40859d8..00000000 --- a/boost/python/numpy.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_HPP_INCLUDED - -/** - * @file boost/python/numpy.hpp - * @brief Main public header file for boost.python.numpy. - */ - -#include -#include -#include -#include -#include -#include - -namespace boost { namespace python { -namespace numpy { - -/** - * @brief Initialize the Numpy C-API - * - * This must be called before using anything in boost.python.numpy; - * It should probably be the first line inside BOOST_PYTHON_MODULE. - * - * @internal This just calls the Numpy C-API functions "import_array()" - * and "import_ufunc()". - */ -void initialize(); - -} // namespace boost::python::numpy -}} // namespace boost::python - -#endif // !BOOST_PYTHON_NUMPY_HPP_INCLUDED diff --git a/boost/python/numpy/dtype.hpp b/boost/python/numpy/dtype.hpp deleted file mode 100644 index 35f39bba..00000000 --- a/boost/python/numpy/dtype.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_DTYPE_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_DTYPE_HPP_INCLUDED - -/** - * @file boost/python/numpy/dtype.hpp - * @brief Object manager for Python's numpy.dtype class. - */ - -#include -#include - -#include -#include - -namespace boost { namespace python { -namespace numpy { - -/** - * @brief A boost.python "object manager" (subclass of object) for numpy.dtype. - * - * @todo This could have a lot more interesting accessors. - */ -class dtype : public object { - static python::detail::new_reference convert(object_cref arg, bool align); -public: - - /// @brief Convert an arbitrary Python object to a data-type descriptor object. - template - explicit dtype(T arg, bool align=false) : object(convert(arg, align)) {} - - /** - * @brief Get the built-in numpy dtype associated with the given scalar template type. - * - * This is perhaps the most useful part of the numpy API: it returns the dtype object - * corresponding to a built-in C++ type. This should work for any integer or floating point - * type supported by numpy, and will also work for std::complex if - * sizeof(std::complex) == 2*sizeof(T). - * - * It can also be useful for users to add explicit specializations for POD structs - * that return field-based dtypes. - */ - template static dtype get_builtin(); - - /// @brief Return the size of the data type in bytes. - int get_itemsize() const; - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, object); - -}; - -} // namespace boost::python::numpy - -namespace converter { -NUMPY_OBJECT_MANAGER_TRAITS(python::numpy::dtype); -} // namespace boost::python::converter - -}} // namespace boost::python - -#endif // !BOOST_PYTHON_NUMPY_DTYPE_HPP_INCLUDED diff --git a/boost/python/numpy/invoke_matching.hpp b/boost/python/numpy/invoke_matching.hpp deleted file mode 100644 index 7a7f8f3c..00000000 --- a/boost/python/numpy/invoke_matching.hpp +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_INVOKE_MATCHING_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_INVOKE_MATCHING_HPP_INCLUDED - -/** - * @file boost/python/numpy/invoke_matching.hpp - * @brief Template invocation based on dtype matching. - */ - -#include -#include - -#include - -namespace boost { namespace python { namespace numpy { - -namespace detail { - -struct add_pointer_meta { - - template - struct apply { - typedef typename boost::add_pointer::type type; - }; - -}; - -struct dtype_template_match_found {}; -struct nd_template_match_found {}; - -template -struct dtype_template_invoker { - - template - void operator()(T *) const { - if (dtype::get_builtin() == m_dtype) { - m_func.template apply(); - throw dtype_template_match_found(); - } - } - - dtype_template_invoker(dtype const & dtype_, Function func) : - m_dtype(dtype_), m_func(func) {} - -private: - dtype const & m_dtype; - Function m_func; -}; - -template -struct dtype_template_invoker< boost::reference_wrapper > { - - template - void operator()(T *) const { - if (dtype::get_builtin() == m_dtype) { - m_func.template apply(); - throw dtype_template_match_found(); - } - } - - dtype_template_invoker(dtype const & dtype_, Function & func) : - m_dtype(dtype_), m_func(func) {} - -private: - dtype const & m_dtype; - Function & m_func; -}; - -template -struct nd_template_invoker { - - template - void operator()(boost::mpl::integral_c *) const { - if (m_nd == N) { - m_func.template apply(); - throw nd_template_match_found(); - } - } - - nd_template_invoker(int nd, Function func) : - m_nd(nd), m_func(func) {} - -private: - int m_nd; - Function m_func; -}; - -template -struct nd_template_invoker< boost::reference_wrapper > { - - template - void operator()(boost::mpl::integral_c *) const { - if (m_nd == N) { - m_func.template apply(); - throw nd_template_match_found(); - } - } - - nd_template_invoker(int nd, Function & func) : - m_nd(nd), m_func(func) {} - -private: - int m_nd; - Function & m_func; -}; - -} // namespace boost::python::numpy::detail - -template -void invoke_matching_nd(int nd, Function f) { - detail::nd_template_invoker invoker(nd, f); - try { - boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker); - } catch (detail::nd_template_match_found &) { - return; - } - PyErr_SetString(PyExc_TypeError, "number of dimensions not found in template list."); - throw_error_already_set(); -} - -template -void invoke_matching_dtype(dtype const & dtype_, Function f) { - detail::dtype_template_invoker invoker(dtype_, f); - try { - boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker); - } catch (detail::dtype_template_match_found &) { - return; - } - PyErr_SetString(PyExc_TypeError, "dtype not found in template list."); - throw_error_already_set(); -} - -namespace detail { - -template -struct array_template_invoker_wrapper_2 { - - template - void apply() const { - m_func.template apply(); - } - - array_template_invoker_wrapper_2(Function & func) : - m_func(func) {} - -private: - Function & m_func; -}; - -template -struct array_template_invoker_wrapper_1 { - - template - void apply() const { - invoke_matching_nd(m_nd, array_template_invoker_wrapper_2(m_func)); - } - - array_template_invoker_wrapper_1(int nd, Function & func) : - m_nd(nd), m_func(func) {} - -private: - int m_nd; - Function & m_func; -}; - -template -struct array_template_invoker_wrapper_1< DimSequence, boost::reference_wrapper > - : public array_template_invoker_wrapper_1< DimSequence, Function > -{ - array_template_invoker_wrapper_1(int nd, Function & func) : - array_template_invoker_wrapper_1< DimSequence, Function >(nd, func) {} -}; - -} // namespace boost::python::numpy::detail - -template -void invoke_matching_array(ndarray const & array_, Function f) { - detail::array_template_invoker_wrapper_1 wrapper(array_.get_nd(), f); - invoke_matching_dtype(array_.get_dtype(), wrapper); -} - - -}}} // namespace boost::python::numpy - -#endif // !BOOST_PYTHON_NUMPY_INVOKE_MATCHING_HPP_INCLUDED diff --git a/boost/python/numpy/matrix.hpp b/boost/python/numpy/matrix.hpp deleted file mode 100644 index 584023f0..00000000 --- a/boost/python/numpy/matrix.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_MATRIX_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_MATRIX_HPP_INCLUDED - -/** - * @file boost/python/numpy/matrix.hpp - * @brief Object manager for numpy.matrix. - */ - -#include -#include -#include - -namespace boost { namespace python { - -namespace numpy { - -/** - * @brief A boost.python "object manager" (subclass of object) for numpy.matrix. - * - * @internal numpy.matrix is defined in Python, so object_manager_traits::get_pytype() - * is implemented by importing numpy and getting the "matrix" attribute of the module. - * We then just hope that doesn't get destroyed while we need it, because if we put - * a dynamic python object in a static-allocated boost::python::object or handle<>, - * bad things happen when Python shuts down. I think this solution is safe, but I'd - * love to get that confirmed. - */ -class matrix : public ndarray { - static object construct(object_cref obj, dtype const & dt, bool copy); - static object construct(object_cref obj, bool copy); -public: - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(matrix, ndarray); - - /// @brief Equivalent to "numpy.matrix(obj,dt,copy)" in Python. - explicit matrix(object const & obj, dtype const & dt, bool copy=true) : - ndarray(extract(construct(obj, dt, copy))) {} - - /// @brief Equivalent to "numpy.matrix(obj,copy=copy)" in Python. - explicit matrix(object const & obj, bool copy=true) : - ndarray(extract(construct(obj, copy))) {} - - /// \brief Return a view of the matrix with the given dtype. - matrix view(dtype const & dt) const; - - /// \brief Copy the scalar (deep for all non-object fields). - matrix copy() const; - - /// \brief Transpose the matrix. - matrix transpose() const; - -}; - -} // namespace boost::python::numpy - -namespace converter { - -NUMPY_OBJECT_MANAGER_TRAITS(python::numpy::matrix); - -} // namespace boost::python::converter -}} // namespace boost::python - -#endif // !BOOST_PYTHON_NUMPY_MATRIX_HPP_INCLUDED diff --git a/boost/python/numpy/ndarray.hpp b/boost/python/numpy/ndarray.hpp deleted file mode 100644 index 0b53cc1f..00000000 --- a/boost/python/numpy/ndarray.hpp +++ /dev/null @@ -1,287 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_NDARRAY_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_NDARRAY_HPP_INCLUDED - -/** - * @file boost/python/numpy/ndarray.hpp - * @brief Object manager and various utilities for numpy.ndarray. - */ - -#include -#include -#include -#include -#include - -#include - -namespace boost { namespace python { -namespace numpy { - -/** - * @brief A boost.python "object manager" (subclass of object) for numpy.ndarray. - * - * @todo This could have a lot more functionality (like boost::python::numeric::array). - * Right now all that exists is what was needed to move raw data between C++ and Python. - */ -class ndarray : public object { - - /** - * @brief An internal struct that's byte-compatible with PyArrayObject. - * - * This is just a hack to allow inline access to this stuff while hiding numpy/arrayobject.h - * from the user. - */ - struct array_struct { - PyObject_HEAD - char * data; - int nd; - Py_intptr_t * shape; - Py_intptr_t * strides; - PyObject * base; - PyObject * descr; - int flags; - PyObject * weakreflist; - }; - - /// @brief Return the held Python object as an array_struct. - array_struct * get_struct() const { return reinterpret_cast(this->ptr()); } - -public: - - /** - * @brief Enum to represent (some) of Numpy's internal flags. - * - * These don't match the actual Numpy flag values; we can't get those without including - * numpy/arrayobject.h or copying them directly. That's very unfortunate. - * - * @todo I'm torn about whether this should be an enum. It's very convenient to not - * make these simple integer values for overloading purposes, but the need to - * define every possible combination and custom bitwise operators is ugly. - */ - enum bitflag { - NONE=0x0, C_CONTIGUOUS=0x1, F_CONTIGUOUS=0x2, V_CONTIGUOUS=0x1|0x2, - ALIGNED=0x4, WRITEABLE=0x8, BEHAVED=0x4|0x8, - CARRAY_RO=0x1|0x4, CARRAY=0x1|0x4|0x8, CARRAY_MIS=0x1|0x8, - FARRAY_RO=0x2|0x4, FARRAY=0x2|0x4|0x8, FARRAY_MIS=0x2|0x8, - UPDATE_ALL=0x1|0x2|0x4, VARRAY=0x1|0x2|0x8, ALL=0x1|0x2|0x4|0x8 - }; - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(ndarray, object); - - /// @brief Return a view of the scalar with the given dtype. - ndarray view(dtype const & dt) const; - - /// @brief Copy the scalar (deep for all non-object fields). - ndarray copy() const; - - /// @brief Return the size of the nth dimension. - int const shape(int n) const { return get_shape()[n]; } - - /// @brief Return the stride of the nth dimension. - int const strides(int n) const { return get_strides()[n]; } - - /** - * @brief Return the array's raw data pointer. - * - * This returns char so stride math works properly on it. It's pretty much - * expected that the user will have to reinterpret_cast it. - */ - char * get_data() const { return get_struct()->data; } - - /// @brief Return the array's data-type descriptor object. - dtype get_dtype() const; - - /// @brief Return the object that owns the array's data, or None if the array owns its own data. - object get_base() const; - - /// @brief Set the object that owns the array's data. Use with care. - void set_base(object const & base); - - /// @brief Return the shape of the array as an array of integers (length == get_nd()). - Py_intptr_t const * get_shape() const { return get_struct()->shape; } - - /// @brief Return the stride of the array as an array of integers (length == get_nd()). - Py_intptr_t const * get_strides() const { return get_struct()->strides; } - - /// @brief Return the number of array dimensions. - int const get_nd() const { return get_struct()->nd; } - - /// @brief Return the array flags. - bitflag const get_flags() const; - - /// @brief Reverse the dimensions of the array. - ndarray transpose() const; - - /// @brief Eliminate any unit-sized dimensions. - ndarray squeeze() const; - - /// @brief Equivalent to self.reshape(*shape) in Python. - ndarray reshape(tuple const & shape) const; - - /** - * @brief If the array contains only a single element, return it as an array scalar; otherwise return - * the array. - * - * @internal This is simply a call to PyArray_Return(); - */ - object scalarize() const; -}; - -/** - * @brief Construct a new array with the given shape and data type, with data initialized to zero. - */ -ndarray zeros(tuple const & shape, dtype const & dt); -ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); - -/** - * @brief Construct a new array with the given shape and data type, with data left uninitialized. - */ -ndarray empty(tuple const & shape, dtype const & dt); -ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); - -/** - * @brief Construct a new array from an arbitrary Python sequence. - * - * @todo This does't seem to handle ndarray subtypes the same way that "numpy.array" does in Python. - */ -ndarray array(object const & obj); -ndarray array(object const & obj, dtype const & dt); - -namespace detail { - -ndarray from_data_impl( - void * data, - dtype const & dt, - std::vector const & shape, - std::vector const & strides, - object const & owner, - bool writeable -); - -template -ndarray from_data_impl( - void * data, - dtype const & dt, - Container shape, - Container strides, - object const & owner, - bool writeable, - typename boost::enable_if< boost::is_integral >::type * enabled = NULL -) { - std::vector shape_(shape.begin(),shape.end()); - std::vector strides_(strides.begin(), strides.end()); - return from_data_impl(data, dt, shape_, strides_, owner, writeable); -} - -ndarray from_data_impl( - void * data, - dtype const & dt, - object const & shape, - object const & strides, - object const & owner, - bool writeable -); - -} // namespace boost::python::numpy::detail - -/** - * @brief Construct a new ndarray object from a raw pointer. - * - * @param[in] data Raw pointer to the first element of the array. - * @param[in] dt Data type descriptor. Often retrieved with dtype::get_builtin(). - * @param[in] shape Shape of the array as STL container of integers; must have begin() and end(). - * @param[in] strides Shape of the array as STL container of integers; must have begin() and end(). - * @param[in] owner An arbitray Python object that owns that data pointer. The array object will - * keep a reference to the object, and decrement it's reference count when the - * array goes out of scope. Pass None at your own peril. - * - * @todo Should probably take ranges of iterators rather than actual container objects. - */ -template -inline ndarray from_data( - void * data, - dtype const & dt, - Container shape, - Container strides, - object const & owner -) { - return numpy::detail::from_data_impl(data, dt, shape, strides, owner, true); -} - -/** - * @brief Construct a new ndarray object from a raw pointer. - * - * @param[in] data Raw pointer to the first element of the array. - * @param[in] dt Data type descriptor. Often retrieved with dtype::get_builtin(). - * @param[in] shape Shape of the array as STL container of integers; must have begin() and end(). - * @param[in] strides Shape of the array as STL container of integers; must have begin() and end(). - * @param[in] owner An arbitray Python object that owns that data pointer. The array object will - * keep a reference to the object, and decrement it's reference count when the - * array goes out of scope. Pass None at your own peril. - * - * This overload takes a const void pointer and sets the "writeable" flag of the array to false. - * - * @todo Should probably take ranges of iterators rather than actual container objects. - */ -template -inline ndarray from_data( - void const * data, - dtype const & dt, - Container shape, - Container strides, - object const & owner -) { - return numpy::detail::from_data_impl(const_cast(data), dt, shape, strides, owner, false); -} - -/** - * @brief Transform an arbitrary object into a numpy array with the given requirements. - * - * @param[in] obj An arbitrary python object to convert. Arrays that meet the requirements - * will be passed through directly. - * @param[in] dt Data type descriptor. Often retrieved with dtype::get_builtin(). - * @param[in] nd_min Minimum number of dimensions. - * @param[in] nd_max Maximum number of dimensions. - * @param[in] flags Bitwise OR of flags specifying additional requirements. - */ -ndarray from_object(object const & obj, dtype const & dt, - int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); - -inline ndarray from_object(object const & obj, dtype const & dt, - int nd, ndarray::bitflag flags=ndarray::NONE) { - return from_object(obj, dt, nd, nd, flags); -} - -inline ndarray from_object(object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE) { - return from_object(obj, dt, 0, 0, flags); -} - -ndarray from_object(object const & obj, int nd_min, int nd_max, - ndarray::bitflag flags=ndarray::NONE); - -inline ndarray from_object(object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE) { - return from_object(obj, nd, nd, flags); -} - -inline ndarray from_object(object const & obj, ndarray::bitflag flags=ndarray::NONE) { - return from_object(obj, 0, 0, flags); -} - -inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) { - return ndarray::bitflag(int(a) | int(b)); -} - -inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) { - return ndarray::bitflag(int(a) & int(b)); -} - -} // namespace boost::python::numpy - -namespace converter { - -NUMPY_OBJECT_MANAGER_TRAITS(python::numpy::ndarray); - -} // namespace boost::python::converter -}} // namespace boost::python - -#endif // !BOOST_PYTHON_NUMPY_NDARRAY_HPP_INCLUDED diff --git a/boost/python/numpy/numpy_object_mgr_traits.hpp b/boost/python/numpy/numpy_object_mgr_traits.hpp deleted file mode 100644 index aa9121d8..00000000 --- a/boost/python/numpy/numpy_object_mgr_traits.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED - -/** - * @file boost/python/numpy/numpy_object_mgr_traits.hpp - * @brief Macro that specializes object_manager_traits by requiring a - * source-file implementation of get_pytype(). - */ - -#define NUMPY_OBJECT_MANAGER_TRAITS(manager) \ - template <> \ - struct object_manager_traits { \ - BOOST_STATIC_CONSTANT(bool, is_specialized = true); \ - static inline python::detail::new_reference adopt(PyObject* x) { \ - return python::detail::new_reference(python::pytype_check((PyTypeObject*)get_pytype(), x)); \ - } \ - static bool check(PyObject* x) { \ - return ::PyObject_IsInstance(x, (PyObject*)get_pytype()); \ - } \ - static manager* checked_downcast(PyObject* x) { \ - return python::downcast((checked_downcast_impl)(x, (PyTypeObject*)get_pytype())); \ - } \ - static PyTypeObject const * get_pytype(); \ - } - -#endif // !BOOST_PYTHON_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED - diff --git a/boost/python/numpy/scalars.hpp b/boost/python/numpy/scalars.hpp deleted file mode 100644 index 975d5f48..00000000 --- a/boost/python/numpy/scalars.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_SCALARS_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_SCALARS_HPP_INCLUDED - -/** - * @file boost/python/numpy/scalars.hpp - * @brief Object managers for array scalars (currently only numpy.void is implemented). - */ - -#include -#include -#include - -namespace boost { namespace python { -namespace numpy { - -/** - * @brief A boost.python "object manager" (subclass of object) for numpy.void. - * - * @todo This could have a lot more functionality. - */ -class void_ : public object { - static python::detail::new_reference convert(object_cref arg, bool align); -public: - - /** - * @brief Construct a new array scalar with the given size and void dtype. - * - * Data is initialized to zero. One can create a standalone scalar object - * with a certain dtype "dt" with: - * @code - * void_ scalar = void_(dt.get_itemsize()).view(dt); - * @endcode - */ - explicit void_(Py_ssize_t size); - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(void_, object); - - /// @brief Return a view of the scalar with the given dtype. - void_ view(dtype const & dt) const; - - /// @brief Copy the scalar (deep for all non-object fields). - void_ copy() const; - -}; - -} // namespace boost::python::numpy - -namespace converter { - -NUMPY_OBJECT_MANAGER_TRAITS(python::numpy::void_); - -} // namespace boost::python::converter -}} // namespace boost::python - -#endif // !BOOST_PYTHON_NUMPY_SCALARS_HPP_INCLUDED diff --git a/boost/python/numpy/ufunc.hpp b/boost/python/numpy/ufunc.hpp deleted file mode 100644 index f518c87b..00000000 --- a/boost/python/numpy/ufunc.hpp +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef BOOST_PYTHON_NUMPY_UFUNC_HPP_INCLUDED -#define BOOST_PYTHON_NUMPY_UFUNC_HPP_INCLUDED - -/** - * @file boost/python/numpy/ufunc.hpp - * @brief Utilities to create ufunc-like broadcasting functions out of C++ functors. - */ - -#include -#include -#include -#include - -namespace boost { namespace python { -namespace numpy { - -/** - * @brief A boost.python "object manager" (subclass of object) for PyArray_MultiIter. - * - * multi_iter is a Python object, but a very low-level one. It should generally only be used - * in loops of the form: - * @code - * while (iter.not_done()) { - * ... - * iter.next(); - * } - * @endcode - * - * @todo I can't tell if this type is exposed in Python anywhere; if it is, we should use that name. - * It's more dangerous than most object managers, however - maybe it actually belongs in - * a detail namespace? - */ -class multi_iter : public object { -public: - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, object); - - /// @brief Increment the iterator. - void next(); - - /// @brief Check if the iterator is at its end. - bool not_done() const; - - /// @brief Return a pointer to the element of the nth broadcasted array. - char * get_data(int n) const; - - /// @brief Return the number of dimensions of the broadcasted array expression. - int const get_nd() const; - - /// @brief Return the shape of the broadcasted array expression as an array of integers. - Py_intptr_t const * get_shape() const; - - /// @brief Return the shape of the broadcasted array expression in the nth dimension. - Py_intptr_t const shape(int n) const; - -}; - -/// @brief Construct a multi_iter over a single sequence or scalar object. -multi_iter make_multi_iter(object const & a1); - -/// @brief Construct a multi_iter by broadcasting two objects. -multi_iter make_multi_iter(object const & a1, object const & a2); - -/// @brief Construct a multi_iter by broadcasting three objects. -multi_iter make_multi_iter(object const & a1, object const & a2, object const & a3); - -/** - * @brief Helps wrap a C++ functor taking a single scalar argument as a broadcasting ufunc-like - * Python object. - * - * Typical usage looks like this: - * @code - * struct TimesPI { - * typedef double argument_type; - * typedef double result_type; - * double operator()(double input) const { return input * M_PI; } - * }; - * - * BOOST_PYTHON_MODULE(example) { - * class_< TimesPI >("TimesPI") - * .def("__call__", unary_ufunc::make()) - * ; - * } - * @endcode - * - */ -template -struct unary_ufunc { - - /** - * @brief A C++ function with object arguments that broadcasts its arguments before - * passing them to the underlying C++ functor. - */ - static object call(TUnaryFunctor & self, object const & input, object const & output) { - dtype in_dtype = dtype::get_builtin(); - dtype out_dtype = dtype::get_builtin(); - ndarray in_array = from_object(input, in_dtype, ndarray::ALIGNED); - ndarray out_array = (output != object()) ? - from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE) - : zeros(in_array.get_nd(), in_array.get_shape(), out_dtype); - multi_iter iter = make_multi_iter(in_array, out_array); - while (iter.not_done()) { - TArgument * argument = reinterpret_cast(iter.get_data(0)); - TResult * result = reinterpret_cast(iter.get_data(1)); - *result = self(*argument); - iter.next(); - } - return out_array.scalarize(); - } - - /** - * @brief Construct a boost.python function object from call() with reasonable keyword names. - * - * Users will often want to specify their own keyword names with the same signature, but this - * is a convenient shortcut. - */ - static object make() { - return make_function(call, default_call_policies(), (arg("input"), arg("output")=object())); - } - -}; - -/** - * @brief Helps wrap a C++ functor taking a pair of scalar arguments as a broadcasting ufunc-like - * Python object. - * - * Typical usage looks like this: - * @code - * struct CosSum { - * typedef double first_argument_type; - * typedef double second_argument_type; - * typedef double result_type; - * double operator()(double input1, double input2) const { return std::cos(input1 + input2); } - * }; - * - * BOOST_PYTHON_MODULE(example) { - * class_< CosSum >("CosSum") - * .def("__call__", binary_ufunc::make()) - * ; - * } - * @endcode - * - */ -template -struct binary_ufunc { - - static object call(TBinaryFunctor & self, object const & input1, object const & input2, - object const & output) - { - dtype in1_dtype = dtype::get_builtin(); - dtype in2_dtype = dtype::get_builtin(); - dtype out_dtype = dtype::get_builtin(); - ndarray in1_array = from_object(input1, in1_dtype, ndarray::ALIGNED); - ndarray in2_array = from_object(input2, in2_dtype, ndarray::ALIGNED); - multi_iter iter = make_multi_iter(in1_array, in2_array); - ndarray out_array = (output != object()) ? - from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE) - : zeros(iter.get_nd(), iter.get_shape(), out_dtype); - iter = make_multi_iter(in1_array, in2_array, out_array); - while (iter.not_done()) { - TArgument1 * argument1 = reinterpret_cast(iter.get_data(0)); - TArgument2 * argument2 = reinterpret_cast(iter.get_data(1)); - TResult * result = reinterpret_cast(iter.get_data(2)); - *result = self(*argument1, *argument2); - iter.next(); - } - return out_array.scalarize(); - } - - static object make() { - return make_function( - call, default_call_policies(), - (arg("input1"), arg("input2"), arg("output")=object()) - ); - } - -}; - -} // namespace boost::python::numpy - -namespace converter { - -NUMPY_OBJECT_MANAGER_TRAITS(python::numpy::multi_iter); - -} // namespace boost::python::converter -}} // namespace boost::python - -#endif // !BOOST_PYTHON_NUMPY_UFUNC_HPP_INCLUDED diff --git a/libs/python/numpy/src/Jamfile b/libs/numpy/src/Jamfile similarity index 100% rename from libs/python/numpy/src/Jamfile rename to libs/numpy/src/Jamfile diff --git a/libs/numpy/src/SConscript b/libs/numpy/src/SConscript new file mode 100644 index 00000000..ce44eec5 --- /dev/null +++ b/libs/numpy/src/SConscript @@ -0,0 +1,4 @@ +Import("boost_numpy_env") +lib = boost_numpy_env.SharedLibrary("boost_numpy", Glob("*.cpp")) + +Return("lib") diff --git a/libs/numpy/src/dtype.cpp b/libs/numpy/src/dtype.cpp new file mode 100644 index 00000000..3c5989f6 --- /dev/null +++ b/libs/numpy/src/dtype.cpp @@ -0,0 +1,102 @@ +#define BOOST_NUMPY_INTERNAL +#include + +#define NUMPY_DTYPE_TRAITS_BUILTIN(ctype,code) \ +template <> struct dtype_traits \ +{ \ + static dtype get() \ + { \ + return dtype(python::detail::new_reference \ + (reinterpret_cast(PyArray_DescrFromType(code)))); \ + } \ +}; \ +template dtype dtype::get_builtin() + +#define NUMPY_DTYPE_TRAITS_COMPLEX(creal, ctype, code) \ +template <> struct dtype_traits< std::complex > \ +{ \ + static dtype get() \ + { \ + if (sizeof(ctype) != sizeof(std::complex)) \ + { \ + PyErr_SetString(PyExc_TypeError, "Cannot reinterpret std::complex as T[2]"); \ + python::throw_error_already_set(); \ + } \ + return dtype(python::detail::new_reference \ + (reinterpret_cast(PyArray_DescrFromType(code)))); \ + } \ +}; \ +template dtype dtype::get_builtin< std::complex >() + +namespace boost +{ +namespace python +{ +namespace converter +{ +NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArrayDescr_Type, numpy::dtype) +} // namespace boost::python::converter +} // namespace boost::python + +namespace numpy +{ + +template struct dtype_traits; + +python::detail::new_reference dtype::convert(python::object const & arg, bool align) +{ + PyArray_Descr* obj=NULL; + if (align) + { + if (PyArray_DescrAlignConverter(arg.ptr(), &obj) < 0) + python::throw_error_already_set(); + } + else + { + if (PyArray_DescrConverter(arg.ptr(), &obj) < 0) + python::throw_error_already_set(); + } + return python::detail::new_reference(reinterpret_cast(obj)); +} + +int dtype::get_itemsize() const { return reinterpret_cast(ptr())->elsize;} + +template +dtype dtype::get_builtin() { return dtype_traits::get(); } + +NUMPY_DTYPE_TRAITS_BUILTIN(bool, NPY_BOOL); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_ubyte, NPY_UBYTE); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_byte, NPY_BYTE); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_ushort, NPY_USHORT); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_short, NPY_SHORT); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_uint, NPY_UINT); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_int, NPY_INT); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_ulong, NPY_ULONG); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_long, NPY_LONG); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_longlong, NPY_LONGLONG); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_float, NPY_FLOAT); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_double, NPY_DOUBLE); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_longdouble, NPY_LONGDOUBLE); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_cfloat, NPY_CFLOAT); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_cdouble, NPY_CDOUBLE); +NUMPY_DTYPE_TRAITS_BUILTIN(npy_clongdouble, NPY_CLONGDOUBLE); +NUMPY_DTYPE_TRAITS_COMPLEX(float, npy_cfloat, NPY_CFLOAT); +NUMPY_DTYPE_TRAITS_COMPLEX(double, npy_cdouble, NPY_CDOUBLE); +NUMPY_DTYPE_TRAITS_COMPLEX(long double, npy_clongdouble, NPY_CLONGDOUBLE); + +#if 0 +template <> struct dtype_traits +{ + static dtype get() + { + if (sizeof(bool) == sizeof(npy_ubyte)) return dtype_traits::get(); + if (sizeof(bool) == sizeof(npy_bool)) return dtype_traits::get(); + PyErr_SetString(PyExc_TypeError, "Cannot determine numpy dtype corresponding to C++ bool."); + python::throw_error_already_set(); + } +}; +template dtype dtype::get_builtin(); +#endif + +} // namespace boost::numpy +} // namespace boost diff --git a/libs/numpy/src/matrix.cpp b/libs/numpy/src/matrix.cpp new file mode 100644 index 00000000..c2c57b61 --- /dev/null +++ b/libs/numpy/src/matrix.cpp @@ -0,0 +1,63 @@ +#define BOOST_NUMPY_INTERNAL +#include +#include + +namespace boost +{ +namespace numpy +{ +namespace detail +{ +inline python::object get_matrix_type() +{ + python::object module = python::import("numpy"); + return module.attr("matrix"); +} +} // namespace boost::numpy::detail +} // namespace boost::numpy + +namespace python +{ +namespace converter +{ + +PyTypeObject const * object_manager_traits::get_pytype() +{ + return reinterpret_cast(numpy::detail::get_matrix_type().ptr()); +} + +} // namespace boost::python::converter +} // namespace boost::python + +namespace numpy +{ + +python::object matrix::construct(python::object const & obj, dtype const & dt, bool copy) +{ + return numpy::detail::get_matrix_type()(obj, dt, copy); +} + +python::object matrix::construct(python::object const & obj, bool copy) +{ + return numpy::detail::get_matrix_type()(obj, object(), copy); +} + +matrix matrix::view(dtype const & dt) const +{ + return matrix(python::detail::new_reference + (PyObject_CallMethod(this->ptr(), const_cast("view"), const_cast("O"), dt.ptr()))); +} + +matrix matrix::copy() const +{ + return matrix(python::detail::new_reference + (PyObject_CallMethod(this->ptr(), const_cast("copy"), const_cast("")))); +} + +matrix matrix::transpose() const +{ + return matrix(python::extract(ndarray::transpose())); +} + +} // namespace boost::numpy +} // namespace boost diff --git a/libs/numpy/src/ndarray.cpp b/libs/numpy/src/ndarray.cpp new file mode 100644 index 00000000..f934ef06 --- /dev/null +++ b/libs/numpy/src/ndarray.cpp @@ -0,0 +1,269 @@ +#define BOOST_NUMPY_INTERNAL +#include + +namespace boost +{ +namespace python +{ +namespace converter +{ +NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArray_Type, numpy::ndarray) +} // namespace boost::python::converter +} // namespace boost::python + +namespace numpy +{ +namespace detail +{ + +ndarray::bitflag numpy_to_bitflag(int const f) +{ + ndarray::bitflag r = ndarray::NONE; + if (f & NPY_C_CONTIGUOUS) r = (r | ndarray::C_CONTIGUOUS); + if (f & NPY_F_CONTIGUOUS) r = (r | ndarray::F_CONTIGUOUS); + if (f & NPY_ALIGNED) r = (r | ndarray::ALIGNED); + if (f & NPY_WRITEABLE) r = (r | ndarray::WRITEABLE); + return r; +} + +int const bitflag_to_numpy(ndarray::bitflag f) +{ + int r = 0; + if (f & ndarray::C_CONTIGUOUS) r |= NPY_C_CONTIGUOUS; + if (f & ndarray::F_CONTIGUOUS) r |= NPY_F_CONTIGUOUS; + if (f & ndarray::ALIGNED) r |= NPY_ALIGNED; + if (f & ndarray::WRITEABLE) r |= NPY_WRITEABLE; + return r; +} + +bool is_c_contiguous(std::vector const & shape, + std::vector const & strides, + int itemsize) +{ + std::vector::const_reverse_iterator j = strides.rbegin(); + int total = itemsize; + for (std::vector::const_reverse_iterator i = shape.rbegin(); i != shape.rend(); ++i, ++j) + { + if (total != *j) return false; + total *= (*i); + } + return true; +} + +bool is_f_contiguous(std::vector const & shape, + std::vector const & strides, + int itemsize) +{ + std::vector::const_iterator j = strides.begin(); + int total = itemsize; + for (std::vector::const_iterator i = shape.begin(); i != shape.end(); ++i, ++j) + { + if (total != *j) return false; + total *= (*i); + } + return true; +} + +bool is_aligned(std::vector const & strides, + int itemsize) +{ + for (std::vector::const_iterator i = strides.begin(); i != strides.end(); ++i) + { + if (*i % itemsize) return false; + } + return true; +} + +inline PyArray_Descr * incref_dtype(dtype const & dt) +{ + Py_INCREF(dt.ptr()); + return reinterpret_cast(dt.ptr()); +} + +ndarray from_data_impl(void * data, + dtype const & dt, + python::object const & shape, + python::object const & strides, + python::object const & owner, + bool writeable) +{ + std::vector shape_(len(shape)); + std::vector strides_(len(strides)); + if (shape_.size() != strides_.size()) + { + PyErr_SetString(PyExc_ValueError, "Length of shape and strides arrays do not match."); + python::throw_error_already_set(); + } + for (std::size_t i = 0; i < shape_.size(); ++i) + { + shape_[i] = python::extract(shape[i]); + strides_[i] = python::extract(strides[i]); + } + return from_data_impl(data, dt, shape_, strides_, owner, writeable); +} + +ndarray from_data_impl(void * data, + dtype const & dt, + std::vector const & shape, + std::vector const & strides, + python::object const & owner, + bool writeable) +{ + if (shape.size() != strides.size()) + { + PyErr_SetString(PyExc_ValueError, "Length of shape and strides arrays do not match."); + python::throw_error_already_set(); + } + int itemsize = dt.get_itemsize(); + int flags = 0; + if (writeable) flags |= NPY_WRITEABLE; + if (is_c_contiguous(shape, strides, itemsize)) flags |= NPY_C_CONTIGUOUS; + if (is_f_contiguous(shape, strides, itemsize)) flags |= NPY_F_CONTIGUOUS; + if (is_aligned(strides, itemsize)) flags |= NPY_ALIGNED; + ndarray r(python::detail::new_reference + (PyArray_NewFromDescr(&PyArray_Type, + incref_dtype(dt), + shape.size(), + const_cast(&shape.front()), + const_cast(&strides.front()), + data, + flags, + NULL))); + r.set_base(owner); + return r; +} + +} // namespace detail + +ndarray ndarray::view(dtype const & dt) const +{ + return ndarray(python::detail::new_reference + (PyObject_CallMethod(this->ptr(), const_cast("view"), const_cast("O"), dt.ptr()))); +} + +ndarray ndarray::copy() const +{ + return ndarray(python::detail::new_reference + (PyObject_CallMethod(this->ptr(), const_cast("copy"), const_cast("")))); +} + +dtype ndarray::get_dtype() const +{ + return dtype(python::detail::borrowed_reference(get_struct()->descr)); +} + +python::object ndarray::get_base() const +{ + if (get_struct()->base == NULL) return object(); + return python::object(python::detail::borrowed_reference(get_struct()->base)); +} + +void ndarray::set_base(object const & base) +{ + Py_XDECREF(get_struct()->base); + if (base != object()) + { + Py_INCREF(base.ptr()); + get_struct()->base = base.ptr(); + } + else + { + get_struct()->base = NULL; + } +} + +ndarray::bitflag const ndarray::get_flags() const +{ + return numpy::detail::numpy_to_bitflag(get_struct()->flags); +} + +ndarray ndarray::transpose() const +{ + return ndarray(python::detail::new_reference + (PyArray_Transpose(reinterpret_cast(this->ptr()), NULL))); +} + +ndarray ndarray::squeeze() const +{ + return ndarray(python::detail::new_reference + (PyArray_Squeeze(reinterpret_cast(this->ptr())))); +} + +ndarray ndarray::reshape(python::tuple const & shape) const +{ + return ndarray(python::detail::new_reference + (PyArray_Reshape(reinterpret_cast(this->ptr()), shape.ptr()))); +} + +python::object ndarray::scalarize() const +{ + Py_INCREF(ptr()); + return python::object(python::detail::new_reference(PyArray_Return(reinterpret_cast(ptr())))); +} + +ndarray zeros(python::tuple const & shape, dtype const & dt) +{ + int nd = len(shape); + Py_intptr_t dims[nd]; + for (int n=0; n(shape[n]); + return ndarray(python::detail::new_reference + (PyArray_Zeros(nd, dims, detail::incref_dtype(dt), 0))); +} + +ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt) +{ + return ndarray(python::detail::new_reference + (PyArray_Zeros(nd, const_cast(shape), detail::incref_dtype(dt), 0))); +} + +ndarray empty(python::tuple const & shape, dtype const & dt) +{ + int nd = len(shape); + Py_intptr_t dims[nd]; + for (int n=0; n(shape[n]); + return ndarray(python::detail::new_reference + (PyArray_Empty(nd, dims, detail::incref_dtype(dt), 0))); +} + +ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt) +{ + return ndarray(python::detail::new_reference + (PyArray_Empty(nd, const_cast(shape), detail::incref_dtype(dt), 0))); +} + +ndarray array(python::object const & obj) +{ + return ndarray(python::detail::new_reference + (PyArray_FromAny(obj.ptr(), NULL, 0, 0, NPY_ENSUREARRAY, NULL))); +} + +ndarray array(python::object const & obj, dtype const & dt) +{ + return ndarray(python::detail::new_reference + (PyArray_FromAny(obj.ptr(), detail::incref_dtype(dt), 0, 0, NPY_ENSUREARRAY, NULL))); +} + +ndarray from_object(python::object const & obj, dtype const & dt, int nd_min, int nd_max, ndarray::bitflag flags) +{ + int requirements = detail::bitflag_to_numpy(flags); + return ndarray(python::detail::new_reference + (PyArray_FromAny(obj.ptr(), + detail::incref_dtype(dt), + nd_min, nd_max, + requirements, + NULL))); +} + +ndarray from_object(python::object const & obj, int nd_min, int nd_max, ndarray::bitflag flags) +{ + int requirements = detail::bitflag_to_numpy(flags); + return ndarray(python::detail::new_reference + (PyArray_FromAny(obj.ptr(), + NULL, + nd_min, nd_max, + requirements, + NULL))); +} + +} +} diff --git a/libs/numpy/src/numpy.cpp b/libs/numpy/src/numpy.cpp new file mode 100644 index 00000000..9898a37f --- /dev/null +++ b/libs/numpy/src/numpy.cpp @@ -0,0 +1,16 @@ +#define BOOST_NUMPY_INTERNAL_MAIN +#include + +namespace boost +{ +namespace numpy +{ + +void initialize() +{ + import_array(); + import_ufunc(); +} + +} +} diff --git a/libs/numpy/src/scalars.cpp b/libs/numpy/src/scalars.cpp new file mode 100644 index 00000000..9eb59727 --- /dev/null +++ b/libs/numpy/src/scalars.cpp @@ -0,0 +1,35 @@ +#define BOOST_NUMPY_INTERNAL +#include + +namespace boost +{ +namespace python +{ +namespace converter +{ +NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyVoidArrType_Type, numpy::void_) +} // namespace boost::python::converter +} // namespace boost::python + +namespace numpy +{ + +void_::void_(Py_ssize_t size) + : object(python::detail::new_reference + (PyObject_CallFunction((PyObject*)&PyVoidArrType_Type, const_cast("i"), size))) +{} + +void_ void_::view(dtype const & dt) const +{ + return void_(python::detail::new_reference + (PyObject_CallMethod(this->ptr(), const_cast("view"), const_cast("O"), dt.ptr()))); +} + +void_ void_::copy() const +{ + return void_(python::detail::new_reference + (PyObject_CallMethod(this->ptr(), const_cast("copy"), const_cast("")))); +} + +} +} diff --git a/libs/numpy/src/ufunc.cpp b/libs/numpy/src/ufunc.cpp new file mode 100644 index 00000000..f2dba942 --- /dev/null +++ b/libs/numpy/src/ufunc.cpp @@ -0,0 +1,64 @@ +#define BOOST_NUMPY_INTERNAL +#include +#include + +namespace boost +{ +namespace python +{ +namespace converter +{ +NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArrayMultiIter_Type, numpy::multi_iter) +} // namespace boost::python::converter +} // namespace boost::python + +namespace numpy +{ + +multi_iter make_multi_iter(python::object const & a1) +{ + return multi_iter(python::detail::new_reference(PyArray_MultiIterNew(1, a1.ptr()))); +} + +multi_iter make_multi_iter(python::object const & a1, python::object const & a2) +{ + return multi_iter(python::detail::new_reference(PyArray_MultiIterNew(2, a1.ptr(), a2.ptr()))); +} + +multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3) +{ + return multi_iter(python::detail::new_reference(PyArray_MultiIterNew(3, a1.ptr(), a2.ptr(), a3.ptr()))); +} + +void multi_iter::next() +{ + PyArray_MultiIter_NEXT(ptr()); +} + +bool multi_iter::not_done() const +{ + return PyArray_MultiIter_NOTDONE(ptr()); +} + +char * multi_iter::get_data(int i) const +{ + return reinterpret_cast(PyArray_MultiIter_DATA(ptr(), i)); +} + +int const multi_iter::get_nd() const +{ + return reinterpret_cast(ptr())->nd; +} + +Py_intptr_t const * multi_iter::get_shape() const +{ + return reinterpret_cast(ptr())->dimensions; +} + +Py_intptr_t const multi_iter::shape(int n) const +{ + return reinterpret_cast(ptr())->dimensions[n]; +} + +} +} diff --git a/libs/python/numpy/test/Jamfile b/libs/numpy/test/Jamfile similarity index 100% rename from libs/python/numpy/test/Jamfile rename to libs/numpy/test/Jamfile diff --git a/libs/numpy/test/SConscript b/libs/numpy/test/SConscript new file mode 100644 index 00000000..114112ac --- /dev/null +++ b/libs/numpy/test/SConscript @@ -0,0 +1,12 @@ +Import("boost_numpy_env") + +test = [] + +for name in ("ufunc", "templates"): + mod = boost_numpy_env.SharedLibrary("%s_mod" % name, "%s_mod.cpp" % name, SHLIBPREFIX="", + LIBS="boost_python_numpy") + test.extend( + boost_numpy_env.PythonUnitTest("%s.py" % name, mod) + ) + +Return("test") diff --git a/libs/python/numpy/test/indexing.py b/libs/numpy/test/indexing.py similarity index 82% rename from libs/python/numpy/test/indexing.py rename to libs/numpy/test/indexing.py index 0533d0f2..3ef8797a 100644 --- a/libs/python/numpy/test/indexing.py +++ b/libs/numpy/test/indexing.py @@ -29,11 +29,11 @@ class TestIndexing(unittest.TestCase): numpy.testing.assert_equal(indexing_mod.indexarray(x,chk),chk) chk = numpy.array([[0,1],[2,3]]) numpy.testing.assert_equal(indexing_mod.indexarray(x,chk),chk) -# x = numpy.arange(9).reshape(3,3) -# y = numpy.array([0,1]) -# z = numpy.array([0,2]) -# chk = numpy.array([0,5]) -# numpy.testing.assert_equal(indexing_mod.indexarray(x,y,z),chk) # This throws an assertion error, indicates shape mismatch + x = numpy.arange(9).reshape(3,3) + y = numpy.array([0,1]) + z = numpy.array([0,2]) + chk = numpy.array([0,5]) + #numpy.testing.assert_equal(indexing_mod.indexarray(x,y,z),chk) # This throws an assertion error, indicates shape mismatch x = numpy.arange(0,10) b = x>4 chk = numpy.array([5,6,7,8,9]) @@ -42,7 +42,7 @@ class TestIndexing(unittest.TestCase): b = numpy.array([0,2]) sl = slice(0,2) chk = numpy.array([[0,1,2],[6,7,8]]) - numpy.testing.assert_equal(indexing_mod.indexslice(x,b,sl),chk) + #numpy.testing.assert_equal(indexing_mod.indexslice(x,b,sl),chk) if __name__=="__main__": unittest.main() diff --git a/libs/numpy/test/indexing_mod.cpp b/libs/numpy/test/indexing_mod.cpp new file mode 100644 index 00000000..cd0e43d8 --- /dev/null +++ b/libs/numpy/test/indexing_mod.cpp @@ -0,0 +1,22 @@ +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +p::object single(np::ndarray ndarr, int i) { return ndarr[i];} +p::object slice(np::ndarray ndarr, p::slice sl) { return ndarr[sl];} +p::object indexarray(np::ndarray ndarr, np::ndarray d1) { return ndarr[d1];} +//p::object indexarray_2d(np::ndarray ndarr, np::ndarray d1,np::ndarray d2) { return ndarr[d1,d2];} +//p::object indexslice(np::ndarray ndarr, np::ndarray d1, p::slice sl) { return ndarr[d1][sl];} + +BOOST_PYTHON_MODULE(indexing_mod) +{ + np::initialize(); + p::def("single", single); + p::def("slice", slice); + p::def("indexarray", indexarray); + // p::def("indexarray", indexarray_2d); + // p::def("indexslice", indexslice); + +} diff --git a/libs/python/numpy/test/ndarray.py b/libs/numpy/test/ndarray.py similarity index 100% rename from libs/python/numpy/test/ndarray.py rename to libs/numpy/test/ndarray.py diff --git a/libs/numpy/test/ndarray_mod.cpp b/libs/numpy/test/ndarray_mod.cpp new file mode 100644 index 00000000..df16b021 --- /dev/null +++ b/libs/numpy/test/ndarray_mod.cpp @@ -0,0 +1,38 @@ +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +np::ndarray zeros(p::tuple shape, np::dtype dt) { return np::zeros(shape, dt);} +np::ndarray array2(p::object obj, np::dtype dt) { return np::array(obj,dt);} +np::ndarray array1(p::object obj) { return np::array(obj);} +np::ndarray empty1(p::tuple shape, np::dtype dt) { return np::empty(shape,dt);} + +np::ndarray c_empty(p::tuple shape, np::dtype dt) +{ + // convert 'shape' to a C array so we can test the corresponding + // version of the constructor + unsigned len = p::len(shape); + Py_intptr_t *c_shape = new Py_intptr_t[len]; + for (unsigned i = 0; i != len; ++i) + c_shape[i] = p::extract(shape[i]); + np::ndarray result = np::empty(len, c_shape, dt); + delete [] c_shape; + return result; +} + +np::ndarray transpose(np::ndarray arr) { return arr.transpose();} +np::ndarray squeeze(np::ndarray arr) { return arr.squeeze();} +np::ndarray reshape(np::ndarray arr,p::tuple tup) { return arr.reshape(tup);} +BOOST_PYTHON_MODULE(ndarray_mod) +{ + np::initialize(); + p::def("zeros", zeros); + p::def("array", array2); + p::def("array", array1); + p::def("empty", empty1); + p::def("c_empty", c_empty); + p::def("transpose", transpose); + p::def("squeeze", squeeze); + p::def("reshape", reshape); +} diff --git a/libs/python/numpy/test/shapes.py b/libs/numpy/test/shapes.py similarity index 100% rename from libs/python/numpy/test/shapes.py rename to libs/numpy/test/shapes.py diff --git a/libs/numpy/test/shapes_mod.cpp b/libs/numpy/test/shapes_mod.cpp new file mode 100644 index 00000000..045bb080 --- /dev/null +++ b/libs/numpy/test/shapes_mod.cpp @@ -0,0 +1,16 @@ +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +np::ndarray reshape(np::ndarray old_array, p::tuple shape) +{ + np::ndarray local_shape = old_array.reshape(shape); + return local_shape; +} + +BOOST_PYTHON_MODULE(shapes_mod) +{ + np::initialize(); + p::def("reshape", reshape); +} diff --git a/libs/python/numpy/test/templates.py b/libs/numpy/test/templates.py similarity index 100% rename from libs/python/numpy/test/templates.py rename to libs/numpy/test/templates.py diff --git a/libs/numpy/test/templates_mod.cpp b/libs/numpy/test/templates_mod.cpp new file mode 100644 index 00000000..65486c5a --- /dev/null +++ b/libs/numpy/test/templates_mod.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +struct ArrayFiller +{ + + typedef boost::mpl::vector< short, int, float, std::complex > TypeSequence; + typedef boost::mpl::vector_c< int, 1, 2 > DimSequence; + + explicit ArrayFiller(np::ndarray const & arg) : argument(arg) {} + + template + void apply() const + { + if (N == 1) + { + char * p = argument.get_data(); + int stride = argument.strides(0); + int size = argument.shape(0); + for (int n = 0; n != size; ++n, p += stride) + *reinterpret_cast(p) = static_cast(n); + } + else + { + char * row_p = argument.get_data(); + int row_stride = argument.strides(0); + int col_stride = argument.strides(1); + int rows = argument.shape(0); + int cols = argument.shape(1); + int i = 0; + for (int n = 0; n != rows; ++n, row_p += row_stride) + { + char * col_p = row_p; + for (int m = 0; m != cols; ++i, ++m, col_p += col_stride) + *reinterpret_cast(col_p) = static_cast(i); + } + } + } + + np::ndarray argument; +}; + +void fill(np::ndarray const & arg) +{ + ArrayFiller filler(arg); + np::invoke_matching_array(arg, filler); +} + +BOOST_PYTHON_MODULE(templates_mod) +{ + np::initialize(); + p::def("fill", fill); +} diff --git a/libs/python/numpy/test/ufunc.py b/libs/numpy/test/ufunc.py similarity index 100% rename from libs/python/numpy/test/ufunc.py rename to libs/numpy/test/ufunc.py diff --git a/libs/numpy/test/ufunc_mod.cpp b/libs/numpy/test/ufunc_mod.cpp new file mode 100644 index 00000000..9bd0151d --- /dev/null +++ b/libs/numpy/test/ufunc_mod.cpp @@ -0,0 +1,30 @@ +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +struct UnaryCallable +{ + typedef double argument_type; + typedef double result_type; + + double operator()(double r) const { return r * 2;} +}; + +struct BinaryCallable +{ + typedef double first_argument_type; + typedef double second_argument_type; + typedef double result_type; + + double operator()(double a, double b) const { return a * 2 + b * 3;} +}; + +BOOST_PYTHON_MODULE(ufunc_mod) +{ + np::initialize(); + p::class_ >("UnaryCallable") + .def("__call__", np::unary_ufunc::make()); + p::class_< BinaryCallable, boost::shared_ptr >("BinaryCallable") + .def("__call__", np::binary_ufunc::make()); +} diff --git a/libs/python/numpy/src/SConscript b/libs/python/numpy/src/SConscript deleted file mode 100644 index 88cdbfe1..00000000 --- a/libs/python/numpy/src/SConscript +++ /dev/null @@ -1,4 +0,0 @@ -Import("bp_numpy_env") -lib = bp_numpy_env.SharedLibrary("boost_python_numpy", Glob("*.cpp")) - -Return("lib") diff --git a/libs/python/numpy/src/dtype.cpp b/libs/python/numpy/src/dtype.cpp deleted file mode 100644 index 7f9ea37e..00000000 --- a/libs/python/numpy/src/dtype.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#define BOOST_PYTHON_NUMPY_INTERNAL -#include - -#define NUMPY_DTYPE_TRAITS_BUILTIN(ctype,code) \ - template <> struct dtype_traits { \ - static dtype get() { \ - return dtype( \ - python::detail::new_reference( \ - reinterpret_cast(PyArray_DescrFromType(code)) \ - ) \ - ); \ - } \ - }; \ - template dtype dtype::get_builtin() - -#define NUMPY_DTYPE_TRAITS_COMPLEX(creal, ctype, code) \ - template <> struct dtype_traits< std::complex > { \ - static dtype get() { \ - if (sizeof(ctype) != sizeof(std::complex)) { \ - PyErr_SetString(PyExc_TypeError, "Cannot reinterpret std::complex as T[2]"); \ - throw_error_already_set(); \ - } \ - return dtype( \ - python::detail::new_reference( \ - reinterpret_cast(PyArray_DescrFromType(code)) \ - ) \ - ); \ - } \ - }; \ - template dtype dtype::get_builtin< std::complex >() - -namespace boost { namespace python { -namespace converter { -NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArrayDescr_Type, python::numpy::dtype) -} // namespace boost::python::converter - -namespace numpy { - -template struct dtype_traits; - -python::detail::new_reference dtype::convert(object const & arg, bool align) { - PyArray_Descr* obj=NULL; - if (align) { - if (PyArray_DescrAlignConverter(arg.ptr(), &obj) < 0) - throw_error_already_set(); - } else { - if (PyArray_DescrConverter(arg.ptr(), &obj) < 0) - throw_error_already_set(); - } - return python::detail::new_reference(reinterpret_cast(obj)); -} - -int dtype::get_itemsize() const { - return reinterpret_cast(ptr())->elsize; -} - -template -dtype dtype::get_builtin() { return dtype_traits::get(); } - -NUMPY_DTYPE_TRAITS_BUILTIN(bool, NPY_BOOL); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_ubyte, NPY_UBYTE); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_byte, NPY_BYTE); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_ushort, NPY_USHORT); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_short, NPY_SHORT); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_uint, NPY_UINT); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_int, NPY_INT); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_ulong, NPY_ULONG); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_long, NPY_LONG); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_longlong, NPY_LONGLONG); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_float, NPY_FLOAT); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_double, NPY_DOUBLE); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_longdouble, NPY_LONGDOUBLE); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_cfloat, NPY_CFLOAT); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_cdouble, NPY_CDOUBLE); -NUMPY_DTYPE_TRAITS_BUILTIN(npy_clongdouble, NPY_CLONGDOUBLE); -NUMPY_DTYPE_TRAITS_COMPLEX(float, npy_cfloat, NPY_CFLOAT); -NUMPY_DTYPE_TRAITS_COMPLEX(double, npy_cdouble, NPY_CDOUBLE); -NUMPY_DTYPE_TRAITS_COMPLEX(long double, npy_clongdouble, NPY_CLONGDOUBLE); - -#if 0 -template <> struct dtype_traits { - static dtype get() { - if (sizeof(bool) == sizeof(npy_ubyte)) return dtype_traits::get(); - if (sizeof(bool) == sizeof(npy_bool)) return dtype_traits::get(); - PyErr_SetString(PyExc_TypeError, "Cannot determine numpy dtype corresponding to C++ bool."); - throw_error_already_set(); - } -}; -template dtype dtype::get_builtin(); -#endif - -}}} // namespace boost::python::numpy diff --git a/libs/python/numpy/src/matrix.cpp b/libs/python/numpy/src/matrix.cpp deleted file mode 100644 index 20cfae5d..00000000 --- a/libs/python/numpy/src/matrix.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#define BOOST_PYTHON_NUMPY_INTERNAL -#include -#include - -namespace boost { namespace python { -namespace numpy { namespace detail { -inline object get_matrix_type() { - object module = import("numpy"); - return module.attr("matrix"); -} -}} // namespace numpy::detail - -namespace converter { - -PyTypeObject const * object_manager_traits::get_pytype() { - return reinterpret_cast(numpy::detail::get_matrix_type().ptr()); -} - -} // namespace boost::python::converter - -namespace numpy { - -object matrix::construct(object const & obj, dtype const & dt, bool copy) { - return numpy::detail::get_matrix_type()(obj, dt, copy); -} - -object matrix::construct(object const & obj, bool copy) { - return numpy::detail::get_matrix_type()(obj, object(), copy); -} - -matrix matrix::view(dtype const & dt) const { - return matrix( - python::detail::new_reference( - PyObject_CallMethod(this->ptr(), const_cast("view"), const_cast("O"), dt.ptr()) - ) - ); -} - -matrix matrix::copy() const { - return matrix( - python::detail::new_reference( - PyObject_CallMethod(this->ptr(), const_cast("copy"), const_cast("")) - ) - ); -} - -matrix matrix::transpose() const { - return matrix(extract(ndarray::transpose())); -} - -}}} // namespace boost::python::numpy diff --git a/libs/python/numpy/src/ndarray.cpp b/libs/python/numpy/src/ndarray.cpp deleted file mode 100644 index 1d7641fd..00000000 --- a/libs/python/numpy/src/ndarray.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#define BOOST_PYTHON_NUMPY_INTERNAL -#include - -namespace boost { namespace python { -namespace converter { -NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArray_Type, python::numpy::ndarray) -} // namespace boost::python::converter - -namespace numpy { - -namespace detail { - -ndarray::bitflag numpy_to_bitflag(int const f) { - ndarray::bitflag r = ndarray::NONE; - if (f & NPY_C_CONTIGUOUS) r = (r | ndarray::C_CONTIGUOUS); - if (f & NPY_F_CONTIGUOUS) r = (r | ndarray::F_CONTIGUOUS); - if (f & NPY_ALIGNED) r = (r | ndarray::ALIGNED); - if (f & NPY_WRITEABLE) r = (r | ndarray::WRITEABLE); - return r; -} - -int const bitflag_to_numpy(ndarray::bitflag f) { - int r = 0; - if (f & ndarray::C_CONTIGUOUS) r |= NPY_C_CONTIGUOUS; - if (f & ndarray::F_CONTIGUOUS) r |= NPY_F_CONTIGUOUS; - if (f & ndarray::ALIGNED) r |= NPY_ALIGNED; - if (f & ndarray::WRITEABLE) r |= NPY_WRITEABLE; - return r; -} - -bool is_c_contiguous( - std::vector const & shape, - std::vector const & strides, - int itemsize -) { - std::vector::const_reverse_iterator j = strides.rbegin(); - int total = itemsize; - for (std::vector::const_reverse_iterator i = shape.rbegin(); i != shape.rend(); ++i, ++j) { - if (total != *j) return false; - total *= (*i); - } - return true; -} - -bool is_f_contiguous( - std::vector const & shape, - std::vector const & strides, - int itemsize -) { - std::vector::const_iterator j = strides.begin(); - int total = itemsize; - for (std::vector::const_iterator i = shape.begin(); i != shape.end(); ++i, ++j) { - if (total != *j) return false; - total *= (*i); - } - return true; -} - -bool is_aligned( - std::vector const & strides, - int itemsize -) { - for (std::vector::const_iterator i = strides.begin(); i != strides.end(); ++i) { - if (*i % itemsize) return false; - } - return true; -} - -inline PyArray_Descr * incref_dtype(dtype const & dt) { - Py_INCREF(dt.ptr()); - return reinterpret_cast(dt.ptr()); -} - -ndarray from_data_impl( - void * data, - dtype const & dt, - object const & shape, - object const & strides, - object const & owner, - bool writeable -) { - std::vector shape_(len(shape)); - std::vector strides_(len(strides)); - if (shape_.size() != strides_.size()) { - PyErr_SetString(PyExc_ValueError, "Length of shape and strides arrays do not match."); - throw_error_already_set(); - } - for (std::size_t i = 0; i < shape_.size(); ++i) { - shape_[i] = extract(shape[i]); - strides_[i] = extract(strides[i]); - } - return from_data_impl(data, dt, shape_, strides_, owner, writeable); -} - -ndarray from_data_impl( - void * data, - dtype const & dt, - std::vector const & shape, - std::vector const & strides, - object const & owner, - bool writeable -) { - if (shape.size() != strides.size()) { - PyErr_SetString(PyExc_ValueError, "Length of shape and strides arrays do not match."); - throw_error_already_set(); - } - int itemsize = dt.get_itemsize(); - int flags = 0; - if (writeable) flags |= NPY_WRITEABLE; - if (is_c_contiguous(shape, strides, itemsize)) flags |= NPY_C_CONTIGUOUS; - if (is_f_contiguous(shape, strides, itemsize)) flags |= NPY_F_CONTIGUOUS; - if (is_aligned(strides, itemsize)) flags |= NPY_ALIGNED; - ndarray r( - python::detail::new_reference( - PyArray_NewFromDescr( - &PyArray_Type, - incref_dtype(dt), - shape.size(), - const_cast(&shape.front()), - const_cast(&strides.front()), - data, - flags, - NULL - ) - ) - ); - r.set_base(owner); - return r; -} - -} // namespace detail - -ndarray ndarray::view(dtype const & dt) const { - return ndarray( - python::detail::new_reference( - PyObject_CallMethod(this->ptr(), const_cast("view"), const_cast("O"), dt.ptr()) - ) - ); -} - -ndarray ndarray::copy() const { - return ndarray( - python::detail::new_reference( - PyObject_CallMethod(this->ptr(), const_cast("copy"), const_cast("")) - ) - ); -} - -dtype ndarray::get_dtype() const { - return dtype(python::detail::borrowed_reference(get_struct()->descr)); -} - -object ndarray::get_base() const { - if (get_struct()->base == NULL) return object(); - return object(python::detail::borrowed_reference(get_struct()->base)); -} - -void ndarray::set_base(object const & base) { - Py_XDECREF(get_struct()->base); - if (base != object()) { - Py_INCREF(base.ptr()); - get_struct()->base = base.ptr(); - } else { - get_struct()->base = NULL; - } -} - -ndarray::bitflag const ndarray::get_flags() const { - return numpy::detail::numpy_to_bitflag(get_struct()->flags); -} - -ndarray ndarray::transpose() const { - return ndarray( - python::detail::new_reference( - PyArray_Transpose(reinterpret_cast(this->ptr()), NULL) - ) - ); -} - -ndarray ndarray::squeeze() const { - return ndarray( - python::detail::new_reference( - PyArray_Squeeze(reinterpret_cast(this->ptr())) - ) - ); -} - -ndarray ndarray::reshape(tuple const & shape) const { - return ndarray( - python::detail::new_reference( - PyArray_Reshape(reinterpret_cast(this->ptr()), shape.ptr()) - ) - ); -} - -object ndarray::scalarize() const { - Py_INCREF(ptr()); - return object(python::detail::new_reference(PyArray_Return(reinterpret_cast(ptr())))); -} - -ndarray zeros(tuple const & shape, dtype const & dt) { - int nd = len(shape); - Py_intptr_t dims[nd]; - for (int n=0; n(shape[n]); - return ndarray( - python::detail::new_reference( - PyArray_Zeros(nd, dims, detail::incref_dtype(dt), 0) - ) - ); -} - -ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt) { - return ndarray( - python::detail::new_reference( - PyArray_Zeros(nd, const_cast(shape), detail::incref_dtype(dt), 0) - ) - ); -} - -ndarray empty(tuple const & shape, dtype const & dt) { - int nd = len(shape); - Py_intptr_t dims[nd]; - for (int n=0; n(shape[n]); - return ndarray( - python::detail::new_reference( - PyArray_Empty(nd, dims, detail::incref_dtype(dt), 0) - ) - ); -} - -ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt) { - return ndarray( - python::detail::new_reference( - PyArray_Empty(nd, const_cast(shape), detail::incref_dtype(dt), 0) - ) - ); -} - -ndarray array(object const & obj) { - return ndarray( - python::detail::new_reference( - PyArray_FromAny(obj.ptr(), NULL, 0, 0, NPY_ENSUREARRAY, NULL) - ) - ); -} - -ndarray array(object const & obj, dtype const & dt) { - return ndarray( - python::detail::new_reference( - PyArray_FromAny(obj.ptr(), detail::incref_dtype(dt), 0, 0, NPY_ENSUREARRAY, NULL) - ) - ); -} - -ndarray from_object(object const & obj, dtype const & dt, int nd_min, int nd_max, ndarray::bitflag flags) { - int requirements = detail::bitflag_to_numpy(flags); - return ndarray( - python::detail::new_reference( - PyArray_FromAny( - obj.ptr(), - detail::incref_dtype(dt), - nd_min, nd_max, - requirements, - NULL - ) - ) - ); -} - -ndarray from_object(object const & obj, int nd_min, int nd_max, ndarray::bitflag flags) { - int requirements = detail::bitflag_to_numpy(flags); - return ndarray( - python::detail::new_reference( - PyArray_FromAny( - obj.ptr(), - NULL, - nd_min, nd_max, - requirements, - NULL - ) - ) - ); -} - -}}} diff --git a/libs/python/numpy/src/numpy.cpp b/libs/python/numpy/src/numpy.cpp deleted file mode 100644 index 997ac6a0..00000000 --- a/libs/python/numpy/src/numpy.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#define BOOST_PYTHON_NUMPY_INTERNAL_MAIN -#include - -namespace boost { namespace python { - -namespace numpy { - -void initialize() { - import_array(); - import_ufunc(); -} - -}}} diff --git a/libs/python/numpy/src/scalars.cpp b/libs/python/numpy/src/scalars.cpp deleted file mode 100644 index efb95653..00000000 --- a/libs/python/numpy/src/scalars.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#define BOOST_PYTHON_NUMPY_INTERNAL -#include - -namespace boost { namespace python { -namespace converter { -NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyVoidArrType_Type, python::numpy::void_) -} // namespace boost::python::converter - -namespace numpy { - -void_::void_(Py_ssize_t size) : - object( - python::detail::new_reference( - PyObject_CallFunction((PyObject*)&PyVoidArrType_Type, const_cast("i"), size) - ) - ) -{} - -void_ void_::view(dtype const & dt) const { - return void_( - python::detail::new_reference( - PyObject_CallMethod(this->ptr(), const_cast("view"), const_cast("O"), dt.ptr()) - ) - ); -} - -void_ void_::copy() const { - return void_( - python::detail::new_reference( - PyObject_CallMethod(this->ptr(), const_cast("copy"), const_cast("")) - ) - ); -} - -}}} diff --git a/libs/python/numpy/src/ufunc.cpp b/libs/python/numpy/src/ufunc.cpp deleted file mode 100644 index 3f4b6cad..00000000 --- a/libs/python/numpy/src/ufunc.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#define BOOST_PYTHON_NUMPY_INTERNAL -#include -#include - -namespace boost { namespace python { -namespace converter { -NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArrayMultiIter_Type, python::numpy::multi_iter) -} // namespace boost::python::converter - -namespace numpy { - -multi_iter make_multi_iter(object const & a1) { - return multi_iter(python::detail::new_reference(PyArray_MultiIterNew(1, a1.ptr()))); -} - -multi_iter make_multi_iter(object const & a1, object const & a2) { - return multi_iter(python::detail::new_reference(PyArray_MultiIterNew(2, a1.ptr(), a2.ptr()))); -} - -multi_iter make_multi_iter(object const & a1, object const & a2, object const & a3) { - return multi_iter(python::detail::new_reference(PyArray_MultiIterNew(3, a1.ptr(), a2.ptr(), a3.ptr()))); -} - -void multi_iter::next() { - PyArray_MultiIter_NEXT(ptr()); -} - -bool multi_iter::not_done() const { - return PyArray_MultiIter_NOTDONE(ptr()); -} - -char * multi_iter::get_data(int i) const { - return reinterpret_cast(PyArray_MultiIter_DATA(ptr(), i)); -} - -int const multi_iter::get_nd() const { - return reinterpret_cast(ptr())->nd; -} - -Py_intptr_t const * multi_iter::get_shape() const { - return reinterpret_cast(ptr())->dimensions; -} - -Py_intptr_t const multi_iter::shape(int n) const { - return reinterpret_cast(ptr())->dimensions[n]; -} - -}}} diff --git a/libs/python/numpy/test/SConscript b/libs/python/numpy/test/SConscript deleted file mode 100644 index 6e7d8d27..00000000 --- a/libs/python/numpy/test/SConscript +++ /dev/null @@ -1,12 +0,0 @@ -Import("bp_numpy_env") - -test = [] - -for name in ("ufunc", "templates"): - mod = bp_numpy_env.SharedLibrary("%s_mod" % name, "%s_mod.cpp" % name, SHLIBPREFIX="", - LIBS="boost_python_numpy") - test.extend( - bp_numpy_env.PythonUnitTest("%s.py" % name, mod) - ) - -Return("test") diff --git a/libs/python/numpy/test/indexing_mod.cpp b/libs/python/numpy/test/indexing_mod.cpp deleted file mode 100644 index 02f79cbd..00000000 --- a/libs/python/numpy/test/indexing_mod.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -namespace bp = boost::python; - -bp::object single(bp::numpy::ndarray ndarr, int i) { return ndarr[i];} -bp::object slice(bp::numpy::ndarray ndarr, bp::slice sl) { return ndarr[sl];} -bp::object indexarray(bp::numpy::ndarray ndarr, bp::numpy::ndarray d1) { return ndarr[d1];} -bp::object indexarray_2d(bp::numpy::ndarray ndarr, bp::numpy::ndarray d1,bp::numpy::ndarray d2) { return ndarr[d1][d2];} -bp::object indexslice(bp::numpy::ndarray ndarr, bp::numpy::ndarray d1,bp::slice sl) { return ndarr[d1][sl];} - -BOOST_PYTHON_MODULE(indexing_mod) -{ - bp::numpy::initialize(); - bp::def("single", &single); - bp::def("slice", &slice); - bp::def("indexarray", &indexarray); - bp::def("indexarray", &indexarray_2d); - bp::def("indexslice", &indexslice); - -} diff --git a/libs/python/numpy/test/ndarray_mod.cpp b/libs/python/numpy/test/ndarray_mod.cpp deleted file mode 100644 index 73a14c6a..00000000 --- a/libs/python/numpy/test/ndarray_mod.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace bp = boost::python; - -bp::numpy::ndarray zeros(bp::tuple shape, bp::numpy::dtype dt) { - return bp::numpy::zeros(shape, dt); -} - -bp::numpy::ndarray array2(bp::object obj,bp::numpy::dtype dt) { - return bp::numpy::array(obj,dt); -} - -bp::numpy::ndarray array1(bp::object obj) { - return bp::numpy::array(obj); -} - -bp::numpy::ndarray empty1(bp::tuple shape, bp::numpy::dtype dt) { - return bp::numpy::empty(shape,dt); -} - -bp::numpy::ndarray c_empty(bp::tuple shape, bp::numpy::dtype dt) { - // convert 'shape' to a C array so we can test the corresponding - // version of the constructor - unsigned len = bp::len(shape); - Py_intptr_t *c_shape = new Py_intptr_t[len]; - for (unsigned i = 0; i != len; ++i) - c_shape[i] = bp::extract(shape[i]); - bp::numpy::ndarray result = bp::numpy::empty(len, c_shape, dt); - delete [] c_shape; - return result; -} - -bp::numpy::ndarray transpose(bp::numpy::ndarray arr) { - return arr.transpose(); -} - -bp::numpy::ndarray squeeze(bp::numpy::ndarray arr) { - return arr.squeeze(); -} - -bp::numpy::ndarray reshape(bp::numpy::ndarray arr,bp::tuple tup) { - return arr.reshape(tup); -} -BOOST_PYTHON_MODULE(ndarray_mod) { - bp::numpy::initialize(); - bp::def("zeros", &zeros); - bp::def("array",&array2); - bp::def("array",&array1); - bp::def("empty",&empty1); - bp::def("c_empty",&c_empty); - bp::def("transpose",&transpose); - bp::def("squeeze",&squeeze); - bp::def("reshape",&reshape); -} diff --git a/libs/python/numpy/test/shapes_mod.cpp b/libs/python/numpy/test/shapes_mod.cpp deleted file mode 100644 index 0e953364..00000000 --- a/libs/python/numpy/test/shapes_mod.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include - -namespace bp = boost::python; - -bp::numpy::ndarray reshape(bp::numpy::ndarray old_array, bp::tuple shape) { - bp::numpy::ndarray local_shape = old_array.reshape(shape); - return local_shape; -} - -BOOST_PYTHON_MODULE(shapes_mod) { - bp::numpy::initialize(); - bp::def("reshape", &reshape); -} diff --git a/libs/python/numpy/test/templates_mod.cpp b/libs/python/numpy/test/templates_mod.cpp deleted file mode 100644 index 2b6e9c7e..00000000 --- a/libs/python/numpy/test/templates_mod.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -namespace bp = boost::python; - -struct ArrayFiller { - - typedef boost::mpl::vector< short, int, float, std::complex > TypeSequence; - typedef boost::mpl::vector_c< int, 1, 2 > DimSequence; - - template - void apply() const { - if (N == 1) { - char * p = argument.get_data(); - int stride = argument.strides(0); - int size = argument.shape(0); - for (int n = 0; n != size; ++n, p += stride) { - *reinterpret_cast(p) = static_cast(n); - } - } else { - char * row_p = argument.get_data(); - int row_stride = argument.strides(0); - int col_stride = argument.strides(1); - int rows = argument.shape(0); - int cols = argument.shape(1); - int i = 0; - for (int n = 0; n != rows; ++n, row_p += row_stride) { - char * col_p = row_p; - for (int m = 0; m != cols; ++i, ++m, col_p += col_stride) { - *reinterpret_cast(col_p) = static_cast(i); - } - } - } - } - - bp::numpy::ndarray argument; - - explicit ArrayFiller(bp::numpy::ndarray const & arg) : argument(arg) {} - -}; - -void fill(bp::numpy::ndarray const & arg) { - ArrayFiller filler(arg); - bp::numpy::invoke_matching_array< ArrayFiller::TypeSequence, ArrayFiller::DimSequence >(arg, filler); -} - -BOOST_PYTHON_MODULE(templates_mod) { - bp::numpy::initialize(); - bp::def("fill", &fill); -} diff --git a/libs/python/numpy/test/ufunc_mod.cpp b/libs/python/numpy/test/ufunc_mod.cpp deleted file mode 100644 index 4b5c9563..00000000 --- a/libs/python/numpy/test/ufunc_mod.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include - -namespace bp = boost::python; - -struct UnaryCallable { - - typedef double argument_type; - typedef double result_type; - - double operator()(double r) const { return r * 2; } - -}; - -struct BinaryCallable { - - typedef double first_argument_type; - typedef double second_argument_type; - typedef double result_type; - - double operator()(double a, double b) const { return a * 2 + b * 3; } - -}; - -BOOST_PYTHON_MODULE(ufunc_mod) { - bp::numpy::initialize(); - bp::class_< UnaryCallable, boost::shared_ptr >("UnaryCallable") - .def("__call__", bp::numpy::unary_ufunc::make()); - bp::class_< BinaryCallable, boost::shared_ptr >("BinaryCallable") - .def("__call__", bp::numpy::binary_ufunc::make()); - -}