diff --git a/include/aidge/operator/DepthToSpace.hpp b/include/aidge/operator/DepthToSpace.hpp new file mode 100644 index 0000000000000000000000000000000000000000..05d2b2e07bb63c4889f09a072dad7630f18fc708 --- /dev/null +++ b/include/aidge/operator/DepthToSpace.hpp @@ -0,0 +1,111 @@ +/******************************************************************************** + * 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_DEPTHTOSPACE_H_ +#define AIDGE_CORE_OPERATOR_DEPTHTOSPACE_H_ + +#include <array> +#include <memory> +#include <vector> + +#include "aidge/graph/Node.hpp" +#include "aidge/operator/OperatorTensor.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +class DepthToSpace_OpImpl : public OperatorImpl { +public: + DepthToSpace_OpImpl(const Operator& op, const std::string& backend = ""): OperatorImpl(op, backend) {} + void forward() override; +}; + +enum class DepthToSpaceAttr { BlockSize, Mode }; + + +class DepthToSpace_Op : public OperatorTensor, + public Registrable<DepthToSpace_Op, + std::string, + std::shared_ptr<OperatorImpl>(const DepthToSpace_Op &)> { +public: + static const std::string Type; + enum class Mode { DCR, CRD }; + +private: + using Attributes_ = StaticAttributes<DepthToSpaceAttr, std::uint32_t, Mode>; + template <DepthToSpaceAttr e> + using attr = typename Attributes_::template attr<e>; + const std::shared_ptr<Attributes_> mAttributes; + +public: + + DepthToSpace_Op() = delete; + + DepthToSpace_Op(const std::uint32_t blockSize, const Mode mode = Mode::CRD) + : OperatorTensor(Type, {InputCategory::Data}, 1), + mAttributes(std::make_shared<Attributes_>( + attr<DepthToSpaceAttr::BlockSize>(blockSize), + attr<DepthToSpaceAttr::Mode>(mode))) {} + + /** + * @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. + */ + DepthToSpace_Op(const DepthToSpace_Op& op); + + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::DepthToSpace_Op + */ + std::shared_ptr<Operator> clone() const override { + return std::make_shared<DepthToSpace_Op>(*this); + } + + // Data operator[](const char* inputName) override final { + // std::shared_ptr<Tensor> in = (strcmp(inputName, "data")) ? mInputs[0] : + // (strcmp(inputName, "weight") ? mInputs[1] : + // (strcmp(inputName, "bias") ? mInputs[2] : + // nullptr)); + // assert((in!=nullptr) && "No such parameter"); + // return *in; + // } + + + bool forwardDims(bool /*allowDataDependency*/ = false) override final; + + void setBackend(const std::string &name, DeviceIdx_t device = 0) override final; + + inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; } + inline std::uint32_t& blockSize() const { return mAttributes->template getAttr<DepthToSpaceAttr::BlockSize>(); } + inline Mode& mode() const { return mAttributes->template getAttr<DepthToSpaceAttr::Mode>(); } + + static const std::vector<std::string> getInputsName() { + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName() { + return {"data_output"}; + } +}; + +std::shared_ptr<Node> DepthToSpace(const std::uint32_t blockSize, + const DepthToSpace_Op::Mode mode = DepthToSpace_Op::Mode::CRD, + const std::string& name = ""); + +} // namespace Aidge + +namespace { +template <> +const char *const EnumStrings<Aidge::DepthToSpaceAttr>::data[] = { "BlockSize", "Mode" }; +} + +#endif //AIDGE_CORE_OPERATOR_DEPTHTOSPACE_H_ diff --git a/src/operator/DepthToSpace.cpp b/src/operator/DepthToSpace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32db101fc2bc013f0a832d0f5455e5f69cdebc63 --- /dev/null +++ b/src/operator/DepthToSpace.cpp @@ -0,0 +1,90 @@ +/******************************************************************************** + * 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/DepthToSpace.hpp" + +#include <array> +#include <cstddef> // std::size_t +#include <string> +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Types.h" + +void Aidge::DepthToSpace_OpImpl::forward() { + const DepthToSpace_Op& op = dynamic_cast<const DepthToSpace_Op&>(mOp); + // suppose an NCHW Tensor format + + // Get input dimensions + const auto& dims = op.getInput(0)->dims<4>(); + // Assert that c is divisible by blocksize squared + assert(dims[1] % (op.blockSize() * op.blockSize()) == 0 && "Number of channels must be divisible by blocksize squared"); + // get final output dimension + const std::array<DimSize_t, 4> final_dims = op.getOutput(0)->dims<4>(); + + std::size_t b = dims[0]; + std::size_t c = dims[1] / (static_cast<DimSize_t>(op.blockSize()) * static_cast<DimSize_t>(op.blockSize())); + std::size_t h = dims[2]; + std::size_t w = dims[3]; + + // Copt input tensor to output + op.setOutput(0, op.getInput(0)); + + // Step 1: Resize + const std::vector<DimSize_t> resize_dims = + (op.mode() == DepthToSpace_Op::Mode::CRD) ? + std::vector<DimSize_t>({b, c, static_cast<DimSize_t>(op.blockSize()), static_cast<DimSize_t>(op.blockSize()), h, w}) : + std::vector<DimSize_t>({b, static_cast<DimSize_t>(op.blockSize()), static_cast<DimSize_t>(op.blockSize()), c, h, w}); + op.getOutput(0)->resize(resize_dims); + + // Step 2: Transpose + const std::vector<DimSize_t> transpose_order = + (op.mode() == DepthToSpace_Op::Mode::CRD) ? + std::vector<DimSize_t>({0, 1, 4, 2, 5, 3}) : + std::vector<DimSize_t>({0, 3, 4, 1, 5, 2}); + op.getOutput(0)->copyTranspose(*(op.getOutput(0)), transpose_order); + + // Step 3: Final resize + op.getOutput(0)->resize(final_dims); +} + +const std::string Aidge::DepthToSpace_Op::Type = "DepthToSpace"; + + +bool Aidge::DepthToSpace_Op::forwardDims(bool /*allowDataDependency*/) { + if (inputsAssociated()) { + AIDGE_ASSERT(getInput(0)->nbDims() == 4, "{} Operator only accepts 4-D input Tensors.", DepthToSpace_Op::Type); + + // Compute output dims + const std::array<DimSize_t, 4>& inDims = getInput(0)->dims<4>(); + const std::vector<DimSize_t> outDims = + {inDims[0], + inDims[1] / (static_cast<DimSize_t>(blockSize()) * static_cast<DimSize_t>(blockSize())), + inDims[2] * static_cast<DimSize_t>(blockSize()), + inDims[3] * static_cast<DimSize_t>(blockSize())}; + + mOutputs[0]->resize(outDims); + return true; + } + + return false; +} + +void Aidge::DepthToSpace_Op::setBackend(const std::string& name, Aidge::DeviceIdx_t device) { + if (Registrar<DepthToSpace_Op>::exists({name})) { + SET_IMPL_MACRO(DepthToSpace_Op, *this, name); + } + else { + mImpl = std::make_shared<DepthToSpace_OpImpl>(*this); + } + mOutputs[0]->setBackend(name, device); +}