diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp index b81145dec13d39ea701f2ae9dfcbd6b6d17bff39..d9e27d96b4c60d947ddc6a965276531aa7907d89 100644 --- a/include/aidge/backend/cpu.hpp +++ b/include/aidge/backend/cpu.hpp @@ -13,6 +13,7 @@ #define AIDGE_CPU_IMPORTS_H_ #include "aidge/backend/cpu/operator/AddImpl.hpp" +#include "aidge/backend/cpu/operator/ArgMaxImpl.hpp" #include "aidge/backend/cpu/operator/AvgPoolingImpl.hpp" #include "aidge/backend/cpu/operator/MaxPoolingImpl.hpp" #include "aidge/backend/cpu/operator/BatchNormImpl.hpp" diff --git a/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp b/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f93abbbca9630a8b60290bae7660f6428b41b0b3 --- /dev/null +++ b/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp @@ -0,0 +1,60 @@ +/******************************************************************************** + * 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 + * + ********************************************************************************/ + +#ifndef AIDGE_CPU_OPERATOR_ARGMAXIMPL_H_ +#define AIDGE_CPU_OPERATOR_ARGMAXIMPL_H_ + +#include <array> +#include <memory> +#include <tuple> +#include <vector> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/operator/ArgMax.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +class ArgMaxImplForward_cpu + : public Registrable<ArgMaxImplForward_cpu, + std::tuple<DataType, DataType>, + void(std::int32_t, + DimSize_t, + const std::vector<DimSize_t>&, + const void *, + void *)> {}; +class ArgMaxImplBackward_cpu + : public Registrable<ArgMaxImplBackward_cpu, + std::tuple<DataType, DataType>, + void(std::int32_t, + DimSize_t, + const std::vector<DimSize_t>&, + const void *, + void *)> {}; + +class ArgMaxImpl_cpu : public OperatorImpl { + public: + ArgMaxImpl_cpu(const ArgMax_Op& op) : OperatorImpl(op, "cpu") {} + + static std::unique_ptr<ArgMaxImpl_cpu> create(const ArgMax_Op &op) { + return std::make_unique<ArgMaxImpl_cpu>(op); + } + + public: + void forward() override; +}; + +namespace { +static Registrar<ArgMax_Op> registrarArgMaxImpl_cpu("cpu", Aidge::ArgMaxImpl_cpu::create); +} // namespace +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_ARGMAXIMPL_H_ */ diff --git a/include/aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a03a02441ebbc81f923b4506b9478f89e461b01b --- /dev/null +++ b/include/aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp @@ -0,0 +1,90 @@ +/******************************************************************************** + * 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_ARGMAXIMPL_FORWARD_KERNEL_H_ +#define AIDGE_CPU_OPERATOR_ARGMAXIMPL_FORWARD_KERNEL_H_ + +#include <algorithm> // std::for_each +#include <cstddef> // std::size_t +#include <cstdint> // std::int32_t +#include <functional> //std::multiplies +#include <numeric> //std::accumulate +#include <vector> +#include <limits> + +#include "aidge/backend/cpu/operator/ArgMaxImpl.hpp" +#include "aidge/data/Data.hpp" +#include "aidge/operator/ArgMax.hpp" +#include "aidge/utils/Registrar.hpp" + +namespace Aidge { +template <class I, class O> +void ArgMaxImpl_cpu_forward_kernel(std::int32_t axis_, + DimSize_t select_last_index, + const std::vector<DimSize_t>& inputDims, + const void* input_, + void* output_) { + + const I* input = static_cast<const I*>(input_); + O* output = static_cast<O*>(output_); + + const std::size_t axis = static_cast<std::size_t>(axis_); + + const std::size_t nb_dims = inputDims.size(); + + auto stride_post = std::unique_ptr<std::size_t[]>(new std::size_t[nb_dims]); + stride_post[nb_dims - 1] = 1; + for (std::size_t i = nb_dims-2; i != static_cast<std::size_t>(-1); --i) { + stride_post[i] = stride_post[i+1]*inputDims[i+1]; + } + auto stride_pre = std::unique_ptr<std::size_t[]>(new std::size_t[nb_dims]); + stride_pre[0] = 1; + for (std::size_t i = 1; i < nb_dims; ++i) { + stride_pre[i] = stride_pre[i-1]*inputDims[i-1]; + } + + const std::size_t dim_i = inputDims[axis]; + for (std::size_t pre = 0; pre < stride_pre[axis]; ++pre) { + for (std::size_t post = 0; post < stride_post[axis]; ++post) { + const std::size_t idx_i = pre * dim_i * stride_post[axis] + post; + const std::size_t idx_o = pre * stride_post[axis] + post; + I max = std::numeric_limits<I>::min(); + for (std::size_t i = 0; i < dim_i; ++i) { + if (select_last_index) { + if (input[idx_i]>=max) + { + output[idx_o] = i; + } + } + else { + if (input[idx_i] > max) + { + output[idx_o] = i; + } + } + + } + } + } + +} + +namespace { +static Registrar<ArgMaxImplForward_cpu> registrarArgMaxImplForward_cpu_Float32( + {DataType::Float32, DataType::Float32}, Aidge::ArgMaxImpl_cpu_forward_kernel<float, float>); +static Registrar<ArgMaxImplForward_cpu> registrarArgMaxImplForward_cpu_Int32( + {DataType::Int32, DataType::Int32}, Aidge::ArgMaxImpl_cpu_forward_kernel<int, int>); +static Registrar<ArgMaxImplForward_cpu> registrarArgMaxImplForward_cpu_Float64( + {DataType::Float64, DataType::Float64}, Aidge::ArgMaxImpl_cpu_forward_kernel<double, double>); +} // namespace +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_ARGMAXIMPL_FORWARD_KERNEL_H_ */ diff --git a/src/operator/ArgMaxImpl.cpp b/src/operator/ArgMaxImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eda3c0b2de62e8e170c2562945999273bdb921a7 --- /dev/null +++ b/src/operator/ArgMaxImpl.cpp @@ -0,0 +1,34 @@ +/******************************************************************************** + * 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/ArgMaxImpl.hpp" + +#include <memory> +#include <vector> + +#include "aidge/utils/Types.h" +#include "aidge/operator/ArgMax.hpp" +#include "aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp" + +void Aidge::ArgMaxImpl_cpu::forward() { + const ArgMax_Op& op_ = dynamic_cast<const ArgMax_Op&>(mOp); + // Find the correct kernel type + auto kernelFunc = Registrar<ArgMaxImplForward_cpu>::create({ + op_.getInput(0)->dataType(), + op_.getOutput(0)->dataType()}); + + // Call kernel + kernelFunc(op_.axis(), + op_.selectLastIndex(), + op_.getInput(0)->dims(), + op_.getInput(0)->getImpl()->rawPtr(), + op_.getOutput(0)->getImpl()->rawPtr()); +} diff --git a/unit_tests/operator/Test_ArgMax.cpp b/unit_tests/operator/Test_ArgMax.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1851d929f39f246a5331da8bbea7119bc2e3875a --- /dev/null +++ b/unit_tests/operator/Test_ArgMax.cpp @@ -0,0 +1,116 @@ +/******************************************************************************** + * 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 <catch2/catch_test_macros.hpp> +#include <memory> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/ArgMax.hpp" +#include "aidge/operator/Conv.hpp" + +#include "aidge/backend/cpu.hpp" +#include "aidge/utils/TensorUtils.hpp" + +using namespace Aidge; + +TEST_CASE("[cpu/operator] ArgMax(forward)", "[ArgMax][CPU]") { + SECTION("3D Tensor") { + std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(Array3D<float,2,3,4> { + { + { + { 1.0, 2.0, 3.0, 4.0}, + { 8.0, 0.0, 17.0, 1.0}, + { 5.0, 10.0, 6.0, 0.0} + }, + { + { 7.0, 1.0, 9.0, 4.0}, + { 0.0, 8.0, 4.0, 2.0}, + { 9.0, 2.0, 0.0, 5.0} + } + } + }); + SECTION("Axis 2") { + + Tensor myOutput = Tensor(Array2D<float,2,3> { + { + { 3.0, 2.0, 1.0 }, + { 2.0, 1.0, 0.0} + } + }); + + std::shared_ptr<Node> myArgMax = ArgMax(2); + auto op = std::static_pointer_cast<OperatorTensor>(myArgMax -> getOperator()); + op->associateInput(0,myInput); + op->setDataType(DataType::Float32); + op->setBackend("cpu"); + myArgMax->forward(); + op->getOutput(0)->print(); + + REQUIRE(*(op->getOutput(0)) == myOutput); + } + SECTION("Axis 1") { + Tensor myOutput = Tensor(Array2D<float,2,4> { + { + { 1.0, 2.0, 1.0, 0.0 }, + { 2.0, 1.0, 0.0, 2.0 } + } + }); + + std::shared_ptr<Node> myArgMax = ArgMax(1); + auto op = std::static_pointer_cast<OperatorTensor>(myArgMax -> getOperator()); + op->associateInput(0,myInput); + op->setDataType(DataType::Float32); + op->setBackend("cpu"); + myArgMax->forward(); + myOutput.print(); + op->getOutput(0)->print(); + REQUIRE(*(op->getOutput(0)) == myOutput); + } + SECTION("Axis 0") { + Tensor myOutput = Tensor(Array2D<float,3,4> { + { + { 1.0, 0.0, 1.0, 0.0 }, + { 0.0, 1.0, 0.0, 1.0 }, + { 1.0, 0.0, 0.0, 1.0 } + }, + }); + + std::shared_ptr<Node> myArgMax = ArgMax(1); + auto op = std::static_pointer_cast<OperatorTensor>(myArgMax -> getOperator()); + op->associateInput(0,myInput); + op->setDataType(DataType::Float32); + op->setBackend("cpu"); + myArgMax->forward(); + myOutput.print(); + op->getOutput(0)->print(); + REQUIRE(*(op->getOutput(0)) == myOutput); + } + } + SECTION("Select_Last_Index") { + std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(Array1D<float,10> { + { + 1.0, 5.0, 9.0, 0.0, 6.0, 2.0, 9.0, 4.0, 3.0, 9.0 + } + }); + std::shared_ptr<Tensor> myOutput = std::make_shared<Tensor>(Array1D<float,1> {{9}}); + + std::shared_ptr<Node> myArgMax = ArgMax(0, 1, 1); + auto op = std::static_pointer_cast<OperatorTensor>(myArgMax -> getOperator()); + op->associateInput(0,myInput); + op->setDataType(DataType::Float32); + op->setBackend("cpu"); + myArgMax->forward(); + op->getOutput(0)->print(); + + REQUIRE(*(op->getOutput(0)) == *myOutput); + + } +} \ No newline at end of file