[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/yokaze/python-multi-array/master/python_multi_array.cpp [Back]  [Original]

//
//  python_multi_array.cpp
//  python-multi-array
//
//  Copyright (C) 2017 Rue Yokaze
//  Distributed under the MIT License.
//
#include 
#include 
#include 
#include 
#include 
#include 

//  Using from python is avoided because many definitions conflict with names from std.
namespace python = boost::python;
using boost::extents;
using boost::multi_array;
using std::shared_ptr;
using std::vector;

static python::object main_module = python::import("__main__");
static python::object builtin_module = main_module.attr("__builtins__");
static python::object python_hasattr = builtin_module.attr("hasattr");
static python::object python_int = builtin_module.attr("int");

static python::object numpy = python::import("numpy");
static python::object bool8 = numpy.attr("bool8");
static python::object uint8 = numpy.attr("uint8");
static python::object uint16 = numpy.attr("uint16");
static python::object uint32 = numpy.attr("uint32");
static python::object uint64 = numpy.attr("uint64");
static python::object int8 = numpy.attr("int8");
static python::object int16 = numpy.attr("int16");
static python::object int32 = numpy.attr("int32");
static python::object int64 = numpy.attr("int64");
static python::object float32 = numpy.attr("float32");
static python::object float64 = numpy.attr("float64");

namespace python_multi_array
{
    namespace impl
    {
        size_t extract_size_t(python::object obj)
        {
            //  numpy.int32 cannot be converted directly into C++ integer types.
            //  therefore, the value is first converted to python integer type
            //  and then converted to size_t.
            return python::extract(python_int(obj));
        }

        vector extract_index(python::object index)
        {
            if (python::extract(python_hasattr(index, "__len__")) == false)
            {
                return { extract_size_t(index) };
            }
            else
            {
                vector ret;
                size_t length = python::len(index);
                for (size_t i = 0; i < length; ++i)
                {
                    ret.push_back(extract_size_t(index[i]));
                }
                return ret;
            }
        }
    }

    //
    //  [Python]
    //  [array_type] multi_array.make(shape, dtype)
    //
    //  allocate a boost::multi_array of expected shape and data type.
    //
    //  shape: int, list or tuple
    //  dtype: bool8, int8, int16, int32, int64, uint8, uint16, uint32, uint64,
    //         float32 or float64, all defined in numpy
    //
    //  return: a smart-pointer of the array
    //
    python::object make(python::object shape, python::object dtype);

    namespace impl
    {
        template 
        python::object make_typed_sized(const size_t* s, size_t ndim)
        {
            auto reset = [](const auto& arr)
            {
                std::fill(arr->origin(), arr->origin() + arr->num_elements(), 0);
                return arr;
            };

            switch (ndim)
            {
                case 1:
                    return python::object(reset(std::make_shared(extents[s[0]])));
                case 2:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]])));
                case 3:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]][s[2]])));
                case 4:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]][s[2]][s[3]])));
                case 5:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]][s[2]][s[3]][s[4]])));
                case 6:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]][s[2]][s[3]][s[4]][s[5]])));
                case 7:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]][s[2]][s[3]][s[4]][s[5]][s[6]])));
                case 8:
                    return python::object(reset(std::make_shared(extents[s[0]][s[1]][s[2]][s[3]][s[4]][s[5]][s[6]][s[7]])));
                default:
                    throw std::invalid_argument("shape");
            }
        }

        template 
        python::object make_typed(python::object shape)
        {
            vector shape_vector = extract_index(shape);
            return make_typed_sized(shape_vector.data(), shape_vector.size());
        }
    }

    python::object make(python::object shape, python::object dtype)
    {
        if (dtype == bool8)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == int8)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == int16)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == int32)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == int64)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == uint8)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == uint16)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == uint32)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == uint64)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == float32)
        {
            return impl::make_typed(shape);
        }
        else if (dtype == float64)
        {
            return impl::make_typed(shape);
        }
        else
        {
            throw std::invalid_argument("dtype");
        }
    }

    //
    //  [Python]
    //  T x[index]
    //  x[index] = T
    //
    //  get and set one element via index operator.
    //  Example:
    //    x[2, 4] = 2.0
    //
    template 
    T getitem(const shared_ptr& This, python::object index_object)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        vector index = impl::extract_index(index_object);
        if (index.size() != N)
        {
            //  index has an invalid dimensionality
            throw std::invalid_argument("index");
        }
        T* ptr = This->origin();
        for (size_t i = 0; i < N; ++i)
        {
            if (This->shape()[i] strides()[i] * index[i];
        }
        return *ptr;
    }

    template 
    void setitem(const shared_ptr& This, python::object index_object, T value)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        vector index = impl::extract_index(index_object);
        if (index.size() != N)
        {
            //  index has an invalid dimensionality
            throw std::invalid_argument("index");
        }
        T* ptr = This->origin();
        for (size_t i = 0; i < N; ++i)
        {
            if (This->shape()[i] strides()[i] * index[i];
        }
        *ptr = value;
    }

    //
    //  [Python]
    //  x.reset()
    //
    //  This function resets every elements of the array with zero.
    //
    template 
    void reset(const shared_ptr& This)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        std::fill(This->origin(), This->origin() + This->num_elements(), 0);
    }

    //
    //  [Python]
    //  dtype x.element()
    //
    //  return: data type of the array. possible values are bool8, uint8,
    //          uint16, uint32, uint64, int8, int16, int32, int64, float32,
    //          float64, all defined in numpy.
    //
    template 
    python::object element(const shared_ptr& This)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        return python::numpy::dtype::get_builtin();
    }

    //
    //  [Python]
    //  tuple x.shape()
    //
    //  return: the shape of the array.
    //
    template 
    python::object shape(const shared_ptr& This)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        const size_t* s = This->shape();
        switch (N)
        {
            case 1:
                return python::make_tuple(s[0]);
            case 2:
                return python::make_tuple(s[0], s[1]);
            case 3:
                return python::make_tuple(s[0], s[1], s[2]);
            case 4:
                return python::make_tuple(s[0], s[1], s[2], s[3]);
            case 5:
                return python::make_tuple(s[0], s[1], s[2], s[3], s[4]);
            case 6:
                return python::make_tuple(s[0], s[1], s[2], s[3], s[4], s[5]);
            case 7:
                return python::make_tuple(s[0], s[1], s[2], s[3], s[4], s[5], s[6]);
            case 8:
                return python::make_tuple(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]);
            default:
                throw std::invalid_argument("self");
        }
    }

    //
    //  [Python]
    //  int x.num_dimensions()
    //
    //  return: the number of dimensions of the array.
    //
    template 
    size_t num_dimensions(const shared_ptr& This)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        return N;
    }

    //
    //  [Python]
    //  int x.num_elements()
    //
    //  return: the total number of elements of the array.
    //  example:
    //    It returns 8 for an array with shape (2, 4).
    //
    template 
    size_t num_elements(const shared_ptr& This)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        return This->num_elements();
    }

    //
    //  [Python]
    //  numpy.ndarray x.get()
    //
    //  return: a copy of the array stored in numpy.ndarray.
    //
    template 
    python::object get(const shared_ptr& This)
    {
        if (This == nullptr)
        {
            throw std::invalid_argument("self");
        }
        size_t s[N];
        size_t d[N];
        std::copy(This->shape(), This->shape() + N, s);
        std::transform(This->strides(), This->strides() + N, d, [](auto input) { return input * sizeof(T); });
        auto make_tuple_from_array = [](const size_t* a) {
            switch (N)
            {
                case 1:
                    return python::make_tuple(a[0]);
                case 2:
                    return python::make_tuple(a[0], a[1]);
                case 3:
                    return python::make_tuple(a[0], a[1], a[2]);
                case 4:
                    return python::make_tuple(a[0], a[1], a[2], a[3]);
                case 5:
                    return python::make_tuple(a[0], a[1], a[2], a[3], a[4]);
                case 6:
                    return python::make_tuple(a[0], a[1], a[2], a[3], a[4], a[5]);
                case 7:
                    return python::make_tuple(a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
                case 8:
                    return python::make_tuple(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
            }
            throw std::invalid_argument("this");
        };
        python::numpy::dtype dt = python::numpy::dtype::get_builtin();
        python::tuple shape = make_tuple_from_array(s);
        python::tuple strides = make_tuple_from_array(d);
        return boost::python::numpy::from_data(This->origin(), dt, shape, strides, boost::python::object());
    }

    //
    //  [Python]
    //  x.set(numpy.ndarray nd)
    //
    //  Reset the array with values from nd.
    //  nd.dtype may be different from x.element() but the values are implicitly
    //  converted to x.element().
    //
    template 
    void set(const shared_ptr& This, python::numpy::ndarray nd);

    namespace impl
    {
        template 
        void set_typed(shared_ptr This, python::numpy::ndarray nd)
        {
            if (N != nd.get_nd())
            {
                throw std::invalid_argument("nd");
            }
            size_t s[N];
            std::copy(This->shape(), This->shape() + N, s);
            size_t ix[N];
            std::fill(ix, ix + N, 0);

            size_t boost_strides[N];
            std::copy(This->strides(), This->strides() + N, boost_strides);

            size_t numpy_strides[N];
            std::transform(nd.get_strides(), nd.get_strides() + N, numpy_strides, [](auto input) { return input / sizeof(S); });

            T* p_boost_origin = This->origin();
            const S* p_numpy_origin = reinterpret_cast(nd.get_data());

            while (ix[0] < s[0])
            {
                T* p_boost_element = p_boost_origin;
                const S* p_numpy_element = p_numpy_origin;
                for (size_t d = 0; d < (N - 1); ++d)
                {
                    p_boost_element += ix[d] * boost_strides[d];
                    p_numpy_element += ix[d] * numpy_strides[d];
                }
                while (ix[N - 1] < s[N - 1])
                {
                    *p_boost_element = static_cast(*p_numpy_element);
                    p_boost_element += boost_strides[N - 1];
                    p_numpy_element += numpy_strides[N - 1];
                    ++(ix[N - 1]);
                }
                for (size_t d = N - 1; d > 0; --d)
                {
                    if (s[d] 

Web Proxy Viewer  |  New URL  |  Original Page