@@ -34,20 +34,31 @@ enum NodeState {
34
34
35
35
/// Provides functionality to topologically sort a directed graph.
36
36
///
37
+ /// A topological sorter is used to arrange the nodes of a directed acyclic
38
+ /// graph in a linear order such that for every directed edge from node `u` to
39
+ /// node `v`, node `u`` appears before node `v` in the sequence. This ordering
40
+ /// is particularly useful for resolving dependencies in scenarios such as task
41
+ /// scheduling, where certain tasks must be completed before others, and in
42
+ /// build systems, where files or modules depend on one another. The topological
43
+ /// sorting process ensures that all dependencies are satisfied, allowing for
44
+ /// efficient execution and processing of tasks or data.
45
+ ///
37
46
/// The steps required to perform the sorting of a given graph are as follows:
38
47
///
39
- /// 1. Create an instance of the TopologicalSorter with an initial graph.
40
- /// 2. While ` is_active()` is True, iterate over the nodes returned by ` get_ready()` and process them.
41
- /// 3. Call ` done()` on each node as it finishes processing.
48
+ /// 1. Create an instance of the ` TopologicalSorter` with an initial graph.
49
+ /// 2. While :func:`~ is_active()` is True, iterate over the nodes returned by :func:`~ get_ready()` and process them.
50
+ /// 3. Call :func:`~ done()` on each node as it finishes processing.
42
51
///
43
52
/// For example:
44
53
///
45
54
/// .. jupyter-execute::
46
55
///
47
56
/// import rustworkx as rx
48
57
///
49
- /// graph = rx.generators.directed_path_graph(5)
50
- /// sorter = rx.TopologicalSorter(graph)
58
+ /// G = rx.PyDiGraph()
59
+ /// G.add_nodes_from(["A", "B", "C", "D", "E", "F"])
60
+ /// G.add_edges_from_no_data([(0, 2),(1, 2), (2, 3), (3, 4), (3, 5)])
61
+ /// sorter = rx.TopologicalSorter(G)
51
62
/// while sorter.is_active():
52
63
/// nodes = sorter.get_ready()
53
64
/// print(nodes)
@@ -57,27 +68,25 @@ enum NodeState {
57
68
/// but it's not recommended doing it as it may result in a logical-error.
58
69
///
59
70
/// :param PyDiGraph graph: The directed graph to be used.
60
- /// :param bool check_cycle: When this is set to ``True``, we search
71
+ /// :param bool check_cycle: When this is set to ``True`` (the default) , we search
61
72
/// for cycles in the graph during initialization of topological sorter
62
73
/// and raise :class:`~rustworkx.DAGHasCycle` if any cycle is detected. If
63
74
/// it's set to ``False``, topological sorter will output as many nodes
64
- /// as possible until cycles block more progress. By default is ``True``.
65
- /// :param bool reverse: If ``False`` (the default), perform a regular topological ordering. If
66
- /// ``True``, the ordering will be a reversed topological ordering; that is, a topological
67
- /// order if all the edges had their directions flipped, such that the first nodes returned are
68
- /// the ones that have only incoming edges in the DAG.
69
- /// :param Iterable[int] initial: If given, the initial node indices to start the topological
70
- /// ordering from. If not given, the topological ordering will certainly contain every node in
71
- /// the graph. If given, only the ``initial`` nodes and nodes that are dominated by the
72
- /// ``initial`` set will be in the ordering. Notably, the first return from :meth:`get_ready`
73
- /// will be the same set of values as ``initial``, and any node that has a natural in
74
- /// degree of zero will not be in the output ordering if ``initial`` is given and the
75
- /// zero-in-degree node is not in it.
76
- ///
77
- /// It is a :exc:`ValueError` to give an `initial` set where the nodes have even a partial
78
- /// topological order between themselves, though this might not appear until some call
79
- /// to :meth:`done`.
80
- /// :param bool check_args: If ``True`` (the default), then all arguments to :meth:`done` are
75
+ /// as possible until cycles block more progress.
76
+ /// :param bool reverse: If ``False`` (the default), perform a regular
77
+ /// topological ordering, i.e. for a directed edge ``A -> B`` the ``A`` appears
78
+ /// before the ``B``. If ``True``, the ordering will be a reversed topological
79
+ /// ordering, i.e. for a directed edge ``A -> B``, the ``B`` appears before the ``A``.
80
+ /// :param Iterable[int] initial: By default, the topological ordering will
81
+ /// include all nodes in the graph. If ``initial`` node indices are provided, the
82
+ /// ordering will only include those nodes and any nodes that are dominated by
83
+ /// them. In this case, the first output from :meth:`get_ready()` will match
84
+ /// the initial set, and any node with a natural in-degree of zero will be excluded
85
+ /// from the output if it is not part of the initial set. Additionally, providing an
86
+ /// initial set where the nodes have even a partial topological order among
87
+ /// themselves will raise a :exc:`ValueError`, although this may not be detected until
88
+ /// a call to :meth:`done()`.
89
+ /// :param bool check_args: If ``True`` (the default), then all arguments to :meth:`done()` are
81
90
/// checked for validity, and a :exc:`ValueError` is raised if any were not ready, already
82
91
/// done, or not indices of the circuit. If ``False``, the tracking for this is disabled,
83
92
/// which can provide a meaningful performance and memory improvement, but the results will
@@ -160,22 +169,26 @@ impl TopologicalSorter {
160
169
161
170
/// Return ``True`` if more progress can be made and ``False`` otherwise.
162
171
///
163
- /// Progress can be made if either there are still nodes ready that haven't yet
164
- /// been returned by "get_ready" or the number of nodes marked "done" is less than the
165
- /// number that have been returned by "get_ready".
172
+ /// Progress can be made if either there are still nodes that are ready and
173
+ /// haven't yet been returned by :meth:`get_ready()`, or if the number of
174
+ /// nodes marked :meth:`done()` is lower than the number of nodes that have
175
+ /// been returned by :meth:`get_ready()`.
176
+ ///
177
+ /// :returns: ``True`` if further progress is possible, ``False`` otherwise.
178
+ /// :rtype: `bool`
166
179
fn is_active ( & self ) -> bool {
167
180
self . num_finished < self . num_passed_out || !self . ready_nodes . is_empty ( )
168
181
}
169
182
170
183
/// Return a list of all the nodes that are ready.
171
184
///
172
- /// Initially it returns all nodes with no predecessors; once those are marked
173
- /// as processed by calling " done", further calls will return all new nodes that
174
- /// have all their predecessors already processed. Once no more progress can be made,
175
- /// empty lists are returned.
185
+ /// Initially it returns all nodes with no predecessors; once those are
186
+ /// marked as processed by calling :meth:` done()`, subsequent calls will
187
+ /// return any new nodes that have all their predecessors already processed.
188
+ /// Once no more progress can be made, an empty list is returned.
176
189
///
177
190
/// :returns: A list of node indices of all the ready nodes.
178
- /// :rtype: List
191
+ /// :rtype: `list[int]`
179
192
fn get_ready < ' py > ( & mut self , py : Python < ' py > ) -> PyResult < Bound < ' py , PyList > > {
180
193
self . num_passed_out += self . ready_nodes . len ( ) ;
181
194
if let Some ( node2state) = self . node2state . as_mut ( ) {
@@ -191,21 +204,21 @@ impl TopologicalSorter {
191
204
}
192
205
}
193
206
194
- /// Marks a set of nodes returned by " get_ready" as processed.
207
+ /// Marks a set of nodes returned by :meth:` get_ready()` as processed.
195
208
///
196
- /// This method unblocks any successor of each node in * nodes* for being returned
197
- /// in the future by a call to " get_ready" .
209
+ /// This method unblocks any successor of each node in `` nodes`` for being
210
+ /// returned in the future by a call to :meth:` get_ready()` .
198
211
///
199
- /// :param nodes: A node index or list of node indices to mark as done.
200
- /// :type nodes: int | list[int]
212
+ /// :param int | list[int] nodes: A node index or list of node indices to be
213
+ /// marked as done.
201
214
///
202
- /// :raises `ValueError`: If any node in * nodes* has already been marked as
203
- /// processed by a previous call to this method or node has not yet been returned
204
- /// by " get_ready" .
205
- /// :raises `ValueError`: If one of the given ``initial`` nodes is a direct successor of one
206
- /// of the nodes given to :meth:`done`. This can only happen if the ``initial`` nodes had
207
- /// even a partial topological ordering amongst themselves, which is not a valid
208
- /// starting input.
215
+ /// :raises `ValueError`: If any node in `` nodes`` has already been marked
216
+ /// as processed by a previous call to this method or node has not yet been
217
+ /// returned by :meth:` get_ready()`` .
218
+ /// :raises `ValueError`: If one of the given ``initial`` nodes is a direct
219
+ /// successor of one of the nodes given to :meth:`done() `. This can only
220
+ /// happen if the ``initial`` nodes had even a partial topological ordering
221
+ /// amongst themselves, which is not a valid starting input.
209
222
fn done ( & mut self , nodes : & Bound < PyAny > ) -> PyResult < ( ) > {
210
223
if let Ok ( node) = nodes. extract :: < usize > ( ) {
211
224
self . done_single ( nodes. py ( ) , NodeIndex :: new ( node) )
0 commit comments