/***************************************************************************
* 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
{
#ifdef PYBIND11_DESCR // The macro is removed from pybind11 since 2.3
template
struct handle_type_name
{
static PYBIND11_DESCR name()
{
return _("numpy.ndarray[") + npy_format_descriptor::name() + _("]");
}
};
#endif
template
struct pyobject_caster
{
using type = xt::pyarray;
bool load(handle src, bool convert)
{
if (!convert)
{
if (!xt::detail::check_array(src))
{
return false;
}
}
value = type::ensure(src);
return static_cast(value);
}
static handle cast(const handle& src, return_value_policy, handle)
{
return src.inc_ref();
}
#ifdef PYBIND11_DESCR // The macro is removed from pybind11 since 2.3
PYBIND11_TYPE_CASTER(type, handle_type_name::name());
#else
PYBIND11_TYPE_CASTER(type, _("numpy.ndarray[") + npy_format_descriptor::name + _("]"));
#endif
};
// 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 operatordimension() == 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);
detail::default_initialize(m_storage);
}
/**
* Allocates a pyarray with nested initializer lists.
*/
template
inline pyarray::pyarray(const value_type& t)
: base_type()
{
base_type::resize(xt::shape(t), layout_type::row_major);
nested_copy(m_storage.begin(), t);
}
template
inline pyarray::pyarray(nested_initializer_list_t t)
: base_type()
{
base_type::resize(xt::shape(t), layout_type::row_major);
nested_copy(m_storage.begin(), t);
}
template
inline pyarray::pyarray(nested_initializer_list_t t)
: base_type()
{
base_type::resize(xt::shape(t), layout_type::row_major);
nested_copy(m_storage.begin(), t);
}
template
inline pyarray::pyarray(nested_initializer_list_t t)
: base_type()
{
base_type::resize(xt::shape(t), layout_type::row_major);
nested_copy(m_storage.begin(), t);
}
template
inline pyarray::pyarray(nested_initializer_list_t t)
: base_type()
{
base_type::resize(xt::shape(t), layout_type::row_major);
nested_copy(m_storage.begin(), t);
}
template
inline pyarray::pyarray(nested_initializer_list_t t)
: base_type()
{
base_type::resize(xt::shape(t), layout_type::row_major);
nested_copy(m_storage.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_storage.begin(), m_storage.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_storage.begin(), m_storage.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);
}
/**
* Allocates and returns an pyarray with the specified shape.
* @param shape the shape of the pyarray
*/
template
template
inline pyarray pyarray::from_shape(S&& shape)
{
auto shp = xtl::forward_sequence(shape);
return self_type(shp);
}
//@}
/**
* @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.storage().cbegin(), rhs.storage().cend(), this->storage().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;
}
auto dtype = pybind11::detail::npy_format_descriptor::dtype();
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_NewFromDescr(&PyArray_Type, (PyArray_Descr*) dtype.release().ptr(), static_cast(shape.size()), shape_data, strides_data,
nullptr, 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())));
if (L != layout_type::dynamic && !do_strides_match(m_shape, m_strides, L))
{
throw std::runtime_error("NumPy: passing container with bad strides for layout (is it a view?).");
}
m_backstrides = backstrides_type(*this);
m_storage = storage_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::storage_impl() noexcept -> storage_type&
{
return m_storage;
}
template
inline auto pyarray::storage_impl() const noexcept -> const storage_type&
{
return m_storage;
}
}
#endif