diff --git a/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp b/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0aec0f52263e41974454e5f9622a9aa83a189cb0 --- /dev/null +++ b/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp @@ -0,0 +1,103 @@ +/******************************************************************************** + * 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_REDUCEMEANIMPL_H_ +#define AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_H_ + +#include <array> +#include <memory> +#include <tuple> +#include <vector> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/operator/ReduceMean.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +// class ReduceMean_Op; + +// compute kernel registry for forward and backward +// DIM 1 +class ReduceMeanImpl1DForward_cpu + : public Registrable<ReduceMeanImpl1DForward_cpu, + std::tuple<DataType, DataType>, + void(const ReduceMean_Op<1>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {}; +class ReduceMeanImpl1DBackward_cpu + : public Registrable<ReduceMeanImpl1DBackward_cpu, + std::tuple<DataType, DataType>, + void(const ReduceMean_Op<1>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {}; + +// DIM 2 +class ReduceMeanImpl2DForward_cpu + : public Registrable<ReduceMeanImpl2DForward_cpu, + std::tuple<DataType, DataType>, + void(const ReduceMean_Op<2>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {}; +class ReduceMeanImpl2DBackward_cpu + : public Registrable<ReduceMeanImpl2DBackward_cpu, + std::tuple<DataType, DataType>, + void(const ReduceMean_Op<2>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {}; +// DIM 3 +class ReduceMeanImpl3DForward_cpu + : public Registrable<ReduceMeanImpl3DForward_cpu, + std::tuple<DataType, DataType>, + void(const ReduceMean_Op<3>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {}; +class ReduceMeanImpl3DBackward_cpu + : public Registrable<ReduceMeanImpl3DBackward_cpu, + std::tuple<DataType, DataType>, + void(const ReduceMean_Op<3>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {}; + +class ReduceMeanImpl1D_cpu : public OperatorImpl { + public: + ReduceMeanImpl1D_cpu(const ReduceMean_Op<1>& op) : OperatorImpl(op) {} + + static std::unique_ptr<ReduceMeanImpl1D_cpu> create(const ReduceMean_Op<1> &op) { + return std::make_unique<ReduceMeanImpl1D_cpu>(op); + } + + public: + NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final; + void forward() override; +}; + +class ReduceMeanImpl2D_cpu : public OperatorImpl { + public: + ReduceMeanImpl2D_cpu(const ReduceMean_Op<2>& op) : OperatorImpl(op) {} + + static std::unique_ptr<ReduceMeanImpl2D_cpu> create(const ReduceMean_Op<2> &op) { + return std::make_unique<ReduceMeanImpl2D_cpu>(op); + } + + public: + NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final; + void forward() override; +}; + +class ReduceMeanImpl3D_cpu : public OperatorImpl { + public: + ReduceMeanImpl3D_cpu(const ReduceMean_Op<3>& op) : OperatorImpl(op) {} + + static std::unique_ptr<ReduceMeanImpl3D_cpu> create(const ReduceMean_Op<3> &op) { + return std::make_unique<ReduceMeanImpl3D_cpu>(op); + } + + public: + NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final; + void forward() override; +}; +namespace { +// add cpu backend to ReduceMean_Op<2> implementation registry +static Registrar<ReduceMean_Op<2>> registrarReduceMeanImpl2D_cpu("cpu", Aidge::ReduceMeanImpl2D_cpu::create); +static Registrar<ReduceMean_Op<3>> registrarReduceMeanImpl3D_cpu("cpu", Aidge::ReduceMeanImpl3D_cpu::create); +} // namespace +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_H_ */ diff --git a/include/aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0a27cf9827d7845f4e0473db4d2d5d195989f1f6 --- /dev/null +++ b/include/aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp @@ -0,0 +1,113 @@ +/******************************************************************************** + * 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_REDUCEMEANIMPL_FORWARD_KERNEL_H_ +#define AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_FORWARD_KERNEL_H_ + +#include "aidge/utils/Registrar.hpp" +#include "aidge/operator/ReduceMean.hpp" +#include "aidge/backend/cpu/operator/ReduceMeanImpl.hpp" +#include <array> +#include <cstddef> +#include <algorithm> +#include "aidge/data/Data.hpp" + +namespace Aidge { +template <class I, class O, DimSize_t DIM> +void ReduceMeanImpl_cpu_forward_kernel(const typename ReduceMean_Op<DIM>::Attrs& attrs, + 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_); + + DimSize_t keepDims = std::get<1>(attrs); + // Calculate the total number of elements in the input array + size_t totalElements = 1; + for (size_t dimSize : inputDims) { + totalElements *= dimSize; + } + + // Create a temporary arrays to store intermediate input/output for each ReduceDim op + std::vector<I> tempInArray(input, input + totalElements); + std::vector<I> tempOutArray(input, input + totalElements); + std::vector<size_t> currentDims = inputDims; + + + std::size_t addedElems = 0; + for(std::size_t i=0; i<1 ; ++i) + { + addedElems = 0; + I* tempOutArrayPtr = tempOutArray.data(); + + std::size_t axis = std::get<0>(attrs)[i]; + std::size_t nbElemAfterAxis = 1; + std::size_t nbElemBeforeAxis = 1; + + for (size_t d = 0; d < currentDims.size(); ++d) { + nbElemAfterAxis *= (d > axis) ? currentDims[d]:1; + nbElemBeforeAxis *= (d < axis) ? currentDims[d]:1; + } + + for (std::size_t j=0; j<nbElemBeforeAxis; ++j) + { + for (std::size_t k=0; k<nbElemAfterAxis; ++k) + { + I mean = 0; + for(std::size_t l=0; l<currentDims[axis];l++) + { + size_t idx = j*(nbElemAfterAxis*currentDims[axis])+l*currentDims[axis]+k; + mean+= tempInArray[idx]; + } + tempOutArrayPtr[addedElems] = mean/currentDims[axis]; + addedElems++; + } + } + + // Update the input for the next slice operation + tempInArray.assign(tempOutArray.begin(), tempOutArray.begin() + addedElems); + if(keepDims) + currentDims[axis] = 1; + else + currentDims.erase(currentDims.begin()+axis); + + } + std::copy_n(tempInArray.data(), addedElems, output); +} +namespace { +// DIM = 1 +static Registrar<ReduceMeanImpl1DForward_cpu> registrarReduceMeanImplForward_1D_cpu_Float32( + {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float,1>); +static Registrar<ReduceMeanImpl1DForward_cpu> registrarReduceMeanImplForward_1D_cpu_Int32( + {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int,1>); +static Registrar<ReduceMeanImpl1DForward_cpu> registrarReduceMeanImplForward_1D_cpu_Float64( + {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double,1>); + +// DIM = 2 +static Registrar<ReduceMeanImpl2DForward_cpu> registrarReduceMeanImplForward_2D_cpu_Float32( + {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float,2>); +static Registrar<ReduceMeanImpl2DForward_cpu> registrarReduceMeanImplForward_2D_cpu_Int32( + {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int,2>); +static Registrar<ReduceMeanImpl2DForward_cpu> registrarReduceMeanImplForward_2D_cpu_Float64( + {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double,2>); + +// DIM = 3 +static Registrar<ReduceMeanImpl3DForward_cpu> registrarReduceMeanImplForward_3D_cpu_Float32( + {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float,3>); +static Registrar<ReduceMeanImpl3DForward_cpu> registrarReduceMeanImplForward_3D_cpu_Int32( + {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int,3>); +static Registrar<ReduceMeanImpl3DForward_cpu> registrarReduceMeanImplForward_3D_cpu_Float64( + {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double,3>); +} // namespace +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_FORWARD_KERNEL_H_ */ diff --git a/src/operator/ReduceMeanImpl.cpp b/src/operator/ReduceMeanImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f8a0d34d713dc2893e32ea2e1298e89022527f0 --- /dev/null +++ b/src/operator/ReduceMeanImpl.cpp @@ -0,0 +1,85 @@ +/******************************************************************************** + * 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 <cassert> +#include <chrono> // std::chrono::milliseconds +#include <numeric> // std::accumulate +#include <thread> // std::this_thread::sleep_for +#include <vector> + +#include "aidge/utils/Types.h" +#include "aidge/operator/ReduceMean.hpp" + +#include "aidge/backend/cpu/operator/ReduceMeanImpl.hpp" +#include "aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp" +Aidge::NbElts_t Aidge::ReduceMeanImpl1D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const { + // this implementation can be in-place + return 0; +} +Aidge::NbElts_t Aidge::ReduceMeanImpl2D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const { + // this implementation can be in-place + return 0; +} +Aidge::NbElts_t Aidge::ReduceMeanImpl3D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const { + // this implementation can be in-place + return 0; +} + +void Aidge::ReduceMeanImpl1D_cpu::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<ReduceMeanImpl1DForward_cpu>::create({ + mOp.getInput(0)->dataType(), + mOp.getOutput(0)->dataType()}); + + // Call kernel + kernelFunc(dynamic_cast<const ReduceMean_Op<1>&>(mOp).getStaticAttributes(), + mOp.getInput(0)->dims(), + mOp.getInput(0)->getImpl()->rawPtr(), + mOp.getOutput(0)->getImpl()->rawPtr()); +} + +void Aidge::ReduceMeanImpl2D_cpu::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<ReduceMeanImpl2DForward_cpu>::create({ + mOp.getInput(0)->dataType(), + mOp.getOutput(0)->dataType()}); + + // Call kernel + kernelFunc(dynamic_cast<const ReduceMean_Op<2>&>(mOp).getStaticAttributes(), + mOp.getInput(0)->dims(), + mOp.getInput(0)->getImpl()->rawPtr(), + mOp.getOutput(0)->getImpl()->rawPtr()); +} + +void Aidge::ReduceMeanImpl3D_cpu::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<ReduceMeanImpl3DForward_cpu>::create({ + mOp.getInput(0)->dataType(), + mOp.getOutput(0)->dataType()}); + + // Call kernel + kernelFunc(dynamic_cast<const ReduceMean_Op<3>&>(mOp).getStaticAttributes(), + mOp.getInput(0)->dims(), + mOp.getInput(0)->getImpl()->rawPtr(), + mOp.getOutput(0)->getImpl()->rawPtr()); +} \ No newline at end of file diff --git a/unit_tests/operator/Test_ReduceMeanImpl.cpp b/unit_tests/operator/Test_ReduceMeanImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24ef7b993fabba8fd2139570990ba6a1fc4784e4 --- /dev/null +++ b/unit_tests/operator/Test_ReduceMeanImpl.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * 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 <memory> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/ReduceMean.hpp" +#include "aidge/operator/Conv.hpp" + +#include "aidge/backend/cpu.hpp" + +using namespace Aidge; + +TEST_CASE("[cpu/operator] ReduceMean(forward)") { + std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(Array3D<float,3,2,2> { + { + { + { 5.0, 1.0 }, + { 20.0, 2.0 } + }, + { + { 30.0, 1.0 }, + { 40.0, 2.0 } + }, + { + { 55.0, 1.0 }, + { 60.0, 2.0 } + } + } + }); + std::shared_ptr<Tensor> myOutput = std::make_shared<Tensor>(Array3D<float,3,1,2> { + { + + {{ 12.5, 1.5 }}, + {{ 35.0, 1.5 }}, + {{ 57.5, 1.5 }} + } + }); + //TODO fix case of DIM=1 + std::shared_ptr<Node> myReduceMean = ReduceMean({1,1}); + myReduceMean->getOperator()->setDatatype(DataType::Float32); + myReduceMean->getOperator()->setBackend("cpu"); + myReduceMean->getOperator()->associateInput(0,myInput); + myReduceMean->getOperator()->computeOutputDims(); + myReduceMean->forward(); + myReduceMean->getOperator()->getOutput(0)->print(); + + float* resPtr = static_cast<float*>(myReduceMean->getOperator()->getOutput(0)->getImpl()->rawPtr()); + float* expectedPtr = static_cast<float*>(myOutput->getImpl()->rawPtr()); + for (std::size_t i = 0; i< myOutput->size(); ++i) { + REQUIRE(std::abs(resPtr[i]-expectedPtr[i]) < 0.00001); + } +} \ No newline at end of file