From 6bb7ae3d82342169e49049b5d9905abce76b4865 Mon Sep 17 00:00:00 2001
From: Houssem Rouis <houssemeddine.rouis92@gmail.com>
Date: Thu, 26 Oct 2023 18:01:28 +0200
Subject: [PATCH] add div operator

---
 include/aidge/backend/cpu.hpp                 |   1 +
 .../aidge/backend/cpu/operator/DivImpl.hpp    |  50 +++++++
 .../cpu/operator/DivImpl_forward_kernels.hpp  |  51 +++++++
 src/operator/DivImpl.cpp                      |  44 ++++++
 unit_tests/operator/Test_DivImpl.cpp          | 132 ++++++++++++++++++
 5 files changed, 278 insertions(+)
 create mode 100644 include/aidge/backend/cpu/operator/DivImpl.hpp
 create mode 100644 include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp
 create mode 100644 src/operator/DivImpl.cpp
 create mode 100644 unit_tests/operator/Test_DivImpl.cpp

diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp
index cc1579ea..71d84999 100644
--- a/include/aidge/backend/cpu.hpp
+++ b/include/aidge/backend/cpu.hpp
@@ -19,6 +19,7 @@
 #include "aidge/backend/cpu/operator/BatchNormImpl.hpp"
 #include "aidge/backend/cpu/operator/ConvDepthWiseImpl.hpp"
 #include "aidge/backend/cpu/operator/ConvImpl.hpp"
+#include "aidge/backend/cpu/operator/DivImpl.hpp"
 #include "aidge/backend/cpu/operator/FCImpl.hpp"
 #include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp"
 #include "aidge/backend/cpu/operator/MatMulImpl.hpp"
diff --git a/include/aidge/backend/cpu/operator/DivImpl.hpp b/include/aidge/backend/cpu/operator/DivImpl.hpp
new file mode 100644
index 00000000..8d42fa97
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/DivImpl.hpp
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * 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_CPU_OPERATOR_DIVIMPL_H_
+#define AIDGE_CPU_OPERATOR_DIVIMPL_H_
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/operator/Div.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+#include <memory>
+#include <vector>
+
+namespace Aidge {
+// class Div_Op;
+
+// compute kernel registry for forward and backward
+class DivImplForward_cpu
+    : public Registrable<DivImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const void*, const void*,void*)> {
+};
+class DivImplBackward_cpu
+    : public Registrable<DivImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const void*, const void*, void*)> {
+};
+
+class DivImpl_cpu : public OperatorImpl {
+public:
+    DivImpl_cpu(const Div_Op& op) : OperatorImpl(op) {}
+
+    static std::unique_ptr<DivImpl_cpu> create(const Div_Op& op) {
+        return std::make_unique<DivImpl_cpu>(op);
+    }
+
+    NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
+    void forward() override;
+};
+
+namespace {
+static Registrar<Div_Op> registrarDivImpl_cpu("cpu", Aidge::DivImpl_cpu::create);
+}
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_DIVIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp
new file mode 100644
index 00000000..3bb3975e
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp
@@ -0,0 +1,51 @@
+/********************************************************************************
+ * 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_CPU_OPERATOR_DIVIMPL_FORWARD_KERNEL_H_
+#define AIDGE_CPU_OPERATOR_DIVIMPL_FORWARD_KERNEL_H_
+
+#include "aidge/utils/Registrar.hpp"
+#include <cmath>
+#include <iostream>
+#include "aidge/backend/cpu/operator/DivImpl.hpp"
+
+namespace Aidge {
+template <class I1, class I2, class O>
+void DivImpl_cpu_forward_kernel(std::size_t inputLenght,
+                                     const void* input1_,
+                                     const void* input2_,
+                                     void* output_) {
+
+    const I1* input_1 = static_cast<const I1*>(input1_);
+    const I2* input_2 = static_cast<const I2*>(input2_);
+    O* output = static_cast<O*>(output_);
+
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        //TODO: handle Div of two tensors the same size
+        output[i] = input_1[i] / input_2[0];
+    }
+}
+
+namespace {
+// TODO: add support for Div(float, int)
+static Registrar<DivImplForward_cpu> registrarDivImplForward_cpu_Float32(
+        {DataType::Float32, DataType::Float32, DataType::Float32},
+        Aidge::DivImpl_cpu_forward_kernel<float, float, float>);
+static Registrar<DivImplForward_cpu> registrarDivImplForward_cpu_Int32(
+        {DataType::Int32, DataType::Int32, DataType::Int32},
+        Aidge::DivImpl_cpu_forward_kernel<int, int, int>);
+static Registrar<DivImplForward_cpu> registrarDivImplForward_cpu_Float64(
+        {DataType::Float64, DataType::Float64, DataType::Float64},
+        Aidge::DivImpl_cpu_forward_kernel<double, double, double>);
+}  // namespace
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_DIVIMPL_FORWARD_KERNEL_H_ */
diff --git a/src/operator/DivImpl.cpp b/src/operator/DivImpl.cpp
new file mode 100644
index 00000000..e0d4cdf8
--- /dev/null
+++ b/src/operator/DivImpl.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 <cassert>
+#include <chrono>  // std::chrono::milliseconds
+#include <numeric> // std::accumulate
+#include <thread>  // std::this_thread::sleep_for
+#include <vector>
+
+#include "aidge/operator/Div.hpp"
+#include "aidge/utils/Types.h"
+
+#include "aidge/backend/cpu/operator/DivImpl.hpp"
+#include "aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp"
+
+Aidge::NbElts_t Aidge::DivImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
+    // this implementation can be in-place
+    return 0;
+}
+
+void Aidge::DivImpl_cpu::forward() {
+    assert(mOp.getInput(0) && "missing input #0");
+    assert(mOp.getInput(1) && "missing input #1");
+
+    // Find the correct kernel type
+    auto kernelFunc = Registrar<DivImplForward_cpu>::create({
+        mOp.getInput(0)->dataType(),
+        mOp.getInput(1)->dataType(),
+        mOp.getOutput(0)->dataType()});
+
+    // Call kernel
+    kernelFunc(std::static_pointer_cast<Tensor>(mOp.getInput(0))->size(),
+        mOp.getInput(0)->getImpl()->rawPtr(),
+        mOp.getInput(1)->getImpl()->rawPtr(),
+        mOp.getOutput(0)->getImpl()->rawPtr());
+}
diff --git a/unit_tests/operator/Test_DivImpl.cpp b/unit_tests/operator/Test_DivImpl.cpp
new file mode 100644
index 00000000..764f63c4
--- /dev/null
+++ b/unit_tests/operator/Test_DivImpl.cpp
@@ -0,0 +1,132 @@
+/********************************************************************************
+ * 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 <catch2/catch_test_macros.hpp>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/Div.hpp"
+
+#include "aidge/backend/cpu.hpp"
+
+#include <memory>
+
+using namespace Aidge;
+
+TEST_CASE("[cpu/operator] Div(forward)") {
+    SECTION("2D Tensor") {
+        std::shared_ptr<Tensor> input_1 = std::make_shared<Tensor>(Array2D<float,2,2> {
+            {
+                {0.07607108, 0.44075000},
+                {0.19494885, 0.20071143}
+            }
+        });
+        std::shared_ptr<Tensor> input_2 =  std::make_shared<Tensor>(Array2D<float,1,1>{{0.5}});
+        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array2D<float,2,2> {
+            {
+                {0.15214217, 0.88150001},
+                {0.38989770, 0.40142286}
+            }
+        });
+
+        std::shared_ptr<Node> myDiv = Div();
+        myDiv->getOperator()->setDatatype(DataType::Float32);
+        myDiv->getOperator()->setBackend("cpu");
+        myDiv->getOperator()->associateInput(0, input_1);
+        myDiv->getOperator()->associateInput(1, input_2);
+        myDiv->getOperator()->computeOutputDims();
+        myDiv->forward();
+
+        float* resPtr = static_cast<float*>(myDiv->getOperator()->getOutput(0)->getImpl()->rawPtr());
+        float* expectedPtr = static_cast<float*>(expectedOutput->getImpl()->rawPtr());
+        for (std::size_t i = 0; i< 4; ++i) {
+            REQUIRE(std::abs(resPtr[i]-expectedPtr[i]) < 0.00001);
+        }
+
+    }
+
+    SECTION("4D Tensor") {
+        std::shared_ptr<Tensor> input_1 = std::make_shared<Tensor>(Array4D<float,2,3,3,3> {
+            {
+                {
+                    {{0.25675946, 0.36265653, 0.22386390},
+                     {0.30483031, 0.97449398, 0.73871714},
+                     {0.36169255, 0.04510212, 0.27525920}},
+
+                    {{0.73255682, 0.03885978, 0.24181491},
+                    {0.14465559, 0.86070061, 0.88848090},
+                    {0.74408931, 0.87412918, 0.19800508}},
+
+                    {{0.43551809, 0.73437816, 0.37513995},
+                     {0.25414777, 0.06396711, 0.98708153},
+                     {0.02140611, 0.84974837, 0.62108254}}
+                },
+                {
+                    {{0.86227137, 0.69357753, 0.41814715},
+                     {0.76048166, 0.46306920, 0.05907208},
+                     {0.76625377, 0.91793799, 0.92988223}},
+
+                    {{0.34362513, 0.85009813, 0.21107805},
+                     {0.65575773, 0.38140792, 0.48540717},
+                     {0.10045588, 0.85803932, 0.23778951}},
+
+                    {{0.30316389, 0.04176688, 0.17290735},
+                     {0.07942408, 0.48647392, 0.39440966},
+                     {0.26543915, 0.92589515, 0.83948994}}
+                }
+            }
+        });
+        std::shared_ptr<Tensor> input_2 =  std::make_shared<Tensor>(Array2D<float,1,1>{{3.0}});
+        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array4D<float,2,3,3,3> {
+            {
+                {
+                    {{0.08558649, 0.12088551, 0.07462130},
+                     {0.10161010, 0.32483134, 0.24623905},
+                     {0.12056419, 0.01503404, 0.09175307}},
+
+                    {{0.24418561, 0.01295326, 0.08060497},
+                     {0.04821853, 0.28690019, 0.29616031},
+                     {0.24802977, 0.29137638, 0.06600169}},
+
+                    {{0.14517270, 0.24479271, 0.12504666},
+                     {0.08471593, 0.02132237, 0.32902718},
+                     {0.00713537, 0.28324947, 0.20702751}}
+                },
+                {
+                    {{0.28742379, 0.23119251, 0.13938238},
+                     {0.25349388, 0.15435641, 0.01969069},
+                     {0.25541791, 0.30597934, 0.30996075}},
+
+                    {{0.11454171, 0.28336605, 0.07035935},
+                     {0.21858591, 0.12713598, 0.16180240},
+                     {0.03348529, 0.28601310, 0.07926317}},
+
+                    {{0.10105463, 0.01392229, 0.05763578},
+                     {0.02647469, 0.16215797, 0.13146989},
+                     {0.08847972, 0.30863172, 0.27982998}}
+                }
+            }
+        });
+
+        std::shared_ptr<Node> myDiv = Div();
+        myDiv->getOperator()->setDatatype(DataType::Float32);
+        myDiv->getOperator()->setBackend("cpu");
+        myDiv->getOperator()->associateInput(0, input_1);
+        myDiv->getOperator()->associateInput(1, input_2);
+        myDiv->getOperator()->computeOutputDims();
+        myDiv->forward();
+
+        float* resPtr = static_cast<float*>(myDiv->getOperator()->getOutput(0)->getImpl()->rawPtr());
+        float* expectedPtr = static_cast<float*>(expectedOutput->getImpl()->rawPtr());
+        for (std::size_t i = 0; i< 54; ++i) {
+            REQUIRE(std::abs(resPtr[i]-expectedPtr[i]) < 0.00001);
+        }
+    }
+}
\ No newline at end of file
-- 
GitLab