diff --git a/aidge_core/export/node_export.py b/aidge_core/export/node_export.py index bea61551d6b4363d234fba4df6138ccef3154331..477989b037da6f6229bd275ff22974d9ef307848 100644 --- a/aidge_core/export/node_export.py +++ b/aidge_core/export/node_export.py @@ -39,7 +39,11 @@ class ExportNode(ABC): if parent_node is not None: self.inputs_dims.append(self.operator.get_input(idx).dims()) else: - self.inputs_dims.append(None) + print(self.operator.get_input(idx)) + if self.operator.get_input(idx) is not None: + self.inputs_dims.append(self.operator.get_input(idx).dims()) + else: + self.inputs_dims.append(None) for idx, child_node in enumerate(self.node.get_children()): self.outputs.append(child_node) diff --git a/include/aidge/data/Tensor.hpp b/include/aidge/data/Tensor.hpp index cdd2aba1bff6bf3fcca2e6c148fcc71f3d54db71..e686737a44928886f97ce636df8be6c883404e56 100644 --- a/include/aidge/data/Tensor.hpp +++ b/include/aidge/data/Tensor.hpp @@ -331,6 +331,8 @@ class Tensor : public Data, return div_.getOutput(0)->clone(); } + ~Tensor() noexcept; + public: /** * @brief Perform a deep copy of the tensor. diff --git a/include/aidge/filler/Filler.hpp b/include/aidge/filler/Filler.hpp index 51d01d87f338d1c8eb33b7b3ec6194390bfe13bf..fe39771b634278909f7eef20068cb941f9922ab8 100644 --- a/include/aidge/filler/Filler.hpp +++ b/include/aidge/filler/Filler.hpp @@ -9,34 +9,20 @@ * ********************************************************************************/ -#ifndef AIDGE_CORE_FILLER_H_ -#define AIDGE_CORE_FILLER_H_ +#ifndef AIDGE_CORE_FILLER_FILLER_H_ +#define AIDGE_CORE_FILLER_FILLER_H_ +#include <cstdint> // std::uint32_t #include <memory> -#include <random> // normal_distribution, uniform_real_distribution #include "aidge/data/Tensor.hpp" namespace Aidge { -inline void calculateFanInFanOut(std::shared_ptr<Tensor> tensor, - unsigned int& fanIn, unsigned int& fanOut) { - AIDGE_ASSERT( - tensor->nbDims() == 4, - "Tensor need to have 4 dimensions to compute FanIn and FanOut."); - // Warning: This function suppose NCXX data layout. - // Aidge currently only support NCHW but this maybe not be true in the - // future. - DimSize_t batchSize = tensor->dims()[0]; - DimSize_t channelSize = tensor->dims()[1]; - AIDGE_ASSERT(batchSize != 0, - "Cannot calculate FanIn if tensor batch size is 0."); - AIDGE_ASSERT(channelSize != 0, - "Cannot calculate FanOut if tensor channel size is 0."); - fanIn = static_cast<unsigned int>(tensor->size() / batchSize); - fanOut = static_cast<unsigned int>(tensor->size() / channelSize); -} -enum VarianceNorm { FanIn, Average, FanOut }; +void calculateFanInFanOut(std::shared_ptr<Tensor> tensor, + std::uint32_t& fanIn, std::uint32_t& fanOut); + +enum class VarianceNorm { FanIn, Average, FanOut }; template <typename T> void constantFiller(std::shared_ptr<Tensor> tensor, T constantValue); @@ -50,14 +36,15 @@ void uniformFiller(std::shared_ptr<Tensor> tensor, T min, T max); template <typename T> void xavierUniformFiller(std::shared_ptr<Tensor> tensor, T scaling = 1.0, - VarianceNorm varianceNorm = FanIn); + VarianceNorm varianceNorm = VarianceNorm::FanIn); template <typename T> void xavierNormalFiller(std::shared_ptr<Tensor> tensor, T scaling = 1.0, - VarianceNorm varianceNorm = FanIn); + VarianceNorm varianceNorm = VarianceNorm::FanIn); template <typename T> -void heFiller(std::shared_ptr<Tensor> tensor, VarianceNorm varianceNorm = FanIn, +void heFiller(std::shared_ptr<Tensor> tensor, VarianceNorm varianceNorm = VarianceNorm::FanIn, T meanNorm = 0.0, T scaling = 1.0); + } // namespace Aidge -#endif /* AIDGE_CORE_FILLER_H_ */ +#endif /* AIDGE_CORE_FILLER_FILLER_H_ */ diff --git a/include/aidge/operator/AvgPooling.hpp b/include/aidge/operator/AvgPooling.hpp index f9d7454f50b516e25f10cf50af6179e9668ef67c..ed5f4e7fc4edb006fc79da404230c5af3ad89852 100644 --- a/include/aidge/operator/AvgPooling.hpp +++ b/include/aidge/operator/AvgPooling.hpp @@ -13,18 +13,12 @@ #define AIDGE_CORE_OPERATOR_AVGPOOLING_H_ #include <array> -#include <cmath> // std::floor -#include <cstddef> // std::size_t #include <string> -#include <utility> // std::pair #include <vector> -#include "aidge/data/Tensor.hpp" #include "aidge/graph/Node.hpp" #include "aidge/operator/OperatorTensor.hpp" -#include "aidge/operator/Producer.hpp" #include "aidge/utils/ArrayHelpers.hpp" -#include "aidge/utils/ErrorHandling.hpp" #include "aidge/utils/StaticAttributes.hpp" #include "aidge/utils/Registrar.hpp" #include "aidge/utils/Types.h" @@ -60,107 +54,36 @@ public: * @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated). * @param op Operator to copy. */ - AvgPooling_Op(const AvgPooling_Op<DIM>& op) - : OperatorTensor(op), - Attributes_(op) - { - if (op.mImpl) { - SET_IMPL_MACRO(AvgPooling_Op<DIM>, *this, op.backend()); - } else { - mImpl = nullptr; - } - } + AvgPooling_Op(const AvgPooling_Op<DIM>& op); /** * @brief Clone the operator using its copy-constructor. * @see Operator::AvgPooling_Op */ - std::shared_ptr<Operator> clone() const override { + std::shared_ptr<Operator> clone() const override final { return std::make_shared<AvgPooling_Op<DIM>>(*this); } - bool computeOutputDims(bool /*allowDataDependency*/ = false) override final { - // check inputs have been associated - if (!getInput(0)) { - AIDGE_THROW_OR_ABORT(std::runtime_error, "{}: input #0 should be associated with a Tensor", type()); - } - if (!(getInput(0)->empty())) { - std::array<DimSize_t, DIM + 2> outputDims; - const std::array<DimSize_t, DIM + 2> inputDims(getInput(0)->template dims<DIM+2>()); - outputDims[0] = inputDims[0]; - outputDims[1] = inputDims[1]; - - for (std::size_t dim = 0; dim < this->template getAttr<AvgPoolingAttr::KernelDims>().size() ; ++dim) { - outputDims[dim+2] = 1 + static_cast<DimSize_t>( - std::floor(static_cast<float>(inputDims[dim+2] - - this->template getAttr<AvgPoolingAttr::KernelDims>()[dim]) / - static_cast<float>(this->template getAttr<AvgPoolingAttr::StrideDims>()[dim]))); - } - getOutput(0)->resize(outputDims); - return true; - } - return false; - } + bool computeOutputDims(bool /*allowDataDependency*/ = false) override final; - std::vector<std::pair<std::vector<Aidge::DimSize_t>, std::vector<DimSize_t>>> + std::vector<std::pair<std::vector<DimSize_t>, std::vector<DimSize_t>>> computeReceptiveField(const std::vector<DimSize_t>& firstEltDims, const std::vector<DimSize_t>& outputDims, - const IOIndex_t outputIdx = 0) const override final { - if (outputIdx != 0) { - AIDGE_THROW_OR_ABORT(std::runtime_error, "Conv_Op Operator has got only one output Tensor."); - } - if (firstEltDims.size() != outputDims.size()) { - AIDGE_THROW_OR_ABORT(std::runtime_error, "outputDims and firstEltDims should have the size of the output Tensor dimensions."); - } - if ((outputDims.size() == (DIM+2)) && outputDimsForwarded()) { - // Offset - std::vector<DimSize_t> inputIdxDims = firstEltDims; - - for (DimIdx_t i = 0; i < (DIM+2); ++i) { - if (((outputDims[i] + firstEltDims[i]) > mOutputs[0]->template dims<DIM+2>()[i]) || (outputDims[i] == 0)) { - AIDGE_THROW_OR_ABORT(std::runtime_error, "Given outputDim out of range for dimension {} ({} + {})", static_cast<std::size_t>(i), firstEltDims[i], outputDims[i]); - } - } - - // padding is not a parameter of Conv_Op. It is handled in Pad_Op Operator - // Width - std::vector<DimSize_t> inputDims; - inputDims.push_back(outputDims[0]); // same batch value - inputDims.push_back(outputDims[1]); // same channel value - - for (DimIdx_t i = 0; i < DIM; ++i) { - inputDims.push_back((outputDims[2+static_cast<std::size_t>(i)] - 1) - * this->template getAttr<AvgPoolingAttr::StrideDims>()[static_cast<std::size_t>(i)] - + 1 - + (this->template getAttr<AvgPoolingAttr::KernelDims>()[static_cast<std::size_t>(i)] - 1)); - inputIdxDims[2+i] *= this->template getAttr<AvgPoolingAttr::StrideDims>()[static_cast<std::size_t>(i)]; - } - std::vector<std::pair<std::vector<Aidge::DimSize_t>, std::vector<DimSize_t>>> res; - res.push_back(std::pair<std::vector<Aidge::DimSize_t>, std::vector<DimSize_t>>(inputIdxDims, inputDims)); - return res; - } - AIDGE_THROW_OR_ABORT(std::runtime_error, "Given outputDim out of range or output dim not forwarded yet."); - } + const IOIndex_t outputIdx = 0) const override final; - void setBackend(const std::string &name, DeviceIdx_t device = 0) override { - SET_IMPL_MACRO(AvgPooling_Op<DIM>, *this, name); - mOutputs[0]->setBackend(name, device); - } + void setBackend(const std::string &name, DeviceIdx_t device = 0) override final; - static const std::vector<std::string> getInputsName(){ + static const std::vector<std::string> getInputsName() { return {"data_input"}; } - static const std::vector<std::string> getOutputsName(){ + static const std::vector<std::string> getOutputsName() { return {"data_output"}; } }; -template <Aidge::DimIdx_t DIM> -const std::string Aidge::AvgPooling_Op<DIM>::Type = "AvgPooling"; - template <std::array<DimSize_t, 1>::size_type DIM> inline std::shared_ptr<Node> AvgPooling(const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", @@ -178,6 +101,12 @@ inline std::shared_ptr<Node> AvgPooling( static_assert(DIM<=MaxDim,"Too many kernel dimensions required by AvgPooling, not supported"); return AvgPooling(to_array(kernel_dims), name, stride_dims); } + +extern template class Aidge::AvgPooling_Op<1>; +extern template class Aidge::AvgPooling_Op<2>; +extern template class Aidge::AvgPooling_Op<3>; +extern template class Aidge::AvgPooling_Op<4>; + } // namespace Aidge namespace { diff --git a/include/aidge/operator/BatchNorm.hpp b/include/aidge/operator/BatchNorm.hpp index 06609f25f24f8ee7f4bc34945a369eeb23d8e888..a373d1645114571c2d4fd7733fec58cb5a90a721 100644 --- a/include/aidge/operator/BatchNorm.hpp +++ b/include/aidge/operator/BatchNorm.hpp @@ -16,13 +16,11 @@ #include <memory> #include <vector> -#include "aidge/utils/Types.h" -#include "aidge/data/Tensor.hpp" #include "aidge/graph/Node.hpp" #include "aidge/operator/OperatorTensor.hpp" -#include "aidge/operator/Producer.hpp" -#include "aidge/utils/StaticAttributes.hpp" #include "aidge/utils/Registrar.hpp" +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" namespace Aidge { @@ -50,16 +48,7 @@ public: * @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated). * @param op Operator to copy. */ - BatchNorm_Op(const BatchNorm_Op<DIM>& op) - : OperatorTensor(op), - Attributes_(op) - { - if (op.mImpl){ - SET_IMPL_MACRO(BatchNorm_Op<DIM>, *this, op.backend()); - }else{ - mImpl = nullptr; - } - } + BatchNorm_Op(const BatchNorm_Op<DIM>& op); /** * @brief Clone the operator using its copy-constructor. @@ -79,36 +68,9 @@ public: // } - bool computeOutputDims(bool /*allowDataDependency*/ = false) override final { - // check inputs have been associated - bool associated = true; - for (IOIndex_t i = 0; i < nbInputs(); ++i) { - associated &= !(getInput(i)->empty()); - } - if (associated) { - const DimSize_t nbFeatures = getInput(0)->dims()[1]; - for (std::size_t i = nbData(); i < nbInputs(); ++i) { - if(getInput(i)->size() != nbFeatures) { - // /!\ Input size should be handled BEFORE calling this function - // This should raise an error - getInput(i)->resize({getInput(0)->dims()[1]}); - } - } - mOutputs[0]->resize(getInput(0)->dims()); - } - return associated; - } + bool computeOutputDims(bool /*allowDataDependency*/ = false) override final; - void setBackend(const std::string &name, DeviceIdx_t device = 0) override { - SET_IMPL_MACRO(BatchNorm_Op<DIM>, *this, name); - mOutputs[0]->setBackend(name, device); - - // By default, automatically set backend for scale, shift, mean and variance - getInput(1)->setBackend(name, device); - getInput(2)->setBackend(name, device); - getInput(3)->setBackend(name, device); - getInput(4)->setBackend(name, device); - } + void setBackend(const std::string &name, DeviceIdx_t device = 0) override final; static const std::vector<std::string> getInputsName() { return {"data_input", "scale", "shift", "mean", "variance"}; @@ -118,22 +80,19 @@ public: } }; -template <DimIdx_t DIM> -const std::string BatchNorm_Op<DIM>::Type = "BatchNorm"; +extern template class Aidge::BatchNorm_Op<2>; +extern template class Aidge::BatchNorm_Op<3>; +extern template class Aidge::BatchNorm_Op<4>; template <DimSize_t DIM> -inline std::shared_ptr<Node> BatchNorm(const DimSize_t nbFeatures, +std::shared_ptr<Node> BatchNorm(const DimSize_t nbFeatures, const float epsilon = 1.0e-5F, const float momentum = 0.1F, - const std::string& name = "") { - static_assert(DIM<=MaxDim,"Too many kernel dimensions required by BatchNorm, not supported"); - auto batchNorm = std::make_shared<Node>(std::make_shared<BatchNorm_Op<static_cast<DimIdx_t>(DIM)>>(epsilon, momentum), name); - addProducer(batchNorm, 1, {nbFeatures}, "scale"); - addProducer(batchNorm, 2, {nbFeatures}, "shift"); - addProducer(batchNorm, 3, {nbFeatures}, "batch_mean"); - addProducer(batchNorm, 4, {nbFeatures}, "batch_variance"); - return batchNorm; -} + const std::string& name = ""); + +extern template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<2>(const DimSize_t, const float, const float, const std::string&); +extern template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<3>(const DimSize_t, const float, const float, const std::string&); +extern template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<4>(const DimSize_t, const float, const float, const std::string&); } // namespace Aidge namespace { diff --git a/include/aidge/operator/FC.hpp b/include/aidge/operator/FC.hpp index 323dbc56084c8a6ad5a3d51cb2ae82c55520ded1..6a562c59ee88aed5d03db3662aee92ed7bfc21de 100644 --- a/include/aidge/operator/FC.hpp +++ b/include/aidge/operator/FC.hpp @@ -75,10 +75,10 @@ public: void setBackend(const std::string& name, DeviceIdx_t device = 0) override; - static const std::vector<std::string> getInputsName(){ + static const std::vector<std::string> getInputsName() { return {"data_input", "weight", "bias"}; } - static const std::vector<std::string> getOutputsName(){ + static const std::vector<std::string> getOutputsName() { return {"data_output"}; } }; diff --git a/include/aidge/operator/Operator.hpp b/include/aidge/operator/Operator.hpp index 5c6ffad27b3ac6702c8fdbf6113e334e56a2deed..3ee2342297208f6f4e4b061409bc5071c811d2ac 100644 --- a/include/aidge/operator/Operator.hpp +++ b/include/aidge/operator/Operator.hpp @@ -187,10 +187,10 @@ public: inline IOIndex_t nbParam() const noexcept { return mNbParam; }; inline IOIndex_t nbOutputs() const noexcept { return mNbOut; }; - static const std::vector<std::string> getInputsName(){ + static const std::vector<std::string> getInputsName() { return {}; } - static const std::vector<std::string> getOutputsName(){ + static const std::vector<std::string> getOutputsName() { return {}; } }; diff --git a/include/aidge/scheduler/MemoryManager.hpp b/include/aidge/scheduler/MemoryManager.hpp index 9f718e8df341b2303c876b903c4e0339461f88b2..360b01f76e7a9b51f36b83d4d35286eced35016a 100644 --- a/include/aidge/scheduler/MemoryManager.hpp +++ b/include/aidge/scheduler/MemoryManager.hpp @@ -9,8 +9,8 @@ * ********************************************************************************/ -#ifndef AIDGE_MEMORY_MANAGER_H -#define AIDGE_MEMORY_MANAGER_H +#ifndef AIDGE_CORE_SCHEDULER_MEMORY_MANAGER_H +#define AIDGE_CORE_SCHEDULER_MEMORY_MANAGER_H #include <memory> #include <vector> @@ -75,12 +75,12 @@ public: count(count_) { assert(offset <= memSpace->size); - // The preceding assert should allow offset == memSpace->size (see + // The preceding assert should allow offset == memSpace->size (see // issue #63). This means immediate wrapping. // It appends if the final offset computed in reallocate() is at // the end of the previous memPlane and is also at the end of the // memSpace (in case for example of in-place memory op.). - // Instead of bringing the offset back to the beginning of the + // Instead of bringing the offset back to the beginning of the // memSpace, we stay attached to this offset in case the memSpace // grows when a new memPlane is added. @@ -128,7 +128,7 @@ public: // Limit is computed dynamically, as memSpace->size may increase after // the creation of this memory space. This is actually necessary to - // ensure that the memory wrapping works correctly, because when + // ensure that the memory wrapping works correctly, because when // computing the margin required for the wrapping, it is assumed that // the previous layer wrapping extends to the full memory space size. inline unsigned int getLimit() const { @@ -193,7 +193,11 @@ public: typedef std::map<std::shared_ptr<Node>, std::vector<MemoryPlane>, CompByNodeName> MemMap_T; +public: MemoryManager(): mClock(0) {} + ~MemoryManager() noexcept; + +public: /// Generates a new MemorySpace std::shared_ptr<MemorySpace> reserve(unsigned int size, const std::set<std::shared_ptr<Node> >& @@ -246,7 +250,7 @@ public: unsigned int stride = 0, unsigned int length = 1, unsigned int count = 1); - /// Generate a new MemoryPlane in an existing MemorySpace, associated to a + /// Generate a new MemoryPlane in an existing MemorySpace, associated to a /// Node unsigned int reallocate(std::shared_ptr<MemorySpace> memSpace, const std::shared_ptr<Node>& node, @@ -321,4 +325,4 @@ const char* const EnumStrings<Aidge::MemoryManager::OptimizeStrategy>::data[] "OptimizeMaxHoleMaxLifetimeFirst"}; } -#endif // AIDGE_MEMORY_MANAGER_H +#endif // AIDGE_CORE_SCHEDULER_MEMORY_MANAGER_H diff --git a/include/aidge/scheduler/ParallelScheduler.hpp b/include/aidge/scheduler/ParallelScheduler.hpp index d471c65ff2d3e8a81c3992d1df06ba387559025e..0b6f963d61bf0079a9a32bd335ba765788aba2a5 100644 --- a/include/aidge/scheduler/ParallelScheduler.hpp +++ b/include/aidge/scheduler/ParallelScheduler.hpp @@ -9,8 +9,8 @@ * ********************************************************************************/ -#ifndef AIDGE_PARALLELSCHEDULER_H_ -#define AIDGE_PARALLELSCHEDULER_H_ +#ifndef AIDGE_CORE_SCHEDULER_PARALLELSCHEDULER_H_ +#define AIDGE_CORE_SCHEDULER_PARALLELSCHEDULER_H_ #include <chrono> #include <memory> @@ -41,4 +41,4 @@ public: }; } // namespace Aidge -#endif /* AIDGE_PARALLELSCHEDULER_H_ */ +#endif /* AIDGE_CORE_SCHEDULER_PARALLELSCHEDULER_H_ */ diff --git a/include/aidge/scheduler/Scheduler.hpp b/include/aidge/scheduler/Scheduler.hpp index 79eeefb2b7e4c0ba94cc062955d0bcc326ffe468..2f8fbb7aeb6562e0dd309f8f53def6d0fed5a08a 100644 --- a/include/aidge/scheduler/Scheduler.hpp +++ b/include/aidge/scheduler/Scheduler.hpp @@ -9,20 +9,20 @@ * ********************************************************************************/ -#ifndef AIDGE_SCHEDULER_H_ -#define AIDGE_SCHEDULER_H_ +#ifndef AIDGE_CORE_SCHEDULER_SCHEDULER_H_ +#define AIDGE_CORE_SCHEDULER_SCHEDULER_H_ +#include <cstddef> // std::size_t #include <chrono> +#include <map> #include <memory> #include <set> #include <string> #include <vector> -#include <map> - -#include "aidge/utils/Types.h" #include "aidge/data/Tensor.hpp" #include "aidge/scheduler/MemoryManager.hpp" +#include "aidge/utils/Types.h" namespace Aidge { class Node; @@ -33,30 +33,36 @@ protected: struct StaticSchedulingElement { StaticSchedulingElement( std::shared_ptr<Node> node_, - size_t early_ = static_cast<size_t>(-1), - size_t late_ = static_cast<size_t>(-1)) + std::size_t early_ = static_cast<std::size_t>(-1), + std::size_t late_ = static_cast<std::size_t>(-1)) : node(node_), early(early_), late(late_) {} std::shared_ptr<Node> node; - size_t early; - size_t late; + std::size_t early; + std::size_t late; std::vector<std::shared_ptr<StaticSchedulingElement>> earlierThan; std::vector<std::shared_ptr<StaticSchedulingElement>> laterThan; }; + /** + * @brief Node with its start/end execution time stored for later display. + */ struct SchedulingElement { SchedulingElement( std::shared_ptr<Node> node_, std::chrono::time_point<std::chrono::high_resolution_clock> start_, std::chrono::time_point<std::chrono::high_resolution_clock> end_) : node(node_), start(start_), end(end_) {} - + ~SchedulingElement() noexcept = default; std::shared_ptr<Node> node; std::chrono::time_point<std::chrono::high_resolution_clock> start; std::chrono::time_point<std::chrono::high_resolution_clock> end; }; - +public: struct PriorProducersConsumers { + PriorProducersConsumers(); + PriorProducersConsumers(const PriorProducersConsumers&); + ~PriorProducersConsumers() noexcept; bool isPrior = false; std::set<std::shared_ptr<Aidge::Node>> requiredProducers; std::set<std::shared_ptr<Aidge::Node>> priorConsumers; @@ -69,10 +75,22 @@ public: { // ctor }; - virtual ~Scheduler() = default; + + virtual ~Scheduler() noexcept; + +public: + /** + * @brief Return a vector of Node ordered by the order they are called by the scheduler. + * @return std::vector<std::shared_ptr<Node>> + */ + std::vector<std::shared_ptr<Node>> getStaticScheduling(std::size_t step = 0) const; + + inline std::shared_ptr<GraphView> graphView() const noexcept { + return mGraphView; + } /** - * Generate full static scheduling of the GraphView. + * @brief Generate full static scheduling of the GraphView. * For each node, an earliest and latest possible execution logical step * is specified. Nodes that may be scheduled at the same logical step have * no data dependency and can be run in parallel. @@ -110,18 +128,21 @@ public: */ void saveSchedulingDiagram(const std::string& fileName) const; + +protected: /** - * @brief Return a vector of Node ordered by the order they are called by the scheduler - * @return std::vector<std::shared_ptr<Node>> + * @brief Getter for the set of children Nodes of the given input Nodes. + * @param producers Set of Nodes for which we want to obtain the set of children Nodes. + * @return std::set<std::shared_ptr<Node>> Children Nodes. */ - std::vector<std::shared_ptr<Node>> getStaticScheduling(size_t step = 0) const; - inline std::shared_ptr<GraphView> getGraphView() const noexcept { - return mGraphView; - } + std::set<std::shared_ptr<Node>> getConsumers(const std::set<std::shared_ptr<Node>>& producers) const; + + Elts_t getNbAvailableData(const std::shared_ptr<Node>& node, const IOIndex_t inputIdx) const; + + PriorProducersConsumers getPriorProducersConsumers(const std::shared_ptr<Node>& node) const; -protected: /** - * Generate an initial base scheduling for the GraphView. + * @brief Generate an initial base scheduling for the GraphView. * The scheduling is entirely sequential and garanteed to be valid w.r.t. * each node producer-consumer model. */ @@ -129,21 +150,15 @@ protected: /** * Fill-in early and late scheduling step from initial base scheduling. - * For each node, specifies the earliest and latest possible execution + * For each node, specifies the earliest and latest possible execution * logical step. */ void generateEarlyLateScheduling(std::vector<std::shared_ptr<StaticSchedulingElement>>& schedule) const; - /** - * @brief Set of layers receiving an input from currently processing layers - * - * @param producers Set of layers ready to run. - * @return std::set<std::shared_ptr<Node>> - */ - std::set<std::shared_ptr<Node>> getConsumers(const std::set<std::shared_ptr<Node>>& producers) const; - Elts_t getNbAvailableData(const std::shared_ptr<Node>& node, const IOIndex_t inputIdx) const; - PriorProducersConsumers getPriorProducersConsumers(const std::shared_ptr<Node>& node) const; +private: + void summarizeConsumerState(const std::shared_ptr<Node>& consumer, const std::string& nodeName) const; +protected: /** @brief Shared ptr to the scheduled graph view */ std::shared_ptr<GraphView> mGraphView; /** @brief Shared ptr to the upper node containing the graph view */ @@ -152,9 +167,9 @@ protected: std::vector<SchedulingElement> mScheduling; /** @brief List of nodes ordered by their */ std::vector<std::vector<std::shared_ptr<StaticSchedulingElement>>> mStaticSchedule; - size_t mStaticScheduleStep = 0; + std::size_t mStaticScheduleStep = 0; mutable std::map<std::shared_ptr<Node>, PriorProducersConsumers> mPriorCache; }; } // namespace Aidge -#endif /* AIDGE_SCHEDULER_H_ */ +#endif /* AIDGE_CORE_SCHEDULER_SCHEDULER_H_ */ diff --git a/include/aidge/scheduler/SequentialScheduler.hpp b/include/aidge/scheduler/SequentialScheduler.hpp index be0a4a991adba0bef40acc26b8d84267a2010d4b..9cf0c2c1877bbbe5930c6b1e39f2a46c33e21d93 100644 --- a/include/aidge/scheduler/SequentialScheduler.hpp +++ b/include/aidge/scheduler/SequentialScheduler.hpp @@ -9,16 +9,15 @@ * ********************************************************************************/ -#ifndef AIDGE_SEQUENTIALSCHEDULER_H_ -#define AIDGE_SEQUENTIALSCHEDULER_H_ +#ifndef AIDGE_CORE_SCHEDULER_SEQUENTIALSCHEDULER_H_ +#define AIDGE_CORE_SCHEDULER_SEQUENTIALSCHEDULER_H_ -#include <chrono> #include <memory> -#include <set> -#include <string> #include <vector> -#include <map> +#include "aidge/data/Tensor.hpp" +#include "aidge/graph/GraphView.hpp" +#include "aidge/graph/Node.hpp" #include "aidge/scheduler/Scheduler.hpp" namespace Aidge { @@ -27,23 +26,26 @@ namespace Aidge { */ class SequentialScheduler : public Scheduler { public: - enum SchedulingPolicy { + enum class SchedulingPolicy { Default, AsSoonAsPossible, AsLateAsPossible }; +public: SequentialScheduler(std::shared_ptr<GraphView> graphView, std::shared_ptr<Node> upperNode = nullptr) : Scheduler(graphView, upperNode), - mSchedulingPolicy(Default) + mSchedulingPolicy(SchedulingPolicy::Default) { // ctor }; + + ~SequentialScheduler() = default; + +public: inline void setSchedulingPolicy(SchedulingPolicy policy) { mSchedulingPolicy = policy; } - ~SequentialScheduler() = default; - /** * @brief Run the provided Computational Graph with a batch of data */ @@ -59,4 +61,4 @@ private: }; } // namespace Aidge -#endif /* AIDGE_SEQUENTIALSCHEDULER_H_ */ +#endif /* AIDGE_CORE_SCHEDULER_SEQUENTIALSCHEDULER_H_ */ diff --git a/include/aidge/scheduler/ThreadPool.hpp b/include/aidge/scheduler/ThreadPool.hpp index 5f2d9192def412d6084abc4eddf36ec31fe3aa84..e016ad4f3eead6e5bfd6d2b994e6e9cb43e61af9 100644 --- a/include/aidge/scheduler/ThreadPool.hpp +++ b/include/aidge/scheduler/ThreadPool.hpp @@ -9,8 +9,8 @@ * ********************************************************************************/ -#ifndef AIDGE_THREADPOOL_H_ -#define AIDGE_THREADPOOL_H_ +#ifndef AIDGE_CORE_SCHEDULER_THREADPOOL_H_ +#define AIDGE_CORE_SCHEDULER_THREADPOOL_H_ #include <thread> #include <mutex> @@ -39,4 +39,4 @@ private: }; } // namespace Aidge -#endif /* AIDGE_THREADPOOL_H_ */ +#endif /* AIDGE_CORE_SCHEDULER_THREADPOOL_H_ */ diff --git a/include/aidge/utils/Log.hpp b/include/aidge/utils/Log.hpp index f20a619c21f611fdbff9ce0cd4c912c0fcd54a9d..a01f81629c8425f9d860bf1ea03bfe421dbd04fa 100644 --- a/include/aidge/utils/Log.hpp +++ b/include/aidge/utils/Log.hpp @@ -27,6 +27,17 @@ namespace Aidge { */ #define AIDGE_LOG_CONTEXT(...) const Log::Context logContext_##__LINE__(__VA_ARGS__) + +template<class U> +static void discard_args(U parg) { + (void)parg; +} +template<class U, class... Us> +static void discard_args(U parg, Us... pargs) { + (void)parg; + discard_args(pargs...); +} + /** * Aidge logging class, for displaying and file logging of events. */ @@ -54,7 +65,7 @@ public: }; /** - * Detailed messages for debugging purposes, providing information helpful + * Detailed messages for debugging purposes, providing information helpful * for developers to trace and identify issues. * Detailed insights of what is appening in an operation, not useful for the * end-user. The operation is performed nominally. @@ -66,11 +77,13 @@ public: #ifndef NDEBUG // only when compiled in Debug log(Debug, fmt::format(std::forward<Args>(args)...)); +#else + discard_args(&args...); #endif } /** - * Messages that provide a record of the normal operation, about + * Messages that provide a record of the normal operation, about * the application's state, progress, or important events. * Reports normal start, end and key steps in an operation. The operation is * performed nominally. @@ -103,7 +116,7 @@ public: } /** - * Signifies a problem or unexpected condition that the application can + * Signifies a problem or unexpected condition that the application can * recover from, but attention is needed to prevent further issues. * The operation could not be performed, but it does not prevent potential * further operations. diff --git a/python_binding/operator/pybind_AvgPooling.cpp b/python_binding/operator/pybind_AvgPooling.cpp index ab52472b4576d4ab4adf05d3fed139ae40c75919..5d72a3507c4926412b48cda42b1c3bcbe10e9460 100644 --- a/python_binding/operator/pybind_AvgPooling.cpp +++ b/python_binding/operator/pybind_AvgPooling.cpp @@ -9,19 +9,18 @@ * ********************************************************************************/ -#include <pybind11/pybind11.h> -#include <pybind11/stl.h> - +#include <array> #include <string> #include <vector> -#include <array> + +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> #include "aidge/backend/OperatorImpl.hpp" #include "aidge/data/Tensor.hpp" #include "aidge/operator/AvgPooling.hpp" #include "aidge/operator/OperatorTensor.hpp" #include "aidge/utils/Types.h" -#include "aidge/data/Tensor.hpp" namespace py = pybind11; namespace Aidge { diff --git a/python_binding/operator/pybind_Operator.cpp b/python_binding/operator/pybind_Operator.cpp index 589bad0be4ebfac10b476990e4501d6c219abbb1..4796917fbe34dbf3b7455841c9e3f1c13ca9c64d 100644 --- a/python_binding/operator/pybind_Operator.cpp +++ b/python_binding/operator/pybind_Operator.cpp @@ -10,11 +10,14 @@ * ********************************************************************************/ +#include <memory> +#include <string> + #include <pybind11/pybind11.h> #include <pybind11/stl.h> #include "aidge/backend/OperatorImpl.hpp" -#include "aidge/data/Tensor.hpp" +#include "aidge/data/Data.hpp" #include "aidge/operator/Operator.hpp" #include "aidge/utils/Types.h" diff --git a/python_binding/operator/pybind_OperatorTensor.cpp b/python_binding/operator/pybind_OperatorTensor.cpp index 301963da29cd985be050059ddae1bed12887d064..abf3f7e8f1a917d99649600a615357be7b7bb272 100644 --- a/python_binding/operator/pybind_OperatorTensor.cpp +++ b/python_binding/operator/pybind_OperatorTensor.cpp @@ -9,13 +9,17 @@ * ********************************************************************************/ +#include <memory> +#include <string> + #include <pybind11/pybind11.h> +#include <pybind11/stl.h> #include "aidge/backend/OperatorImpl.hpp" +#include "aidge/data/Data.hpp" #include "aidge/data/Tensor.hpp" #include "aidge/operator/OperatorTensor.hpp" #include "aidge/operator/Operator.hpp" -#include <pybind11/stl.h> namespace py = pybind11; namespace Aidge { diff --git a/python_binding/utils/pybind_Log.cpp b/python_binding/utils/pybind_Log.cpp index 10a02dcafefe089c8836ee7d4e3a9783a2aa96a6..7b5e7548b3126ed2ebfe3d9243248dc070c54076 100644 --- a/python_binding/utils/pybind_Log.cpp +++ b/python_binding/utils/pybind_Log.cpp @@ -15,7 +15,7 @@ void init_Log(py::module& m){ py::class_<Log>(m, "Log") .def_static("debug", [](const std::string& msg) { Log::debug(msg); }, py::arg("msg"), R"mydelimiter( - Detailed messages for debugging purposes, providing information helpful + Detailed messages for debugging purposes, providing information helpful for developers to trace and identify issues. Detailed insights of what is appening in an operation, not useful for the end-user. The operation is performed nominally. @@ -27,7 +27,7 @@ void init_Log(py::module& m){ )mydelimiter") .def_static("info", [](const std::string& msg) { Log::info(msg); }, py::arg("msg"), R"mydelimiter( - Messages that provide a record of the normal operation, about + Messages that provide a record of the normal operation, about the application's state, progress, or important events. Reports normal start, end and key steps in an operation. The operation is performed nominally. @@ -57,7 +57,7 @@ void init_Log(py::module& m){ )mydelimiter") .def_static("error",[](const std::string& msg) { Log::error(msg); }, py::arg("msg"), R"mydelimiter( - Signifies a problem or unexpected condition that the application can + Signifies a problem or unexpected condition that the application can recover from, but attention is needed to prevent further issues. The operation could not be performed, but it does not prevent potential further operations. @@ -75,21 +75,21 @@ void init_Log(py::module& m){ :param msg: Fatal message. :type msg: str )mydelimiter") - .def_static("setConsoleLevel", &Log::setConsoleLevel, py::arg("level"), + .def_static("set_console_level", &Log::setConsoleLevel, py::arg("level"), R"mydelimiter( Set the minimum log level displayed in the console. :param level: Log level. :type level: Level )mydelimiter") - .def_static("setFileLevel", &Log::setFileLevel, py::arg("level"), + .def_static("set_file_level", &Log::setFileLevel, py::arg("level"), R"mydelimiter( Set the minimum log level saved in the log file. :param level: Log level. :type level: Level )mydelimiter") - .def_static("setFileName", &Log::setFileName, py::arg("fileName"), + .def_static("set_file_name", &Log::setFileName, py::arg("fileName"), R"mydelimiter( Set the log file name. Close the current log file and open the one with the new file name. diff --git a/src/data/Tensor.cpp b/src/data/Tensor.cpp index b350c5bf0fa2b1af6f102c3a74486c159a7505b4..b6aa4f2e50a5a3db8c3965a8e618fcf4f0299fe8 100644 --- a/src/data/Tensor.cpp +++ b/src/data/Tensor.cpp @@ -39,6 +39,10 @@ Aidge::Tensor& Aidge::Tensor::operator=(const Aidge::Tensor& other) { return *this; } + +Aidge::Tensor::~Tensor() noexcept = default; + + void Aidge::Tensor::resize(const std::vector<Aidge::DimSize_t> &dims, std::vector<Aidge::DimSize_t> strides) { // TODO: scalar Tensor not handled if (dims.empty()) { // scalar diff --git a/src/filler/ConstantFiller.cpp b/src/filler/ConstantFiller.cpp index e7db5e4d02b2031e7f5cf6a0203e3c7acbd3b93e..1e992f4a192c2fd629b2c813c902b127f29a2b02 100644 --- a/src/filler/ConstantFiller.cpp +++ b/src/filler/ConstantFiller.cpp @@ -8,15 +8,19 @@ * SPDX-License-Identifier: EPL-2.0 * ********************************************************************************/ -#include <memory> -#include <random> // normal_distribution, uniform_real_distribution #include "aidge/filler/Filler.hpp" + +#include <cstddef> // std::size_t +#include <memory> +#include <string> + #include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" template<typename T> -void Aidge::constantFiller(std::shared_ptr<Aidge::Tensor> tensor, T constantValue){ +void Aidge::constantFiller(std::shared_ptr<Aidge::Tensor> tensor, T constantValue) { AIDGE_ASSERT(tensor->getImpl(), "Tensor got no implementation, cannot fill it."); AIDGE_ASSERT(NativeType<T>::type == tensor->dataType(), "Wrong data type"); diff --git a/src/filler/Filler.cpp b/src/filler/Filler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34e04c2ba84ad493429bceadd54f4fa27df69bcd --- /dev/null +++ b/src/filler/Filler.cpp @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include "aidge/filler/Filler.hpp" + +#include <cstdint> // std::uint32_t +#include <memory> +#include <string> +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Types.h" + + +void Aidge::calculateFanInFanOut(std::shared_ptr<Aidge::Tensor> tensor, + std::uint32_t& fanIn, std::uint32_t& fanOut) { + AIDGE_ASSERT( + tensor->nbDims() == 4, + "Tensor need to have 4 dimensions to compute FanIn and FanOut."); + // Warning: This function suppose NCXX data layout. + // Aidge currently only support NCHW but this maybe not be true in the + // future. + DimSize_t batchSize = tensor->dims()[0]; + DimSize_t channelSize = tensor->dims()[1]; + AIDGE_ASSERT(batchSize != 0, + "Cannot calculate FanIn if tensor batch size is 0."); + AIDGE_ASSERT(channelSize != 0, + "Cannot calculate FanOut if tensor channel size is 0."); + fanIn = static_cast<std::uint32_t>(tensor->size() / batchSize); + fanOut = static_cast<std::uint32_t>(tensor->size() / channelSize); +} diff --git a/src/operator/AvgPooling.cpp b/src/operator/AvgPooling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb868b5ee1ace3a43ed76b4b22424a45e17e13c4 --- /dev/null +++ b/src/operator/AvgPooling.cpp @@ -0,0 +1,114 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include "aidge/operator/AvgPooling.hpp" + +#include <cmath> // std::floor +#include <cstddef> // std::size_t +#include <stdexcept> // std::runtime_error +#include <string> +#include <utility> // std::pair +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" + +template <Aidge::DimIdx_t DIM> +const std::string Aidge::AvgPooling_Op<DIM>::Type = "AvgPooling"; + +template <Aidge::DimIdx_t DIM> +Aidge::AvgPooling_Op<DIM>::AvgPooling_Op(const AvgPooling_Op<DIM>& op): OperatorTensor(op), Attributes_(op) { + if (op.mImpl) { + SET_IMPL_MACRO(AvgPooling_Op<DIM>, *this, op.backend()); + } else { + mImpl = nullptr; + } +} + +template <Aidge::DimIdx_t DIM> +bool Aidge::AvgPooling_Op<DIM>::computeOutputDims(bool /*allowDataDependency*/) { + // check inputs have been associated + if (!getInput(0)) { + AIDGE_THROW_OR_ABORT(std::runtime_error, "{}: input #0 should be associated with a Tensor", type()); + } + if (!(getInput(0)->empty())) { + std::array<DimSize_t, DIM + 2> outputDims; + const std::array<DimSize_t, DIM + 2> inputDims(getInput(0)->template dims<DIM+2>()); + outputDims[0] = inputDims[0]; + outputDims[1] = inputDims[1]; + + for (std::size_t dim = 0; dim < this->template getAttr<AvgPoolingAttr::KernelDims>().size() ; ++dim) { + outputDims[dim+2] = 1 + static_cast<DimSize_t>( + std::floor(static_cast<float>(inputDims[dim+2] - + this->template getAttr<AvgPoolingAttr::KernelDims>()[dim]) / + static_cast<float>(this->template getAttr<AvgPoolingAttr::StrideDims>()[dim]))); + } + getOutput(0)->resize(outputDims); + return true; + } + return false; +} + + +template <Aidge::DimIdx_t DIM> +std::vector<std::pair<std::vector<Aidge::DimSize_t>, std::vector<Aidge::DimSize_t>>> +Aidge::AvgPooling_Op<DIM>::computeReceptiveField(const std::vector<Aidge::DimSize_t>& firstEltDims, + const std::vector<Aidge::DimSize_t>& outputDims, + const Aidge::IOIndex_t outputIdx) const { + if (outputIdx != 0) { + AIDGE_THROW_OR_ABORT(std::runtime_error, "Conv_Op Operator has got only one output Tensor."); + } + if (firstEltDims.size() != outputDims.size()) { + AIDGE_THROW_OR_ABORT(std::runtime_error, "outputDims and firstEltDims should have the size of the output Tensor dimensions."); + } + if ((outputDims.size() == (DIM+2)) && outputDimsForwarded()) { + // Offset + std::vector<DimSize_t> inputIdxDims = firstEltDims; + + for (DimIdx_t i = 0; i < (DIM+2); ++i) { + if (((outputDims[i] + firstEltDims[i]) > mOutputs[0]->template dims<DIM+2>()[i]) || (outputDims[i] == 0)) { + AIDGE_THROW_OR_ABORT(std::runtime_error, "Given outputDim out of range for dimension {} ({} + {})", static_cast<std::size_t>(i), firstEltDims[i], outputDims[i]); + } + } + + // padding is not a parameter of Conv_Op. It is handled in Pad_Op Operator + // Width + std::vector<DimSize_t> inputDims; + inputDims.push_back(outputDims[0]); // same batch value + inputDims.push_back(outputDims[1]); // same channel value + + for (DimIdx_t i = 0; i < DIM; ++i) { + inputDims.push_back((outputDims[2+static_cast<std::size_t>(i)] - 1) + * this->template getAttr<AvgPoolingAttr::StrideDims>()[static_cast<std::size_t>(i)] + + 1 + + (this->template getAttr<AvgPoolingAttr::KernelDims>()[static_cast<std::size_t>(i)] - 1)); + inputIdxDims[2+i] *= this->template getAttr<AvgPoolingAttr::StrideDims>()[static_cast<std::size_t>(i)]; + } + std::vector<std::pair<std::vector<Aidge::DimSize_t>, std::vector<DimSize_t>>> res; + res.push_back(std::pair<std::vector<Aidge::DimSize_t>, std::vector<DimSize_t>>(inputIdxDims, inputDims)); + return res; + } + AIDGE_THROW_OR_ABORT(std::runtime_error, "Given outputDim out of range or output dim not forwarded yet."); +} + + +template <Aidge::DimIdx_t DIM> +void Aidge::AvgPooling_Op<DIM>::setBackend(const std::string &name, Aidge::DeviceIdx_t device) { + SET_IMPL_MACRO(AvgPooling_Op<DIM>, *this, name); + mOutputs[0]->setBackend(name, device); +} + +template class Aidge::AvgPooling_Op<1>; +template class Aidge::AvgPooling_Op<2>; +template class Aidge::AvgPooling_Op<3>; +template class Aidge::AvgPooling_Op<4>; \ No newline at end of file diff --git a/src/operator/BatchNorm.cpp b/src/operator/BatchNorm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..506a870d764d51bd3697710ff460214db4c86ba0 --- /dev/null +++ b/src/operator/BatchNorm.cpp @@ -0,0 +1,91 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include "aidge/operator/BatchNorm.hpp" + +#include <cstddef> // std::size_t +#include <stdexcept> // std::runtime_error +#include <string> +#include <utility> // std::pair +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/Producer.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" + +template <Aidge::DimIdx_t DIM> +const std::string Aidge::BatchNorm_Op<DIM>::Type = "BatchNorm"; + +template <Aidge::DimIdx_t DIM> +Aidge::BatchNorm_Op<DIM>::BatchNorm_Op(const BatchNorm_Op<DIM>& op): OperatorTensor(op), Attributes_(op) { + if (op.mImpl) { + SET_IMPL_MACRO(BatchNorm_Op<DIM>, *this, op.backend()); + } else { + mImpl = nullptr; + } +} + +template <Aidge::DimIdx_t DIM> +bool Aidge::BatchNorm_Op<DIM>::computeOutputDims(bool /*allowDataDependency*/) { + // check inputs have been associated + bool associated = true; + for (IOIndex_t i = 0; i < nbInputs(); ++i) { + associated &= !(getInput(i)->empty()); + } + if (associated) { + const DimSize_t nbFeatures = getInput(0)->dims()[1]; + for (std::size_t i = nbData(); i < nbInputs(); ++i) { + if(getInput(i)->size() != nbFeatures) { + // /!\ Input size should be handled BEFORE calling this function + // This should raise an error + getInput(i)->resize({getInput(0)->dims()[1]}); + } + } + mOutputs[0]->resize(getInput(0)->dims()); + } + return associated; +} + +template <Aidge::DimIdx_t DIM> +void Aidge::BatchNorm_Op<DIM>::setBackend(const std::string &name, Aidge::DeviceIdx_t device) { + SET_IMPL_MACRO(BatchNorm_Op<DIM>, *this, name); + mOutputs[0]->setBackend(name, device); + + // By default, automatically set backend for scale, shift, mean and variance + getInput(1)->setBackend(name, device); + getInput(2)->setBackend(name, device); + getInput(3)->setBackend(name, device); + getInput(4)->setBackend(name, device); +} + +template class Aidge::BatchNorm_Op<2>; +template class Aidge::BatchNorm_Op<3>; +template class Aidge::BatchNorm_Op<4>; + +template <Aidge::DimSize_t DIM> +inline std::shared_ptr<Aidge::Node> Aidge::BatchNorm(const DimSize_t nbFeatures, + const float epsilon, + const float momentum, + const std::string& name) { + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by BatchNorm, not supported"); + auto batchNorm = std::make_shared<Node>(std::make_shared<BatchNorm_Op<static_cast<DimIdx_t>(DIM)>>(epsilon, momentum), name); + addProducer(batchNorm, 1, {nbFeatures}, "scale"); + addProducer(batchNorm, 2, {nbFeatures}, "shift"); + addProducer(batchNorm, 3, {nbFeatures}, "batch_mean"); + addProducer(batchNorm, 4, {nbFeatures}, "batch_variance"); + return batchNorm; +} + +template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<2>(const DimSize_t, const float, const float, const std::string&); +template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<3>(const DimSize_t, const float, const float, const std::string&); +template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<4>(const DimSize_t, const float, const float, const std::string&); \ No newline at end of file diff --git a/src/recipes/FuseBatchNorm.cpp b/src/recipes/FuseBatchNorm.cpp index ac1fc8d7922827217d31385395666db53c401306..76c15a0627ee65ed23c2dc385d9cd3787f9f0979 100644 --- a/src/recipes/FuseBatchNorm.cpp +++ b/src/recipes/FuseBatchNorm.cpp @@ -50,9 +50,9 @@ void Aidge::fuseBatchNorm(std::shared_ptr<Aidge::Node> convNode, const std::shared_ptr<BatchNorm_Op<2>> batchOp = std::static_pointer_cast<BatchNorm_Op<2>>(batchnormNode->getOperator()); - DimSize_t convNbOutChannels; - DimSize_t channelsSize; - std::array<DimSize_t, 2> kernelDims; + DimSize_t convNbOutChannels = 1; + DimSize_t channelsSize = 1; + std::array<DimSize_t, 2> kernelDims = {1,1}; AIDGE_ASSERT(convNode->getOperator()->operatorType() == OperatorType::Tensor, "Operator must be of Tensor type."); std::shared_ptr<OperatorTensor> convOp = std::static_pointer_cast<OperatorTensor>(convNode->getOperator()); if (convNode->type() == Conv_Op<2>::Type) { @@ -66,7 +66,6 @@ void Aidge::fuseBatchNorm(std::shared_ptr<Aidge::Node> convNode, const std::shared_ptr<ConvDepthWise_Op<2>> convOpPtr = std::static_pointer_cast<ConvDepthWise_Op<2>>(convNode->getOperator()); convNbOutChannels = convOpPtr->getAttr<DimSize_t>("Channels"); - channelsSize = 1; kernelDims = convOpPtr->getAttr<std::array<DimSize_t, 2>>("KernelDims"); } diff --git a/src/scheduler/MemoryManager.cpp b/src/scheduler/MemoryManager.cpp index 9599dbf74f4b1044534b94014e16cebe5731c503..6fe0d1f0745a464b8fd61bf634d7105b9d22faf8 100644 --- a/src/scheduler/MemoryManager.cpp +++ b/src/scheduler/MemoryManager.cpp @@ -14,6 +14,8 @@ #include "aidge/scheduler/MemoryManager.hpp" #include "aidge/utils/ErrorHandling.hpp" +Aidge::MemoryManager::~MemoryManager() noexcept = default; + std::shared_ptr<Aidge::MemoryManager::MemorySpace> Aidge::MemoryManager::reserve( unsigned int size, const std::set<std::shared_ptr<Node> >& dependencies) diff --git a/src/scheduler/Scheduler.cpp b/src/scheduler/Scheduler.cpp index b3b2d5e5b3944e64b6df7b8499237477d88d5b50..4e3f9978837120bd01a3de2cfe2d22e33f9d7828 100644 --- a/src/scheduler/Scheduler.cpp +++ b/src/scheduler/Scheduler.cpp @@ -11,21 +11,35 @@ #include "aidge/scheduler/Scheduler.hpp" +#include <algorithm> // std::find, std::find_if, std::max, std::min, std::replace, std::transform +#include <cassert> #include <chrono> +#include <cstddef> // std::size_t +#include <cstdio> // std::fclose, std::fopen +#include <iterator> // std::back_inserter, std::distance +#include <map> #include <memory> #include <set> #include <string> +#include <vector> -#include <fmt/ranges.h> +#include <fmt/core.h> #include <fmt/color.h> +#include <fmt/ranges.h> #include "aidge/graph/GraphView.hpp" #include "aidge/graph/Node.hpp" -#include "aidge/operator/OperatorTensor.hpp" -#include "aidge/utils/Types.h" -#include "aidge/operator/Producer.hpp" #include "aidge/operator/Memorize.hpp" #include "aidge/operator/MetaOperator.hpp" +#include "aidge/operator/OperatorTensor.hpp" +#include "aidge/operator/Producer.hpp" +#include "aidge/utils/Types.h" + + +Aidge::Scheduler::~Scheduler() noexcept = default; +Aidge::Scheduler::PriorProducersConsumers::PriorProducersConsumers() = default; +Aidge::Scheduler::PriorProducersConsumers::PriorProducersConsumers(const PriorProducersConsumers&) = default; +Aidge::Scheduler::PriorProducersConsumers::~PriorProducersConsumers() noexcept = default; void Aidge::Scheduler::generateScheduling() { auto schedule = generateBaseScheduling(); @@ -34,29 +48,37 @@ void Aidge::Scheduler::generateScheduling() { } std::vector<std::shared_ptr<Aidge::Scheduler::StaticSchedulingElement>> Aidge::Scheduler::generateBaseScheduling() const { - // 1) Setup initial consumers list: - // It is the list of input nodes - std::set<std::shared_ptr<Node>> consumers = mGraphView->inputNodes(); - // Plus the list of nodes inside the graph connected to an inner producer - std::set<std::shared_ptr<Node>> producers; - for (const std::shared_ptr<Node>& nodePtr : mGraphView->getNodes()) { - if (nodePtr->type() == Producer_Op::Type) { - producers.insert(nodePtr); - } - } - const auto producersConsumers = getConsumers(producers); - consumers.insert(producersConsumers.begin(), producersConsumers.end()); + // 0) setup useful variables + // map associating each node with string "name (type#rank)" const std::map<std::shared_ptr<Node>, std::string> namePtrTable = mGraphView->getRankedNodesName("{0} ({1}#{3})"); - // Still consumers are consumers that were run by can still consume data. + // consumers that were run by but can still consume data. // They must be run AFTER the remaining consumer to ensure a non-greedy // producers-consumers model! std::set<std::shared_ptr<Node>> stillConsumers; std::vector<std::shared_ptr<StaticSchedulingElement>> schedule; + + // 1) Initialize consumers list: + // 1.1) List of the GraphView's input nodes + std::set<std::shared_ptr<Node>> consumers = mGraphView->inputNodes(); + + // 1.2) List of nodes inside the GraphView connected to an inner Producer + std::set<std::shared_ptr<Node>> producers; + for (const std::shared_ptr<Node>& nodePtr : mGraphView->getNodes()) { + if (nodePtr->type() == Producer_Op::Type) { + for (const auto& child : nodePtr->getChildren()) { + // Do not schedule childs outside current graph! + if (mGraphView->inView(child)) { + consumers.insert(child); + } + } + } + } + do { // 2) From the current consumers list, check if any prior consumer node // is needed. A prior will generally be required for any node consuming @@ -69,8 +91,8 @@ std::vector<std::shared_ptr<Aidge::Scheduler::StaticSchedulingElement>> Aidge::S // in the new priorConsumers list. The initial consumer will become // again a consumer later, by construction. Log::debug("List of consumers with their priors:"); - std::set<std::shared_ptr<Node>> requiredProducers; - std::set<std::shared_ptr<Node>> priorConsumers; + std::set<std::shared_ptr<Node>> requiredProducers; // Priors of type Producer + std::set<std::shared_ptr<Node>> priorConsumers; // Priors of other type mPriorCache.clear(); for (const auto& consumer : consumers) { @@ -119,23 +141,7 @@ std::vector<std::shared_ptr<Aidge::Scheduler::StaticSchedulingElement>> Aidge::S std::set<std::shared_ptr<Node>> runnableConsumers; Log::debug("Updated list of consumers:"); for (const auto& consumer : consumers) { - Log::debug("\t- consumer: {}", fmt::styled(namePtrTable.at(consumer), fg(fmt::color::orange))); - - std::string crLog = "\t\tC/R:\t"; - for (IOIndex_t inId = 0; inId < consumer->nbInputs() - 1; ++inId) { - crLog += fmt::format("{}/{}\n\t\t\t", consumer->getOperator()->getNbConsumedData(inId), - consumer->getOperator()->getNbRequiredData(inId)); - } - crLog += fmt::format("{}/{}", consumer->getOperator()->getNbConsumedData(static_cast<IOIndex_t>(consumer->nbInputs()) - 1), - consumer->getOperator()->getNbRequiredData(static_cast<IOIndex_t>(consumer->nbInputs()) - 1)); - Log::debug("{}", crLog); - - std::string pLog = "\t\tP:\t"; - for (IOIndex_t outId = 0; outId < consumer->nbOutputs() - 1; ++outId) { - pLog += fmt::format("{}\n\t\t\t", consumer->getOperator()->getNbProducedData(outId)); - } - pLog += fmt::format("{}", consumer->getOperator()->getNbProducedData(static_cast<IOIndex_t>(consumer->nbOutputs()) - 1)); - Log::debug("{}", pLog); + summarizeConsumerState(consumer, namePtrTable.at(consumer)); // debug print bool isRunnable = true; for (IOIndex_t inputIdx = 0; inputIdx < consumer->nbInputs(); ++inputIdx) { @@ -184,24 +190,7 @@ std::vector<std::shared_ptr<Aidge::Scheduler::StaticSchedulingElement>> Aidge::S // 7) Update consumers list Log::debug("Updating producer and consumer lists..."); for (const auto& consumer : runnableConsumers) { - Log::debug("\t- consumer: {}", fmt::styled(namePtrTable.at(consumer), fg(fmt::color::orange))); - - std::string crLog = "\t\tC/R:\t"; - for (IOIndex_t inId = 0; inId < consumer->nbInputs() - 1; ++inId) { - crLog += fmt::format("{}/{}\n\t\t\t", consumer->getOperator()->getNbConsumedData(inId), - consumer->getOperator()->getNbRequiredData(inId)); - } - crLog += fmt::format("{}/{}", consumer->getOperator()->getNbConsumedData(static_cast<IOIndex_t>(consumer->nbInputs()) - 1), - consumer->getOperator()->getNbRequiredData(static_cast<IOIndex_t>(consumer->nbInputs()) - 1)); - Log::debug("{}", crLog); - - std::string pLog = "\t\tP:\t"; - for (IOIndex_t outId = 0; outId < consumer->nbOutputs() - 1; ++outId) { - pLog += fmt::format("{}\n\t\t\t", consumer->getOperator()->getNbProducedData(outId)); - } - pLog += fmt::format("{}", consumer->getOperator()->getNbProducedData(static_cast<IOIndex_t>(consumer->nbOutputs()) - 1)); - Log::debug("{}", pLog); - + summarizeConsumerState(consumer, namePtrTable.at(consumer)); // debug print // 7.1) If the current consumer has still data to consume, it will // be put back in the consumers list once the remaining consumers // have been exhausted. @@ -297,16 +286,37 @@ std::vector<std::shared_ptr<Aidge::Scheduler::StaticSchedulingElement>> Aidge::S return schedule; } + +void Aidge::Scheduler::summarizeConsumerState(const std::shared_ptr<Aidge::Node>& consumer, const std::string& nodeName) const { + Log::debug("\t- consumer: {}", fmt::styled(nodeName, fg(fmt::color::orange))); + std::string crLog = "\t\tC/R:\t"; + for (IOIndex_t inId = 0; inId < consumer->nbInputs() - 1; ++inId) { + crLog += fmt::format("{}/{}\n\t\t\t", consumer->getOperator()->getNbConsumedData(inId), + consumer->getOperator()->getNbRequiredData(inId)); + } + crLog += fmt::format("{}/{}", consumer->getOperator()->getNbConsumedData(static_cast<IOIndex_t>(consumer->nbInputs()) - 1), + consumer->getOperator()->getNbRequiredData(static_cast<IOIndex_t>(consumer->nbInputs()) - 1)); + Log::debug("{}", crLog); + + std::string pLog = "\t\tP:\t"; + for (IOIndex_t outId = 0; outId < consumer->nbOutputs() - 1; ++outId) { + pLog += fmt::format("{}\n\t\t\t", consumer->getOperator()->getNbProducedData(outId)); + } + pLog += fmt::format("{}", consumer->getOperator()->getNbProducedData(static_cast<IOIndex_t>(consumer->nbOutputs()) - 1)); + Log::debug("{}", pLog); +} + + void Aidge::Scheduler::generateEarlyLateScheduling(std::vector<std::shared_ptr<StaticSchedulingElement>>& schedule) const { - size_t latest = 0; + std::size_t latest = 0; // Calculate early (logical) start - for (size_t elt = 0; elt < schedule.size(); ++elt) { + for (std::size_t elt = 0; elt < schedule.size(); ++elt) { const auto node = schedule[elt]->node; const auto itNode = std::find_if(schedule.rend() - elt, schedule.rend(), [node](const auto& v) { return (v->node == node); }); // Node can be run the earliest just after its childs were run the last time! - size_t early = 0; + std::size_t early = 0; if (itNode != schedule.rend()) { for (const auto& child : node->getChildren()) { // Find child node next scheduled position @@ -314,7 +324,7 @@ void Aidge::Scheduler::generateEarlyLateScheduling(std::vector<std::shared_ptr<S [child](const auto& v) { return (v->node == child); }); AIDGE_INTERNAL_ASSERT(it != schedule.rend()); - const size_t step = std::distance(schedule.begin(), it.base()) - 1; + const std::size_t step = std::distance(schedule.begin(), it.base()) - 1; early = std::max(early, schedule[step]->early + 1); schedule[step]->earlierThan.push_back(schedule[elt]); } @@ -326,7 +336,7 @@ void Aidge::Scheduler::generateEarlyLateScheduling(std::vector<std::shared_ptr<S const auto it = std::find_if(schedule.rend() - elt, schedule.rend(), [parent](const auto& v) { return (v->node == parent); }); if (it != schedule.rend()) { - const size_t step = std::distance(schedule.begin(), it.base()) - 1; + const std::size_t step = std::distance(schedule.begin(), it.base()) - 1; early = std::max(early, schedule[step]->early + 1); schedule[step]->earlierThan.push_back(schedule[elt]); } @@ -337,13 +347,13 @@ void Aidge::Scheduler::generateEarlyLateScheduling(std::vector<std::shared_ptr<S } // Calculate late (logical) start - for (size_t elt = schedule.size(); elt-- != 0; ) { + for (std::size_t elt = schedule.size(); elt-- != 0; ) { const auto node = schedule[elt]->node; const auto itNode = std::find_if(schedule.begin() + elt + 1, schedule.end(), [node](const auto& v) { return (v->node == node); }); // Node can be run the latest just before its parents are run the next time! - size_t late = latest; + std::size_t late = latest; if (itNode != schedule.end()) { for (const auto& parent : node->getParents()) { // Find child node next scheduled position @@ -351,7 +361,7 @@ void Aidge::Scheduler::generateEarlyLateScheduling(std::vector<std::shared_ptr<S [parent](const auto& v) { return (v->node == parent); }); AIDGE_INTERNAL_ASSERT(it != schedule.end()); - const size_t step = std::distance(schedule.begin(), it); + const std::size_t step = std::distance(schedule.begin(), it); late = std::min(late, schedule[step]->late - 1); schedule[step]->laterThan.push_back(schedule[elt]); } @@ -363,7 +373,7 @@ void Aidge::Scheduler::generateEarlyLateScheduling(std::vector<std::shared_ptr<S const auto it = std::find_if(schedule.begin() + elt + 1, schedule.end(), [child](const auto& v) { return (v->node == child); }); if (it != schedule.end()) { - const size_t step = std::distance(schedule.begin(), it); + const std::size_t step = std::distance(schedule.begin(), it); late = std::min(late, schedule[step]->late - 1); schedule[step]->laterThan.push_back(schedule[elt]); } @@ -389,7 +399,7 @@ void Aidge::Scheduler::resetScheduling() { Aidge::MemoryManager Aidge::Scheduler::generateMemory(bool incProducers, bool wrapAroundBuffer) const { MemoryManager memManager; - for (size_t step = 0; step < mStaticSchedule.size(); ++step) { + for (std::size_t step = 0; step < mStaticSchedule.size(); ++step) { for (const auto& node : getStaticScheduling(step)) { if (!incProducers && node->type() == Producer_Op::Type) { memManager.releaseDependencies(node); @@ -412,10 +422,10 @@ Aidge::MemoryManager Aidge::Scheduler::generateMemory(bool incProducers, bool wr node->name(), node->type()); // By default, specifies a fully monolithic memory block - size_t size = requiredSize.data; - size_t stride = 0; - size_t length = 1; - size_t count = 1; + std::size_t size = requiredSize.data; + std::size_t stride = 0; + std::size_t length = 1; + std::size_t count = 1; if (op->getOutput(outputIdx) && op->getOutput(outputIdx)->dims().size() > 3) { // If it is possible, assume a NCHW layout @@ -428,8 +438,8 @@ Aidge::MemoryManager Aidge::Scheduler::generateMemory(bool incProducers, bool wr // Check if wrap around buffer is possible for this node // (re-using previous node outputs memory for this node outputs). // => only if this node is the only child of its parent(s) - size_t wrapAroundSize = 0; - size_t wrapAroundExtra = 0; + std::size_t wrapAroundSize = 0; + std::size_t wrapAroundExtra = 0; wrapAroundMemPlane.push_back(nullptr); // Select the best parent among all allocable nodes for @@ -575,7 +585,7 @@ void Aidge::Scheduler::saveStaticSchedulingDiagram(const std::string& fileName) fmt::print(fp.get(), "\n"); } -std::vector<std::shared_ptr<Aidge::Node>> Aidge::Scheduler::getStaticScheduling(size_t step) const { +std::vector<std::shared_ptr<Aidge::Node>> Aidge::Scheduler::getStaticScheduling(std::size_t step) const { const auto& staticSchedule = mStaticSchedule.at(step); std::vector<std::shared_ptr<Node>> schedule; std::transform(staticSchedule.begin(), staticSchedule.end(), std::back_inserter(schedule), [](const auto& v) { return v->node; }); @@ -659,24 +669,26 @@ Aidge::Scheduler::PriorProducersConsumers Aidge::Scheduler::getPriorProducersCon if ((node->getOperator()->getNbConsumedData(inputIdx) + node->getOperator()->getNbRequiredData(inputIdx)) > parent.first->getOperator()->getNbProducedData(parent.second)) { + // the node needs more data than the current parent has provided yet if (!mGraphView->inView(parent.first)) { // Do not schedule prior outside the current graph! - return PriorProducersConsumers(); + // return PriorProducersConsumers(); // not scheduled + prior.priorConsumers.insert(node); } - if (parent.first->type() == Producer_Op::Type) { + else if (parent.first->type() == Producer_Op::Type) { prior.requiredProducers.insert(parent.first); prior.priorConsumers.insert(node); } else if (parent.first->type() == Memorize_Op::Type) { // Break cycles - return PriorProducersConsumers(); + return PriorProducersConsumers(); // not scheduled } else { const auto& parentPrior = getPriorProducersConsumers(parent.first); if (!parentPrior.isPrior) { - return PriorProducersConsumers(); + return PriorProducersConsumers(); // not scheduled } else { prior.requiredProducers.insert(parentPrior.requiredProducers.cbegin(), parentPrior.requiredProducers.cend()); diff --git a/src/scheduler/SequentialScheduler.cpp b/src/scheduler/SequentialScheduler.cpp index 1454cb2bac982465248e7761673ad3646165c2dc..801f46ffb0293696dad8a84908bdda2bbd789bfc 100644 --- a/src/scheduler/SequentialScheduler.cpp +++ b/src/scheduler/SequentialScheduler.cpp @@ -47,11 +47,11 @@ void Aidge::SequentialScheduler::forward(bool forwardDims, std::vector<std::shar // Sort static scheduling according to the policy std::vector<std::shared_ptr<StaticSchedulingElement>> staticSchedule(mStaticSchedule.at(mStaticScheduleStep).begin(), mStaticSchedule.at(mStaticScheduleStep).end()); - if (mSchedulingPolicy == AsSoonAsPossible) { + if (mSchedulingPolicy == SchedulingPolicy::AsSoonAsPossible) { std::stable_sort(staticSchedule.begin(), staticSchedule.end(), [](const auto& lhs, const auto& rhs) { return (lhs->early < rhs->early); }); } - else if (mSchedulingPolicy == AsLateAsPossible) { + else if (mSchedulingPolicy == SchedulingPolicy::AsLateAsPossible) { std::stable_sort(staticSchedule.begin(), staticSchedule.end(), [](const auto& lhs, const auto& rhs) { return (lhs->late < rhs->late); }); } diff --git a/unit_tests/scheduler/Test_Scheduler.cpp b/unit_tests/scheduler/Test_Scheduler.cpp index 7eb0290d9d2dadbb7328604332fb84af2a1be941..e2c1a8fcb96256fa8c3f26a3495913bd987de2d4 100644 --- a/unit_tests/scheduler/Test_Scheduler.cpp +++ b/unit_tests/scheduler/Test_Scheduler.cpp @@ -27,7 +27,7 @@ #include "aidge/operator/Producer.hpp" #include "aidge/scheduler/SequentialScheduler.hpp" -using namespace Aidge; +namespace Aidge { TEST_CASE("randomScheduling", "[Scheduler][randomGen]") { const size_t nbTests = 10; @@ -132,3 +132,5 @@ TEST_CASE("randomScheduling", "[Scheduler][randomGen]") { } fmt::print("nbUnicity = {}/{}\n", nbUnicity, nbTests); } + +} // namespace Aidge