// -*- C++ -*- #ifndef __multi_array_operation_h__ #define __multi_array_operation_h__ #include #include #include #include #include #include "base_def.h" #include "mbase_def.h" #include "block/block.h" #include "operation_utils.h" #include "ranges/rheader.h" #include "pack_num.h" #include "ranges/index_info.h" #include "arith.h" namespace MultiArrayTools { namespace { using namespace MultiArrayHelper; } template Block makeBlock(const T* vec, size_t stepSize, size_t blockSize); template MBlock makeBlock(T* vec, size_t stepSize, size_t blockSize); // dont use this for now !! template std::shared_ptr seekBlockIndex(std::shared_ptr ownIdx, const OpClass& second); template const IndexInfo* seekBlockIndex(const IndexInfo* ownII, const OpClass& second); template class OperationTemplate { public: OperationClass& THIS() { return static_cast(*this); } const OperationClass& THIS() const { return static_cast(*this); } template auto operator+(const Second& in) const -> Operation,OperationClass,Second>; template auto operator-(const Second& in) const -> Operation,OperationClass,Second>; template auto operator*(const Second& in) const -> Operation,OperationClass,Second>; template auto operator/(const Second& in) const -> Operation,OperationClass,Second>; template auto c(std::shared_ptr& ind) const -> Contraction; private: friend OperationClass; OperationTemplate() = default; }; template class OperationMaster { public: class AssignmentExpr { private: AssignmentExpr() = default; //AssignmentExpr(const AssignmentExpr& in) = default; //AssignmentExpr& operator=(const AssignmentExpr& in) = default; OperationMaster& mM; const OpClass& mSec; public: static size_t layer() { return 0; } static constexpr size_t LAYER = 0; static constexpr size_t SIZE = OpClass::SIZE; typedef decltype(mSec.rootSteps()) ExtType; AssignmentExpr(OperationMaster& m, const OpClass& sec); AssignmentExpr(const AssignmentExpr& in) = default; AssignmentExpr(AssignmentExpr&& in) = default; //AssignmentExpr& operator=(const AssignmentExpr&& in) = default; inline void operator()(size_t start = 0); inline void operator()(size_t start, ExtType last); auto rootSteps(std::intptr_t iPtrNum = 0) -> ExtType; }; typedef T value_type; typedef OperationBase OB; typedef ContainerRange CRange; typedef typename MultiRange::IndexType IndexType; typedef MBlock bType; static size_t rootNum() { return 1; } OperationMaster(MutableMultiArrayBase& ma, const OpClass& second, std::shared_ptr& index); OperationMaster(MutableMultiArrayBase& ma, const OpClass& second, std::shared_ptr& index, const IndexInfo* blockIndex); MBlock& get(); const Block& get() const; inline T& get(size_t pos); inline const T& get(size_t pos) const; std::vector block(const IndexInfo* blockIndex, bool init = false) const; const OperationMaster& block() const; private: std::shared_ptr mkIndex(std::shared_ptr& index); void performAssignment(std::intptr_t blockIndexNum); OpClass const& mSecond; MutableMultiArrayBase& mArrayRef; std::vector& mData; std::shared_ptr mIndex; IndexInfo mIInfo; mutable bType mBlock; }; template class ConstOperationRoot : /*public OperationBase,*/ public OperationTemplate > { public: typedef T value_type; typedef OperationBase OB; typedef OperationTemplate > OT; typedef ContainerRange CRange; typedef typename CRange::IndexType IndexType; typedef Block bType; static size_t rootNum() { return 1; } static const size_t SIZE = 1; ConstOperationRoot(const MultiArrayBase& ma, const std::shared_ptr&... indices); const Block& get() const; template inline const T& get(const ET& pos) const; std::vector block(const IndexInfo* blockIndex, bool init = false) const; const ConstOperationRoot& block() const; std::tuple rootSteps(std::intptr_t iPtrNum = 0) const; // nullptr for simple usage with decltype template Expr loop(Expr exp) const; private: std::shared_ptr mkIndex(const MultiArrayBase& ma, const std::shared_ptr&... indices); MultiArrayBase const& mArrayRef; const std::vector& mData; std::shared_ptr mIndex; IndexInfo mIInfo; mutable bType mBlock; }; template class OperationRoot : public OperationTemplate > { public: typedef T value_type; typedef OperationBase OB; typedef OperationTemplate > OT; typedef ContainerRange CRange; typedef typename CRange::IndexType IndexType; typedef MBlock bType; static size_t rootNum() { return 1; } static const size_t SIZE = 1; OperationRoot(MutableMultiArrayBase& ma, const std::shared_ptr&... indices); template OperationMaster operator=(const OpClass& in); const MBlock& get() const; MBlock& get(); template inline const T& get(const ET& pos) const; template inline T& get(const ET& pos); OperationRoot& set(const IndexInfo* blockIndex); std::vector block(const IndexInfo* blockIndex, bool init = false) const; const OperationRoot& block() const; std::tuple rootSteps(std::intptr_t iPtrNum = 0) const; // nullptr for simple usage with decltype template Expr loop(Expr exp) const; private: std::shared_ptr mkIndex(const MultiArrayBase& ma, const std::shared_ptr&... indices); MutableMultiArrayBase& mArrayRef; std::vector& mData; std::shared_ptr mIndex; IndexInfo mIInfo; mutable bType mBlock; const IndexInfo* mBlockII; // predefine to save time }; template size_t sumRootNum() { return typename Op::rootNum(); } template size_t sumRootNum() { return typename Op1::rootNum() + sumRootNum(); } template struct RootSumN { template struct rs { static const size_t SIZE = Op1::SIZE + RootSumN::template rs::SIZE; }; }; template <> struct RootSumN<0> { template struct rs { static const size_t SIZE = Op1::SIZE; }; }; template struct RootSum { static const size_t SIZE = RootSumN::template rs::SIZE; }; template class Operation : public OperationTemplate > { public: typedef T value_type; typedef OperationBase OB; typedef OperationTemplate > OT; typedef OpFunction F; typedef BlockResult bType; static size_t rootNum() { return sumRootNum(); } static const size_t SIZE = RootSum::SIZE; private: std::tuple mOps; mutable bType mRes; public: typedef decltype(PackNum::mkStepTuple(0, mOps)) ETuple; //typedef decltype(PackNum::template mkLoopType) LType; Operation(const Ops&... ops); const BlockResult& get() const; template inline T get(const ET& pos) const; std::vector block(const IndexInfo* blockIndex, bool init = false) const; const Operation& block() const; auto rootSteps(std::intptr_t iPtrNum = 0) const // nullptr for simple usage with decltype -> decltype(PackNum::mkStepTuple(iPtrNum, mOps)); template auto loop(Expr exp) const -> decltype(PackNum::mkLoop( mOps, exp)); }; template class Contraction : public OperationTemplate > { public: typedef T value_type; typedef OperationTemplate > OT; typedef BlockResult bType; static size_t rootNum() { return typename Op::rootNum(); } static const size_t SIZE = Op::SIZE; private: const Op& mOp; std::shared_ptr mInd; mutable bType mRes; public: typedef decltype(mOp.rootSteps(0)) ETuple; Contraction(const Op& op, std::shared_ptr ind); const BlockResult& get() const; template inline T get(const ET& pos) const; std::vector block(const IndexInfo* blockIndex, bool init = false) const; const Contraction& block() const; auto rootSteps(std::intptr_t iPtrNum = 0) const // nullptr for simple usage with decltype -> decltype(mOp.rootSteps(iPtrNum)); template auto loop(Expr&& exp) const -> decltype(mInd->iforh(exp))&&; }; } /* ========================= * * --- TEMPLATE CODE --- * * ========================= */ namespace MultiArrayTools { namespace { using namespace MultiArrayHelper; } template Block makeBlock(const T* vec, size_t stepSize, size_t blockSize) { return Block(vec, 0, blockSize, stepSize); } template MBlock makeBlock(T* vec, size_t stepSize, size_t blockSize) { return MBlock(vec, 0, blockSize, stepSize); } // dont use this for now !! template std::shared_ptr seekBlockIndex(std::shared_ptr ownIdx, const OpClass& second) { assert(0); // dont use this for now !! std::vector > ivec; seekIndexInst(ownIdx, ivec); std::map, std::vector > mp; for(auto& xx: ivec){ mp[xx] = second.block(xx); } // seek minimal number of VALUEs => guarantees absence of conflicting blocks minimizeAppearanceOfType(mp, BlockType::VALUE); // seek mininmal number of SPLITs => maximal vectorization possible minimizeAppearanceOfType(mp, BlockType::SPLIT); return mp.begin()->first; } template const IndexInfo* seekBlockIndex(const IndexInfo* ownII, const OpClass& second) { const IndexInfo* ii = ownII; while(ii->type() == IndexType::CONT or ii->type() == IndexType::MULTI){ ii = ii->getPtr(ii->dim()-1); } return ii; } /*************************** * OperationTemplate * ***************************/ template template auto OperationTemplate::operator+(const Second& in) const -> Operation,OperationClass,Second> { return Operation,OperationClass,Second>(THIS(), in); } template template auto OperationTemplate::operator-(const Second& in) const -> Operation,OperationClass,Second> { return Operation,OperationClass,Second>(THIS(), in); } template template auto OperationTemplate::operator*(const Second& in) const -> Operation,OperationClass,Second> { return Operation,OperationClass,Second>(THIS(), in); } template template auto OperationTemplate::operator/(const Second& in) const -> Operation,OperationClass,Second> { return Operation,OperationClass,Second>(THIS(), in); } template template auto OperationTemplate::c(std::shared_ptr& ind) const -> Contraction { return Contraction(THIS(), ind); } /***************************************** * OperationMaster::AssignmentExpr * *****************************************/ template OperationMaster::AssignmentExpr:: AssignmentExpr(OperationMaster& m, const OpClass& sec) : mM(m), mSec(sec) {} /* template inline void OperationMaster::AssignmentExpr:: operator()(size_t start) { }*/ template inline void OperationMaster::AssignmentExpr:: operator()(size_t start, ExtType last) { mM.get(start) = mSec.template get(last); } template typename OperationMaster::AssignmentExpr::ExtType OperationMaster::AssignmentExpr:: rootSteps(std::intptr_t iPtrNum) { return mSec.rootSteps(iPtrNum); } /************************* * OperationMaster * *************************/ template OperationMaster:: OperationMaster(MutableMultiArrayBase& ma, const OpClass& second, std::shared_ptr& index) : mSecond(second), mArrayRef(ma), mData(mArrayRef.datav()), mIndex(mkIndex(index)), mIInfo(*mIndex) { auto blockIndex = seekBlockIndex( &mIInfo, second); std::intptr_t blockIndexNum = blockIndex->getPtrNum(); block(blockIndex, true); second.block(blockIndex, true); performAssignment(blockIndexNum); } template OperationMaster:: OperationMaster(MutableMultiArrayBase& ma, const OpClass& second, std::shared_ptr& index, const IndexInfo* blockIndex) : mSecond(second), mArrayRef(ma), mData(mArrayRef.datav()), mIndex(mkIndex(index)), mIInfo(*mIndex) { std::intptr_t blockIndexNum = blockIndex->getPtrNum(); second.block(blockIndex, true); performAssignment(blockIndexNum); } template std::shared_ptr::IndexType> OperationMaster:: mkIndex(std::shared_ptr& index) { MultiRangeFactory mrf( index->range() ); std::shared_ptr > mr = std::dynamic_pointer_cast >( mrf.create() ); auto i = std::make_shared( mr->begin() ); (*i) = *index; return i; } template void OperationMaster::performAssignment(std::intptr_t blockIndexNum) { #define XX_USE_NEW_LOOP_ROUTINE_XX #ifdef XX_USE_NEW_LOOP_ROUTINE_XX // === N E W === AssignmentExpr ae(*this, mSecond); // Expression to be executed within loop //const auto hiddenLoop = mSecond.loop(AssignmentExpr(*this, mSecond)); // hidden loop within 'mSecond' e.g. contractions const auto loop = mIndex->ifor ( mSecond.template loop ( std::move(ae) ) ); // init overall loop(s) loop(); // execute overall loop(s) and so internal hidden loops and so the inherited expressions #else // === O L D === for(*mIndex = 0; mIndex->pos() != mIndex->max(); mIndex->pp(blockIndexNum) ){ block(); get() = mSecond.get(); } #endif } template MBlock& OperationMaster::get() { return mBlock; } template const Block& OperationMaster::get() const { return mBlock; } template inline T& OperationMaster::get(size_t pos) { //assert(pos < mIndex->max()); //if(pos >= mIndex->max()) { VCHECK(pos); VCHECK(mIndex->max()); assert(0); } //VCHECK(pos); return mData[pos]; } template inline const T& OperationMaster::get(size_t pos) const { //VCHECK(pos); return mData[pos]; } template std::vector OperationMaster::block(const IndexInfo* blockIndex, bool init) const { std::vector btv(1, getBlockType( &mIInfo, blockIndex, true) ); if(init){ mBlock = makeBlock(mArrayRef.data(), btv[0].second, blockIndex->max()); } return btv; } template const OperationMaster& OperationMaster::block() const { mBlock.set( mIndex->pos() ); return *this; } /**************************** * ConstOperationRoot * ****************************/ template ConstOperationRoot:: ConstOperationRoot(const MultiArrayBase& ma, const std::shared_ptr&... indices) : //OperationTemplate >(this), mArrayRef(ma), mData(mArrayRef.datav()), mIndex( mkIndex(ma,indices...) ), mIInfo(*mIndex) {} template std::shared_ptr::IndexType> ConstOperationRoot:: mkIndex(const MultiArrayBase& ma, const std::shared_ptr&... indices) { auto i = std::make_shared( ma.range() ); (*mIndex)(indices...); return i; } template const Block& ConstOperationRoot::get() const { block(); return mBlock; } template template inline const T& ConstOperationRoot::get(const ET& pos) const { return mData[std::get(pos)]; } template std::vector ConstOperationRoot::block(const IndexInfo* blockIndex, bool init) const { std::vector btv(1, getBlockType( &mIInfo, blockIndex, true) ); if(init){ mBlock = makeBlock(mArrayRef.data(), btv[0].second, blockIndex->max()); } return btv; } template const ConstOperationRoot& ConstOperationRoot::block() const { mBlock.set( (*mIndex)().pos() ); return *this; } template std::tuple ConstOperationRoot::rootSteps(std::intptr_t iPtrNum) const { return std::tuple(0ul); // !!!!!! } template template Expr ConstOperationRoot::loop(Expr exp) const { return exp; } /*********************** * OperationRoot * ***********************/ template OperationRoot:: OperationRoot(MutableMultiArrayBase& ma, const std::shared_ptr&... indices) : //OperationTemplate >(this), mArrayRef(ma), mData(mArrayRef.datav()), mIndex( mkIndex( ma, indices... ) ), mIInfo(*mIndex), mBlockII(nullptr) {} template std::shared_ptr::IndexType> OperationRoot:: mkIndex(const MultiArrayBase& ma, const std::shared_ptr&... indices) { auto i = std::make_shared( ma.range() ); (*mIndex)(indices...); return i; } template template OperationMaster OperationRoot::operator=(const OpClass& in) { if(mBlockII != nullptr){ return OperationMaster(mArrayRef, in, mIndex, mBlockII); } else { return OperationMaster(mArrayRef, in, mIndex); } } template const MBlock& OperationRoot::get() const { block(); return mBlock; } template MBlock& OperationRoot::get() { block(); return mBlock; } template template inline const T& OperationRoot::get(const ET& pos) const { return mData[std::get( pos )]; } template template inline T& OperationRoot::get(const ET& pos) { return mData[std::get( pos )]; } template OperationRoot& OperationRoot::set(const IndexInfo* blockIndex) { mBlockII = blockIndex; return *this; } template std::vector OperationRoot::block(const IndexInfo* blockIndex, bool init) const { std::vector btv(1, getBlockType( &mIInfo, blockIndex, true) ); if(init){ mBlock = makeBlock(mArrayRef.data(), btv[0].second, blockIndex->max()); } return btv; } template const OperationRoot& OperationRoot::block() const { mBlock.set( (*mIndex)().pos() ); return *this; } template std::tuple OperationRoot::rootSteps(std::intptr_t iPtrNum) const { return std::tuple(0ul); // !!!!!! } template template Expr OperationRoot::loop(Expr exp) const { return exp; } /******************* * Operation * *******************/ template Operation::Operation(const Ops&... ops) : //OperationTemplate >(this), mOps(ops...) {} template const BlockResult& Operation::get() const { PackNum::template unpackArgs(mRes, mOps); return mRes; } template template inline T Operation::get(const ET& pos) const { typedef std::tuple OpTuple; return PackNum:: template mkOpExpr(pos, mOps); } template std::vector Operation::block(const IndexInfo* blockIndex, bool init) const { std::vector btv; PackNum::makeBlockTypeVec(btv, mOps, blockIndex, init); if(init){ mRes.init(blockIndex->max()); } return btv; } template const Operation& Operation::block() const { //mBlock.set( mIndex->pos() ); return *this; } template auto Operation::rootSteps(std::intptr_t iPtrNum) const -> decltype(PackNum::mkStepTuple(iPtrNum, mOps)) { return PackNum::mkStepTuple(iPtrNum, mOps); } template template auto Operation::loop(Expr exp) const -> decltype(PackNum::mkLoop( mOps, exp )) { return PackNum::mkLoop( mOps, exp ); } /********************* * Contraction * *********************/ template Contraction::Contraction(const Op& op, std::shared_ptr ind) : //OperationTemplate >(this), mOp(op), mInd(ind) {} template const BlockResult& Contraction::get() const { BlockBinaryOpSelf,decltype(mOp.get())> f(mRes); for(*mInd = 0; mInd->pos() != mInd->max(); ++(*mInd)){ f(mOp.get()); } return mRes; } // forward loop !!!! template template inline T Contraction::get(const ET& pos) const { return mOp.template get(pos); } template std::vector Contraction::block(const IndexInfo* blockIndex, bool init) const { if(init){ mRes.init(blockIndex->max()); } return mOp.block(blockIndex, init); } template const Contraction& Contraction::block() const { return *this; } template auto Contraction::rootSteps(std::intptr_t iPtrNum) const -> decltype(mOp.rootSteps(iPtrNum)) { return mOp.rootSteps(iPtrNum); } template template auto Contraction::loop(Expr exp) const -> decltype(mInd->iforh(exp)) { return mInd->iforh(exp); } } #endif