From 44740599570c420c67a773cfacf0bacb50548f82 Mon Sep 17 00:00:00 2001
From: hrouis <houssemeddine.rouis92@gmail.com>
Date: Wed, 15 Nov 2023 15:04:29 +0100
Subject: [PATCH] add ReduceMean operator

---
 include/aidge/operator/ReduceMean.hpp         | 194 ++++++++++++++++++
 python_binding/operator/pybind_ReduceMean.cpp |  58 ++++++
 2 files changed, 252 insertions(+)
 create mode 100644 include/aidge/operator/ReduceMean.hpp
 create mode 100644 python_binding/operator/pybind_ReduceMean.cpp

diff --git a/include/aidge/operator/ReduceMean.hpp b/include/aidge/operator/ReduceMean.hpp
new file mode 100644
index 000000000..31456f5d9
--- /dev/null
+++ b/include/aidge/operator/ReduceMean.hpp
@@ -0,0 +1,194 @@
+/********************************************************************************
+ * 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_REDUCEMEAN_H_
+#define AIDGE_CORE_OPERATOR_REDUCEMEAN_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 ReduceMeanAttr { Axes, KeepDims };
+
+template <DimIdx_t DIM>
+class ReduceMean_Op : public Operator,
+                public Registrable<ReduceMean_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const ReduceMean_Op<DIM> &)>,
+                public StaticAttributes<ReduceMeanAttr, std::array<DimSize_t, DIM>, DimSize_t> {
+   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 = "ReduceMean";
+
+    ReduceMean_Op() = delete;
+
+    using Attributes_ = StaticAttributes<ReduceMeanAttr, std::array<DimSize_t, DIM>, DimSize_t>;
+    template <ReduceMeanAttr e>
+    using attr = typename Attributes_::template attr<e>;
+
+    constexpr ReduceMean_Op(const std::array<DimSize_t, DIM> &axes, DimSize_t keep_dims)
+        : Operator(Type),
+          Attributes_(attr<ReduceMeanAttr::Axes>(axes),
+                      attr<ReduceMeanAttr::KeepDims>(keep_dims)) {
+        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.
+     */
+    ReduceMean_Op(const ReduceMean_Op<DIM>& op)
+        : Operator(Type),
+          Attributes_(op),
+          mOutput(std::make_shared<Tensor>(*op.mOutput))
+    {
+        // cpy-ctor
+        setDatatype(op.mOutput->dataType());
+        mImpl = op.mImpl ? Registrar<ReduceMean_Op<DIM>>::create(mOutput->getImpl()->backend())(*this) : nullptr;
+    }
+
+    /**
+     * @brief Clone the operator using its copy-constructor.
+     * @see Operator::ReduceMean_Op
+     */
+    std::shared_ptr<Operator> clone() const override {
+        return std::make_shared<ReduceMean_Op<DIM>>(*this);
+    }
+
+    void associateInput(const IOIndex_t inputIdx, std::shared_ptr<Data> data) override final {
+        assert(inputIdx == 0 && "ReduceMean 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()) {
+            std::vector<DimSize_t> outDims;
+            for(std::size_t d=0; d<mInput->dims().size(); ++d)
+            {
+                bool reducedDim =  false;
+                for(std::size_t i=0; i<DIM; ++i)
+                {
+                    if(this->template getAttr<ReduceMeanAttr::Axes>()[i] == d)
+                    {
+                        reducedDim = true;
+                        break;
+                    }
+                }
+                if(!reducedDim)
+                {
+                    if(this->template getAttr<ReduceMeanAttr::KeepDims>())
+                        outDims.push_back(1);
+                }
+                else
+                    outDims.push_back(mInput->dims()[d]);
+            }
+            mOutput->resize(outDims);
+        }
+    }
+
+    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 && "ReduceMean 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) && "ReduceMean 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 && "ReduceMean Operators supports only 1 input");
+        (void) inputIdx; // avoid unused warning
+        return std::static_pointer_cast<Data>(mInput);
+    }
+    std::shared_ptr<Data> getRawOutput(const IOIndex_t outputIdx) const override final {
+        assert(outputIdx == 0 && "ReduceMean 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<ReduceMean_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> ReduceMean(const std::array<DimSize_t, DIM> &axes,
+                                        DimSize_t keep_dims,
+                                        const std::string& name = "") {
+    // FIXME: properly handle default w&b initialization in every cases
+    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by ReduceMean, not supported");
+    return std::make_shared<Node>(std::make_shared<ReduceMean_Op<static_cast<DimIdx_t>(DIM)>>(axes, keep_dims), 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> ReduceMean(
+    DimSize_t const (&axes)[DIM],
+    DimSize_t keep_dims = 1,
+    const std::string& name = "") {
+    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by ReduceMean, not supported");
+    return ReduceMean(to_array(axes), keep_dims, name);
+}
+}  // namespace Aidge
+
+namespace {
+template <>
+const char *const EnumStrings<Aidge::ReduceMeanAttr>::data[] = {"Axes", "KeepDims"};
+}
+
+#endif /* AIDGE_CORE_OPERATOR_REDUCEMEAN_H_ */
diff --git a/python_binding/operator/pybind_ReduceMean.cpp b/python_binding/operator/pybind_ReduceMean.cpp
new file mode 100644
index 000000000..3322de897
--- /dev/null
+++ b/python_binding/operator/pybind_ReduceMean.cpp
@@ -0,0 +1,58 @@
+// /********************************************************************************
+//  * 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 <iostream>
+// #include <string>
+// #include <vector>
+// #include <array>
+
+// #include "aidge/backend/OperatorImpl.hpp"
+// #include "aidge/operator/ReduceMean.hpp"
+// #include "aidge/operator/Operator.hpp"
+// #include "aidge/utils/Types.h"
+
+// namespace py = pybind11;
+// namespace Aidge {
+
+// template <DimIdx_t DIM> void declare_ReduceMeanOp(py::module &m) {
+//   py::class_<ReduceMean_Op<DIM>, std::shared_ptr<ReduceMean_Op<DIM>>, Operator, Attributes>(
+//     m, ("ReduceMeanOp" + std::to_string(DIM) + "D").c_str(),
+//     py::multiple_inheritance())
+//   .def(py::init<const std::array<DimSize_t, DIM> &, DimSize_t>(),
+//         py::arg("axes"),
+//         py::arg("keep_dims"))
+//     .def("get_inputs_name", &ReduceMean_Op<DIM>::getInputsName)
+//     .def("get_outputs_name", &ReduceMean_Op<DIM>::getOutputsName)
+//     ;
+
+//   m.def(("ReduceMean" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& axes,
+//                                                                 DimSize_t keepDims,
+//                                                                 const std::string& name) {
+//         AIDGE_ASSERT(axes.size() == DIM, "axes size [%ld] does not match DIM [%d]", axes.size(), DIM);
+
+//         return ReduceMean<DIM>(to_array<DIM>(axes.begin()), keepDims, name);
+//     }, py::arg("axes"),
+//        py::arg("keep_dims") = 1);
+// }
+
+
+// void init_ReduceMean(py::module &m) {
+//   declare_ReduceMeanOp<1>(m);
+//   declare_ReduceMeanOp<2>(m);
+//   declare_ReduceMeanOp<3>(m);
+
+//   // FIXME:
+//   // m.def("ReduceMean1D", static_cast<NodeAPI(*)(const char*, int, int, int const
+//   // (&)[1])>(&ReduceMean));
+// }
+// } // namespace Aidge
-- 
GitLab