diff --git a/include/aidge/aidge.hpp b/include/aidge/aidge.hpp index 931b1b26a04e8886c211d77f8b0147c2140d350a..940440bad52e367fe04872a308c99e4c802fa242 100644 --- a/include/aidge/aidge.hpp +++ b/include/aidge/aidge.hpp @@ -59,9 +59,11 @@ #include "aidge/operator/ReduceMean.hpp" #include "aidge/operator/ReLU.hpp" #include "aidge/operator/Reshape.hpp" +#include "aidge/operator/Shape.hpp" #include "aidge/operator/Scaling.hpp" #include "aidge/operator/Slice.hpp" #include "aidge/operator/Softmax.hpp" +#include "aidge/operator/Split.hpp" #include "aidge/operator/Sqrt.hpp" #include "aidge/operator/Sub.hpp" #include "aidge/operator/Transpose.hpp" diff --git a/include/aidge/operator/Shape.hpp b/include/aidge/operator/Shape.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3132e4ab7adcc331772d627147cc31c25597570a --- /dev/null +++ b/include/aidge/operator/Shape.hpp @@ -0,0 +1,103 @@ +/******************************************************************************** + * 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_CORE_OPERATOR_SHAPE_H_ +#define AIDGE_CORE_OPERATOR_SHAPE_H_ + +#include <cstdint> // std::int64_t +#include <memory> +#include <string> +#include <vector> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/graph/Node.hpp" +#include "aidge/operator/OperatorTensor.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +class Shape_OpImpl : public OperatorImpl { +public: + Shape_OpImpl(const Operator& op, const std::string& backend = ""): OperatorImpl(op, backend) {} + void forward() override; +}; + +enum class ShapeAttr { Start, End }; + +class Shape_Op : public OperatorTensor, + public Registrable<Shape_Op, + std::string, + std::shared_ptr<OperatorImpl>(const Shape_Op&)>, + public StaticAttributes<ShapeAttr, std::int64_t, std::int64_t> { + +public: + static const std::string Type; + + Shape_Op() = delete; + + using Attributes_ = StaticAttributes<ShapeAttr, std::int64_t, std::int64_t>; + template <ShapeAttr e> using attr = typename Attributes_::template attr<e>; + Shape_Op(std::int64_t start, std::int64_t end) + : OperatorTensor(Type, 1, 0, 1), + Attributes_(attr<ShapeAttr::Start>(start), + attr<ShapeAttr::End>(end)) + { + mImpl = std::make_shared<Shape_OpImpl>(*this); + } + + /** + * @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated). + * @param op Operator to copy. + */ + Shape_Op(const Shape_Op& op) + : OperatorTensor(op), + Attributes_(op) + { + if (!op.backend().empty()) { + SET_IMPL_MACRO(Shape_Op, *this, op.backend()); + } + else { + mImpl = std::make_shared<Shape_OpImpl>(*this); + } + } + + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::Shape_Op + */ + std::shared_ptr<Operator> clone() const override { + return std::make_shared<Shape_Op>(*this); + } + + bool forwardDims(bool /*allowDataDependency*/ = false) override final; + + void setBackend(const std::string& name, DeviceIdx_t device = 0) override; + + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } +}; + +inline std::shared_ptr<Node> Shape(std::int64_t start = 0, std::int64_t end = -1, const std::string& name = "") { + return std::make_shared<Node>(std::make_shared<Shape_Op>(start, end), name); +} +} // namespace Aidge + +namespace { +template <> +const char *const EnumStrings<Aidge::ShapeAttr>::data[] = {"Start", "End"}; +} + +#endif /* AIDGE_CORE_OPERATOR_SHAPE_H_ */ diff --git a/include/aidge/operator/Split.hpp b/include/aidge/operator/Split.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ff50a6aa7b8de971431515a09ca4e684dcc51865 --- /dev/null +++ b/include/aidge/operator/Split.hpp @@ -0,0 +1,111 @@ +/******************************************************************************** + * 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_CORE_OPERATOR_SPLIT_H_ +#define AIDGE_CORE_OPERATOR_SPLIT_H_ + +#include <memory> +#include <vector> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/graph/Node.hpp" +#include "aidge/operator/OperatorTensor.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +class Split_OpImpl : public OperatorImpl { +public: + Split_OpImpl(const Operator& op, const std::string& backend = ""): OperatorImpl(op, backend) {} + void forward() override; +}; + +enum class SplitAttr { Axis, Split }; + +class Split_Op + : public OperatorTensor, + public Registrable<Split_Op, std::string, std::shared_ptr<OperatorImpl>(const Split_Op &)>, + public StaticAttributes<SplitAttr, std::int8_t, std::vector<DimSize_t>> { + +public: + static const std::string Type; + + Split_Op() = delete; + + using Attributes_ = StaticAttributes<SplitAttr, std::int8_t, std::vector<DimSize_t>>; + template <SplitAttr e> using attr = typename Attributes_::template attr<e>; + Split_Op( std::int8_t axis, DimSize_t nbOutputs, const std::vector<DimSize_t>& split) + : OperatorTensor(Type, 2, 0, nbOutputs), + Attributes_(attr<SplitAttr::Axis>(axis), + attr<SplitAttr::Split>(split)) + { + mImpl = std::make_shared<Split_OpImpl>(*this); + } + + + /** + * @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its + * input tensors (the new operator has no input associated). + * @param op Operator to copy. + */ + Split_Op(const Split_Op &op) + : OperatorTensor(op), + Attributes_(op) + { + if (!op.backend().empty()) { + SET_IMPL_MACRO(Split_Op, *this, op.backend()); + } + else { + mImpl = std::make_shared<Split_OpImpl>(*this); + } + } +public: + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::Split_Op + */ + std::shared_ptr<Operator> clone() const override { return std::make_shared<Split_Op>(*this); } + + bool dimsForwarded() const override final; + bool forwardDims(bool allowDataDependency = false) override final; + + void setBackend(const std::string &name, DeviceIdx_t device = 0) override; + + static const std::vector<std::string> getInputsName(){ + return {"data_input", "split"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output_0", "data_output_n"}; + } + +}; + +/** + * @brief Exract a sub-Tensor from a bigger original Tensor. + * @param name Name of the Operator. + * @return std::shared_ptr<Node> A Node containing the Operator. + */ +inline std::shared_ptr<Node> Split(DimSize_t nbOutput, + std::int8_t axis = 0, + const std::vector<DimSize_t>& split = {}, + const std::string &name = "") { + return std::make_shared<Node>(std::make_shared<Split_Op>(axis, nbOutput, split), name); +} +} // namespace Aidge + +namespace { +template <> +const char *const EnumStrings<Aidge::SplitAttr>::data[] = { "Axis", "Split" }; +} + +#endif /* AIDGE_CORE_OPERATOR_SPLIT_H_ */ diff --git a/python_binding/operator/pybind_Shape.cpp b/python_binding/operator/pybind_Shape.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbae1d95d81ef65d27167bcd0774366dcc41b325 --- /dev/null +++ b/python_binding/operator/pybind_Shape.cpp @@ -0,0 +1,38 @@ +/******************************************************************************** + * 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 <pybind11/pybind11.h> +#include <string> +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/Shape.hpp" +#include "aidge/operator/OperatorTensor.hpp" + +namespace py = pybind11; +namespace Aidge { + +void init_Shape(py::module& m) { + py::class_<Shape_Op, std::shared_ptr<Shape_Op>, Attributes, OperatorTensor>(m, "ShapeOp", py::multiple_inheritance()) + .def(py::init<std::int64_t, + std::int64_t>(), + py::arg("start"), + py::arg("end")) + .def_static("get_inputs_name", &Shape_Op::getInputsName) + .def_static("get_outputs_name", &Shape_Op::getOutputsName) + .def_static("attributes_name", &Shape_Op::staticGetAttrsName); + + declare_registrable<Shape_Op>(m, "ShapeOp"); + + m.def("Shape", &Shape, py::arg("start") = 0, py::arg("end") = -1, py::arg("name") = ""); +} + +} // namespace Aidge diff --git a/python_binding/operator/pybind_Split.cpp b/python_binding/operator/pybind_Split.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6efc123864f21bf8ea02008b29fe59f31685f17c --- /dev/null +++ b/python_binding/operator/pybind_Split.cpp @@ -0,0 +1,38 @@ +/******************************************************************************** + * 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 <pybind11/pybind11.h> +#include <string> +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/Split.hpp" +#include "aidge/operator/OperatorTensor.hpp" + +namespace py = pybind11; +namespace Aidge { + +void init_Split(py::module& m) { + py::class_<Split_Op, std::shared_ptr<Split_Op>, Attributes, OperatorTensor>(m, "SplitOp", py::multiple_inheritance()) + .def(py::init<DimSize_t, std::int8_t, std::vector<DimSize_t>&>(), + py::arg("nb_outputs"), + py::arg("axis"), + py::arg("split")) + .def_static("get_inputs_name", &Split_Op::getInputsName) + .def_static("get_outputs_name", &Split_Op::getOutputsName) + .def_static("attributes_name", &Split_Op::staticGetAttrsName); + + declare_registrable<Split_Op>(m, "SplitOp"); + + m.def("Split", &Split, py::arg("nb_outputs"), py::arg("axis") = 0, py::arg("split") = std::vector<DimSize_t>(), py::arg("name") = ""); +} + +} // namespace Aidge diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp index 7b38c2d72d5f4b2eed8d8bbf9f41f47144b51060..42e29fd43324d12ea4cac2c16c88a056903b7c54 100644 --- a/python_binding/pybind_core.cpp +++ b/python_binding/pybind_core.cpp @@ -52,9 +52,11 @@ void init_ReduceMean(py::module&); void init_ReLU(py::module&); void init_Reshape(py::module&); void init_Scaling(py::module&); +void init_Shape(py::module&); void init_Sigmoid(py::module&); void init_Slice(py::module&); void init_Softmax(py::module&); +void init_Split(py::module&); void init_Sqrt(py::module&); void init_Sub(py::module&); void init_Tanh(py::module&); @@ -120,9 +122,11 @@ void init_Aidge(py::module& m) { init_ReLU(m); init_Reshape(m); init_Scaling(m); + init_Shape(m); init_Sigmoid(m); init_Slice(m); init_Softmax(m); + init_Split(m); init_Sqrt(m); init_Sub(m); init_Tanh(m); diff --git a/src/operator/Shape.cpp b/src/operator/Shape.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d11cf39e1cd301d49f21863dcb1f250e96c6e502 --- /dev/null +++ b/src/operator/Shape.cpp @@ -0,0 +1,70 @@ +/******************************************************************************** + * 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 <cstddef> // std::size_t +#include <cstdint> // std::int64_t +#include <string> +#include <vector> + +#include "aidge/operator/Shape.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Types.h" + +void Aidge::Shape_OpImpl::forward() { + const Shape_Op& op = dynamic_cast<const Shape_Op&>(mOp); + const auto start = op.template getAttr<std::int64_t>("Start"); + const auto end = op.template getAttr<std::int64_t>("End"); + + op.getOutput(0)->getImpl()->copyCast(std::next(op.getInput(0)->dims().data(), + start), + DataType::UInt64, + end - start + 1); +} + +const std::string Aidge::Shape_Op::Type = "Shape"; + +bool Aidge::Shape_Op::forwardDims(bool /*allowDataDependency*/) { + // check data input has been associated + if (!getInput(0)) { + AIDGE_THROW_OR_ABORT(std::runtime_error, "{}: input #0 should be associated with a Tensor", type()); + } + + if (getInput(0)->empty()) { + return false; + } + + if (this->template getAttr<std::int64_t>("Start") < 0) + this->template getAttr<std::int64_t>("Start") += static_cast<std::int64_t>(getInput(0)->nbDims()); + if (this->template getAttr<std::int64_t>("End") < 0) + this->template getAttr<std::int64_t>("End") += static_cast<std::int64_t>(getInput(0)->nbDims()); + + const auto start = this->template getAttr<std::int64_t>("Start"); + const auto end = this->template getAttr<std::int64_t>("End"); + const auto nbDims = static_cast<std::int64_t>(getInput(0)->nbDims()); + const DimSize_t roi = end - start + 1; + + AIDGE_ASSERT(start < nbDims && end < nbDims, "'Start' and 'End' must be < {}", nbDims); + AIDGE_ASSERT(roi> 1, "Unvalid ROI for Shape"); + + mOutputs[0]->resize({roi}); + return true; +} + +void Aidge::Shape_Op::setBackend(const std::string& name, Aidge::DeviceIdx_t device) { + if (Registrar<Shape_Op>::exists({name})) { + SET_IMPL_MACRO(Shape_Op, *this, name); + } + else { + mImpl = std::make_shared<Shape_OpImpl>(*this); + } + mOutputs[0]->setBackend(name, device); +} diff --git a/src/operator/Split.cpp b/src/operator/Split.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d0493ea4da0b80bf572a33fa4ee466804d0d270 --- /dev/null +++ b/src/operator/Split.cpp @@ -0,0 +1,142 @@ +/******************************************************************************** + * 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/operator/Split.hpp" + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <string> +#include <utility> +#include <vector> + +#include <fmt/format.h> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/data/Data.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Types.h" + +void Aidge::Split_OpImpl::forward() { + const Split_Op& op = dynamic_cast<const Split_Op&>(mOp); + const auto axis = op.template getAttr<std::int8_t>("Axis"); + const auto splits = op.template getAttr<std::vector<DimSize_t>>("Split"); + const auto dims = op.getInput(0)->dims(); + + //Compute pre/post axis strides + const std::size_t stride_pre = std::accumulate(dims.cbegin(), dims.cbegin() + axis, 1, std::multiplies<std::size_t>()); + const std::size_t stride_post = std::accumulate(dims.crbegin(), dims.crbegin() + dims.size() -1 - axis, 1, std::multiplies<std::size_t>()); + for (auto i = 0; i < op.nbOutputs(); ++i) + { + DimIdx_t chunkIdxOnAxis = std::accumulate(splits.cbegin(), splits.cbegin() + i, 0) * stride_post; + DimIdx_t offset = 0; + for (std::size_t j = 0; j < stride_pre; ++j) + { + // Compute chunk position in input tensor + DimIdx_t idx = j * stride_post * dims[axis] + chunkIdxOnAxis; + // Copy chunk in ouput + op.getOutput(i)->getImpl()->copy(op.getInput(0)->getImpl()->rawPtr(idx), + splits[i] * stride_post, offset); + offset += splits[i] * stride_post; + } + + } +} + +const std::string Aidge::Split_Op::Type = "Split"; + +bool Aidge::Split_Op::dimsForwarded() const { + if ((getInput(1) && !getInput(1)->empty())) + { + // output dims are data dependent + return false; + } + + return OperatorTensor::dimsForwarded(); +} + +bool Aidge::Split_Op::forwardDims(bool allowDataDependency) { + // check inputs have been associated + if (!getInput(0)) { + AIDGE_THROW_OR_ABORT(std::runtime_error, "{}: input #0 should be associated with a Tensor", type()); + } + + if (getInput(0)->empty()) { + return false; + } + + std::shared_ptr<Tensor> fallback; + + if (getInput(1) && !getInput(1)->empty()) { // Split is given, replace + if (!this->template getAttr<SplitAttr::Split>().empty()) { + Log::notice("Split_Op: ignoring non-empty Split attribute because input#1 takes precedence"); + } + + if (!allowDataDependency) { + Log::warn("Split_Op: unable to forwardDims() because output dims are data dependent on input#1"); + return false; + } + + this->template getAttr<SplitAttr::Split>().reserve(getInput(1)->size()); + const auto& splits = getInput(1)->refCastFrom(fallback, NativeType<DimSize_t>::type, "cpu"); + std::copy_n(static_cast<DimSize_t*>(splits.getImpl()->hostPtr()), + splits.size(), + std::back_inserter(this->template getAttr<SplitAttr::Split>())); + } + + if (this->template getAttr<std::int8_t>("Axis") < 0) + this->template getAttr<std::int8_t>("Axis") += static_cast<std::int8_t>(getInput(0)->nbDims()); + + DimSize_t dimToSplit = getInput(0)->dims()[this->template getAttr<std::int8_t>("Axis")]; + DimSize_t nbOutput = this->nbOutputs(); + // Fill Split attr if empty + if(this->template getAttr<SplitAttr::Split>().empty()) { + // In case the input Split is not provided, divide the dimension of Axis into equal slices + AIDGE_ASSERT(dimToSplit > nbOutput, "Split_Op: Output number {} musn't be bigger than dimension {}.", nbOutput, dimToSplit); + DimSize_t baseSliceSize = dimToSplit / nbOutput; + + DimSize_t remainder = dimToSplit % nbOutput; + + for (DimSize_t i = 0; i < static_cast<DimSize_t>(nbOutput -1); ++i) { + this->template getAttr<SplitAttr::Split>().push_back(baseSliceSize); + } + this->template getAttr<SplitAttr::Split>().push_back(baseSliceSize + remainder); + } + + const auto splits = this->template getAttr<SplitAttr::Split>(); + AIDGE_ASSERT(splits.size() == nbOutput, "Split_Op: number of slices {} must be equal to number of outputs {}", splits, nbOutput); + DimSize_t totalSplitSize = std::accumulate(splits.cbegin(), splits.cend(), 0); + AIDGE_ASSERT(totalSplitSize == dimToSplit, "Split_Op: Total chunks size {} is different from dimension size {}.", totalSplitSize, dimToSplit); + + std::vector<DimSize_t> outDims = getInput(0)->dims(); + for (std::size_t i = 0; i < nbOutput; ++i) + { + outDims[this->template getAttr<std::int8_t>("Axis")] = this->template getAttr<SplitAttr::Split>()[i]; + mOutputs[i]->resize(outDims); + } + + return true; +} + +void Aidge::Split_Op::setBackend(const std::string& name, Aidge::DeviceIdx_t device) { + if (Registrar<Split_Op>::exists({name})) { + SET_IMPL_MACRO(Split_Op, *this, name); + } + else { + mImpl = std::make_shared<Split_OpImpl>(*this); + } + for (std::size_t i = 0; i < this->nbOutputs(); i++) + { + mOutputs[i]->setBackend(name, device); + } + +} diff --git a/unit_tests/operator/Test_ShapeImpl.cpp b/unit_tests/operator/Test_ShapeImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45df89df061148fbfb892112e3c6d01edf27ffb4 --- /dev/null +++ b/unit_tests/operator/Test_ShapeImpl.cpp @@ -0,0 +1,86 @@ +/******************************************************************************** + * 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/Shape.hpp" + +#include <cstdint> +#include <memory> + + +using namespace Aidge; + +TEST_CASE("[cpu/operator] Shape(forward)", "[Shape][CPU]") { + SECTION("Default attributes") { + std::shared_ptr<Tensor> input = std::make_shared<Tensor>(Array4D<int,1,2,3,5> { + { + { + { + { 1, 2, 3, 4, 5}, + { 6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15} + }, + { + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25}, + {26, 27, 28, 29, 30} + } + } + } + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<int,4> { + {1, 2, 3, 5} + }); + + std::shared_ptr<Node> myShape = Shape(); + auto op = std::static_pointer_cast<OperatorTensor>(myShape -> getOperator()); + op->associateInput(0,input); + op->setDataType(DataType::Int32); + op->setBackend("cpu"); + myShape->forward(); + + REQUIRE(*(op->getOutput(0)) == *expectedOutput); + + } + SECTION("Using attributes") { + std::shared_ptr<Tensor> input = std::make_shared<Tensor>(Array4D<int,1,2,3,5> { + { + { + { + { 1, 2, 3, 4, 5}, + { 6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15} + }, + { + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25}, + {26, 27, 28, 29, 30} + } + } + } + }); + std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<int,2> { + {2, 3} + }); + + std::shared_ptr<Node> myShape = Shape(1, 2); + auto op = std::static_pointer_cast<OperatorTensor>(myShape -> getOperator()); + op->associateInput(0,input); + op->setDataType(DataType::Int32); + op->setBackend("cpu"); + myShape->forward(); + + REQUIRE(*(op->getOutput(0)) == *expectedOutput); + + } +} \ No newline at end of file diff --git a/unit_tests/operator/Test_SplitImpl.cpp b/unit_tests/operator/Test_SplitImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f6f191fccbc0ee5331a9ccaf83563e169eb6abe --- /dev/null +++ b/unit_tests/operator/Test_SplitImpl.cpp @@ -0,0 +1,119 @@ +/******************************************************************************** + * 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 "aidge/data/Tensor.hpp" +#include "aidge/operator/Split.hpp" + +using namespace Aidge; + +TEST_CASE("[cpu/operator] Split(forward)", "[Split][CPU]") { + std::shared_ptr<Tensor> input = std::make_shared<Tensor>(Array4D<int,1,3,7,2> { + { + { + {{ 1, 2},{ 3, 4},{ 5, 6},{ 7, 8},{ 9, 10},{11, 12},{13, 14}}, + {{15, 16},{17, 18},{19, 20},{21, 22},{23, 24},{25, 26},{27, 28}}, + {{30, 31},{32, 33},{34, 35},{36, 37},{38, 39},{40, 41},{42, 43}} + } + } + }); + SECTION("Default split") { + std::shared_ptr<Tensor> expectedOutput0 = std::make_shared<Tensor>(Array4D<int,1,3,2,2> { + { + { + {{ 1, 2},{ 3, 4}}, + {{15, 16},{17, 18}}, + {{30, 31},{32, 33}} + } + } + }); + std::shared_ptr<Tensor> expectedOutput1 = std::make_shared<Tensor>(Array4D<int,1,3,2,2> { + { + { + {{ 5, 6},{ 7, 8}}, + {{19, 20},{21, 22}}, + {{34, 35},{36, 37}} + } + } + }); + std::shared_ptr<Tensor> expectedOutput2 = std::make_shared<Tensor>(Array4D<int,1,3,3,2> { + { + { + {{ 9, 10},{11, 12},{13, 14}}, + {{23, 24},{25, 26},{27, 28}}, + {{38, 39},{40, 41},{42, 43}} + } + } + }); + auto mySplit = Split(DimSize_t(3), int8_t(2)); // Split on axis 2 into 3 outputs + mySplit->getOperator()->associateInput(0, input); + mySplit->getOperator()->setBackend("cpu"); + mySplit->getOperator()->setDataType(DataType::Int32); + mySplit->forward(); + + REQUIRE(*std::static_pointer_cast<OperatorTensor>(mySplit->getOperator())->getOutput(0) == *expectedOutput0); + REQUIRE(*std::static_pointer_cast<OperatorTensor>(mySplit->getOperator())->getOutput(1) == *expectedOutput1); + REQUIRE(*std::static_pointer_cast<OperatorTensor>(mySplit->getOperator())->getOutput(2) == *expectedOutput2); + } + SECTION("Split with different chunk size") { + std::shared_ptr<Tensor> expectedOutput0 = std::make_shared<Tensor>(Array4D<int,1,3,4,2> { + { + { + {{ 1, 2},{ 3, 4},{ 5, 6},{ 7, 8}}, + {{15, 16},{17, 18},{19, 20},{21, 22}}, + {{30, 31},{32, 33},{34, 35},{36, 37}} + } + } + }); + std::shared_ptr<Tensor> expectedOutput1 = std::make_shared<Tensor>(Array4D<int,1,3,1,2> { + { + { + {{ 9, 10}}, + {{23, 24}}, + {{38, 39}} + } + } + }); + std::shared_ptr<Tensor> expectedOutput2 = std::make_shared<Tensor>(Array4D<int,1,3,2,2> { + { + { + {{11, 12},{13, 14}}, + {{25, 26},{27, 28}}, + {{40, 41},{42, 43}} + } + } + }); + auto mySplit = Split(DimSize_t(3), int8_t(2), {DimSize_t(4), DimSize_t(1), DimSize_t(2)}); + mySplit->getOperator()->associateInput(0, input); + mySplit->getOperator()->setBackend("cpu"); + mySplit->getOperator()->setDataType(DataType::Int32); + mySplit->forward(); + + REQUIRE(*std::static_pointer_cast<OperatorTensor>(mySplit->getOperator())->getOutput(0) == *expectedOutput0); + REQUIRE(*std::static_pointer_cast<OperatorTensor>(mySplit->getOperator())->getOutput(1) == *expectedOutput1); + REQUIRE(*std::static_pointer_cast<OperatorTensor>(mySplit->getOperator())->getOutput(2) == *expectedOutput2); + } + SECTION("Split with bad split attribute") { + auto mySplit = Split(DimSize_t(3), int8_t(2), {DimSize_t(4), DimSize_t(1), DimSize_t(3)}); + mySplit->getOperator()->associateInput(0, input); + mySplit->getOperator()->setBackend("cpu"); + mySplit->getOperator()->setDataType(DataType::Int32); + REQUIRE_THROWS(mySplit->forward()); + } + SECTION("Split with bad outNumber") { + auto mySplit = Split(DimSize_t(8), int8_t(2)); + mySplit->getOperator()->associateInput(0, input); + mySplit->getOperator()->setBackend("cpu"); + mySplit->getOperator()->setDataType(DataType::Int32); + REQUIRE_THROWS(mySplit->forward()); + } +} \ No newline at end of file