[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/bjodah/xtensor-python/master/include/xtensor-python/pyarray.hpp [Back]  [Original]

/***************************************************************************
* 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());
    }

    template 
    inline auto pyarray_backstrides::rbegin() const -> const_reverse_iterator
    {
        return crbegin();
    }

    template 
    inline auto pyarray_backstrides::rend() const -> const_reverse_iterator
    {
        return crend();
    }

    template 
    inline auto pyarray_backstrides::crbegin() const -> const_reverse_iterator
    {
        return const_reverse_iterator(end());
    }

    template 
    inline auto pyarray_backstrides::crend() const -> const_reverse_iterator
    {
        return const_reverse_iterator(begin());
    }

    /**************************
     * 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()
    {
        if (!static_cast(*this))
        {
            return;
        }
        
        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, 1))
        {
            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_buffer_size());
    }

    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

Web Proxy Viewer  |  New URL  |  Original Page