From 4a1c45368e24bc238d26bf46794d590420745962 Mon Sep 17 00:00:00 2001 From: hrouis <houssemeddine.rouis92@gmail.com> Date: Mon, 13 Nov 2023 13:25:28 +0100 Subject: [PATCH] add Transpose operator --- include/aidge/operator/Transpose.hpp | 178 +++++++++++++++++++ python_binding/operator/pybind_Transpose.cpp | 44 +++++ 2 files changed, 222 insertions(+) create mode 100644 include/aidge/operator/Transpose.hpp create mode 100644 python_binding/operator/pybind_Transpose.cpp diff --git a/include/aidge/operator/Transpose.hpp b/include/aidge/operator/Transpose.hpp new file mode 100644 index 000000000..d6170396c --- /dev/null +++ b/include/aidge/operator/Transpose.hpp @@ -0,0 +1,178 @@ +/******************************************************************************** + * 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_TRANSPOSE_H_ +#define AIDGE_CORE_OPERATOR_TRANSPOSE_H_ + +#include <array> +#include <cmath> +#include <numeric> +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/graph/Node.hpp" +#include "aidge/operator/Operator.hpp" +#include "aidge/operator/Producer.hpp" +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +enum class TransposeAttr { OutputDimsOrder }; + +template <DimIdx_t DIM> +class Transpose_Op : public Operator, + public Registrable<Transpose_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const Transpose_Op<DIM> &)>, + public StaticAttributes<TransposeAttr, + std::array<DimSize_t, DIM>> { + public: + // FIXME: change accessibility + std::shared_ptr<Tensor> mInput = std::make_shared<Tensor>(); + const std::shared_ptr<Tensor> mOutput = std::make_shared<Tensor>(); + + public: + static constexpr const char *Type = "Transpose"; + + Transpose_Op() = delete; + + using Attributes_ = StaticAttributes<TransposeAttr, + std::array<DimSize_t, DIM>>; + template <TransposeAttr e> + using attr = typename Attributes_::template attr<e>; + + constexpr Transpose_Op(const std::array<DimSize_t, DIM> &output_dims_order) + : Operator(Type), + Attributes_(attr<TransposeAttr::OutputDimsOrder>(output_dims_order)) { + setDatatype(DataType::Float32); + } + + /** + * @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. + */ + Transpose_Op(const Transpose_Op<DIM>& op) + : Operator(Type), + Attributes_(op), + mOutput(std::make_shared<Tensor>(*op.mOutput)) + { + // cpy-ctor + setDatatype(op.mOutput->dataType()); + mImpl = op.mImpl ? Registrar<Transpose_Op<DIM>>::create(mOutput->getImpl()->backend())(*this) : nullptr; + } + + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::Transpose_Op + */ + std::shared_ptr<Operator> clone() const override { + return std::make_shared<Transpose_Op<DIM>>(*this); + } + + void associateInput(const IOIndex_t inputIdx, std::shared_ptr<Data> data) override final { + assert(inputIdx == 0 && "Transpose operator supports only 1 input"); + assert(strcmp(data->type(), Tensor::Type) == 0 && "input data must be of Tensor type"); + + mInput = std::dynamic_pointer_cast<Tensor>(data); + } + + void computeOutputDims() override final { + if (!mInput->empty()) { + auto attr = (this)->getStaticAttributes(); + const std::array<DimSize_t, DIM>& outDimsOrder = static_cast<const std::array<DimSize_t, DIM>&>(std::get<0>(attr)); + std::vector<DimSize_t> outputDims; + for (std::size_t i = 0; i < DIM; ++i) { + outputDims.push_back(mInput->dims()[outDimsOrder[i]]); + } + mOutput->resize(outputDims); + } + } + + bool outputDimsForwarded() const override final { return !(mOutput->empty()); } + + + inline Tensor& input(const IOIndex_t /*inputIdx*/) const override final { return *(mInput.get()); } + inline Tensor& output(const IOIndex_t /*outputIdx*/) const override final { return *(mOutput.get()); } + + + inline std::shared_ptr<Tensor> getInput(const IOIndex_t inputIdx) const override final { + assert((inputIdx == 0) && "Transpose Operators supports only 1 input"); + (void) inputIdx; // avoid unused warning + return mInput; + } + inline std::shared_ptr<Tensor> getOutput(const IOIndex_t outputIdx) const override final { + assert((outputIdx == 0) && "Transpose Operator has only 1 output"); + (void) outputIdx; // avoid unused warning + return mOutput; + } + + + std::shared_ptr<Data> getRawInput(const IOIndex_t inputIdx) const override final { + assert((inputIdx == 0) && "Transpose Operator supports only 1 input"); + return std::static_pointer_cast<Data>(mInput); + } + std::shared_ptr<Data> getRawOutput(const IOIndex_t outputIdx) const override final { + assert((outputIdx == 0) && "Transpose operator supports only 1 output"); + (void) outputIdx; // avoid unused warning + return std::static_pointer_cast<Data>(mOutput); + } + + + + void setBackend(const std::string &name) override { + mImpl = Registrar<Transpose_Op<DIM>>::create(name)(*this); + mOutput->setBackend(name); + + // FIXME: temporary workaround + mInput->setBackend(name); + } + + void setDatatype(const DataType &datatype) override { + mOutput->setDatatype(datatype); + + // FIXME: temporary workaround + mInput->setDatatype(datatype); + } + + inline IOIndex_t nbInputs() const noexcept override final { return 1; } + inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } + inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + 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> Transpose(const std::array<DimSize_t, DIM> &output_dims_order, + const std::string& name = "") { + // FIXME: properly handle default w&b initialization in every cases + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Transpose, not supported"); + return std::make_shared<Node>(std::make_shared<Transpose_Op<static_cast<DimIdx_t>(DIM)>>(output_dims_order), name); +} + +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction +template <DimSize_t DIM> +inline std::shared_ptr<Node> Transpose( + DimSize_t const (&output_dims_order)[DIM], + const std::string& name = "") { + static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Transpose, not supported"); + return Transpose(to_array(output_dims_order), name); +} +} // namespace Aidge + +namespace { +template <> +const char *const EnumStrings<Aidge::TransposeAttr>::data[] = {"OutputDimsOrder"}; +} + +#endif /* AIDGE_CORE_OPERATOR_TRANSPOSE_H_ */ diff --git a/python_binding/operator/pybind_Transpose.cpp b/python_binding/operator/pybind_Transpose.cpp new file mode 100644 index 000000000..59895cc44 --- /dev/null +++ b/python_binding/operator/pybind_Transpose.cpp @@ -0,0 +1,44 @@ +/******************************************************************************** + * 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 <pybind11/pybind11.h> +#include <pybind11/stl.h> + +#include <string> +#include <vector> +#include <array> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/operator/Transpose.hpp" +#include "aidge/operator/Operator.hpp" +#include "aidge/utils/Types.h" +#include "aidge/data/Tensor.hpp" + +namespace py = pybind11; +namespace Aidge { + +template <DimIdx_t DIM> +void init_Transpose(py::module &m) { + py::class_<Transpose_Op<DIM>, std::shared_ptr<Transpose_Op<DIM>>, Operator, Attributes>( + m, ("TransposeOp" + std::to_string(DIM) + "D").c_str(), py::multiple_inheritance()) + .def("get_inputs_name", &Transpose_Op<DIM>::getInputsName) + .def("get_outputs_name", &Transpose_Op<DIM>::getOutputsName); + + m.def(("Transpose" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& output_dims_order, + const std::string& name) { + AIDGE_ASSERT(output_dims_order.size() == DIM, "output_dims_order size [%ld] does not match DIM [%d]", output_dims_order.size(), DIM); + return Transpose<DIM>(to_array<DIM>(output_dims_order.begin()), name); + }, py::arg("output_dims_order"), + py::arg("name") = ""); + +} + +} // namespace Aidge -- GitLab