From ba7a19c25a7ae7391437c5c047ee75ea72316dd5 Mon Sep 17 00:00:00 2001
From: Vincent TEMPLIER <vincent.templier@cea.fr>
Date: Tue, 25 Jul 2023 07:27:20 +0000
Subject: [PATCH] Add Python binding for Core library

---
 .../backend/pybind_OperatorImpl.cpp           |  20 +++
 .../_Core/python_binding/data/pybind_Data.cpp |  37 +++++
 .../python_binding/data/pybind_Tensor.cpp     | 138 ++++++++++++++++++
 .../python_binding/graph/pybind_Connector.cpp |  29 ++++
 .../python_binding/graph/pybind_GraphView.cpp |  60 ++++++++
 .../python_binding/graph/pybind_Node.cpp      |  49 +++++++
 .../python_binding/graph/pybind_OpArgs.cpp    |  38 +++++
 .../graphmatching/pybind_GRegex.cpp           |  25 ++++
 .../graphmatching/pybind_Match.cpp            |  25 ++++
 .../graphmatching/pybind_NodeRegex.cpp        |  22 +++
 .../python_binding/operator/pybind_Add.cpp    |  32 ++++
 .../python_binding/operator/pybind_Conv.cpp   |  61 ++++++++
 .../python_binding/operator/pybind_FC.cpp     |  32 ++++
 .../operator/pybind_GenericOperator.cpp       |  67 +++++++++
 .../python_binding/operator/pybind_Matmul.cpp |  32 ++++
 .../operator/pybind_Operator.cpp              |  28 ++++
 .../operator/pybind_Producer.cpp              |  46 ++++++
 .../python_binding/operator/pybind_Relu.cpp   |  24 +++
 aidge/_Core/python_binding/pybind_core.cpp    |  73 +++++++++
 .../recipies/pybind_Recipies.cpp              |  26 ++++
 .../scheduler/pybind_Scheduler.cpp            |  26 ++++
 21 files changed, 890 insertions(+)
 create mode 100644 aidge/_Core/python_binding/backend/pybind_OperatorImpl.cpp
 create mode 100644 aidge/_Core/python_binding/data/pybind_Data.cpp
 create mode 100644 aidge/_Core/python_binding/data/pybind_Tensor.cpp
 create mode 100644 aidge/_Core/python_binding/graph/pybind_Connector.cpp
 create mode 100644 aidge/_Core/python_binding/graph/pybind_GraphView.cpp
 create mode 100644 aidge/_Core/python_binding/graph/pybind_Node.cpp
 create mode 100644 aidge/_Core/python_binding/graph/pybind_OpArgs.cpp
 create mode 100644 aidge/_Core/python_binding/graphmatching/pybind_GRegex.cpp
 create mode 100644 aidge/_Core/python_binding/graphmatching/pybind_Match.cpp
 create mode 100644 aidge/_Core/python_binding/graphmatching/pybind_NodeRegex.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_Add.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_Conv.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_FC.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_GenericOperator.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_Matmul.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_Operator.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_Producer.cpp
 create mode 100644 aidge/_Core/python_binding/operator/pybind_Relu.cpp
 create mode 100644 aidge/_Core/python_binding/pybind_core.cpp
 create mode 100644 aidge/_Core/python_binding/recipies/pybind_Recipies.cpp
 create mode 100644 aidge/_Core/python_binding/scheduler/pybind_Scheduler.cpp

diff --git a/aidge/_Core/python_binding/backend/pybind_OperatorImpl.cpp b/aidge/_Core/python_binding/backend/pybind_OperatorImpl.cpp
new file mode 100644
index 00000000..ca413a7a
--- /dev/null
+++ b/aidge/_Core/python_binding/backend/pybind_OperatorImpl.cpp
@@ -0,0 +1,20 @@
+/********************************************************************************
+ * 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 "backend/OperatorImpl.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_OperatorImpl(py::module& m){
+    py::class_<OperatorImpl, std::shared_ptr<OperatorImpl>>(m, "OperatorImpl");
+}
+}
diff --git a/aidge/_Core/python_binding/data/pybind_Data.cpp b/aidge/_Core/python_binding/data/pybind_Data.cpp
new file mode 100644
index 00000000..dfa841bd
--- /dev/null
+++ b/aidge/_Core/python_binding/data/pybind_Data.cpp
@@ -0,0 +1,37 @@
+/********************************************************************************
+ * 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 "data/Data.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+
+void init_Data(py::module& m){
+    // TODO : extend with more values !
+    py::enum_<DataType>(m, "DataType")
+    .value("Float64", DataType::Float64)
+    .value("Float32", DataType::Float32)
+    .value("Float16", DataType::Float16)
+    .value("Int8", DataType::Int8)
+    .value("Int32", DataType::Int32)
+    .value("Int64", DataType::Int64)
+    .value("UInt8", DataType::UInt8)
+    .value("UInt32", DataType::UInt32)
+    .value("UInt64", DataType::UInt64)   
+    ;
+
+    py::class_<Data, std::shared_ptr<Data>>(m,"Data")
+    .def(py::init<const char*>());
+
+    
+}
+}
diff --git a/aidge/_Core/python_binding/data/pybind_Tensor.cpp b/aidge/_Core/python_binding/data/pybind_Tensor.cpp
new file mode 100644
index 00000000..7342b225
--- /dev/null
+++ b/aidge/_Core/python_binding/data/pybind_Tensor.cpp
@@ -0,0 +1,138 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+#include <pybind11/operators.h>
+#include <pybind11/numpy.h>
+
+#include "data/Tensor.hpp"
+#include "data/Data.hpp"
+#include "utils/Registrar.hpp"
+#include "utils/Types.h"
+#include "backend/TensorImpl.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+
+
+template<typename T>
+void addCtor(py::class_<Tensor,
+                        std::shared_ptr<Tensor>, 
+                        Data, 
+                        Registrable<std::tuple<std::string, DataType>, 
+                        std::unique_ptr<TensorImpl>(const Tensor&)>>& mTensor){
+    mTensor.def(py::init([]( py::array_t<T, py::array::c_style | py::array::forcecast> b) {
+        /* Request a buffer descriptor from Python */
+        py::buffer_info info = b.request();
+        Tensor* newTensor = new Tensor();
+        newTensor->setDatatype(NativeType<T>::type);
+        const std::vector<DimSize_t> dims(info.shape.begin(), info.shape.end());
+        newTensor->resize(dims);
+        // TODO : Find a better way to choose backend
+        std::set<std::string> availableBackends = Tensor::getAvailableBackends();
+        if (availableBackends.find("ref_cpp") != availableBackends.end()){
+            newTensor->setBackend("ref_cpp");
+            newTensor->getImpl()->setRawPtr(static_cast<T*>(info.ptr));
+        }else{
+            printf("Warning : Could not use aidge_ref_cpp backend, verify you have `import aidge_ref_cpp`\n");
+        }
+        
+        return newTensor;
+    }));
+}
+
+
+void init_Tensor(py::module& m){
+    py::class_<Registrable<std::tuple<std::string, DataType>, 
+    std::unique_ptr<TensorImpl>(const Tensor&)>,
+    std::shared_ptr<Registrable<std::tuple<std::string, DataType>, 
+    std::unique_ptr<TensorImpl>(const Tensor&)>>>(m,"TensorRegistrable");
+
+    py::class_<Tensor, std::shared_ptr<Tensor>, 
+               Data, 
+               Registrable<std::tuple<std::string, DataType>, 
+               std::unique_ptr<TensorImpl>(const Tensor&)>> pyClassTensor
+        (m,"Tensor", py::multiple_inheritance(), py::buffer_protocol());
+
+    pyClassTensor.def(py::init<>())
+    .def("set_backend", &Tensor::setBackend, py::arg("name"))
+    .def("dims", (const std::vector<DimSize_t>& (Tensor::*)()const) &Tensor::dims)
+    .def("dtype", &Tensor::dataType)
+    .def("size", &Tensor::size)
+    .def("resize", (void (Tensor::*)(const std::vector<DimSize_t>&)) &Tensor::resize)
+    .def("has_impl", &Tensor::hasImpl)
+    .def_static("get_available_backends", &Tensor::getAvailableBackends)
+    .def("__str__", [](Tensor& b) {
+        return b.toString();
+    })
+    .def("__len__", [](Tensor& b) -> size_t{
+        return b.size();
+    })
+    .def("__getitem__", [](Tensor& b, size_t idx)-> py::object {
+        // TODO : Should return error if backend not compatible with get
+        if (idx >= b.size()) throw py::index_error();
+        switch(b.dataType()){
+            case DataType::Float32:
+                return py::cast(static_cast<float*>(b.getImpl()->rawPtr())[idx]);
+            case DataType::Int32:
+                return py::cast(static_cast<int*>(b.getImpl()->rawPtr())[idx]);
+            default:
+                return py::none();
+        }
+    })
+    .def_buffer([](Tensor& b) -> py::buffer_info {
+        const std::unique_ptr<TensorImpl>& tensorImpl = b.getImpl();
+
+        std::vector<ssize_t> dims;
+        std::vector<ssize_t> strides;
+        ssize_t stride = tensorImpl->scalarSize();
+
+        for (unsigned int dim = 0; dim < b.nbDims(); ++dim) {
+            dims.push_back(b.dims()[dim]);
+            strides.push_back(stride);
+            stride *= b.dims()[dim];
+        }
+        std::reverse(dims.begin(), dims.end());
+        std::reverse(strides.begin(), strides.end());
+
+        std::string dataFormatDescriptor;
+        switch(b.dataType()){
+            case DataType::Float32:
+                dataFormatDescriptor = py::format_descriptor<float>::format();
+                break;
+            case DataType::Int32:
+                dataFormatDescriptor = py::format_descriptor<int>::format();
+                break;
+            default:
+                throw py::value_error("Unsupported data format");
+        }
+
+        return py::buffer_info(
+            tensorImpl->rawPtr(),                       /* Pointer to buffer */
+            tensorImpl->scalarSize(),                   /* Size of one scalar */
+            dataFormatDescriptor,                /* Python struct-style format descriptor */
+            b.nbDims(),                                 /* Number of dimensions */
+            dims,                                       /* Buffer dimensions */
+            strides                                     /* Strides (in bytes) for each index */
+        );
+    });
+
+    // TODO : If the ctor with the right data type does not exist, pybind will always convert the data to INT !
+    // Need to find a way to avoid this !
+    addCtor<int>(pyClassTensor);
+    addCtor<float>(pyClassTensor);
+// #if SIZE_MAX != 0xFFFFFFFF
+    addCtor<double>(pyClassTensor);
+// #endif
+    
+}
+}
diff --git a/aidge/_Core/python_binding/graph/pybind_Connector.cpp b/aidge/_Core/python_binding/graph/pybind_Connector.cpp
new file mode 100644
index 00000000..a937fb4f
--- /dev/null
+++ b/aidge/_Core/python_binding/graph/pybind_Connector.cpp
@@ -0,0 +1,29 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+#include "graph/Connector.hpp"
+#include "graph/Node.hpp"
+#include "graph/GraphView.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_Connector(py::module& m){
+    py::class_<Connector, std::shared_ptr<Connector>>(m, "Connector")
+    .def(py::init<>())
+    .def(py::init<std::shared_ptr<Node>>())
+    .def("__getitem__", &Connector::operator[], py::arg("key"))
+    ;
+    m.def("generate_graph", &Aidge::generateGraph, py::arg("output_connectors"));
+    // m.def("generate_graph", (std::shared_ptr<GraphView>(*)(std::vector<Connector>)) &Aidge::generateGraph, py::arg("output_connectors"));
+}
+}
diff --git a/aidge/_Core/python_binding/graph/pybind_GraphView.cpp b/aidge/_Core/python_binding/graph/pybind_GraphView.cpp
new file mode 100644
index 00000000..b7ef2e16
--- /dev/null
+++ b/aidge/_Core/python_binding/graph/pybind_GraphView.cpp
@@ -0,0 +1,60 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+#include <memory>
+#include <string>
+#include "graph/GraphView.hpp"
+#include "graph/Node.hpp"
+#include "utils/Types.h"
+#include "data/Data.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_GraphView(py::module& m) {
+    py::class_<GraphView, std::shared_ptr<GraphView>>(m, "GraphView")
+            .def(py::init<>())
+            .def("save", &GraphView::save, py::arg("path"), py::arg("verbose") = false)
+            .def("get_output_nodes", &GraphView::outputNodes)
+            .def("get_input_nodes", &GraphView::inputNodes)
+            .def("add", (void (GraphView::*)(std::shared_ptr<Node>, bool)) & GraphView::add,
+                 py::arg("other_node"), py::arg("include_learnable_parameters") = true)
+            .def("add_child",
+                 (void (GraphView::*)(std::shared_ptr<Node>,
+                                      std::shared_ptr<Node>,
+                                      const IOIndex_t,
+                                      IOIndex_t)) &
+                         GraphView::addChild,
+                 py::arg("toOtherNode"), py::arg("fromOutNode") = nullptr,
+                 py::arg("fromTensor") = 0U, py::arg("toTensor") = gk_IODefaultIndex)
+            .def("replace_with", &GraphView::replaceWith, py::arg("new_nodes"))
+            .def("get_nodes", &GraphView::getNodes)
+            .def("get_node", &GraphView::getNode, py::arg("node_name"))
+            .def("forward_dims", &GraphView::forwardDims)
+            .def("__call__", &GraphView::operator(), py::arg("connectors"))
+            .def("set_datatype", &GraphView::setDatatype, py::arg("datatype"))
+            .def("set_backend", &GraphView::setBackend, py::arg("backend"))
+          //   .def("__getitem__", [](Tensor& b, size_t idx)-> py::object {
+          //      // TODO : Should return error if backend not compatible with get
+          //      if (idx >= b.size()) throw py::index_error();
+          //      switch(b.dataType()){
+          //           case DataType::Float32:
+          //                return py::cast(static_cast<float*>(b.getImpl()->rawPtr())[idx]);
+          //           case DataType::Int32:
+          //                return py::cast(static_cast<int*>(b.getImpl()->rawPtr())[idx]);
+          //           default:
+          //                return py::none();
+          //           }
+          //      })
+            ;
+}
+}  // namespace Aidge
diff --git a/aidge/_Core/python_binding/graph/pybind_Node.cpp b/aidge/_Core/python_binding/graph/pybind_Node.cpp
new file mode 100644
index 00000000..0d957eb2
--- /dev/null
+++ b/aidge/_Core/python_binding/graph/pybind_Node.cpp
@@ -0,0 +1,49 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+
+#include <memory>
+
+#include "graph/GraphView.hpp"
+#include "graph/Node.hpp"
+#include "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)
+            .def("type", &Node::type)
+            .def("get_operator", &Node::getOperator)
+            .def("set_name", &Node::setName, py::arg("name"))
+            .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") = -1)
+            .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))
+            .def("inputs", &Node::inputs)
+            .def("input", &Node::input, py::arg("inID"))
+            .def("outputs", &Node::outputs)
+            .def("output", &Node::output, py::arg("outID"))
+            .def("get_nb_inputs", &Node::nbInputs)
+            .def("get_nb_datainputs", &Node::nbDataInputs)
+            .def("get_nb_outputs", &Node::nbOutputs)
+            .def("__call__", &Node::operator(), py::arg("connectors"));
+}
+}  // namespace Aidge
diff --git a/aidge/_Core/python_binding/graph/pybind_OpArgs.cpp b/aidge/_Core/python_binding/graph/pybind_OpArgs.cpp
new file mode 100644
index 00000000..fa7a9743
--- /dev/null
+++ b/aidge/_Core/python_binding/graph/pybind_OpArgs.cpp
@@ -0,0 +1,38 @@
+/********************************************************************************
+ * 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 "graph/OpArgs.hpp"
+#include "graph/Node.hpp"
+#include "graph/GraphView.hpp"
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+#include <pybind11/chrono.h>
+
+
+namespace py = pybind11;
+namespace Aidge {
+void init_OpArgs(py::module& m){
+    py::class_<OpArgs, std::shared_ptr<OpArgs>>(m, "OpArgs")
+    .def("node", &OpArgs::node)
+    .def("view", &OpArgs::view)
+    ;
+
+    py::implicitly_convertible<Node, OpArgs>();
+    py::implicitly_convertible<GraphView, OpArgs>();
+
+    m.def("sequential", &Sequential, py::arg("inputs"));
+    m.def("parallel", &Parallel, py::arg("inputs"));
+    m.def("residual", &Residual, py::arg("inputs"));
+
+}
+}
diff --git a/aidge/_Core/python_binding/graphmatching/pybind_GRegex.cpp b/aidge/_Core/python_binding/graphmatching/pybind_GRegex.cpp
new file mode 100644
index 00000000..4eefccec
--- /dev/null
+++ b/aidge/_Core/python_binding/graphmatching/pybind_GRegex.cpp
@@ -0,0 +1,25 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+#include "graph/GraphView.hpp"
+#include "graphmatching/GRegex.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_GRegex(py::module& m){
+    py::class_<GRegex, std::shared_ptr<GRegex>>(m, "GRegex")
+    .def(py::init<const std::map<std::string,NodeRegex*>&, std::vector<std::string>&>(), py::arg("nodesRegex"), py::arg("seqRegexps"))
+    .def("match", &GRegex::match, py::arg("graphToMatch"))
+    ;
+}
+}
diff --git a/aidge/_Core/python_binding/graphmatching/pybind_Match.cpp b/aidge/_Core/python_binding/graphmatching/pybind_Match.cpp
new file mode 100644
index 00000000..8646ff91
--- /dev/null
+++ b/aidge/_Core/python_binding/graphmatching/pybind_Match.cpp
@@ -0,0 +1,25 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+#include "graphmatching/Match.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_Match(py::module& m){
+    py::class_<Match, std::shared_ptr<Match>>(m, "Match")
+    .def(py::init<>())
+    .def("get_nb_match", &Match::getNbMatch)
+    .def("get_start_nodes", &Match::getStartNodes)
+    .def("get_match_nodes", &Match::getMatchNodes);
+}
+}
diff --git a/aidge/_Core/python_binding/graphmatching/pybind_NodeRegex.cpp b/aidge/_Core/python_binding/graphmatching/pybind_NodeRegex.cpp
new file mode 100644
index 00000000..1ee4c933
--- /dev/null
+++ b/aidge/_Core/python_binding/graphmatching/pybind_NodeRegex.cpp
@@ -0,0 +1,22 @@
+/********************************************************************************
+ * 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 "graphmatching/NodeRegex.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_NodeRegex(py::module& m){
+    py::class_<NodeRegex, std::shared_ptr<NodeRegex>>(m, "NodeRegex")
+    .def(py::init<const std::string>(), py::arg("condition"))
+    ;
+}
+}
diff --git a/aidge/_Core/python_binding/operator/pybind_Add.cpp b/aidge/_Core/python_binding/operator/pybind_Add.cpp
new file mode 100644
index 00000000..3585db04
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_Add.cpp
@@ -0,0 +1,32 @@
+/********************************************************************************
+ * 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 "operator/Add.hpp"
+#include "utils/Parameter.hpp"
+#include "backend/OperatorImpl.hpp"
+#include "operator/Operator.hpp"
+#include "utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+template <std::size_t NUM> void declare_Add(py::module &m) {
+  py::class_<Add_Op<NUM>, std::shared_ptr<Add_Op<NUM>>, Operator>(m, "Add_Op", py::multiple_inheritance());
+
+  m.def("Add", &Add<NUM>, py::arg("name") = nullptr);
+}
+
+void init_Add(py::module &m) {
+  declare_Add<2>(m);
+}
+} // namespace Aidge
diff --git a/aidge/_Core/python_binding/operator/pybind_Conv.cpp b/aidge/_Core/python_binding/operator/pybind_Conv.cpp
new file mode 100644
index 00000000..16e86a7c
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_Conv.cpp
@@ -0,0 +1,61 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+
+#include <string>
+
+#include "utils/Parameter.hpp"
+#include "backend/OperatorImpl.hpp"
+#include "operator/Conv.hpp"
+#include "operator/Operator.hpp"
+#include "utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+template <DimIdx_t DIM> void declare_ConvOp(py::module &m) {
+  py::class_<Conv_Op<DIM>, std::shared_ptr<Conv_Op<DIM>>, Operator>(
+    m, ("ConvOp" + std::to_string(DIM) + "D").c_str(),
+    py::multiple_inheritance())
+  .def(py::init<DimSize_t, DimSize_t, const std::array<DimSize_t, DIM> &>(),
+        py::arg("in_channels"), py::arg("out_channels"),
+        py::arg("kernel_dims"));
+  
+  m.def(("Conv" + std::to_string(DIM) + "D").c_str(), [](DimSize_t in_channels, DimSize_t out_channels, std::vector<DimSize_t>& kernel_dims, const char* name) {
+        // Lambda function wrapper because PyBind fails to convert const array.
+        // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array. 
+        if (kernel_dims.size() != DIM) {
+            throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
+        }
+        DimSize_t tmp_kernel_dims_array[DIM];
+        for (size_t i = 0; i < DIM; ++i) {
+            tmp_kernel_dims_array[i] = kernel_dims[i];
+        }
+        const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array;
+        return Conv<DIM>(in_channels, out_channels, kernel_dims_array, name);
+    }, py::arg("in_channels"), py::arg("out_channels"),
+        py::arg("kernel_dims"), py::arg("name") = nullptr);
+  
+}
+
+
+void init_Conv(py::module &m) {
+  declare_ConvOp<1>(m);
+  declare_ConvOp<2>(m);
+  declare_ConvOp<3>(m);
+ 
+  // FIXME:
+  // m.def("Conv1D", static_cast<NodeAPI(*)(const char*, int, int, int const
+  // (&)[1])>(&Conv));
+}
+} // namespace Aidge
diff --git a/aidge/_Core/python_binding/operator/pybind_FC.cpp b/aidge/_Core/python_binding/operator/pybind_FC.cpp
new file mode 100644
index 00000000..293ae841
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_FC.cpp
@@ -0,0 +1,32 @@
+/********************************************************************************
+ * 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 "operator/FC.hpp"
+#include "utils/Parameter.hpp"
+#include "backend/OperatorImpl.hpp"
+#include "operator/Operator.hpp"
+#include "utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+void declare_FC(py::module &m) {
+  py::class_<FC_Op, std::shared_ptr<FC_Op>, Operator>(m, "FC_Op", py::multiple_inheritance());
+
+  m.def("FC", &FC, py::arg("out_channels"), py::arg("nobias") = false, py::arg("name") = nullptr);
+}
+
+void init_FC(py::module &m) {
+  declare_FC(m);
+}
+} // namespace Aidge
diff --git a/aidge/_Core/python_binding/operator/pybind_GenericOperator.cpp b/aidge/_Core/python_binding/operator/pybind_GenericOperator.cpp
new file mode 100644
index 00000000..7aa5d42b
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_GenericOperator.cpp
@@ -0,0 +1,67 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+#include <stdio.h>
+
+#include "backend/OperatorImpl.hpp"
+#include "operator/GenericOperator.hpp"
+#include "operator/Operator.hpp"
+namespace py = pybind11;
+namespace Aidge {
+
+void init_GenericOperator(py::module& m) {
+    py::class_<GenericOperator_Op, std::shared_ptr<GenericOperator_Op>, Operator>(m, "GenericOperatorOp",
+                                                                                  py::multiple_inheritance())
+            .def("get_parameter_type", &GenericOperator_Op::getParameterType)
+    .def("get_parameters_name", &GenericOperator_Op::getParametersName)
+    .def("add_parameter", &GenericOperator_Op::addParameter<bool>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<int>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<float>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<std::string>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<std::vector<bool>>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<std::vector<int>>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<std::vector<float>>)
+    .def("add_parameter", &GenericOperator_Op::addParameter<std::vector<std::string>>)
+    .def("get_parameter", [](GenericOperator_Op& self, std::string key) -> py::object {
+        /*
+        This getParameter method returns the good python type without having to have 
+        prior knowledge of the parameter type.
+        */
+        py::object res = py::none(); 
+        std::string paramType = self.getParameterType(key);
+        if(paramType == typeid(int).name())
+            res = py::cast(self.getParameter<int>(key));
+        else if(paramType == typeid(float).name())
+            res = py::cast(self.getParameter<float>(key));
+        else if(paramType == typeid(bool).name())
+            res = py::cast(self.getParameter<bool>(key));
+        else if(paramType == typeid(std::string).name())
+            res = py::cast(self.getParameter<std::string>(key));
+        else if(paramType == typeid(std::vector<bool>).name())
+            res = py::cast(self.getParameter<std::vector<bool>>(key));
+        else if(paramType == typeid(std::vector<int>).name())
+            res = py::cast(self.getParameter<std::vector<int>>(key));
+        else if(paramType == typeid(std::vector<float>).name())
+            res = py::cast(self.getParameter<std::vector<float>>(key));
+        else if(paramType == typeid(std::vector<std::string>).name())
+            res = py::cast(self.getParameter<std::vector<std::string>>(key));
+        else {
+            throw py::key_error("Failed to convert parameter type " + key + ", this issue may come from typeid function which gave an unknown key : [" + paramType + "]. Please open an issue asking to add the support for this key.");
+        }
+        return res;
+    });
+
+    m.def("GenericOperator", &GenericOperator, py::arg("type"), py::arg("nbDataIn"), py::arg("nbIn"), py::arg("nbOut"),
+          py::arg("name") = nullptr);
+}
+}  // namespace Aidge
diff --git a/aidge/_Core/python_binding/operator/pybind_Matmul.cpp b/aidge/_Core/python_binding/operator/pybind_Matmul.cpp
new file mode 100644
index 00000000..7a2748fc
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_Matmul.cpp
@@ -0,0 +1,32 @@
+/********************************************************************************
+ * 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 "operator/Matmul.hpp"
+#include "utils/Parameter.hpp"
+#include "backend/OperatorImpl.hpp"
+#include "operator/Operator.hpp"
+#include "utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+void declare_Matmul(py::module &m) {
+  py::class_<Matmul_Op, std::shared_ptr<Matmul_Op>, Operator>(m, "Matmul_Op", py::multiple_inheritance());
+
+  m.def("Matmul", &Matmul, py::arg("out_channels"), py::arg("name") = nullptr);
+}
+
+void init_Matmul(py::module &m) {
+  declare_Matmul(m);
+}
+} // namespace Aidge
diff --git a/aidge/_Core/python_binding/operator/pybind_Operator.cpp b/aidge/_Core/python_binding/operator/pybind_Operator.cpp
new file mode 100644
index 00000000..1ea1ba63
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_Operator.cpp
@@ -0,0 +1,28 @@
+/********************************************************************************
+ * 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 "backend/OperatorImpl.hpp"
+#include "operator/Operator.hpp"
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+namespace Aidge {
+void init_Operator(py::module& m){
+    py::class_<Operator, std::shared_ptr<Operator>>(m, "Operator")
+    .def("get_output", &Operator::getOutput, py::arg("outputIdx"))
+    .def("get_input", &Operator::getInput, py::arg("inputIdx"))
+    .def("set_input", &Operator::setInput, 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/aidge/_Core/python_binding/operator/pybind_Producer.cpp b/aidge/_Core/python_binding/operator/pybind_Producer.cpp
new file mode 100644
index 00000000..2a60b1cf
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_Producer.cpp
@@ -0,0 +1,46 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+
+#include "utils/Types.h"
+#include "utils/Parameter.hpp"
+// #include "backend/OperatorImpl.hpp>
+#include "operator/Operator.hpp"
+#include "operator/Producer.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+
+template <DimIdx_t DIM>
+void declare_Producer(py::module &m) {
+    py::class_<Producer_Op<DIM>,  std::shared_ptr<Producer_Op<DIM>>, Operator>(
+        m, 
+        ("ProducerOp" + std::to_string(DIM)+"D").c_str(), 
+        py::multiple_inheritance())
+    .def("set_output", &Producer_Op<DIM>::setOutput, py::arg("output_data"));
+    // m.def(("Producer_" + std::to_string(DIM)+"D").c_str(), py::overload_cast<shared_ptr<Node>&>(&Producer<DIM>), py::arg("dims"), py::arg("name"));
+    m.def("Producer", static_cast<std::shared_ptr<Node>(*)(const std::array<DimSize_t, DIM>&, const char*)>(&Producer), py::arg("dims"), py::arg("name"));
+    
+}
+
+
+void init_Producer(py::module &m) {
+    declare_Producer<1>(m);
+    declare_Producer<2>(m);
+    declare_Producer<3>(m);
+    declare_Producer<4>(m);
+    declare_Producer<5>(m);
+    declare_Producer<6>(m);
+    
+}
+}
diff --git a/aidge/_Core/python_binding/operator/pybind_Relu.cpp b/aidge/_Core/python_binding/operator/pybind_Relu.cpp
new file mode 100644
index 00000000..42d983fa
--- /dev/null
+++ b/aidge/_Core/python_binding/operator/pybind_Relu.cpp
@@ -0,0 +1,24 @@
+/********************************************************************************
+ * 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 "operator/ReLU.hpp"
+#include "operator/Operator.hpp"
+namespace py = pybind11;
+namespace Aidge {
+
+void init_ReLU(py::module& m) {
+    py::class_<ReLU_Op, std::shared_ptr<ReLU_Op>, Operator>(m, "ReLU_Op", py::multiple_inheritance());
+
+    m.def("ReLU", &ReLU, py::arg("alpha") = 0.0f, py::arg("name") = nullptr);
+}
+}  // namespace Aidge
diff --git a/aidge/_Core/python_binding/pybind_core.cpp b/aidge/_Core/python_binding/pybind_core.cpp
new file mode 100644
index 00000000..06d739c2
--- /dev/null
+++ b/aidge/_Core/python_binding/pybind_core.cpp
@@ -0,0 +1,73 @@
+/********************************************************************************
+ * 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>
+
+namespace py = pybind11;
+
+namespace Aidge {
+void init_Data(py::module&);
+void init_Tensor(py::module&);
+void init_OperatorImpl(py::module&);
+void init_Operator(py::module&);
+void init_GraphView(py::module&);
+void init_Conv(py::module&);
+void init_ReLU(py::module&);
+void init_Matmul(py::module&);
+void init_Add(py::module&);
+void init_FC(py::module&);
+void init_GenericOperator(py::module&);
+void init_Producer(py::module&);
+void init_OpArgs(py::module&);
+// void init_Structured(py::module&);
+void init_Node(py::module&);
+void init_Connector(py::module&);
+void init_Match(py::module&);
+void init_NodeRegex(py::module&);
+void init_GRegex(py::module&);
+void init_Recipies(py::module&);
+void init_Scheduler(py::module&);
+
+
+void set_python_flag(){
+    // Set an env variable to know if we run with ypthon or cpp
+    py::module os_module = py::module::import("os");
+    os_module.attr("environ")["AIDGE_CORE_WITH_PYBIND"] = "1";
+};
+
+void init_Aidge(py::module& m){
+    set_python_flag();
+    init_Data(m);
+    init_Tensor(m);
+    init_Node(m);
+    init_OpArgs(m);
+    init_OperatorImpl(m);
+    init_Operator(m);
+    init_Conv(m);
+    init_ReLU(m);
+    init_Matmul(m);
+    init_Add(m);
+    init_FC(m);
+    init_GenericOperator(m);
+    init_Producer(m);
+    init_GraphView(m);
+    init_Connector(m);
+    init_Match(m);
+    init_NodeRegex(m);
+    init_GRegex(m);
+    init_Recipies(m);
+    init_Scheduler(m);
+}
+
+PYBIND11_MODULE(aidge_core, m) {
+    init_Aidge(m);
+}
+}
diff --git a/aidge/_Core/python_binding/recipies/pybind_Recipies.cpp b/aidge/_Core/python_binding/recipies/pybind_Recipies.cpp
new file mode 100644
index 00000000..3ebffd95
--- /dev/null
+++ b/aidge/_Core/python_binding/recipies/pybind_Recipies.cpp
@@ -0,0 +1,26 @@
+/********************************************************************************
+ * 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 <pybind11/stl.h>
+
+#include <string>
+
+#include "utils/Recipies.hpp"
+
+namespace py = pybind11;
+
+namespace Aidge {
+void init_Recipies(py::module &m) {
+  m.def("fuse_mul_add", &fuseMulAdd, py::arg("nodes"));
+  
+}
+} // namespace Aidge
diff --git a/aidge/_Core/python_binding/scheduler/pybind_Scheduler.cpp b/aidge/_Core/python_binding/scheduler/pybind_Scheduler.cpp
new file mode 100644
index 00000000..0f2598c7
--- /dev/null
+++ b/aidge/_Core/python_binding/scheduler/pybind_Scheduler.cpp
@@ -0,0 +1,26 @@
+/********************************************************************************
+ * 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 "scheduler/Scheduler.hpp"
+#include "graph/GraphView.hpp"
+
+namespace py = pybind11;
+namespace Aidge {
+void init_Scheduler(py::module& m){
+    py::class_<SequentialScheduler, std::shared_ptr<SequentialScheduler>>(m, "SequentialScheduler")
+    .def(py::init<std::shared_ptr<GraphView>&>(), py::arg("graph_view"))
+    .def("forward", &SequentialScheduler::forward, py::arg("forward_dims")=true, py::arg("verbose")=false)
+    .def("save_scheduling_diagram", &SequentialScheduler::saveSchedulingDiagram, py::arg("file_name"))
+    ;
+}
+}
+
-- 
GitLab