From 6b0cb49ea4e22a71cb7af53d3f936527658367b2 Mon Sep 17 00:00:00 2001 From: Jerome Hue <jerome.hue@cea.fr> Date: Fri, 31 Jan 2025 10:52:26 +0000 Subject: [PATCH] FEAT: Add Heaviside implementation for CPU backend. --- include/aidge/backend/cpu.hpp | 2 +- .../backend/cpu/operator/HeavisideImpl.hpp | 32 ++++++ .../cpu/operator/HeavisideImpl_kernels.hpp | 46 +++++++++ src/operator/HeavisideImpl.cpp | 37 +++++++ unit_tests/operator/Test_HeavisideImpl.cpp | 98 +++++++++++++++++++ 5 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 include/aidge/backend/cpu/operator/HeavisideImpl.hpp create mode 100644 include/aidge/backend/cpu/operator/HeavisideImpl_kernels.hpp create mode 100644 src/operator/HeavisideImpl.cpp create mode 100644 unit_tests/operator/Test_HeavisideImpl.cpp diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp index 0c8ab84d..5db19a2b 100644 --- a/include/aidge/backend/cpu.hpp +++ b/include/aidge/backend/cpu.hpp @@ -34,6 +34,7 @@ #include "aidge/backend/cpu/operator/FCImpl.hpp" #include "aidge/backend/cpu/operator/FoldImpl.hpp" #include "aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp" +#include "aidge/backend/cpu/operator/HeavisideImpl.hpp" #include "aidge/backend/cpu/operator/LRNImpl.hpp" #include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp" #include "aidge/backend/cpu/operator/LnImpl.hpp" @@ -59,4 +60,3 @@ #include "aidge/backend/cpu/data/TensorImpl.hpp" #endif /* AIDGE_CPU_IMPORTS_H_ */ - diff --git a/include/aidge/backend/cpu/operator/HeavisideImpl.hpp b/include/aidge/backend/cpu/operator/HeavisideImpl.hpp new file mode 100644 index 00000000..7a3ba9ad --- /dev/null +++ b/include/aidge/backend/cpu/operator/HeavisideImpl.hpp @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2025 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_HEAVISIDEIMPL_H_ +#define AIDGE_CPU_OPERATOR_HEAVISIDEIMPL_H_ + +#include <cstddef> // std::size_t + +#include "aidge/backend/cpu/operator/OperatorImpl.hpp" +#include "aidge/operator/Heaviside.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/future_std/span.hpp" + +namespace Aidge { +using HeavisideImplCpu = + OperatorImpl_cpu<Heaviside_Op, + void(std::size_t, const void *, void *, const float), + void(const float, std::size_t, const void *, void *)>; + +// Implementation entry point registration for operator Heaviside +REGISTRAR(Heaviside_Op, "cpu", HeavisideImplCpu::create); +} // namespace Aidge + +#endif // AIDGE_CPU_OPERATOR_HEAVISIDEIMPL_H_ diff --git a/include/aidge/backend/cpu/operator/HeavisideImpl_kernels.hpp b/include/aidge/backend/cpu/operator/HeavisideImpl_kernels.hpp new file mode 100644 index 00000000..3fd6ca7d --- /dev/null +++ b/include/aidge/backend/cpu/operator/HeavisideImpl_kernels.hpp @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2025 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_HEAVISIDEIMPL_KERNELS_H_ +#define AIDGE_CPU_OPERATOR_HEAVISIDEIMPL_KERNELS_H_ + +#include "aidge/utils/Registrar.hpp" + +#include <cstddef> // std::size_t + +#include "aidge/backend/cpu/operator/HeavisideImpl.hpp" +#include "aidge/utils/ErrorHandling.hpp" + + +namespace Aidge { + +template <class I, class O> +void HeavisideImplCpuForwardKernel(std::size_t inputLenght, + const void *input_, + void *output_, + const float value) { + const I *input = static_cast<const I *>(input_); + O *output = static_cast<O *>(output_); + + for (std::size_t i = 0; i < inputLenght; ++i) { + output[i] = (input[i] > 0) ? 1 : (input[i] == 0 ? value : 0); + } +} + +// Kernels registration to implementation entry point +REGISTRAR(HeavisideImplCpu, + {DataType::Float32}, + {ProdConso::inPlaceModel, + Aidge::HeavisideImplCpuForwardKernel<float, float>, + nullptr}); +} // namespace Aidge + +#endif // AIDGE_CPU_OPERATOR_HEAVISIDEIMPL_KERNELS_H__H_ diff --git a/src/operator/HeavisideImpl.cpp b/src/operator/HeavisideImpl.cpp new file mode 100644 index 00000000..56ceb9b0 --- /dev/null +++ b/src/operator/HeavisideImpl.cpp @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2025 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/HeavisideImpl.hpp" + +#include <stdexcept> + +#include "aidge/backend/cpu/operator/HeavisideImpl_kernels.hpp" +#include "aidge/backend/cpu/data/GetCPUPtr.h" +#include "aidge/utils/ErrorHandling.hpp" + +template <> void Aidge::HeavisideImplCpu::forward() { + const Heaviside_Op &op_ = dynamic_cast<const Heaviside_Op &>(mOp); + std::shared_ptr<Tensor> input0 = op_.getInput(0); + std::shared_ptr<Tensor> output0 = op_.getOutput(0); + AIDGE_ASSERT(input0, "missing input #0"); + + const auto impl = + Registrar<HeavisideImplCpu>::create(getBestMatch(getRequiredSpec())); + + impl.forward(input0->size(), + getCPUPtr(mOp.getRawInput(0)), + getCPUPtr(mOp.getRawOutput(0)), + op_.value()); +} + +template <> void Aidge::HeavisideImplCpu::backward() { + AIDGE_THROW_OR_ABORT(std::runtime_error, "Heaviside backward not implemented yet"); +} diff --git a/unit_tests/operator/Test_HeavisideImpl.cpp b/unit_tests/operator/Test_HeavisideImpl.cpp new file mode 100644 index 00000000..4cbdf1a0 --- /dev/null +++ b/unit_tests/operator/Test_HeavisideImpl.cpp @@ -0,0 +1,98 @@ +/******************************************************************************** + * Copyright (c) 2025 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/HeavisideImpl_kernels.hpp" + +#include <memory> +#include <cstdlib> +#include <random> + +#include <catch2/catch_test_macros.hpp> + +#include "aidge/data/Tensor.hpp" +#include "aidge/backend/cpu/operator/HeavisideImpl.hpp" +#include "aidge/graph/Node.hpp" +#include "aidge/utils/TensorUtils.hpp" + +namespace Aidge +{ + +TEST_CASE("[cpu/operator] Heaviside(forward)", "[Heaviside][CPU]") { + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<float> valueDist(-1.0f, 1.0f); + std::uniform_int_distribution<std::size_t> dimSizeDist(std::size_t(2), std::size_t(10)); + std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(1), std::size_t(5)); + + SECTION("1D Tensor") { + + std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array1D<float,10> { + {0, 1, 2,-3, 4,-5,-6, 7, 8, 9} + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<float,10> { + {0.5, 1, 1, 0, 1, 0, 0, 1, 1, 1} + }); + + std::shared_ptr<Node> heaviside = Heaviside(0.5); + auto op = std::static_pointer_cast<OperatorTensor>(heaviside->getOperator()); + op->associateInput(0, input0); + op->setBackend("cpu"); + op->setDataType(DataType::Float32); + + op->forward(); + REQUIRE(approxEq<float>(*op->getOutput(0),*expectedOutput)); + } + + SECTION("+1-D Tensor") + { + auto dims = std::vector<std::size_t>(); + auto nbDims = nbDimsDist(gen); + + for (auto i = 0u; i < nbDims; ++i) { + dims.push_back(dimSizeDist(gen)); + } + + auto numberOfElements = std::accumulate(dims.cbegin(), dims.cend(), std::size_t(1), std::multiplies<std::size_t>()); + float* inputArray = new float[numberOfElements]; + float* resultArray = new float[numberOfElements]; + + for(auto i = 0u; i < numberOfElements; ++i) + { + inputArray[i] = valueDist(gen); + resultArray[i] = inputArray[i] > 0 ? 1 : (inputArray[i] == 0 ? 0.5 : 0); + } + + auto T0 = std::make_shared<Tensor>(); + T0->setDataType(DataType::Float32); + T0->setBackend("cpu"); + + auto T1 = std::make_shared<Tensor>(); + T1->setDataType(DataType::Float32); + T1->setBackend("cpu"); + + T0->resize(dims); + T0->getImpl()->setRawPtr(inputArray, numberOfElements); + T1->resize(dims); + T1->getImpl()->setRawPtr(resultArray, numberOfElements); + + std::shared_ptr<Node> heaviside = Heaviside(0.5); + auto op = std::static_pointer_cast<OperatorTensor>(heaviside->getOperator()); + op->associateInput(0, T0); + op->setBackend("cpu"); + op->setDataType(DataType::Float32); + + op->forward(); + + REQUIRE(approxEq<float>(*(op->getOutput(0)), *T1)); + } +} +} -- GitLab