.. _program_listing_file_src_array.tpp: Program Listing for File array.tpp ================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/array.tpp``) .. |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 . */ template Array Array::view() const{ // return Array(_data,_num,_own,_ref,_offset,_shape,_stride,false); return Array(_data, _num, _shift, _own, _ref, _shape, _stride, false); } template Array Array::view(const ind_t i) const { if (i<_shape[0]){ shape_t oshape{_shape}; oshape[0] = 1; // shape_t oofst{_offset}; oofst[0] += i; ind_t oshift{_shift + i*_stride[0]}; return Array(_data, _num, oshift, _own, _ref, oshape, _stride, false); } throw std::runtime_error("Array index too large"); } template Array Array::view(const ind_t i, const ind_t j) const { if (i(_data, _num, oshift, _own, _ref, oshape, _stride, false); } throw std::runtime_error("Array view indexing error"); } template Array Array::view(const shape_t& v) const { shape_t oshape{_shape}; // shape_t oofst{_offset}; ind_t oshift{_shift}; for (size_t i=0; i<_shape.size(); ++i) if (v[i] < _shape[i]) { // oofst[i] += v[i]; oshape[i] -= v[i]; oshift += v[i]*_stride[i]; } return Array(_data, _num, oshift, _own, _ref, oshape, _stride, false); } template Array Array::extract(const ind_t i) const{ return this->view(i).decouple(); } template template std::enable_if_t, Array> Array::extract(const Array& i) const { if (i.numel() != i.size(0)) throw std::runtime_error("Array extraction requires (N,{1,...,1}) shape"); for (auto x: i.valItr()) if (!(0 <= x && static_cast(x) < _shape[0])) throw std::runtime_error("Array extract index must be in range"); shape_t osize{_shape}; osize[0] = i.size(0); Array out(osize); ind_t j{0}; for (auto v: i.valItr()) out.set(j++, this->view(static_cast(v))); return out; } template template std::enable_if_t, Array> Array::extract(const std::vector& i) const { for (auto x: i) if (!(0 <= x && static_cast(x) < _shape[0])) throw std::runtime_error("Array extract index must be in range"); shape_t osize{_shape}; osize[0] = static_cast(i.size()); Array out(osize); for (ind_t j=0; jview(i[j])); return out; } template template std::enable_if_t, Array> Array::extract(const std::array& i) const { for (auto x: i) if (!(0 <= x && static_cast(x) < _shape[0])) throw std::runtime_error("Array extract index must be in range"); shape_t osize{_shape}; osize[0] = static_cast(Nel); Array out(osize); for (ind_t j=0; jview(static_cast(i[j]))); } return out; } template template std::enable_if_t, Array> Array::extract(const std::vector>& i) const { for (auto a: i) for (auto x: a) if (!(0 <= x && static_cast(x) < _shape[0])) throw std::runtime_error("Array extract index must be in range"); shape_t osh{static_cast(i.size()), static_cast(Nel)}; for (ind_t i=1; indim(); ++i) osh.push_back(_shape[i]); Array out(osh); shape_t xi{_shape}; for (ind_t a=0; a(i[a][b]); for (auto sub: SubIt(_shape, xi)) { for (ind_t j=0; j<_shape.size(); ++j) osh[j+1]=sub[j]; out[sub] = _data[this->s2l_d(xi)]; } } } return out; } template Array Array::extract(const Array& i) const { shape_t isize = i.shape(); if (isize[0] > _shape[0]) throw std::runtime_error("Boolean Array extraction requires no more bools than the first Array dimension"); shape_t osize{_shape}; osize[0] = i.count(); Array out(osize); ind_t n = isize[0] < _shape[0] ? isize[0] : _shape[0]; ind_t idx{0}; for (ind_t j=0; jview(j)); return out; } template Array Array::extract(const std::vector& i) const { if (i.size() > _shape[0]) throw std::runtime_error("Boolean Array extraction requires no more bools than the first Array dimension"); shape_t osize{_shape}; auto count = std::count(i.begin(), i.end(), true); osize[0] = static_cast(count); Array out(osize); ind_t n = i.size() < _shape[0] ? i.size() : _shape[0]; ind_t idx{0}; for (ind_t j=0; jview(j)); return out; } template bool Array::set(const ind_t i, const Array& in){ // we might be able to do this better/faster if we both *this and in // have the same strides_ and we account for any offset. For now calculate // the 'hard' way no matter what: shape_t inshape = in.shape(); size_t ndim = _shape.size(); for (ind_t dim=1; dim(inshape)){ const T& tmp = in[sub]; sub[0] = i; // [0,0,…,0],[0,0,…,1],…,[0,1,…,0],… to [i,0,…,0],[i,0,…,1],…,[i,1,…,0],… _data[this->s2l_d(sub)] = tmp; } return true; } template template bool Array::set(const ind_t i, const Array& in){ // we might be able to do this better/faster if we both *this and in // have the same strides_ and we account for any offset. For now calculate // the 'hard' way no matter what: shape_t inshape = in.shape(); size_t ndim = _shape.size(); for (ind_t dim=1; dim(inshape)){ const R& tmp = in[sub]; sub[0] = i; // [0,0,…,0],[0,0,…,1],…,[0,1,…,0],… to [i,0,…,0],[i,0,…,1],…,[i,1,…,0],… _data[this->s2l_d(sub)] = static_cast(tmp); } return true; } template bool Array::set(const ind_t i, const std::vector& in){ if (this->numel() != _shape[0]*in.size()) throw std::runtime_error("Set requires the correct number of elements"); shape_t tsize = this->shape(); tsize[0] = 1u; // in is (hopefully) a row-ordered linear indexing size_t idx{0}; for (auto sub: SubIt(tsize)){ sub[0] = i; _data[this->s2l_d(sub)] = in[idx++]; } return true; } template template bool Array::set(const ind_t i, const std::array& in){ if (this->numel() != _shape[0]*Nel) throw std::runtime_error("Set requires the correct number of elements"); shape_t tsize = this->shape(); tsize[0] = 1u; // in is (hopefully) a row-ordered linear indexing size_t idx{0}; for (auto sub: SubIt(tsize)){ sub[0] = i; _data[this->s2l_d(sub)] = in[idx++]; } return true; } template T Array::set(const shape_t& sub, const T in){ auto ind = this->s2l_d(sub); _data[ind] = in; return _data[ind]; } template std::string Array::to_string() const{ if (this->_num == 0) return std::string("Unallocated Array"); size_t width{0}; for (ind_t i=0; i<_num; ++i){ size_t l = my_to_string(_data[i]).size(); if (l > width) width = l; } std::string out; std::vector isin(this->ndim(), false); bool preamble{false}; size_t ndim = _shape.size(); for (auto sub: SubIt(_shape)){ if (!preamble){ for (ind_t i=0; is2l_d(sub)], width); // out += std::to_string(_data[this->s2l_d(sub)]); if (sub[ndim-1]+1 < _shape[ndim-1]){ out += ", "; } else { for (ind_t i=0; i std::string Array::to_string(const ind_t i) const { auto out = this->view(i).to_string(); out.pop_back(); // remove the trailing \n return out; } template Array& Array::reshape(const shape_t& ns){ ind_t num = this->size_from_shape(ns); info_update_if(num > _num, "Array::reshape only intended for equal-element number changes."); // assert( num <= _num ); if (!this->is_contiguous()) throw std::runtime_error("Array::reshape does not work for strided arrays"); _shape = ns; // _shift *is* the linear offset, and doesn't care about the subscript shape // ind_t linoffset = sub2lin(_offset, _stride); _stride = this->calculate_stride(ns); // _offset = lin2sub(linoffset, _stride); return *this; } /*! \brief Modify the number of arrays Allocate a new T* and copy the overlapping region. The new shape *must* have the same number of dimensions as the old shape. Resizing an array will decouple it from any other arrays sharing their underlying data. Such an operation should not be taken lightly. @param ns the new shape @param init an optional initialization value for any non-overlapping region */ template Array& Array::resize(const shape_t& ns, const T init) { ind_t nnum = this->size_from_shape(ns); //if (nnum == _num) return this->reshape(ns); // otherwise resizing can not change dimensionality assert(_shape.size() == ns.size()); // determine which axes are changing and how std::vector shrinking, growing; for (size_t i=0; i= _shape[i]); } bool shrink = std::all_of(shrinking.begin(), shrinking.end(), [](bool a){return a;}); bool grow = !shrink && std::all_of(growing.begin(), growing.end(), [](bool a){return a;}); if (!shrink && !grow){ std::string msg = "This method is not able to resize from { "; for (auto x : _shape) msg += std::to_string(x) + " "; msg += "} to { "; for (auto x : ns) msg += std::to_string(x) + " "; msg += "}"; throw std::runtime_error(msg); } // make the new array T* nd = new T[nnum](); // fill the whole thing instead of trying to figure out difference indices if (grow) std::fill(nd, nd+nnum, init); // loop over the subscripted incides which fit inside both arrays shape_t inside = shrink ? ns : _shape; // find the new stride (maintaining row or column order) shape_t nt = calculate_stride(ns); // ensure our simple indexing will work // ind_t linoffset = sub2lin(_offset, _stride); // if (linoffset > 0) if (_shift > 0) throw std::runtime_error("Resizing only works for zero-offset Arrays at present. Please extend."); // copy into the new array if the old array isn't the null pointer if (_data != nullptr && nd != nullptr) for (auto x : SubIt(inside)) nd[sub2lin(x, nt)] = _data[sub2lin(x, _stride)]; // delete the old _data if we hold the only reference if (_own && _ref.use_count()==1 && _data != nullptr) delete[] _data; // update data, number, ownership, reference, size, stride, and mutability _data = nd; _num = nnum; _own = true; _ref = std::make_shared(); // resizing in place can not change the template parameter // _offset = shape_t(ns.size(), 0); _shape = ns; _stride = nt; _mutable = true; // pass back a reference to this object return *this; } template template Array& Array::resize(const I ns, const T init) { shape_t nshape{_shape}; if (nshape.size()>0) nshape[0] = static_cast(ns); else{ throw std::runtime_error("Resizing a null-shaped Array not supported (yet)"); //nshape.push_back(static_cast(ns)); } return this->resize(nshape, init); } template Array& Array::append(const ind_t dim, const Array& extra) { assert(this != &extra); ind_t ndim = this->ndim(); assert(extra.ndim() == ndim && dimresize(eshape); // so we need only iterate over the extra Array for (auto x : SubIt(extra.shape())){ // offsetting its subscript to the new position auto y = x; y[dim] += tshapedim; // offset by the original shape along the dimension // and copying the contents (*this)[y] = extra[x]; } return *this; } template bool Array::all(const ind_t n) const{ ind_t nml = this->numel(); ind_t count = (n > 0 && n < nml) ? n : nml; for (ind_t i=0; il2l_d(i)]) return false; return true; } template bool Array::any(const ind_t n) const{ ind_t nml = this->numel(); ind_t count = (n > 0 && n < nml) ? n : nml; for (ind_t i=0; il2l_d(i)]) return true; return false; } template bool Array::all(const T val, const ind_t n) const{ ind_t nml = this->numel(); ind_t count = (n > 0 && n < nml) ? n : nml; for (ind_t i=0; il2l_d(i)]) return false; return true; } template bool Array::any(const T val, const ind_t n) const{ ind_t nml = this->numel(); ind_t count = (n > 0 && n < nml) ? n : nml; for (ind_t i=0; il2l_d(i)]) return true; return false; } template ind_t Array::count(const ind_t n) const{ ind_t el = this->numel(); ind_t no = (n > 0 && n < el) ? n : el; ind_t count{0}; for (ind_t i=0; il2l_d(i)]) ++count; return count; } template ind_t Array::count(const T val, const ind_t n) const{ ind_t el = this->numel(); ind_t no = (n > 0 && n < el) ? n : el; ind_t count{0}; for (ind_t i=0; il2l_d(i)]) ++count; return count; } template ind_t Array::first(const ind_t n) const{ ind_t el = this->numel(); ind_t no = (n > 0 && n < el) ? n : el; for (ind_t i=0; il2l_d(i)]) return i; return no; } template ind_t Array::first(const T val, const ind_t n) const{ ind_t el = this->numel(); ind_t no = (n > 0 && n < el) ? n : el; for (ind_t i=0; il2l_d(i)]) return i; return no; } template ind_t Array::last(const ind_t n) const{ ind_t el = this->numel(); ind_t no = (n > 0 && n < el) ? n : el; for (ind_t i=no; i--;) if (_data[this->l2l_d(i)]) return i; return no; } template ind_t Array::last(const T val, const ind_t n) const{ ind_t el = this->numel(); ind_t no = (n > 0 && n < el) ? n : el; for (ind_t i=no; i--;) if (val == _data[this->l2l_d(i)]) return i; return no; } #define ARRAY_ELEMENTWISE_INT_TRANSFORM(X) template\ Array\ Array:: X () const {\ Array out(_shape, _stride);\ for (auto x: SubIt(out.shape()))\ out[x] = static_cast(std:: X (_data[this->s2l_d(x)]));\ return out;\ } ARRAY_ELEMENTWISE_INT_TRANSFORM(round) ARRAY_ELEMENTWISE_INT_TRANSFORM(floor) ARRAY_ELEMENTWISE_INT_TRANSFORM(ceil) #undef ARRAY_ELEMENTWISE_INT_TRANSFORM template Array Array::sum(const ind_t dim) const{ size_t ndim = _shape.size(); assert(dim < ndim); shape_t ostride = this->stride(); shape_t osize = this->shape(); osize[dim] = 1u; if (this->is_row_ordered()){ for (ind_t i=0; i out(osize, ostride); for (auto oidx: SubIt(osize)) { T tmp{0}; auto idx = oidx; for (ind_t i=0; i<_shape[dim]; ++i) { idx[dim] = i; tmp += _data[this->s2l_d(idx)]; } out[oidx] = tmp; } return out; } template Array Array::prod(const ind_t dim) const{ size_t ndim = _shape.size(); assert(dim < ndim); shape_t ostride = this->stride(); shape_t osize = this->shape(); osize[dim] = 1u; if (this->is_row_ordered()){ for (ind_t i=0; i out(osize, ostride); for (auto oidx: SubIt(osize)) { T tmp{1}; auto idx = oidx; for (ind_t i=0; i<_shape[dim]; ++i) { idx[dim] = i; tmp *= _data[this->s2l_d(idx)]; } out[oidx] = tmp; } return out; } template Array Array::min(const ind_t dim) const{ size_t ndim = _shape.size(); assert(dim < ndim); shape_t ostride = this->stride(); shape_t osize = this->shape(); osize[dim] = 1u; if (this->is_row_ordered()){ for (ind_t i=0; i out(osize, ostride); for (auto oidx: SubIt(osize)) { T tmp{(std::numeric_limits::max)()}; auto idx = oidx; for (ind_t i=0; i<_shape[dim]; ++i) { idx[dim] = i; T tst = _data[this->s2l_d(idx)]; if (tst < tmp) tmp = tst; } out[oidx] = tmp; } return out; } template Array Array::max(const ind_t dim) const{ size_t ndim = _shape.size(); assert(dim < ndim); shape_t ostride = this->stride(); shape_t osize = this->shape(); osize[dim] = 1u; if (this->is_row_ordered()){ for (ind_t i=0; i out(osize, ostride); for (auto oidx: SubIt(osize)) { T tmp{std::numeric_limits::lowest()}; auto idx = oidx; for (ind_t i=0; i<_shape[dim]; ++i) { idx[dim] = i; T tst = _data[this->s2l_d(idx)]; if (tst > tmp) tmp = tst; } out[oidx] = tmp; } return out; } template T Array::sum() const{ T out{0}; for (auto x : _shape) out += _data[this->s2l_d(x)]; return out; } template T Array::prod() const{ T out{1}; for (auto x : _shape) out *= _data[this->s2l_d(x)]; return out; } template template bool Array::match(const ind_t i, const ind_t j, const std::array& rot, const int order) const { assert(this->ndim() == 2u); // only defined for 2-D Arrays assert(Nel == _shape[1]*_shape[1]); ind_t n = _shape[1]; auto ai=this->view(i); auto aj=this->view(j); std::vector tmp(n,T(0)); for (ind_t k=0; k eq(brille::cmp::eq); if (order < 0){ int o{0}; do{ //check against the current tmp vector whether this order rotation has moved j to i if (eq(n, ai.data(), tmp.data())) return true; // otherwise apply the rotation (again) brille::utils::mul_mat_vec_inplace(n, rot.data(), tmp.data()); } while (o++ < std::abs(order)); return false; } else { // rotate exactly order times for (int o=0; o bool Array::match(const ind_t i, const ind_t j, brille::ops op, T val) const { auto ai= this->view(i); auto aj= this->view(j); brille::Comparer neq(brille::cmp::neq); ind_t no = this->numel()/_shape[0]; switch (op){ case brille::ops::plus: for (ind_t k=0; k bool Array::all(const brille::cmp expr, const T val) const { if (brille::cmp::le_ge == expr) return this->all(brille::cmp::le, val) || this->all(brille::cmp::ge, val); ind_t no = this->numel(); brille::Comparer op(expr); for (ind_t k=0; kl2l_d(k)], val)) return false; return true; } template bool Array::any(const brille::cmp expr, const T val) const { return this->first(expr, val) < this->numel(); } template ind_t Array::first(const brille::cmp expr, const T val) const { ind_t no = this->numel(); brille::Comparer op(expr); for (ind_t k=0; kl2l_d(k)], val)) return k; return no; } template ind_t Array::last(const brille::cmp expr, const T val) const { ind_t no = this->numel(); brille::Comparer op(expr); for (ind_t k=no; k--;) if(op(_data[this->l2l_d(k)], val)) return k; return no; } template ind_t Array::count(const brille::cmp expr, const T val) const { ind_t no = this->numel(); brille::Comparer op(expr); ind_t cnt{0}; for (ind_t k=0; kl2l_d(k)], val)) ++cnt; return cnt; } template Array Array::is(const brille::cmp expr, const T val) const { Array out(_shape, _stride, true); ind_t no = this->numel(); brille::Comparer op(expr); for (ind_t k=0; kl2l_d(k)], val); return out; } template std::vector Array::find(const brille::cmp expr, const T val) const { Array this_is = this->is(expr, val); ind_t no = this->numel(); std::vector out; for (ind_t k=0; k template Array Array::is(const brille::cmp expr, const Array& that) const { // To handle singleton-dimension broadcasting, this function needs to be split auto tsize = that.shape(); if (!std::equal(_shape.begin(), _shape.end(), tsize.begin(), [](ind_t a, ind_t b){return 1==b || a==b;})){ std::string msg = "An Array with size ( "; for (auto x: tsize) msg += std::to_string(x) + " "; msg += ") can not be broadcast to match one with size ( "; for (auto x: _shape) msg += std::to_string(x) + " "; msg += ")."; throw std::runtime_error(msg); } Array out(_shape, _stride, true); size_t ndim = _shape.size(); if (std::equal(_shape.begin(), _shape.end(), tsize.begin())){ // No broadcast brille::Comparer op(expr); // no guarantees about same stride, so use subscript iterator: for (auto sub: SubIt(_shape)) out[sub] = op(_data[this->s2l_d(sub)], that[sub]); } else { // Broadcast for (ind_t i=1; iview(i).is(expr, that)); } return out; } template template std::vector Array::is(const brille::cmp expr, const std::vector& val) const{ assert(2==this->ndim()); assert(val.size() == _shape[1]); std::vector out; out.reserve(_shape[0]); brille::Comparer op(expr); for (ind_t i=0; i<_shape[0]; ++i) out.push_back(op(_shape[1], this->ptr(i), _stride[1], val.data(), 1u)); return out; } template template bool Array::is(const Array& that) const { return this->is(brille::cmp::eq, that).all(true); } template std::vector Array::is_unique() const{ assert(2==this->ndim()); if (_shape[0] < 1u) return std::vector(); std::vector out(1u, true); out.reserve(_shape[0]); brille::Comparer op(brille::cmp::neq); ind_t sz{_shape[1]}, st{_stride[1]}; for (ind_t i=1; i<_shape[0]; ++i){ bool isu{true}; for (ind_t j=0; jptr(i), st, this->ptr(j), st); if (!isu) break; } out.push_back(isu); } return out; } template std::vector Array::unique_idx() const{ assert(2==this->ndim()); if (_shape[0] < 1u) return std::vector(); std::vector out(1u, 0u); out.reserve(_shape[0]); brille::Comparer op(brille::cmp::eq); ind_t sz{_shape[1]}, st{_stride[1]}; for (ind_t i=1; i<_shape[0]; ++i){ ind_t idx{i}; for (ind_t j=0; jptr(i), st, this->ptr(j), st)){ idx=j; break; } out.push_back(idx); } return out; } template Array Array::unique() const { std::vector isu = this->is_unique(); size_t u_count = std::count(isu.begin(), isu.end(), true); shape_t osize{_shape}; osize[0] = static_cast(u_count); Array out(osize, _stride); for (ind_t i=0,u=0; i<_shape[0]; ++i) if (isu[i]) out.set(u++, this->view(i)); return out; } template T Array::dot(ind_t i, ind_t j) const { assert(2==this->ndim()); assert(i<_shape[0]); assert(j<_shape[0]); ind_t sz{_shape[1]}, st{_stride[1]}; std::vector prods(sz,0); brille::RawBinaryOperator prod(brille::ops::times); // perform elementwise multiplication on the views of i and j prod(sz, prods.data(), 1u, this->ptr(i), st, this->ptr(j), st); // find the sum of the products // std::reduce can do this in parallel but gccview(i).valItr(); // auto vj = this->view(j).valItr(); // return std::transform(vi.begin() vi.end(), vj.begin(), vj.end(), T(0), std::multiples, std::plus); } template T Array::norm(ind_t i) const { return std::sqrt(this->dot(i,i)); } template Array Array::operator-() const{ Array neg(_shape, _stride); for (auto s: SubIt(_shape)) neg[s] = -_data[this->s2l_d(s)]; return neg; } template template void Array::permute(std::vector& p){ std::vector s=p, o(_shape[0]); std::iota(o.begin(), o.end(), 0u); std::sort(s.begin(), s.end()); debug_update_if(!std::includes(o.begin(),o.end(),s.begin(),s.end()),"The permutation vector ",p," is invalid. Expected permutation of ",o); // get the inverse permutation to enable element swapping: for (size_t i=0; iswap(i,s[i]); std::swap(s[i], s[s[i]]); } else{ ++i; } } debug_update_if(!std::is_sorted(s.begin(),s.end()), "Undoing the permutation ",p," failed. End result is ",s); } template bool Array::swap(const ind_t a, const ind_t b){ assert(a<_shape[0] && b<_shape[0]); shape_t sub{_shape}; sub[0] = a; // fix the 0th index to 'a' in the iterator auto itr = SubIt(_shape, sub); for (auto aidx: itr){ // for each of the [a,...] subscripted indices, construct [b,...] shape_t bidx{aidx}; bidx[0] = b; // precalculate the offset/stride/shaped subscripts: auto sa = this->s2l_d(aidx); auto sb = this->s2l_d(bidx); // perform the actual swap: T store = _data[sa]; _data[sa] = _data[sb]; _data[sb] = store; } return true; } template bool Array::swap(ind_t i, ind_t a, ind_t b){ assert(this->ndim() == 2 && i < _shape[0] && a < _shape[1] && b < _shape[1]); shape_t ia{i,a}, ib{i,b}; T store = _data[this->s2l_d(ia)]; _data[this->s2l_d(ia)] = _data[this->s2l_d(ib)]; _data[this->s2l_d(ib)] = store; return true; } template std::vector Array::to_std() const { std::vector out; for (auto x: this->valItr()) out.push_back(x); return out; } template T* Array::ptr(){ shape_t idx(_shape.size(), 0); return _data + s2l_d(idx); } template T* Array::ptr(const ind_t i0){ assert(i0 < _shape[0]); shape_t idx(_shape.size(), 0); idx[0] = i0; return _data + s2l_d(idx); } template template T* Array::ptr(const ind_t i0, Subs... subscripts){ const size_t nsub = 1+sizeof...(Subs); if (nsub != this->ndim()) throw std::runtime_error("Wrong number of subscripts for value access"); return this->ptr(brille::utils::make_vector(i0, subscripts...)); } template T* Array::ptr(const shape_t& p){ assert(p.size() <= _shape.size()); for (size_t i=0; i const T* Array::ptr() const { shape_t idx(_shape.size(), 0); return _data + s2l_d(idx); } template const T* Array::ptr(const ind_t i0) const { assert(i0 < _shape[0]); shape_t idx(_shape.size(), 0); idx[0] = i0; return _data + s2l_d(idx); } template template const T* Array::ptr(const ind_t i0, Subs... subscripts) const { const size_t nsub = 1+sizeof...(Subs); if (nsub != this->ndim()) throw std::runtime_error("Wrong number of subscripts for value access"); return this->ptr(brille::utils::make_vector(i0, subscripts...)); } template const T* Array::ptr(const shape_t& p) const { assert(p.size() <= _shape.size()); for (size_t i=0; i T& Array::val(const ind_t i0){ assert(i0 < _shape[0]); shape_t idx(_shape.size(), 0); idx[0] = i0; return _data[s2l_d(idx)]; } template template T& Array::val(const ind_t i0, Subs... subscripts){ const size_t nsub = 1+sizeof...(Subs); if (nsub != this->ndim()) throw std::runtime_error("Wrong number of subscripts for value access"); return this->val(brille::utils::make_vector(i0, subscripts...)); } template T& Array::val(const shape_t& p){ assert(p.size() <= _shape.size()); for (size_t i=0; i template T& Array::val(std::initializer_list l){ shape_t idx; for (auto& x: l) idx.emplace_back(x); return this->val(idx); } template const T& Array::val(const ind_t i0) const { assert(i0 < _shape[0]); shape_t idx(_shape.size(), 0); idx[0] = i0; return _data[s2l_d(idx)]; } template template const T& Array::val(const ind_t i0, Subs... subscripts) const { const size_t nsub = 1+sizeof...(Subs); if (nsub != this->ndim()) throw std::runtime_error("Wrong number of subscripts for value access"); return this->val(brille::utils::make_vector(i0, subscripts...)); } template const T& Array::val(const shape_t& p) const { assert(p.size() <= _shape.size()); for (size_t i=0; i template const T& Array::val(std::initializer_list l) const { shape_t idx; for (auto& x: l) idx.emplace_back(x); return this->val(idx); } template Array Array::contiguous_copy() const { if (this->is_contiguous()) return Array(*this); Array out(_shape, this->calculate_stride(_shape)); for (auto x : SubIt(_shape)) out[x] = (*this)[x]; return out; } template Array Array::contiguous_row_ordered_copy() const { if (this->is_row_ordered() && this->is_contiguous()) return Array(*this); // make sure we calculate a row-ordered stride: shape_t st(_shape.size(), 1u); for (size_t i=st.size()-1; i--;) st[i] = st[i+1]*_shape[i+1]; Array out(_shape, st); for (auto x : SubIt(_shape)) out[x] = (*this)[x]; return out; } // template // Array Array::squeeze() const { // ind_t lin = sub2lin(_offset, _stride); // shape_t n_off, n_str, n_shp; // for (ind_t i=0; i<_shape.size(); ++i) if (_shape[i] > 1) { // n_off.push_back(_offset[i]); // n_str.push_back(_stride[i]); // n_shp.push_back(_shape[i]); // } // if (n_shp.size() < 1){ // n_off.push_back(lin); // n_str.push_back(1u); // n_shp.push_back(1u); // } // ind_t n_lin = sub2lin(n_off, n_str); // if (n_lin < lin) for (ind_t i=0; i(_data, _num, _own, _ref, n_off, n_shp, n_str, _mutable); // } template Array Array::squeeze() const { shape_t n_str, n_shp; for (ind_t i=0; i<_shape.size(); ++i) if (_shape[i] > 1) { n_str.push_back(_stride[i]); n_shp.push_back(_shape[i]); } if (n_shp.size() < 1){ n_str.push_back(1u); n_shp.push_back(1u); } return Array(_data, _num, _shift, _own, _ref, n_shp, n_str, _mutable); } // template // Array Array::squeeze(const ind_t ax) const { // ind_t lin = sub2lin(_offset, _stride); // shape_t n_off, n_str, n_shp; // for (ind_t i=0; i<_shape.size(); ++i) if (ax != i || _shape[i] > 1) { // n_off.push_back(_offset[i]); // n_str.push_back(_stride[i]); // n_shp.push_back(_shape[i]); // } // if (n_shp.size() < 1){ // n_off.push_back(lin); // n_str.push_back(1u); // n_shp.push_back(1u); // } // ind_t n_lin = sub2lin(n_off, n_str); // if (n_lin < lin) for (ind_t i=0; i(_data, _num, _own, _ref, n_off, n_shp, n_str, _mutable); // } template Array Array::squeeze(const ind_t ax) const { shape_t n_str, n_shp; for (ind_t i=0; i<_shape.size(); ++i) if (ax != i || _shape[i] > 1) { n_str.push_back(_stride[i]); n_shp.push_back(_shape[i]); } if (n_shp.size() < 1){ n_str.push_back(1u); n_shp.push_back(1u); } return Array(_data, _num, _shift, _own, _ref, n_shp, n_str, _mutable); } // template // Array Array::slice(const ind_t i) const { // if (i<_shape[0]){ // // update the offset along the first dimension to include i // shape_t t_off{_offset}; // t_off[0] += i; // // calculate the linear offset // ind_t lin = sub2lin(t_off, _stride); // // pull together the remaining dimension(s) // shape_t n_off, n_str, n_shp; // for (ind_t i=1; i<_shape.size(); ++i){ // n_off.push_back(_offset[i]); // n_str.push_back(_stride[i]); // n_shp.push_back(_shape[i]); // } // // ensuring that we keep at least one // if (n_shp.size() < 1){ // n_off.push_back(lin); // n_str.push_back(1u); // n_shp.push_back(1u); // } // // make sure the total linear offset remains the same // ind_t n_lin = sub2lin(n_off, n_str); // if (n_lin < lin) for (ind_t i=0; i(_data, _num, _own, _ref, n_off, n_shp, n_str, _mutable); // } // throw std::runtime_error("Array index too large"); // } template Array Array::slice(const ind_t i) const { if (i<_shape[0]){ // update the offset along the first dimension to include i ind_t n_shift{_shift + i*_stride[0]}; // pull together the remaining dimension(s) shape_t n_str, n_shp; for (ind_t i=1; i<_shape.size(); ++i){ n_str.push_back(_stride[i]); n_shp.push_back(_shape[i]); } // ensuring that we keep at least one if (n_shp.size() < 1){ n_str.push_back(1u); n_shp.push_back(1u); } return Array(_data, _num, n_shift, _own, _ref, n_shp, n_str, _mutable); } throw std::runtime_error("Array index too large"); } #define SCALAR_INPLACE_OP(X) template\ Array& Array::operator X (const T& val){\ if (!this->ismutable()) throw std::runtime_error("Immutable Arrays can not be modified");\ ind_t nel=this->numel();\ ind_t idx{0};\ for (ind_t i=0; il2l_d(i);\ _data[idx] X val;\ }\ return *this;\ } SCALAR_INPLACE_OP(+=) SCALAR_INPLACE_OP(-=) SCALAR_INPLACE_OP(*=) SCALAR_INPLACE_OP(/=) #undef SCALAR_INPLACE_OP template bool broadcast_shape_check(const std::vector& a, const std::vector&b){ bool ok = a.size() == b.size(); ok &= brille::approx::vector(a.size(), a.data(), b.data()); if (!ok){ std::string msg = "In place broadcasting is not possible for { "; for (auto x: a) msg += std::to_string(x) + " "; msg += "} shaped Array and { "; for (auto x: b) msg += std::to_string(x) + " "; msg += "} shaped operand"; throw std::runtime_error(msg); } return ok; } #define ARRAY_INPLACE_OP(X) template template\ Array& Array::operator X (const Array& val){\ if (!this->ismutable()) throw std::runtime_error("Immutable Arrays can not be modified");\ BroadcastIt itr(_shape, val.shape());\ broadcast_shape_check(_shape, itr.shape());\ for (auto [ox, tx, vx]: itr) _data[this->s2l_d(tx)] X val[vx];\ return *this;\ } ARRAY_INPLACE_OP(+=) ARRAY_INPLACE_OP(-=) ARRAY_INPLACE_OP(*=) ARRAY_INPLACE_OP(/=) #undef ARRAY_INPLACE_OP /* (Temporarily) Define basic Array OP Array here */ // 'pure' Array [+-*/] 'pure' Array #define ARRAY_ARRAY_OP(X)\ template>\ Array\ operator X (const Array& a, const Array& b){\ auto itr = a.broadcastItr(b);\ Array out(itr.shape());\ for (auto [ox, ax, bx]: itr) out[ox] = a[ax] X b[bx];\ return out;\ } ARRAY_ARRAY_OP(+) ARRAY_ARRAY_OP(-) ARRAY_ARRAY_OP(*) ARRAY_ARRAY_OP(/) #undef ARRAY_ARRAY_OP