From 1f60b19b179f0145a505703c07a2dfb8bb540bbd Mon Sep 17 00:00:00 2001 From: Olivier BICHLER <olivier.bichler@cea.fr> Date: Mon, 5 May 2025 18:33:52 +0200 Subject: [PATCH] Added rounding mechanism --- include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp | 1 + .../backend/cpu/operator/AvgPoolingImpl_kernels.hpp | 9 +++++---- .../backend/cpu/operator/GlobalAveragePoolingImpl.hpp | 2 +- .../cpu/operator/GlobalAveragePoolingImpl_kernels.hpp | 10 +++++----- include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp | 1 + .../backend/cpu/operator/ReduceMeanImpl_kernels.hpp | 11 ++++++----- include/aidge/backend/cpu/operator/RoundImpl.hpp | 2 +- .../aidge/backend/cpu/operator/RoundImpl_kernels.hpp | 6 +++--- src/operator/AvgPoolingImpl.cpp | 1 + src/operator/GlobalAveragePoolingImpl.cpp | 3 ++- src/operator/ReduceMeanImpl.cpp | 1 + src/operator/RoundImpl.cpp | 5 ++++- unit_tests/operator/Test_RoundImpl.cpp | 2 +- 13 files changed, 32 insertions(+), 22 deletions(-) diff --git a/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp b/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp index 7c76657f..f805f204 100644 --- a/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp +++ b/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp @@ -32,6 +32,7 @@ using AvgPoolingImpl2D_cpu = OperatorImpl_cpu<AvgPooling_Op<2>, const std::array<DimSize_t, 2>&, const std::array<DimSize_t, 4>&, bool, + RoundingMode, const void *, void *)>; diff --git a/include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp b/include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp index f9cc13b5..9980d884 100644 --- a/include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp +++ b/include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp @@ -29,14 +29,14 @@ using Acc_T = typename std::conditional<std::is_floating_point<T>::value, T, dou template <typename T> typename std::enable_if<std::is_floating_point<T>::value, T>::type -castFromFloat(T value) { +castFromFloat(T value, RoundingMode /*roundingMode*/) { return value; } template <typename T> typename std::enable_if<!std::is_floating_point<T>::value, T>::type -castFromFloat(double value) { - return static_cast<T>(std::nearbyint(value)); +castFromFloat(double value, RoundingMode roundingMode) { + return static_cast<T>(round(value, roundingMode)); } /** @@ -54,6 +54,7 @@ void AvgPoolingImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& strideD const std::array<DimSize_t, 2>& dilations, const std::array<DimSize_t, 4> &dims, bool ceilMode, + RoundingMode roundingMode, const void *input_, void *output_) { const I *input = static_cast<const I *>(input_); @@ -115,7 +116,7 @@ void AvgPoolingImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& strideD } } - output[oIndexFull] = count > 0 ? castFromFloat<O>(sum / count) : 0; + output[oIndexFull] = count > 0 ? castFromFloat<O>(sum / count, roundingMode) : 0; } } } diff --git a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp index a71174c0..24561ed2 100644 --- a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp +++ b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp @@ -22,7 +22,7 @@ namespace Aidge { // Operator implementation entry point for the backend using GlobalAveragePoolingImpl_cpu = OperatorImpl_cpu<GlobalAveragePooling_Op, - void(const std::shared_ptr<Tensor>&, void *)>; + void(RoundingMode, const std::shared_ptr<Tensor>&, void *)>; // Implementation entry point registration to Operator REGISTRAR(GlobalAveragePooling_Op, "cpu", Aidge::GlobalAveragePoolingImpl_cpu::create); diff --git a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp index 3cab0ad9..8fe83370 100644 --- a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp +++ b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp @@ -45,18 +45,18 @@ static stableMean(const T* vec, std::size_t size) { template <typename T> typename std::enable_if_t<std::is_floating_point<T>::value, T> -static castFromFloat(T value) { +static castFromFloat(T value, RoundingMode /*roundingMode*/) { return value; } template <typename T> typename std::enable_if_t<!std::is_floating_point<T>::value, T> -static castFromFloat(double value) { - return static_cast<T>(std::nearbyint(value)); +static castFromFloat(double value, RoundingMode roundingMode) { + return static_cast<T>(round(value, roundingMode)); } template <DataType DT_I, DataType DT_O = DT_I> -void GlobalAveragePoolingImpl_cpu_forward_kernel(const std::shared_ptr<Tensor>& inputTensor, void *output_) { +void GlobalAveragePoolingImpl_cpu_forward_kernel(RoundingMode roundingMode, const std::shared_ptr<Tensor>& inputTensor, void *output_) { // computation using I = cpptype_t<DT_I>; @@ -81,7 +81,7 @@ void GlobalAveragePoolingImpl_cpu_forward_kernel(const std::shared_ptr<Tensor>& for (int channel = 0; channel < static_cast<int>(dims[1]); ++channel) { const I *filter_start = std::next( input, (batch * in_batch_nb_elems) + (channel * in_channel_nb_elems)); - output[batch * out_batch_nb_elems + channel] = castFromFloat<O>(stableMean<I>(filter_start, in_channel_nb_elems)); + output[batch * out_batch_nb_elems + channel] = castFromFloat<O>(stableMean<I>(filter_start, in_channel_nb_elems), roundingMode); } } } diff --git a/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp b/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp index d6c60c35..993e9941 100644 --- a/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp +++ b/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp @@ -26,6 +26,7 @@ namespace Aidge { using ReduceMeanImpl_cpu = OperatorImpl_cpu<ReduceMean_Op, void(const std::vector<std::int32_t>&, DimSize_t, + RoundingMode, const std::vector<DimSize_t>&, const void *, void *)>; diff --git a/include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp b/include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp index 73aa283d..eea790d5 100644 --- a/include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp +++ b/include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp @@ -52,19 +52,20 @@ stableMean(const T* vec, std::size_t len, std::size_t stride) { template <typename T> typename std::enable_if_t<std::is_floating_point<T>::value, T> -castFromFloat(T value) { +castFromFloat(T value, RoundingMode /*roundingMode*/) { return value; } template <typename T> typename std::enable_if_t<!std::is_floating_point<T>::value, T> -castFromFloat(double value) { - return static_cast<T>(std::nearbyint(value)); +castFromFloat(double value, RoundingMode roundingMode) { + return static_cast<T>(round(value, roundingMode)); } template <class I, class O> void ReduceMeanImpl_cpu_forward_kernel(const std::vector<std::int32_t>& axes, DimSize_t /*keepDims*/, + RoundingMode roundingMode, const std::vector<DimSize_t>& inputDims, const void* input_, void* output_) { @@ -87,7 +88,7 @@ void ReduceMeanImpl_cpu_forward_kernel(const std::vector<std::int32_t>& axes, for (std::size_t post = 0; post < stride_post; ++post) { const std::size_t idx_i = pre * dim_i * stride_post + post; const std::size_t idx_o = pre * stride_post + post; - output[idx_o] = castFromFloat<O>(stableMean(input + idx_i, dim_i, stride_post)); + output[idx_o] = castFromFloat<O>(stableMean(input + idx_i, dim_i, stride_post), roundingMode); } } } else { @@ -133,7 +134,7 @@ void ReduceMeanImpl_cpu_forward_kernel(const std::vector<std::int32_t>& axes, } std::transform(inputAccumulation, inputAccumulation + outputElements, output, - [](auto value) { return castFromFloat<O>(value); }); + [roundingMode](auto value) { return castFromFloat<O>(value, roundingMode); }); if (outputAccumulation) { delete[] outputAccumulation; } diff --git a/include/aidge/backend/cpu/operator/RoundImpl.hpp b/include/aidge/backend/cpu/operator/RoundImpl.hpp index c595e251..02e35f74 100644 --- a/include/aidge/backend/cpu/operator/RoundImpl.hpp +++ b/include/aidge/backend/cpu/operator/RoundImpl.hpp @@ -25,7 +25,7 @@ namespace Aidge { // Operator implementation entry point for the backend using RoundImpl_cpu = OperatorImpl_cpu<Round_Op, - void(const std::size_t, const void*, void*)>; + void(RoundingMode, const std::size_t, const void*, void*)>; // Implementation entry point registration to Operator REGISTRAR(Round_Op, "cpu", Aidge::RoundImpl_cpu::create); diff --git a/include/aidge/backend/cpu/operator/RoundImpl_kernels.hpp b/include/aidge/backend/cpu/operator/RoundImpl_kernels.hpp index 7ac4319b..03304347 100644 --- a/include/aidge/backend/cpu/operator/RoundImpl_kernels.hpp +++ b/include/aidge/backend/cpu/operator/RoundImpl_kernels.hpp @@ -21,7 +21,8 @@ namespace Aidge { template <class I, class O> -void RoundImpl_cpu_forward_kernel(const std::size_t inputLength, +void RoundImpl_cpu_forward_kernel(RoundingMode roundingMode, + const std::size_t inputLength, const void* input_, void* output_) { @@ -29,8 +30,7 @@ void RoundImpl_cpu_forward_kernel(const std::size_t inputLength, O* output = static_cast<O*>(output_); for (std::size_t i = 0; i < inputLength; ++i) { - //std::round would not work since it doesn't follow the halves rules (See ONNX Round) - output[i] = static_cast<O>(std::nearbyint(static_cast<float>(input[i]))); + output[i] = static_cast<O>(round(input[i], roundingMode)); } } diff --git a/src/operator/AvgPoolingImpl.cpp b/src/operator/AvgPoolingImpl.cpp index eb5ef87b..53f0f799 100644 --- a/src/operator/AvgPoolingImpl.cpp +++ b/src/operator/AvgPoolingImpl.cpp @@ -35,6 +35,7 @@ void Aidge::AvgPoolingImpl2D_cpu::forward() { op_.dilations(), op_.getInput(0)->template dims<4>(), op_.ceilMode(), + op_.roundingMode(), getCPUPtr(op_.getInput(0)), getCPUPtr(op_.getOutput(0))); } diff --git a/src/operator/GlobalAveragePoolingImpl.cpp b/src/operator/GlobalAveragePoolingImpl.cpp index 1b6d9a06..ba613e55 100644 --- a/src/operator/GlobalAveragePoolingImpl.cpp +++ b/src/operator/GlobalAveragePoolingImpl.cpp @@ -38,7 +38,8 @@ void Aidge::GlobalAveragePoolingImpl_cpu::forward() const auto impl = Registrar<GlobalAveragePoolingImpl_cpu>::create(getBestMatch(getRequiredSpec())); // Call kernel - impl.forward(op_.getInput(0), + impl.forward(op_.roundingMode(), + op_.getInput(0), op_.getOutput(0)->getImpl()->rawPtr()); } diff --git a/src/operator/ReduceMeanImpl.cpp b/src/operator/ReduceMeanImpl.cpp index 62267256..7ad6df0e 100644 --- a/src/operator/ReduceMeanImpl.cpp +++ b/src/operator/ReduceMeanImpl.cpp @@ -28,6 +28,7 @@ void Aidge::ReduceMeanImpl_cpu::forward() { // Call kernel impl.forward(op_.axes(), op_.keepDims(), + op_.roundingMode(), op_.getInput(0)->dims(), op_.getInput(0)->getImpl()->rawPtr(), op_.getOutput(0)->getImpl()->rawPtr()); diff --git a/src/operator/RoundImpl.cpp b/src/operator/RoundImpl.cpp index 6f19f064..e18dbdc7 100644 --- a/src/operator/RoundImpl.cpp +++ b/src/operator/RoundImpl.cpp @@ -22,6 +22,8 @@ template <> void Aidge::RoundImpl_cpu::forward() { + const Round_Op& op_ = dynamic_cast<const Round_Op&>(mOp); + std::shared_ptr<Tensor> in0 = std::static_pointer_cast<Tensor>(mOp.getRawInput(0)); std::shared_ptr<Tensor> out0 = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0)); AIDGE_ASSERT(in0, "missing input #0"); @@ -30,7 +32,8 @@ void Aidge::RoundImpl_cpu::forward() { const auto impl = Registrar<RoundImpl_cpu>::create(getBestMatch(getRequiredSpec())); // Call kernel - impl.forward(in0->size(), + impl.forward(op_.roundingMode(), + in0->size(), getCPUPtr(mOp.getRawInput(0)), getCPUPtr(mOp.getRawOutput(0))); } diff --git a/unit_tests/operator/Test_RoundImpl.cpp b/unit_tests/operator/Test_RoundImpl.cpp index e658b061..1c93eb5e 100644 --- a/unit_tests/operator/Test_RoundImpl.cpp +++ b/unit_tests/operator/Test_RoundImpl.cpp @@ -83,7 +83,7 @@ TEST_CASE("[cpu/operator] Round_Test", "[Round][CPU]") { for (std::size_t i = 0; i < nb_elements; ++i) { array0[i] = valueDist(gen); - result[i] = std::nearbyint(array0[i]); + result[i] = std::round(array0[i]); } -- GitLab