From 189915bc8b8474fb9890dea4971ad8c4fa8b1a64 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Mon, 8 Mar 2010 20:12:06 +0000 Subject: [PATCH 001/128] folder for new numpy project: improved boost.python bindings for numpy From eef2eef7dd113241748ff06f68eb98ef8c69838c Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Mon, 8 Mar 2010 21:50:13 +0000 Subject: [PATCH 002/128] initial sandbox import for numpy utilities in boost.python --- boost/python/numpy.hpp | 32 ++ boost/python/numpy/dtype.hpp | 56 ++++ boost/python/numpy/internal.hpp | 29 ++ boost/python/numpy/matrix.hpp | 62 ++++ boost/python/numpy/ndarray.hpp | 285 ++++++++++++++++++ .../python/numpy/numpy_object_mgr_traits.hpp | 27 ++ boost/python/numpy/scalars.hpp | 55 ++++ boost/python/numpy/ufunc.hpp | 193 ++++++++++++ libs/python/numpy/src/dtype.cpp | 84 ++++++ libs/python/numpy/src/matrix.cpp | 51 ++++ libs/python/numpy/src/ndarray.cpp | 277 +++++++++++++++++ libs/python/numpy/src/numpy.cpp | 13 + libs/python/numpy/src/scalars.cpp | 35 +++ libs/python/numpy/src/ufunc.cpp | 48 +++ 14 files changed, 1247 insertions(+) create mode 100644 boost/python/numpy.hpp create mode 100644 boost/python/numpy/dtype.hpp create mode 100644 boost/python/numpy/internal.hpp create mode 100644 boost/python/numpy/matrix.hpp create mode 100644 boost/python/numpy/ndarray.hpp create mode 100644 boost/python/numpy/numpy_object_mgr_traits.hpp create mode 100644 boost/python/numpy/scalars.hpp create mode 100644 boost/python/numpy/ufunc.hpp create mode 100644 libs/python/numpy/src/dtype.cpp create mode 100644 libs/python/numpy/src/matrix.cpp create mode 100644 libs/python/numpy/src/ndarray.cpp create mode 100644 libs/python/numpy/src/numpy.cpp create mode 100644 libs/python/numpy/src/scalars.cpp create mode 100644 libs/python/numpy/src/ufunc.cpp diff --git a/boost/python/numpy.hpp b/boost/python/numpy.hpp new file mode 100644 index 00000000..9e0f6a44 --- /dev/null +++ b/boost/python/numpy.hpp @@ -0,0 +1,32 @@ +#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 + +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 new file mode 100644 index 00000000..53c61f34 --- /dev/null +++ b/boost/python/numpy/dtype.hpp @@ -0,0 +1,56 @@ +#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 + +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/internal.hpp b/boost/python/numpy/internal.hpp new file mode 100644 index 00000000..55d1c70c --- /dev/null +++ b/boost/python/numpy/internal.hpp @@ -0,0 +1,29 @@ +#ifndef BOOST_PYTHON_NUMPY_INTERNAL_HPP_INCLUDED +#define BOOST_PYTHON_NUMPY_INTERNAL_HPP_INCLUDED + +/** + * @file boost/python/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. + */ + +#include +#ifdef BOOST_PYTHON_NUMPY_INTERNAL +#define NO_IMPORT_ARRAY +#define NO_IMPORT_UFUNC +#else +#ifndef BOOST_PYTHON_NUMPY_INTERNAL_MAIN +ERROR_internal_hpp_is_for_internal_use_only +#endif +#endif +#define PY_ARRAY_UNIQUE_SYMBOL BOOST_NUMPY_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL BOOST_UFUNC_ARRAY_API +#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 diff --git a/boost/python/numpy/matrix.hpp b/boost/python/numpy/matrix.hpp new file mode 100644 index 00000000..584023f0 --- /dev/null +++ b/boost/python/numpy/matrix.hpp @@ -0,0 +1,62 @@ +#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 new file mode 100644 index 00000000..170112c3 --- /dev/null +++ b/boost/python/numpy/ndarray.hpp @@ -0,0 +1,285 @@ +#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 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 new file mode 100644 index 00000000..aa9121d8 --- /dev/null +++ b/boost/python/numpy/numpy_object_mgr_traits.hpp @@ -0,0 +1,27 @@ +#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 new file mode 100644 index 00000000..975d5f48 --- /dev/null +++ b/boost/python/numpy/scalars.hpp @@ -0,0 +1,55 @@ +#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 new file mode 100644 index 00000000..f518c87b --- /dev/null +++ b/boost/python/numpy/ufunc.hpp @@ -0,0 +1,193 @@ +#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/dtype.cpp b/libs/python/numpy/src/dtype.cpp new file mode 100644 index 00000000..852038e7 --- /dev/null +++ b/libs/python/numpy/src/dtype.cpp @@ -0,0 +1,84 @@ +#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 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(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_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); + +template <> struct dtype_traits { + static dtype get() { + if (sizeof(bool) == sizeof(npy_bool)) return dtype_traits::get(); + if (sizeof(bool) == sizeof(npy_ubyte)) 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(); + +}}} // namespace boost::python::numpy diff --git a/libs/python/numpy/src/matrix.cpp b/libs/python/numpy/src/matrix.cpp new file mode 100644 index 00000000..20cfae5d --- /dev/null +++ b/libs/python/numpy/src/matrix.cpp @@ -0,0 +1,51 @@ +#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 new file mode 100644 index 00000000..30820f4b --- /dev/null +++ b/libs/python/numpy/src/ndarray.cpp @@ -0,0 +1,277 @@ +#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())) + ) + ); +} + +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 new file mode 100644 index 00000000..997ac6a0 --- /dev/null +++ b/libs/python/numpy/src/numpy.cpp @@ -0,0 +1,13 @@ +#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 new file mode 100644 index 00000000..efb95653 --- /dev/null +++ b/libs/python/numpy/src/scalars.cpp @@ -0,0 +1,35 @@ +#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 new file mode 100644 index 00000000..3f4b6cad --- /dev/null +++ b/libs/python/numpy/src/ufunc.cpp @@ -0,0 +1,48 @@ +#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]; +} + +}}} From e2b2ebe862ba4affb77a8b44eeefc447a0db2e77 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Fri, 14 May 2010 22:47:14 +0000 Subject: [PATCH 003/128] numpy python extension - added basic SCons build system, started on unit tests --- SConstruct | 51 ++++++++++++++++++++++++++++ libs/python/numpy/src/SConscript | 5 +++ libs/python/numpy/test/SConscript | 17 ++++++++++ libs/python/numpy/test/ufunc.py | 49 ++++++++++++++++++++++++++ libs/python/numpy/test/ufunc_mod.cpp | 31 +++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 SConstruct create mode 100644 libs/python/numpy/src/SConscript create mode 100644 libs/python/numpy/test/SConscript create mode 100755 libs/python/numpy/test/ufunc.py create mode 100644 libs/python/numpy/test/ufunc_mod.cpp diff --git a/SConstruct b/SConstruct new file mode 100644 index 00000000..14d42ee5 --- /dev/null +++ b/SConstruct @@ -0,0 +1,51 @@ +import distutils.sysconfig +import numpy.distutils.misc_util +import re +import os + +def ApplyFlags(env, flags): + flags = env.ParseFlags(flags) + flags["CCFLAGS"] = [opt for opt in flags["CCFLAGS"] if not opt.startswith("-O")] + flags["CFLAGS"] = [opt for opt in flags["CFLAGS"] if not opt.startswith("-O")] + debug = ARGUMENTS.get('debug', 0) + if int(debug): + try: + flags["CPPDEFINES"].remove("NDEBUG") + except: pass + env.MergeFlags(flags) + +def ConfigurePython(env): + cflags = " ".join(v for v in distutils.sysconfig.get_config_vars("BASECFLAGS","OPT") + if v is not None).split() + libs = " ".join(v for v in distutils.sysconfig.get_config_vars("BLDLIBRARY","LIBS") + if v is not None).split() + try: # not valid for C++ + cflags.remove("-Wstrict-prototypes") + except ValueError: pass + cflags = [f for f in cflags if not f.startswith("-O")] + try: + libs.remove("-L.") + except ValueError: pass + cflags.append("-I%s" % distutils.sysconfig.get_python_inc()) + ApplyFlags(env, cflags + libs) + +def ConfigureNumpy(env): + folders = numpy.distutils.misc_util.get_numpy_include_dirs() + env.Append(CPPPATH=folders) + +env = Environment() +ConfigurePython(env) +ConfigureNumpy(env) +env.Append(LIBS = "boost_python") +env.Append(CPPPATH = "#") + +Export("env") +lib = SConscript("libs/python/numpy/src/SConscript") +libpath = os.path.abspath("libs/python/numpy/src") +if os.environ.has_key("LD_LIBRARY_PATH"): + env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) +else: + env["ENV"]["LD_LIBRARY_PATH"] = libpath +env.Append(LIBPATH=libpath) +Export("lib") +SConscript("libs/python/numpy/test/SConscript") diff --git a/libs/python/numpy/src/SConscript b/libs/python/numpy/src/SConscript new file mode 100644 index 00000000..c06300a1 --- /dev/null +++ b/libs/python/numpy/src/SConscript @@ -0,0 +1,5 @@ +Import("env") + +lib = env.SharedLibrary("boost_python_numpy", Glob("*.cpp")) + +Return("lib") diff --git a/libs/python/numpy/test/SConscript b/libs/python/numpy/test/SConscript new file mode 100644 index 00000000..f9615976 --- /dev/null +++ b/libs/python/numpy/test/SConscript @@ -0,0 +1,17 @@ +Import("env") +import os + +test_env = env.Clone() +test_env.Append(LIBS="boost_python_numpy") + +tests = ("ufunc",) +test_mods = [test_env.SharedLibrary("%s_mod" % k, "%s_mod.cpp" % k, SHLIBPREFIX="") + for k in tests] +os.path.abspath(".") +test_runs = [test_env.Command(".%s" % name, [mod,"%s.py" % name], + "cd %s; python %s.py" % (os.path.abspath("."), name)) + for name, mod in zip(tests, test_mods)] + +test = Alias("test",[test_runs]) + +Return("test") diff --git a/libs/python/numpy/test/ufunc.py b/libs/python/numpy/test/ufunc.py new file mode 100755 index 00000000..c66a148b --- /dev/null +++ b/libs/python/numpy/test/ufunc.py @@ -0,0 +1,49 @@ +import ufunc_mod +import unittest +import numpy + +class TestUnary(unittest.TestCase): + + def testScalar(self): + f = ufunc_mod.UnaryCallable() + self.assertEqual(f(1.0), 2.0) + self.assertEqual(f(3.0), 6.0) + + def testArray(self): + f = ufunc_mod.UnaryCallable() + a = numpy.arange(5, dtype=float) + b = f(a) + self.assert_((b == a*2.0).all()) + c = numpy.zeros(5, dtype=float) + d = f(a,output=c) + self.assert_((c == a*2.0).all()) + self.assert_((d == a*2.0).all()) + + def testList(self): + f = ufunc_mod.UnaryCallable() + a = range(5) + b = f(a) + self.assert_((b/2.0 == a).all()) + +class TestBinary(unittest.TestCase): + + def testScalar(self): + f = ufunc_mod.BinaryCallable() + self.assertEqual(f(1.0, 3.0), 11.0) + self.assertEqual(f(3.0, 2.0), 12.0) + + def testArray(self): + f = ufunc_mod.BinaryCallable() + a = numpy.random.randn(5) + b = numpy.random.randn(5) + self.assert_((f(a,b) == (a*2+b*3)).all()) + c = numpy.zeros(5, dtype=float) + d = f(a,b,output=c) + self.assert_((c == a*2 + b*3).all()) + self.assert_((d == a*2 + b*3).all()) + self.assert_((f(a, 2.0) == a*2 + 6.0).all()) + self.assert_((f(1.0, b) == 2.0 + b*3).all()) + + +if __name__=="__main__": + unittest.main() diff --git a/libs/python/numpy/test/ufunc_mod.cpp b/libs/python/numpy/test/ufunc_mod.cpp new file mode 100644 index 00000000..4b5c9563 --- /dev/null +++ b/libs/python/numpy/test/ufunc_mod.cpp @@ -0,0 +1,31 @@ +#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()); + +} From e0fa8ec61965e1a71fe3b234ac550a487625e431 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Fri, 14 May 2010 23:43:38 +0000 Subject: [PATCH 004/128] numpy python extensions - moved main header file inside subdirectory --- boost/python/{ => numpy}/numpy.hpp | 0 libs/python/numpy/test/ufunc_mod.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename boost/python/{ => numpy}/numpy.hpp (100%) diff --git a/boost/python/numpy.hpp b/boost/python/numpy/numpy.hpp similarity index 100% rename from boost/python/numpy.hpp rename to boost/python/numpy/numpy.hpp diff --git a/libs/python/numpy/test/ufunc_mod.cpp b/libs/python/numpy/test/ufunc_mod.cpp index 4b5c9563..0fc2e3a6 100644 --- a/libs/python/numpy/test/ufunc_mod.cpp +++ b/libs/python/numpy/test/ufunc_mod.cpp @@ -1,4 +1,4 @@ -#include +#include namespace bp = boost::python; From c3d186e0bf89d0cb623f0e27cf1f6e0722560a0d Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Sat, 15 May 2010 03:44:45 +0000 Subject: [PATCH 005/128] boost python numpy extensions - updated source files to reflect previous header move --- boost/python/numpy/internal.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boost/python/numpy/internal.hpp b/boost/python/numpy/internal.hpp index 55d1c70c..1ec1f19f 100644 --- a/boost/python/numpy/internal.hpp +++ b/boost/python/numpy/internal.hpp @@ -21,7 +21,7 @@ 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; } From 99d3a54ad58c4ff060fff18fadab6808468583a8 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Tue, 18 May 2010 06:59:41 +0000 Subject: [PATCH 006/128] boost.python numpy support - improvements to build system --- SConstruct | 52 ++----- libs/python/numpy/src/SConscript | 4 +- libs/python/numpy/test/SConscript | 19 +-- site_scons/scons_tools.py | 236 ++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+), 59 deletions(-) create mode 100755 site_scons/scons_tools.py diff --git a/SConstruct b/SConstruct index 14d42ee5..7972cd04 100644 --- a/SConstruct +++ b/SConstruct @@ -1,51 +1,17 @@ -import distutils.sysconfig -import numpy.distutils.misc_util -import re +import scons_tools import os -def ApplyFlags(env, flags): - flags = env.ParseFlags(flags) - flags["CCFLAGS"] = [opt for opt in flags["CCFLAGS"] if not opt.startswith("-O")] - flags["CFLAGS"] = [opt for opt in flags["CFLAGS"] if not opt.startswith("-O")] - debug = ARGUMENTS.get('debug', 0) - if int(debug): - try: - flags["CPPDEFINES"].remove("NDEBUG") - except: pass - env.MergeFlags(flags) +bp_numpy_env = scons_tools.MakeEnvironment() +bp_numpy_env.SetupPackages(["boost.python","numpy"]) +bp_numpy_env.Append(CPPPATH = "#") -def ConfigurePython(env): - cflags = " ".join(v for v in distutils.sysconfig.get_config_vars("BASECFLAGS","OPT") - if v is not None).split() - libs = " ".join(v for v in distutils.sysconfig.get_config_vars("BLDLIBRARY","LIBS") - if v is not None).split() - try: # not valid for C++ - cflags.remove("-Wstrict-prototypes") - except ValueError: pass - cflags = [f for f in cflags if not f.startswith("-O")] - try: - libs.remove("-L.") - except ValueError: pass - cflags.append("-I%s" % distutils.sysconfig.get_python_inc()) - ApplyFlags(env, cflags + libs) - -def ConfigureNumpy(env): - folders = numpy.distutils.misc_util.get_numpy_include_dirs() - env.Append(CPPPATH=folders) - -env = Environment() -ConfigurePython(env) -ConfigureNumpy(env) -env.Append(LIBS = "boost_python") -env.Append(CPPPATH = "#") - -Export("env") +Export("bp_numpy_env") lib = SConscript("libs/python/numpy/src/SConscript") libpath = os.path.abspath("libs/python/numpy/src") if os.environ.has_key("LD_LIBRARY_PATH"): - env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) + bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) else: - env["ENV"]["LD_LIBRARY_PATH"] = libpath -env.Append(LIBPATH=libpath) -Export("lib") + bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = libpath +bp_numpy_env.Append(LIBPATH="#libs/python/numpy/src") + SConscript("libs/python/numpy/test/SConscript") diff --git a/libs/python/numpy/src/SConscript b/libs/python/numpy/src/SConscript index c06300a1..9084ae1c 100644 --- a/libs/python/numpy/src/SConscript +++ b/libs/python/numpy/src/SConscript @@ -1,5 +1,5 @@ -Import("env") +Import("bp_numpy_env") -lib = env.SharedLibrary("boost_python_numpy", Glob("*.cpp")) +lib = bp_numpy_env.SharedLibrary("boost_python_numpy", Glob("*.cpp")) Return("lib") diff --git a/libs/python/numpy/test/SConscript b/libs/python/numpy/test/SConscript index f9615976..7dc0d24b 100644 --- a/libs/python/numpy/test/SConscript +++ b/libs/python/numpy/test/SConscript @@ -1,17 +1,8 @@ -Import("env") -import os +Import("bp_numpy_env") -test_env = env.Clone() -test_env.Append(LIBS="boost_python_numpy") - -tests = ("ufunc",) -test_mods = [test_env.SharedLibrary("%s_mod" % k, "%s_mod.cpp" % k, SHLIBPREFIX="") - for k in tests] -os.path.abspath(".") -test_runs = [test_env.Command(".%s" % name, [mod,"%s.py" % name], - "cd %s; python %s.py" % (os.path.abspath("."), name)) - for name, mod in zip(tests, test_mods)] - -test = Alias("test",[test_runs]) +ufunc_mod = bp_numpy_env.SharedLibrary("ufunc_mod", "ufunc_mod.cpp", SHLIBPREFIX="", + LIBS="boost_python_numpy") +ufunc_test = bp_numpy_env.PythonUnitTest("ufunc.py", ufunc_mod) +test = ufunc_test Return("test") diff --git a/site_scons/scons_tools.py b/site_scons/scons_tools.py new file mode 100755 index 00000000..b6f74ff7 --- /dev/null +++ b/site_scons/scons_tools.py @@ -0,0 +1,236 @@ +import SCons.Script as scons +import re +import os + +database = {} + +def ApplyFlags(env, flags): + flags = env.ParseFlags(flags) + flags["CCFLAGS"] = [opt for opt in flags["CCFLAGS"] if not opt.startswith("-O")] + flags["CFLAGS"] = [opt for opt in flags["CFLAGS"] if not opt.startswith("-O")] + debug = scons.ARGUMENTS.get('debug', 0) + if int(debug): + try: + flags["CPPDEFINES"].remove("NDEBUG") + except: pass + env.MergeFlags(flags) + +class Configuration(object): + + def __new__(cls, name=None, dependencies=None): + if name is None: + name = cls.name + if dependencies is None: + dependencies = cls.dependencies + if database.has_key(name): + return database[name] + else: + self = object.__new__(cls) + self._checked = False + if not hasattr(cls, "dependencies"): + self.dependencies = dependencies + if not hasattr(cls, "name"): + self.name = name + database[name] = self + return self + + def check(self): + if scons.GetOption("help") or scons.GetOption("clean"): + return True + if not self._checked: + self._available = self._check_dependencies() and self._check() + self._checked = True + return self._available + + def _check(self): + return True + + def _check_dependencies(self): + for dependency in self.dependencies: + if not database[dependency].check(): + return False + return True + + def apply(self, environment): + if scons.GetOption("help") or scons.GetOption("clean"): + return + if self.check(): + self._apply_dependencies(environment) + self._apply(environment) + + def _apply(self, environment): + pass + + def _apply_dependencies(self, environment): + for dependency in self.dependencies: + database[dependency].apply(environment) + +class FlagConfiguration(Configuration): + + def _apply(self, environment): + ApplyFlags(environment, self._flags) + +class VariableConfiguration(Configuration): + + def _apply(self, environment): + environment.Append(**self._variables) + +class PythonConfiguration(FlagConfiguration): + + name = "python" + dependencies = () + + def _check(self): + env = MakeEnvironment() + context = scons.Configure(env) + try: + from distutils.sysconfig import get_config_vars, get_python_inc + except ImportError: + return False + self._flags = " ".join(v for v in get_config_vars("BASECFLAGS", "OPT", "BLDLIBRARY", "LIBS") + if v is not None).split() + try: # not valid for C++ + self._flags.remove("-Wstrict-prototypes") + except ValueError: pass + try: + self._flags.remove("-L.") + except ValueError: pass + self._flags = [f for f in self._flags if not f.startswith("-O")] + self._flags.append("-I%s" % get_python_inc()) + self._apply(env) + if not context.CheckHeader(["Python.h"]): + return False + context.Finish() + return True + +PythonConfiguration() + +class NumPyConfiguration(VariableConfiguration): + + name = "numpy" + dependencies = ("python",) + + def _check(self): + env = MakeEnvironment() + context = scons.Configure(env) + self._apply_dependencies(context.env) + try: + import numpy.distutils.misc_util + self._variables = {"CPPPATH": numpy.distutils.misc_util.get_numpy_include_dirs()} + except ImportError: + context.Result(False) + return False + self._apply(context.env) + if not context.CheckHeader(["Python.h", "numpy/arrayobject.h"]): + return False + context.Finish() + return True + +NumPyConfiguration() + +class LibraryConfiguration(VariableConfiguration): + + def __new__(cls, name, headers=None, libraries=None, dependencies=()): + self = VariableConfiguration.__new__(cls, name, dependencies) + self._headers = headers + self._libraries = libraries + self._variables = {} + return self + + def _check(self): + env = MakeEnvironment() + context = scons.Configure(env) + self._apply_dependencies(context.env) + self._apply(context.env) + if self._headers: + if not context.CheckHeader(self._headers, language="C++"): + return False + if self._libraries: + if not context.CheckLib(self._libraries, language="C++"): + return False + self._variables = {"LIBS": self._libraries} + context.Finish() + return True + +LibraryConfiguration(name="boost.python", + headers=["boost/python.hpp"], + libraries=["boost_python"], + dependencies=("python",) + ) + +class LocalConfiguration(VariableConfiguration): + + def __new__(cls, name, libraries=None, dependencies=()): + self = VariableConfiguration.__new__(cls, name, dependencies) + self._libraries = libraries + self._variables = {} + return self + + def _check(self): + if self._libraries: + self._variables["LIBS"] = self._libraries + return True + +def RecursiveInstall(env, target, source, regex): + regex = re.compile(regex) + source = scons.Dir(source) + target = scons.Dir(target) + result = [] + for dirpath, dirnames, filenames in os.walk(source.abspath): + try: + dirnames.remove(".svn") + except ValueError: + pass + relpath = dirpath[len(source.abspath)+1:] + for filename in filenames: + m = regex.match(filename) + if m: + result.extend(env.InstallAs(os.path.join(target.abspath, relpath, m.group(1)), + os.path.join(dirpath, m.group(1)))) + return result + +def RunProgramUnitTest(target, source, env): + path, filename = os.path.split(source[0].abspath) + if not env.Execute("cd %s; ./%s" % (path, filename)): + env.Execute(scons.Touch(target)) + +def RunPythonUnitTest(target, source, env): + path, filename = os.path.split(source[0].abspath) + if not env.Execute("cd %s; python %s" % (path, filename)): + env.Execute(scons.Touch(target)) + +def BoostUnitTest(env, name, source): + try: + libs = env["LIBS"] + ["boost_unit_test_framework"] + except KeyError: + libs = "boost_unit_test_framework" + bin = env.Program(name, source, LIBS=libs) + run = env.Command(".%s.succeeded" % name, name, RunProgramUnitTest) + env.Depends(run, bin) + return run + +def PythonUnitTest(env, script, dependencies): + run = env.Command(".%s.succeeded" % script, script, RunPythonUnitTest) + env.Depends(run, dependencies) + return run + +def SetupPackages(env, packages): + for package in packages: + database[package].apply(env) + +def MakeEnvironment(): + env = scons.Environment() + env.AddMethod(RecursiveInstall, "RecursiveInstall") + env.AddMethod(SetupPackages, "SetupPackages") + env.AddMethod(BoostUnitTest, "BoostUnitTest") + env.AddMethod(PythonUnitTest, "PythonUnitTest") + for var in ("PATH", "LD_LIBRARY_PATH", "PYTHONPATH", "PKG_CONFIG_PATH"): + if os.environ.has_key(var): + env["ENV"][var] = os.environ[var] + debug = scons.ARGUMENTS.get('debug', 0) + if int(debug): + env.Replace(CCFLAGS=["-Wall","-g","-O0"]) + else: + env.Replace(CCFLAGS=["-Wall","-O2"]) + env.Append(CPPDEFINES="NDEBUG") + return env From 40bd0326f31aa5f7e696c7c1a32e0c1407395227 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Tue, 8 Jun 2010 01:44:59 +0000 Subject: [PATCH 007/128] boost.python numpy - build system separates debug and standard builds --- SConstruct | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/SConstruct b/SConstruct index 7972cd04..fd7da357 100644 --- a/SConstruct +++ b/SConstruct @@ -5,13 +5,20 @@ bp_numpy_env = scons_tools.MakeEnvironment() bp_numpy_env.SetupPackages(["boost.python","numpy"]) bp_numpy_env.Append(CPPPATH = "#") +if ARGUMENTS.get("debug", 0): + build_dir = "build.debug" +else: + build_dir = "build" +Export("build_dir") + Export("bp_numpy_env") -lib = SConscript("libs/python/numpy/src/SConscript") -libpath = os.path.abspath("libs/python/numpy/src") +lib = SConscript("libs/python/numpy/src/SConscript", + variant_dir="%s/python/numpy/src" % build_dir, duplicate=False) +libpath = os.path.abspath("%s/python/numpy/src" % build_dir) if os.environ.has_key("LD_LIBRARY_PATH"): bp_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="#libs/python/numpy/src") +bp_numpy_env.Append(LIBPATH=libpath) -SConscript("libs/python/numpy/test/SConscript") +SConscript("libs/python/numpy/test/SConscript", variant_dir="%s/python/numpy/test" % build_dir) From f3aecdf2f498c282e8babe6e63e29d4c0b82096e Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Thu, 24 Jun 2010 22:20:55 +0000 Subject: [PATCH 008/128] boost.python.numpy - added dtype template invoker --- boost/python/numpy/dtype.hpp | 52 ++++++++++++++++++++++++ libs/python/numpy/test/SConscript | 12 ++++-- libs/python/numpy/test/templates.py | 17 ++++++++ libs/python/numpy/test/templates_mod.cpp | 34 ++++++++++++++++ 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100755 libs/python/numpy/test/templates.py create mode 100644 libs/python/numpy/test/templates_mod.cpp diff --git a/boost/python/numpy/dtype.hpp b/boost/python/numpy/dtype.hpp index 53c61f34..cb1666b5 100644 --- a/boost/python/numpy/dtype.hpp +++ b/boost/python/numpy/dtype.hpp @@ -9,6 +9,9 @@ #include #include +#include +#include + namespace boost { namespace python { namespace numpy { @@ -43,8 +46,57 @@ public: BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, object); + template + void invoke_matching_template(Function f); + }; +namespace detail { + +struct add_pointer_meta { + + template + struct apply { + typedef typename boost::add_pointer::type type; + }; + +}; + +struct dtype_template_match_found {}; + +template +struct dtype_template_invoker { + + template + void operator()(T * x) 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; +}; + +} // namespace boost::python::numpy::detail + +template +void dtype::invoke_matching_template(Function f) { + detail::dtype_template_invoker invoker(*this, f); + try { + boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker); + } catch (detail::dtype_template_match_found &) { + return; + } + PyErr_SetString(PyExc_TypeError, "numpy.dtype not found in template list."); + throw_error_already_set(); +} + } // namespace boost::python::numpy namespace converter { diff --git a/libs/python/numpy/test/SConscript b/libs/python/numpy/test/SConscript index 7dc0d24b..6e7d8d27 100644 --- a/libs/python/numpy/test/SConscript +++ b/libs/python/numpy/test/SConscript @@ -1,8 +1,12 @@ Import("bp_numpy_env") -ufunc_mod = bp_numpy_env.SharedLibrary("ufunc_mod", "ufunc_mod.cpp", SHLIBPREFIX="", - LIBS="boost_python_numpy") -ufunc_test = bp_numpy_env.PythonUnitTest("ufunc.py", ufunc_mod) +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) + ) -test = ufunc_test Return("test") diff --git a/libs/python/numpy/test/templates.py b/libs/python/numpy/test/templates.py new file mode 100755 index 00000000..c2967c1c --- /dev/null +++ b/libs/python/numpy/test/templates.py @@ -0,0 +1,17 @@ +import templates_mod +import unittest +import numpy + +class TestTemplates(unittest.TestCase): + + def testTemplates(self): + for dtype in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128): + a1 = numpy.zeros((12,), dtype=dtype) + a2 = numpy.arange(12, dtype=dtype) + templates_mod.fill(a1) + self.assert_((a1 == a2).all()) + a1 = numpy.zeros((12,), dtype=numpy.float64) + self.assertRaises(TypeError, templates_mod.fill, a1) + +if __name__=="__main__": + unittest.main() diff --git a/libs/python/numpy/test/templates_mod.cpp b/libs/python/numpy/test/templates_mod.cpp new file mode 100644 index 00000000..63e69f4d --- /dev/null +++ b/libs/python/numpy/test/templates_mod.cpp @@ -0,0 +1,34 @@ +#include +#include + +namespace bp = boost::python; + +struct ArrayFiller { + + typedef boost::mpl::vector< short, int, float, std::complex > Sequence; + + template + void apply() const { + 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); + } + } + + bp::numpy::ndarray argument; + + explicit ArrayFiller(bp::numpy::ndarray const & arg) : argument(arg) {} + +}; + +void fill(bp::numpy::ndarray const & arg) { + ArrayFiller filler(arg); + arg.get_dtype().invoke_matching_template< ArrayFiller::Sequence >(filler); +} + +BOOST_PYTHON_MODULE(templates_mod) { + bp::numpy::initialize(); + bp::def("fill", &fill); +} From c7db44f617f7e508219f7efc9ee49036cdd673b2 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Tue, 29 Jun 2010 07:55:33 +0000 Subject: [PATCH 009/128] boost.python.numpy - added ndarray::reshape --- boost/python/numpy/ndarray.hpp | 3 +++ libs/python/numpy/src/ndarray.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/boost/python/numpy/ndarray.hpp b/boost/python/numpy/ndarray.hpp index 170112c3..6ca0f499 100644 --- a/boost/python/numpy/ndarray.hpp +++ b/boost/python/numpy/ndarray.hpp @@ -115,6 +115,9 @@ public: /// @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. diff --git a/libs/python/numpy/src/ndarray.cpp b/libs/python/numpy/src/ndarray.cpp index 30820f4b..1d7641fd 100644 --- a/libs/python/numpy/src/ndarray.cpp +++ b/libs/python/numpy/src/ndarray.cpp @@ -185,6 +185,14 @@ ndarray ndarray::squeeze() const { ); } +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())))); From b988e8c45f3e7f1c9376146d1881b7099db2a2a8 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Sun, 25 Jul 2010 00:18:18 +0000 Subject: [PATCH 010/128] boost.python.numpy - adding missing symbols, fixed constness in dtype --- boost/python/numpy/dtype.hpp | 23 +++++++++++++++++++++-- libs/python/numpy/src/dtype.cpp | 5 +++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/boost/python/numpy/dtype.hpp b/boost/python/numpy/dtype.hpp index cb1666b5..edbde0e0 100644 --- a/boost/python/numpy/dtype.hpp +++ b/boost/python/numpy/dtype.hpp @@ -47,7 +47,7 @@ public: BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, object); template - void invoke_matching_template(Function f); + void invoke_matching_template(Function f) const; }; @@ -83,10 +83,29 @@ private: Function m_func; }; +template +struct dtype_template_invoker< boost::reference_wrapper > { + + template + void operator()(T * x) 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; +}; + } // namespace boost::python::numpy::detail template -void dtype::invoke_matching_template(Function f) { +void dtype::invoke_matching_template(Function f) const { detail::dtype_template_invoker invoker(*this, f); try { boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker); diff --git a/libs/python/numpy/src/dtype.cpp b/libs/python/numpy/src/dtype.cpp index 852038e7..fc882ac9 100644 --- a/libs/python/numpy/src/dtype.cpp +++ b/libs/python/numpy/src/dtype.cpp @@ -30,6 +30,10 @@ 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; @@ -61,6 +65,7 @@ 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); From 42ca807c82b0b24a6ee82a20ee7cdf47d165ad83 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Wed, 6 Oct 2010 00:31:09 +0000 Subject: [PATCH 011/128] boost.python.numpy - fixed missing bool instantiation for dtype::get_builtin --- libs/python/numpy/src/dtype.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/python/numpy/src/dtype.cpp b/libs/python/numpy/src/dtype.cpp index fc882ac9..7f9ea37e 100644 --- a/libs/python/numpy/src/dtype.cpp +++ b/libs/python/numpy/src/dtype.cpp @@ -57,6 +57,7 @@ int dtype::get_itemsize() const { 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); @@ -76,14 +77,16 @@ 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_bool)) return dtype_traits::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 From ba1416fff0a20b21e09e28f8fcd399ea0d7bd1e6 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Wed, 6 Oct 2010 19:05:20 +0000 Subject: [PATCH 012/128] boost.python.numpy - moved dtype::invoke_matching_template into separate header, added similar code for invocation based on dimensionality --- boost/python/numpy/dtype.hpp | 68 --------- boost/python/numpy/invoke_matching.hpp | 175 +++++++++++++++++++++++ boost/python/numpy/ndarray.hpp | 1 - boost/python/numpy/numpy.hpp | 1 + libs/python/numpy/test/templates.py | 12 +- libs/python/numpy/test/templates_mod.cpp | 45 ++++-- 6 files changed, 220 insertions(+), 82 deletions(-) create mode 100644 boost/python/numpy/invoke_matching.hpp diff --git a/boost/python/numpy/dtype.hpp b/boost/python/numpy/dtype.hpp index edbde0e0..35f39bba 100644 --- a/boost/python/numpy/dtype.hpp +++ b/boost/python/numpy/dtype.hpp @@ -46,76 +46,8 @@ public: BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, object); - template - void invoke_matching_template(Function f) const; - }; -namespace detail { - -struct add_pointer_meta { - - template - struct apply { - typedef typename boost::add_pointer::type type; - }; - -}; - -struct dtype_template_match_found {}; - -template -struct dtype_template_invoker { - - template - void operator()(T * x) 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 * x) 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; -}; - -} // namespace boost::python::numpy::detail - -template -void dtype::invoke_matching_template(Function f) const { - detail::dtype_template_invoker invoker(*this, f); - try { - boost::mpl::for_each< Sequence, detail::add_pointer_meta >(invoker); - } catch (detail::dtype_template_match_found &) { - return; - } - PyErr_SetString(PyExc_TypeError, "numpy.dtype not found in template list."); - throw_error_already_set(); -} - } // namespace boost::python::numpy namespace converter { diff --git a/boost/python/numpy/invoke_matching.hpp b/boost/python/numpy/invoke_matching.hpp new file mode 100644 index 00000000..546072f9 --- /dev/null +++ b/boost/python/numpy/invoke_matching.hpp @@ -0,0 +1,175 @@ +#ifndef BOOST_PYTHON_NUMPY_INVOKE_MATCHING_HPP_INCLUDED +#define BOOST_PYTHON_NUMPY_INVOKE_MATCHING_HPP_INCLUDED + +/** + * @file boost/python/numpy/ndarray.hpp + * @brief Object manager and various utilities for numpy.ndarray. + */ + +#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 * x) const { + if (dtype::get_builtin() == m_dtype) { + m_func.apply(x); + 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 * x) const { + if (dtype::get_builtin() == m_dtype) { + m_func.apply(x); + 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()(T * x) const { + if (m_nd == T::value) { + m_func.apply(x); + 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()(T * x) const { + if (m_nd == T::value) { + m_func.apply(x); + 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 { + + template + void apply(T * x) const { + invoke_matching_nd(m_nd, m_func.nest(x)); + } + + array_template_invoker_wrapper(int nd, Function func) : + m_nd(nd), m_func(func) {} + +private: + int m_nd; + Function m_func; +}; + +template +struct array_template_invoker_wrapper< DimSequence, boost::reference_wrapper > { + + template + void apply(T * x) const { + invoke_matching_nd(m_nd, m_func.nest(x)); + } + + array_template_invoker_wrapper(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_array(ndarray const & array_, Function f) { + detail::array_template_invoker_wrapper 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/ndarray.hpp b/boost/python/numpy/ndarray.hpp index 6ca0f499..0b53cc1f 100644 --- a/boost/python/numpy/ndarray.hpp +++ b/boost/python/numpy/ndarray.hpp @@ -275,7 +275,6 @@ inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) { return ndarray::bitflag(int(a) & int(b)); } - } // namespace boost::python::numpy namespace converter { diff --git a/boost/python/numpy/numpy.hpp b/boost/python/numpy/numpy.hpp index 9e0f6a44..c40859d8 100644 --- a/boost/python/numpy/numpy.hpp +++ b/boost/python/numpy/numpy.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace boost { namespace python { namespace numpy { diff --git a/libs/python/numpy/test/templates.py b/libs/python/numpy/test/templates.py index c2967c1c..e848141f 100755 --- a/libs/python/numpy/test/templates.py +++ b/libs/python/numpy/test/templates.py @@ -6,12 +6,16 @@ class TestTemplates(unittest.TestCase): def testTemplates(self): for dtype in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128): - a1 = numpy.zeros((12,), dtype=dtype) - a2 = numpy.arange(12, dtype=dtype) - templates_mod.fill(a1) - self.assert_((a1 == a2).all()) + v = numpy.arange(12, dtype=dtype) + for shape in ((12,), (4, 3), (2, 6)): + a1 = numpy.zeros(shape, dtype=dtype) + a2 = v.reshape(a1.shape) + templates_mod.fill(a1) + self.assert_((a1 == a2).all()) a1 = numpy.zeros((12,), dtype=numpy.float64) self.assertRaises(TypeError, templates_mod.fill, a1) + a1 = numpy.zeros((12,2,3), dtype=numpy.float32) + self.assertRaises(TypeError, templates_mod.fill, a1) if __name__=="__main__": unittest.main() diff --git a/libs/python/numpy/test/templates_mod.cpp b/libs/python/numpy/test/templates_mod.cpp index 63e69f4d..82c5511e 100644 --- a/libs/python/numpy/test/templates_mod.cpp +++ b/libs/python/numpy/test/templates_mod.cpp @@ -1,21 +1,48 @@ #include #include +#include namespace bp = boost::python; struct ArrayFiller { - typedef boost::mpl::vector< short, int, float, std::complex > Sequence; + typedef boost::mpl::vector< short, int, float, std::complex > TypeSequence; + typedef boost::mpl::vector_c< int, 1, 2 > DimSequence; template - void apply() const { - 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); + struct nested { + + void apply(boost::mpl::integral_c * ) const { + 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); + } } - } + + void apply(boost::mpl::integral_c * ) const { + 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); + } + } + } + + explicit nested(bp::numpy::ndarray const & arg) : argument(arg) {} + + bp::numpy::ndarray argument; + }; + + template + nested nest(T *) const { return nested(argument); } bp::numpy::ndarray argument; @@ -25,7 +52,7 @@ struct ArrayFiller { void fill(bp::numpy::ndarray const & arg) { ArrayFiller filler(arg); - arg.get_dtype().invoke_matching_template< ArrayFiller::Sequence >(filler); + bp::numpy::invoke_matching_array< ArrayFiller::TypeSequence, ArrayFiller::DimSequence >(arg, filler); } BOOST_PYTHON_MODULE(templates_mod) { From 584df88fb20c453adda847a7477e4337db7846ee Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Wed, 6 Oct 2010 22:40:41 +0000 Subject: [PATCH 013/128] boost.python.numpy - switched to simpler syntax for invoke_matching_array --- boost/python/numpy/invoke_matching.hpp | 61 ++++++++++++++---------- libs/python/numpy/test/templates_mod.cpp | 20 ++------ 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/boost/python/numpy/invoke_matching.hpp b/boost/python/numpy/invoke_matching.hpp index 546072f9..4d1a7a0f 100644 --- a/boost/python/numpy/invoke_matching.hpp +++ b/boost/python/numpy/invoke_matching.hpp @@ -9,6 +9,8 @@ #include #include +#include + namespace boost { namespace python { namespace numpy { namespace detail { @@ -29,9 +31,9 @@ template struct dtype_template_invoker { template - void operator()(T * x) const { + void operator()(T *) const { if (dtype::get_builtin() == m_dtype) { - m_func.apply(x); + m_func.template apply(); throw dtype_template_match_found(); } } @@ -48,9 +50,9 @@ template struct dtype_template_invoker< boost::reference_wrapper > { template - void operator()(T * x) const { + void operator()(T *) const { if (dtype::get_builtin() == m_dtype) { - m_func.apply(x); + m_func.template apply(); throw dtype_template_match_found(); } } @@ -66,10 +68,10 @@ private: template struct nd_template_invoker { - template - void operator()(T * x) const { - if (m_nd == T::value) { - m_func.apply(x); + template + void operator()(boost::mpl::integral_c *) const { + if (m_nd == N) { + m_func.template apply(); throw nd_template_match_found(); } } @@ -85,10 +87,10 @@ private: template struct nd_template_invoker< boost::reference_wrapper > { - template - void operator()(T * x) const { - if (m_nd == T::value) { - m_func.apply(x); + template + void operator()(boost::mpl::integral_c *) const { + if (m_nd == N) { + m_func.template apply(); throw nd_template_match_found(); } } @@ -129,31 +131,30 @@ void invoke_matching_dtype(dtype const & dtype_, Function f) { namespace detail { -template -struct array_template_invoker_wrapper { +template +struct array_template_invoker_wrapper_2 { - template - void apply(T * x) const { - invoke_matching_nd(m_nd, m_func.nest(x)); + template + void apply() const { + m_func.template apply(); } - array_template_invoker_wrapper(int nd, Function func) : - m_nd(nd), m_func(func) {} + array_template_invoker_wrapper_2(Function & func) : + m_func(func) {} private: - int m_nd; - Function m_func; + Function & m_func; }; template -struct array_template_invoker_wrapper< DimSequence, boost::reference_wrapper > { +struct array_template_invoker_wrapper_1 { template - void apply(T * x) const { - invoke_matching_nd(m_nd, m_func.nest(x)); + void apply() const { + invoke_matching_nd(m_nd, array_template_invoker_wrapper_2(m_func)); } - array_template_invoker_wrapper(int nd, Function & func) : + array_template_invoker_wrapper_1(int nd, Function & func) : m_nd(nd), m_func(func) {} private: @@ -161,11 +162,19 @@ private: 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 wrapper(array_.get_nd(), f); + detail::array_template_invoker_wrapper_1 wrapper(array_.get_nd(), f); invoke_matching_dtype(array_.get_dtype(), wrapper); } diff --git a/libs/python/numpy/test/templates_mod.cpp b/libs/python/numpy/test/templates_mod.cpp index 82c5511e..6da9f581 100644 --- a/libs/python/numpy/test/templates_mod.cpp +++ b/libs/python/numpy/test/templates_mod.cpp @@ -9,19 +9,16 @@ struct ArrayFiller { typedef boost::mpl::vector< short, int, float, std::complex > TypeSequence; typedef boost::mpl::vector_c< int, 1, 2 > DimSequence; - template - struct nested { - - void apply(boost::mpl::integral_c * ) const { + 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); } - } - - void apply(boost::mpl::integral_c * ) const { + } else { char * row_p = argument.get_data(); int row_stride = argument.strides(0); int col_stride = argument.strides(1); @@ -35,14 +32,7 @@ struct ArrayFiller { } } } - - explicit nested(bp::numpy::ndarray const & arg) : argument(arg) {} - - bp::numpy::ndarray argument; - }; - - template - nested nest(T *) const { return nested(argument); } + } bp::numpy::ndarray argument; From 252c3aa6956738660c137cdaa4e8375bbb028df3 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Tue, 4 Jan 2011 19:19:20 +0000 Subject: [PATCH 014/128] numpy - updates to site_scons, header documentation --- boost/python/numpy/invoke_matching.hpp | 4 +-- site_scons/scons_tools.py | 44 ++++++++++++++++++-------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/boost/python/numpy/invoke_matching.hpp b/boost/python/numpy/invoke_matching.hpp index 4d1a7a0f..7a7f8f3c 100644 --- a/boost/python/numpy/invoke_matching.hpp +++ b/boost/python/numpy/invoke_matching.hpp @@ -2,8 +2,8 @@ #define BOOST_PYTHON_NUMPY_INVOKE_MATCHING_HPP_INCLUDED /** - * @file boost/python/numpy/ndarray.hpp - * @brief Object manager and various utilities for numpy.ndarray. + * @file boost/python/numpy/invoke_matching.hpp + * @brief Template invocation based on dtype matching. */ #include diff --git a/site_scons/scons_tools.py b/site_scons/scons_tools.py index b6f74ff7..897de00b 100755 --- a/site_scons/scons_tools.py +++ b/site_scons/scons_tools.py @@ -1,5 +1,6 @@ import SCons.Script as scons import re +import sys import os database = {} @@ -65,6 +66,12 @@ class Configuration(object): for dependency in self.dependencies: database[dependency].apply(environment) + def require(self): + if not all(database[pkg].check() for pkg in self.dependencies): + print "Missing dependencies for required package '%s'." % self.name + scons.Exit(1) + + class FlagConfiguration(Configuration): def _apply(self, environment): @@ -82,7 +89,6 @@ class PythonConfiguration(FlagConfiguration): def _check(self): env = MakeEnvironment() - context = scons.Configure(env) try: from distutils.sysconfig import get_config_vars, get_python_inc except ImportError: @@ -98,7 +104,9 @@ class PythonConfiguration(FlagConfiguration): self._flags = [f for f in self._flags if not f.startswith("-O")] self._flags.append("-I%s" % get_python_inc()) self._apply(env) + context = scons.Configure(env) if not context.CheckHeader(["Python.h"]): + context.Finish() return False context.Finish() return True @@ -112,16 +120,17 @@ class NumPyConfiguration(VariableConfiguration): def _check(self): env = MakeEnvironment() - context = scons.Configure(env) - self._apply_dependencies(context.env) + self._apply_dependencies(env) try: import numpy.distutils.misc_util self._variables = {"CPPPATH": numpy.distutils.misc_util.get_numpy_include_dirs()} except ImportError: - context.Result(False) + print "numpy.distutils.misc_util not found" return False - self._apply(context.env) + self._apply(env) + context = scons.Configure(env) if not context.CheckHeader(["Python.h", "numpy/arrayobject.h"]): + context.Finish() return False context.Finish() return True @@ -139,14 +148,16 @@ class LibraryConfiguration(VariableConfiguration): def _check(self): env = MakeEnvironment() + self._apply_dependencies(env) + self._apply(env) context = scons.Configure(env) - self._apply_dependencies(context.env) - self._apply(context.env) if self._headers: if not context.CheckHeader(self._headers, language="C++"): + context.Finish() return False if self._libraries: if not context.CheckLib(self._libraries, language="C++"): + context.Finish() return False self._variables = {"LIBS": self._libraries} context.Finish() @@ -195,9 +206,12 @@ def RunProgramUnitTest(target, source, env): env.Execute(scons.Touch(target)) def RunPythonUnitTest(target, source, env): - path, filename = os.path.split(source[0].abspath) - if not env.Execute("cd %s; python %s" % (path, filename)): + path, filename = os.path.split(target[0].abspath) + env["ENV"]["TESTPATH"] = path + env["ENV"]["PYTHONPATH"] = ":".join([path] + env["ENV"]["PYTHONPATH"].split(":")) + if not env.Execute('%s %s' % (sys.executable, source[0].abspath)): env.Execute(scons.Touch(target)) + env["ENV"]["PYTHONPATH"] = ":".join(env["ENV"]["PYTHONPATH"].split(":")[1:]) def BoostUnitTest(env, name, source): try: @@ -205,12 +219,12 @@ def BoostUnitTest(env, name, source): except KeyError: libs = "boost_unit_test_framework" bin = env.Program(name, source, LIBS=libs) - run = env.Command(".%s.succeeded" % name, name, RunProgramUnitTest) + run = env.Command(".%s.succeeded" % str(name), name, RunProgramUnitTest) env.Depends(run, bin) return run def PythonUnitTest(env, script, dependencies): - run = env.Command(".%s.succeeded" % script, script, RunPythonUnitTest) + run = env.Command(".%s.succeeded" % str(script), script, RunPythonUnitTest) env.Depends(run, dependencies) return run @@ -219,7 +233,9 @@ def SetupPackages(env, packages): database[package].apply(env) def MakeEnvironment(): - env = scons.Environment() + env = scons.Environment(tools = ["default", "doxygen"]) + env.Append(CPPPATH="#include") + env.Append(LIBPATH="#lib") env.AddMethod(RecursiveInstall, "RecursiveInstall") env.AddMethod(SetupPackages, "SetupPackages") env.AddMethod(BoostUnitTest, "BoostUnitTest") @@ -227,10 +243,12 @@ def MakeEnvironment(): for var in ("PATH", "LD_LIBRARY_PATH", "PYTHONPATH", "PKG_CONFIG_PATH"): if os.environ.has_key(var): env["ENV"][var] = os.environ[var] + else: + env["ENV"][var] = "" debug = scons.ARGUMENTS.get('debug', 0) if int(debug): env.Replace(CCFLAGS=["-Wall","-g","-O0"]) else: env.Replace(CCFLAGS=["-Wall","-O2"]) - env.Append(CPPDEFINES="NDEBUG") + env.Append(CPPDEFINES=["NDEBUG"]) return env From 3d7f523384ab40c53ce9a69df4bf389ac4c7118f Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Thu, 6 Jan 2011 19:30:58 +0000 Subject: [PATCH 015/128] numpy - added missing doxygen builder --- site_scons/site_tools/doxygen.py | 252 +++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100755 site_scons/site_tools/doxygen.py diff --git a/site_scons/site_tools/doxygen.py b/site_scons/site_tools/doxygen.py new file mode 100755 index 00000000..c60d940e --- /dev/null +++ b/site_scons/site_tools/doxygen.py @@ -0,0 +1,252 @@ +# vim: set et sw=3 tw=0 fo=awqorc ft=python: +# +# Astxx, the Asterisk C++ API and Utility Library. +# Copyright (C) 2005, 2006 Matthew A. Nicholson +# Copyright (C) 2006 Tim Blechmann +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 2.1 as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os +import os.path +import glob +from fnmatch import fnmatch + +def DoxyfileParse(file_contents): + """ + Parse a Doxygen source file and return a dictionary of all the values. + Values will be strings and lists of strings. + """ + data = {} + + import shlex + lex = shlex.shlex(instream = file_contents, posix = True) + lex.wordchars += "*+./-:" + lex.whitespace = lex.whitespace.replace("\n", "") + lex.escape = "" + + lineno = lex.lineno + token = lex.get_token() + key = token # the first token should be a key + last_token = "" + key_token = False + next_key = False + new_data = True + + def append_data(data, key, new_data, token): + if new_data or len(data[key]) == 0: + data[key].append(token) + else: + data[key][-1] += token + + while token: + if token in ['\n']: + if last_token not in ['\\']: + key_token = True + elif token in ['\\']: + pass + elif key_token: + key = token + key_token = False + else: + if token == "+=": + if not data.has_key(key): + data[key] = list() + elif token == "=": + if key == "TAGFILES" and data.has_key(key): + append_data( data, key, False, "=" ) + new_data=False + else: + data[key] = list() + else: + append_data( data, key, new_data, token ) + new_data = True + + last_token = token + token = lex.get_token() + + if last_token == '\\' and token != '\n': + new_data = False + append_data( data, key, new_data, '\\' ) + + # compress lists of len 1 into single strings + for (k, v) in data.items(): + if len(v) == 0: + data.pop(k) + + # items in the following list will be kept as lists and not converted to strings + if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS", "TAGFILES"]: + continue + + if len(v) == 1: + data[k] = v[0] + + return data + +def DoxySourceScan(node, env, path): + """ + Doxygen Doxyfile source scanner. This should scan the Doxygen file and add + any files used to generate docs to the list of source files. + """ + default_file_patterns = [ + '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx', + '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++', + '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm', + '*.py', + ] + + default_exclude_patterns = [ + '*~', + ] + + sources = [] + + data = DoxyfileParse(node.get_contents()) + + if data.get("RECURSIVE", "NO") == "YES": + recursive = True + else: + recursive = False + + file_patterns = data.get("FILE_PATTERNS", default_file_patterns) + exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns) + + # We're running in the top-level directory, but the doxygen + # configuration file is in the same directory as node; this means + # that relative pathnames in node must be adjusted before they can + # go onto the sources list + conf_dir = os.path.dirname(str(node)) + + for node in data.get("INPUT", []): + if not os.path.isabs(node): + node = os.path.join(conf_dir, node) + if os.path.isfile(node): + sources.append(node) + elif os.path.isdir(node): + if recursive: + for root, dirs, files in os.walk(node): + for f in files: + filename = os.path.join(root, f) + + pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False) + exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True) + + if pattern_check and not exclude_check: + sources.append(filename) + else: + for pattern in file_patterns: + sources.extend(glob.glob("/".join([node, pattern]))) + + # Add tagfiles to the list of source files: + for node in data.get("TAGFILES", []): + file = node.split("=")[0] + if not os.path.isabs(file): + file = os.path.join(conf_dir, file) + sources.append(file) + + # Add additional files to the list of source files: + def append_additional_source(option): + file = data.get(option, "") + if file != "": + if not os.path.isabs(file): + file = os.path.join(conf_dir, file) + if os.path.isfile(file): + sources.append(file) + + append_additional_source("HTML_STYLESHEET") + append_additional_source("HTML_HEADER") + append_additional_source("HTML_FOOTER") + + sources = map( lambda path: env.File(path), sources ) + return sources + + +def DoxySourceScanCheck(node, env): + """Check if we should scan this file""" + return os.path.isfile(node.path) + +def DoxyEmitter(source, target, env): + """Doxygen Doxyfile emitter""" + # possible output formats and their default values and output locations + output_formats = { + "HTML": ("YES", "html"), + "LATEX": ("YES", "latex"), + "RTF": ("NO", "rtf"), + "MAN": ("YES", "man"), + "XML": ("NO", "xml"), + } + + data = DoxyfileParse(source[0].get_contents()) + + targets = [] + out_dir = data.get("OUTPUT_DIRECTORY", ".") + if not os.path.isabs(out_dir): + conf_dir = os.path.dirname(str(source[0])) + out_dir = os.path.join(conf_dir, out_dir) + + # add our output locations + for (k, v) in output_formats.items(): + if data.get("GENERATE_" + k, v[0]) == "YES": + targets.append(env.Dir( os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))) ) + + # add the tag file if neccessary: + tagfile = data.get("GENERATE_TAGFILE", "") + if tagfile != "": + if not os.path.isabs(tagfile): + conf_dir = os.path.dirname(str(source[0])) + tagfile = os.path.join(conf_dir, tagfile) + targets.append(env.File(tagfile)) + + # don't clobber targets + for node in targets: + env.Precious(node) + + # set up cleaning stuff + for node in targets: + env.Clean(node, node) + + return (targets, source) + +def generate(env): + """ + Add builders and construction variables for the + Doxygen tool. This is currently for Doxygen 1.4.6. + """ + doxyfile_scanner = env.Scanner( + DoxySourceScan, + "DoxySourceScan", + scan_check = DoxySourceScanCheck, + ) + + import SCons.Builder + doxyfile_builder = SCons.Builder.Builder( + action = "cd ${SOURCE.dir} && ${DOXYGEN} ${SOURCE.file}", + emitter = DoxyEmitter, + target_factory = env.fs.Entry, + single_source = True, + source_scanner = doxyfile_scanner, + ) + + env.Append(BUILDERS = { + 'Doxygen': doxyfile_builder, + }) + + env.AppendUnique( + DOXYGEN = 'doxygen', + ) + +def exists(env): + """ + Make sure doxygen exists. + """ + return env.Detect("doxygen") From 65db10061f8fda1272f977b5b875a10829365f1d Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Thu, 6 Jan 2011 19:37:28 +0000 Subject: [PATCH 016/128] numpy - (build system) fixed setting lib output path in tests --- SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/SConstruct b/SConstruct index fd7da357..c748a50c 100644 --- a/SConstruct +++ b/SConstruct @@ -15,6 +15,7 @@ Export("bp_numpy_env") lib = SConscript("libs/python/numpy/src/SConscript", variant_dir="%s/python/numpy/src" % build_dir, duplicate=False) libpath = os.path.abspath("%s/python/numpy/src" % build_dir) +bp_numpy_env.Append(LIBPATH=[libpath]) if os.environ.has_key("LD_LIBRARY_PATH"): bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) else: From a300f7cdd077c544bdb1fa547a60bd227d558bee Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Fri, 7 Jan 2011 23:39:45 +0000 Subject: [PATCH 017/128] boost.python.numpy - updates to build system, added some svn:ignores --- site_scons/scons_tools.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/site_scons/scons_tools.py b/site_scons/scons_tools.py index 897de00b..762b9a9a 100755 --- a/site_scons/scons_tools.py +++ b/site_scons/scons_tools.py @@ -234,8 +234,14 @@ def SetupPackages(env, packages): def MakeEnvironment(): env = scons.Environment(tools = ["default", "doxygen"]) - env.Append(CPPPATH="#include") - env.Append(LIBPATH="#lib") + if scons.ARGUMENTS.has_key('LIBPATH'): + env.Append(LIBPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['LIBPATH'].split(":")]) + if scons.ARGUMENTS.has_key('RPATH'): + env.Append(RPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['RPATH'].split(":")]) + if scons.ARGUMENTS.has_key('CPPPATH'): + env.Append(CPPPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['CPPPATH'].split(":")]) + env.Append(CPPPATH=["#include"]) + env.Append(LIBPATH=["#lib"]) env.AddMethod(RecursiveInstall, "RecursiveInstall") env.AddMethod(SetupPackages, "SetupPackages") env.AddMethod(BoostUnitTest, "BoostUnitTest") From 91a1119070963ae5de1d32ce300236ac916fee01 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Sat, 15 Jan 2011 23:00:43 +0000 Subject: [PATCH 018/128] Boost.Python.Numpy - cleaning up build system --- SConscript | 39 +++++++++ SConstruct | 25 +----- libs/python/numpy/src/SConscript | 1 - site_scons/scons_tools.py | 145 ++++++++++++++++++++++++++----- 4 files changed, 167 insertions(+), 43 deletions(-) create mode 100644 SConscript diff --git a/SConscript b/SConscript new file mode 100644 index 00000000..7f5fe223 --- /dev/null +++ b/SConscript @@ -0,0 +1,39 @@ +import scons_tools +import os + +targets = {"boost.python.numpy":{}} + +scons_tools.LocalConfiguration( + name="boost.python.numpy", + libraries=["boost_python_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()) +if os.environ.has_key("LD_LIBRARY_PATH"): + bp_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") + +targets["boost.python.numpy"]["lib"] = ( + SConscript("libs/python/numpy/src/SConscript", + variant_dir="%s/python/numpy/src" % scons_tools.GetBuildDir(), + duplicate=False) + ) +targets["boost.python.numpy"]["install"] = ( + bp_numpy_env.RecursiveInstall( + os.path.join(bp_numpy_env["INSTALL_HEADERS"], "boost"), + "boost", + regex="(.*\.hpp)") + + bp_numpy_env.Install(bp_numpy_env["INSTALL_LIB"], targets["boost.python.numpy"]["lib"]) + ) +targets["boost.python.numpy"]["test"] = ( + SConscript("libs/python/numpy/test/SConscript", + variant_dir="%s/python/numpy/test" % scons_tools.GetBuildDir()) + ) + +Return("targets") diff --git a/SConstruct b/SConstruct index c748a50c..f50f6e64 100644 --- a/SConstruct +++ b/SConstruct @@ -1,25 +1,6 @@ import scons_tools import os -bp_numpy_env = scons_tools.MakeEnvironment() -bp_numpy_env.SetupPackages(["boost.python","numpy"]) -bp_numpy_env.Append(CPPPATH = "#") - -if ARGUMENTS.get("debug", 0): - build_dir = "build.debug" -else: - build_dir = "build" -Export("build_dir") - -Export("bp_numpy_env") -lib = SConscript("libs/python/numpy/src/SConscript", - variant_dir="%s/python/numpy/src" % build_dir, duplicate=False) -libpath = os.path.abspath("%s/python/numpy/src" % build_dir) -bp_numpy_env.Append(LIBPATH=[libpath]) -if os.environ.has_key("LD_LIBRARY_PATH"): - bp_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) - -SConscript("libs/python/numpy/test/SConscript", variant_dir="%s/python/numpy/test" % build_dir) +base_env = scons_tools.MakeEnvironment() +targets = SConscript("SConscript") +scons_tools.MakeAliases(targets) diff --git a/libs/python/numpy/src/SConscript b/libs/python/numpy/src/SConscript index 9084ae1c..88cdbfe1 100644 --- a/libs/python/numpy/src/SConscript +++ b/libs/python/numpy/src/SConscript @@ -1,5 +1,4 @@ Import("bp_numpy_env") - lib = bp_numpy_env.SharedLibrary("boost_python_numpy", Glob("*.cpp")) Return("lib") diff --git a/site_scons/scons_tools.py b/site_scons/scons_tools.py index 762b9a9a..2dc98a69 100755 --- a/site_scons/scons_tools.py +++ b/site_scons/scons_tools.py @@ -1,10 +1,24 @@ import SCons.Script as scons import re +import distutils.sysconfig import sys import os database = {} +_environment = None + +scons.AddOption("--prefix", dest="install_prefix", type="string", nargs=1, action="store", metavar="DIR", + help="installation prefix") +scons.AddOption("--home", dest="install_home", type="string", nargs=1, action="store", metavar="DIR", + help="home directory to install under") +scons.AddOption("--install-lib", dest="install_lib", type="string", nargs=1, action="store", metavar="DIR", + help="installation directory for libraries") +scons.AddOption("--install-headers", dest="install_headers", type="string", nargs=1, + action="store", metavar="DIR", help="installation directory for C++ header files") +scons.AddOption("--install-python", dest="install_python", type="string", nargs=1, + action="store", metavar="DIR", help="installation directory for Python modules") + def ApplyFlags(env, flags): flags = env.ParseFlags(flags) flags["CCFLAGS"] = [opt for opt in flags["CCFLAGS"] if not opt.startswith("-O")] @@ -88,7 +102,7 @@ class PythonConfiguration(FlagConfiguration): dependencies = () def _check(self): - env = MakeEnvironment() + env = GetEnvironment().Clone() try: from distutils.sysconfig import get_config_vars, get_python_inc except ImportError: @@ -119,7 +133,7 @@ class NumPyConfiguration(VariableConfiguration): dependencies = ("python",) def _check(self): - env = MakeEnvironment() + env = GetEnvironment().Clone() self._apply_dependencies(env) try: import numpy.distutils.misc_util @@ -147,7 +161,7 @@ class LibraryConfiguration(VariableConfiguration): return self def _check(self): - env = MakeEnvironment() + env = GetEnvironment().Clone() self._apply_dependencies(env) self._apply(env) context = scons.Configure(env) @@ -232,29 +246,120 @@ def SetupPackages(env, packages): for package in packages: database[package].apply(env) -def MakeEnvironment(): - env = scons.Environment(tools = ["default", "doxygen"]) +def MakeAliases(targets): + env = GetEnvironment() + all_all = [] + build_all = [] + install_all = [] + test_all = [] + scons.Help(""" +To specify additional directories to add to the include or library paths, specify them +with colon-separated lists on the command line. For example: + +scons CPPPATH="/home/me/include:/opt/include" LIBPATH="/home/me/lib" + +Supported variables are CPPPATH, LIBPATH and RPATH. + +Global targets: 'all' (builds everything) + 'build' (builds headers, libraries, and python wrappers) + 'install' (copies files to global bin, include and lib directories) + 'test' (runs unit tests; requires install) + +These targets can be built for individual packages with the syntax +'[package]-[target]'. Some packages support additional targets, given below. + +Packages: + +""" + ) + for pkg_name in sorted(targets.keys()): + pkg_targets = targets[pkg_name] + extra_targets = tuple(k for k in pkg_targets.keys() if k not in + ("all","build","install","test")) + if extra_targets: + scons.Help("%-25s %s\n" % (pkg_name, ", ".join("'%s'" % k for k in extra_targets))) + else: + scons.Help("%-25s (none)\n" % pkg_name) + pkg_all = pkg_targets.values() + pkg_build = [pkg_targets[k] for k in ("headers", "lib", "python") if k in pkg_targets] + env.Alias(pkg_name, pkg_all) + env.Alias("%s-all" % pkg_name, pkg_all) + env.Alias("%s-build" % pkg_name, pkg_build) + for target_name in pkg_targets: + env.Alias("%s-%s" % (pkg_name, target_name), pkg_targets[target_name]) + all_all.extend(pkg_all) + build_all.extend(pkg_build) + install_all.extend(pkg_targets.get("install", pkg_build)) + test_all.extend(pkg_targets.get("test", pkg_targets.get("install", pkg_build))) + env.Alias("all", all_all) + env.Alias("build", build_all) + env.Alias("install", install_all) + env.Alias("test", test_all) + env.Default("build") + + +def MakeEnvironment(default_prefix="/usr/local", prefix_is_home=False): + global _environment + _environment = scons.Environment(tools = ["default", "doxygen"]) if scons.ARGUMENTS.has_key('LIBPATH'): - env.Append(LIBPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['LIBPATH'].split(":")]) + _environment.Append(LIBPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['LIBPATH'].split(":")]) if scons.ARGUMENTS.has_key('RPATH'): - env.Append(RPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['RPATH'].split(":")]) + _environment.Append(RPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['RPATH'].split(":")]) if scons.ARGUMENTS.has_key('CPPPATH'): - env.Append(CPPPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['CPPPATH'].split(":")]) - env.Append(CPPPATH=["#include"]) - env.Append(LIBPATH=["#lib"]) - env.AddMethod(RecursiveInstall, "RecursiveInstall") - env.AddMethod(SetupPackages, "SetupPackages") - env.AddMethod(BoostUnitTest, "BoostUnitTest") - env.AddMethod(PythonUnitTest, "PythonUnitTest") + _environment.Append(CPPPATH=[os.path.abspath(s) for s in scons.ARGUMENTS['CPPPATH'].split(":")]) + prefix = scons.GetOption("install_prefix") + if prefix is None: + prefix = scons.GetOption("install_home") + if prefix is None: + prefix = default_prefix + else: + preefix_is_home = True + install_lib = scons.GetOption("install_lib") + if install_lib is None: + install_lib = os.path.join(prefix, "lib") + install_headers = scons.GetOption("install_headers") + if install_headers is None: + install_headers = os.path.join(prefix, "include") + install_python = scons.GetOption("install_python") + if install_python is None: + if prefix_is_home: + install_python = os.path.join(install_lib, "python") + else: + python_lib = distutils.sysconfig.get_python_lib() + if python_lib.startswith(distutils.sysconfig.PREFIX): + install_python = os.path.join(prefix, python_lib[len(distutils.sysconfig.PREFIX)+1:]) + else: + print "Cannot determine default Python install directory." + print "Please specify --install-python on the command line." + scons.Exit(1) + _environment["INSTALL_LIB"] = install_lib + _environment["INSTALL_HEADERS"] = install_headers + _environment["INSTALL_PYTHON"] = install_python + _environment.AddMethod(RecursiveInstall, "RecursiveInstall") + _environment.AddMethod(SetupPackages, "SetupPackages") + _environment.AddMethod(BoostUnitTest, "BoostUnitTest") + _environment.AddMethod(PythonUnitTest, "PythonUnitTest") for var in ("PATH", "LD_LIBRARY_PATH", "PYTHONPATH", "PKG_CONFIG_PATH"): if os.environ.has_key(var): - env["ENV"][var] = os.environ[var] + _environment["ENV"][var] = os.environ[var] else: - env["ENV"][var] = "" + _environment["ENV"][var] = "" debug = scons.ARGUMENTS.get('debug', 0) if int(debug): - env.Replace(CCFLAGS=["-Wall","-g","-O0"]) + _environment.Replace(CCFLAGS=["-Wall","-g","-O0"]) else: - env.Replace(CCFLAGS=["-Wall","-O2"]) - env.Append(CPPDEFINES=["NDEBUG"]) - return env + _environment.Replace(CCFLAGS=["-Wall","-O2"]) + _environment.Append(CPPDEFINES=["NDEBUG"]) + return _environment + +def GetEnvironment(): + if _environment is None: + raise LogicErrorException("scons_tools error: root environment not initialized") + return _environment + +def GetBuildDir(): + if scons.ARGUMENTS.get("debug", 0): + return "build.debug" + else: + return "build" + From 70c7fbd1d844b24c329e94090f5469dcc5c5c297 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Mon, 17 Jan 2011 20:01:48 +0000 Subject: [PATCH 019/128] Boost.Python.Numpy - removing malfunctioning variant build directories --- SConscript | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/SConscript b/SConscript index 7f5fe223..3ffd5a57 100644 --- a/SConscript +++ b/SConscript @@ -19,11 +19,7 @@ bp_numpy_env.Append(LIBPATH=[libpath]) bp_numpy_env.SetupPackages(["boost.python", "numpy"]) Export("bp_numpy_env") -targets["boost.python.numpy"]["lib"] = ( - SConscript("libs/python/numpy/src/SConscript", - variant_dir="%s/python/numpy/src" % scons_tools.GetBuildDir(), - duplicate=False) - ) +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"), @@ -31,9 +27,7 @@ targets["boost.python.numpy"]["install"] = ( regex="(.*\.hpp)") + bp_numpy_env.Install(bp_numpy_env["INSTALL_LIB"], targets["boost.python.numpy"]["lib"]) ) -targets["boost.python.numpy"]["test"] = ( - SConscript("libs/python/numpy/test/SConscript", - variant_dir="%s/python/numpy/test" % scons_tools.GetBuildDir()) - ) +targets["boost.python.numpy"]["test"] = SConscript("libs/python/numpy/test/SConscript") + Return("targets") From 77ec5715115d614d167fbf32c7a5eb5c903fbd3d Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Wed, 16 Feb 2011 00:24:22 +0000 Subject: [PATCH 020/128] Boost.Python.Numpy - moved convenience header one directory lower --- boost/python/{numpy => }/numpy.hpp | 0 boost/python/numpy/internal.hpp | 2 +- libs/python/numpy/test/templates_mod.cpp | 2 +- libs/python/numpy/test/ufunc_mod.cpp | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename boost/python/{numpy => }/numpy.hpp (100%) diff --git a/boost/python/numpy/numpy.hpp b/boost/python/numpy.hpp similarity index 100% rename from boost/python/numpy/numpy.hpp rename to boost/python/numpy.hpp diff --git a/boost/python/numpy/internal.hpp b/boost/python/numpy/internal.hpp index 1ec1f19f..55d1c70c 100644 --- a/boost/python/numpy/internal.hpp +++ b/boost/python/numpy/internal.hpp @@ -21,7 +21,7 @@ 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; } diff --git a/libs/python/numpy/test/templates_mod.cpp b/libs/python/numpy/test/templates_mod.cpp index 6da9f581..2b6e9c7e 100644 --- a/libs/python/numpy/test/templates_mod.cpp +++ b/libs/python/numpy/test/templates_mod.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/libs/python/numpy/test/ufunc_mod.cpp b/libs/python/numpy/test/ufunc_mod.cpp index 0fc2e3a6..4b5c9563 100644 --- a/libs/python/numpy/test/ufunc_mod.cpp +++ b/libs/python/numpy/test/ufunc_mod.cpp @@ -1,4 +1,4 @@ -#include +#include namespace bp = boost::python; From f0345b25211df0800ff579b46250d98ea49dfebe Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 28 May 2011 12:45:52 +0000 Subject: [PATCH 021/128] New addition to support boost.build --- Jamroot.jam | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Jamroot.jam diff --git a/Jamroot.jam b/Jamroot.jam new file mode 100644 index 00000000..aeaff166 --- /dev/null +++ b/Jamroot.jam @@ -0,0 +1,7 @@ + + +project boost_numpy + : requirements . + : build-dir bin.v2 + ; + From 64b2c1697b70a05941fea3fa7381c912a3203834 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 28 May 2011 12:47:37 +0000 Subject: [PATCH 022/128] Patch to fix scons issue --- patch | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 patch diff --git a/patch b/patch new file mode 100644 index 00000000..ddaa489d --- /dev/null +++ b/patch @@ -0,0 +1,14 @@ +Index: SConscript +=================================================================== +--- SConscript (revision 70774) ++++ SConscript (working copy) +@@ -10,7 +10,8 @@ + ) + 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()) ++#libpath = os.path.abspath("%s/python/numpy/src" % scons_tools.GetBuildDir()) ++libpath = os.path.abspath("libs/python/numpy/src") + if os.environ.has_key("LD_LIBRARY_PATH"): + bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) + else: From 930167e9612f27ecc341f392b89abd45c8028656 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 28 May 2011 12:52:29 +0000 Subject: [PATCH 023/128] New addition to support boost.build --- libs/python/numpy/src/Jamfile | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 libs/python/numpy/src/Jamfile diff --git a/libs/python/numpy/src/Jamfile b/libs/python/numpy/src/Jamfile new file mode 100644 index 00000000..c8b6fd3b --- /dev/null +++ b/libs/python/numpy/src/Jamfile @@ -0,0 +1,61 @@ +import os ; +import indirect ; +import modules ; +import feature ; +import python ; + +using python ; + +libraries = ; + +local rule probe ( python-cmd ) +{ + local full-cmd = + $(python-cmd)" -c \"from numpy.distutils import misc_util; print misc_util.get_numpy_include_dirs()" ; + + local output = [ shell-cmd $(full-cmd) ] ; + return $(output) ; +} + + + +if [ python.configured ] +{ + +project boost/numpy + : source-location . +# : requirements + ; + +lib boost_python ; + +lib boost_numpy + : # sources + dtype.cpp + scalars.cpp + ndarray.cpp + matrix.cpp + ufunc.cpp + numpy.cpp + : # requirements + /python//python_for_extensions + #/boost/python//boost_python + boost_python + : # default build + shared + ; + +libraries += boost_numpy ; + +} +else if ! ( --without-python in [ modules.peek : ARGV ] ) +{ + message boost_numpy + : "warning: No python installation configured and autoconfiguration" + : "note: failed. See http://www.boost.org/libs/python/doc/building.html" + : "note: for configuration instructions or pass --without-python to" + : "note: suppress this message and silently skip all Boost.NumPy targets" + ; +} + +#boost-install $(libraries) ; From 39b90471900fc00b9663572cb723bf65feca7c30 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 28 May 2011 12:53:03 +0000 Subject: [PATCH 024/128] New addition to support boost.build --- libs/python/numpy/test/Jamfile | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 libs/python/numpy/test/Jamfile diff --git a/libs/python/numpy/test/Jamfile b/libs/python/numpy/test/Jamfile new file mode 100644 index 00000000..1e712918 --- /dev/null +++ b/libs/python/numpy/test/Jamfile @@ -0,0 +1,26 @@ +import testing ; +import python ; + +use-project /boost/numpy : ../src ; +project /boost/numpy/test + : requirements + # Why isn't this provided by 'using python' ? + #/usr/include/python2.7 + ; + +rule numpy-test ( name : sources * : requirements * ) +{ + # local s ; + sources ?= $(name).py $(name)_mod.cpp ; + return [ testing.make-test run-pyd : $(sources) /boost/numpy//boost_numpy + : $(requirements) : $(name) ] ; +} + + +test-suite numpy + : + +[ numpy-test templates ] +[ numpy-test ufunc ] + + ; From 6904a166f70ea06608b5d05c51caf149499bc6d9 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sun, 29 May 2011 18:33:56 +0000 Subject: [PATCH 025/128] Updated to include the numpy.jam module --- libs/python/numpy/src/Jamfile | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/libs/python/numpy/src/Jamfile b/libs/python/numpy/src/Jamfile index c8b6fd3b..e2c8c4c2 100644 --- a/libs/python/numpy/src/Jamfile +++ b/libs/python/numpy/src/Jamfile @@ -1,23 +1,11 @@ -import os ; -import indirect ; -import modules ; -import feature ; import python ; +import numpy ; using python ; libraries = ; -local rule probe ( python-cmd ) -{ - local full-cmd = - $(python-cmd)" -c \"from numpy.distutils import misc_util; print misc_util.get_numpy_include_dirs()" ; - - local output = [ shell-cmd $(full-cmd) ] ; - return $(output) ; -} - - +#How will the probe command be called ? if [ python.configured ] { From 37968255235c16fe4b6da061819709e4788b0463 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sun, 29 May 2011 18:34:21 +0000 Subject: [PATCH 026/128] Updated to include the numpy.jam module --- libs/python/numpy/test/Jamfile | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/libs/python/numpy/test/Jamfile b/libs/python/numpy/test/Jamfile index 1e712918..4f65c406 100644 --- a/libs/python/numpy/test/Jamfile +++ b/libs/python/numpy/test/Jamfile @@ -1,5 +1,5 @@ -import testing ; -import python ; +import numpy ; + use-project /boost/numpy : ../src ; project /boost/numpy/test @@ -8,19 +8,11 @@ project /boost/numpy/test #/usr/include/python2.7 ; -rule numpy-test ( name : sources * : requirements * ) -{ - # local s ; - sources ?= $(name).py $(name)_mod.cpp ; - return [ testing.make-test run-pyd : $(sources) /boost/numpy//boost_numpy - : $(requirements) : $(name) ] ; -} - test-suite numpy : -[ numpy-test templates ] -[ numpy-test ufunc ] +[ numpy.numpy-test templates ] +[ numpy.numpy-test ufunc ] ; From 718cfd468ef05b5927e244c1de2a9fe0a8e8f776 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sun, 29 May 2011 18:39:06 +0000 Subject: [PATCH 027/128] First build of numpy.jam module to check for the location of the numpy build directory --- numpy.jam | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 numpy.jam diff --git a/numpy.jam b/numpy.jam new file mode 100644 index 00000000..e18a0b5d --- /dev/null +++ b/numpy.jam @@ -0,0 +1,18 @@ +import testing ; +import python ; + +rule numpy-test ( name : sources * : requirements * ) +{ + sources ?= $(name).py $(name)_mod.cpp ; + return [ testing.make-test run-pyd : $(sources) /boost/numpy//boost_numpy + : $(requirements) : $(name) ] ; +} + +rule probe ( python-cmd ) +{ + local full-cmd = + $(python-cmd)" -c \"from numpy.distutils import misc_util; print misc_util.get_numpy_include_dirs()" ; + + local output = [ shell-cmd $(full-cmd) ] ; + return $(output) ; +} From daf466a697f8cb93b895b4da0f2905689dc6ab38 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Mon, 30 May 2011 01:18:16 +0000 Subject: [PATCH 028/128] Work on numpy configuration --- numpy.jam | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/numpy.jam b/numpy.jam index e18a0b5d..79b53169 100644 --- a/numpy.jam +++ b/numpy.jam @@ -1,18 +1,23 @@ import testing ; import python ; +rule init ( ) +{ + project.push-current $(.project) ; + debug-message Configuring numpy... ; + + local full-cmd = + $(python.interpreter)" -c \"from numpy.distutils import misc_util; print misc_util.get_numpy_include_dirs()" ; + + local output = [ shell-cmd $(full-cmd) ] ; + includes = $(output) ; + + project.pop-current ; +} + rule numpy-test ( name : sources * : requirements * ) { sources ?= $(name).py $(name)_mod.cpp ; return [ testing.make-test run-pyd : $(sources) /boost/numpy//boost_numpy : $(requirements) : $(name) ] ; } - -rule probe ( python-cmd ) -{ - local full-cmd = - $(python-cmd)" -c \"from numpy.distutils import misc_util; print misc_util.get_numpy_include_dirs()" ; - - local output = [ shell-cmd $(full-cmd) ] ; - return $(output) ; -} From 0a76801936e53a6859f6648d5c6f43f68860cbef Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Mon, 30 May 2011 20:39:43 +0000 Subject: [PATCH 029/128] Move build system improvements --- Jamroot.jam => Jamroot | 0 libs/python/numpy/src/Jamfile | 23 +++++++++++++++++++---- libs/python/numpy/test/Jamfile | 20 +++++++++++--------- numpy.jam | 23 ----------------------- patch | 14 -------------- 5 files changed, 30 insertions(+), 50 deletions(-) rename Jamroot.jam => Jamroot (100%) delete mode 100644 numpy.jam delete mode 100644 patch diff --git a/Jamroot.jam b/Jamroot similarity index 100% rename from Jamroot.jam rename to Jamroot diff --git a/libs/python/numpy/src/Jamfile b/libs/python/numpy/src/Jamfile index e2c8c4c2..8f52e27b 100644 --- a/libs/python/numpy/src/Jamfile +++ b/libs/python/numpy/src/Jamfile @@ -1,12 +1,10 @@ import python ; -import numpy ; - +#import numpy ; +import regex ; using python ; libraries = ; -#How will the probe command be called ? - if [ python.configured ] { @@ -17,6 +15,20 @@ project boost/numpy lib boost_python ; +rule numpy-includes ( properties * ) +{ + import feature ; + local python-interpreter = [ feature.get-values python.interpreter : $(properties) ] ; + if $(python-interpreter) + { + local full-cmd = + $(python-interpreter)" -c \"from numpy.distutils import misc_util; print ':'.join(misc_util.get_numpy_include_dirs())\" " ; + local output = [ SHELL $(full-cmd) ] ; + local includes = [ regex.split $(output) ":" ] ; + return $(includes) ; + } +} + lib boost_numpy : # sources dtype.cpp @@ -29,8 +41,11 @@ lib boost_numpy /python//python_for_extensions #/boost/python//boost_python boost_python + @numpy-includes : # default build shared + : # usage requirements + @numpy-includes ; libraries += boost_numpy ; diff --git a/libs/python/numpy/test/Jamfile b/libs/python/numpy/test/Jamfile index 4f65c406..6d6b0344 100644 --- a/libs/python/numpy/test/Jamfile +++ b/libs/python/numpy/test/Jamfile @@ -1,18 +1,20 @@ -import numpy ; - +import testing ; +import python ; use-project /boost/numpy : ../src ; -project /boost/numpy/test - : requirements - # Why isn't this provided by 'using python' ? - #/usr/include/python2.7 - ; +project /boost/numpy/test ; +rule numpy-test ( name : sources * : requirements * ) +{ + sources ?= $(name).py $(name)_mod.cpp ; + return [ testing.make-test run-pyd : $(sources) /boost/numpy//boost_numpy + : $(requirements) : $(name) ] ; +} test-suite numpy : -[ numpy.numpy-test templates ] -[ numpy.numpy-test ufunc ] + [ numpy-test templates ] + [ numpy-test ufunc ] ; diff --git a/numpy.jam b/numpy.jam deleted file mode 100644 index 79b53169..00000000 --- a/numpy.jam +++ /dev/null @@ -1,23 +0,0 @@ -import testing ; -import python ; - -rule init ( ) -{ - project.push-current $(.project) ; - debug-message Configuring numpy... ; - - local full-cmd = - $(python.interpreter)" -c \"from numpy.distutils import misc_util; print misc_util.get_numpy_include_dirs()" ; - - local output = [ shell-cmd $(full-cmd) ] ; - includes = $(output) ; - - project.pop-current ; -} - -rule numpy-test ( name : sources * : requirements * ) -{ - sources ?= $(name).py $(name)_mod.cpp ; - return [ testing.make-test run-pyd : $(sources) /boost/numpy//boost_numpy - : $(requirements) : $(name) ] ; -} diff --git a/patch b/patch deleted file mode 100644 index ddaa489d..00000000 --- a/patch +++ /dev/null @@ -1,14 +0,0 @@ -Index: SConscript -=================================================================== ---- SConscript (revision 70774) -+++ SConscript (working copy) -@@ -10,7 +10,8 @@ - ) - 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()) -+#libpath = os.path.abspath("%s/python/numpy/src" % scons_tools.GetBuildDir()) -+libpath = os.path.abspath("libs/python/numpy/src") - if os.environ.has_key("LD_LIBRARY_PATH"): - bp_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) - else: From 2794a9bd154d0336b80063ae91429c7fc8b150c4 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Tue, 14 Jun 2011 03:32:10 +0000 Subject: [PATCH 030/128] Test to check the shape of the ndarray --- libs/python/numpy/test/shapes.py | 14 ++++++++++++++ libs/python/numpy/test/shapes_mod.cpp | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 libs/python/numpy/test/shapes.py create mode 100644 libs/python/numpy/test/shapes_mod.cpp diff --git a/libs/python/numpy/test/shapes.py b/libs/python/numpy/test/shapes.py new file mode 100644 index 00000000..de83b412 --- /dev/null +++ b/libs/python/numpy/test/shapes.py @@ -0,0 +1,14 @@ +import shapes_mod +import unittest +import numpy + +class TestShapes(unittest.TestCase): + + def testShapes(self): + a1 = numpy.array([(0,1),(2,3)]) + a1_shape = (1,4) + a1 = shapes_mod.reshape(a1,a1_shape) + self.assertEqual(a1_shape,a1.shape) + +if __name__=="__main__": + unittest.main() diff --git a/libs/python/numpy/test/shapes_mod.cpp b/libs/python/numpy/test/shapes_mod.cpp new file mode 100644 index 00000000..0e953364 --- /dev/null +++ b/libs/python/numpy/test/shapes_mod.cpp @@ -0,0 +1,13 @@ +#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); +} From 20b68f2a2f32601b32f34c63100539717ce6eaee Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Tue, 14 Jun 2011 03:36:59 +0000 Subject: [PATCH 031/128] Added build rule for shapes --- libs/python/numpy/test/Jamfile | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/python/numpy/test/Jamfile b/libs/python/numpy/test/Jamfile index 6d6b0344..fdd66c4b 100644 --- a/libs/python/numpy/test/Jamfile +++ b/libs/python/numpy/test/Jamfile @@ -16,5 +16,6 @@ test-suite numpy [ numpy-test templates ] [ numpy-test ufunc ] + [ numpy-test shapes ] ; From b269b4b124a9b7a746c2981c7a1b507a2a089cdd Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sun, 19 Jun 2011 19:38:51 +0000 Subject: [PATCH 032/128] Adding test for ndarray.Fails as of now. --- libs/python/numpy/test/ndarray.py | 44 ++++++++++++++++++++++++++ libs/python/numpy/test/ndarray_mod.cpp | 34 ++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 libs/python/numpy/test/ndarray.py create mode 100644 libs/python/numpy/test/ndarray_mod.cpp diff --git a/libs/python/numpy/test/ndarray.py b/libs/python/numpy/test/ndarray.py new file mode 100644 index 00000000..905e7cab --- /dev/null +++ b/libs/python/numpy/test/ndarray.py @@ -0,0 +1,44 @@ +import ndarray_mod +import unittest +import numpy + +class TestNdarray(unittest.TestCase): + + def testNdzeros(self): + for dtp in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128): + v = numpy.zeros(60, dtype=dtp) + dt = numpy.dtype(dtp) + for shape in ((60,),(6,10),(4,3,5),(2,2,3,5)): + a1 = ndarray_mod.zeros(shape,dt) + a2 = v.reshape(a1.shape) + self.assertEqual(shape,a1.shape) + self.assert_((a1 == a2).all()) + + def testNdarray(self): + a = range(0,60) + for dtp in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128): + v = numpy.array(a, dtype=dtp) + dt = numpy.dtype(dtp) + a1 = ndarray_mod.array(a) + a2 = ndarray_mod.array(a,dt) + self.assert_((a1 == v).all()) + self.assert_((a2 == v).all()) + for shape in ((60,),(6,10),(4,3,5),(2,2,3,5)): + a1 = a1.reshape(shape) + self.assertEqual(shape,a1.shape) + a2 = a2.reshape(shape) + self.assertEqual(shape,a2.shape) + + def testNdempty(self): + for dtp in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128): + dt = numpy.dtype(dtp) + for shape in ((60,),(6,10),(4,3,5),(2,2,3,5)): + a1 = ndarray_mod.empty(shape,dt) + a2 = ndarray_mod.empty(len(shape),shape,dt) + self.assert_(shape,a1.shape) + self.assert_(type(a1),dtp) + self.assert_(shape,a2.shape) + self.assert_(dt,type(a2)) + +if __name__=="__main__": + unittest.main() diff --git a/libs/python/numpy/test/ndarray_mod.cpp b/libs/python/numpy/test/ndarray_mod.cpp new file mode 100644 index 00000000..9ec2a7a1 --- /dev/null +++ b/libs/python/numpy/test/ndarray_mod.cpp @@ -0,0 +1,34 @@ +#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(int nd,bp::tuple shape, bp::numpy::dtype dt) { + bp::tuple c_tup = bp::make_tuple(shape); + Py_intptr_t* c_shape = bp::extract(c_tup); + return bp::numpy::empty(nd,c_shape,dt); +} + +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("empty",&c_empty); +} From 36ee7d23f9e2073c8321f7287c27042a73f57ed4 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 19 Jun 2011 20:08:48 +0000 Subject: [PATCH 033/128] Fix ndarray tests. --- libs/python/numpy/test/Jamfile | 3 ++- libs/python/numpy/test/ndarray.py | 10 +++++----- libs/python/numpy/test/ndarray_mod.cpp | 14 +++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libs/python/numpy/test/Jamfile b/libs/python/numpy/test/Jamfile index fdd66c4b..9d335382 100644 --- a/libs/python/numpy/test/Jamfile +++ b/libs/python/numpy/test/Jamfile @@ -16,6 +16,7 @@ test-suite numpy [ numpy-test templates ] [ numpy-test ufunc ] - [ numpy-test shapes ] + [ numpy-test shapes ] + [ numpy-test ndarray ] ; diff --git a/libs/python/numpy/test/ndarray.py b/libs/python/numpy/test/ndarray.py index 905e7cab..8ec85a80 100644 --- a/libs/python/numpy/test/ndarray.py +++ b/libs/python/numpy/test/ndarray.py @@ -34,11 +34,11 @@ class TestNdarray(unittest.TestCase): dt = numpy.dtype(dtp) for shape in ((60,),(6,10),(4,3,5),(2,2,3,5)): a1 = ndarray_mod.empty(shape,dt) - a2 = ndarray_mod.empty(len(shape),shape,dt) - self.assert_(shape,a1.shape) - self.assert_(type(a1),dtp) - self.assert_(shape,a2.shape) - self.assert_(dt,type(a2)) + a2 = ndarray_mod.c_empty(shape,dt) + self.assertEqual(shape,a1.shape) + #self.assert_(type(a1),dtp) + self.assertEqual(shape,a2.shape) + #self.assert_(dtp,type(a2)) if __name__=="__main__": unittest.main() diff --git a/libs/python/numpy/test/ndarray_mod.cpp b/libs/python/numpy/test/ndarray_mod.cpp index 9ec2a7a1..355fb18d 100644 --- a/libs/python/numpy/test/ndarray_mod.cpp +++ b/libs/python/numpy/test/ndarray_mod.cpp @@ -18,10 +18,14 @@ bp::numpy::ndarray empty1(bp::tuple shape, bp::numpy::dtype dt) { return bp::numpy::empty(shape,dt); } -bp::numpy::ndarray c_empty(int nd,bp::tuple shape, bp::numpy::dtype dt) { - bp::tuple c_tup = bp::make_tuple(shape); - Py_intptr_t* c_shape = bp::extract(c_tup); - return bp::numpy::empty(nd,c_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]); + return bp::numpy::empty(len, c_shape, dt); } BOOST_PYTHON_MODULE(ndarray_mod) { @@ -30,5 +34,5 @@ BOOST_PYTHON_MODULE(ndarray_mod) { bp::def("array",&array2); bp::def("array",&array1); bp::def("empty",&empty1); - bp::def("empty",&c_empty); + bp::def("c_empty",&c_empty); } From 69d9f34f3e9ac0a496e9f4412f87808c671f6585 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 19 Jun 2011 20:16:06 +0000 Subject: [PATCH 034/128] Fix ndarray tests. --- libs/python/numpy/test/ndarray.py | 2 +- libs/python/numpy/test/ndarray_mod.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/python/numpy/test/ndarray.py b/libs/python/numpy/test/ndarray.py index 8ec85a80..ebc032bd 100644 --- a/libs/python/numpy/test/ndarray.py +++ b/libs/python/numpy/test/ndarray.py @@ -38,7 +38,7 @@ class TestNdarray(unittest.TestCase): self.assertEqual(shape,a1.shape) #self.assert_(type(a1),dtp) self.assertEqual(shape,a2.shape) - #self.assert_(dtp,type(a2)) + #self.assert_(dt,type(a2)) if __name__=="__main__": unittest.main() diff --git a/libs/python/numpy/test/ndarray_mod.cpp b/libs/python/numpy/test/ndarray_mod.cpp index 355fb18d..f328060e 100644 --- a/libs/python/numpy/test/ndarray_mod.cpp +++ b/libs/python/numpy/test/ndarray_mod.cpp @@ -25,7 +25,9 @@ bp::numpy::ndarray c_empty(bp::tuple shape, bp::numpy::dtype dt) { Py_intptr_t *c_shape = new Py_intptr_t[len]; for (unsigned i = 0; i != len; ++i) c_shape[i] = bp::extract(shape[i]); - return bp::numpy::empty(len, c_shape, dt); + bp::numpy::ndarray result = bp::numpy::empty(len, c_shape, dt); + delete [] c_shape; + return result; } BOOST_PYTHON_MODULE(ndarray_mod) { From 085f574d6ef6dd93675b78c65331eba7311680e9 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Mon, 20 Jun 2011 17:18:43 +0000 Subject: [PATCH 035/128] Added more tests for ndarray --- libs/python/numpy/test/ndarray.py | 22 ++++++++++++++++++++-- libs/python/numpy/test/ndarray_mod.cpp | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/libs/python/numpy/test/ndarray.py b/libs/python/numpy/test/ndarray.py index ebc032bd..c1f148b6 100644 --- a/libs/python/numpy/test/ndarray.py +++ b/libs/python/numpy/test/ndarray.py @@ -36,9 +36,27 @@ class TestNdarray(unittest.TestCase): a1 = ndarray_mod.empty(shape,dt) a2 = ndarray_mod.c_empty(shape,dt) self.assertEqual(shape,a1.shape) - #self.assert_(type(a1),dtp) self.assertEqual(shape,a2.shape) - #self.assert_(dt,type(a2)) + + def testTranspose(self): + for dtp in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128): + dt = numpy.dtype(dtp) + for shape in ((6,10),(4,3,5),(2,2,3,5)): + a1 = numpy.empty(shape,dt) + a2 = a1.transpose() + a1 = ndarray_mod.transpose(a1) + self.assertEqual(a1.shape,a2.shape) + + def testSqueeze(self): + a1 = numpy.array([[[3,4,5]]]) + a2 = a1.squeeze() + a1 = ndarray_mod.squeeze(a1) + self.assertEqual(a1.shape,a2.shape) + + def testReshape(self): + a1 = numpy.empty((2,2)) + a2 = ndarray_mod.reshape(a1,(1,4)) + self.assertEqual(a2.shape,(1,4)) if __name__=="__main__": unittest.main() diff --git a/libs/python/numpy/test/ndarray_mod.cpp b/libs/python/numpy/test/ndarray_mod.cpp index f328060e..73a14c6a 100644 --- a/libs/python/numpy/test/ndarray_mod.cpp +++ b/libs/python/numpy/test/ndarray_mod.cpp @@ -30,6 +30,17 @@ bp::numpy::ndarray c_empty(bp::tuple shape, bp::numpy::dtype dt) { 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); @@ -37,4 +48,7 @@ BOOST_PYTHON_MODULE(ndarray_mod) { 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); } From 419b6ec97313ea9c5e0efcfe748f9d6678c26b8d Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Fri, 24 Jun 2011 12:58:57 +0000 Subject: [PATCH 036/128] Added tests for indexing --- libs/python/numpy/test/Jamfile | 1 + libs/python/numpy/test/indexing.py | 21 ++++++++++++++++ libs/python/numpy/test/indexing_mod.cpp | 33 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 libs/python/numpy/test/indexing.py create mode 100644 libs/python/numpy/test/indexing_mod.cpp diff --git a/libs/python/numpy/test/Jamfile b/libs/python/numpy/test/Jamfile index 9d335382..75b15835 100644 --- a/libs/python/numpy/test/Jamfile +++ b/libs/python/numpy/test/Jamfile @@ -18,5 +18,6 @@ test-suite numpy [ numpy-test ufunc ] [ numpy-test shapes ] [ numpy-test ndarray ] + [ numpy-test indexing ] ; diff --git a/libs/python/numpy/test/indexing.py b/libs/python/numpy/test/indexing.py new file mode 100644 index 00000000..0b4986c5 --- /dev/null +++ b/libs/python/numpy/test/indexing.py @@ -0,0 +1,21 @@ +import unittest +import numpy +import indexing_mod + +class TestIndexing(unittest.TestCase): + + def testSingle(self): + x = numpy.arange(0,10) + for i in range(0,10): + indexing_mod.single(x,i,i) + for i in range(-10,0): + indexing_mod.single(x,i,10+i) + + def testSlice(self): + x = numpy.arange(0,10) + sl = slice(3,8) + b = [3,4,5,6,7] + indexing_mod.slice(x,sl,b) + +if __name__=="__main__": + unittest.main() diff --git a/libs/python/numpy/test/indexing_mod.cpp b/libs/python/numpy/test/indexing_mod.cpp new file mode 100644 index 00000000..9adf7674 --- /dev/null +++ b/libs/python/numpy/test/indexing_mod.cpp @@ -0,0 +1,33 @@ +#include +#include +#include + +namespace bp = boost::python; + +void single(bp::numpy::ndarray ndarr, int i,bp::object value) { + bp::object element = bp::extract(ndarr[i]); + assert(element == value); +} + + +void slice(bp::numpy::ndarray ndarr, bp::slice sl,bp::object val) { +// bp::object element = bp::extract(ndarr[sl]); + int start = bp::extract(sl.start()); + int stop = bp::extract(sl.stop()); + unsigned j=0; + for (int i = start; i < stop; i++) + { + bp::object element = bp::extract(ndarr[i]); + bp::object value = bp::extract(val[j]); + assert(element == value); + ++j; + } + +} + +BOOST_PYTHON_MODULE(indexing_mod) { + bp::numpy::initialize(); + bp::def("single",&single); + bp::def("slice",&slice); +} + From fa51b58cd69d1b0c9b94bae12a323520f348f586 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Mon, 27 Jun 2011 04:09:12 +0000 Subject: [PATCH 037/128] Added test for slices with steps. Auto-detection of step not implemented yet --- libs/python/numpy/test/indexing.py | 6 ++++++ libs/python/numpy/test/indexing_mod.cpp | 21 ++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/libs/python/numpy/test/indexing.py b/libs/python/numpy/test/indexing.py index 0b4986c5..0515784d 100644 --- a/libs/python/numpy/test/indexing.py +++ b/libs/python/numpy/test/indexing.py @@ -17,5 +17,11 @@ class TestIndexing(unittest.TestCase): b = [3,4,5,6,7] indexing_mod.slice(x,sl,b) + def testStepSlice(self): + x = numpy.arange(0,10) + sl = slice(3,8,2) + b = [3,5,7] + indexing_mod.step_slice(x,sl,b) + if __name__=="__main__": unittest.main() diff --git a/libs/python/numpy/test/indexing_mod.cpp b/libs/python/numpy/test/indexing_mod.cpp index 9adf7674..c65f3e7e 100644 --- a/libs/python/numpy/test/indexing_mod.cpp +++ b/libs/python/numpy/test/indexing_mod.cpp @@ -11,7 +11,6 @@ void single(bp::numpy::ndarray ndarr, int i,bp::object value) { void slice(bp::numpy::ndarray ndarr, bp::slice sl,bp::object val) { -// bp::object element = bp::extract(ndarr[sl]); int start = bp::extract(sl.start()); int stop = bp::extract(sl.stop()); unsigned j=0; @@ -21,13 +20,29 @@ void slice(bp::numpy::ndarray ndarr, bp::slice sl,bp::object val) { bp::object value = bp::extract(val[j]); assert(element == value); ++j; - } - + } } + +void step_slice(bp::numpy::ndarray ndarr, bp::slice sl,bp::object val) { + int start = bp::extract(sl.start()); + int stop = bp::extract(sl.stop()); + int step = bp::extract(sl.step()); + unsigned j=0; + for (int i = start; i < stop; i=i+step) + { + bp::object element = bp::extract(ndarr[i]); + bp::object value = bp::extract(val[j]); + assert(element == value); + ++j; + } +} + + BOOST_PYTHON_MODULE(indexing_mod) { bp::numpy::initialize(); bp::def("single",&single); bp::def("slice",&slice); + bp::def("step_slice",&step_slice); } From 88dd5330d05ddbfc1e5d6f933ede355004935897 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Mon, 27 Jun 2011 14:07:54 +0000 Subject: [PATCH 038/128] Tidy indexing tests. --- libs/python/numpy/test/indexing.py | 34 ++++++++--------- libs/python/numpy/test/indexing_mod.cpp | 50 ++++--------------------- 2 files changed, 25 insertions(+), 59 deletions(-) diff --git a/libs/python/numpy/test/indexing.py b/libs/python/numpy/test/indexing.py index 0515784d..b11e86e9 100644 --- a/libs/python/numpy/test/indexing.py +++ b/libs/python/numpy/test/indexing.py @@ -4,24 +4,24 @@ import indexing_mod class TestIndexing(unittest.TestCase): - def testSingle(self): - x = numpy.arange(0,10) - for i in range(0,10): - indexing_mod.single(x,i,i) - for i in range(-10,0): - indexing_mod.single(x,i,10+i) + def testSingle(self): + x = numpy.arange(0,10) + for i in range(0,10): + numpy.testing.assert_equal(indexing_mod.single(x,i), i) + for i in range(-10,0): + numpy.testing.assert_equal(indexing_mod.single(x,i),10+i) - def testSlice(self): - x = numpy.arange(0,10) - sl = slice(3,8) - b = [3,4,5,6,7] - indexing_mod.slice(x,sl,b) + def testSlice(self): + x = numpy.arange(0,10) + sl = slice(3,8) + b = [3,4,5,6,7] + numpy.testing.assert_equal(indexing_mod.slice(x,sl), b) - def testStepSlice(self): - x = numpy.arange(0,10) - sl = slice(3,8,2) - b = [3,5,7] - indexing_mod.step_slice(x,sl,b) + def testStepSlice(self): + x = numpy.arange(0,10) + sl = slice(3,8,2) + b = [3,5,7] + numpy.testing.assert_equal(indexing_mod.slice(x,sl), b) if __name__=="__main__": - unittest.main() + unittest.main() diff --git a/libs/python/numpy/test/indexing_mod.cpp b/libs/python/numpy/test/indexing_mod.cpp index c65f3e7e..533a20a3 100644 --- a/libs/python/numpy/test/indexing_mod.cpp +++ b/libs/python/numpy/test/indexing_mod.cpp @@ -1,48 +1,14 @@ #include -#include #include namespace bp = boost::python; -void single(bp::numpy::ndarray ndarr, int i,bp::object value) { - bp::object element = bp::extract(ndarr[i]); - assert(element == value); +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];} + +BOOST_PYTHON_MODULE(indexing_mod) +{ + bp::numpy::initialize(); + bp::def("single", single); + bp::def("slice", slice); } - - -void slice(bp::numpy::ndarray ndarr, bp::slice sl,bp::object val) { - int start = bp::extract(sl.start()); - int stop = bp::extract(sl.stop()); - unsigned j=0; - for (int i = start; i < stop; i++) - { - bp::object element = bp::extract(ndarr[i]); - bp::object value = bp::extract(val[j]); - assert(element == value); - ++j; - } -} - - -void step_slice(bp::numpy::ndarray ndarr, bp::slice sl,bp::object val) { - int start = bp::extract(sl.start()); - int stop = bp::extract(sl.stop()); - int step = bp::extract(sl.step()); - unsigned j=0; - for (int i = start; i < stop; i=i+step) - { - bp::object element = bp::extract(ndarr[i]); - bp::object value = bp::extract(val[j]); - assert(element == value); - ++j; - } -} - - -BOOST_PYTHON_MODULE(indexing_mod) { - bp::numpy::initialize(); - bp::def("single",&single); - bp::def("slice",&slice); - bp::def("step_slice",&step_slice); -} - From 2979e4b0621d48b3e968cada5019af9c05c70cc0 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Thu, 30 Jun 2011 02:50:40 +0000 Subject: [PATCH 039/128] Added index array and boolean tests --- libs/python/numpy/test/indexing.py | 55 +++++++++++++++++-------- libs/python/numpy/test/indexing_mod.cpp | 13 ++++-- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/libs/python/numpy/test/indexing.py b/libs/python/numpy/test/indexing.py index b11e86e9..0533d0f2 100644 --- a/libs/python/numpy/test/indexing.py +++ b/libs/python/numpy/test/indexing.py @@ -4,24 +4,45 @@ import indexing_mod class TestIndexing(unittest.TestCase): - def testSingle(self): - x = numpy.arange(0,10) - for i in range(0,10): - numpy.testing.assert_equal(indexing_mod.single(x,i), i) - for i in range(-10,0): - numpy.testing.assert_equal(indexing_mod.single(x,i),10+i) + def testSingle(self): + x = numpy.arange(0,10) + for i in range(0,10): + numpy.testing.assert_equal(indexing_mod.single(x,i), i) + for i in range(-10,0): + numpy.testing.assert_equal(indexing_mod.single(x,i),10+i) - def testSlice(self): - x = numpy.arange(0,10) - sl = slice(3,8) - b = [3,4,5,6,7] - numpy.testing.assert_equal(indexing_mod.slice(x,sl), b) + def testSlice(self): + x = numpy.arange(0,10) + sl = slice(3,8) + b = [3,4,5,6,7] + numpy.testing.assert_equal(indexing_mod.slice(x,sl), b) - def testStepSlice(self): - x = numpy.arange(0,10) - sl = slice(3,8,2) - b = [3,5,7] - numpy.testing.assert_equal(indexing_mod.slice(x,sl), b) + def testStepSlice(self): + x = numpy.arange(0,10) + sl = slice(3,8,2) + b = [3,5,7] + numpy.testing.assert_equal(indexing_mod.slice(x,sl), b) + + def testIndex(self): + x = numpy.arange(0,10) + chk = numpy.array([3,4,5,6]) + 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(0,10) + b = x>4 + chk = numpy.array([5,6,7,8,9]) + numpy.testing.assert_equal(indexing_mod.indexarray(x,b),chk) + x = numpy.arange(9).reshape(3,3) + 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) if __name__=="__main__": - unittest.main() + unittest.main() diff --git a/libs/python/numpy/test/indexing_mod.cpp b/libs/python/numpy/test/indexing_mod.cpp index 533a20a3..02f79cbd 100644 --- a/libs/python/numpy/test/indexing_mod.cpp +++ b/libs/python/numpy/test/indexing_mod.cpp @@ -5,10 +5,17 @@ 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::numpy::initialize(); + bp::def("single", &single); + bp::def("slice", &slice); + bp::def("indexarray", &indexarray); + bp::def("indexarray", &indexarray_2d); + bp::def("indexslice", &indexslice); + } From b2519a25a948a36eafb545d166cd7b2df607ea35 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 3 Jul 2011 16:40:30 +0000 Subject: [PATCH 040/128] 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()); - -} From c572b4db73b439842749d7150357c1126d19a1a6 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 3 Jul 2011 16:42:15 +0000 Subject: [PATCH 041/128] Rename (and move) boost.python.numpy to boost.numpy. From 196c9e653fd68b9f20ab1aeb3d8d1f7a09024857 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 3 Jul 2011 20:36:55 +0000 Subject: [PATCH 042/128] Fix indexing tests. --- libs/numpy/test/indexing.py | 6 +++--- libs/numpy/test/indexing_mod.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/numpy/test/indexing.py b/libs/numpy/test/indexing.py index 3ef8797a..31ba0798 100644 --- a/libs/numpy/test/indexing.py +++ b/libs/numpy/test/indexing.py @@ -33,16 +33,16 @@ class TestIndexing(unittest.TestCase): 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 + numpy.testing.assert_equal(indexing_mod.indexarray(x,y,z),chk) x = numpy.arange(0,10) b = x>4 chk = numpy.array([5,6,7,8,9]) numpy.testing.assert_equal(indexing_mod.indexarray(x,b),chk) x = numpy.arange(9).reshape(3,3) b = numpy.array([0,2]) - sl = slice(0,2) + sl = slice(0,3) 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 index cd0e43d8..6d44af3f 100644 --- a/libs/numpy/test/indexing_mod.cpp +++ b/libs/numpy/test/indexing_mod.cpp @@ -7,8 +7,8 @@ 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];} +p::object indexarray_2d(np::ndarray ndarr, np::ndarray d1,np::ndarray d2) { return ndarr[p::make_tuple(d1,d2)];} +p::object indexslice(np::ndarray ndarr, np::ndarray d1, p::slice sl) { return ndarr[p::make_tuple(d1, sl)];} BOOST_PYTHON_MODULE(indexing_mod) { @@ -16,7 +16,7 @@ BOOST_PYTHON_MODULE(indexing_mod) p::def("single", single); p::def("slice", slice); p::def("indexarray", indexarray); - // p::def("indexarray", indexarray_2d); - // p::def("indexslice", indexslice); + p::def("indexarray", indexarray_2d); + p::def("indexslice", indexslice); } From 9c56469358c055cd56d81beca55a19b36252c1d6 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Tue, 5 Jul 2011 14:20:57 +0000 Subject: [PATCH 043/128] Add example(s) --- libs/numpy/example/Jamfile | 23 +++++++++++++++++++++++ libs/numpy/example/simple.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 libs/numpy/example/Jamfile create mode 100644 libs/numpy/example/simple.cpp diff --git a/libs/numpy/example/Jamfile b/libs/numpy/example/Jamfile new file mode 100644 index 00000000..06db5ef8 --- /dev/null +++ b/libs/numpy/example/Jamfile @@ -0,0 +1,23 @@ +# Copyright 2011 Stefan Seefeld. +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import python ; + +use-project /boost/numpy : ../src ; +project /boost/numpy/example ; + +lib boost_python ; + + +#lib libxml2 : : xml2 /usr/lib/ ; + +#project : requirements +# /usr/include/libxml2 +# ../../.. +# libxml2 +# /boost/xml//boost_xml +# ; + +exe simple : simple.cpp ../src//boost_numpy boost_python /python//python ; diff --git a/libs/numpy/example/simple.cpp b/libs/numpy/example/simple.cpp new file mode 100644 index 00000000..d375b35c --- /dev/null +++ b/libs/numpy/example/simple.cpp @@ -0,0 +1,30 @@ +// Copyright 2011 Stefan Seefeld. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +int main(int argc, char **argv) +{ + // Initialize the Python runtime. + Py_Initialize(); + // Initialize NumPy + np::initialize(); + // Create a 3x3 shape... + p::tuple shape = p::make_tuple(3, 3); + // ...as well as a type for C++ float + np::dtype dtype = np::dtype::get_builtin(); + // Construct an array with the above shape and type + np::ndarray a = np::zeros(shape, dtype); + // Print the array + std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; + // Reshape the array into a 1D array + a = a.reshape(p::make_tuple(9)); + // Print it again. + std::cout << "Reshaped array:\n" << p::extract(p::str(a)) << std::endl; +} From 8fa1f9db9e32f5f7f3c8d972a87ffccbd7e9e420 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Tue, 19 Jul 2011 04:32:25 +0000 Subject: [PATCH 044/128] Add support for ReST docs. --- libs/numpy/doc/Jamfile | 25 ++++++ libs/numpy/doc/rst.css | 149 ++++++++++++++++++++++++++++++++++++ libs/numpy/doc/tutorial.rst | 37 +++++++++ 3 files changed, 211 insertions(+) create mode 100644 libs/numpy/doc/Jamfile create mode 100644 libs/numpy/doc/rst.css create mode 100644 libs/numpy/doc/tutorial.rst diff --git a/libs/numpy/doc/Jamfile b/libs/numpy/doc/Jamfile new file mode 100644 index 00000000..6ec6fed5 --- /dev/null +++ b/libs/numpy/doc/Jamfile @@ -0,0 +1,25 @@ +# Copyright David Abrahams 2006. Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +project user-config : requirements rst2html ; + +import docutils ; + +import path ; +sources = tutorial.rst ; +bases = $(sources:S=) ; + +# This is a path relative to the html/ subdirectory where the +# generated output will eventually be moved. +stylesheet = "--stylesheet=rst.css" ; + +for local b in $(bases) +{ + html $(b) : $(b).rst : + + "-gdt --source-url="./$(b).rst" --link-stylesheet --traceback --trim-footnote-reference-space --footnote-references=superscript "$(stylesheet) + ; +} + +alias htmls : $(bases) ; +stage . : $(bases) ; diff --git a/libs/numpy/doc/rst.css b/libs/numpy/doc/rst.css new file mode 100644 index 00000000..afd9a98c --- /dev/null +++ b/libs/numpy/doc/rst.css @@ -0,0 +1,149 @@ +@import url("doc/src/boostbook.css"); +@import url("doc/src/docutils.css"); +/* Copyright David Abrahams 2006. Distributed under the Boost + Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +dl.docutils dt { + font-weight: bold } + +img.boost-logo { + border: none; + vertical-align: middle +} + +pre.literal-block span.concept { + font-style: italic; +} + +.nav { +display: inline; +list-style-type: none; +} + +.prevpage { +padding-top: -5px; +text-align: left; +float: left; +} + +.nextpage { +padding-top: -20px; +text-align: right; +float: right; +} + +div.small { + font-size: smaller } + +h2 a { + font-size: 90%; +} +h3 a { + font-size: 80%; +} +h4 a { + font-size: 70%; +} +h5 a { + font-size: 60%; +} + +dl,table +{ + text-align: left; + font-size: 10pt; + line-height: 1.15; +} + + +/*============================================================================= + Tables +=============================================================================*/ + +/* The only clue docutils gives us that tables are logically tables, + and not, e.g., footnotes, is that they have border="1". Therefore + we're keying off of that. We used to manually patch docutils to + add a "table" class to all logical tables, but that proved much too + fragile. +*/ + + table[border="1"] + { + width: 92%; + margin-left: 4%; + margin-right: 4%; + } + + table[border="1"] + { + padding: 4px; + } + + /* Table Cells */ + table[border="1"] tr td + { + padding: 0.5em; + text-align: left; + font-size: 9pt; + } + + table[border="1"] tr th + { + padding: 0.5em 0.5em 0.5em 0.5em; + border: 1pt solid white; + font-size: 80%; + } + + @media screen + { + + /* Tables */ + table[border="1"] tr td + { + border: 1px solid #DCDCDC; + } + + table[border="1"] tr th + { + background-color: #F0F0F0; + border: 1px solid #DCDCDC; + } + + pre, + .screen + { + border: 1px solid #DCDCDC; + } + + td pre + td .screen + { + border: 0px + } + + .sidebar pre + { + border: 0px + } + + } + + pre, + .screen + { + font-size: 9pt; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + /* Program listings in tables don't get borders */ + td pre, + td .screen + { + margin: 0pc 0pc 0pc 0pc; + padding: 0pc 0pc 0pc 0pc; + } + diff --git a/libs/numpy/doc/tutorial.rst b/libs/numpy/doc/tutorial.rst new file mode 100644 index 00000000..771f050c --- /dev/null +++ b/libs/numpy/doc/tutorial.rst @@ -0,0 +1,37 @@ +A simple tutorial on Arrays +=========================== + +Let's start with a simple tutorial to create and modify arrays. + +Get the necessary headers for numpy components and set up necessary namespaces:: + + #include + #include + + namespace p = boost::python; + namespace np = boost::numpy; + +Initialise the Python runtime, and the numpy module. Failure to call these results in segmentation errors:: + + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); + + +Zero filled n-dimensional arrays can be created using the shape and data-type of the array as a parameter. Here, the shape is 3x3 and the datatype is the built-in float type:: + + p::tuple shape = p::make_tuple(3, 3); + np::dtype dtype = np::dtype::get_builtin(); + np::ndarray a = np::zeros(shape, dtype); + +Print the original and reshaped array. The array a which is a list is first converted to a string, and each value in the list is extracted using extract< >:: + + std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; + + // Reshape the array into a 1D array + a = a.reshape(p::make_tuple(9)); + // Print it again. + std::cout << "Reshaped array:\n" << p::extract(p::str(a)) << std::endl; + } + From bbd9aad6e9f6d23ba6b385e48f1cd0cbf6b3ae1f Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Wed, 3 Aug 2011 20:07:20 +0000 Subject: [PATCH 045/128] New examples --- libs/numpy/example/dtype.cpp | 33 +++++++++++++++++++++++++++++++++ libs/numpy/example/ndarray.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 libs/numpy/example/dtype.cpp create mode 100644 libs/numpy/example/ndarray.cpp diff --git a/libs/numpy/example/dtype.cpp b/libs/numpy/example/dtype.cpp new file mode 100644 index 00000000..33e040d4 --- /dev/null +++ b/libs/numpy/example/dtype.cpp @@ -0,0 +1,33 @@ +/** + * @brief An example to show how to create ndarrays with built-in python data types, and extract the types and values + * of member variables + * + * @todo Add an example to show type conversion. + * Add an example to show use of user-defined types + * Doesn't work for char. Works for int, long int, short int, float, double + */ + +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +int main(int argc, char **argv) +{ + // Initialize the Python runtime. + Py_Initialize(); + // Initialize NumPy + np::initialize(); + // Create a 3x3 shape... + p::tuple shape = p::make_tuple(3, 3); + // ...as well as a type for C++ double + np::dtype dtype = np::dtype::get_builtin(); + // Construct an array with the above shape and type + np::ndarray a = np::zeros(shape, dtype); + // Print the array + std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; + // Print the datatype of the elements + std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; + +} diff --git a/libs/numpy/example/ndarray.cpp b/libs/numpy/example/ndarray.cpp new file mode 100644 index 00000000..9bfd5dc5 --- /dev/null +++ b/libs/numpy/example/ndarray.cpp @@ -0,0 +1,33 @@ +/** + * @brief An example to show how to create ndarrays using arbitrary Python sequences + * The Python sequence could be any object whose __array__ method returns an array, or any (nested) sequence. + * + * @todo Find a way to create a list explicitly + * + */ + +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + +int main(int argc, char **argv) +{ + // Initialize the Python runtime. + Py_Initialize(); + // Initialize NumPy + np::initialize(); + // Create an ndarray from a simple tuple + p::object tu = p::make_tuple('a','b','c') ; + np::ndarray example_tuple = np::array (tu) ; + // and from a list + p::list l ; + l.append('a') ; + np::ndarray example_list = np::array (l) ; + // Optionally, you can also specify a dtype + np::dtype dt = np::dtype::get_builtin(); + np::ndarray example_list1 = np::array (l,dt); +} + + From 37a73f344b1ad3a1c4f0a24d69fc2f2f9003aa8e Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Wed, 3 Aug 2011 20:07:40 +0000 Subject: [PATCH 046/128] Modified Jamfile --- libs/numpy/example/Jamfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/numpy/example/Jamfile b/libs/numpy/example/Jamfile index 06db5ef8..cf219520 100644 --- a/libs/numpy/example/Jamfile +++ b/libs/numpy/example/Jamfile @@ -10,7 +10,6 @@ project /boost/numpy/example ; lib boost_python ; - #lib libxml2 : : xml2 /usr/lib/ ; #project : requirements @@ -21,3 +20,5 @@ lib boost_python ; # ; exe simple : simple.cpp ../src//boost_numpy boost_python /python//python ; +exe dtype : dtype.cpp ../src//boost_numpy boost_python /python//python ; +exe ndarray : ndarray.cpp ../src//boost_numpy boost_python /python//python ; From 45e52301e96ef346705453856768578295f91bfd Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Thu, 4 Aug 2011 17:39:04 +0000 Subject: [PATCH 047/128] Added from_data implementation, zeros(..) examples --- libs/numpy/example/ndarray.cpp | 14 +++++++++++++- libs/numpy/example/simple.cpp | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libs/numpy/example/ndarray.cpp b/libs/numpy/example/ndarray.cpp index 9bfd5dc5..3ab28f97 100644 --- a/libs/numpy/example/ndarray.cpp +++ b/libs/numpy/example/ndarray.cpp @@ -3,6 +3,8 @@ * The Python sequence could be any object whose __array__ method returns an array, or any (nested) sequence. * * @todo Find a way to create a list explicitly + * + * */ @@ -23,11 +25,21 @@ int main(int argc, char **argv) np::ndarray example_tuple = np::array (tu) ; // and from a list p::list l ; - l.append('a') ; np::ndarray example_list = np::array (l) ; // Optionally, you can also specify a dtype np::dtype dt = np::dtype::get_builtin(); np::ndarray example_list1 = np::array (l,dt); + // You can also create an array by supplying data.First,create an integer array + int data[] = {1,2,3,4} ; + // Create a shape, and strides, needed by the function + p::tuple shape = p::make_tuple(2,2) ; + p::tuple strides = p::make_tuple(strides(data)) ; + // The function also needs an owner, to keep track of the data array passed. Passing none is dangerous + p::object owner ; + // The from_data function takes the data array, datatype,shape,stride and owner as arguments + // and returns an ndarray + np::ndarray data_ex1 = np::from_data(data,dt, shape,strides,owner); + } diff --git a/libs/numpy/example/simple.cpp b/libs/numpy/example/simple.cpp index d375b35c..b1f2dc3f 100644 --- a/libs/numpy/example/simple.cpp +++ b/libs/numpy/example/simple.cpp @@ -21,6 +21,8 @@ int main(int argc, char **argv) np::dtype dtype = np::dtype::get_builtin(); // Construct an array with the above shape and type np::ndarray a = np::zeros(shape, dtype); + // Construct an empty array with the above shape and dtype as well + np::ndarray b = np::empty(shape,dtype); // Print the array std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; // Reshape the array into a 1D array From 36e9e3837370e89d4700e946828bb6f808d8818e Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Thu, 4 Aug 2011 17:45:00 +0000 Subject: [PATCH 048/128] Tutorial for ndarrays --- libs/numpy/doc/ndarray.rst | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 libs/numpy/doc/ndarray.rst diff --git a/libs/numpy/doc/ndarray.rst b/libs/numpy/doc/ndarray.rst new file mode 100644 index 00000000..42653b99 --- /dev/null +++ b/libs/numpy/doc/ndarray.rst @@ -0,0 +1,60 @@ +Creating ndarrays +================= + +The Boost.Numpy library exposes quite a few methods to create ndarrays. ndarrays can be created in a variety of ways, include empty arrays and zero filled arrays. +ndarrays can also be created from arbitrary python sequences as well as from data and dtypes. + +This tutorial will introduce you to some of the ways in which you can create ndarrays. + +First, as before, initialise the necessary namepaces and runtimes :: + + #include + #include + + namespace p = boost::python; + namespace np = boost::numpy; + + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); + +Let's now create an ndarray from a simple tuple. We first create a tuple object, and then pass it to the array method, to generate the necessary tuple :: + + p::object tu = p::make_tuple('a','b','c') ; + np::ndarray example_tuple = np::array(tu) ; + +Let's now try the same with a list. We create an empty list, add an element using the append method, and as before, call the array method :: + + p::list l ; + l.append('a') ; + np::ndarray example_list = np::array (l) ; + +Optionally, we can also specify a dtype for the array :: + + np::dtype dt = np::dtype::get_builtin(); + np::ndarray example_list1 = np::array (l,dt); + +We can also create an array by supplying data arrays and a few other parameters. + +First,create an integer array :: + + int data[] = {1,2,3,4} ; + +Create a shape, and strides, needed by the function :: + + p::tuple shape = p::make_tuple(2,2) ; + p::tuple strides = p::make_tuple(strides(data)) ; + +The function also needs an owner, to keep track of the data array passed. Passing none is dangerous :: + + p::object owner ; + +The from_data function takes the data array, datatype,shape,stride and owner as arguments and returns an ndarray :: + + np::ndarray data_ex1 = np::from_data(data,dt, shape,strides,owner); + +} + + + From 79182d71891f0d8d48a194efe9ba91ae122b717c Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Thu, 4 Aug 2011 17:47:09 +0000 Subject: [PATCH 049/128] Added ndarray.rst --- libs/numpy/doc/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/numpy/doc/Jamfile b/libs/numpy/doc/Jamfile index 6ec6fed5..f76da2c4 100644 --- a/libs/numpy/doc/Jamfile +++ b/libs/numpy/doc/Jamfile @@ -6,7 +6,7 @@ project user-config : requirements rst2html ; import docutils ; import path ; -sources = tutorial.rst ; +sources = tutorial.rst ndarray.rst ; bases = $(sources:S=) ; # This is a path relative to the html/ subdirectory where the From 7add755ae07a4812737c6084ff07673b597e2d1e Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Fri, 5 Aug 2011 09:06:19 +0000 Subject: [PATCH 050/128] Added tutorial for dtype --- libs/numpy/doc/Jamfile | 2 +- libs/numpy/doc/dtype.rst | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 libs/numpy/doc/dtype.rst diff --git a/libs/numpy/doc/Jamfile b/libs/numpy/doc/Jamfile index f76da2c4..bf38ffb7 100644 --- a/libs/numpy/doc/Jamfile +++ b/libs/numpy/doc/Jamfile @@ -6,7 +6,7 @@ project user-config : requirements rst2html ; import docutils ; import path ; -sources = tutorial.rst ndarray.rst ; +sources = tutorial.rst dtype.rst ndarray.rst ; bases = $(sources:S=) ; # This is a path relative to the html/ subdirectory where the diff --git a/libs/numpy/doc/dtype.rst b/libs/numpy/doc/dtype.rst new file mode 100644 index 00000000..8aa0994a --- /dev/null +++ b/libs/numpy/doc/dtype.rst @@ -0,0 +1,34 @@ +How to use dtypes +================= + +Here is a brief tutorial to show how to create ndarrays with built-in python data types, and extract the types and values of member variables + +Like before, first get the necessary headers, setup the namespaces and initialize the Python runtime and numpy module:: + + #include + #include + + namespace p = boost::python; + namespace np = boost::numpy; + + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); + +Next, we create the shape and dtype. We use the get_builtin method to get the numpy dtype corresponding to the builtin C++ dtype +Here, we will create a 3x3 array passing a tuple with (3,3) for the size, and double as the data type :: + + p::tuple shape = p::make_tuple(3, 3); + np::dtype dtype = np::dtype::get_builtin(); + np::ndarray a = np::zeros(shape, dtype); + +Finally, we can print the array using the extract method in the python namespace. +Here, we first convert the variable into a string, and then extract it as a C++ character array from the python string using the template :: + + std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; + +We can also print the dtypes of the data members of the ndarray by using the get_dtype method for the ndarray :: + + std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; + } From 437373456fd0444b246b25f456b16bd6f535fbd2 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Fri, 5 Aug 2011 17:04:29 +0000 Subject: [PATCH 051/128] Added example for custom dtype --- libs/numpy/example/dtype.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/numpy/example/dtype.cpp b/libs/numpy/example/dtype.cpp index 33e040d4..2674ef8f 100644 --- a/libs/numpy/example/dtype.cpp +++ b/libs/numpy/example/dtype.cpp @@ -4,7 +4,7 @@ * * @todo Add an example to show type conversion. * Add an example to show use of user-defined types - * Doesn't work for char. Works for int, long int, short int, float, double + * */ #include @@ -29,5 +29,12 @@ int main(int argc, char **argv) std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; // Print the datatype of the elements std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; - + // Roundabout way of creating a user defined dtype. Fix this if possible +/* + p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ; + p::list list_for_dtype ; + list_for_dtype.append(for_custom_dtype) ; + np::dtype custom_dtype = np::dtype(list_for_dtype) ; + np::ndarray new_array = np::zeros(shape,z); +*/ } From c314274a5607fa6a3d566cf672ef063a04181946 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 6 Aug 2011 21:12:42 +0000 Subject: [PATCH 052/128] Added working custom dtype exampel --- libs/numpy/doc/dtype.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libs/numpy/doc/dtype.rst b/libs/numpy/doc/dtype.rst index 8aa0994a..02bb513b 100644 --- a/libs/numpy/doc/dtype.rst +++ b/libs/numpy/doc/dtype.rst @@ -31,4 +31,25 @@ Here, we first convert the variable into a string, and then extract it as a C++ We can also print the dtypes of the data members of the ndarray by using the get_dtype method for the ndarray :: std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; + +We can also create custom dtypes and build ndarrays with the custom dtypes + +We use the dtype constructor to create a custom dtype. This constructor takes a list as an argument. + +The list should contain one or more tuples of the format (variable name, variable type) + +So first create a tuple with a variable name and its dtype, double, to create a custom dtype :: + + p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ; + +Next, create a list, and add this tuple to the list. Then use the list to create the custom dtype :: + + p::list list_for_dtype ; + list_for_dtype.append(for_custom_dtype) ; + np::dtype custom_dtype = np::dtype(list_for_dtype) ; + +We are now ready to create an ndarray with dimensions specified by \*shape\* and of custom dtpye :: + + np::ndarray new_array = np::zeros(shape,custom_dtype); + } From 3ffcf3335a98e5e3a453518caa136f9ecc5fff3a Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 6 Aug 2011 21:13:13 +0000 Subject: [PATCH 053/128] Added working custom dtype example --- libs/numpy/example/dtype.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libs/numpy/example/dtype.cpp b/libs/numpy/example/dtype.cpp index 2674ef8f..dfed6783 100644 --- a/libs/numpy/example/dtype.cpp +++ b/libs/numpy/example/dtype.cpp @@ -29,12 +29,16 @@ int main(int argc, char **argv) std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; // Print the datatype of the elements std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; - // Roundabout way of creating a user defined dtype. Fix this if possible -/* + // Using user defined dtypes to create dtype and an array of the custom dtype + // First create a tuple with a variable name and its dtype, double, to create a custom dtype p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ; + // The list needs to be created, because the constructor to create the custom dtype + // takes a list of (variable,variable_type) as an argument p::list list_for_dtype ; list_for_dtype.append(for_custom_dtype) ; + // Create the custom dtype np::dtype custom_dtype = np::dtype(list_for_dtype) ; - np::ndarray new_array = np::zeros(shape,z); -*/ + // Create an ndarray with the custom dtype + np::ndarray new_array = np::zeros(shape,custom_dtype); + } From 5bed132ff81be8c9c39c722ba1ef5debb1a8b704 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Mon, 8 Aug 2011 17:05:09 +0000 Subject: [PATCH 054/128] Added a line for zeros --- libs/numpy/doc/tutorial.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/numpy/doc/tutorial.rst b/libs/numpy/doc/tutorial.rst index 771f050c..3c462962 100644 --- a/libs/numpy/doc/tutorial.rst +++ b/libs/numpy/doc/tutorial.rst @@ -25,6 +25,10 @@ Zero filled n-dimensional arrays can be created using the shape and data-type of np::dtype dtype = np::dtype::get_builtin(); np::ndarray a = np::zeros(shape, dtype); +You can also create an empty array like this :: + + np::ndarray b = np::empty(shape,dtype); + Print the original and reshaped array. The array a which is a list is first converted to a string, and each value in the list is extracted using extract< >:: std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; From 4c12b004ec547a23f13446ae00b926e0dab8e8bb Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Fri, 12 Aug 2011 05:16:20 +0000 Subject: [PATCH 055/128] Added example for data access using pointers --- libs/numpy/example/Jamfile | 2 ++ libs/numpy/example/fromdata.cpp | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 libs/numpy/example/fromdata.cpp diff --git a/libs/numpy/example/Jamfile b/libs/numpy/example/Jamfile index cf219520..5aa47d99 100644 --- a/libs/numpy/example/Jamfile +++ b/libs/numpy/example/Jamfile @@ -22,3 +22,5 @@ lib boost_python ; exe simple : simple.cpp ../src//boost_numpy boost_python /python//python ; exe dtype : dtype.cpp ../src//boost_numpy boost_python /python//python ; exe ndarray : ndarray.cpp ../src//boost_numpy boost_python /python//python ; +exe hybrid : hybrid.cpp ../src//boost_numpy boost_python /python//python ; +exe fromdata : fromdata.cpp ../src//boost_numpy boost_python /python//python ; diff --git a/libs/numpy/example/fromdata.cpp b/libs/numpy/example/fromdata.cpp new file mode 100644 index 00000000..3cfcdc36 --- /dev/null +++ b/libs/numpy/example/fromdata.cpp @@ -0,0 +1,46 @@ +/** + * @brief An example to show how to create ndarrays from C++ containers of (almost) arbitrary type. + * + */ + +#include +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + + +int main(int argc, char **argv) +{ + // Initialize the Python runtime. + Py_Initialize(); + // Initialize NumPy + np::initialize(); + // Create an array in C++ + int arr[] = {1,2,3,4} ; + // Create the ndarray in Python + np::ndarray py_array = np::from_data(arr, np::dtype::get_builtin() , p::make_tuple(4), p::make_tuple(4), p::object()); + // Print the ndarray that we created, just to be sure + std::cout << "C++ array :" << std::endl ; + for (int j=0;j<4;j++) + { + std::cout << arr[j] << ' ' ; + } + std::cout << std::endl << "Python ndarray :" << p::extract(p::str(py_array)) << std::endl; + // Change an element in the python ndarray + py_array[1] = 5 ; + // And see if the C++ container is changed or not + std::cout << "Is the change reflected in the C++ array used to create the ndarray ? " << std::endl ; + for (int j = 0;j<4 ; j++) + { + std::cout << arr[j] << ' ' ; + } + // Conversely, change it in C++ + arr[2] = 8 ; + // And see if the changes are reflected in the Python ndarray + std::cout << std::endl << "Is the change reflected in the Python ndarray ?" << std::endl << p::extract(p::str(py_array)) << std::endl; + + // Now print it. +// std::cout << "Pixel array :" << p::extract(p::str(py_array)) << std::endl; +} From c33460c2657652a7463e4309ad5145ed77e90991 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Fri, 12 Aug 2011 09:26:22 +0000 Subject: [PATCH 056/128] Made a few modifications --- libs/numpy/example/fromdata.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libs/numpy/example/fromdata.cpp b/libs/numpy/example/fromdata.cpp index 3cfcdc36..dc5a3a2a 100644 --- a/libs/numpy/example/fromdata.cpp +++ b/libs/numpy/example/fromdata.cpp @@ -1,10 +1,10 @@ /** - * @brief An example to show how to create ndarrays from C++ containers of (almost) arbitrary type. + * @brief An example to show how to access data using raw pointers.This shows that you can use and manipulate data in either Python or C++ and have the changes + * reflected in both * */ #include -#include #include namespace p = boost::python; @@ -21,7 +21,7 @@ int main(int argc, char **argv) int arr[] = {1,2,3,4} ; // Create the ndarray in Python np::ndarray py_array = np::from_data(arr, np::dtype::get_builtin() , p::make_tuple(4), p::make_tuple(4), p::object()); - // Print the ndarray that we created, just to be sure + // Print the ndarray that we just created, and the source C++ array std::cout << "C++ array :" << std::endl ; for (int j=0;j<4;j++) { @@ -41,6 +41,4 @@ int main(int argc, char **argv) // And see if the changes are reflected in the Python ndarray std::cout << std::endl << "Is the change reflected in the Python ndarray ?" << std::endl << p::extract(p::str(py_array)) << std::endl; - // Now print it. -// std::cout << "Pixel array :" << p::extract(p::str(py_array)) << std::endl; } From 3dda62f8b8d0ba3099b1d674c8697e5715919bce Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Fri, 12 Aug 2011 09:26:55 +0000 Subject: [PATCH 057/128] Added tutorial for fromdata, i.e. copy free data access --- libs/numpy/doc/Jamfile | 2 +- libs/numpy/doc/fromdata.rst | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 libs/numpy/doc/fromdata.rst diff --git a/libs/numpy/doc/Jamfile b/libs/numpy/doc/Jamfile index bf38ffb7..ea99cad1 100644 --- a/libs/numpy/doc/Jamfile +++ b/libs/numpy/doc/Jamfile @@ -6,7 +6,7 @@ project user-config : requirements rst2html ; import docutils ; import path ; -sources = tutorial.rst dtype.rst ndarray.rst ; +sources = tutorial.rst dtype.rst ndarray.rst fromdata.rst ; bases = $(sources:S=) ; # This is a path relative to the html/ subdirectory where the diff --git a/libs/numpy/doc/fromdata.rst b/libs/numpy/doc/fromdata.rst new file mode 100644 index 00000000..2937c78e --- /dev/null +++ b/libs/numpy/doc/fromdata.rst @@ -0,0 +1,51 @@ +How to access data using raw pointers +===================================== + +One of the advantages of the ndarray wrapper is that the same data can be used in both Python and C++ and changes can be made to reflect at both ends. +The from_data method makes this possible. + +Like before, first get the necessary headers, setup the namespaces and initialize the Python runtime and numpy module:: + + #include + #include + + namespace p = boost::python; + namespace np = boost::numpy; + + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); + +Create an array in C++ , and pass the pointer to it to the from_data method to create an ndarray:: + + int arr[] = {1,2,3,4} ; + np::ndarray py_array = np::from_data(arr, np::dtype::get_builtin() , p::make_tuple(4), p::make_tuple(4), p::object()); + +Print the source C++ array, as well as the ndarray, to check if they are the same:: + + std::cout << "C++ array :" << std::endl ; + for (int j=0;j<4;j++) + { + std::cout << arr[j] << ' ' ; + } + std::cout << std::endl << "Python ndarray :" << p::extract(p::str(py_array)) << std::endl; + +Now, change an element in the Python ndarray, and check if the value changed correspondingly in the source C++ array:: + + py_array[1] = 5 ; + std::cout << "Is the change reflected in the C++ array used to create the ndarray ? " << std::endl ; + for (int j = 0;j<4 ; j++) + { + std::cout << arr[j] << ' ' ; + } + +Next, change an element of the source C++ array and see if it is reflected in the Python ndarray:: + + arr[2] = 8 ; + std::cout << std::endl << "Is the change reflected in the Python ndarray ?" << std::endl << p::extract(p::str(py_array)) << std::endl; + +} + +As we can see, the changes are reflected across the ends. This happens because the from_data method passes the C++ array by reference to create the ndarray, and thus uses the same locations for storing data. + From 7b088c9df236a32c87715f8de40ad5dec670337f Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sat, 13 Aug 2011 13:55:03 +0000 Subject: [PATCH 058/128] Removed todo --- libs/numpy/example/ndarray.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/numpy/example/ndarray.cpp b/libs/numpy/example/ndarray.cpp index 3ab28f97..ec4dfade 100644 --- a/libs/numpy/example/ndarray.cpp +++ b/libs/numpy/example/ndarray.cpp @@ -1,10 +1,7 @@ /** * @brief An example to show how to create ndarrays using arbitrary Python sequences * The Python sequence could be any object whose __array__ method returns an array, or any (nested) sequence. - * - * @todo Find a way to create a list explicitly * - * */ From 67b5b07976a689a0482566820ef3322ee4cd25ca Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sun, 14 Aug 2011 06:48:46 +0000 Subject: [PATCH 059/128] Added non-unit strides example --- libs/numpy/example/ndarray.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/libs/numpy/example/ndarray.cpp b/libs/numpy/example/ndarray.cpp index ec4dfade..1c1e7bf5 100644 --- a/libs/numpy/example/ndarray.cpp +++ b/libs/numpy/example/ndarray.cpp @@ -1,7 +1,7 @@ /** - * @brief An example to show how to create ndarrays using arbitrary Python sequences + * @brief An example to show how to create ndarrays using arbitrary Python sequences. * The Python sequence could be any object whose __array__ method returns an array, or any (nested) sequence. - * + * This example also shows how to create arrays using both unit and non-unit strides * */ @@ -29,13 +29,32 @@ int main(int argc, char **argv) // You can also create an array by supplying data.First,create an integer array int data[] = {1,2,3,4} ; // Create a shape, and strides, needed by the function - p::tuple shape = p::make_tuple(2,2) ; - p::tuple strides = p::make_tuple(strides(data)) ; + p::tuple shape = p::make_tuple(4) ; + p::tuple stride = p::make_tuple(4) ; // The function also needs an owner, to keep track of the data array passed. Passing none is dangerous - p::object owner ; + p::object own ; // The from_data function takes the data array, datatype,shape,stride and owner as arguments // and returns an ndarray - np::ndarray data_ex1 = np::from_data(data,dt, shape,strides,owner); + np::ndarray data_ex = np::from_data(data,dt,shape,stride,own); + // Print the ndarray we created + std::cout << "Single dimensional array ::" << std::endl << p::extract < char const * > (p::str(data_ex)) << std::endl ; + // Now lets make an 3x2 ndarray from a multi-dimensional array using non-unit strides + // First lets create a 3x4 array of 8-bit integers + uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}}; + // Now let's create an array of 3x2 elements, picking the first and third elements from each row + // For that, the shape will be 3x2 + shape = p::make_tuple(3,2) ; + // The strides will be 4x2 i.e. 4 bytes to go to the next desired row, and 2 bytes to go to the next desired column + stride = p::make_tuple(4,2) ; + // Get the numpy dtype for the built-in 8-bit integer data type + np::dtype dt1 = np::dtype::get_builtin(); + // First lets create and print out the ndarray as is + np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object()); + std::cout << "Original multi dimensional array :: " << std::endl << p::extract < char const * > (p::str(mul_data_ex)) << std::endl ; + // Now create the new ndarray using the shape and strides + mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object()); + // Print out the array we created using non-unit strides + std::cout << "Selective multidimensional array :: "< (p::str(mul_data_ex)) << std::endl ; } From 715e5cbc31bd0c5b4225957faa7e765d1a89dcd8 Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Sun, 14 Aug 2011 06:49:08 +0000 Subject: [PATCH 060/128] Added non-unit strides example --- libs/numpy/doc/ndarray.rst | 50 ++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/libs/numpy/doc/ndarray.rst b/libs/numpy/doc/ndarray.rst index 42653b99..c0078391 100644 --- a/libs/numpy/doc/ndarray.rst +++ b/libs/numpy/doc/ndarray.rst @@ -4,7 +4,7 @@ Creating ndarrays The Boost.Numpy library exposes quite a few methods to create ndarrays. ndarrays can be created in a variety of ways, include empty arrays and zero filled arrays. ndarrays can also be created from arbitrary python sequences as well as from data and dtypes. -This tutorial will introduce you to some of the ways in which you can create ndarrays. +This tutorial will introduce you to some of the ways in which you can create ndarrays. The methods covered here include creating ndarrays from arbitrary Python sequences, as well as from C++ containers, using both unit and non-unit strides First, as before, initialise the necessary namepaces and runtimes :: @@ -43,18 +43,52 @@ First,create an integer array :: Create a shape, and strides, needed by the function :: - p::tuple shape = p::make_tuple(2,2) ; - p::tuple strides = p::make_tuple(strides(data)) ; + p::tuple shape = p::make_tuple(4) ; + p::tuple stride = p::make_tuple(4) ; + +Here, shape is 1x4 , and the stride is also 4. +A stride is the number of bytes that must be travelled to get to the next desired element while constructing the ndarray. Here, the size of the "int" is 32 bits and hence, the stride is 4 to access each element. The function also needs an owner, to keep track of the data array passed. Passing none is dangerous :: - p::object owner ; + p::object own ; The from_data function takes the data array, datatype,shape,stride and owner as arguments and returns an ndarray :: - np::ndarray data_ex1 = np::from_data(data,dt, shape,strides,owner); + np::ndarray data_ex1 = np::from_data(data,dt, shape,stride,own); + +Now let's print the ndarray we created :: + + std::cout << "Single dimensional array ::" << std::endl << p::extract < char const * > (p::str(data_ex)) << std::endl ; + +Let's make it a little more interesting. Lets make an 3x2 ndarray from a multi-dimensional array using non-unit strides + +First lets create a 3x4 array of 8-bit integers :: + + uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}}; + +Now let's create an array of 3x2 elements, picking the first and third elements from each row . For that, the shape will be 3x2. +The strides will be 4x2 i.e. 4 bytes to go to the next desired row, and 2 bytes to go to the next desired column :: + + shape = p::make_tuple(3,2) ; + stride = p::make_tuple(4,2) ; + +Get the numpy dtype for the built-in 8-bit integer data type :: + + np::dtype dt1 = np::dtype::get_builtin(); + +Now lets first create and print out the ndarray as is. +Notice how we can pass the shape and strides in the function directly, as well as the owner. The last part can be done because we don't have any use to +manipulate the "owner" object :: + + np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object()); + std::cout << "Original multi dimensional array :: " << std::endl << p::extract < char const * > (p::str(mul_data_ex)) << std::endl ; + +Now create the new ndarray using the shape and strides and print out the array we created using non-unit strides :: + + mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object()); + std::cout << "Selective multidimensional array :: "< (p::str(mul_data_ex)) << std::endl ; + +Note : The from_data method will throw "error_already_set" if the number of elements dictated by the shape and the corresponding strides don't match } - - - From 777e16e1d0566b8e35b43e19cbecf8cbc464dfaf Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Wed, 17 Aug 2011 18:25:44 +0000 Subject: [PATCH 061/128] Added example for ufunc --- libs/numpy/example/Jamfile | 1 + libs/numpy/example/ufunc.cpp | 81 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 libs/numpy/example/ufunc.cpp diff --git a/libs/numpy/example/Jamfile b/libs/numpy/example/Jamfile index 5aa47d99..1cab0490 100644 --- a/libs/numpy/example/Jamfile +++ b/libs/numpy/example/Jamfile @@ -24,3 +24,4 @@ exe dtype : dtype.cpp ../src//boost_numpy boost_python /python//python ; exe ndarray : ndarray.cpp ../src//boost_numpy boost_python /python//python ; exe hybrid : hybrid.cpp ../src//boost_numpy boost_python /python//python ; exe fromdata : fromdata.cpp ../src//boost_numpy boost_python /python//python ; +exe ufunc : ufunc.cpp ../src//boost_numpy boost_python /python//python ; diff --git a/libs/numpy/example/ufunc.cpp b/libs/numpy/example/ufunc.cpp new file mode 100644 index 00000000..a751cdcc --- /dev/null +++ b/libs/numpy/example/ufunc.cpp @@ -0,0 +1,81 @@ +/** + * @brief An example to demonstrate use of universal functions or ufuncs + * + * + * @todo Calling the overloaded () operator is in a roundabout manner, find a simpler way + * None of the methods like np::add, np::multiply etc are supported as yet + */ + +#include +#include + +namespace p = boost::python; +namespace np = boost::numpy; + + +// Create the structs necessary to implement the ufuncs +// The typedefs *must* be made + +struct UnarySquare +{ + typedef double argument_type; + typedef double result_type; + + double operator()(double r) const { return r * r;} +}; + +struct BinarySquare +{ + typedef double first_argument_type; + typedef double second_argument_type; + typedef double result_type; + + double operator()(double a,double b) const { return (a*a + b*b) ; } +}; + +int main(int argc, char **argv) +{ + // Initialize the Python runtime. + Py_Initialize(); + // Initialize NumPy + np::initialize(); + // Expose the struct UnarySquare to Python as a class, and let ud be the class object + p::object ud = p::class_ >("UnarySquare") + .def("__call__", np::unary_ufunc::make()); + // Let inst be an instance of the class ud + p::object inst = ud(); + // Use the "__call__" method to call the overloaded () operator and print the value + std::cout << "Square of unary scalar 1.0 is " << p::extract (p::str(inst.attr("__call__")(1.0))) << std::endl ; + // Create an array in C++ + int arr[] = {1,2,3,4} ; + // ..and use it to create the ndarray in Python + np::ndarray demo_array = np::from_data(arr, np::dtype::get_builtin() , p::make_tuple(4), p::make_tuple(4), p::object()); + // Print out the demo array + std::cout << "Demo array is " << p::extract (p::str(demo_array)) << std::endl ; + // Call the "__call__" method to perform the operation and assign the value to result_array + p::object result_array = inst.attr("__call__")(demo_array) ; + // Print the resultant array + std::cout << "Square of demo array is " << p::extract (p::str(result_array)) << std::endl ; + // Lets try the same with a list + p::list li ; + li.append(3); + li.append(7); + // Print out the demo list + std::cout << "Demo list is " << p::extract (p::str(li)) << std::endl ; + // Call the ufunc for the list + result_array = inst.attr("__call__")(li) ; + // And print the list out + std::cout << "Square of demo list is " << p::extract (p::str(result_array)) << std::endl ; + // Now lets try Binary ufuncs + // Expose the struct BinarySquare to Python as a class, and let ud be the class object + ud = p::class_ >("BinarySquare") + .def("__call__", np::binary_ufunc::make()); + // Again initialise inst as an instance of the class ud + inst = ud(); + // Print the two input listsPrint the two input lists + std::cout << "The two input list for binary ufunc are " << std::endl << p::extract (p::str(demo_array)) << std::endl << p::extract (p::str(demo_array)) << std::endl ; + // Call the binary ufunc taking demo_array as both inputs + result_array = inst.attr("__call__")(demo_array,demo_array) ; + std::cout << "Square of list with binary ufunc is " << p::extract (p::str(result_array)) << std::endl ; +} + From 7064cf31867fb467173069f6fe3ddd38ff97733d Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Wed, 17 Aug 2011 18:26:20 +0000 Subject: [PATCH 062/128] Added tutorial for ufunc --- libs/numpy/doc/Jamfile | 2 +- libs/numpy/doc/ufunc.rst | 116 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 libs/numpy/doc/ufunc.rst diff --git a/libs/numpy/doc/Jamfile b/libs/numpy/doc/Jamfile index ea99cad1..0afe6236 100644 --- a/libs/numpy/doc/Jamfile +++ b/libs/numpy/doc/Jamfile @@ -6,7 +6,7 @@ project user-config : requirements rst2html ; import docutils ; import path ; -sources = tutorial.rst dtype.rst ndarray.rst fromdata.rst ; +sources = tutorial.rst dtype.rst ndarray.rst fromdata.rst ufunc.rst ; bases = $(sources:S=) ; # This is a path relative to the html/ subdirectory where the diff --git a/libs/numpy/doc/ufunc.rst b/libs/numpy/doc/ufunc.rst new file mode 100644 index 00000000..1943e675 --- /dev/null +++ b/libs/numpy/doc/ufunc.rst @@ -0,0 +1,116 @@ +Ufuncs +====== + +Ufuncs or universal functions operate on ndarrays element by element, and support array broadcasting, type casting, and other features. + +Lets try and see how we can use the binary and unary ufunc methods + +After the neccessary includes :: + + #include + #include + + namespace p = boost::python; + namespace np = boost::numpy; + +Now we create the structs necessary to implement the ufuncs. The typedefs *must* be made as the ufunc generators take these typedefs as inputs and return an error otherwise :: + + struct UnarySquare + { + typedef double argument_type; + typedef double result_type; + + double operator()(double r) const { return r * r;} + }; + + struct BinarySquare + { + typedef double first_argument_type; + typedef double second_argument_type; + typedef double result_type; + + double operator()(double a,double b) const { return (a*a + b*b) ; } + }; + +Initialise the Python runtime and the numpy module :: + + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); + +Now expose the struct UnarySquare to Python as a class, and let ud be the class object. :: + + p::object ud = p::class_ >("UnarySquare") + .def("__call__", np::unary_ufunc::make()); + +Let inst be an instance of the class ud :: + + p::object inst = ud(); + +Use the "__call__" method to call the overloaded () operator and print the value :: + + std::cout << "Square of unary scalar 1.0 is " << p::extract (p::str(inst.attr("__call__")(1.0))) << std::endl ; + +Create an array in C++ :: + + int arr[] = {1,2,3,4} ; + + +..and use it to create the ndarray in Python :: + + np::ndarray demo_array = np::from_data(arr, np::dtype::get_builtin() , p::make_tuple(4), p::make_tuple(4), p::object()); + +Print out the demo array :: + + std::cout << "Demo array is " << p::extract (p::str(demo_array)) << std::endl ; + +Call the "__call__" method to perform the operation and assign the value to result_array :: + + p::object result_array = inst.attr("__call__")(demo_array) ; + +Print the resultant array :: + + std::cout << "Square of demo array is " << p::extract (p::str(result_array)) << std::endl ; + +Lets try the same with a list :: + + p::list li ; + li.append(3); + li.append(7); + +Print out the demo list :: + + std::cout << "Demo list is " << p::extract (p::str(li)) << std::endl ; + +Call the ufunc for the list :: + + result_array = inst.attr("__call__")(li) ; + +And print the list out :: + + std::cout << "Square of demo list is " << p::extract (p::str(result_array)) << std::endl ; + +Now lets try Binary ufuncs. Again, expose the struct BinarySquare to Python as a class, and let ud be the class object :: + + ud = p::class_ >("BinarySquare") + .def("__call__", np::binary_ufunc::make()); + +And initialise ud :: + + inst = ud(); + +Print the two input lists :: + + std::cout << "The two input list for binary ufunc are " << std::endl << p::extract (p::str(demo_array)) << std::endl << p::extract (p::str(demo_array)) << std::endl ; + +Call the binary ufunc taking demo_array as both inputs :: + + result_array = inst.attr("__call__")(demo_array,demo_array) ; + +And print the output :: + + std::cout << "Square of list with binary ufunc is " << p::extract (p::str(result_array)) << std::endl ; + +} + From 32d2135462a8bff00e975371df0efd9c6aedd9c4 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Thu, 25 Aug 2011 23:32:43 +0000 Subject: [PATCH 063/128] boost/numpy - enabled new unit tests in old SCons build system --- libs/numpy/test/SConscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/numpy/test/SConscript b/libs/numpy/test/SConscript index 114112ac..860cbf65 100644 --- a/libs/numpy/test/SConscript +++ b/libs/numpy/test/SConscript @@ -2,9 +2,9 @@ Import("boost_numpy_env") test = [] -for name in ("ufunc", "templates"): +for name in ("ufunc", "templates", "ndarray", "indexing", "shapes"): mod = boost_numpy_env.SharedLibrary("%s_mod" % name, "%s_mod.cpp" % name, SHLIBPREFIX="", - LIBS="boost_python_numpy") + LIBS="boost_numpy") test.extend( boost_numpy_env.PythonUnitTest("%s.py" % name, mod) ) From 2aca81bca9af7b61a320ff0a0a81af15e404613f Mon Sep 17 00:00:00 2001 From: Ankit Daftery Date: Wed, 31 Aug 2011 12:35:56 +0000 Subject: [PATCH 064/128] Added temporary directory for Reference documentation --- libs/numpy/doc/Reference/Jamfile | 25 ++ libs/numpy/doc/Reference/binary_ufunc.rst | 95 ++++++ libs/numpy/doc/Reference/dtype.rst | 88 +++++ libs/numpy/doc/Reference/multi_iter.rst | 104 ++++++ libs/numpy/doc/Reference/ndarray.rst | 383 ++++++++++++++++++++++ libs/numpy/doc/Reference/unary_ufunc.rst | 87 +++++ 6 files changed, 782 insertions(+) create mode 100644 libs/numpy/doc/Reference/Jamfile create mode 100644 libs/numpy/doc/Reference/binary_ufunc.rst create mode 100644 libs/numpy/doc/Reference/dtype.rst create mode 100644 libs/numpy/doc/Reference/multi_iter.rst create mode 100644 libs/numpy/doc/Reference/ndarray.rst create mode 100644 libs/numpy/doc/Reference/unary_ufunc.rst diff --git a/libs/numpy/doc/Reference/Jamfile b/libs/numpy/doc/Reference/Jamfile new file mode 100644 index 00000000..ca045773 --- /dev/null +++ b/libs/numpy/doc/Reference/Jamfile @@ -0,0 +1,25 @@ +# Copyright David Abrahams 2006. Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +project user-config : requirements rst2html ; + +import docutils ; + +import path ; +sources = dtype.rst ndarray.rst multi_iter.rst unary_ufunc.rst binary_ufunc.rst ; +bases = $(sources:S=) ; + +# This is a path relative to the html/ subdirectory where the +# generated output will eventually be moved. +stylesheet = "--stylesheet=rst.css" ; + +for local b in $(bases) +{ + html $(b) : $(b).rst : + + "-gdt --source-url="./$(b).rst" --link-stylesheet --traceback --trim-footnote-reference-space --footnote-references=superscript "$(stylesheet) + ; +} + +alias htmls : $(bases) ; +stage . : $(bases) ; diff --git a/libs/numpy/doc/Reference/binary_ufunc.rst b/libs/numpy/doc/Reference/binary_ufunc.rst new file mode 100644 index 00000000..6f569ffe --- /dev/null +++ b/libs/numpy/doc/Reference/binary_ufunc.rst @@ -0,0 +1,95 @@ +binary_ufunc +============ + +.. contents :: + +A ``binary_ufunc`` is a struct used as an intermediate step to broadcast two arguments so that a C++ function can be converted to a ufunc like function + + ```` contains the ``binary_ufunc`` structure definitions + + +synopsis +-------- + +:: + + namespace boost + { + namespace numpy + { + + template + + struct binary_ufunc + { + + static python::object call(TBinaryFunctor & self, python::object const & input1, python::object const & input2,python::object const & output) + + static python::object make(); + + } + } + } + + +constructors +------------ + +:: + + struct example_binary_ufunc + { + typedef any_valid first_argument_type; + typedef any_valid second_argument_type; + typedef any_valid result_type; + }; + +:Requirements: The ``any_valid`` type must be defined using typedef as a valid C++ type in order to use the struct methods correctly + +:Note: The struct must be exposed as a Python class, and an instance of the class must be created to use the ``call`` method corresponding to the ``__call__`` attribute of the Python object + +accessors +--------- + +:: + + template + static python::object call(TBinaryFunctor & self, python::object const & input, python::object const & output) ; + +:Requires: Typenames ``TBinaryFunctor`` and optionally ``TArgument1`` and ``TArgument2`` for argument type and ``TResult`` for result type + +:Effects: Passes a Python object to the underlying C++ functor after broadcasting its arguments + +:: + + template + static python::object make(); + +:Requires: Typenames ``TBinaryFunctor`` and optionally ``TArgument1`` and ``TArgument2`` for argument type and ``TResult`` for result type + +:Returns: A Python function object to call the overloaded () operator in the struct (in typical usage) + + + +Example(s) +---------- + +:: + + struct BinarySquare + { + typedef double first_argument_type; + typedef double second_argument_type; + typedef double result_type; + + double operator()(double a,double b) const { return (a*a + b*b) ; } + }; + + p::object ud = p::class_ >("BinarySquare").def("__call__", np::binary_ufunc::make()); + p::object inst = ud(); + result_array = inst.attr("__call__")(demo_array,demo_array) ; + std::cout << "Square of list with binary ufunc is " << p::extract (p::str(result_array)) << std::endl ; + diff --git a/libs/numpy/doc/Reference/dtype.rst b/libs/numpy/doc/Reference/dtype.rst new file mode 100644 index 00000000..d059ef46 --- /dev/null +++ b/libs/numpy/doc/Reference/dtype.rst @@ -0,0 +1,88 @@ +dtype +===== + +.. contents :: + +A `dtype`_ is an object describing the type of the elements of an ndarray + +.. _dtype: http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html#data-type-objects-dtype + + ```` contains the method calls necessary to generate a python object equivalent to a numpy.dtype from builtin C++ objects, as well as to create custom dtypes from user defined types + + +synopsis +-------- + +:: + + namespace boost + { + namespace numpy + { + + class dtype : public python::object + { + static python::detail::new_reference convert(python::object::object_cref arg, bool align); + public: + + // Convert an arbitrary Python object to a data-type descriptor object. + template + explicit dtype(T arg, bool align=false) : python::object(convert(arg, align)) {} + + template static dtype get_builtin(); // Get the built-in numpy dtype associated with the given scalar template type. + + int get_itemsize() const; // Return the size of the data type in bytes. + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, python::object); + + }; + + } + +constructors +------------ + +:: + + template + explicit dtype(T arg, bool align=false) : python::object(convert(arg, align)) {} + +:Requirements: The typename supplied, ``T`` must be either : + * a built-in C++ typename convertible to object + * a valid python object or convertible to object + +:Effects: Constructs an object from the supplied python object / convertible + to object / builtin C++ data type + +:Throws: Nothing + +:: + + template static dtype get_builtin(); + +:Requirements: The typename supplied, ``T`` must be a builtin C++ type also supported by numpy + +:Returns: Numpy dtype corresponding to builtin C++ type + +accessors +--------- + +:: + + int get_itemsize() const; + +:Returns: the size of the data type in bytes. + + +Example(s) +---------- + +:: + + namespace np = boost::numpy ; + + np::dtype dtype = np::dtype::get_builtin(); + + p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ; + np::dtype custom_dtype = np::dtype(list_for_dtype) ; + diff --git a/libs/numpy/doc/Reference/multi_iter.rst b/libs/numpy/doc/Reference/multi_iter.rst new file mode 100644 index 00000000..9317b52e --- /dev/null +++ b/libs/numpy/doc/Reference/multi_iter.rst @@ -0,0 +1,104 @@ +multi_iter +========== + +.. contents :: + +A ``multi_iter`` is a Python object, intended to be used as an iterator It should generally only be used in loops. + + ```` contains the class definitions for ``multi_iter`` + + +synopsis +-------- + +:: + + namespace boost + { + namespace numpy + { + + class multi_iter : public python::object + { + public: + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, python::object); + + void next(); + + bool not_done() const; + + char * get_data(int n) const; + + int const get_nd() const; + + Py_intptr_t const * get_shape() const; + + Py_intptr_t const shape(int n) const; + + }; + + + multi_iter make_multi_iter(python::object const & a1); + + multi_iter make_multi_iter(python::object const & a1, python::object const & a2); + + multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); + + } + } // namespace boost::numpy + + +constructors +------------ + +:: + + multi_iter make_multi_iter(python::object const & a1); + + multi_iter make_multi_iter(python::object const & a1, python::object const & a2); + + multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); + +:Returns: A Python iterator object broadcasting over one, two or three sequences as supplied + +accessors +--------- + +:: + + void next(); + +:Effects: Increments the iterator + +:: + + bool not_done() const; + +:Returns: boolean value indicating whether the iterator is at its end + +:: + + char * get_data(int n) const; + +:Returns: a pointer to the element of the nth broadcasted array. + +:: + + int const get_nd() const; + +:Returns: the number of dimensions of the broadcasted array expression + +:: + + Py_intptr_t const * get_shape() const; + +:Returns: the shape of the broadcasted array expression as an array of integers. + +:: + + Py_intptr_t const shape(int n) const; + +:Returns: the shape of the broadcasted array expression in the nth dimension. + + diff --git a/libs/numpy/doc/Reference/ndarray.rst b/libs/numpy/doc/Reference/ndarray.rst new file mode 100644 index 00000000..1adc0da4 --- /dev/null +++ b/libs/numpy/doc/Reference/ndarray.rst @@ -0,0 +1,383 @@ +ndarray +======= + +.. contents :: + +A `ndarray`_ is an N-dimensional array which contains items of the same type and size, where N is the number of dimensions and is specified in the form of a ``shape`` tuple. Optionally, the numpy ``dtype`` for the objects contained may also be specified. + +.. _ndarray: http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html +.. _dtype: http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html#data-type-objects-dtype + + ```` contains the structures and methods necessary to move raw data between C++ and Python and create ndarrays from the data + + + +synopsis +-------- + +:: + + namespace boost + { + namespace numpy + { + + class ndarray : public python::object + { + + public: + + 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); + + ndarray view(dtype const & dt) const; + + ndarray copy() const; + + int const shape(int n) const { return get_shape()[n]; } + + int const strides(int n) const { return get_strides()[n]; } + + char * get_data() const { return get_struct()->data; } + + dtype get_dtype() const; + + python::object get_base() const; + + void set_base(object const & base); + + Py_intptr_t const * get_shape() const { return get_struct()->shape; } + + Py_intptr_t const * get_strides() const { return get_struct()->strides; } + + int const get_nd() const { return get_struct()->nd; } + + bitflag const get_flags() const; + + ndarray transpose() const; + + ndarray squeeze() const; + + ndarray reshape(python::tuple const & shape) const; + + python::object scalarize() const; + }; + + ndarray zeros(python::tuple const & shape, dtype const & dt); + ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); + + ndarray empty(python::tuple const & shape, dtype const & dt); + ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); + + ndarray array(python::object const & obj); + ndarray array(python::object const & obj, dtype const & dt); + + template + inline ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner); + + template + inline ndarray from_data(void const * data, dtype const & dt, Container shape, Container strides, python::object const & owner); + + 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); + + inline ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE); + + 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); + + inline ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) + + inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) ; + + inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b); + + } // namespace boost::numpy + + +constructors +------------ + +:: + + ndarray view(dtype const & dt) const; + +:Returns: new ndarray with old ndarray data cast as supplied dtype + +:: + + ndarray copy() const; + +:Returns: Copy of calling ndarray object + +:: + + ndarray transpose() const; + +:Returns: An ndarray with the rows and columns interchanged + +:: + + ndarray squeeze() const; + +:Returns: An ndarray with all unit-shaped dimensions removed + +:: + + ndarray reshape(python::tuple const & shape) const; + +:Requirements: The new ``shape`` of the ndarray must be supplied as a tuple + +:Returns: An ndarray with the same data but reshaped to the ``shape`` supplied + + +:: + + python::object scalarize() const; + +:Returns: A scalar if the ndarray has only one element, otherwise it returns the entire array + +:: + + ndarray zeros(python::tuple const & shape, dtype const & dt); + ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); + +:Requirements: The following parameters must be supplied as required : + * the ``shape`` or the size of all dimensions, as a tuple + * the ``dtype`` of the data + * the ``nd`` size for a square shaped ndarray + * the ``shape`` Py_intptr_t + +:Returns: A new ndarray with the given shape and data type, with data initialized to zero. + +:: + + ndarray empty(python::tuple const & shape, dtype const & dt); + ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); + + +:Requirements: The following parameters must be supplied : + * the ``shape`` or the size of all dimensions, as a tuple + * the ``dtype`` of the data + * the ``shape`` Py_intptr_t + +:Returns: A new ndarray with the given shape and data type, with data left uninitialized. + +:: + + ndarray array(python::object const & obj); + ndarray array(python::object const & obj, dtype const & dt); + +:Returns: A new ndarray from an arbitrary Python sequence, with dtype of each element specified optionally + +:: + + template + inline ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner) + +:Requirements: The following parameters must be supplied : + * the ``data`` which is a generic C++ data container + * the dtype ``dt`` of the data + * the ``shape`` of the ndarray as Python object + * the ``strides`` of each dimension of the array as a Python object + * the ``owner`` of the data, in case it is not the ndarray itself + +:Returns: ndarray with attributes and data supplied + +:Note: The ``Container`` typename must be one that is convertible to a std::vector or python object type + +:: + + ndarray from_object(python::object const & obj, dtype const & dt,int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); + +:Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray + * the dtype ``dt`` of the data + * minimum number of dimensions ``nd_min`` of the ndarray as Python object + * maximum number of dimensions ``nd_max`` of the ndarray as Python object + * optional ``flags`` bitflags + +:Returns: ndarray constructed with dimensions and data supplied as parameters + +:: + + inline ndarray from_object(python::object const & obj, dtype const & dt, int nd, ndarray::bitflag flags=ndarray::NONE); + +:Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray + * the dtype ``dt`` of the data + * number of dimensions ``nd`` of the ndarray as Python object + * optional ``flags`` bitflags + +:Returns: ndarray with dimensions ``nd`` x ``nd`` and suplied parameters + +:: + + inline ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE) + +:Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray + * the dtype ``dt`` of the data + * optional ``flags`` bitflags + +:Returns: Supplied Python object as ndarray + +:: + + ndarray from_object(python::object const & obj, int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); + +:Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray + * minimum number of dimensions ``nd_min`` of the ndarray as Python object + * maximum number of dimensions ``nd_max`` of the ndarray as Python object + * optional ``flags`` bitflags + +:Returns: ndarray with supplied dimension limits and parameters + +:Note: dtype need not be supplied here + +:: + + inline ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); + +:Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray + * the dtype ``dt`` of the data + * number of dimensions ``nd`` of the ndarray as Python object + * optional ``flags`` bitflags + +:Returns: ndarray of ``nd`` x ``nd`` dimensions constructed from the supplied object + +:: + + inline ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) + +:Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray + * optional ``flags`` bitflags + +:Returns: ndarray of same dimensions and dtype as supplied Python object + + +accessors +--------- + +:: + + int const shape(int n) const { return get_shape()[n]; } + +:Returns: The size of the n-th dimension of the ndarray + +:: + + int const strides(int n) const { return get_strides()[n]; } + +:Returns: The stride of the nth dimension. + +:: + + char * get_data() const { return get_struct()->data; } + +:Returns: Array's raw data pointer as a char + +:Note: This returns char so stride math works properly on it.User will have to reinterpret_cast it. + +:: + + dtype get_dtype() const; + +:Returns: Array's data-type descriptor object (dtype) + + +:: + + python::object get_base() const; + +:Returns: Object that owns the array's data, or None if the array owns its own data. + + +:: + + void set_base(object const & base); + +:Returns: Set the object that owns the array's data. Exercise caution while using this + + +:: + + Py_intptr_t const * get_shape() const { return get_struct()->shape; } + +:Returns: Shape of the array as an array of integers + + +:: + + Py_intptr_t const * get_strides() const { return get_struct()->strides; } + +:Returns: Stride of the array as an array of integers + + +:: + + int const get_nd() const { return get_struct()->nd; } + +:Returns: Number of array dimensions + + +:: + + bitflag const get_flags() const; + +:Returns: Array flags + +:: + + inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) + +:Returns: bitflag logically OR-ed as (a | b) + +:: + + inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) + +:Returns: bitflag logically AND-ed as (a & b) + + +Example(s) +---------- + +:: + + p::object tu = p::make_tuple('a','b','c') ; + np::ndarray example_tuple = np::array (tu) ; + + p::list l ; + np::ndarray example_list = np::array (l) ; + + np::dtype dt = np::dtype::get_builtin(); + np::ndarray example_list1 = np::array (l,dt); + + int data[] = {1,2,3,4} ; + p::tuple shape = p::make_tuple(4) ; + p::tuple stride = p::make_tuple(4) ; + p::object own ; + np::ndarray data_ex = np::from_data(data,dt,shape,stride,own); + + uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}}; + shape = p::make_tuple(3,2) ; + stride = p::make_tuple(4,2) ; + np::dtype dt1 = np::dtype::get_builtin(); + + np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object()); + mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object()); + diff --git a/libs/numpy/doc/Reference/unary_ufunc.rst b/libs/numpy/doc/Reference/unary_ufunc.rst new file mode 100644 index 00000000..84771706 --- /dev/null +++ b/libs/numpy/doc/Reference/unary_ufunc.rst @@ -0,0 +1,87 @@ +unary_ufunc +=========== + +.. contents :: + +A ``unary_ufunc`` is a struct used as an intermediate step to broadcast a single argument so that a C++ function can be converted to a ufunc like function + + ```` contains the ``unary_ufunc`` structure definitions + + +synopsis +-------- + +:: + + namespace boost + { + namespace numpy + { + + template + struct unary_ufunc + { + + static python::object call(TUnaryFunctor & self, python::object const & input, python::object const & output) ; + + static python::object make(); + + } + } + } + + +constructors +------------ + +:: + + struct example_unary_ufunc + { + typedef any_valid_type argument_type; + typedef any_valid_type result_type; + }; + +:Requirements: The ``any_valid`` type must be defined using typedef as a valid C++ type in order to use the struct methods correctly + +:Note: The struct must be exposed as a Python class, and an instance of the class must be created to use the ``call`` method corresponding to the ``__call__`` attribute of the Python object + +accessors +--------- + +:: + + template + static python::object call(TUnaryFunctor & self, python::object const & input, python::object const & output) ; + +:Requires: Typenames ``TUnaryFunctor`` and optionally ``TArgument`` for argument type and ``TResult`` for result type + +:Effects: Passes a Python object to the underlying C++ functor after broadcasting its arguments + +:: + + template + static python::object make(); + +:Requires: Typenames ``TUnaryFunctor`` and optionally ``TArgument`` for argument type and ``TResult`` for result type + +:Returns: A Python function object to call the overloaded () operator in the struct (in typical usage) + + + +Example(s) +---------- + +:: + + struct UnarySquare + { + typedef double argument_type; + typedef double result_type; + double operator()(double r) const { return r * r;} + }; + + p::object ud = p::class_ >("UnarySquare").def("__call__", np::unary_ufunc::make()); + p::object inst = ud(); + std::cout << "Square of unary scalar 1.0 is " << p::extract (p::str(inst.attr("__call__")(1.0))) << std::endl ; + From 55c3b0569efb49a7b60987448b474a2ebf3abd7d Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Thu, 27 Oct 2011 14:12:14 +0000 Subject: [PATCH 065/128] Restructure documentation sources to build with sphinx. --- libs/numpy/doc/Makefile | 132 +++++++++++ libs/numpy/doc/_static/boost.css | 66 ++++++ libs/numpy/doc/_static/boost.png | Bin 0 -> 6308 bytes libs/numpy/doc/_templates/layout.html | 120 ++++++++++ libs/numpy/doc/conf.py | 219 ++++++++++++++++++ libs/numpy/doc/index.rst | 16 ++ libs/numpy/doc/make.bat | 170 ++++++++++++++ .../doc/{Reference => reference}/Jamfile | 0 .../{Reference => reference}/binary_ufunc.rst | 0 .../doc/{Reference => reference}/dtype.rst | 0 libs/numpy/doc/reference/index.rst | 14 ++ .../{Reference => reference}/multi_iter.rst | 0 .../doc/{Reference => reference}/ndarray.rst | 0 .../{Reference => reference}/unary_ufunc.rst | 0 libs/numpy/doc/{ => tutorial}/dtype.rst | 0 libs/numpy/doc/{ => tutorial}/fromdata.rst | 0 libs/numpy/doc/tutorial/index.rst | 14 ++ libs/numpy/doc/{ => tutorial}/ndarray.rst | 0 .../doc/{tutorial.rst => tutorial/simple.rst} | 0 libs/numpy/doc/{ => tutorial}/ufunc.rst | 0 20 files changed, 751 insertions(+) create mode 100644 libs/numpy/doc/Makefile create mode 100644 libs/numpy/doc/_static/boost.css create mode 100644 libs/numpy/doc/_static/boost.png create mode 100644 libs/numpy/doc/_templates/layout.html create mode 100644 libs/numpy/doc/conf.py create mode 100644 libs/numpy/doc/index.rst create mode 100644 libs/numpy/doc/make.bat rename libs/numpy/doc/{Reference => reference}/Jamfile (100%) rename libs/numpy/doc/{Reference => reference}/binary_ufunc.rst (100%) rename libs/numpy/doc/{Reference => reference}/dtype.rst (100%) create mode 100644 libs/numpy/doc/reference/index.rst rename libs/numpy/doc/{Reference => reference}/multi_iter.rst (100%) rename libs/numpy/doc/{Reference => reference}/ndarray.rst (100%) rename libs/numpy/doc/{Reference => reference}/unary_ufunc.rst (100%) rename libs/numpy/doc/{ => tutorial}/dtype.rst (100%) rename libs/numpy/doc/{ => tutorial}/fromdata.rst (100%) create mode 100644 libs/numpy/doc/tutorial/index.rst rename libs/numpy/doc/{ => tutorial}/ndarray.rst (100%) rename libs/numpy/doc/{tutorial.rst => tutorial/simple.rst} (100%) rename libs/numpy/doc/{ => tutorial}/ufunc.rst (100%) diff --git a/libs/numpy/doc/Makefile b/libs/numpy/doc/Makefile new file mode 100644 index 00000000..92112a24 --- /dev/null +++ b/libs/numpy/doc/Makefile @@ -0,0 +1,132 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +all: html + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BoostNumPy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BoostNumPy.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/BoostNumPy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BoostNumPy" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/libs/numpy/doc/_static/boost.css b/libs/numpy/doc/_static/boost.css new file mode 100644 index 00000000..986c4050 --- /dev/null +++ b/libs/numpy/doc/_static/boost.css @@ -0,0 +1,66 @@ +/*============================================================================= + Copyright 2002 William E. Kempf + Distributed under the Boost Software License, Version 1.0. (See accompany- + ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +H1 +{ + FONT-SIZE: 200%; + COLOR: #00008B; +} +H2 +{ + FONT-SIZE: 150%; +} +H3 +{ + FONT-SIZE: 125%; +} +H4 +{ + FONT-SIZE: 108%; +} +BODY +{ + FONT-SIZE: 100%; + BACKGROUND-COLOR: #ffffff; + COLOR: #000000; +} +PRE +{ + MARGIN-LEFT: 2em; + FONT-FAMILY: Courier, + monospace; +} +CODE +{ + FONT-FAMILY: Courier, + monospace; +} +CODE.as_pre +{ + white-space: pre; +} +.index +{ + TEXT-ALIGN: left; +} +.page-index +{ + TEXT-ALIGN: left; +} +.definition +{ + TEXT-ALIGN: left; +} +.footnote +{ + FONT-SIZE: 66%; + VERTICAL-ALIGN: super; + TEXT-DECORATION: none; +} +.function-semantics +{ + CLEAR: left; +} \ No newline at end of file diff --git a/libs/numpy/doc/_static/boost.png b/libs/numpy/doc/_static/boost.png new file mode 100644 index 0000000000000000000000000000000000000000..b4d51fcd5c9149fd77f5ca6ed2b6b1b70e8fe24f GIT binary patch literal 6308 zcmeAS@N?(olHy`uVBq!ia0y~yU=(FwU~)iH)5g zbF}vT%$qmo{2pS9asE?~yS4 zV~}*@!()Y0Y8HLTe-Cy%RA)$&m?L5Mr-f6RO~ozV_0NRY3o~1Lgxi$dvW4w;NZndD zlR*^3VYi=hVqyLvMt=V-1{{V_+&@0IB#0`@6669QfIo7R{( z3ohQ;EYDP7Gx74VKmF=OCnj|XE)MOKH}{k2T<}9tMWRb$ZQh>=2c7MBTjaeg3`Gp1 zOn)q7?Ek%8_>X~zVbts&3mN$xw|)-2UDdxRz3$(!-R|jkeqPU&t9|EQf4U-d>xFFg z{x{Ws1M+_v3h7m71U^}DEu~!BQ6S*mp|rVu(zeF6FR#qBJpbplS--)u7*~_>#FgoP zC0;&Blc}(I*wXQNtz+fgBa>K#&Pz3gS=T4tG_Ef>tdM*v)Mf9R;~oktCWxAuS7f+M zkY)H5Kj*{7I$4cZ0Rpn~XC94B`jlQOxK3N&`sd?@$!_ceJ*?v{!!S?h|1u2N0KqFL{jZD3|yYm@!7 zD{$ko5T!kC`67XKbkL|AAk>9FP1vNkmjDiX|^R^y;cp z^UAV29q)=Z9@9`fO2<&WREDl_D6c zquluTRZ|NyzoZApW6$HZ`(0auB>B#Jzf759_*7~`{jMwhHPzc6w)9V3{3&onmEH2% zy1=bZw@F0$zb(W{9 z3y<+%SSy^|qZ!UEsHV8u))-3_d=8X|6u5RU;FQR;B`srKi(_hUww{zF=hR3r! zXLBzsJ+<)d^@Z9(9_Fnfdv|Wq@t&1)^nJt=i_#|%nJxW)9rf71f7X9z|KnWAZGFS3 z6Aswit-5^U?sor@cTX63S$-tEJ0!F~YKzzL2CkY3US8MpYB<;)MEE6iKJ-umChec13b_tyxeQ=+0@?B&Bs0~0tT{en%M z!%db795*#F_ZI8rm3nY-i<6%G8IdAE^ zDcP6n-YUQkEQw;TGU7HvCEhOlZXez|o}Ik#ZL?n#f0 zm&@0usQ+!aHM{IEtN)He?(1_cF30}4pyQB{na7p?Yt8cWT4DhvyZ3Gk>NcJ@p<`k8 zb?L*N3y*YaGWf?i-b}h|dV}Y$;ZwfjTPJK+?Nynme|0gZZy~q5i)`feg`e7*FI?+1 z3@AFW_=oZTk8Tl*&Of=by#9l1Jjd5fcb~`G2q;xtI3*F#bbUFm$+xKbV)a$C6pB^^ z1lmV4oNhM{)j56VprliIp4*~^l%?ygdcFK&!0`C8>zBZ7qSF(;8{W9w$+1hRPbq0j z+)56cPdt$hA_o`mkbLbYy3?Fb@PqKL13660Kc?a<=bYSg062edXWB zWk)QFCOK^l>QzbpWBd40zP}UyN9`o5y*vwbUtjd_Dwm#8d^#yhph!H`qy`^UIetc?g_a^_;BpJd%79iM`k@&x+!*DZpNpYH*(v#^RIqB%W%DElQ`FVt9xtp`qG%b zC$}Fd&--&DH83jV)Kse%$@gWZo1E>i$#8qWhU1%|-rcg^X`PE_ty+0>&6INuT-yR% zxPP|Y*w}t-ZO3=V6RVy*aV_z@=v=p|=8kdC)JwZxtCz&GZ9Y^LW^|u{U;} zH9gjEzv*t-9Vo|D|*8v{A{fASF z4@9J_E8;M}@S;Pv?~(;Wf{ALi+8wFP_qSPdPi;PPZT2qF^A}I27XSOPuxC%n@p-B( zt8}g_)E>XC@_5s$Ihsaw+xtIH>NwT4DojsxvZltzn@X#$*eqz$;ZVGtzxlvVrCkAL zC0jgvN+r{B`J+0jc8c<>U@`AYK5={|i<-zw(b;?XCpx|^@%g&??zdAd8Z`mar`!L! zZNKl7bW!WK4_O=6t-C$pQNDb)q{7qLpXc>9NX>H&=WWnz^4h7YXsF`0BZgJh_OeNJ z%)&xn`-aN`M%?~q7tWm>=<~qKRC8tM8$a%L{ey2kI8txkxx3xJ;j7Q%D{3XiFZ{lA z@!CjtaC^NE+kflmOwWbxatr6_&(6MF|6t{woyqeTuLzr<&3y94wX=cek{6aeUJ?*A zA|7V$;*0%XQzrZUg(%toS)1l9e_o_KgKhsEl z%>TgoO~d@AO(F$~s`B5L-VF&l#Q9|Tyjsb)_4*kL6%Aq?l9x1=_fJ3j^FjMRW|bX# zU6Q`WES&Q2hiJpbyKS#ltvcns@e11nSMJJ^B1hxqr*{?>ay(Q_Ii`J_HS?Id?{T} zdujR8{u=dDvyGLc7uJ{ttvLQ5V9SoEsjpLa>P>%Dv{6m`!HQ{LoEx3>RuuJJe^uzo z{$VBSd{ZZrwhL!mZPoQYZBh&R_c2j-wsMC1wHuXxj_54pxvBSE<#2{|uAf=& zLd15vt&n7VlFY&B#>m^cA54%ctolSO;BmGUrUyj5Bh|5j?+)pHw?WDm47pW+O9 zapkFU{jK18t4ejHF7IXSZmrrDuz$w-=P9P)*ZONBbwjSyJihQL^)G|A>LjU0Uffq_ zP3tT!?=ek$DPW;3yhBoPfpqV_$y2N5tS-y=v!LPh+Jb#i)vEJb_8ysf>GQqyWr3G< z<*w!ME8g1r{q(aHPxvJ*`JS!~nLbHq{`A-XO}A#ukbjnCIr)d1)J)4iQ)ixFu9xvX zdbIm!h~4~ndy9g%0`-4k^9VYS(!B`f@odS%TN&ghk&qa}1AIa%-D z$8C%`#j>qQJDtBXs~)#t@!o%HYf)UqGs6jT?Ds6M{Z!I6SYh*cVS(xQgvW+jf6sp3 z%so|0|7hvz$A68DHtp`@W!duRzSKsWSBHw;cBd95xB2s=OnIuGqAJRyPqPNOGM$x>QK{U%tz_lxWgo+MmmXe~{? z<6L};Q#fzI>}!WZYtIYx=emVuWtaUr=Jt1c(B+_*qx+(6Z#*WwGihVyleYH@II39l zJ}fpd_~g2ix4PlMk40;i#J?3PS$kamrfc7Gj+R&Nj#^!K)PC&K&X9*flk{r%&Oecw z$vY#nZ;SG@=TDqu>)3-l<3c~F*7BZTJDcS);{$`1J7N=4Ib!F!Nk6J$F<%w-=ETb* zKNiM^ef`t@yRiBGJlV-tye__U(Eq=+*}b5bt!U-f)v-ozIo<^wx8%2qmlKz;H<``9 z?#>>rkBeCDRDDZI&GXC@{m^vb)UT`G?N;lEg*ZCYBr$8H9;}L#f4(j5@a&6A^WvF4 z-KzNbjJK*TGEmh|QQ>&<&6sJY_`RR0-w|QI;&7gQM?mVFbDfhbtTLL{JbD+t`=L;x z5R-ZP?ckrzeJ5hpeVBAYT>IlE<(%Q0v@s{d_?sS#t{kL+XXYA?fR9UiJUAOA%gODBCxwpUXuhfe;p`E03 zF7@-)mBOc2Y&2ObS(`l1>~XeE`mL@HGKXcqe`|lb|JUUuB2T9)&#W?3Pc@RhUfArJ zyRzeiw0>PSkE+X(D_c~;pY7>but)IN+MnFs2lQCxw%<`b;5pB_y`hp}j@J%xmRmJt zmG7Nz-@T=L*3GgZ!k>Fd{M&8Hm0x%d)HS~rKNOX~^lAQ&kK7BmOU|pG{P}BH;grd* zcRf8^%)U(Af3CLa^D-&!^*_@@mYZ@vnIU*d|Kpy@>rZ$;&R*YjC2Q6s>wclboSK^^ zTwkTNsdABYb!6d+t*s}nGXKhVU8lm@`n>WETQ|pN&8znRch4_*+-mo?`Tc`uZ%$~< zk7Zzswd4Ks@yU0&EdQ%Kdt+lSYqQCHJ;^Mb@O!7c?W;~k2ie3u#h10u|4r_Q$g2Ol zZBb|8JnQWtmS0mZ&U$ub)Q*n)+)&UqVQtiM#gL#cmh&@0ItsTs z1)iH;(qFcZ!+k4*`^u;(Ul%WsH?`<`8nrS;xc92vJR^3&@_Ugd;<&aq`)=*cadVcG zSbO-b{ey(~&x@zOElW;N>i25gBfd~SUEyK7O?S}hSL-%QulI9dUHCM#Sh*$1EyLhw z{+Sn(0yL*P3VVNAs&Z=i4knq*eG|mGGUM#oS}yJC*yE>>s#K&Fbgo0xGeG*)g6aR- zgzYqX*7nQnWy+HF)o+s!N?Rwr^p51>^tWyM_FO3sG1@zKN`2Iehf5g0m>=JD?rY(e zn3bwY6X%xa{CGOCa<^SujI{g2TF&o1udT{g#<26AEBF54z`5tM!koOeyv31oA0<2$ zpI6DlGih7-8g@RmKF(K0?|gUX9x)NPGo!lvzPqxak4N*}r>hsY?VHXq_5Xo~9vk-U z-5e9-s>%1~=l%WG_22f~;S$O%+SuCUmfRM+@S%;t5;hezvt#dN`<<1i2E{$fo+lr+ z;5g^gMbL zx2)>qe`Ql(vEz}_s*s)w=XIIGRvhf&y)RI7!tiwcglRu~o0XPidAYVPa1^Usv&Gu+ z$22i!{>62V*pCz4}_#;jX@bd|JO za2+kbcnxgq{F*xk#37XT8qPBx!(S?FYm8#N8M!PH;<~B z_}HVu&bV24@xKSh7w*}aSu1ohwf6kQ{JTae@i`7N1aFy5bJ%r$*DMj!_%lD0j_Ybk zR!Dq3SlURq6Xm1$<7<^CO0zSow1OaD-NHDJb{Ibplgsxl00r+8kT@_*YnA?-hW?T&hC41^5*9p{QuwU%Wj!(x1(12-A4wS zzC(|b!za#vICVRp^XnEX+xd!u{r@h1|NcOMrS;_7f3GgQzd!F+bLp<~@3j^ldp7<5 zaAo7^$Y~?mc2%Zv>*mS3f^JXSvsldxyo}N%gGWcP}gc$%I_v zI|}R%7S_)W_+iME$M%82zO+w%vZkhY-)~NN;aG*Zd4FDdrv#=Mu8aKf#B1k6xnR5b z=b7HmGhL#3)|lb%YwrDjYVrg(yt0&-*~(de@xNa!!-J2mYkodwsCe-4-_Q5oOF|<% zE+`x~KEBS;^xK=>4;wpP#ZuI{a?4J5hV)2Qt0!ho(xkd_=2*otMnY2&#==q+9 z`$N{{NXhOher>VsLy_Ev1hI2cR_Fgtu6GIe*3kcce{lMv?fo?l6&}Zp87dwa7U&<> z@%eF^U-}bELvEt9`pO0Mju#GUS3NrZ-&51?@R9z<+I!~Be^9Ee9g-YDqJ}hT+{K3mK7V(2mOeeZM zFiYMbpC@MVvi+Frg}{do7urZD9>}%$!ytS(p6f(Qw9a1c?-Bve*4;k#@NMIXrgFR5 zM<$H+X{{~$IpSD*m_GQO=~M0r&MW8WP=4{^-pl+v!6%%G@h1d7-QCn7uw87Cx6~Ky z7prR5|37B)_u*oGA-1#^OHLjvyl=Jeu!H{lJ?#zIjpdHgCb{uV>++@KTEraXjaM7( zeBStb+eFuyk)jS9$GCTJ-duKB-ez9ec zlkl@RE6K&I!>P~SranO>?6_bYpT`MvUh^JSn+tn)KTLY6@qMM!j|&zq-pMZN@iWjrucTLs03Gq8jX0xSk+4b=;_re8-+I5;FDkRnx{&`T~q*+sATf}42 zb4{e~gaB8UjEvj~KQlHtx78B%771J0Wu!z5KNv&>|5)e}DE&*?nQ!rWt~!CQ<&w81 z+kK%?tyDs7T(@uhz*ICMX35d@9K_ej)97$*Hl iJhEc{m;X#W3{O<*lBMphi)3J6VDNPHb6Mw<&;$TZQ2{;x literal 0 HcmV?d00001 diff --git a/libs/numpy/doc/_templates/layout.html b/libs/numpy/doc/_templates/layout.html new file mode 100644 index 00000000..d109db5a --- /dev/null +++ b/libs/numpy/doc/_templates/layout.html @@ -0,0 +1,120 @@ +{%- macro navbar() %} + +{%- endmacro %} + + + + + + {{ metatags }} + {%- if builder != 'htmlhelp' %} + {%- set titlesuffix = docstitle|e %} + {%- set titlesuffix = " - " + titlesuffix %} + {%- endif %} + {{ title|striptags }}{{ titlesuffix }} + {%- if builder == 'web' %} + + {%- for link, type, title in page_links %} + + {%- endfor %} + {%- else %} + + + + {%- endif %} + {%- if builder != 'htmlhelp' %} + + {%- for scriptfile in script_files %} + + {%- endfor %} + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + + + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + + +
+ + + + + + + +
+

C++ Boost

+
+

Boost.NumPy

+ +
+ {%- if pagename != "search" %} + + + {%- endif %} +
+
+
+
+ {%- block top_navbar %}{{ navbar() }}{% endblock %} + {% block body %} {% endblock %} + {%- block bottom_navbar %}{{ navbar() }}{% endblock %} +
+ + diff --git a/libs/numpy/doc/conf.py b/libs/numpy/doc/conf.py new file mode 100644 index 00000000..19a0033b --- /dev/null +++ b/libs/numpy/doc/conf.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +# +# Boost.NumPy documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 27 09:04:58 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Boost.NumPy' +copyright = u'2011, Stefan Seefeld' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'default' + +highlight_language = 'c++' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'boost.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'BoostNumPydoc' + +html_add_permalinks = False + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'BoostNumPy.tex', u'Boost.NumPy Documentation', + u'Stefan Seefeld', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'boostnumpy', u'Boost.NumPy Documentation', + [u'Stefan Seefeld'], 1) +] diff --git a/libs/numpy/doc/index.rst b/libs/numpy/doc/index.rst new file mode 100644 index 00000000..01c8c08a --- /dev/null +++ b/libs/numpy/doc/index.rst @@ -0,0 +1,16 @@ +.. Boost.NumPy documentation master file, created by + sphinx-quickstart on Thu Oct 27 09:04:58 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Boost.NumPy's documentation! +======================================= + +Contents: + +.. toctree:: + :maxdepth: 2 + + Tutorial + Reference + diff --git a/libs/numpy/doc/make.bat b/libs/numpy/doc/make.bat new file mode 100644 index 00000000..fbf68af6 --- /dev/null +++ b/libs/numpy/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\BoostNumPy.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\BoostNumPy.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/libs/numpy/doc/Reference/Jamfile b/libs/numpy/doc/reference/Jamfile similarity index 100% rename from libs/numpy/doc/Reference/Jamfile rename to libs/numpy/doc/reference/Jamfile diff --git a/libs/numpy/doc/Reference/binary_ufunc.rst b/libs/numpy/doc/reference/binary_ufunc.rst similarity index 100% rename from libs/numpy/doc/Reference/binary_ufunc.rst rename to libs/numpy/doc/reference/binary_ufunc.rst diff --git a/libs/numpy/doc/Reference/dtype.rst b/libs/numpy/doc/reference/dtype.rst similarity index 100% rename from libs/numpy/doc/Reference/dtype.rst rename to libs/numpy/doc/reference/dtype.rst diff --git a/libs/numpy/doc/reference/index.rst b/libs/numpy/doc/reference/index.rst new file mode 100644 index 00000000..27de3ee2 --- /dev/null +++ b/libs/numpy/doc/reference/index.rst @@ -0,0 +1,14 @@ +Boost.NumPy Reference +===================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + dtype + ndarray + unary_ufunc + binary_ufunc + multi_iter + diff --git a/libs/numpy/doc/Reference/multi_iter.rst b/libs/numpy/doc/reference/multi_iter.rst similarity index 100% rename from libs/numpy/doc/Reference/multi_iter.rst rename to libs/numpy/doc/reference/multi_iter.rst diff --git a/libs/numpy/doc/Reference/ndarray.rst b/libs/numpy/doc/reference/ndarray.rst similarity index 100% rename from libs/numpy/doc/Reference/ndarray.rst rename to libs/numpy/doc/reference/ndarray.rst diff --git a/libs/numpy/doc/Reference/unary_ufunc.rst b/libs/numpy/doc/reference/unary_ufunc.rst similarity index 100% rename from libs/numpy/doc/Reference/unary_ufunc.rst rename to libs/numpy/doc/reference/unary_ufunc.rst diff --git a/libs/numpy/doc/dtype.rst b/libs/numpy/doc/tutorial/dtype.rst similarity index 100% rename from libs/numpy/doc/dtype.rst rename to libs/numpy/doc/tutorial/dtype.rst diff --git a/libs/numpy/doc/fromdata.rst b/libs/numpy/doc/tutorial/fromdata.rst similarity index 100% rename from libs/numpy/doc/fromdata.rst rename to libs/numpy/doc/tutorial/fromdata.rst diff --git a/libs/numpy/doc/tutorial/index.rst b/libs/numpy/doc/tutorial/index.rst new file mode 100644 index 00000000..ca15971c --- /dev/null +++ b/libs/numpy/doc/tutorial/index.rst @@ -0,0 +1,14 @@ +Boost.NumPy Tutorial +==================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + simple + dtype + ndarray + ufunc + fromdata + diff --git a/libs/numpy/doc/ndarray.rst b/libs/numpy/doc/tutorial/ndarray.rst similarity index 100% rename from libs/numpy/doc/ndarray.rst rename to libs/numpy/doc/tutorial/ndarray.rst diff --git a/libs/numpy/doc/tutorial.rst b/libs/numpy/doc/tutorial/simple.rst similarity index 100% rename from libs/numpy/doc/tutorial.rst rename to libs/numpy/doc/tutorial/simple.rst diff --git a/libs/numpy/doc/ufunc.rst b/libs/numpy/doc/tutorial/ufunc.rst similarity index 100% rename from libs/numpy/doc/ufunc.rst rename to libs/numpy/doc/tutorial/ufunc.rst From beaa4b0e4d39914e7523ac5db0818b04c79b16c0 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sat, 29 Oct 2011 18:20:25 +0000 Subject: [PATCH 066/128] Remove implementation details from documentation. --- libs/numpy/doc/conf.py | 2 +- libs/numpy/doc/reference/dtype.rst | 13 +++--- libs/numpy/doc/reference/multi_iter.rst | 8 ---- libs/numpy/doc/reference/ndarray.rst | 61 +++++++++---------------- 4 files changed, 28 insertions(+), 56 deletions(-) diff --git a/libs/numpy/doc/conf.py b/libs/numpy/doc/conf.py index 19a0033b..84e20f4c 100644 --- a/libs/numpy/doc/conf.py +++ b/libs/numpy/doc/conf.py @@ -112,7 +112,7 @@ html_theme = 'default' # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = 'boost.png' +html_logo = '_static/boost.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 diff --git a/libs/numpy/doc/reference/dtype.rst b/libs/numpy/doc/reference/dtype.rst index d059ef46..2902e896 100644 --- a/libs/numpy/doc/reference/dtype.rst +++ b/libs/numpy/doc/reference/dtype.rst @@ -27,14 +27,13 @@ synopsis // Convert an arbitrary Python object to a data-type descriptor object. template - explicit dtype(T arg, bool align=false) : python::object(convert(arg, align)) {} + explicit dtype(T arg, bool align=false); - template static dtype get_builtin(); // Get the built-in numpy dtype associated with the given scalar template type. - - int get_itemsize() const; // Return the size of the data type in bytes. - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, python::object); + // Get the built-in numpy dtype associated with the given scalar template type. + template static dtype get_builtin(); + // Return the size of the data type in bytes. + int get_itemsize() const; }; } @@ -45,7 +44,7 @@ constructors :: template - explicit dtype(T arg, bool align=false) : python::object(convert(arg, align)) {} + explicit dtype(T arg, bool align=false) :Requirements: The typename supplied, ``T`` must be either : * a built-in C++ typename convertible to object diff --git a/libs/numpy/doc/reference/multi_iter.rst b/libs/numpy/doc/reference/multi_iter.rst index 9317b52e..b502e494 100644 --- a/libs/numpy/doc/reference/multi_iter.rst +++ b/libs/numpy/doc/reference/multi_iter.rst @@ -21,19 +21,11 @@ synopsis class multi_iter : public python::object { public: - - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, python::object); - void next(); - bool not_done() const; - char * get_data(int n) const; - int const get_nd() const; - Py_intptr_t const * get_shape() const; - Py_intptr_t const shape(int n) const; }; diff --git a/libs/numpy/doc/reference/ndarray.rst b/libs/numpy/doc/reference/ndarray.rst index 1adc0da4..0e447179 100644 --- a/libs/numpy/doc/reference/ndarray.rst +++ b/libs/numpy/doc/reference/ndarray.rst @@ -36,29 +36,17 @@ synopsis UPDATE_ALL=0x1|0x2|0x4, VARRAY=0x1|0x2|0x8, ALL=0x1|0x2|0x4|0x8 }; - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(ndarray, object); - ndarray view(dtype const & dt) const; - ndarray copy() const; - - int const shape(int n) const { return get_shape()[n]; } - - int const strides(int n) const { return get_strides()[n]; } - - char * get_data() const { return get_struct()->data; } - + int const shape(int n) const; + int const strides(int n) const; + char * get_data() const; dtype get_dtype() const; - python::object get_base() const; - void set_base(object const & base); - - Py_intptr_t const * get_shape() const { return get_struct()->shape; } - - Py_intptr_t const * get_strides() const { return get_struct()->strides; } - - int const get_nd() const { return get_struct()->nd; } + Py_intptr_t const * get_shape() const; + Py_intptr_t const * get_strides() const; + int const get_nd() const; bitflag const get_flags() const; @@ -81,28 +69,21 @@ synopsis ndarray array(python::object const & obj, dtype const & dt); template - inline ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner); - + ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner); template - inline ndarray from_data(void const * data, dtype const & dt, Container shape, Container strides, python::object const & owner); + ndarray from_data(void const * data, dtype const & dt, Container shape, Container strides, python::object const & owner); 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); - - inline ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE); - + ndarray from_object(python::object const & obj, dtype const & dt,int nd, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE); ndarray from_object(python::object const & obj, int nd_min, int nd_max,ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) - inline ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); + ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) ; + ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b); - inline ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) - - inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) ; - - inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b); - - } // namespace boost::numpy + } constructors @@ -274,19 +255,19 @@ accessors :: - int const shape(int n) const { return get_shape()[n]; } + int const shape(int n) const; :Returns: The size of the n-th dimension of the ndarray :: - int const strides(int n) const { return get_strides()[n]; } + int const strides(int n) const; :Returns: The stride of the nth dimension. :: - char * get_data() const { return get_struct()->data; } + char * get_data() const; :Returns: Array's raw data pointer as a char @@ -315,21 +296,21 @@ accessors :: - Py_intptr_t const * get_shape() const { return get_struct()->shape; } + Py_intptr_t const * get_shape() const; :Returns: Shape of the array as an array of integers :: - Py_intptr_t const * get_strides() const { return get_struct()->strides; } + Py_intptr_t const * get_strides() const; :Returns: Stride of the array as an array of integers :: - int const get_nd() const { return get_struct()->nd; } + int const get_nd() const; :Returns: Number of array dimensions From 2a8823f7457a14f25fc5b4a38e3fef6bdc62da0c Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sat, 29 Oct 2011 20:39:37 +0000 Subject: [PATCH 067/128] Fix formatting issues in the Reference Manual. --- libs/numpy/doc/reference/binary_ufunc.rst | 85 +++++---- libs/numpy/doc/reference/dtype.rst | 55 +++--- libs/numpy/doc/reference/multi_iter.rst | 61 +++--- libs/numpy/doc/reference/ndarray.rst | 218 +++++++++++----------- libs/numpy/doc/reference/unary_ufunc.rst | 70 ++++--- 5 files changed, 254 insertions(+), 235 deletions(-) diff --git a/libs/numpy/doc/reference/binary_ufunc.rst b/libs/numpy/doc/reference/binary_ufunc.rst index 6f569ffe..23a36f77 100644 --- a/libs/numpy/doc/reference/binary_ufunc.rst +++ b/libs/numpy/doc/reference/binary_ufunc.rst @@ -13,24 +13,29 @@ synopsis :: - namespace boost - { - namespace numpy - { + namespace boost + { + namespace numpy + { - template + template - struct binary_ufunc - { + struct binary_ufunc + { - static python::object call(TBinaryFunctor & self, python::object const & input1, python::object const & input2,python::object const & output) + static python::object call(TBinaryFunctor & self, + python::object const & input1, + python::object const & input2, + python::object const & output); - static python::object make(); + static python::object make(); + }; - } - } - } + } + } constructors @@ -38,12 +43,12 @@ constructors :: - struct example_binary_ufunc - { - typedef any_valid first_argument_type; - typedef any_valid second_argument_type; - typedef any_valid result_type; - }; + struct example_binary_ufunc + { + typedef any_valid first_argument_type; + typedef any_valid second_argument_type; + typedef any_valid result_type; + }; :Requirements: The ``any_valid`` type must be defined using typedef as a valid C++ type in order to use the struct methods correctly @@ -54,9 +59,13 @@ accessors :: - template - static python::object call(TBinaryFunctor & self, python::object const & input, python::object const & output) ; + template + static python::object call(TBinaryFunctor & self, + python::object const & input, + python::object const & output); :Requires: Typenames ``TBinaryFunctor`` and optionally ``TArgument1`` and ``TArgument2`` for argument type and ``TResult`` for result type @@ -64,32 +73,32 @@ accessors :: - template - static python::object make(); + template + static python::object make(); :Requires: Typenames ``TBinaryFunctor`` and optionally ``TArgument1`` and ``TArgument2`` for argument type and ``TResult`` for result type :Returns: A Python function object to call the overloaded () operator in the struct (in typical usage) - - Example(s) ---------- :: - struct BinarySquare - { - typedef double first_argument_type; - typedef double second_argument_type; - typedef double result_type; + struct BinarySquare + { + typedef double first_argument_type; + typedef double second_argument_type; + typedef double result_type; - double operator()(double a,double b) const { return (a*a + b*b) ; } - }; + double operator()(double a,double b) const { return (a*a + b*b) ; } + }; - p::object ud = p::class_ >("BinarySquare").def("__call__", np::binary_ufunc::make()); - p::object inst = ud(); - result_array = inst.attr("__call__")(demo_array,demo_array) ; - std::cout << "Square of list with binary ufunc is " << p::extract (p::str(result_array)) << std::endl ; + p::object ud = p::class_ >("BinarySquare").def("__call__", np::binary_ufunc::make()); + p::object inst = ud(); + result_array = inst.attr("__call__")(demo_array,demo_array) ; + std::cout << "Square of list with binary ufunc is " << p::extract (p::str(result_array)) << std::endl ; diff --git a/libs/numpy/doc/reference/dtype.rst b/libs/numpy/doc/reference/dtype.rst index 2902e896..b76e79a4 100644 --- a/libs/numpy/doc/reference/dtype.rst +++ b/libs/numpy/doc/reference/dtype.rst @@ -15,38 +15,39 @@ synopsis :: - namespace boost - { - namespace numpy - { + namespace boost + { + namespace numpy + { - class dtype : public python::object - { - static python::detail::new_reference convert(python::object::object_cref arg, bool align); - public: + class dtype : public python::object + { + static python::detail::new_reference convert(python::object::object_cref arg, bool align); + public: - // Convert an arbitrary Python object to a data-type descriptor object. - template - explicit dtype(T arg, bool align=false); + // Convert an arbitrary Python object to a data-type descriptor object. + template + explicit dtype(T arg, bool align=false); - // Get the built-in numpy dtype associated with the given scalar template type. - template static dtype get_builtin(); + // Get the built-in numpy dtype associated with the given scalar template type. + template static dtype get_builtin(); - // Return the size of the data type in bytes. - int get_itemsize() const; - }; + // Return the size of the data type in bytes. + int get_itemsize() const; + }; - } + } constructors ------------ :: - template - explicit dtype(T arg, bool align=false) + template + explicit dtype(T arg, bool align=false) + +:Requirements: ``T`` must be either : -:Requirements: The typename supplied, ``T`` must be either : * a built-in C++ typename convertible to object * a valid python object or convertible to object @@ -57,7 +58,7 @@ constructors :: - template static dtype get_builtin(); + template static dtype get_builtin(); :Requirements: The typename supplied, ``T`` must be a builtin C++ type also supported by numpy @@ -68,7 +69,7 @@ accessors :: - int get_itemsize() const; + int get_itemsize() const; :Returns: the size of the data type in bytes. @@ -78,10 +79,8 @@ Example(s) :: - namespace np = boost::numpy ; - - np::dtype dtype = np::dtype::get_builtin(); - - p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ; - np::dtype custom_dtype = np::dtype(list_for_dtype) ; + namespace np = boost::numpy; + np::dtype dtype = np::dtype::get_builtin(); + p::tuple for_custom_dtype = p::make_tuple("ha",dtype); + np::dtype custom_dtype = np::dtype(list_for_dtype); diff --git a/libs/numpy/doc/reference/multi_iter.rst b/libs/numpy/doc/reference/multi_iter.rst index b502e494..610f91ee 100644 --- a/libs/numpy/doc/reference/multi_iter.rst +++ b/libs/numpy/doc/reference/multi_iter.rst @@ -13,32 +13,29 @@ synopsis :: - namespace boost - { - namespace numpy - { + namespace boost + { + namespace numpy + { - class multi_iter : public python::object - { - public: - void next(); - bool not_done() const; - char * get_data(int n) const; - int const get_nd() const; - Py_intptr_t const * get_shape() const; - Py_intptr_t const shape(int n) const; - - }; + class multi_iter : public python::object + { + public: + void next(); + bool not_done() const; + char * get_data(int n) const; + int const get_nd() const; + Py_intptr_t const * get_shape() const; + Py_intptr_t const shape(int n) const; + }; - multi_iter make_multi_iter(python::object const & a1); + multi_iter make_multi_iter(python::object const & a1); + multi_iter make_multi_iter(python::object const & a1, python::object const & a2); + multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); - multi_iter make_multi_iter(python::object const & a1, python::object const & a2); - - multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); - - } - } // namespace boost::numpy + } + } constructors @@ -46,11 +43,9 @@ constructors :: - multi_iter make_multi_iter(python::object const & a1); - - multi_iter make_multi_iter(python::object const & a1, python::object const & a2); - - multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); + multi_iter make_multi_iter(python::object const & a1); + multi_iter make_multi_iter(python::object const & a1, python::object const & a2); + multi_iter make_multi_iter(python::object const & a1, python::object const & a2, python::object const & a3); :Returns: A Python iterator object broadcasting over one, two or three sequences as supplied @@ -59,37 +54,37 @@ accessors :: - void next(); + void next(); :Effects: Increments the iterator :: - bool not_done() const; + bool not_done() const; :Returns: boolean value indicating whether the iterator is at its end :: - char * get_data(int n) const; + char * get_data(int n) const; :Returns: a pointer to the element of the nth broadcasted array. :: - int const get_nd() const; + int const get_nd() const; :Returns: the number of dimensions of the broadcasted array expression :: - Py_intptr_t const * get_shape() const; + Py_intptr_t const * get_shape() const; :Returns: the shape of the broadcasted array expression as an array of integers. :: - Py_intptr_t const shape(int n) const; + Py_intptr_t const shape(int n) const; :Returns: the shape of the broadcasted array expression in the nth dimension. diff --git a/libs/numpy/doc/reference/ndarray.rst b/libs/numpy/doc/reference/ndarray.rst index 0e447179..fd5c76d0 100644 --- a/libs/numpy/doc/reference/ndarray.rst +++ b/libs/numpy/doc/reference/ndarray.rst @@ -17,73 +17,70 @@ synopsis :: - namespace boost - { - namespace numpy - { + namespace boost + { + namespace numpy + { - class ndarray : public python::object - { + class ndarray : public python::object + { - public: - - 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 - }; + public: + + 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 + }; - ndarray view(dtype const & dt) const; - ndarray copy() const; - int const shape(int n) const; - int const strides(int n) const; - char * get_data() const; - dtype get_dtype() const; - python::object get_base() const; - void set_base(object const & base); - Py_intptr_t const * get_shape() const; - Py_intptr_t const * get_strides() const; - int const get_nd() const; - - bitflag const get_flags() const; - - ndarray transpose() const; - - ndarray squeeze() const; - - ndarray reshape(python::tuple const & shape) const; - - python::object scalarize() const; - }; + ndarray view(dtype const & dt) const; + ndarray copy() const; + int const shape(int n) const; + int const strides(int n) const; + char * get_data() const; + dtype get_dtype() const; + python::object get_base() const; + void set_base(object const & base); + Py_intptr_t const * get_shape() const; + Py_intptr_t const * get_strides() const; + int const get_nd() const; + + bitflag const get_flags() const; + + ndarray transpose() const; + ndarray squeeze() const; + ndarray reshape(python::tuple const & shape) const; + python::object scalarize() const; + }; - ndarray zeros(python::tuple const & shape, dtype const & dt); - ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); + ndarray zeros(python::tuple const & shape, dtype const & dt); + ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); - ndarray empty(python::tuple const & shape, dtype const & dt); - ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); + ndarray empty(python::tuple const & shape, dtype const & dt); + ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); - ndarray array(python::object const & obj); - ndarray array(python::object const & obj, dtype const & dt); + ndarray array(python::object const & obj); + ndarray array(python::object const & obj, dtype const & dt); - template - ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner); - template - ndarray from_data(void const * data, dtype const & dt, Container shape, Container strides, python::object const & owner); + template + ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner); + template + ndarray from_data(void const * data, dtype const & dt, Container shape, Container strides, python::object const & owner); - ndarray from_object(python::object const & obj, dtype const & dt,int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); - ndarray from_object(python::object const & obj, dtype const & dt,int nd, ndarray::bitflag flags=ndarray::NONE); - ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE); - ndarray from_object(python::object const & obj, int nd_min, int nd_max,ndarray::bitflag flags=ndarray::NONE); - ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); - ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) + ndarray from_object(python::object const & obj, dtype const & dt,int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, dtype const & dt,int nd, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, int nd_min, int nd_max,ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) - ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) ; - ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b); + ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) ; + ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b); - } + } constructors @@ -91,31 +88,31 @@ constructors :: - ndarray view(dtype const & dt) const; + ndarray view(dtype const & dt) const; :Returns: new ndarray with old ndarray data cast as supplied dtype :: - ndarray copy() const; + ndarray copy() const; :Returns: Copy of calling ndarray object :: - ndarray transpose() const; + ndarray transpose() const; :Returns: An ndarray with the rows and columns interchanged :: - ndarray squeeze() const; + ndarray squeeze() const; :Returns: An ndarray with all unit-shaped dimensions removed :: - ndarray reshape(python::tuple const & shape) const; + ndarray reshape(python::tuple const & shape) const; :Requirements: The new ``shape`` of the ndarray must be supplied as a tuple @@ -124,16 +121,17 @@ constructors :: - python::object scalarize() const; + python::object scalarize() const; :Returns: A scalar if the ndarray has only one element, otherwise it returns the entire array :: - ndarray zeros(python::tuple const & shape, dtype const & dt); - ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); + ndarray zeros(python::tuple const & shape, dtype const & dt); + ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt); :Requirements: The following parameters must be supplied as required : + * the ``shape`` or the size of all dimensions, as a tuple * the ``dtype`` of the data * the ``nd`` size for a square shaped ndarray @@ -143,11 +141,12 @@ constructors :: - ndarray empty(python::tuple const & shape, dtype const & dt); - ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); + ndarray empty(python::tuple const & shape, dtype const & dt); + ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt); :Requirements: The following parameters must be supplied : + * the ``shape`` or the size of all dimensions, as a tuple * the ``dtype`` of the data * the ``shape`` Py_intptr_t @@ -156,17 +155,18 @@ constructors :: - ndarray array(python::object const & obj); - ndarray array(python::object const & obj, dtype const & dt); + ndarray array(python::object const & obj); + ndarray array(python::object const & obj, dtype const & dt); :Returns: A new ndarray from an arbitrary Python sequence, with dtype of each element specified optionally :: - template - inline ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner) + template + inline ndarray from_data(void * data,dtype const & dt,Container shape,Container strides,python::object const & owner) :Requirements: The following parameters must be supplied : + * the ``data`` which is a generic C++ data container * the dtype ``dt`` of the data * the ``shape`` of the ndarray as Python object @@ -179,9 +179,10 @@ constructors :: - ndarray from_object(python::object const & obj, dtype const & dt,int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, dtype const & dt,int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); :Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray * the dtype ``dt`` of the data * minimum number of dimensions ``nd_min`` of the ndarray as Python object @@ -192,9 +193,10 @@ constructors :: - inline ndarray from_object(python::object const & obj, dtype const & dt, int nd, ndarray::bitflag flags=ndarray::NONE); + inline ndarray from_object(python::object const & obj, dtype const & dt, int nd, ndarray::bitflag flags=ndarray::NONE); :Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray * the dtype ``dt`` of the data * number of dimensions ``nd`` of the ndarray as Python object @@ -204,9 +206,10 @@ constructors :: - inline ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE) + inline ndarray from_object(python::object const & obj, dtype const & dt, ndarray::bitflag flags=ndarray::NONE) :Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray * the dtype ``dt`` of the data * optional ``flags`` bitflags @@ -215,9 +218,10 @@ constructors :: - ndarray from_object(python::object const & obj, int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); + ndarray from_object(python::object const & obj, int nd_min, int nd_max, ndarray::bitflag flags=ndarray::NONE); :Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray * minimum number of dimensions ``nd_min`` of the ndarray as Python object * maximum number of dimensions ``nd_max`` of the ndarray as Python object @@ -229,9 +233,10 @@ constructors :: - inline ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); + inline ndarray from_object(python::object const & obj, int nd, ndarray::bitflag flags=ndarray::NONE); :Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray * the dtype ``dt`` of the data * number of dimensions ``nd`` of the ndarray as Python object @@ -241,9 +246,10 @@ constructors :: - inline ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) + inline ndarray from_object(python::object const & obj, ndarray::bitflag flags=ndarray::NONE) :Requirements: The following parameters must be supplied : + * the ``obj`` Python object to convert to ndarray * optional ``flags`` bitflags @@ -255,19 +261,19 @@ accessors :: - int const shape(int n) const; + int const shape(int n) const; :Returns: The size of the n-th dimension of the ndarray :: - int const strides(int n) const; + int const strides(int n) const; :Returns: The stride of the nth dimension. :: - char * get_data() const; + char * get_data() const; :Returns: Array's raw data pointer as a char @@ -275,61 +281,61 @@ accessors :: - dtype get_dtype() const; + dtype get_dtype() const; :Returns: Array's data-type descriptor object (dtype) :: - python::object get_base() const; + python::object get_base() const; :Returns: Object that owns the array's data, or None if the array owns its own data. :: - void set_base(object const & base); + void set_base(object const & base); :Returns: Set the object that owns the array's data. Exercise caution while using this :: - Py_intptr_t const * get_shape() const; + Py_intptr_t const * get_shape() const; :Returns: Shape of the array as an array of integers :: - Py_intptr_t const * get_strides() const; + Py_intptr_t const * get_strides() const; :Returns: Stride of the array as an array of integers :: - int const get_nd() const; + int const get_nd() const; :Returns: Number of array dimensions :: - bitflag const get_flags() const; + bitflag const get_flags() const; :Returns: Array flags :: - inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) + inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) :Returns: bitflag logically OR-ed as (a | b) :: - inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) + inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) :Returns: bitflag logically AND-ed as (a & b) @@ -339,26 +345,26 @@ Example(s) :: - p::object tu = p::make_tuple('a','b','c') ; - np::ndarray example_tuple = np::array (tu) ; + p::object tu = p::make_tuple('a','b','c') ; + np::ndarray example_tuple = np::array (tu) ; - p::list l ; - np::ndarray example_list = np::array (l) ; + p::list l ; + np::ndarray example_list = np::array (l) ; - np::dtype dt = np::dtype::get_builtin(); - np::ndarray example_list1 = np::array (l,dt); + np::dtype dt = np::dtype::get_builtin(); + np::ndarray example_list1 = np::array (l,dt); - int data[] = {1,2,3,4} ; - p::tuple shape = p::make_tuple(4) ; - p::tuple stride = p::make_tuple(4) ; - p::object own ; - np::ndarray data_ex = np::from_data(data,dt,shape,stride,own); + int data[] = {1,2,3,4} ; + p::tuple shape = p::make_tuple(4) ; + p::tuple stride = p::make_tuple(4) ; + p::object own ; + np::ndarray data_ex = np::from_data(data,dt,shape,stride,own); - uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}}; - shape = p::make_tuple(3,2) ; - stride = p::make_tuple(4,2) ; - np::dtype dt1 = np::dtype::get_builtin(); + uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}}; + shape = p::make_tuple(3,2) ; + stride = p::make_tuple(4,2) ; + np::dtype dt1 = np::dtype::get_builtin(); - np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object()); - mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object()); + np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object()); + mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object()); diff --git a/libs/numpy/doc/reference/unary_ufunc.rst b/libs/numpy/doc/reference/unary_ufunc.rst index 84771706..1cdf6c75 100644 --- a/libs/numpy/doc/reference/unary_ufunc.rst +++ b/libs/numpy/doc/reference/unary_ufunc.rst @@ -13,22 +13,26 @@ synopsis :: - namespace boost - { - namespace numpy - { + namespace boost + { + namespace numpy + { - template - struct unary_ufunc - { + template + struct unary_ufunc + { - static python::object call(TUnaryFunctor & self, python::object const & input, python::object const & output) ; + static python::object call(TUnaryFunctor & self, + python::object const & input, + python::object const & output) ; - static python::object make(); + static python::object make(); - } - } - } + }; + } + } constructors @@ -36,11 +40,11 @@ constructors :: - struct example_unary_ufunc - { - typedef any_valid_type argument_type; - typedef any_valid_type result_type; - }; + struct example_unary_ufunc + { + typedef any_valid_type argument_type; + typedef any_valid_type result_type; + }; :Requirements: The ``any_valid`` type must be defined using typedef as a valid C++ type in order to use the struct methods correctly @@ -51,8 +55,12 @@ accessors :: - template - static python::object call(TUnaryFunctor & self, python::object const & input, python::object const & output) ; + template + static python::object call(TUnaryFunctor & self, + python::object const & input, + python::object const & output); :Requires: Typenames ``TUnaryFunctor`` and optionally ``TArgument`` for argument type and ``TResult`` for result type @@ -60,8 +68,10 @@ accessors :: - template - static python::object make(); + template + static python::object make(); :Requires: Typenames ``TUnaryFunctor`` and optionally ``TArgument`` for argument type and ``TResult`` for result type @@ -74,14 +84,14 @@ Example(s) :: - struct UnarySquare - { - typedef double argument_type; - typedef double result_type; - double operator()(double r) const { return r * r;} - }; + struct UnarySquare + { + typedef double argument_type; + typedef double result_type; + double operator()(double r) const { return r * r;} + }; - p::object ud = p::class_ >("UnarySquare").def("__call__", np::unary_ufunc::make()); - p::object inst = ud(); - std::cout << "Square of unary scalar 1.0 is " << p::extract (p::str(inst.attr("__call__")(1.0))) << std::endl ; + p::object ud = p::class_ >("UnarySquare").def("__call__", np::unary_ufunc::make()); + p::object inst = ud(); + std::cout << "Square of unary scalar 1.0 is " << p::extract (p::str(inst.attr("__call__")(1.0))) << std::endl ; From 9d7dfd84494f1541acbde46e7f5c1dbcf5bf31f3 Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Sun, 30 Oct 2011 14:43:53 +0000 Subject: [PATCH 068/128] added gaussian example, updated scons build --- SConscript | 3 +- libs/numpy/example/SConscript | 11 ++ libs/numpy/example/demo_gaussian.py | 32 ++++ libs/numpy/example/gaussian.cpp | 237 ++++++++++++++++++++++++++++ site_scons/scons_tools.py | 8 +- 5 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 libs/numpy/example/SConscript create mode 100644 libs/numpy/example/demo_gaussian.py create mode 100644 libs/numpy/example/gaussian.cpp diff --git a/SConscript b/SConscript index 9a43d32b..984a4351 100644 --- a/SConscript +++ b/SConscript @@ -10,7 +10,7 @@ scons_tools.LocalConfiguration( ) 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()) +libpath = os.path.abspath("libs/numpy/src") if os.environ.has_key("LD_LIBRARY_PATH"): boost_numpy_env["ENV"]["LD_LIBRARY_PATH"] = "%s:%s" % (libpath, os.environ["LD_LIBRARY_PATH"]) else: @@ -28,6 +28,7 @@ targets["boost.numpy"]["install"] = ( + boost_numpy_env.Install(boost_numpy_env["INSTALL_LIB"], targets["boost.numpy"]["lib"]) ) targets["boost.numpy"]["test"] = SConscript("libs/numpy/test/SConscript") +targets["boost.numpy"]["example"] = SConscript("libs/numpy/example/SConscript") Return("targets") diff --git a/libs/numpy/example/SConscript b/libs/numpy/example/SConscript new file mode 100644 index 00000000..5fce67fc --- /dev/null +++ b/libs/numpy/example/SConscript @@ -0,0 +1,11 @@ +Import("boost_numpy_env") + +example = [] + +for name in ("ufunc", "dtype", "fromdata", "ndarray", "simple"): + example.extend(boost_numpy_env.Program(name, "%s.cpp" % name, LIBS="boost_numpy")) + +for name in ("gaussian",): + example.extend(boost_numpy_env.SharedLibrary(name, "%s.cpp" % name, SHLIBPREFIX="", LIBS="boost_numpy")) + +Return("example") diff --git a/libs/numpy/example/demo_gaussian.py b/libs/numpy/example/demo_gaussian.py new file mode 100644 index 00000000..51fe1318 --- /dev/null +++ b/libs/numpy/example/demo_gaussian.py @@ -0,0 +1,32 @@ +import numpy +import gaussian + +mu = numpy.zeros(2, dtype=float) +sigma = numpy.identity(2, dtype=float) +sigma[0, 1] = 0.15 +sigma[1, 0] = 0.15 + +g = gaussian.bivariate_gaussian(mu, sigma) + +r = numpy.linspace(-40, 40, 1001) +x, y = numpy.meshgrid(r, r) + +z = g(x, y) + +s = z.sum() * (r[1] - r[0])**2 +print "sum (should be ~ 1):", s + +xc = (z * x).sum() / z.sum() +print "x centroid (should be ~ %f): %f" % (mu[0], xc) + +yc = (z * y).sum() / z.sum() +print "y centroid (should be ~ %f): %f" % (mu[1], yc) + +xx = (z * (x - xc)**2).sum() / z.sum() +print "xx moment (should be ~ %f): %f" % (sigma[0,0], xx) + +yy = (z * (y - yc)**2).sum() / z.sum() +print "yy moment (should be ~ %f): %f" % (sigma[1,1], yy) + +xy = 0.5 * (z * (x - xc) * (y - yc)).sum() / z.sum() +print "xy moment (should be ~ %f): %f" % (sigma[0,1], xy) diff --git a/libs/numpy/example/gaussian.cpp b/libs/numpy/example/gaussian.cpp new file mode 100644 index 00000000..c20eb30f --- /dev/null +++ b/libs/numpy/example/gaussian.cpp @@ -0,0 +1,237 @@ +#include +#include + +#include +#include +#include + +namespace bp = boost::python; +namespace bn = boost::numpy; + +/** + * This class represents a simple 2-d Gaussian (Normal) distribution, defined by a + * mean vector 'mu' and a covariance matrix 'sigma'. + */ +class bivariate_gaussian { +public: + + /** + * Boost.NumPy isn't designed to support specific C++ linear algebra or matrix/vector libraries; + * it's intended as a lower-level interface that can be used with any such C++ library. + * + * Here, we'll demonstrate these techniques with boost::ublas, but the same general principles + * should apply to other matrix/vector libraries. + */ + typedef boost::numeric::ublas::c_vector vector; + typedef boost::numeric::ublas::c_matrix matrix; + + vector const & get_mu() const { return _mu; } + + matrix const & get_sigma() const { return _sigma; } + + /** + * Evaluate the density of the distribution at a point defined by a two-element vector. + */ + double operator()(vector const & p) const { + vector u = prod(_cholesky, p - _mu); + return 0.5 * _cholesky(0, 0) * _cholesky(1, 1) * std::exp(-0.5 * inner_prod(u, u)) / M_PI; + } + + /** + * Evaluate the density of the distribution at an (x, y) point. + */ + double operator()(double x, double y) const { + vector p; + p[0] = x; + p[1] = y; + return operator()(p); + } + + /** + * Construct from a mean vector and covariance matrix. + */ + bivariate_gaussian(vector const & mu, matrix const & sigma) + : _mu(mu), _sigma(sigma), _cholesky(compute_inverse_cholesky(sigma)) + {} + +private: + + /** + * This evaluates the inverse of the Cholesky factorization of a 2x2 matrix; + * it's just a shortcut in evaluating the density. + */ + static matrix compute_inverse_cholesky(matrix const & m) { + matrix l; + // First do cholesky factorization: l l^t = m + l(0, 0) = std::sqrt(m(0, 0)); + l(0, 1) = m(0, 1) / l(0, 0); + l(1, 1) = std::sqrt(m(1, 1) - l(0,1) * l(0,1)); + // Now do forward-substitution (in-place) to invert: + l(0, 0) = 1.0 / l(0, 0); + l(1, 0) = l(0, 1) = -l(0, 1) / l(1, 1); + l(1, 1) = 1.0 / l(1, 1); + return l; + } + + vector _mu; + matrix _sigma; + matrix _cholesky; + +}; + +/* + * We have a two options for wrapping get_mu and get_sigma into NumPy-returning Python methods: + * - we could deep-copy the data, making totally new NumPy arrays; + * - we could make NumPy arrays that point into the existing memory. + * The latter is often preferable, especially if the arrays are large, but it's dangerous unless + * the reference counting is correct: the returned NumPy array needs to hold a reference that + * keeps the memory it points to from being deallocated as long as it is alive. This is what the + * "owner" argument to from_data does - the NumPy array holds a reference to the owner, keeping it + * from being destroyed. + * + * Note that this mechanism isn't completely safe for data members that can have their internal + * storage reallocated. A std::vector, for instance, can be invalidated when it is resized, + * so holding a Python reference to a C++ class that holds a std::vector may not be a guarantee + * that the memory in the std::vector will remain valid. + */ + +/** + * These two functions are custom wrappers for get_mu and get_sigma, providing the shallow-copy + * conversion with reference counting described above. + * + * It's also worth noting that these return NumPy arrays that cannot be modified in Python; + * the const overloads of vector::data() and matrix::data() return const references, + * and passing a const pointer to from_data causes NumPy's 'writeable' flag to be set to false. + */ +static bn::ndarray py_get_mu(bp::object const & self) { + bivariate_gaussian::vector const & mu = bp::extract(self)().get_mu(); + return bn::from_data( + mu.data(), + bn::dtype::get_builtin(), + bp::make_tuple(2), + bp::make_tuple(sizeof(double)), + self + ); +} +static bn::ndarray py_get_sigma(bp::object const & self) { + bivariate_gaussian::matrix const & sigma = bp::extract(self)().get_sigma(); + return bn::from_data( + sigma.data(), + bn::dtype::get_builtin(), + bp::make_tuple(2, 2), + bp::make_tuple(2 * sizeof(double), sizeof(double)), + self + ); +} + +/** + * To allow the constructor to work, we need to define some from-Python converters from NumPy arrays + * to the ublas types. The rvalue-from-python functionality is not well-documented in Boost.Python + * itself; you can learn more from boost/python/converter/rvalue_from_python_data.hpp. + */ + +/** + * We start with two functions that just copy a NumPy array into ublas objects. These will be used + * in the templated converted below. The first just uses the operator[] overloads provided by + * bp::object. + */ +static void copy_ndarray_to_ublas(bn::ndarray const & array, bivariate_gaussian::vector & vec) { + vec[0] = bp::extract(array[0]); + vec[1] = bp::extract(array[1]); +} +/** + * Here, we'll take the alternate approach of using the strides to access the array's memory directly. + * This can be much faster for large arrays. + */ +static void copy_ndarray_to_ublas(bn::ndarray const & array, bivariate_gaussian::matrix & mat) { + // Unfortunately, get_strides() can't be inlined, so it's best to call it once up-front. + Py_intptr_t const * strides = array.get_strides(); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + mat(i, j) = *reinterpret_cast(array.get_data() + i * strides[0] + j * strides[1]); + } + } +} + +template +struct bivariate_gaussian_ublas_from_python { + + /** + * Register the converter. + */ + bivariate_gaussian_ublas_from_python() { + bp::converter::registry::push_back( + &convertible, + &construct, + bp::type_id< T >() + ); + } + + /** + * Test to see if we can convert this to the desired type; if not return zero. + * If we can convert, returned pointer can be used by construct(). + */ + static void * convertible(PyObject * p) { + try { + bp::object obj(bp::handle<>(bp::borrowed(p))); + std::auto_ptr array( + new bn::ndarray( + bn::from_object(obj, bn::dtype::get_builtin(), N, N, bn::ndarray::V_CONTIGUOUS) + ) + ); + if (array->shape(0) != 2) return 0; + if (N == 2 && array->shape(1) != 2) return 0; + return array.release(); + } catch (bp::error_already_set & err) { + bp::handle_exception(); + return 0; + } + } + + /** + * Finish the conversion by initializing the C++ object into memory prepared by Boost.Python. + */ + static void construct(PyObject * obj, bp::converter::rvalue_from_python_stage1_data * data) { + // Extract the array we passed out of the convertible() member function. + std::auto_ptr array(reinterpret_cast(data->convertible)); + // Find the memory block Boost.Python has prepared for the result. + typedef bp::converter::rvalue_from_python_storage storage_t; + storage_t * storage = reinterpret_cast(data); + // Use placement new to initialize the result. + T * ublas_obj = new (storage->storage.bytes) T(); + // Fill the result with the values from the NumPy array. + copy_ndarray_to_ublas(*array, *ublas_obj); + // Finish up. + data->convertible = storage->storage.bytes; + } + +}; + + +BOOST_PYTHON_MODULE(gaussian) { + bn::initialize(); + + // Register the from-python converters + bivariate_gaussian_ublas_from_python< bivariate_gaussian::vector, 1 >(); + bivariate_gaussian_ublas_from_python< bivariate_gaussian::matrix, 2 >(); + + typedef double (bivariate_gaussian::*call_vector)(bivariate_gaussian::vector const &) const; + + bp::class_("bivariate_gaussian", bp::init()) + + // Declare the constructor (wouldn't work without the from-python converters). + .def(bp::init< bivariate_gaussian::vector const &, bivariate_gaussian::matrix const & >()) + + // Use our custom reference-counting getters + .add_property("mu", &py_get_mu) + .add_property("sigma", &py_get_sigma) + + // First overload accepts a two-element array argument + .def("__call__", (call_vector)&bivariate_gaussian::operator()) + + // This overload works like a binary NumPy universal function: you can pass + // in scalars or arrays, and the C++ function will automatically be called + // on each element of an array argument. + .def("__call__", bn::binary_ufunc::make()) + ; +} diff --git a/site_scons/scons_tools.py b/site_scons/scons_tools.py index 2dc98a69..8ccf2532 100755 --- a/site_scons/scons_tools.py +++ b/site_scons/scons_tools.py @@ -251,6 +251,7 @@ def MakeAliases(targets): all_all = [] build_all = [] install_all = [] + example_all = [] test_all = [] scons.Help(""" To specify additional directories to add to the include or library paths, specify them @@ -263,7 +264,8 @@ Supported variables are CPPPATH, LIBPATH and RPATH. Global targets: 'all' (builds everything) 'build' (builds headers, libraries, and python wrappers) 'install' (copies files to global bin, include and lib directories) - 'test' (runs unit tests; requires install) + 'example' (builds examples; may require install) + 'test' (runs unit tests; may require install) These targets can be built for individual packages with the syntax '[package]-[target]'. Some packages support additional targets, given below. @@ -275,7 +277,7 @@ Packages: for pkg_name in sorted(targets.keys()): pkg_targets = targets[pkg_name] extra_targets = tuple(k for k in pkg_targets.keys() if k not in - ("all","build","install","test")) + ("all","build","install","test","example")) if extra_targets: scons.Help("%-25s %s\n" % (pkg_name, ", ".join("'%s'" % k for k in extra_targets))) else: @@ -290,11 +292,13 @@ Packages: all_all.extend(pkg_all) build_all.extend(pkg_build) install_all.extend(pkg_targets.get("install", pkg_build)) + example_all.extend(pkg_targets.get("example", pkg_targets.get("install", pkg_build))) test_all.extend(pkg_targets.get("test", pkg_targets.get("install", pkg_build))) env.Alias("all", all_all) env.Alias("build", build_all) env.Alias("install", install_all) env.Alias("test", test_all) + env.Alias("example", example_all) env.Default("build") From 7a84a00673040646f844a48b2fcbefa1b2c38e22 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 30 Oct 2011 20:54:20 +0000 Subject: [PATCH 069/128] build new 'gaussian.cpp' extension. --- libs/numpy/example/Jamfile | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libs/numpy/example/Jamfile b/libs/numpy/example/Jamfile index 1cab0490..7355f0e9 100644 --- a/libs/numpy/example/Jamfile +++ b/libs/numpy/example/Jamfile @@ -10,18 +10,11 @@ project /boost/numpy/example ; lib boost_python ; -#lib libxml2 : : xml2 /usr/lib/ ; - -#project : requirements -# /usr/include/libxml2 -# ../../.. -# libxml2 -# /boost/xml//boost_xml -# ; - exe simple : simple.cpp ../src//boost_numpy boost_python /python//python ; exe dtype : dtype.cpp ../src//boost_numpy boost_python /python//python ; exe ndarray : ndarray.cpp ../src//boost_numpy boost_python /python//python ; exe hybrid : hybrid.cpp ../src//boost_numpy boost_python /python//python ; exe fromdata : fromdata.cpp ../src//boost_numpy boost_python /python//python ; exe ufunc : ufunc.cpp ../src//boost_numpy boost_python /python//python ; + +python-extension gaussian : gaussian.cpp ../src//boost_numpy boost_python ; From 0cb3bd7aa5a2ef7e6d2ef942eeb49d1fe062eaf5 Mon Sep 17 00:00:00 2001 From: Stefan Seefeld Date: Sun, 30 Oct 2011 22:25:34 +0000 Subject: [PATCH 070/128] Add navigation bar. --- libs/numpy/doc/_static/home.png | Bin 0 -> 358 bytes libs/numpy/doc/_static/next.png | Bin 0 -> 336 bytes libs/numpy/doc/_static/prev.png | Bin 0 -> 334 bytes libs/numpy/doc/_static/up.png | Bin 0 -> 370 bytes libs/numpy/doc/_templates/layout.html | 14 +++++++------- 5 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 libs/numpy/doc/_static/home.png create mode 100644 libs/numpy/doc/_static/next.png create mode 100644 libs/numpy/doc/_static/prev.png create mode 100644 libs/numpy/doc/_static/up.png diff --git a/libs/numpy/doc/_static/home.png b/libs/numpy/doc/_static/home.png new file mode 100644 index 0000000000000000000000000000000000000000..5584aacb097a80e66a5320312b6e4eb017af1a06 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0y~yU=Rjj7G?$phK4%r{|pQa%*9TgAsieWw;%dHU|?WS z3GfMV6&Dfg?(A@OuseF>a6(*+nzF*onKLh6zO-%YmOy{sw6wJU|Nk%gvq74Hfq|za z$S?Rm0x$^OKX;CSfq}EYBeIx*fm;ZK886+f`@_J%pjzS@Q4*Y=R#Ki=l*-_nm|T>f zo0^iDsNj}alvU8YOY t9}F9JU6`43jMG5vNQA&%OQ656nzF)*4nJa0`Jj)#l9-t%+}PK^d+g590~2^trx_V+aGYt)W#Kgko@Q{~>i6>w}LxPb)_bi1gN;4a>^d{wc%%ZlYo1O0u~loc*tzSP~>;p||S5Et|R|Noh1c$yg)73T~N2spa`a*~JRJ5eh~I1}5!gYtAz;Fo=OPI2WZRmSpDVDTHL^rZN~B=o=X8 z8<-ql-^0nkz!2u?;uumfC;0|1OPoRyGxLNShYX~Tl_Wf9G1_imu)%RA9}mw<0X2^e zQioc&m}WXSvRw^OFi2qFa&lm1W^U?K=~^Ook|m{hVvche^Q6-g?(V)Vn8U=toEqFE UkjD9gfq{X+)78&qol`;+00?PtqyPW_ literal 0 HcmV?d00001 diff --git a/libs/numpy/doc/_static/up.png b/libs/numpy/doc/_static/up.png new file mode 100644 index 0000000000000000000000000000000000000000..17d9c3ec491ae1ba22188ce85985623c92ffa9be GIT binary patch literal 370 zcmeAS@N?(olHy`uVBq!ia0y~yU=Rjj7G?$phK4%r{|pQa%*9TgAsieWw;%dHU|?X- z3h)VW6&DdqOG|Thu-mqE%ZlYoyE{8BU%nLR@2jS)FmvY2qel)W#Kk;%^zi@x|I?Un z*fKCM@RbDl1^-6|46X<6oM2#J;4JWnEM{Qf76M_$OLy!3FfcHvmbgZg1m~xflqVLY zGWaGY7v<-srer26xMdclmgg5`7c2NiC>R+Sn6#IzInThrAO_OlT$Gwvl9`{U5R#dj z%3x@qZ(yu%U~+tY4<`cyLy@P8V@SoEspmFwHW&!FJyeg_(XezvV9WvAI|r@_>dZZG zPW6aiOT!J--9O?NG0%AP;}ge|4lDQN4=-}8`?JGwx}?mMnO)OdyQdu$nQCjPRV}jm z$u!Qa8E-cQ-r3Nz>Y(YPTd#BPEH+&8GWqfD!}4*53%dA!%#3$cIv;a~fq{X+)78&q Iol`;+0POUaApigX literal 0 HcmV?d00001 diff --git a/libs/numpy/doc/_templates/layout.html b/libs/numpy/doc/_templates/layout.html index d109db5a..d1f54cf1 100644 --- a/libs/numpy/doc/_templates/layout.html +++ b/libs/numpy/doc/_templates/layout.html @@ -1,17 +1,17 @@ {%- macro navbar() %} -