GeezerVison is showing raw.githubusercontent.com — raw.githubusercontent.com /***************************************************************************
* Copyright (c) 2016, Johan Mabille and Sylvain Corlay *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/
#ifndef PY_ARRAY_HPP
#define PY_ARRAY_HPP
#include
#include
#include
#include "xtensor/xbuffer_adaptor.hpp"
#include "xtensor/xiterator.hpp"
#include "xtensor/xsemantic.hpp"
#include "pycontainer.hpp"
#include "pystrides_adaptor.hpp"
#include "xtensor_type_caster_base.hpp"
namespace xt
{
template
class pyarray;
}
namespace pybind11
{
namespace detail
{
template
struct handle_type_name>
{
static PYBIND11_DESCR name()
{
return _("numpy.ndarray[") + make_caster::name() + _("]");
}
};
template
struct pyobject_caster>
{
using type = xt::pyarray;
// we need to do special checks for unsigned long long and long long
inline int get_py_type_num(handle src)
{
int type_num = PyArray_TYPE(reinterpret_cast(src.ptr()));
if (type_num == NPY_LONGLONG)
{
return xt::detail::numpy_traits::type_num;
}
if (type_num == NPY_ULONGLONG)
{
return xt::detail::numpy_traits::type_num;
}
return type_num;
}
bool load(handle src, bool convert)
{
if (!convert)
{
if (!PyArray_Check(src.ptr()))
{
return false;
}
int type_num = xt::detail::numpy_traits::type_num;
int py_type_num = get_py_type_num(src);
if (py_type_num != type_num)
{
return false;
}
}
value = type::ensure(src);
return static_cast(value);
}
static handle cast(const handle& src, return_value_policy, handle)
{
return src.inc_ref();
}
PYBIND11_TYPE_CASTER(type, handle_type_name::name());
};
// Type caster for casting ndarray to xexpression
template
struct type_caster>> : pyobject_caster>
{
using Type = xt::xexpression>;
operator Type&()
{
return this->value;
}
operator const Type&()
{
return this->value;
}
};
// Type caster for casting xarray to ndarray
template
struct type_caster> : xtensor_type_caster_base>
{
};
}
}
namespace xt
{
/**************************
* pybackstrides_iterator *
**************************/
template
class pybackstrides_iterator
{
public:
using self_type = pybackstrides_iterator;
using value_type = typename B::value_type;
using pointer = const value_type*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
inline pybackstrides_iterator(const B* b, std::size_t offset)
: p_b(b), m_offset(offset)
{
}
inline reference operator*() const
{
return p_b->operator[](m_offset);
}
inline pointer operator->() const
{
// Returning the address of a temporary
value_type res = p_b->operator[](m_offset);
return &res;
}
inline reference operator[](difference_type n) const
{
return p_b->operator[](m_offset + n);
}
inline self_type& operator++()
{
++m_offset;
return *this;
}
inline self_type& operator--()
{
--m_offset;
return *this;
}
inline self_type operator++(int)
{
self_type tmp(*this);
++m_offset;
return tmp;
}
inline self_type operator--(int)
{
self_type tmp(*this);
--m_offset;
return tmp;
}
inline self_type& operator+=(difference_type n)
{
m_offset += n;
return *this;
}
inline self_type& operator-=(difference_type n)
{
m_offset -= n;
return *this;
}
inline self_type operator+(difference_type n) const
{
return self_type(p_b, m_offset + n);
}
inline self_type operator-(difference_type n) const
{
return self_type(p_b, m_offset - n);
}
inline self_type operator-(const self_type& rhs) const
{
self_type tmp(*this);
tmp -= (m_offset - rhs.m_offset);
return tmp;
}
inline std::size_t offset() const
{
return m_offset;
}
private:
const B* p_b;
std::size_t m_offset;
};
template
inline bool operator==(const pybackstrides_iterator& lhs,
const pybackstrides_iterator& rhs)
{
return lhs.offset() == rhs.offset();
}
template
inline bool operator!=(const pybackstrides_iterator& lhs,
const pybackstrides_iterator& rhs)
{
return !(lhs == rhs);
}
template
inline bool operator<(const pybackstrides_iterator& lhs,
const pybackstrides_iterator& rhs)
{
return lhs.offset() < rhs.offset();
}
template
inline bool operator<=(const pybackstrides_iterator& lhs,
const pybackstrides_iterator& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
template
inline bool operator>(const pybackstrides_iterator& lhs,
const pybackstrides_iterator& rhs)
{
return !(lhs <= rhs);
}
template
inline bool operator>=(const pybackstrides_iterator& lhs,
const pybackstrides_iterator& rhs)
{
return !(lhs < rhs);
}
template
class pyarray_backstrides
{
public:
using self_type = pyarray_backstrides; using array_type = A; using value_type = typename array_type::size_type; using const_reference = value_type; using const_pointer = const value_type*; using size_type = typename array_type::size_type; using difference_type = typename array_type::difference_type; using const_iterator = pybackstrides_iterator; pyarray_backstrides() = default; pyarray_backstrides(const array_type& a); bool empty() const; size_type size() const; value_type operator[](size_type i) const; const_reference front() const; const_reference back() const; const_iterator begin() const; const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; private: const array_type* p_a; }; template struct xiterable_inner_types> : xcontainer_iterable_types> { }; template struct xcontainer_inner_types> { using container_type = xbuffer_adaptor; using shape_type = std::vector; using strides_type = shape_type; using backstrides_type = pyarray_backstrides>; using inner_shape_type = xbuffer_adaptor; using inner_strides_type = pystrides_adaptor; using inner_backstrides_type = backstrides_type; using temporary_type = pyarray; static constexpr layout_type layout = layout_type::dynamic; }; /** * @class pyarray * @brief Multidimensional container providing the xtensor container semantics to a numpy array. * * pyarray is similar to the xarray container in that it has a dynamic dimensionality. Reshapes of * a pyarray container are reflected in the underlying numpy array. * * @tparam T The type of the element stored in the pyarray. * @sa pytensor */ template class pyarray : public pycontainer>, public xcontainer_semantic> { public: using self_type = pyarray; using semantic_base = xcontainer_semantic; using base_type = pycontainer; using container_type = typename base_type::container_type; using value_type = typename base_type::value_type; using reference = typename base_type::reference; using const_reference = typename base_type::const_reference; using pointer = typename base_type::pointer; using size_type = typename base_type::size_type; using shape_type = typename base_type::shape_type; using strides_type = typename base_type::strides_type; using backstrides_type = typename base_type::backstrides_type; using inner_shape_type = typename base_type::inner_shape_type; using inner_strides_type = typename base_type::inner_strides_type; using inner_backstrides_type = typename base_type::inner_backstrides_type; pyarray(); pyarray(const value_type& t); pyarray(nested_initializer_list_t t); pyarray(nested_initializer_list_t t); pyarray(nested_initializer_list_t t); pyarray(nested_initializer_list_t t); pyarray(nested_initializer_list_t t); pyarray(pybind11::handle h, pybind11::object::borrowed_t); pyarray(pybind11::handle h, pybind11::object::stolen_t); pyarray(const pybind11::object& o); explicit pyarray(const shape_type& shape, layout_type l = layout_type::row_major); explicit pyarray(const shape_type& shape, const_reference value, layout_type l = layout_type::row_major); explicit pyarray(const shape_type& shape, const strides_type& strides, const_reference value); explicit pyarray(const shape_type& shape, const strides_type& strides); pyarray(const self_type& rhs); self_type& operator=(const self_type& rhs); pyarray(self_type&&) = default; self_type& operator=(self_type&& e) = default; template pyarray(const xexpression& e); template self_type& operator=(const xexpression& e); using base_type::begin; using base_type::end; static self_type ensure(pybind11::handle h); static bool check_(pybind11::handle h); private: inner_shape_type m_shape; inner_strides_type m_strides; mutable inner_backstrides_type m_backstrides; container_type m_data; void init_array(const shape_type& shape, const strides_type& strides); void init_from_python(); const inner_shape_type& shape_impl() const noexcept; const inner_strides_type& strides_impl() const noexcept; const inner_backstrides_type& backstrides_impl() const noexcept; container_type& data_impl() noexcept; const container_type& data_impl() const noexcept; friend class xcontainer>; }; /************************************** * pyarray_backstrides implementation * **************************************/ template inline pyarray_backstrides::pyarray_backstrides(const array_type& a) : p_a(&a) { } template inline bool pyarray_backstrides::empty() const { return p_a->dimension() == 0; } template inline auto pyarray_backstrides::size() const -> size_type { return p_a->dimension(); } template inline auto pyarray_backstrides::operator[](size_type i) const -> value_type { value_type sh = p_a->shape()[i]; value_type res = sh == 1 ? 0 : (sh - 1) * p_a->strides()[i]; return res; } template inline auto pyarray_backstrides::front() const -> const_reference { value_type sh = p_a->shape()[0]; value_type res = sh == 1 ? 0 : (sh - 1) * p_a->strides()[0]; return res; } template inline auto pyarray_backstrides::back() const -> const_reference { auto index = p_a->size() - 1; value_type sh = p_a->shape()[index]; value_type res = sh == 1 ? 0 : (sh - 1) * p_a->strides()[index]; return res; } template inline auto pyarray_backstrides::begin() const -> const_iterator { return cbegin(); } template inline auto pyarray_backstrides::end() const -> const_iterator { return cend(); } template inline auto pyarray_backstrides::cbegin() const -> const_iterator { return const_iterator(this, 0); } template inline auto pyarray_backstrides::cend() const -> const_iterator { return const_iterator(this, size()); } /************************** * pyarray implementation * **************************/ /** * @name Constructors */ //@{ template inline pyarray::pyarray() : base_type() { // TODO: avoid allocation shape_type shape = xtl::make_sequence(0, size_type(1)); strides_type strides = xtl::make_sequence(0, size_type(0)); init_array(shape, strides); m_data[0] = T(); } /** * Allocates a pyarray with nested initializer lists. */ template inline pyarray::pyarray(const value_type& t) : base_type() { base_type::reshape(xt::shape(t), layout_type::row_major); nested_copy(m_data.begin(), t); } template inline pyarray::pyarray(nested_initializer_list_t t) : base_type() { base_type::reshape(xt::shape(t), layout_type::row_major); nested_copy(m_data.begin(), t); } template inline pyarray::pyarray(nested_initializer_list_t t) : base_type() { base_type::reshape(xt::shape(t), layout_type::row_major); nested_copy(m_data.begin(), t); } template inline pyarray::pyarray(nested_initializer_list_t t) : base_type() { base_type::reshape(xt::shape(t), layout_type::row_major); nested_copy(m_data.begin(), t); } template inline pyarray::pyarray(nested_initializer_list_t t) : base_type() { base_type::reshape(xt::shape(t), layout_type::row_major); nested_copy(m_data.begin(), t); } template inline pyarray::pyarray(nested_initializer_list_t t) : base_type() { base_type::reshape(xt::shape(t), layout_type::row_major); nested_copy(m_data.begin(), t); } template inline pyarray::pyarray(pybind11::handle h, pybind11::object::borrowed_t b) : base_type(h, b) { init_from_python(); } template inline pyarray::pyarray(pybind11::handle h, pybind11::object::stolen_t s) : base_type(h, s) { init_from_python(); } template inline pyarray::pyarray(const pybind11::object& o) : base_type(o) { init_from_python(); } /** * Allocates an uninitialized pyarray with the specified shape and * layout. * @param shape the shape of the pyarray * @param l the layout of the pyarray */ template inline pyarray::pyarray(const shape_type& shape, layout_type l) : base_type() { strides_type strides(shape.size()); compute_strides(shape, l, strides); init_array(shape, strides); } /** * Allocates a pyarray with the specified shape and layout. Elements * are initialized to the specified value. * @param shape the shape of the pyarray * @param value the value of the elements * @param l the layout of the pyarray */ template inline pyarray::pyarray(const shape_type& shape, const_reference value, layout_type l) : base_type() { strides_type strides(shape.size()); compute_strides(shape, l, strides); init_array(shape, strides); std::fill(m_data.begin(), m_data.end(), value); } /** * Allocates an uninitialized pyarray with the specified shape and strides. * Elements are initialized to the specified value. * @param shape the shape of the pyarray * @param strides the strides of the pyarray * @param value the value of the elements */ template inline pyarray::pyarray(const shape_type& shape, const strides_type& strides, const_reference value) : base_type() { init_array(shape, strides); std::fill(m_data.begin(), m_data.end(), value); } /** * Allocates an uninitialized pyarray with the specified shape and strides. * @param shape the shape of the pyarray * @param strides the strides of the pyarray */ template inline pyarray::pyarray(const shape_type& shape, const strides_type& strides) : base_type() { init_array(shape, strides); } //@} /** * @name Copy semantic */ //@{ /** * The copy constructor. */ template inline pyarray::pyarray(const self_type& rhs) : base_type(), semantic_base(rhs) { auto tmp = pybind11::reinterpret_steal( PyArray_NewLikeArray(rhs.python_array(), NPY_KEEPORDER, nullptr, 1)); if (!tmp) { throw std::runtime_error("NumPy: unable to create ndarray"); } this->m_ptr = tmp.release().ptr(); init_from_python(); std::copy(rhs.data().cbegin(), rhs.data().cend(), this->data().begin()); } /** * The assignment operator. */ template inline auto pyarray::operator=(const self_type& rhs) -> self_type& { self_type tmp(rhs); *this = std::move(tmp); return *this; } //@} /** * @name Extended copy semantic */ //@{ /** * The extended copy constructor. */ template template inline pyarray::pyarray(const xexpression& e) : base_type() { // TODO: prevent intermediary shape allocation shape_type shape = xtl::forward_sequence(e.derived_cast().shape()); strides_type strides = xtl::make_sequence(shape.size(), size_type(0)); compute_strides(shape, layout_type::row_major, strides); init_array(shape, strides); semantic_base::assign(e); } /** * The extended assignment operator. */ template template inline auto pyarray::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} template inline auto pyarray::ensure(pybind11::handle h) -> self_type { return base_type::ensure(h); } template inline bool pyarray::check_(pybind11::handle h) { return base_type::check_(h); } template inline void pyarray::init_array(const shape_type& shape, const strides_type& strides) { strides_type adapted_strides(strides); std::transform(strides.begin(), strides.end(), adapted_strides.begin(), [](auto v) { return sizeof(T) * v; }); int flags = NPY_ARRAY_ALIGNED; if (!std::is_const::value) { flags |= NPY_ARRAY_WRITEABLE; } int type_num = detail::numpy_traits::type_num; npy_intp* shape_data = reinterpret_cast(const_cast(shape.data())); npy_intp* strides_data = reinterpret_cast(adapted_strides.data()); auto tmp = pybind11::reinterpret_steal( PyArray_New(&PyArray_Type, static_cast(shape.size()), shape_data, type_num, strides_data, nullptr, static_cast(sizeof(T)), flags, nullptr)); if (!tmp) { throw std::runtime_error("NumPy: unable to create ndarray"); } this->m_ptr = tmp.release().ptr(); init_from_python(); } template inline void pyarray::init_from_python() { m_shape = inner_shape_type(reinterpret_cast(PyArray_SHAPE(this->python_array())), static_cast(PyArray_NDIM(this->python_array()))); m_strides = inner_strides_type(reinterpret_cast(PyArray_STRIDES(this->python_array())), static_cast(PyArray_NDIM(this->python_array()))); m_backstrides = backstrides_type(*this); m_data = container_type(reinterpret_cast(PyArray_DATA(this->python_array())), this->get_min_stride() * static_cast(PyArray_SIZE(this->python_array()))); } template inline auto pyarray::shape_impl() const noexcept -> const inner_shape_type& { return m_shape; } template inline auto pyarray::strides_impl() const noexcept -> const inner_strides_type& { return m_strides; } template inline auto pyarray::backstrides_impl() const noexcept -> const inner_backstrides_type& { // m_backstrides wraps the numpy array backstrides, which is a raw pointer. // The address of the raw pointer stored in the wrapper would be invalidated when the pyarray is copied. // Hence, we build a new backstrides object (cheap wrapper around the underlying pointer) upon access. m_backstrides = backstrides_type(*this); return m_backstrides; } template inline auto pyarray::data_impl() noexcept -> container_type& { return m_data; } template inline auto pyarray::data_impl() const noexcept -> const container_type& { return m_data; } } #endif |