diff --git a/include/aidge/graph/Connector.hpp b/include/aidge/graph/Connector.hpp index 599ca7d6defd729b6e6536dcc95f326d345701d9..f123cbb34ff61874498b1c328e8760404b06d66d 100644 --- a/include/aidge/graph/Connector.hpp +++ b/include/aidge/graph/Connector.hpp @@ -83,4 +83,4 @@ class Connector { std::shared_ptr<GraphView> generateGraph(std::vector<Connector> ctors); } // namespace Aidge -#endif /* AIDGE_CORE_GRAPH_CONNECTOR_H_ */ \ No newline at end of file +#endif /* AIDGE_CORE_GRAPH_CONNECTOR_H_ */ diff --git a/include/aidge/operator/Identity.hpp b/include/aidge/operator/Identity.hpp new file mode 100644 index 0000000000000000000000000000000000000000..003990ecc4f438b8b2cdbd14aaaa690549f9e673 --- /dev/null +++ b/include/aidge/operator/Identity.hpp @@ -0,0 +1,143 @@ +/******************************************************************************** + * 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_CORE_OPERATOR_IDENTITY_H_ +#define AIDGE_CORE_OPERATOR_IDENTITY_H_ + +#include <cassert> +#include <memory> +#include <vector> + +#include "aidge/utils/Registrar.hpp" +#include "aidge/operator/Operator.hpp" +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/data/Data.hpp" +#include "aidge/graph/Node.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { + +/** + * @brief Indentity_Op is an helper operator made to ease the declaration of MetaNodes. + * This Operator has no Implementation, it just forward its input Tensor. + * Note: Error may occur if new methods are added in Operator which use an implementation. + * Has we need to update this class to remove the use of Impl. + * + */ +class Identity_Op : public Operator, + public Registrable<Identity_Op, std::string, std::unique_ptr<OperatorImpl>(const Identity_Op&)> { +public: + // FIXME: change accessibility + std::shared_ptr<Tensor> mInput = std::make_shared<Tensor>(); + +public: + static constexpr const char* Type = "Identity"; + + Identity_Op() + : Operator(Type) + { + setDatatype(DataType::Float32); + mImpl = std::make_shared<OperatorImpl>(*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. + */ + Identity_Op(const Identity_Op& op) + : Operator(Type) + { + // cpy-ctor + setDatatype(op.mInput->dataType()); + mImpl = std::make_shared<OperatorImpl>(*this); + } + + /** + * @brief Clone the operator using its copy-constructor. + * @see Operator::Identity_Op + */ + std::shared_ptr<Operator> clone() const override { + return std::make_shared<Identity_Op>(*this); + } + + void associateInput(const IOIndex_t inputIdx, std::shared_ptr<Data> data) override final { + assert(inputIdx == 0 && "operator supports only 1 input"); + (void) inputIdx; // avoid unused warning + assert(strcmp(data->type(), Tensor::Type)==0 && "input data must be of Tensor type"); + mInput = std::dynamic_pointer_cast<Tensor>(data); + } + + void computeOutputDims() override final {} // Do nothing + + bool outputDimsForwarded() const override final { + return true; + } + + + void forward() override { runHooks(); } + + void backward() override { } + + inline Tensor& input(const IOIndex_t /*inputIdx*/) const override final { return *(mInput.get()); } + // output = input + inline Tensor& output(const IOIndex_t /*outputIdx*/) const override final { return *(mInput.get()); } + + + inline std::shared_ptr<Tensor> getInput(const IOIndex_t inputIdx) const override final { + assert((inputIdx == 0) && "ReLU Operator has only 1 input"); + (void) inputIdx; // avoid unused warning + return mInput; + } + inline std::shared_ptr<Tensor> getOutput(const IOIndex_t outputIdx) const override final { + assert((outputIdx == 0) && "Identity Operator has only 1 output"); + (void) outputIdx; // avoid unused warning + return mInput; + } + + + std::shared_ptr<Data> getRawInput(const IOIndex_t inputIdx) const override final { + assert(inputIdx == 0 && "operator supports only 1 input"); + (void) inputIdx; // avoid unused warning + return std::static_pointer_cast<Data>(mInput); + } + std::shared_ptr<Data> getRawOutput(const IOIndex_t outputIdx) const override final { + assert(outputIdx == 0 && "operator supports only 1 output"); + (void) outputIdx; // avoid unused warning + return std::static_pointer_cast<Data>(mInput); + } + + + + void setBackend(const std::string& name) override { + // setBackend do nothing, Identity node has no backend it just pass the same Tensor + } + void setDatatype(const DataType& datatype) override { + // setDatatype do nothing, Identity node has no backend it just pass the same Tensor + } + + inline IOIndex_t nbInputs() const noexcept override final { return 1; } + inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } + inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + 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> Identity(const std::string& name = "") { + return std::make_shared<Node>(std::make_shared<Identity_Op>(), name); +} +} + +#endif /* AIDGE_CORE_OPERATOR_IDENTITY_H_ */ diff --git a/python_binding/graph/pybind_GraphView.cpp b/python_binding/graph/pybind_GraphView.cpp index 6ac2199b4ba59faba16c9815277ad134c6f183f4..cea1f27035a1138bedaa004070a8420505dd0127 100644 --- a/python_binding/graph/pybind_GraphView.cpp +++ b/python_binding/graph/pybind_GraphView.cpp @@ -51,9 +51,18 @@ void init_GraphView(py::module& m) { Include a Node to the current GraphView object. :param other_node: Node to add - :type oth_Node: Node - :param includeLearnableParameter: include non-data inputs, like weights and biases. Default True. - :type includeLearnableParameter: bool + :type other_node: Node + :param include_learnable_parameters: include non-data inputs, like weights and biases, default True. + :type include_learnable_parameters: bool, optional + )mydelimiter") + + .def("add", (void (GraphView::*)(std::shared_ptr<GraphView>)) & GraphView::add, + py::arg("other_graph"), + R"mydelimiter( + Include a GraphView to the current GraphView object. + + :param other_graph: GraphView to add + :type other_graph: GraphView )mydelimiter") .def("add_child", @@ -105,4 +114,4 @@ void init_GraphView(py::module& m) { // }) ; } -} // namespace Aidge \ No newline at end of file +} // namespace Aidge diff --git a/python_binding/graph/pybind_Node.cpp b/python_binding/graph/pybind_Node.cpp index e3666d247324fc419570611f41bbe67c7c68cc4e..29e9a7b663c851f157e1951d695b5f5cb2c0e8ee 100644 --- a/python_binding/graph/pybind_Node.cpp +++ b/python_binding/graph/pybind_Node.cpp @@ -16,136 +16,150 @@ #include "aidge/graph/GraphView.hpp" #include "aidge/graph/Node.hpp" +#include "aidge/graph/Connector.hpp" #include "aidge/utils/Types.h" namespace py = pybind11; namespace Aidge { void init_Node(py::module& m) { py::class_<Node, std::shared_ptr<Node>>(m, "Node") - .def("name", &Node::name, - R"mydelimiter( - Name of the Node. - )mydelimiter") - - .def("type", &Node::type, - R"mydelimiter( - Type of the node. - )mydelimiter") - - .def("get_operator", &Node::getOperator, - R"mydelimiter( - Get the Operator object of the Node. - )mydelimiter") - - .def("set_name", &Node::setName, py::arg("name"), - R"mydelimiter( - Set the Node name. - - :param name: New name for the node. - :type name: str - :rtype: str - )mydelimiter") - - .def("add_child", - (void (Node::*)(std::shared_ptr<Node>, const IOIndex_t, IOIndex_t)) & - Node::addChild, - py::arg("other_node"), py::arg("out_id") = 0, py::arg("other_in_id") = gk_IODefaultIndex, - R"mydelimiter( - Link another Node to an output of the current Node. - - :param other_node: Pointer to the other Node. - :type other_node: :py:class: Node - :param out_id: ID of the current Node output to connect to the other Node. Default to 0. - :type out_id: int - :param other_in_id: ID of the other Node input to connect to the current Node. Default to the first avaible data input. - :type other_in_id: int - )mydelimiter") - - .def("add_child", - (void (Node::*)(std::shared_ptr<GraphView>, const IOIndex_t, - std::pair<std::shared_ptr<Node>, IOIndex_t>)) & - Node::addChild, - py::arg("other_graph"), py::arg("out_id") = 0, - py::arg("other_in_id") = - std::pair<std::shared_ptr<Node>, IOIndex_t>(nullptr, gk_IODefaultIndex), - R"mydelimiter( - Link a Node from a specific GraphView to the current Node. - - :param other_view: Pointer to the GraphView whose content should be linked to the current Node. - :type other_view: :py:class: GraphView - :param out_id: ID of the current Node output to connect to the other Node. Default to 0. - :type out_id: int - :param other_in_id: Pair of Node and input connection ID for specifying the connection. If the GraphView whose content is linked has only one input Node, then it defaults to the first available data input ID of this Node. - :type other_in_id: tuple[:py:class: Node, int] - )mydelimiter") - - .def("inputs", &Node::inputs, - R"mydelimiter( - Get ordered list of parent Node and the associated output index connected to the current Node's inputs. - - :return: List of connections. When an input is not linked to any parent, the default value is (None, default_index) - :rtype: list[tuple[Node, int]] - )mydelimiter") - - .def("input", &Node::input, py::arg("in_id"), - R"mydelimiter( - Get the parent Node and the associated output index connected to the i-th input of the current Node. - - :param in_id: input index of the current Node object. - :type in_id: int - :return: i-th connection. When an input is not linked to any parent, the default value is (None, default_index) - :rtype: tuple[Node, int] - )mydelimiter") - - .def("outputs", &Node::outputs, - R"mydelimiter( - Get, for each output of the Node, a list of the children Node and the associated input index connected to it. - - :return: List of a list of connections. When an outut is not linked to any child, its list a empty. - :rtype: list[list[tuple[Node, int]]] - )mydelimiter") - - .def("output", &Node::output, py::arg("out_id"), - R"mydelimiter( - Get a list of the children Node for a specific output and the associated input index connected to it. - - :param out_id: input index of the current Node object. - :type out_id: int - :return: i-th connection. When an input is not linked to any parent, the default value is (None, default_index) - :rtype: list[tuple[Node, int]] - )mydelimiter") - - .def("get_nb_inputs", &Node::nbInputs, - R"mydelimiter( - Number of inputs. - - :rtype: int - )mydelimiter") - - .def("get_nb_datainputs", &Node::nbDataInputs, - R"mydelimiter( - Number of data inputs. - - :rtype: int - )mydelimiter") - - .def("get_nb_outputs", &Node::nbOutputs, - R"mydelimiter( - Number of outputs. - - :rtype: int - )mydelimiter") - - .def("get_parents", &Node::getParents, - R"mydelimiter( - Get parents. - )mydelimiter") - - .def("get_children", (std::set<std::shared_ptr<Node>> (Node::*)() const) &Node::getChildren, - R"mydelimiter( - Get children. - )mydelimiter") - - .def("__call__", &Node::operator(), py::arg("connectors")); + .def("name", &Node::name, + R"mydelimiter( + Name of the Node. + )mydelimiter") + + .def("type", &Node::type, + R"mydelimiter( + Type of the node. + )mydelimiter") + + .def("get_operator", &Node::getOperator, + R"mydelimiter( + Get the Operator object of the Node. + )mydelimiter") + + .def("set_name", &Node::setName, py::arg("name"), + R"mydelimiter( + Set the Node name. + + :param name: New name for the node. + :type name: str + :rtype: str + )mydelimiter") + + .def("add_child", + (void (Node::*)(std::shared_ptr<Node>, const IOIndex_t, IOIndex_t)) & + Node::addChild, + py::arg("other_node"), py::arg("out_id") = 0, py::arg("other_in_id") = gk_IODefaultIndex, + R"mydelimiter( + Link another Node to an output of the current Node. + + :param other_node: Pointer to the other Node. + :type other_node: :py:class: Node + :param out_id: ID of the current Node output to connect to the other Node. Default to 0. + :type out_id: int + :param other_in_id: ID of the other Node input to connect to the current Node. Default to the first avaible data input. + :type other_in_id: int + )mydelimiter") + + .def("add_child", + (void (Node::*)(std::shared_ptr<GraphView>, const IOIndex_t, + std::pair<std::shared_ptr<Node>, IOIndex_t>)) & + Node::addChild, + py::arg("other_graph"), py::arg("out_id") = 0, + py::arg("other_in_id") = + std::pair<std::shared_ptr<Node>, IOIndex_t>(nullptr, gk_IODefaultIndex), + R"mydelimiter( + Link a Node from a specific GraphView to the current Node. + + :param other_view: Pointer to the GraphView whose content should be linked to the current Node. + :type other_view: :py:class: GraphView + :param out_id: ID of the current Node output to connect to the other Node. Default to 0. + :type out_id: int + :param other_in_id: Pair of Node and input connection ID for specifying the connection. If the GraphView whose content is linked has only one input Node, then it defaults to the first available data input ID of this Node. + :type other_in_id: tuple[:py:class: Node, int] + )mydelimiter") + + .def("inputs", &Node::inputs, + R"mydelimiter( + Get ordered list of parent Node and the associated output index connected to the current Node's inputs. + + :return: List of connections. When an input is not linked to any parent, the default value is (None, default_index) + :rtype: list[tuple[Node, int]] + )mydelimiter") + + .def("input", &Node::input, py::arg("in_id"), + R"mydelimiter( + Get the parent Node and the associated output index connected to the i-th input of the current Node. + + :param in_id: input index of the current Node object. + :type in_id: int + :return: i-th connection. When an input is not linked to any parent, the default value is (None, default_index) + :rtype: tuple[Node, int] + )mydelimiter") + + .def("outputs", &Node::outputs, + R"mydelimiter( + Get, for each output of the Node, a list of the children Node and the associated input index connected to it. + + :return: List of a list of connections. When an outut is not linked to any child, its list a empty. + :rtype: list[list[tuple[Node, int]]] + )mydelimiter") + + .def("output", &Node::output, py::arg("out_id"), + R"mydelimiter( + Get a list of the children Node for a specific output and the associated input index connected to it. + + :param out_id: input index of the current Node object. + :type out_id: int + :return: i-th connection. When an input is not linked to any parent, the default value is (None, default_index) + :rtype: list[tuple[Node, int]] + )mydelimiter") + + .def("get_nb_inputs", &Node::nbInputs, + R"mydelimiter( + Number of inputs. + + :rtype: int + )mydelimiter") + + .def("get_nb_datainputs", &Node::nbDataInputs, + R"mydelimiter( + Number of data inputs. + + :rtype: int + )mydelimiter") + + .def("get_nb_outputs", &Node::nbOutputs, + R"mydelimiter( + Number of outputs. + + :rtype: int + )mydelimiter") + + .def("get_parents", &Node::getParents, + R"mydelimiter( + Get parents. + )mydelimiter") + + .def("get_children", (std::set<std::shared_ptr<Node>> (Node::*)() const) &Node::getChildren, + R"mydelimiter( + Get children. + )mydelimiter") + + .def("__call__", + [](Node &self, pybind11::args args) { + std::vector<Connector> connectors; + for (const auto &arg : args) { + // Check if the argument is an instance of Connector + if (pybind11::isinstance<Connector>(arg)) { + // Convert Python object to C++ object adn push it ot vector + connectors.push_back(arg.cast<Connector>()); + } else { + throw std::runtime_error("One of the arguments was not a Connector."); + } + } + return self(connectors); + }); } } // namespace Aidge diff --git a/python_binding/operator/pybind_Identity.cpp b/python_binding/operator/pybind_Identity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1b1e8888976c578ff490f35776c890ba59911dc --- /dev/null +++ b/python_binding/operator/pybind_Identity.cpp @@ -0,0 +1,27 @@ +/******************************************************************************** + * 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 <pybind11/pybind11.h> + +#include "aidge/operator/Identity.hpp" +#include "aidge/operator/Operator.hpp" + +namespace py = pybind11; +namespace Aidge { + +void init_Identity(py::module& m) { + py::class_<Identity_Op, std::shared_ptr<Identity_Op>, Operator>(m, "IdentityOp", py::multiple_inheritance()) + .def("get_inputs_name", &Identity_Op::getInputsName) + .def("get_outputs_name", &Identity_Op::getOutputsName); + + m.def("Identity", &Identity, py::arg("name") = ""); +} +} // namespace Aidge diff --git a/python_binding/operator/pybind_MetaOperatorDefs.cpp b/python_binding/operator/pybind_MetaOperatorDefs.cpp index aa9f3c50e6b8c6ab9e7be46776d5fba30d775be2..3fc0f89c0e2d02052031df357f2d36e4b67b6b41 100644 --- a/python_binding/operator/pybind_MetaOperatorDefs.cpp +++ b/python_binding/operator/pybind_MetaOperatorDefs.cpp @@ -121,6 +121,15 @@ void init_MetaOperatorDefs(py::module &m) { declare_PaddedMaxPoolingOp<2>(m); declare_PaddedMaxPoolingOp<3>(m); + py::class_<MetaOperator_Op, std::shared_ptr<MetaOperator_Op>, Operator>(m, "MetaOperator_Op", py::multiple_inheritance()); + + m.def("meta_operator", &MetaOperator, + py::arg("type"), + py::arg("graph"), + py::arg("name") = "", + py::arg("input_nodes") = std::vector<NodePtr>(), + py::arg("output_nodes") = std::vector<NodePtr>() + ); } } // namespace Aidge diff --git a/python_binding/operator/pybind_Operator.cpp b/python_binding/operator/pybind_Operator.cpp index ef02b8aaef9f4ea3bd97559ad9e94c38c5b1d29e..b786a27dd04d218da94c148a8087a4b89f8ed6aa 100644 --- a/python_binding/operator/pybind_Operator.cpp +++ b/python_binding/operator/pybind_Operator.cpp @@ -23,6 +23,7 @@ void init_Operator(py::module& m){ .def("nb_inputs", &Operator::nbInputs) .def("nb_data_inputs", &Operator::nbDataInputs) .def("nb_outputs", &Operator::nbOutputs) + .def("output_dims_forwarded", &Operator::outputDimsForwarded) .def("associate_input", &Operator::associateInput, py::arg("inputIdx"), py::arg("data")) .def("set_datatype", &Operator::setDatatype, py::arg("datatype")) .def("set_backend", &Operator::setBackend, py::arg("name")) diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp index 6cc597b5ee934e4a3b849d45e92e5cb62be1b312..9c8d8c61daf836c52b06b90b1c88577172fe22e5 100644 --- a/python_binding/pybind_core.cpp +++ b/python_binding/pybind_core.cpp @@ -39,6 +39,7 @@ void init_ReLU(py::module&); void init_Softmax(py::module&); void init_Sqrt(py::module&); void init_Sub(py::module&); +void init_Identity(py::module&); void init_Node(py::module&); void init_GraphView(py::module&); @@ -83,6 +84,7 @@ void init_Aidge(py::module& m){ init_Softmax(m); init_Sqrt(m); init_Sub(m); + init_Identity(m); init_Producer(m); init_GraphRegex(m); diff --git a/src/graph/Connector.cpp b/src/graph/Connector.cpp index cd2ceff8b58076a5054269e4676120b94c8b5beb..98f58259a97b7c4194b29ae7b75a4140885ee122 100644 --- a/src/graph/Connector.cpp +++ b/src/graph/Connector.cpp @@ -41,6 +41,7 @@ std::shared_ptr<Aidge::GraphView> Aidge::generateGraph(std::vector<Connector> ct std::vector<std::shared_ptr<Node>> parents = nodesToAdd.back()->getParents(); const std::set<std::shared_ptr<Node>>& alreadyAdded = graph->getNodes(); for (std::shared_ptr<Node> parent : parents) { + if (!parent) continue; if (alreadyAdded.find(parent) == alreadyAdded.end()) { buffer.push_back(parent); } @@ -51,4 +52,4 @@ std::shared_ptr<Aidge::GraphView> Aidge::generateGraph(std::vector<Connector> ct buffer = {}; } return graph; -} \ No newline at end of file +}