diff --git a/include/aidge/operator/Fold.hpp b/include/aidge/operator/Fold.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4fa16305a20910969cfa2d33f35d6bdc422f6564 --- /dev/null +++ b/include/aidge/operator/Fold.hpp @@ -0,0 +1,139 @@ +/******************************************************************************** + * 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 + * + ********************************************************************************/ + +#ifndef AIDGE_CORE_OPERATOR_FOLD_H_ +#define AIDGE_CORE_OPERATOR_FOLD_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/Registrar.hpp" // SET_IMPL_MACRO +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +enum class FoldAttr { OutputDims, StrideDims, DilationDims, KernelDims }; + +template <DimIdx_t DIM> +class Fold_Op : public OperatorTensor, + public Registrable<Fold_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const Fold_Op<DIM> &)>, + public StaticAttributes<FoldAttr, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>> { + +public: + static const std::string Type; + + Fold_Op() = delete; + + using Attributes_ = StaticAttributes<FoldAttr, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>>; + template <FoldAttr e> + using attr = typename Attributes_::template attr<e>; + + constexpr Fold_Op(const std::array<DimSize_t, DIM> &outputDims, + const std::array<DimSize_t, DIM> &kernelDims, + const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1)) + : OperatorTensor(Type, {InputCategory::Data}, 1), + Attributes_(attr<FoldAttr::OutputDims>(outputDims), + attr<FoldAttr::StrideDims>(strideDims), + attr<FoldAttr::DilationDims>(dilationDims), + attr<FoldAttr::KernelDims>(kernelDims)) {} + + /** + * @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. + */ + Fold_Op(const Fold_Op<DIM> &op) + : OperatorTensor(op), + Attributes_(op) + { + if (!op.backend().empty()) { + SET_IMPL_MACRO(Fold_Op<DIM>, *this, op.backend()); + } + else { + mImpl = nullptr; + } + } + + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::Fold_Op + */ + std::shared_ptr<Operator> clone() const override { + return std::make_shared<Fold_Op<DIM>>(*this); + } + + bool forwardDims(bool /*allowDataDependency*/ = false) override final; + + void setBackend(const std::string &name, DeviceIdx_t device = 0) override; + + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } +}; + +template <std::array<DimSize_t, 1>::size_type DIM> +inline std::shared_ptr<Node> Fold(const std::array<DimSize_t, DIM> &outputDims, + const std::array<DimSize_t, DIM> &kernelDims, + const std::string& name = "", + const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1)) { + // FIXME: properly handle default w&b initialization in every cases + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Fold, not supported"); + return std::make_shared<Node>(std::make_shared<Fold_Op<static_cast<DimIdx_t>(DIM)>>(outputDims, kernelDims, strideDims, dilationDims), name); +} + +template <DimSize_t DIM> +inline std::shared_ptr<Node> Fold( + DimSize_t const (&outputDims)[DIM], + DimSize_t const (&kernelDims)[DIM], + const std::string& name = "", + const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1)) { + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Fold, not supported"); + return Fold(to_array(outputDims), to_array(kernelDims), name, strideDims, dilationDims); +} +} // namespace Aidge + +extern template class Aidge::Fold_Op<2>; + +namespace { +template <> +const char *const EnumStrings<Aidge::FoldAttr>::data[] = { + "OutputDims", + "StrideDims", + "DilationDims", + "KernelDims" +}; +} + +#endif /* AIDGE_CORE_OPERATOR_FOLD_H_ */ diff --git a/include/aidge/operator/Unfold.hpp b/include/aidge/operator/Unfold.hpp new file mode 100644 index 0000000000000000000000000000000000000000..778c78da28044e1b6262decb903a8562ad0e5ee1 --- /dev/null +++ b/include/aidge/operator/Unfold.hpp @@ -0,0 +1,142 @@ +/******************************************************************************** + * 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 + * + ********************************************************************************/ + +#ifndef AIDGE_CORE_OPERATOR_UNFOLD_H_ +#define AIDGE_CORE_OPERATOR_UNFOLD_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/Registrar.hpp" // SET_IMPL_MACRO +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +template <DimIdx_t DIM> +class Unfold_OpImpl : public OperatorImpl { +public: + Unfold_OpImpl(const Operator& op, const std::string& backend = ""): OperatorImpl(op, backend) {} + void forward() override; +}; + +enum class UnfoldAttr { StrideDims, DilationDims, KernelDims }; + +template <DimIdx_t DIM> +class Unfold_Op : public OperatorTensor, + public Registrable<Unfold_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const Unfold_Op<DIM> &)>, + public StaticAttributes<UnfoldAttr, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>> { + +public: + static const std::string Type; + + Unfold_Op() = delete; + + using Attributes_ = StaticAttributes<UnfoldAttr, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>, + std::array<DimSize_t, DIM>>; + template <UnfoldAttr e> + using attr = typename Attributes_::template attr<e>; + + constexpr Unfold_Op(const std::array<DimSize_t, DIM> &kernelDims, + const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1)) + : OperatorTensor(Type, {InputCategory::Data}, 1), + Attributes_(attr<UnfoldAttr::StrideDims>(strideDims), + attr<UnfoldAttr::DilationDims>(dilationDims), + attr<UnfoldAttr::KernelDims>(kernelDims)) + { + mImpl = std::make_shared<Unfold_OpImpl<DIM>>(*this); + } + + /** + * @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. + */ + Unfold_Op(const Unfold_Op<DIM> &op) + : OperatorTensor(op), + Attributes_(op) + { + if (!op.backend().empty()) { + SET_IMPL_MACRO(Unfold_Op<DIM>, *this, op.backend()); + } + else { + mImpl = std::make_shared<Unfold_OpImpl<DIM>>(*this); + } + } + + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::Unfold_Op + */ + std::shared_ptr<Operator> clone() const override { + return std::make_shared<Unfold_Op>(*this); + } + + bool forwardDims(bool /*allowDataDependency*/ = false) override final; + + void setBackend(const std::string &name, DeviceIdx_t device = 0) override; + + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } +}; + +template <std::array<DimSize_t, 1>::size_type DIM> +inline std::shared_ptr<Node> Unfold(const std::array<DimSize_t, DIM> &kernelDims, + const std::string& name = "", + const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1)) { + // FIXME: properly handle default w&b initialization in every cases + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Unfold, not supported"); + return std::make_shared<Node>(std::make_shared<Unfold_Op<static_cast<DimIdx_t>(DIM)>>(kernelDims, strideDims, dilationDims), name); +} + +template <DimSize_t DIM> +inline std::shared_ptr<Node> Unfold( + DimSize_t const (&kernelDims)[DIM], + const std::string& name = "", + const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1)) { + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Unfold, not supported"); + return Unfold(to_array(kernelDims), name, strideDims, dilationDims); +} +} // namespace Aidge + +extern template class Aidge::Unfold_Op<2>; + +namespace { +template <> +const char *const EnumStrings<Aidge::UnfoldAttr>::data[] = { + "StrideDims", + "DilationDims", + "KernelDims" +}; +} + +#endif /* AIDGE_CORE_OPERATOR_UNFOLD_H_ */ diff --git a/src/operator/Fold.cpp b/src/operator/Fold.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a57e117ad00d623c57d057fad9772e620c279b1b --- /dev/null +++ b/src/operator/Fold.cpp @@ -0,0 +1,67 @@ +/******************************************************************************** + * 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/Fold.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::Fold_Op<DIM>::Type = "Fold"; + +template <Aidge::DimIdx_t DIM> +bool Aidge::Fold_Op<DIM>::forwardDims(bool /*allowDataDependency*/) { + if (inputsAssociated()) { + auto dims(getInput(0)->dims()); + DimSize_t k = 1; + DimSize_t l = 1; + + for (std::size_t dim = 0; dim < this->template getAttr<FoldAttr::KernelDims>().size() ; ++dim) { + const DimSize_t kernelExtent = this->template getAttr<FoldAttr::DilationDims>()[dim] * + (this->template getAttr<FoldAttr::KernelDims>()[dim] - 1) + 1; + + k *= this->template getAttr<FoldAttr::KernelDims>()[dim]; + l *= 1 + static_cast<DimSize_t>( + floor(static_cast<float>(this->template getAttr<FoldAttr::OutputDims>()[dim] - kernelExtent) / + static_cast<float>(this->template getAttr<FoldAttr::StrideDims>()[dim]))); + } + + AIDGE_ASSERT(dims[dims.size() - 2] % k == 0 , "Fold: input number of channels ({}) is not divisible by the product of provided kernel dims ({})!", + dims[dims.size() - 2], k); + AIDGE_ASSERT(dims[dims.size() - 1] == l, "Fold: mismatch between expected input 3rd dim {} and provided input 3rd dim {}", + dims[dims.size() - 1], l); + + dims[dims.size() - 2] /= k; + dims.pop_back(); + dims.insert(dims.end(), this->template getAttr<FoldAttr::OutputDims>().begin(), this->template getAttr<FoldAttr::OutputDims>().end()); + mOutputs[0]->resize(dims); + return true; + } + + return false; +} + +template <Aidge::DimIdx_t DIM> +void Aidge::Fold_Op<DIM>::setBackend(const std::string &name, Aidge::DeviceIdx_t device) { + SET_IMPL_MACRO(Fold_Op<DIM>, *this, name); + mOutputs[0]->setBackend(name, device); +} + +template class Aidge::Fold_Op<2>; \ No newline at end of file diff --git a/src/operator/MatMul.cpp b/src/operator/MatMul.cpp index 17b4960dfdfc9de199cc25b0119a5cb000bcf48c..5abfff9d8202003cbe5a76a94fab9d9ab5176b6e 100644 --- a/src/operator/MatMul.cpp +++ b/src/operator/MatMul.cpp @@ -53,7 +53,7 @@ bool Aidge::MatMul_Op::forwardDims(bool /*allowDataDependency*/) { dims0.insert(dims0.cbegin(), dims1.size() - dims0.size(), std::size_t(1)); } - AIDGE_ASSERT(dims0[dims_size-1] == dims1[dims_size-2], "Incompatible matrices sizes."); + AIDGE_ASSERT(dims0[dims_size-1] == dims1[dims_size-2], "Incompatible matrices sizes: {} vs {}.", dims0, dims1); std::vector<std::size_t> outDims = std::vector<std::size_t>(dims_size-2, 1); for (std::size_t i = 0; i < dims_size-2; ++i) { diff --git a/src/operator/Unfold.cpp b/src/operator/Unfold.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eed950ac24b370ba379a090e63d839e85fb59af1 --- /dev/null +++ b/src/operator/Unfold.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * 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/Unfold.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> +void Aidge::Unfold_OpImpl<DIM>::forward() { + const Unfold_Op<DIM>& op = dynamic_cast<const Unfold_Op<DIM>&>(mOp); + const auto kernelDims = op.template getAttr<UnfoldAttr::KernelDims>(); + const auto dilationDims = op.template getAttr<UnfoldAttr::DilationDims>(); + const auto strideDims = op.template getAttr<UnfoldAttr::StrideDims>(); + const DimSize_t inHeight = op.getInput(0)->dims()[2]; + const DimSize_t inWidth = op.getInput(0)->dims()[3]; + + const DimSize_t kernelExtentHeight = op.template getAttr<UnfoldAttr::DilationDims>()[0] * + (op.template getAttr<UnfoldAttr::KernelDims>()[0] - 1) + 1; + const DimSize_t outHeight = 1 + static_cast<DimSize_t>( + floor(static_cast<float>(inHeight - kernelExtentHeight) / + static_cast<float>(op.template getAttr<UnfoldAttr::StrideDims>()[0]))); + const DimSize_t kernelExtentWidth = op.template getAttr<UnfoldAttr::DilationDims>()[1] * + (op.template getAttr<UnfoldAttr::KernelDims>()[1] - 1) + 1; + const DimSize_t outWidth = 1 + static_cast<DimSize_t>( + floor(static_cast<float>(inWidth - kernelExtentWidth) / + static_cast<float>(op.template getAttr<UnfoldAttr::StrideDims>()[1]))); + const DimSize_t outChannels = op.getOutput(0)->dims()[1]; + + for (DimSize_t outC = 0; outC < outChannels; ++outC) { + const auto inOffsetH = outC % kernelDims[0]; + const auto inOffsetW = (outC / kernelDims[0]) % kernelDims[1]; + const auto inC = outC / kernelDims[0] / kernelDims[1]; + + for (DimSize_t outH = 0; outH < outHeight; ++outH) { + const auto inH = outH * strideDims[1] + inOffsetH * dilationDims[1]; + + for (DimSize_t outW = 0; outW < outWidth; ++outW) { + const auto inW = outW * strideDims[0] + inOffsetW * dilationDims[0]; + + op.getOutput(0)->getImpl()->copy(op.getInput(0)->getImpl()->rawPtr((inC * inHeight + inH) * inWidth + inW), 1, + (outC * outHeight + outH) * outWidth + outW); + } + } + } +} + +template <Aidge::DimIdx_t DIM> +const std::string Aidge::Unfold_Op<DIM>::Type = "Unfold"; + +template <Aidge::DimIdx_t DIM> +bool Aidge::Unfold_Op<DIM>::forwardDims(bool /*allowDataDependency*/) { + if (inputsAssociated()) { + const std::array<DimSize_t, DIM + 2> inputDims(getInput(0)->template dims<DIM+2>()); + DimSize_t k = 1; + DimSize_t l = 1; + + for (std::size_t dim = 0; dim < this->template getAttr<UnfoldAttr::KernelDims>().size() ; ++dim) { + const DimSize_t kernelExtent = this->template getAttr<UnfoldAttr::DilationDims>()[dim] * + (this->template getAttr<UnfoldAttr::KernelDims>()[dim] - 1) + 1; + + k *= this->template getAttr<UnfoldAttr::KernelDims>()[dim]; + l *= 1 + static_cast<DimSize_t>( + floor(static_cast<float>(inputDims[dim+2] - kernelExtent) / + static_cast<float>(this->template getAttr<UnfoldAttr::StrideDims>()[dim]))); + } + + mOutputs[0]->resize({inputDims[0], inputDims[1] * k, l}); + return true; + } + + return false; +} + +template <Aidge::DimIdx_t DIM> +void Aidge::Unfold_Op<DIM>::setBackend(const std::string &name, Aidge::DeviceIdx_t device) { + if (Registrar<Unfold_Op<DIM>>::exists({name})){ + SET_IMPL_MACRO(Unfold_Op<DIM>, *this, name); + } + else { + mImpl = std::make_shared<Unfold_OpImpl<DIM>>(*this); + } + mOutputs[0]->setBackend(name, device); +} + +template class Aidge::Unfold_OpImpl<2>; +template class Aidge::Unfold_Op<2>; \ No newline at end of file