.. _program_listing_file_src_array2.hpp: Program Listing for File array2.hpp =================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/array2.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* This file is part of brille. Copyright © 2020 Greg Tucker brille is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. brille is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with brille. If not, see . */ #ifndef BRILLE_ARRAY2_HPP #define BRILLE_ARRAY2_HPP #include #include #include #include #include #include #include #include #include #include #include "subscript.hpp" #include "utilities.hpp" #include "comparisons.hpp" #include "approx.hpp" #include "types.hpp" #include "array_.hpp" #include "array.hpp" namespace brille { template class Array2{ public: using ref_t = std::shared_ptr; using shape_t = std::array; using bItr = BroadcastIt2; using sItr = SubIt2; using aItr = Array2It; protected: T* _data; ind_t _num; ind_t _shift; bool _own; ref_t _ref; bool _mutable; shape_t _shape; shape_t _stride; public: // accessors T* data() {return _data;} T* data(const ind_t& idx){ return _data + this->l2l_d(idx);} T* data(const ind_t& i0, const ind_t& i1){ return _data + this->s2l_d(i0,i1);} T* data(const shape_t& idx){ return _data + this->s2l_d(idx);} const T* data() const {return _data;} const T* data(const ind_t& idx) const {return _data + this->l2l_d(idx);} const T* data(const ind_t& i0, const ind_t& i1) const { return _data + this->s2l_d(i0,i1);} const T* data(const shape_t& idx) const {return _data + this->s2l_d(idx);} ind_t raw_size() const {return _num;} ind_t raw_shift() const {return _shift;} bool own() const {return _own;} ref_t ref() const {return _ref;} bool ismutable(void) const {return _mutable;} bool isconst(void) const {return !_mutable;} ind_t ndim(void) const {return 2;} ind_t numel(void) const { return _shape[0]*_shape[1]; } ind_t size(const ind_t dim) const { assert(dim < 2); return _shape[dim]; } shape_t shape(void) const {return _shape;} shape_t stride(void) const {return _stride;} shape_t cstride() const { shape_t cs(_stride); for (auto& s: cs) s *= sizeof(T); return cs; } sItr subItr() const { return sItr(_shape); } sItr subItr(const shape_t& fix) const { return sItr(_shape, fix); } aItr valItr() const { return aItr(*this); } bItr broadcastItr(const shape_t& other) const { return bItr(_shape, other); } template bItr broadcastItr(const Array2& other) const {return bItr(_shape, other.shape());} bool is_column_ordered() const { // stride {1,2,4,8} is column ordered return _stride[0] <= _stride[1]; } bool is_row_ordered() const { // stride {8,4,2,1} is row ordered return _stride[1] <= _stride[0]; } bool is_contiguous() const { shape_t expected({1, 1}); if (this->is_row_ordered()) expected[0] = _shape[1]; if (this->is_column_ordered()) expected[1] = _shape[0]; // if a dimension is 1 (or 0?) then its stride does not impact here return (_shape[0]<2||expected[0]==_stride[0]) && (_shape[1]<2||expected[1]==_stride[1]); } // empty initializer explicit Array2() : _data(nullptr), _num(0), _shift(0u), _own(false), _ref(std::make_shared()), _mutable(false), _shape({0,0}), _stride({0,1}) {} // 1D initializer Array2(T* data, const ind_t num, const bool own, const bool mut=true) : _data(data), _num(num), _shift(0u), _own(own), _ref(std::make_shared()), _mutable(mut), _shape({num,1}), _stride({1,1}) { this->init_check(); } // 2D initializer Array2(T* data, const ind_t num, const bool own, const shape_t& shape, const shape_t& stride, const bool mut=true) : _data(data), _num(num), _shift(0u), _own(own), _ref(std::make_shared()), _mutable(mut), _shape(shape), _stride(stride) { this->init_check(); } // 2D initializer with reference-counter specified (used in pybind11 wrapper) template Array2(T* data, const ind_t num, const bool own, std::shared_ptr

ref, const shape_t& shape, const shape_t& stride, const bool mut=true) : _data(data), _num(num), _shift(0u), _own(own), _ref(ref), _mutable(mut), _shape(shape), _stride(stride) { this->init_check(); } // 2D view constructor template Array2(T* data, const ind_t num, const ind_t shift, const bool own, const std::shared_ptr

& ref, const shape_t& shape, const shape_t& stride, const bool mut=true) : _data(data), _num(num), _shift(shift), _own(own), _ref(ref), _mutable(mut), _shape(shape), _stride(stride) { this->init_check(); } // 2D allocate new memory constructor Array2(const ind_t s0, const ind_t s1) : _shift(0u), _mutable(true), _shape({s0,s1}), _stride({s1,1}) { this->construct(); this->init_check(); } // 2D initialize new memory constructor Array2(const ind_t s0, const ind_t s1, const T init) : _shift(0u), _mutable(true), _shape({s0,s1}), _stride({s1,1}) { this->construct(init); this->init_check(); } // 2D allocate new memory constructor Array2(const shape_t& shape) : _shift(0u), _mutable(true), _shape(shape) { this->set_stride(); this->construct(); this->init_check(); } // ND initialize new memory constructor Array2(const shape_t& shape, const T init) : _shift(0u), _mutable(true), _shape(shape) { this->set_stride(); this->construct(init); this->init_check(); } // ND allocate new memory with specified stride constructor Array2(const shape_t& shape, const shape_t& stride) : _shift(0u), _mutable(true), _shape(shape), _stride(stride) { this->construct(); this->init_check(); } // ND initialize new memory with specified stride constructor Array2(const shape_t& shape, const shape_t& stride, const T init) : _shift(0u), _mutable(true), _shape(shape), _stride(stride) { this->construct(init); this->init_check(); } // otherwise possibly ambiguous constructor from std::vector static Array2 from_std(const std::vector& data){ ind_t num = static_cast(data.size()); T* d = new T[num](); for (ind_t i=0; i(d, num, true); } template static Array2 from_std(const std::vector>& data){ shape_t shape{{static_cast(data.size()), static_cast(Nel)}}; shape_t stride{shape[1], 1}; ind_t num = shape[0]*shape[1]; T* d = new T[num](); ind_t x{0}; for (ind_t i=0; i(data[i][j]); return Array2(d, num, true, shape, stride); } // Construct an Array2 from an Array, unravelling higher dimensions Array2(const Array& nd) : _num(0u), _shift(0u), _shape({0,1}), _stride({1,1}) { // we always want to construct contiguous row-ordered arrays if (nd.ndim()>0){ _shape[0] = nd.size(0); if (nd.ndim()>1){ for (ind_t i=1; i& o) : _data(o._data), _num(o._num), _shift(o._shift), _own(o._own), _ref(o._ref), _mutable(o._mutable), _shape(o._shape), _stride(o._stride) {} ~Array2(){ if (_own && _ref.use_count()==1 && _data != nullptr) delete[] _data; } Array2& operator=(const Array2& other){ if (this != &other){ if (_own){ T* old_data = _data; ref_t old_ref = _ref; _data = other._data; _ref = other._ref; if (old_ref.use_count()==1 && old_data != nullptr) delete[] old_data; } else { _data = other._data; _ref = other._ref; } _own = other.own(); _num = other.raw_size(); _shift = other.raw_shift(); _mutable = other.ismutable(); _shape = other.shape(); _stride = other.stride(); } return *this; } // type casting requires a copy // the reference pointer type does not need to be P since a new raw array is made // handles also Array2(Array2&) reference type conversion template Array2(const Array2& other) : _shift(0u), _mutable(true), _shape(other.shape()) { this->set_stride(); this->construct(); for (auto x: this->subItr()) _data[s2l_d(x)] = static_cast(other[x]); } template Array2& operator=(const Array2& other){ _mutable = true; _shape = other.shape(); _shift = 0; this->set_stride(); this->construct(); for (auto x: this->subItr()) _data[s2l_d(x)] = static_cast(other[x]); return *this; } // modifiers bool make_mutable() {_mutable = true; return _mutable;} bool make_immutable() {_mutable = false; return _mutable;} Array2 decouple() const { if (!_own || !_mutable || _ref.use_count() > 1) return this->_decouple(); return *this; } // data accessors T& operator[](ind_t lin) {return _data[this->l2l_d(lin)];} const T& operator[](ind_t lin) const {return _data[this->l2l_d(lin)];} T& operator[](shape_t& sub) {return _data[this->s2l_d(sub)];} const T& operator[](shape_t& sub) const {return _data[this->s2l_d(sub)];} protected: // so inherited classes can calculate subscript indexes into their data ind_t l2l_d(const ind_t l) const { return l + _shift; } ind_t ij2l_d(const ind_t x, const ind_t y) const { return sub2lin(x, y, _stride) + _shift; } ind_t s2l_d(const shape_t& s) const { return sub2lin(s, _stride) + _shift; } private: ind_t size_from_shape(const shape_t& s) const { return s[0]*s[1]; } ind_t size_from_shape() const {return this->size_from_shape(_shape);} void construct() { _num = this->size_from_shape(); if (_num > 0){ _ref = std::make_shared(); _data = new T[_num](); _own = true; } else { _data = nullptr; _own = false; } } void construct(const T init){ this->construct(); if (_num > 0 && _data != nullptr) std::fill(_data, _data+_num, init); } void set_stride(void){ _stride[1] = 1; _stride[0] = _shape[1]; } void init_check(void){ ind_t offset_size = _shift + this->size_from_shape(_shape); if (_num < offset_size) { std::string msg = "The shift { " + std::to_string(_shift) + " "; msg += "} and size { "; for (auto x: _shape) msg += std::to_string(x) + " "; msg += "} of an Array must not exceed the allocated pointer size "; msg += std::to_string(_num); throw std::runtime_error(msg); } } shape_t calculate_stride(const shape_t& shape) const { shape_t stride{{1,1}}; if (_stride[0] < _stride[1]){ stride[1] = shape[0]; } else { stride[0] = shape[1]; } return stride; } void reset_stride(){ if (!this->is_contiguous()) throw std::runtime_error("Re-calculating non-contiguous strides is not yet working"); _stride = this->calculate_stride(_shape); } Array2 _decouple() const { ind_t nnum = this->size_from_shape(_shape); T* new_data = new T[nnum](); shape_t new_st{_stride}; if (this->is_contiguous() && nnum == _num) { std::copy(_data, _data+_num, new_data); } else { //subscript conversion necessary due to offset or strided array new_st = this->calculate_stride(_shape); // vv(no offset)vvv vvv(offset)vvv for (auto x: this->subItr()) new_data[sub2lin(x,new_st)] = _data[this->s2l_d(x)]; } bool new_own = true; // always take ownership of C++ allocated memory auto new_ref = std::make_shared(); // always use the default with C++ created arrays bool new_mut = true; // new allocated memory should be mutable return Array2(new_data, nnum, new_own, new_ref, _shape, new_st, new_mut); } public: // sub-array access Array2 view() const; // whole array non-owning view Array2 view(ind_t i) const; Array2 view(ind_t i, ind_t j) const; Array2 view(const shape_t&) const; // duplication of one or more sub-arrays: Array2 extract(ind_t i) const; template std::enable_if_t, Array2> extract(const Array2& i) const; template std::enable_if_t, Array2> extract(const std::vector& i) const; template std::enable_if_t, Array2> extract(const std::array& i) const; template std::enable_if_t, Array2> extract(const std::vector>& i) const; Array2 extract(const Array2& i) const; Array2 extract(const std::vector& i) const; bool set(const ind_t i, const Array2& in); template bool set(const ind_t i, const Array2& in); bool set(const ind_t i, const std::vector& in); template bool set(const ind_t i, const std::array& in); T set(const shape_t& sub, T in); Array2& append(const ind_t, const Array2&); std::string to_string() const; std::string to_string(const ind_t) const; Array2& reshape(const shape_t& ns); Array2& resize(const shape_t&, T init=T(0)); template Array2& resize(const I, T init=T(0)); bool all(ind_t n=0) const; bool any(ind_t n=0) const; ind_t count(ind_t n=0) const; ind_t first(ind_t n=0) const; ind_t last(ind_t n=0) const; // bool all() const; // bool any() const; // ind_t count() const; // ind_t first() const; // ind_t last() const; bool all(T val, ind_t n=0) const; bool any(T val, ind_t n=0) const; ind_t count(T val, ind_t n=0) const; ind_t first(T val, ind_t n=0) const; ind_t last(T val, ind_t n=0) const; Array2 round() const; Array2 floor() const; Array2 ceil() const; Array2 sum(ind_t dim=0) const; Array2 prod(ind_t dim=0) const; Array2 min(ind_t dim=0) const; Array2 max(ind_t dim=0) const; T sum() const; T prod() const; template bool match(ind_t i, ind_t j, const std::array& rot, int order=1) const; bool match(ind_t i, ind_t j, ops op=ops::plus, T val=T{0}) const; bool all(cmp expr, T val) const; bool any(cmp expr, T val) const; ind_t first(cmp expr, T val) const; ind_t last(cmp expr, T val) const; ind_t count(cmp expr, T val) const; Array2 is(cmp expr, T val) const; std::vector find(cmp expr, T val) const; template Array2 is(cmp expr, const Array2& that) const; template std::vector is(cmp expr, const std::vector& val) const; template bool is(const Array2& that) const; std::vector is_unique() const; std::vector unique_idx() const; Array2 unique() const; Array2 operator-() const; Array2& operator +=(const T&); Array2& operator -=(const T&); Array2& operator *=(const T&); Array2& operator /=(const T&); template Array2& operator +=(const Array2&); template Array2& operator -=(const Array2&); template Array2& operator *=(const Array2&); template Array2& operator /=(const Array2&); T dot(ind_t i, ind_t j) const; T norm(ind_t i) const; template::value>> void permute(std::vector& p); bool swap(ind_t a, ind_t b); bool swap(ind_t i, ind_t a, ind_t b); std::vector to_std() const; T* ptr(const ind_t i0); T* ptr(const ind_t i0, const ind_t j0); T* ptr(const shape_t& partial_subscript); const T* ptr(const ind_t i0) const; const T* ptr(const ind_t i0, const ind_t j0) const; const T* ptr(const shape_t& partial_subscript) const; T& val(const ind_t i0); T& val(const ind_t i0, const ind_t j0); T& val(const shape_t& partial_subscript); template T& val(std::initializer_list l); const T& val(const ind_t i0) const; const T& val(const ind_t i0, const ind_t j0) const; const T& val(const shape_t& partial_subscript) const; template const T& val(std::initializer_list l) const; Array2 contiguous_copy() const; Array2 contiguous_row_ordered_copy() const; // ^^^^^^^^^^ IMPLEMENTED ^^^^^^^^^^^vvvvvvvvv TO IMPLEMENT vvvvvvvvvvvvvvvvv }; template class Array2It { public: Array2 array; SubIt2 subit; public: // constructing with array(a) does not copy the underlying data: explicit Array2It() : array(), subit() {} Array2It(const Array2& a, const SubIt2& s) : array(a), subit(s) {} Array2It(const Array2& a) : array(a), subit(a.shape()) // initialises to first element, e.g., {0,…,0} {} // Array2It begin() const { return Array2It(array); } Array2It end() const { return Array2It(array, subit.end()); } Array2It& operator++() { ++subit; return *this; } const SubIt2& iterator() const {return subit;} bool operator==(const Array2It& other) const { // add checking to ensure array and other.array point to the same data? return subit == other.iterator(); } bool operator!=(const Array2It& other) const { return subit != other.iterator(); } const T& operator*() const {return array[*subit];} const T* operator->() const {return &(array[*subit]);} T& operator*() {return array[*subit];} T* operator->() {return &(array[*subit]);} }; #include "array2.tpp" } // end namespace brille #endif // ARRAY_HPP