From d0f38001257265795fe375f802eb70c776591e71 Mon Sep 17 00:00:00 2001 From: "julian.speith" Date: Tue, 7 May 2024 17:16:04 +0200 Subject: [PATCH] add python bindings --- .../python/python_bindings.cpp | 606 +++++++++++++++--- 1 file changed, 528 insertions(+), 78 deletions(-) diff --git a/plugins/graph_algorithm/python/python_bindings.cpp b/plugins/graph_algorithm/python/python_bindings.cpp index 2547a1863a6..209485bd6f2 100644 --- a/plugins/graph_algorithm/python/python_bindings.cpp +++ b/plugins/graph_algorithm/python/python_bindings.cpp @@ -1,6 +1,9 @@ #include "hal_core/python_bindings/python_bindings.h" +#include "graph_algorithm/algorithms/components.h" +#include "graph_algorithm/algorithms/neighborhood.h" +#include "graph_algorithm/netlist_graph.h" #include "graph_algorithm/plugin_graph_algorithm.h" #pragma GCC diagnostic push @@ -37,84 +40,531 @@ namespace hal py::module m("graph_algorithm", "hal GraphAlgorithmPlugin python bindings"); #endif // ifdef PYBIND11_MODULE - py::class_, BasePluginInterface>(m, "GraphAlgorithmPlugin") - .def_property_readonly("name", &GraphAlgorithmPlugin::get_name, R"( - The name of the plugin. - - :type: str - )") - .def("get_name", &GraphAlgorithmPlugin::get_name, R"( - Get the name of the plugin. - - :returns: Plugin name. - :rtype: str - )") - .def_property_readonly("version", &GraphAlgorithmPlugin::get_version, R"( - The version of the plugin. - - :type: str - )") - .def("get_version", &GraphAlgorithmPlugin::get_version, R"( - Get the version of the plugin. - - :returns: Plugin version. - :rtype: str - )") - // .def("get_communities", &GraphAlgorithmPlugin::get_communities, py::arg("netlist"), R"( - // Get a dict of community IDs to communities. Each community is represented by a set of gates. - - // :param hal_py.Netlist netlist: The netlist to operate on. - // :returns: A dict from community IDs to communities. - // :rtype: dict[int,set[hal_py.get_gate()]] - // )") - // .def("get_communities_spinglass", &GraphAlgorithmPlugin::get_communities_spinglass, py::arg("netlist"), py::arg("spins"), R"( - // Get a dict of community IDs to communities running the spinglass clustering algorithm. Each community is represented by a set of gates. - - // :param hal_py.Netlist netlist: The netlist to operate on. - // :param int spins: The number of spins. - // :returns: A dict from community IDs to communities. - // :rtype: dict[int,set[hal_py.get_gate()]] - // )") - // .def("get_communities_fast_greedy", &GraphAlgorithmPlugin::get_communities_fast_greedy, py::arg("netlist"), R"( - // Get a dict of community IDs to communities running the fast greedy clustering algorithm from igraph. Each community is represented by a set of gates. - - // :param hal_py.Netlist netlist: The netlist to operate on. - // :returns: A dict from community IDs to communities. - // :rtype: dict[set[hal_py.get_gate()]] - // )") - // /* - // .def("get_communities_multilevel", &GraphAlgorithmPlugin::get_communities_multilevel, py::arg("netlist"), R"( - // Get a dict of community IDs to communities running the multilevel clustering algorithm from igraph. Each community is represented by a set of gates. - - // :param hal_py.Netlist netlist: The netlist to operate on. - // :returns: A dict from community IDs to communities. - // :rtype: dict[int,set[hal_py.get_gate()]] - // )") */ - // .def("get_strongly_connected_components", &GraphAlgorithmPlugin::get_strongly_connected_components, py::arg("netlist"), R"( - // Get a list of strongly connected components (SCC) with each SSC being represented by a list of gates. - - // :param hal_py.Netlist netlist: The netlist to operate on. - // :returns: A list of SCCs. - // :rtype: list[list[hal_py.get_gate()]] - // )") - // .def("get_graph_cut", - // &GraphAlgorithmPlugin::get_graph_cut, - // py::arg("netlist"), - // py::arg("gate"), - // py::arg("depth") = std::numeric_limits::max(), - // py::arg("terminal_gate_type") = std::set(), - // R"( - // Get a graph cut for a specific gate and depth. Further, a set of gates can be specified that limit the graph cut, i.e., flip-flops and memory cells. - // The graph cut is returned as a list of sets of gates with the list's index representing the distance of each set to the starting point. - - // :param hal_py.Netlist netlist: The netlist to operate on. - // :param hal_py.get_gate() gate: The gate that is the starting point for the graph cut. - // :param int depth: The depth of the graph cut. - // :param set[str] terminal_gate_type: A set of gates at which to terminate the graph cut. - // :returns: The graph cut as a list of sets of gates. - // :rtype: list[set[hal_py.get_gate()]] - // )") - ; + py::class_, BasePluginInterface> py_graph_algorithm_plugin(m, "GraphAlgorithmPlugin"); + + py_graph_algorithm_plugin.def_property_readonly("name", &GraphAlgorithmPlugin::get_name, R"( + The name of the plugin. + + :type: str + )"); + + py_graph_algorithm_plugin.def("get_name", &GraphAlgorithmPlugin::get_name, R"( + Get the name of the plugin. + + :returns: Plugin name. + :rtype: str + )"); + + py_graph_algorithm_plugin.def_property_readonly("version", &GraphAlgorithmPlugin::get_version, R"( + The version of the plugin. + + :type: str + )"); + + py_graph_algorithm_plugin.def("get_version", &GraphAlgorithmPlugin::get_version, R"( + Get the version of the plugin. + + :returns: Plugin version. + :rtype: str + )"); + + py::class_> py_netlist_graph(m, "NetlistGraph", R"( + Holds a directed graph corresponding to a netlist. + )"); + + py::enum_(py_netlist_graph, "Direction", R"( + Defines the direction of a pin. + )") + .value("NONE", graph_algorithm::NetlistGraph::Direction::NONE, R"(Invalid direction.)") + .value("IN", graph_algorithm::NetlistGraph::Direction::IN, R"(Vertex fan-in.)") + .value("OUT", graph_algorithm::NetlistGraph::Direction::OUT, R"(Vertex fan-out.)") + .value("ALL", graph_algorithm::NetlistGraph::Direction::ALL, R"(All directions.)") + .export_values(); + + py_netlist_graph.def_static( + "from_netlist", + [](Netlist* nl, bool create_dummy_nodes = false, const std::function& filter = nullptr) -> std::unique_ptr { + auto res = graph_algorithm::NetlistGraph::from_netlist(nl, create_dummy_nodes, filter); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while creating a graph from a netlist:\n{}", res.get_error().get()); + return nullptr; + } + }, + py::arg("nl"), + py::arg("create_dummy_nodes") = false, + py::arg("filter") = nullptr, + R"(Create a directed graph from a netlist. Optionally create dummy nodes at nets missing a source or destination. An optional filter can be applied to exclude undesired edges. + + :param hal_py.Netlist nl: The netlist. + :param bool create_dummy_nodes: Set ``True`` to create dummy nodes, ``False`` otherwise. Defaults to ``False``. + :param lambda filter: An optional filter that is evaluated on every net of the netlist. Defaults to ``None``. + :returns: The netlist graph on success, ``None`` otherwise. + :rtype: graph_algorithm.NetlistGraph or None + )"); + + py_netlist_graph.def_static( + "from_netlist_no_edges", + [](Netlist* nl) -> std::unique_ptr { + auto res = graph_algorithm::NetlistGraph::from_netlist_no_edges(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while creating a graph from a netlist:\n{}", res.get_error().get()); + return nullptr; + } + }, + py::arg("nl"), + R"(Create an empty directed graph from a netlist, i.e., vertices for all gates are created, but no edges are added. + + :param hal_py.Netlist nl: The netlist. + :returns: The netlist graph on success, ``None`` otherwise. + :rtype: graph_algorithm.NetlistGraph or None + )"); + + py_netlist_graph.def("get_netlist", &graph_algorithm::NetlistGraph::get_netlist, R"( + Get the netlist associated with the netlist graph. + + :returns: The netlist. + :rtype: hal_py.Netlist + )"); + + py_netlist_graph.def( + "get_gates_from_vertices", + [](const graph_algorithm::NetlistGraph& self, const std::vector& vertices) -> std::optional> { + auto res = self.get_gates_from_vertices(vertices); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting gates from vertices:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("vertices"), + R"( + Get the gates corresponding to the specified list of vertices. + + :param list[int] vertices: A list of vertices. + :returns: A list of gates on success, ``None`` otherwise. + :rtype: list[hal_py.Gate] or None + )"); + + py_netlist_graph.def( + "get_gates_from_vertices", + [](const graph_algorithm::NetlistGraph& self, const std::set& vertices) -> std::optional> { + const auto res = self.get_gates_from_vertices(vertices); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting gates from vertices:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("vertices"), + R"( + Get the gates corresponding to the specified set of vertices. + + :param set[int] vertices: A set of vertices. + :returns: A list of gates on success, ``None`` otherwise. + :rtype: list[hal_py.Gate] or None + )"); + + py_netlist_graph.def( + "get_gate_from_vertex", + [](const graph_algorithm::NetlistGraph& self, const u32 vertex) -> Gate* { + const auto res = self.get_gate_from_vertex(vertex); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting gate from vertex:\n{}", res.get_error().get()); + return nullptr; + } + }, + py::arg("vertex"), + R"( + Get the gates corresponding to the specified vertex. + + :param int vertex: A vertex. + :returns: A gate on success, ``None`` otherwise. + :rtype: hal_py.Gate or None + )"); + + py_netlist_graph.def( + "get_vertices_from_gates", + [](const graph_algorithm::NetlistGraph& self, const std::vector& gates) -> std::optional> { + auto res = self.get_vertices_from_gates(gates); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting vertices from gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("gates"), + R"( + Get the vertices corresponding to the specified list of gates. + + :param list[hal_py.Gate] gates: A list of gates. + :returns: A list of vertices on success, ``None`` otherwise. + :rtype: list[int] or None + )"); + + py_netlist_graph.def( + "get_vertices_from_gates", + [](const graph_algorithm::NetlistGraph& self, const std::set& gates) -> std::optional> { + auto res = self.get_vertices_from_gates(gates); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting vertices from gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("gates"), + R"( + Get the vertices corresponding to the specified set of gates. + + :param set[hal_py.Gate] gates: A set of gates. + :returns: A list of vertices on success, ``None`` otherwise. + :rtype: list[int] or None + )"); + + py_netlist_graph.def( + "get_vertex_from_gate", + [](const graph_algorithm::NetlistGraph& self, Gate* g) -> std::optional { + auto res = self.get_vertex_from_gate(g); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting vertex from gate:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("g"), + R"( + Get the vertex corresponding to the specified gate. + + :param hal_py.Gate g: A gate. + :returns: A vertex on success, ``None`` otherwise. + :rtype: int or None + )"); + + py_netlist_graph.def( + "get_edges", + [](const graph_algorithm::NetlistGraph& self) -> std::optional>> { + auto res = self.get_edges(); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting edges:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + R"( + Get the edges between vertices in the netlist graph. + + :returns: A list of edges on success, ``None`` otherwise. + :rtype: list[tuple(int,int)] or None + )"); + + py_netlist_graph.def( + "get_edges_in_netlist", + [](const graph_algorithm::NetlistGraph& self) -> std::optional>> { + auto res = self.get_edges_in_netlist(); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting edges:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + R"( + Get the edges between gates in the netlist corresponding to the netlist graph. + + :returns: A list of edges on success, ``None`` otherwise. + :rtype: list[tuple(hal_py.Gate,hal_py.Gate)] or None + )"); + + py_netlist_graph.def( + "add_edges", + [](graph_algorithm::NetlistGraph& self, const std::vector>& edges) -> bool { + auto res = self.add_edges(edges); + if (res.is_ok()) + { + return true; + } + else + { + log_error("python_context", "error encountered while adding edges:\n{}", res.get_error().get()); + return false; + } + }, + py::arg("edges"), + R"( + Add edges between the specified pairs of source and destination gates to the netlist graph. + The gates must already correspond to vertices in the graph. + + :param list[tuple(hal_py.Gate,hal_py.Gate)] edges: The edges to add as pairs of gates. + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool + )"); + + py_netlist_graph.def( + "add_edges", + [](graph_algorithm::NetlistGraph& self, const std::vector>& edges) -> bool { + auto res = self.add_edges(edges); + if (res.is_ok()) + { + return true; + } + else + { + log_error("python_context", "error encountered while adding edges:\n{}", res.get_error().get()); + return false; + } + }, + py::arg("edges"), + R"( + Add edges between the specified pairs of source and destination vertices to the netlist graph. + The vertices must already exist in the graph. + + :param list[tuple(int,int)] edges: The edges to add as pairs of vertices. + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool + )"); + + py_netlist_graph.def( + "delete_edges", + [](graph_algorithm::NetlistGraph& self, const std::vector>& edges) -> bool { + auto res = self.delete_edges(edges); + if (res.is_ok()) + { + return true; + } + else + { + log_error("python_context", "error encountered while deleting edges:\n{}", res.get_error().get()); + return false; + } + }, + py::arg("edges"), + R"( + Delete edges between the specified pairs of source and destination gates from the netlist graph. + + :param list[tuple(hal_py.Gate,hal_py.Gate)] edges: The edges to delete as pairs of gates. + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool + )"); + + py_netlist_graph.def( + "delete_edges", + [](graph_algorithm::NetlistGraph& self, const std::vector>& edges) -> bool { + auto res = self.delete_edges(edges); + if (res.is_ok()) + { + return true; + } + else + { + log_error("python_context", "error encountered while deleting edges:\n{}", res.get_error().get()); + return false; + } + }, + py::arg("edges"), + R"( + Delete edges between the specified pairs of source and destination vertices from the netlist graph. + + :param list[tuple(int,int)] edges: The edges to delete as pairs of vertices. + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool + )"); + + py_netlist_graph.def("print", &graph_algorithm::NetlistGraph::print, R"( + Print the edge list of the graph to stdout. + )"); + + m.def( + "get_connected_components", + [](graph_algorithm::NetlistGraph* graph, bool strong, u32 min_size = 0) -> std::optional>> { + auto res = graph_algorithm::get_connected_components(graph, strong, min_size); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while computing connected components:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("graph"), + py::arg("strong"), + py::arg("min_size") = 0, + R"( + Compute the (strongly) connected components of the specified graph. + Returns each connected component as a list of vertices in the netlist graph. + + :param graph_algorithm.NetlistGraph graph: The netlist graph. + :param bool strong: Set ``True`` to compute strongly connected components, ``False`` otherwise. + :param int min_size: Minimal size of a connected component to be part of the result. Set to ``0`` to include all components. Defaults to ``0``. + :returns: A list of strongly connected components on success, ``None`` otherwise. + :rtype: list[list[int]] or None + )"); + + m.def( + "get_neighborhood", + [](graph_algorithm::NetlistGraph* graph, std::vector start_gates, u32 order, graph_algorithm::NetlistGraph::Direction direction, u32 min_dist = 0) + -> std::optional>> { + auto res = graph_algorithm::get_neighborhood(graph, start_gates, order, direction, min_dist); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while computing neighborhood:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("graph"), + py::arg("start_gates"), + py::arg("order"), + py::arg("direction"), + py::arg("min_dist") = 0, + R"( + Compute the neighborhood of the given order for each of the specified gates within the given netlist graph. + For order 0, only the vertex itself is returned. For order 1, the vertex itself and all vertices that are its direct predecessors and/or successors (depending on the specified direction). For order 2, the neighborhood of order 1 plus all direct predecessors and/or successors of the vertices in order 1 are returned, etc. + Returns each neighborhood as a list of vertices in the netlist graph. + + :param graph_algorithm.NetlistGraph graph: The netlist graph. + :param list[hal_py.Gate] start_gates: The gates for which to compute the neighborhood. + :param int order: The order of the neighborhood to compute. + :param graph_algorithm.NetlistGraph.Direction direction: The direction in which the neighborhood should be computed. + :param int min_dist: The minimum distance of the vertices to include in the result. + :returns: A list of neighborhoods of each of the provided start gates (in order) on success, ``None`` otherwise. + :rtype: list[list[int]] or None + )"); + + m.def( + "get_neighborhood", + [](graph_algorithm::NetlistGraph* graph, std::vector start_vertices, u32 order, graph_algorithm::NetlistGraph::Direction direction, u32 min_dist = 0) + -> std::optional>> { + auto res = graph_algorithm::get_neighborhood(graph, start_vertices, order, direction, min_dist); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while computing neighborhood:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("graph"), + py::arg("start_vertices"), + py::arg("order"), + py::arg("direction"), + py::arg("min_dist") = 0, + R"( + Compute the neighborhood of the given order for each of the specified vertices within the given netlist graph. + For order 0, only the vertex itself is returned. For order 1, the vertex itself and all vertices that are its direct predecessors and/or successors (depending on the specified direction). For order 2, the neighborhood of order 1 plus all direct predecessors and/or successors of the vertices in order 1 are returned, etc. + Returns each neighborhood as a list of vertices in the netlist graph. + + :param graph_algorithm.NetlistGraph graph: The netlist graph. + :param list[int] start_vertices: The vertices for which to compute the neighborhood. + :param int order: The order of the neighborhood to compute. + :param graph_algorithm.NetlistGraph.Direction direction: The direction in which the neighborhood should be computed. + :param int min_dist: The minimum distance of the vertices to include in the result. + :returns: A list of neighborhoods of each of the provided start vertices (in order) on success, ``None`` otherwise. + :rtype: list[list[int]] or None + )"); + + // .def("get_communities", &GraphAlgorithmPlugin::get_communities, py::arg("netlist"), R"( + // Get a dict of community IDs to communities. Each community is represented by a set of gates. + + // :param hal_py.Netlist netlist: The netlist to operate on. + // :returns: A dict from community IDs to communities. + // :rtype: dict[int,set[hal_py.get_gate()]] + // )") + // .def("get_communities_spinglass", &GraphAlgorithmPlugin::get_communities_spinglass, py::arg("netlist"), py::arg("spins"), R"( + // Get a dict of community IDs to communities running the spinglass clustering algorithm. Each community is represented by a set of gates. + + // :param hal_py.Netlist netlist: The netlist to operate on. + // :param int spins: The number of spins. + // :returns: A dict from community IDs to communities. + // :rtype: dict[int,set[hal_py.get_gate()]] + // )") + // .def("get_communities_fast_greedy", &GraphAlgorithmPlugin::get_communities_fast_greedy, py::arg("netlist"), R"( + // Get a dict of community IDs to communities running the fast greedy clustering algorithm from igraph. Each community is represented by a set of gates. + + // :param hal_py.Netlist netlist: The netlist to operate on. + // :returns: A dict from community IDs to communities. + // :rtype: dict[set[hal_py.get_gate()]] + // )") + // /* + // .def("get_communities_multilevel", &GraphAlgorithmPlugin::get_communities_multilevel, py::arg("netlist"), R"( + // Get a dict of community IDs to communities running the multilevel clustering algorithm from igraph. Each community is represented by a set of gates. + + // :param hal_py.Netlist netlist: The netlist to operate on. + // :returns: A dict from community IDs to communities. + // :rtype: dict[int,set[hal_py.get_gate()]] + // )") */ + // .def("get_strongly_connected_components", &GraphAlgorithmPlugin::get_strongly_connected_components, py::arg("netlist"), R"( + // Get a list of strongly connected components (SCC) with each SSC being represented by a list of gates. + + // :param hal_py.Netlist netlist: The netlist to operate on. + // :returns: A list of SCCs. + // :rtype: list[list[hal_py.get_gate()]] + // )") + // .def("get_graph_cut", + // &GraphAlgorithmPlugin::get_graph_cut, + // py::arg("netlist"), + // py::arg("gate"), + // py::arg("depth") = std::numeric_limits::max(), + // py::arg("terminal_gate_type") = std::set(), + // R"( + // Get a graph cut for a specific gate and depth. Further, a set of gates can be specified that limit the graph cut, i.e., flip-flops and memory cells. + // The graph cut is returned as a list of sets of gates with the list's index representing the distance of each set to the starting point. + + // :param hal_py.Netlist netlist: The netlist to operate on. + // :param hal_py.get_gate() gate: The gate that is the starting point for the graph cut. + // :param int depth: The depth of the graph cut. + // :param set[str] terminal_gate_type: A set of gates at which to terminate the graph cut. + // :returns: The graph cut as a list of sets of gates. + // :rtype: list[set[hal_py.get_gate()]] + // )") + ; #ifndef PYBIND11_MODULE return m.ptr();