Ravi wrote: > On Wednesday 21 January 2009 10:22:36 Neal Becker wrote: >> > [http://mail.python.org/pipermail/cplusplus-sig/2008- October/013825.html >> >> Thanks for reminding me about this! >> >> Do you have a current version of the code? I grabbed the files from the >> above message, but I see some additional subsequent messages with more >> patches. > > That is the latest publicly posted code. Since then, there is just one > minor patch (attached) which enables use of row-major (c-contiguous) > arrays. > > This does *not* work with strided arrays which would be a fair bit of > effort to support. Further, you will have to work with the numpy iterator > interface, which, while well-designed, is a great illustration of the > effort required to support OO programming in an non-OO language, and is > pretty tedious to map to the ublas storage iterator interface. If you do > implement it, I would very much like to take a look at it. > > Regards, > Ravi
I'm only interested in simple strided 1-d vectors. In that case, I think your code already works. If you have c++ code using the iterator interface, the iterators dereference will use (*array )[index]. This will use operator[], which will call PyArray_GETPTR. So I think this will obey strides. Unfortunately, it will also be slow. I suggest something like the enclosed. I have done some simple tests, and it seems to work.
// Copyright Ravikiran Rajagopal 2003. // 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 NUMPY_RR2003_H #define NUMPY_RR2003_H // Meta-programming magic to interface ublas and numpy #include <boost/iterator/iterator_facade.hpp> #include <boost/mpl/at.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/int.hpp> #include <boost/mpl/map.hpp> #include <boost/numeric/ublas/matrix.hpp> #include <boost/numeric/ublas/vector.hpp> #include <boost/python/refcount.hpp> #include <boost/type_traits/add_const.hpp> #include <boost/type_traits/add_reference.hpp> #include <boost/type_traits/is_const.hpp> #include <boost/type_traits/is_convertible.hpp> #include <boost/utility/enable_if.hpp> #include <algorithm> #include <complex> #include <exception> #include <memory> #ifndef IN_FILE_IMPORTING_ARRAY #define NO_IMPORT_ARRAY #endif #define PY_ARRAY_UNIQUE_SYMBOL Wicket_Numpy #include <numpy/arrayobject.h> namespace numpy { // Create a type map for numpy supported types typedef boost::mpl::map< boost::mpl::pair< short, boost::mpl::int_<PyArray_SHORT> > , boost::mpl::pair< int, boost::mpl::int_<PyArray_INT> > , boost::mpl::pair< long, boost::mpl::int_<PyArray_LONG> > , boost::mpl::pair< long long, boost::mpl::int_<PyArray_LONGLONG> > , boost::mpl::pair< unsigned short, boost::mpl::int_<PyArray_USHORT> > , boost::mpl::pair< unsigned int, boost::mpl::int_<PyArray_UINT> > , boost::mpl::pair< unsigned long, boost::mpl::int_<PyArray_ULONG> > , boost::mpl::pair< unsigned long long, boost::mpl::int_<PyArray_ULONGLONG> > , boost::mpl::pair< float, boost::mpl::int_<PyArray_FLOAT> > , boost::mpl::pair< double, boost::mpl::int_<PyArray_DOUBLE> > , boost::mpl::pair< long double, boost::mpl::int_<PyArray_LONGDOUBLE> > , boost::mpl::pair< std::complex<float>, boost::mpl::int_<PyArray_CFLOAT> > , boost::mpl::pair< std::complex<double>, boost::mpl::int_<PyArray_CDOUBLE> > , boost::mpl::pair< std::complex<long double>, boost::mpl::int_<PyArray_CLONGDOUBLE> > > numpy_typemap; namespace detail { // Storage shared with a numpy array template<class T> class numpy_storage_array: public boost::numeric::ublas::storage_array<numpy_storage_array<T> > { typedef numpy_storage_array<T> self_type; template < class Value > class simple_iter : public boost::iterator_facade< simple_iter<Value>, Value, boost::random_access_traversal_tag, typename boost::add_reference<Value>::type, npy_intp > { private: struct enabler {}; typedef typename boost::mpl::if_<typename boost::is_const<Value>::type, const numpy_storage_array<T>, numpy_storage_array<T> >::type array_type; public: typedef boost::iterator_facade< simple_iter<Value>, Value, boost::random_access_traversal_tag, typename boost::add_reference<Value>::type, npy_intp > self_t; typedef typename self_t::pointer pointer_t; explicit simple_iter( array_type *a ) : pt (m_stride >= 0 ? (pointer_t)(PyArray_DATA (a->array_)) : ((pointer_t)(PyArray_DATA (a->array_ + a->size())))), m_stride (a->stride()) {} // template <class OtherValue> // simple_iter( simple_iter<OtherValue> const &other, // typename boost::enable_if< // boost::is_convertible< OtherValue*, Value* >, enabler // >::type = enabler() ) // : pt (&array( other.array ), m_stride (other.array->stride()), index( other.index ) {} private: friend class boost::iterator_core_access; template <class> friend class simple_iter; void increment() { pt += m_stride; } void decrement() { pt -= m_stride; } template <class OtherValue> bool equal( simple_iter<OtherValue> const &other ) const { return pt == other.pt; } // Value &dereference() const { return ( *array )[index]; } Value & dereference() const { return *pt; } void advance( npy_intp n ) { pt += n*m_stride; } template <class OtherValue> npy_intp distance_to( simple_iter<OtherValue> const &other ) const { return (other.pt - pt)/m_stride; } private: pointer_t pt; // npy_intp index; npy_intp m_stride; }; public: typedef typename std::allocator<T>::size_type size_type; typedef typename std::allocator<T>::difference_type difference_type; typedef T value_type; typedef const T &const_reference; typedef T &reference; typedef const T *const_pointer; typedef T *pointer; typedef simple_iter<typename boost::add_const<T>::type> const_iterator; typedef simple_iter<T> iterator; private: // No default constructor; must initialize object using a numpy array! explicit BOOST_UBLAS_INLINE numpy_storage_array(); public: // Construction and destruction explicit BOOST_UBLAS_INLINE numpy_storage_array( PyObject *arr ) : boost::numeric::ublas::storage_array<numpy_storage_array<T> >() { if ( !PyArray_Check( arr ) || ( PyArray_TYPE( arr ) != boost::mpl::at<numpy_typemap, T>::type::value ) || ( PyArray_NDIM( arr ) != 1 ) ) throw std::bad_alloc(); array_ = ( PyArrayObject * )arr; // Get a reference to the right sort of array #ifdef NUMPY_STORAGE_ARRAY_MUST_BE_CONTIGUOUS array_ = PyArray_GETCONTIGUOUS( array_ ); #else Py_INCREF( array_ ); #endif } BOOST_UBLAS_INLINE numpy_storage_array(const numpy_storage_array &c) : boost::numeric::ublas::storage_array<numpy_storage_array<T> >(), array_( c.array_ ) { Py_INCREF( array_ ); } BOOST_UBLAS_INLINE ~numpy_storage_array() { Py_DECREF( array_ ); } BOOST_UBLAS_INLINE void resize( size_type size_a ) { if ( size_a != size() ) { npy_intp newsize = size_a; PyArray_Dims newshape = { 1, &newsize }; PyObject *dummy = PyArray_Resize( array_, &newshape, 1, NPY_ANYORDER ); if ( dummy == 0 ) throw std::bad_alloc(); Py_DECREF( dummy ); // Note that all iterators are invalid at this point, i.e., iterators of other // arrays which refer to this underlying storage could also be invalid! } } // Random Access Container BOOST_UBLAS_INLINE size_type size() const { return PyArray_SIZE( array_ ); } BOOST_UBLAS_INLINE size_type max_size() const { return size(); } BOOST_UBLAS_INLINE bool empty() const { return size() == 0; } // Element access BOOST_UBLAS_INLINE const_reference operator[](size_type i) const { BOOST_UBLAS_CHECK( i < size(), boost::numeric::ublas::bad_index() ); return *( static_cast<T*>( PyArray_GETPTR1( array_, i ) ) ); } BOOST_UBLAS_INLINE reference operator[](size_type i) { BOOST_UBLAS_CHECK( i < size(), boost::numeric::ublas::bad_index() ); return *( static_cast<T*>( PyArray_GETPTR1( array_, i ) ) ); } // Assignment BOOST_UBLAS_INLINE numpy_storage_array &operator=( const numpy_storage_array &a ) { if ( this != &a ) { resize( a.size() ); std::copy( a.begin(), a.end(), begin() ); } return *this; } BOOST_UBLAS_INLINE numpy_storage_array &assign_temporary( numpy_storage_array &a ) { swap( a ); return *this; } // Swapping BOOST_UBLAS_INLINE void swap( numpy_storage_array &a ) { if ( this != &a ) { std::swap( array_, a.array_ ); } } BOOST_UBLAS_INLINE friend void swap( numpy_storage_array &a1, numpy_storage_array &a2 ) { a1.swap( a2 ); } BOOST_UBLAS_INLINE const_iterator begin() const { return const_iterator(this); } BOOST_UBLAS_INLINE const_iterator end() const { const_iterator it = begin(); it.advance (size()); return it; } BOOST_UBLAS_INLINE iterator begin() { return iterator( this ); } BOOST_UBLAS_INLINE iterator end() { iterator it = begin(); it.advance (size()); return it; } // Reverse iterators typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; BOOST_UBLAS_INLINE const_reverse_iterator rbegin() const { return const_reverse_iterator( end() ); } BOOST_UBLAS_INLINE const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); } BOOST_UBLAS_INLINE reverse_iterator rbegin() { return reverse_iterator( end() ); } BOOST_UBLAS_INLINE reverse_iterator rend() { return reverse_iterator( begin() ); } // Underlying array object PyArrayObject *get_array() const { return array_; } npy_intp stride () const { return PyArray_STRIDE (array_, 0)/npy_intp(sizeof(T)); } #if 0 private: friend class boost::serialization::access; // Serialization template<class Archive> void serialize(Archive & ar, const unsigned int version) { serialization::collection_size_type s(size_); ar & serialization::make_nvp("size",s); if ( Archive::is_loading::value ) { resize(s); } ar & serialization::make_array(data_, s); } #endif private: PyArrayObject *array_; }; // numpy_storage_array } // namespace detail template <typename data_t> struct array_from_py { typedef typename boost::numeric::ublas::vector< data_t, detail::numpy_storage_array<data_t> > type; }; template <typename data_t> struct const_array_from_py { private: typedef typename array_from_py<data_t>::type o_type; public: typedef typename boost::add_const<o_type>::type type; }; } // namespace numpy #endif
#define IN_FILE_IMPORTING_ARRAY #include "numpy.hpp" #include "numpyregister.hpp" #include <boost/python/def.hpp> #include <boost/python/extract.hpp> #include <boost/python/module.hpp> #include <boost/python/scope.hpp> //#include <boost/python/return_value_policy.hpp> //#include <boost/python/return_by_value.hpp> #include "reference_existing_object_1.hpp" //#include <iostream> //#include <boost/shared_ptr.hpp> #include <boost/python.hpp> #include <complex> #include <boost/range.hpp> namespace ublas = boost::numeric::ublas; namespace bp = boost::python; typedef std::complex<double> complex_t; template<typename out_t, typename in_t> out_t sum (in_t const& in) { out_t out (boost::size (in)); typedef typename boost::range_value<out_t>::type value_t; typename boost::range_const_iterator<in_t>::type i = boost::begin (in); typename boost::range_const_iterator<in_t>::type e = boost::end (in); typename boost::range_iterator<out_t>::type o = boost::begin (out); value_t s = 0; for (; i != e; ++i, ++o) { s += *i; *o = s; } return out; } BOOST_PYTHON_MODULE (test1) { import_array(); import_smart_ptr_deleter_type(); numpy::register_default_ublas_to_python( true ); numpy::register_ublas_from_python_converters(); bp::def ("sum", &sum<ublas::vector<complex_t>,numpy::array_from_py<complex_t>::type>); }
import numpy import test1 import sys u = numpy.array (xrange (4), dtype=complex) print sys.getrefcount (u) v = test1.sum (u) print sys.getrefcount (u), sys.getrefcount (v) w = test1.sum (u[::2]) x = test1.sum (u[::-1]) y = test1.sum (u[::-2])
_______________________________________________ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion