diff --git a/include/aidge/aidge.hpp b/include/aidge/aidge.hpp index 931b1b26a04e8886c211d77f8b0147c2140d350a..1f90ec388017e17a8cd69fdedd6f08b9927989de 100644 --- a/include/aidge/aidge.hpp +++ b/include/aidge/aidge.hpp @@ -59,6 +59,7 @@ #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" 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/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/pybind_core.cpp b/python_binding/pybind_core.cpp index 7b38c2d72d5f4b2eed8d8bbf9f41f47144b51060..84f5bb0ac5a96a35f25ca20bc39a7e177ff63805 100644 --- a/python_binding/pybind_core.cpp +++ b/python_binding/pybind_core.cpp @@ -52,6 +52,7 @@ 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&); @@ -120,6 +121,7 @@ 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); 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/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