@@ -19,7 +19,7 @@ use dfs_visit::{dfs_handler, PyDfsVisitor};
1919use dijkstra_visit:: { dijkstra_handler, PyDijkstraVisitor } ;
2020
2121use rustworkx_core:: traversal:: {
22- ancestors as core_ancestors, bfs_predecessors as core_bfs_predecessors,
22+ ancestors as core_ancestors, bfs_layers , bfs_predecessors as core_bfs_predecessors,
2323 bfs_successors as core_bfs_successors, breadth_first_search, depth_first_search,
2424 descendants as core_descendants, dfs_edges, dijkstra_search,
2525} ;
@@ -32,6 +32,8 @@ use hashbrown::HashSet;
3232
3333use pyo3:: exceptions:: { PyIndexError , PyTypeError } ;
3434use pyo3:: prelude:: * ;
35+ use pyo3:: types:: PyList ;
36+ use pyo3:: IntoPyObjectExt ;
3537use pyo3:: Python ;
3638
3739use petgraph:: graph:: NodeIndex ;
@@ -1116,3 +1118,77 @@ pub fn graph_dijkstra_search(
11161118
11171119 Ok ( ( ) )
11181120}
1121+
1122+ /// Return the BFS layers of a PyGraph as a list of lists.
1123+ ///
1124+ /// :param graph: The input PyGraph to use for BFS traversal
1125+ /// :type graph: PyGraph
1126+ /// :param sources: An optional list of node indices to use as the starting
1127+ /// nodes for the BFS traversal. If not specified, all nodes in the graph
1128+ /// will be used as sources.
1129+ /// :type sources: list[int] or None
1130+ ///
1131+ /// :returns: A list of lists where each inner list contains the node indices
1132+ /// at that BFS layer/level from the source nodes
1133+ /// :rtype: list[list[int]]
1134+ #[ pyfunction]
1135+ #[ pyo3( signature = ( graph, sources=None ) ) ]
1136+ pub fn graph_bfs_layers (
1137+ py : Python ,
1138+ graph : & graph:: PyGraph ,
1139+ sources : Option < Vec < usize > > ,
1140+ ) -> PyResult < PyObject > {
1141+ let starts: Vec < NodeIndex > = match sources {
1142+ Some ( v) => v. into_iter ( ) . map ( NodeIndex :: new) . collect ( ) ,
1143+ None => graph. graph . node_indices ( ) . collect ( ) ,
1144+ } ;
1145+
1146+ validate_source_nodes ( & graph. graph , & starts) ?;
1147+
1148+ let layers = bfs_layers ( & graph. graph , starts) ;
1149+
1150+ let py_layers = PyList :: empty ( py) ;
1151+ for layer in layers {
1152+ let ids: Vec < usize > = layer. into_iter ( ) . map ( |n| n. index ( ) ) . collect ( ) ;
1153+ let sublist = PyList :: new ( py, & ids) ?;
1154+ py_layers. append ( sublist) ?;
1155+ }
1156+ py_layers. into_py_any ( py)
1157+ }
1158+
1159+ /// Return the BFS layers of a PyDiGraph as a list of lists.
1160+ ///
1161+ /// :param graph: The input PyDiGraph to use for BFS traversal
1162+ /// :type graph: PyDiGraph
1163+ /// :param sources: An optional list of node indices to use as the starting
1164+ /// nodes for the BFS traversal. If not specified, all nodes in the graph
1165+ /// will be used as sources.
1166+ /// :type sources: list[int] or None
1167+ ///
1168+ /// :returns: A list of lists where each inner list contains the node indices
1169+ /// at that BFS layer/level from the source nodes
1170+ /// :rtype: list[list[int]]
1171+ #[ pyfunction]
1172+ #[ pyo3( signature = ( digraph, sources=None ) ) ]
1173+ pub fn digraph_bfs_layers (
1174+ py : Python ,
1175+ digraph : & digraph:: PyDiGraph ,
1176+ sources : Option < Vec < usize > > ,
1177+ ) -> PyResult < PyObject > {
1178+ let starts: Vec < NodeIndex > = match sources {
1179+ Some ( v) => v. into_iter ( ) . map ( NodeIndex :: new) . collect ( ) ,
1180+ None => digraph. graph . node_indices ( ) . collect ( ) ,
1181+ } ;
1182+
1183+ validate_source_nodes ( & digraph. graph , & starts) ?;
1184+
1185+ let layers = bfs_layers ( & digraph. graph , starts) ;
1186+
1187+ let py_layers = PyList :: empty ( py) ;
1188+ for layer in layers {
1189+ let ids: Vec < usize > = layer. into_iter ( ) . map ( |n| n. index ( ) ) . collect ( ) ;
1190+ let sublist = PyList :: new ( py, & ids) ?;
1191+ py_layers. append ( sublist) ?;
1192+ }
1193+ py_layers. into_py_any ( py)
1194+ }
0 commit comments