diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp index dd0654a1318d1a5d597f67691d3e25c508b3c613..41a393e7b7aca7ef8b8e2ede0b18ec6e3f19e6fb 100644 --- a/include/aidge/backend/cpu.hpp +++ b/include/aidge/backend/cpu.hpp @@ -23,6 +23,7 @@ #include "aidge/backend/cpu/operator/AvgPoolingImpl.hpp" #include "aidge/backend/cpu/operator/MaxPoolingImpl.hpp" #include "aidge/backend/cpu/operator/BatchNormImpl.hpp" +#include "aidge/backend/cpu/operator/BitErrorRateImpl.hpp" #include "aidge/backend/cpu/operator/BitShiftImpl.hpp" #include "aidge/backend/cpu/operator/ClipImpl.hpp" #include "aidge/backend/cpu/operator/ConvDepthWiseImpl.hpp" diff --git a/include/aidge/backend/cpu/operator/BitErrorRateImpl.hpp b/include/aidge/backend/cpu/operator/BitErrorRateImpl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d90d870c0e057fe7a48cae6272d237ee589d7b65 --- /dev/null +++ b/include/aidge/backend/cpu/operator/BitErrorRateImpl.hpp @@ -0,0 +1,38 @@ + +/******************************************************************************** + * 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_BITERRORRATEIMPL_H_ +#define AIDGE_CPU_OPERATOR_BITERRORRATEIMPL_H_ + +#include <memory> +#include <tuple> +#include <vector> + +#include "aidge/backend/cpu/operator/OperatorImpl.hpp" +#include "aidge/operator/BitErrorRate.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" +#include "aidge/backend/cpu/data/GetCPUPtr.h" + +namespace Aidge { +// Operator implementation entry point for the backend +using BitErrorRateImpl_Cpu = OperatorImpl_cpu<BitErrorRate_Op, + void(const void *, + void *, + std::size_t, + float)>; + +// Implementation entry point registration to Operator +REGISTRAR(BitErrorRate_Op, "cpu", Aidge::BitErrorRateImpl_Cpu::create); +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_BITERRORRATEIMPL_H */ diff --git a/include/aidge/backend/cpu/operator/BitErrorRateImpl_kernels.hpp b/include/aidge/backend/cpu/operator/BitErrorRateImpl_kernels.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2d8ce26ec4559f6c399f1185d59a1cc6242907ec --- /dev/null +++ b/include/aidge/backend/cpu/operator/BitErrorRateImpl_kernels.hpp @@ -0,0 +1,130 @@ +/******************************************************************************** + * 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_NBITFLIPIMPL_KERNELS_H_ +#define AIDGE_CPU_OPERATOR_NBITFLIPIMPL_KERNELS_H_ + +#include <algorithm> +#include <random> + +#include "aidge/backend/cpu/operator/BitErrorRateImpl.hpp" +#include "aidge/utils/Registrar.hpp" + + +namespace Aidge { + +template<typename T2, typename T1> +struct BitCastHelper { + static constexpr bool isValid = + sizeof(T1) == sizeof(T2) and + std::is_trivially_copyable<T1>::value and + std::is_trivially_copyable<T2>::value and + std::is_trivially_constructible<T2>::value; +}; + +template<typename T2, typename T1> +typename std::enable_if<BitCastHelper<T2, T1>::isValid, T2>::type +bitCast(const T1& src) { + T2 dst; + std::memcpy(&dst, &src, sizeof(T2)); + return dst; +} + +template<typename T> +typename std::enable_if<sizeof(T) == 4>::type +flipSingleBit(T& value, std::size_t bitToFlip) +{ + std::uint32_t intRepresentation = bitCast<std::uint32_t>(value); + intRepresentation ^= (1u << bitToFlip); + value = bitCast<T>(intRepresentation); +} + +template<typename T> +typename std::enable_if<sizeof(T) == 8>::type +flipSingleBit(T& value, std::size_t bitToFlip) +{ + std::uint64_t intRepresentation = bitCast<std::uint64_t>(value); + intRepresentation ^= (1ull << bitToFlip); + value = bitCast<T>(intRepresentation); +} + + +template<typename T> +typename std::enable_if<(sizeof(T) != 4 and sizeof(T) != 8)>::type +flipSingleBit(T& vlaue, std::size_t bitToFlip) +{ + AIDGE_THROW_OR_ABORT(std::runtime_error, + "BitFlip error not implemented for this type of data."); +} + +template <class I, class O> +void BitErrorRateImpl_cpu_forward_kernel(const void* input_, + void* output_, + std::size_t inputLength, + float ber) { + + const I* input = static_cast<const I*>(input_); + O* output = static_cast<O*>(output_); + std::copy(input, input + inputLength, output); + + // Compute the total number of bits in the tensor. + std::size_t totalBits = inputLength * sizeof(O) * 8; + // Determine the number of bits to flip using the bit error rate (rounded to the nearest integer). + std::size_t nBitToFlip = static_cast<std::size_t>(std::round(ber * totalBits)); + + if(nBitToFlip == 0) { + return; + } + + Log::info("Performing {} bit flips on tensor, length is {}", nBitToFlip, inputLength); + + std::random_device rd; + std::mt19937 gen(rd()); + + std::uniform_int_distribution<size_t> dist(0, inputLength - 1); + std::uniform_int_distribution<size_t> bitDist(0, sizeof(O) * 8 - 1); + + for(auto i = 0U; i < nBitToFlip; ++i) { + + auto randomIndex = dist(gen); + auto bitToFlip = bitDist(gen); + + O oldValue = output[randomIndex]; + flipSingleBit(output[randomIndex], bitToFlip); + O newValue = output[randomIndex]; + + Log::notice("\t Flipping bit {} of weight {}.\tFrom {} to {}", + bitToFlip, + randomIndex, + oldValue, + newValue); + } +} + +// Kernels registration to implementation entry point +REGISTRAR(BitErrorRateImpl_Cpu, + {DataType::Float32}, + {ProdConso::inPlaceModel, + Aidge::BitErrorRateImpl_cpu_forward_kernel<float, float>, + nullptr}); +REGISTRAR(BitErrorRateImpl_Cpu, + {DataType::Float64}, + {ProdConso::inPlaceModel, + Aidge::BitErrorRateImpl_cpu_forward_kernel<double, double>, + nullptr}); +REGISTRAR(BitErrorRateImpl_Cpu, + {DataType::Int32}, + {ProdConso::inPlaceModel, + Aidge::BitErrorRateImpl_cpu_forward_kernel<std::uint32_t, std::uint32_t>, + nullptr}); +} // namespace Aidge + +#endif diff --git a/include/aidge/backend/cpu/operator/NBitFlipImpl_kernels.hpp b/include/aidge/backend/cpu/operator/NBitFlipImpl_kernels.hpp index 4a9863e91528ac43904b8e2d2d2eaec1c9219906..03f40b1b776e93d2299c64f4967d242a8d80b6a6 100644 --- a/include/aidge/backend/cpu/operator/NBitFlipImpl_kernels.hpp +++ b/include/aidge/backend/cpu/operator/NBitFlipImpl_kernels.hpp @@ -23,7 +23,7 @@ namespace Aidge { template<typename T2, typename T1> struct BitCastHelper { - static constexpr bool isValid = + static constexpr bool isValid = sizeof(T1) == sizeof(T2) and std::is_trivially_copyable<T1>::value and std::is_trivially_copyable<T2>::value and @@ -61,7 +61,7 @@ template<typename T> typename std::enable_if<(sizeof(T) != 4 and sizeof(T) != 8)>::type flipSingleBit(T& vlaue, std::size_t bitToFlip) { - AIDGE_THROW_OR_ABORT(std::runtime_error, + AIDGE_THROW_OR_ABORT(std::runtime_error, "BitFlip error not implemented for this type of data."); } @@ -71,7 +71,7 @@ void NBitFlipImpl_cpu_forward_kernel(const void* input_, std::size_t inputLength, std::uint32_t nBitToFlip) { - + const I* input = static_cast<const I*>(input_); O* output = static_cast<O*>(output_); @@ -81,7 +81,7 @@ void NBitFlipImpl_cpu_forward_kernel(const void* input_, return; } - Log::info("Performing {} bit flips on tensor", nBitToFlip); + Log::info("Performing {} bit flips on tensor, length is {}", nBitToFlip, inputLength); std::random_device rd; std::mt19937 gen(rd()); @@ -97,7 +97,7 @@ void NBitFlipImpl_cpu_forward_kernel(const void* input_, O oldValue = output[randomIndex]; flipSingleBit(output[randomIndex], bitToFlip); O newValue = output[randomIndex]; - + Log::notice("\t Flipping bit {} of weight {}.\tFrom {} to {}", bitToFlip, randomIndex, @@ -111,20 +111,19 @@ void NBitFlipImpl_cpu_forward_kernel(const void* input_, // Kernels registration to implementation entry point REGISTRAR(NBitFlipImpl_cpu, {DataType::Float32}, - {ProdConso::inPlaceModel, - Aidge::NBitFlipImpl_cpu_forward_kernel<float, float>, + {ProdConso::inPlaceModel, + Aidge::NBitFlipImpl_cpu_forward_kernel<float, float>, nullptr}); REGISTRAR(NBitFlipImpl_cpu, {DataType::Float64}, - {ProdConso::inPlaceModel, - Aidge::NBitFlipImpl_cpu_forward_kernel<double, double>, + {ProdConso::inPlaceModel, + Aidge::NBitFlipImpl_cpu_forward_kernel<double, double>, nullptr}); REGISTRAR(NBitFlipImpl_cpu, {DataType::Int32}, - {ProdConso::inPlaceModel, - Aidge::NBitFlipImpl_cpu_forward_kernel<std::uint32_t, std::uint32_t>, + {ProdConso::inPlaceModel, + Aidge::NBitFlipImpl_cpu_forward_kernel<std::uint32_t, std::uint32_t>, nullptr}); } // namespace Aidge #endif - diff --git a/include/aidge/backend/cpu_version.h b/include/aidge/backend/cpu_version.h index fc25859332d39663db013637ace3ed8921a0eac8..fc271801e85b1e18d021ab15199b613cb48de8f9 100644 --- a/include/aidge/backend/cpu_version.h +++ b/include/aidge/backend/cpu_version.h @@ -6,6 +6,6 @@ static constexpr const int PROJECT_VERSION_MAJOR = 0; static constexpr const int PROJECT_VERSION_MINOR = 6; static constexpr const int PROJECT_VERSION_PATCH = 1; static constexpr const char * PROJECT_VERSION = "0.6.1"; -static constexpr const char * PROJECT_GIT_HASH = "ae873a1"; +static constexpr const char * PROJECT_GIT_HASH = "fd321fc"; } #endif // VERSION_H diff --git a/src/operator/BitErrorRate.cpp b/src/operator/BitErrorRate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f6877e6dec631c1a06cd2ed77f58db2d67ed4fa0 --- /dev/null +++ b/src/operator/BitErrorRate.cpp @@ -0,0 +1,48 @@ +/******************************************************************************** + * 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/NBitFlipImpl.hpp" + +#include <stdexcept> +#include <vector> + +#include "aidge/backend/cpu/data/GetCPUPtr.h" +#include "aidge/backend/cpu/operator/BitErrorRateImpl_kernels.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/BitErrorRate.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Log.hpp" +#include "aidge/utils/Types.h" +#include "aidge/utils/Registrar.hpp" + +template <> +void Aidge::BitErrorRateImpl_Cpu::forward() { + const BitErrorRate_Op& op_ = dynamic_cast<const BitErrorRate_Op&>(mOp); + + std::shared_ptr<Tensor> in0 = op_.getInput(0); + std::shared_ptr<Tensor> out0 = op_.getOutput(0); + AIDGE_ASSERT(in0, "missing input #0"); + + // Find the correct kernel type + const auto impl = Registrar<BitErrorRateImpl_Cpu>::create(getBestMatch(getRequiredSpec())); + + // Call kernel + impl.forward( + getCPUPtr(mOp.getRawInput(0)), + getCPUPtr(mOp.getRawOutput(0)), + in0->size(), + op_.ber()); +} + +template <> +void Aidge::BitErrorRateImpl_Cpu::backward() { + AIDGE_THROW_OR_ABORT(std::runtime_error, "Not implemented yet"); +}