From e5fab1bce2d040a1a66cab52f655998b617f9945 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gr=C3=A9goire=20KUBLER?= <gregoire.kubler@proton.me>
Date: Tue, 10 Sep 2024 14:14:16 +0200
Subject: [PATCH] feat : operator constant of shape

---
 include/aidge/backend/cpu.hpp                 |   1 +
 .../cpu/operator/ConstantOfShapeImpl.hpp      |  52 ++++++++
 .../ConstantOfShapeImpl_forward_kernels.hpp   |  78 ++++++++++++
 src/operator/ConstantOfShapeImpl.cpp          |  40 ++++++
 .../operator/Test_ConstantOfShapeImpl.cpp     | 120 ++++++++++++++++++
 5 files changed, 291 insertions(+)
 create mode 100644 include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp
 create mode 100644 include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp
 create mode 100644 src/operator/ConstantOfShapeImpl.cpp
 create mode 100644 unit_tests/operator/Test_ConstantOfShapeImpl.cpp

diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp
index 4134f5c5..963895c1 100644
--- a/include/aidge/backend/cpu.hpp
+++ b/include/aidge/backend/cpu.hpp
@@ -21,6 +21,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/ConstantOfShapeImpl.hpp"
 #include "aidge/backend/cpu/operator/DivImpl.hpp"
 #include "aidge/backend/cpu/operator/ErfImpl.hpp"
 #include "aidge/backend/cpu/operator/FCImpl.hpp"
diff --git a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp
new file mode 100644
index 00000000..80efb31d
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp
@@ -0,0 +1,52 @@
+/********************************************************************************
+ * 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_CONSTANTOFSHAPEIMPL_H_
+#define AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_H_
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/operator/ConstantOfShape.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+// class ConstantOfShape_op;
+
+class ConstantOfShapeImplForward_cpu
+    : public Registrable<
+          ConstantOfShapeImplForward_cpu, std::tuple<DataType>,
+          void(const std::vector<DimSize_t>, const Tensor&, void *)> {};
+
+class ConstantOfShapeImpl_cpu : public OperatorImpl {
+public:
+  ConstantOfShapeImpl_cpu(const ConstantOfShape_Op &op)
+      : OperatorImpl(op, "cpu") {}
+
+  static std::unique_ptr<ConstantOfShapeImpl_cpu>
+  create(const ConstantOfShapeImpl_cpu &op) {
+    return std::make_unique<ConstantOfShapeImpl_cpu>(op);
+  }
+
+  void forward() override;
+};
+
+namespace {
+static Registrar<ConstantOfShape_Op> registrarConstantOfShapeImpl_cpu(
+    "cpu", Aidge::ConstantOfShapeImpl_cpu::create);
+}
+} // namespace Aidge
+
+#endif /* _AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_H_ */
+
diff --git a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp
new file mode 100644
index 00000000..59a3475e
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp
@@ -0,0 +1,78 @@
+/********************************************************************************
+ * 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_CONSTANTOFSHAPEIMPL_FORWARD_KERNEL_H_
+#define AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_FORWARD_KERNEL_H_
+
+#include <aidge/data/Tensor.hpp>
+#include <aidge/data/half.hpp>
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <functional> // std::multiplies
+#include <numeric>    // std::accumulate
+#include <vector>
+
+#include "aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp"
+#include "aidge/data/Data.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+template <class O>
+void ConstantOfShapeimpl_cpu_forward_kernel(
+    const std::vector<DimSize_t> output_dims, const Tensor &value,
+    void *output_) {
+
+  O *output = static_cast<O *>(output_);
+  O val;
+  std::copy(static_cast<O *>(value.getImpl()->hostPtr()),
+            static_cast<O *>(value.getImpl()->hostPtr()) +
+                static_cast<NbElts_t>(1),
+            &val);
+  const size_t output_size = std::accumulate(
+      output_dims.begin(), output_dims.end(), 1, std::multiplies<DimSize_t>());
+  for (size_t i = 0; i < output_size; ++i) {
+    output[i] = val;
+  }
+}
+
+// Then we add the Registrar declaration for different input/output types
+namespace {
+static Registrar<ConstantOfShapeImplForward_cpu>
+    registrarConstantOfShapeImplForward_cpu_Float16(
+        {DataType::Float16},
+        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<half_float::half>);
+static Registrar<ConstantOfShapeImplForward_cpu>
+    registrarConstantOfShapeImplForward_cpu_Float32(
+        {DataType::Float32},
+        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<float>);
+static Registrar<ConstantOfShapeImplForward_cpu>
+    registrarConstantOfShapeImplForward_cpu_Float64(
+        {DataType::Float64},
+        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<double>);
+static Registrar<ConstantOfShapeImplForward_cpu>
+    registrarConstantOfShapeImplForward_cpu_Int16(
+        {DataType::Int16},
+        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int16_t>);
+static Registrar<ConstantOfShapeImplForward_cpu>
+    registrarConstantOfShapeImplForward_cpu_Int32(
+        {DataType::Int32},
+        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int32_t>);
+static Registrar<ConstantOfShapeImplForward_cpu>
+    registrarConstantOfShapeImplForward_cpu_Int64(
+        {DataType::Int64}, Aidge::ConstantOfShapeimpl_cpu_forward_kernel <std::int64_t>);
+} // namespace
+} // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_FORWARD_KERNEL_H_ */
+
diff --git a/src/operator/ConstantOfShapeImpl.cpp b/src/operator/ConstantOfShapeImpl.cpp
new file mode 100644
index 00000000..7d727c04
--- /dev/null
+++ b/src/operator/ConstantOfShapeImpl.cpp
@@ -0,0 +1,40 @@
+/********************************************************************************
+ * Copyright (c) 2024 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/backend/cpu/operator/ConstantOfShapeImpl.hpp"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp"
+#include "aidge/data/Data.hpp"
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/ConstantOfShape.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+void Aidge::ConstantOfShapeImpl_cpu::forward() {
+  const ConstantOfShape_Op &op_ = static_cast<const ConstantOfShape_Op &>(mOp);
+  // Check if input is provided
+  AIDGE_ASSERT(op_.getInput(0), "{} : Missing input 0", __func__);
+
+  // Create the forward kernal with the wanted types
+  auto kernelFunc = Registrar<ConstantOfShapeImplForward_cpu>::create(
+      {op_.getOutput(0)->dataType()});
+
+  // Call kernel
+  kernelFunc(op_.getOutput(0)->dims(),
+             op_.value(), 
+             op_.getOutput(0)->getImpl()->rawPtr());
+}
+
diff --git a/unit_tests/operator/Test_ConstantOfShapeImpl.cpp b/unit_tests/operator/Test_ConstantOfShapeImpl.cpp
new file mode 100644
index 00000000..42505d38
--- /dev/null
+++ b/unit_tests/operator/Test_ConstantOfShapeImpl.cpp
@@ -0,0 +1,120 @@
+/********************************************************************************
+ * 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 <algorithm>
+#include <chrono>
+#include <cmath>
+#include <cstddef> // std::size_t
+#include <cstdint> // std::uint16_t
+#include <iostream>
+#include <memory>
+#include <numeric> // std::accumulate
+#include <ostream>
+#include <random> // std::random_device, std::mt19937, std::uniform_real_distribution
+
+#include "catch2/internal/catch_compiler_capabilities.hpp"
+#include "catch2/internal/catch_enforce.hpp"
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/ConstantOfShape.hpp"
+#include "aidge/utils/TensorUtils.hpp"
+#include <aidge/data/Data.hpp>
+#include <aidge/data/half.hpp>
+#include <aidge/filler/Filler.hpp>
+#include <aidge/operator/OperatorTensor.hpp>
+#include <aidge/operator/Reshape.hpp>
+#include <aidge/utils/TensorUtils.hpp>
+#include <aidge/utils/Types.h>
+
+namespace Aidge {
+TEST_CASE("[cpu/operator] ConstantOfShape", "[ConstantOfShape][CPU]") {
+  constexpr std::uint16_t NBTRIALS = 10;
+  // Create a random number generator
+  auto random_seed = Catch::Generators::Detail::getSeed;
+  std::mt19937 gen(random_seed());
+  std::uniform_real_distribution<float> valueDist(
+      0.1f, 1.1f); // Random float distribution between 0 and 1
+  std::uniform_int_distribution<DimSize_t> input_tensor_size_dist(
+      std::size_t(1), std::size_t(10));
+  std::uniform_int_distribution<int64_t> input_tensor_values_dist(
+      std::size_t(1), std::size_t(7));
+  std::uniform_real_distribution<double> operator_attr_value_dist(-100., 100.);
+
+  ///////////////////////////////////////////////
+  // SETUP FUNCTIONS
+  auto generate_input_tensor =
+      [&gen, &input_tensor_size_dist,
+       &input_tensor_values_dist]() -> std::shared_ptr<Tensor> {
+    std::vector<DimSize_t> input_dims;
+    input_dims.push_back(input_tensor_size_dist(gen));
+
+    auto result = std::make_shared<Tensor>(input_dims);
+    result->setDataType(DataType::Int64);
+    result->setBackend("cpu");
+    for (DimSize_t i = 0; i < result->size(); ++i) {
+      result->set<int64_t>(i, input_tensor_values_dist(gen));
+    }
+    return result;
+  };
+
+  auto generate_random_operator =
+      [&gen,
+       &operator_attr_value_dist]() -> std::shared_ptr<ConstantOfShape_Op> {
+    auto node = ConstantOfShape(Tensor(operator_attr_value_dist(gen)));
+    auto op = std::static_pointer_cast<ConstantOfShape_Op>(node->getOperator());
+    op->setDataType(DataType::Float64);
+    op->setBackend("cpu");
+    return op;
+  };
+
+  auto generate_output_tensor = [](std::shared_ptr<Tensor> input_tensor,
+                                   std::shared_ptr<ConstantOfShape_Op> op) {
+    std::vector<DimSize_t> output_dims;
+    output_dims.reserve(input_tensor->size());
+    for (DimSize_t i = 0; i < input_tensor->size(); ++i) {
+      output_dims.push_back(input_tensor->get<int64_t>(i));
+    }
+    auto result = std::make_shared<Tensor>(output_dims);
+    result->setDataType(op->value().dataType());
+    result->setBackend("cpu");
+    constantFiller(result, op->value().get<double>(0));
+    return result;
+  };
+
+  /////////////////////////////////////
+  // BENCHMARKING
+  std::chrono::time_point<std::chrono::system_clock> start;
+  std::chrono::time_point<std::chrono::system_clock> end;
+  std::chrono::duration<double, std::micro> duration{};
+  int number_of_operation{0};
+
+  SECTION("ConstantOfShapeImpl_cpu::forward()") {
+    for (int i = 0; i < NBTRIALS; ++i) {
+      auto input_T = generate_input_tensor();
+      std::shared_ptr<ConstantOfShape_Op> op = generate_random_operator();
+      auto output_T = generate_output_tensor(input_T, op);
+      op->associateInput(0, input_T);
+
+      REQUIRE(op->forwardDims(true));
+      REQUIRE_NOTHROW(op->forward());
+
+      CHECK(output_T->nbDims() == op->getOutput(0)->nbDims());
+      for (DimIdx_t i = 0; i < output_T->nbDims(); ++i) {
+        CHECK(output_T->dims().at(i) == op->getOutput(0)->dims().at(i));
+      }
+      CHECK(approxEq<double>(*output_T, *op->getOutput(0)));
+    }
+  }
+}
+} // namespace Aidge
+
-- 
GitLab