diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp index 1f45d700f6fc9f1d69682cb2de601979049c0af6..e3fa457c5cf557328a7fbf2049b9e577c44391f9 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/data/TensorImpl.hpp" + #include "aidge/backend/cpu/operator/AddImpl.hpp" #include "aidge/backend/cpu/operator/AvgPoolingImpl.hpp" #include "aidge/backend/cpu/operator/MaxPoolingImpl.hpp" @@ -24,7 +25,8 @@ #include "aidge/backend/cpu/operator/MatMulImpl.hpp" #include "aidge/backend/cpu/operator/ProducerImpl.hpp" #include "aidge/backend/cpu/operator/ReLUImpl.hpp" -#include "aidge/backend/cpu/operator/SoftmaxImpl.hpp" #include "aidge/backend/cpu/operator/ScalingImpl.hpp" +#include "aidge/backend/cpu/operator/SliceImpl.hpp" +#include "aidge/backend/cpu/operator/SoftmaxImpl.hpp" #endif /* AIDGE_CPU_IMPORTS_H_ */ \ No newline at end of file diff --git a/include/aidge/backend/cpu/operator/SliceImpl.hpp b/include/aidge/backend/cpu/operator/SliceImpl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c50049206f9fd9b70d6d724aa6d651998d1f1de1 --- /dev/null +++ b/include/aidge/backend/cpu/operator/SliceImpl.hpp @@ -0,0 +1,112 @@ +/******************************************************************************** + * 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_SLICEIMPL_H_ +#define AIDGE_CPU_OPERATOR_SLICEIMPL_H_ + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/operator/Slice.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" +#include <memory> +#include <vector> + +namespace Aidge { +// class Slice_Op; + +// compute kernel registry for forward and backward +template <DimIdx_t DIM> +class SliceImplForward_cpu + : public Registrable<SliceImplForward_cpu<DIM>, std::tuple<DataType>, void(std::array<DimSize_t, DIM>, std::array<DimSize_t, DIM>, const void*, void*)> { +}; +template <DimIdx_t DIM> +class SliceImplBackward_cpu + : public Registrable<SliceImplBackward_cpu<DIM>, std::tuple<DataType>, void(std::array<DimSize_t, DIM>, std::array<DimSize_t, DIM>, const void*, void*)> { +}; + +template <DimIdx_t DIM> +class SliceImpl_cpu : public OperatorImpl { + private: + const Slice_Op<DIM>& mOp; + std::array<NbElts_t, 1> mNbConsumedData; + std::array<NbElts_t, 1> mNbProducedData; + + public: + SliceImpl_cpu(const Slice_Op<DIM>& op) : mOp(op), mNbConsumedData({0}), mNbProducedData({0}) {} + + static std::unique_ptr<SliceImpl_cpu> create(const Slice_Op<DIM>& op) { + return std::make_unique<SliceImpl_cpu>(op); + } + + public: + NbElts_t getNbRequiredData(const IOIndex_t /*inputIdx*/) const override final { + assert(mOp.getInput(0) && "requires valid input"); + + // Requires the whole tensors + const auto& inputDims = mOp.getInput(0)->dims(); + + return std::accumulate(inputDims.begin(), inputDims.end(), + static_cast<NbElts_t>(1), std::multiplies<NbElts_t>()); + } + NbElts_t getNbRequiredProtected(const IOIndex_t /*inputIdx*/) const override final { + return 0; + } + NbElts_t getRequiredMemory(const IOIndex_t outputIdx, const std::vector<DimSize_t>& inputsSize) const override final { + (void) outputIdx; + (void) inputsSize; + const auto& outputDims = mOp.getOutput(0)->dims(); + return std::accumulate(outputDims.begin(), outputDims.end(), + static_cast<NbElts_t>(1), std::multiplies<NbElts_t>()); + } + NbElts_t getNbConsumedData(const IOIndex_t /*inputIdx*/) const override final { + return mNbConsumedData[0]; + } + NbElts_t getNbProducedData(const IOIndex_t outputIdx) const override final { + return mNbProducedData[0]; + } + void updateConsummerProducer() override final { + mNbConsumedData[0]+= getNbRequiredData(0); // each input is consumed by the minimum amount for a forward pass + + mNbProducedData[0]+= getRequiredMemory(0, {}); + } + + void forward() { + // FIXME: uncomment the following code once memory handling will work + assert(mOp.getInput(0) && "missing input #0"); + + // Find the correct kernel type + auto kernelFunc = Registrar<SliceImplForward_cpu<DIM>>::create({ + mOp.getInput(0)->dataType(), + mOp.getOutput(0)->dataType()}); + + // Call kernel + kernelFunc(mOp->getInput(0)->dims(), + mOp->template getAttr<SliceAttr::SliceDims>(), + mOp.getInput(0)->getImpl()->rawPtr(), + mOp.getOutput(0)->getImpl()->rawPtr()); + + mNbConsumedData[0]+= getNbRequiredData(0); // each input is consumed by the minimum amount for a forward pass + + mNbProducedData[0]+= getRequiredMemory(0, {}); + } + + void backward() { + printf("Not implemented yet.\n"); + } +}; + +namespace { +template <DimIdx_t DIM> +static Registrar<Slice_Op<DIM>> registrarSliceImpl_cpu("cpu", Aidge::SliceImpl_cpu<DIM>::create); +} +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_H_ */ diff --git a/include/aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp new file mode 100644 index 0000000000000000000000000000000000000000..18e599880a91fd881525977c1d37591944565c8c --- /dev/null +++ b/include/aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp @@ -0,0 +1,71 @@ +/******************************************************************************** + * 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_SLICEIMPL_FORWARD_KERNEL_H_ +#define AIDGE_CPU_OPERATOR_SLICEIMPL_FORWARD_KERNEL_H_ + +#include "aidge/utils/Registrar.hpp" +#include <array> +#include <cstddef> + +#include "aidge/backend/cpu/operator/SliceImpl.hpp" + +namespace Aidge { +template <class I, class O, std::size_t DIM> +void SliceImpl_cpu_forward_kernel(const std::array<std::size_t, DIM> inputDims, + const std::array<std::size_t, DIM> slicedDims, + const void* input_, + void* output_) { + + const I* input = static_cast<const I*>(input_); + O* output = static_cast<O*>(output_); + + // for inputDims = {4,5,5,3} & slicedDims = {3,2,2,1}, substractDims = {1,5,5,3} + std::array<std::size_t, DIM> substractedDims; + for (std::size_t i = 0; i < DIM; ++i) { + substractedDims[i] = inputDims[i] - slicedDims[i]; + } + + // for slicedDims = {3,2,2,1}, prodSlicedDims = {12,4,2,1} + std::array<std::size_t, DIM> prodSlicedDims; + std::array<std::size_t, DIM+1> prodInputDims; + prodSlicedDims[DIM - 1] = slicedDims[DIM - 1]; + prodInputDims[DIM - 1] = inputDims[DIM - 1]; + prodInputDims[DIM] = 1; + for (std::size_t i = 2; i < DIM; ++i) { + prodSlicedDims[DIM - i] = prodSlicedDims[DIM - i + 1]*slicedDims[DIM - i]; + prodInputDims[DIM - i] = prodInputDims[DIM - i + 1]*inputDims[DIM - i]; + } + + std::size_t j = 0; + std::size_t i = 0; + for (std::size_t = 0; j < prodSlicedDims[0]; ++j) { + output[j] = input[i++]; + for (std::size_t idx = DIM - 1; idx > 0; --idx) { + i += j % prodSlicedDims[idx] == 0 ? substractedDims[idx]*prodInputDims[idx+1] : 0; + } + } +} + +namespace { +template <std::size_t DIM> +static Registrar<SliceImplForward_cpu<DIM>> registrarSliceImplForward_cpu_Float32( + {DataType::Float32, DataType::Float32}, Aidge::SliceImpl_cpu_forward_kernel<float, float, DIM>); +template <std::size_t DIM> +static Registrar<SliceImplForward_cpu<DIM>> registrarSliceImplForward_cpu_Int32( + {DataType::Int32, DataType::Int32}, Aidge::SliceImpl_cpu_forward_kernel<int, int, DIM>); +template <std::size_t DIM> +static Registrar<SliceImplForward_cpu<DIM>> registrarSliceImplForward_cpu_Float64( + {DataType::Float64, DataType::Float64}, Aidge::SliceImpl_cpu_forward_kernel<double, double, DIM>); +} // namespace +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_FORWARD_KERNEL_H_ */ diff --git a/unit_tests/operator/Test_SliceImpl.cpp b/unit_tests/operator/Test_SliceImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..486f6edec006d3505cabe2d66b3820862cde3b69 --- /dev/null +++ b/unit_tests/operator/Test_SliceImpl.cpp @@ -0,0 +1,150 @@ +/******************************************************************************** + * 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 <catch2/catch_test_macros.hpp> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/Slice.hpp" + +#include "aidge/backend/cpu.hpp" + +using namespace Aidge; + +TEST_CASE("[cpu/operator] Slice(forward)") { + SECTION("1D Tensor") { + std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array1D<int,10> { + {0, 1, 2,-3, 4,-5,-6, 7, 8, 9} + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<int,4> { + {0, 1, 2,-3} + }); + + std::shared_ptr<Node> mySlice = Slice(0, {4}); + mySlice->getOperator()->setDatatype(DataType::Int32); + mySlice->getOperator()->setBackend("cpu"); + mySlice->getOperator()->associateInput(0,input0); + mySlice->getOperator()->computeOutputDims(); + mySlice->forward(); + REQUIRE(mySlice->getOperator()->output(0) == *expectedOutput); + } + + SECTION("2D Tensor") { + std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array2D<int,2,10> { + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + } + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array2D<int,2,3> { + { + {-5,-6, 7}, + {-5,-6, 7} + } + }); + + std::shared_ptr<Node> mySlice = Slice(5, {2,3}); + mySlice->getOperator()->setDatatype(DataType::Int32); + mySlice->getOperator()->setBackend("cpu"); + mySlice->getOperator()->associateInput(0,input0); + mySlice->getOperator()->computeOutputDims(); + mySlice->forward(); + REQUIRE(*mySlice->getOperator()->getOutput(0) == *expectedOutput); + } + + SECTION("3D Tensor") { + std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array3D<int,2,2,10> { + { + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + }, + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + } + } + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array3D<int,1,1,3> { + { + { + { 4,-5,-6} + } + } + }); + + std::shared_ptr<Node> mySlice = Slice(14, {1,1,3}); + mySlice->getOperator()->setDatatype(DataType::Int32); + mySlice->getOperator()->setBackend("cpu"); + mySlice->getOperator()->associateInput(0,input0); + mySlice->getOperator()->computeOutputDims(); + mySlice->forward(); + REQUIRE(mySlice->getOperator()->output(0) == *expectedOutput); + } + + SECTION("4D Tensor") { + std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array4D<int,2,2,2,10> { + { + { + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + }, + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + } + }, + { + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + }, + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + } + } + } + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array4D<int,2,2,2,10> { + { + { + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + }, + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + } + }, + { + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + }, + { + { 0, 1, 2,-3, 4,-5,-6, 7, 8, 9}, + {-5, 4, 2,-3, 4,-5,-6, 7,-1,10} + } + } + } + }); + + std::shared_ptr<Node> mySlice = Slice(0, {2,2,2,10}); + mySlice->getOperator()->setDatatype(DataType::Int32); + mySlice->getOperator()->setBackend("cpu"); + mySlice->getOperator()->associateInput(0,input0); + mySlice->getOperator()->computeOutputDims(); + mySlice->forward(); + REQUIRE(mySlice->getOperator()->output(0) == *expectedOutput); + } +} \ No newline at end of file