2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-23 05:42:30 +00:00
This commit is contained in:
Stefan Seefeld
2016-10-07 20:03:12 -04:00
64 changed files with 5131 additions and 35 deletions

View File

@@ -0,0 +1,38 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#ifndef BOOST_NUMPY_HPP_INCLUDED
#define BOOST_NUMPY_HPP_INCLUDED
/**
* @file boost/numpy.hpp
* @brief Main public header file for boost.numpy.
*/
#include <boost/numpy/dtype.hpp>
#include <boost/numpy/ndarray.hpp>
#include <boost/numpy/scalars.hpp>
#include <boost/numpy/matrix.hpp>
#include <boost/numpy/ufunc.hpp>
#include <boost/numpy/invoke_matching.hpp>
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()", and then calls
* dtype::register_scalar_converters().
*/
void initialize(bool register_scalar_converters=true);
} // namespace boost::numpy
} // namespace boost
#endif // !BOOST_NUMPY_HPP_INCLUDED

View File

@@ -0,0 +1,115 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#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 <boost/python.hpp>
#include <boost/numpy/numpy_object_mgr_traits.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/type_traits/add_pointer.hpp>
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 <typename T>
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<T>) == 2*sizeof(T).
*
* It can also be useful for users to add explicit specializations for POD structs
* that return field-based dtypes.
*/
template <typename T> static dtype get_builtin();
/// @brief Return the size of the data type in bytes.
int get_itemsize() const;
/**
* @brief Compare two dtypes for equivalence.
*
* This is more permissive than equality tests. For instance, if long and int are the same
* size, the dtypes corresponding to each will be equivalent, but not equal.
*/
friend bool equivalent(dtype const & a, dtype const & b);
/**
* @brief Register from-Python converters for NumPy's built-in array scalar types.
*
* This is usually called automatically by initialize(), and shouldn't be called twice
* (doing so just adds unused converters to the Boost.Python registry).
*/
static void register_scalar_converters();
BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, python::object);
};
bool equivalent(dtype const & a, dtype const & b);
namespace detail
{
template <int bits, bool isUnsigned> dtype get_int_dtype();
template <int bits> dtype get_float_dtype();
template <int bits> dtype get_complex_dtype();
template <typename T, bool isInt=boost::is_integral<T>::value>
struct builtin_dtype;
template <typename T>
struct builtin_dtype<T,true> {
static dtype get() { return get_int_dtype< 8*sizeof(T), boost::is_unsigned<T>::value >(); }
};
template <>
struct builtin_dtype<bool,true> {
static dtype get();
};
template <typename T>
struct builtin_dtype<T,false> {
static dtype get() { return get_float_dtype< 8*sizeof(T) >(); }
};
template <typename T>
struct builtin_dtype< std::complex<T>, false > {
static dtype get() { return get_complex_dtype< 16*sizeof(T) >(); }
};
} // namespace detail
template <typename T>
inline dtype dtype::get_builtin() { return detail::builtin_dtype<T>::get(); }
}} // namespace boost::numpy
namespace boost { namespace python { namespace converter {
NUMPY_OBJECT_MANAGER_TRAITS(numpy::dtype);
}}} // namespace boost::python::converter
#endif // !BOOST_NUMPY_DTYPE_HPP_INCLUDED

View File

@@ -0,0 +1,33 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#ifndef BOOST_NUMPY_INTERNAL_HPP_INCLUDED
#define BOOST_NUMPY_INTERNAL_HPP_INCLUDED
/**
* @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.numpy library itself.
*/
#include <boost/python.hpp>
#ifdef BOOST_NUMPY_INTERNAL
#define NO_IMPORT_ARRAY
#define NO_IMPORT_UFUNC
#else
#ifndef BOOST_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 <numpy/arrayobject.h>
#include <numpy/ufuncobject.h>
#include <boost/numpy.hpp>
#define NUMPY_OBJECT_MANAGER_TRAITS_IMPL(pytype,manager) \
PyTypeObject const * object_manager_traits<manager>::get_pytype() { return &pytype; }
#endif // !BOOST_NUMPY_INTERNAL_HPP_INCLUDED

View File

@@ -0,0 +1,190 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#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 <boost/numpy/dtype.hpp>
#include <boost/numpy/ndarray.hpp>
#include <boost/mpl/integral_c.hpp>
namespace boost
{
namespace numpy
{
namespace detail
{
struct add_pointer_meta
{
template <typename T>
struct apply
{
typedef typename boost::add_pointer<T>::type type;
};
};
struct dtype_template_match_found {};
struct nd_template_match_found {};
template <typename Function>
struct dtype_template_invoker
{
template <typename T>
void operator()(T *) const
{
if (dtype::get_builtin<T>() == m_dtype)
{
m_func.Function::template apply<T>();
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 <typename Function>
struct dtype_template_invoker< boost::reference_wrapper<Function> >
{
template <typename T>
void operator()(T *) const
{
if (dtype::get_builtin<T>() == m_dtype)
{
m_func.Function::template apply<T>();
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 <typename Function>
struct nd_template_invoker
{
template <int N>
void operator()(boost::mpl::integral_c<int,N> *) const
{
if (m_nd == N)
{
m_func.Function::template apply<N>();
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 <typename Function>
struct nd_template_invoker< boost::reference_wrapper<Function> >
{
template <int N>
void operator()(boost::mpl::integral_c<int,N> *) const
{
if (m_nd == N)
{
m_func.Function::template apply<N>();
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 <typename Sequence, typename Function>
void invoke_matching_nd(int nd, Function f)
{
detail::nd_template_invoker<Function> 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 <typename Sequence, typename Function>
void invoke_matching_dtype(dtype const & dtype_, Function f)
{
detail::dtype_template_invoker<Function> 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 <typename T, typename Function>
struct array_template_invoker_wrapper_2
{
template <int N>
void apply() const { m_func.Function::template apply<T,N>();}
array_template_invoker_wrapper_2(Function & func) : m_func(func) {}
private:
Function & m_func;
};
template <typename DimSequence, typename Function>
struct array_template_invoker_wrapper_1
{
template <typename T>
void apply() const { invoke_matching_nd<DimSequence>(m_nd, array_template_invoker_wrapper_2<T,Function>(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 <typename DimSequence, typename Function>
struct array_template_invoker_wrapper_1< DimSequence, boost::reference_wrapper<Function> >
: 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 <typename TypeSequence, typename DimSequence, typename Function>
void invoke_matching_array(ndarray const & array_, Function f)
{
detail::array_template_invoker_wrapper_1<DimSequence,Function> wrapper(array_.get_nd(), f);
invoke_matching_dtype<TypeSequence>(array_.get_dtype(), wrapper);
}
} // namespace boost::numpy
} // namespace boost
#endif // !BOOST_NUMPY_INVOKE_MATCHING_HPP_INCLUDED

View File

@@ -0,0 +1,85 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#ifndef BOOST_NUMPY_MATRIX_HPP_INCLUDED
#define BOOST_NUMPY_MATRIX_HPP_INCLUDED
/**
* @file boost/numpy/matrix.hpp
* @brief Object manager for numpy.matrix.
*/
#include <boost/python.hpp>
#include <boost/numpy/numpy_object_mgr_traits.hpp>
#include <boost/numpy/ndarray.hpp>
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<matrix>::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<ndarray>(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<ndarray>(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;
};
/**
* @brief CallPolicies that causes a function that returns a numpy.ndarray to
* return a numpy.matrix instead.
*/
template <typename Base = python::default_call_policies>
struct as_matrix : Base {
static PyObject * postcall(PyObject *, PyObject * result) {
python::object a = python::object(python::handle<>(result));
numpy::matrix m(a, false);
Py_INCREF(m.ptr());
return m.ptr();
}
};
} // 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

View File

@@ -0,0 +1,303 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#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 <boost/python.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/numpy/numpy_object_mgr_traits.hpp>
#include <boost/numpy/dtype.hpp>
#include <vector>
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<array_struct*>(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 array, cast to a specified type.
ndarray astype(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.
Py_intptr_t shape(int n) const { return get_shape()[n]; }
/// @brief Return the stride of the nth dimension.
Py_intptr_t 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 get_nd() const { return get_struct()->nd; }
/// @brief Return the array flags.
bitflag 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<Py_intptr_t> const & shape,
std::vector<Py_intptr_t> const & strides,
python::object const & owner,
bool writeable);
template <typename Container>
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<typename Container::value_type> >::type * enabled = NULL)
{
std::vector<Py_intptr_t> shape_(shape.begin(),shape.end());
std::vector<Py_intptr_t> 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 <typename Container>
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 <typename Container>
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<void*>(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

View File

@@ -0,0 +1,35 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#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<manager> \
{ \
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<manager>((checked_downcast_impl)(x, (PyTypeObject*)get_pytype())); \
} \
static PyTypeObject const * get_pytype(); \
}
#endif // !BOOST_NUMPY_NUMPY_OBJECT_MGR_TRAITS_HPP_INCLUDED

View File

@@ -0,0 +1,64 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#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 <boost/python.hpp>
#include <boost/numpy/numpy_object_mgr_traits.hpp>
#include <boost/numpy/dtype.hpp>
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

View File

@@ -0,0 +1,213 @@
// Copyright Jim Bosch 2010-2012.
// 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)
#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 <boost/python.hpp>
#include <boost/numpy/numpy_object_mgr_traits.hpp>
#include <boost/numpy/dtype.hpp>
#include <boost/numpy/ndarray.hpp>
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 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 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<TimesPI>::make());
* }
* @endcode
*
*/
template <typename TUnaryFunctor,
typename TArgument=typename TUnaryFunctor::argument_type,
typename TResult=typename TUnaryFunctor::result_type>
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<TArgument>();
dtype out_dtype = dtype::get_builtin<TResult>();
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<TArgument*>(iter.get_data(0));
TResult * result = reinterpret_cast<TResult*>(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<CosSum>::make());
* }
* @endcode
*
*/
template <typename TBinaryFunctor,
typename TArgument1=typename TBinaryFunctor::first_argument_type,
typename TArgument2=typename TBinaryFunctor::second_argument_type,
typename TResult=typename TBinaryFunctor::result_type>
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<TArgument1>();
dtype in2_dtype = dtype::get_builtin<TArgument2>();
dtype out_dtype = dtype::get_builtin<TResult>();
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<TArgument1*>(iter.get_data(0));
TArgument2 * argument2 = reinterpret_cast<TArgument2*>(iter.get_data(1));
TResult * result = reinterpret_cast<TResult*>(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