Skip to content

Commit 2b8a173

Browse files
authored
Enhance docs in toposort (#1409)
1 parent fba1275 commit 2b8a173

File tree

1 file changed

+56
-43
lines changed

1 file changed

+56
-43
lines changed

src/toposort.rs

Lines changed: 56 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,31 @@ enum NodeState {
3434

3535
/// Provides functionality to topologically sort a directed graph.
3636
///
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+
///
3746
/// The steps required to perform the sorting of a given graph are as follows:
3847
///
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.
4251
///
4352
/// For example:
4453
///
4554
/// .. jupyter-execute::
4655
///
4756
/// import rustworkx as rx
4857
///
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)
5162
/// while sorter.is_active():
5263
/// nodes = sorter.get_ready()
5364
/// print(nodes)
@@ -57,27 +68,25 @@ enum NodeState {
5768
/// but it's not recommended doing it as it may result in a logical-error.
5869
///
5970
/// :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
6172
/// for cycles in the graph during initialization of topological sorter
6273
/// and raise :class:`~rustworkx.DAGHasCycle` if any cycle is detected. If
6374
/// 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
8190
/// checked for validity, and a :exc:`ValueError` is raised if any were not ready, already
8291
/// done, or not indices of the circuit. If ``False``, the tracking for this is disabled,
8392
/// which can provide a meaningful performance and memory improvement, but the results will
@@ -160,22 +169,26 @@ impl TopologicalSorter {
160169

161170
/// Return ``True`` if more progress can be made and ``False`` otherwise.
162171
///
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`
166179
fn is_active(&self) -> bool {
167180
self.num_finished < self.num_passed_out || !self.ready_nodes.is_empty()
168181
}
169182

170183
/// Return a list of all the nodes that are ready.
171184
///
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.
176189
///
177190
/// :returns: A list of node indices of all the ready nodes.
178-
/// :rtype: List
191+
/// :rtype: `list[int]`
179192
fn get_ready<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
180193
self.num_passed_out += self.ready_nodes.len();
181194
if let Some(node2state) = self.node2state.as_mut() {
@@ -191,21 +204,21 @@ impl TopologicalSorter {
191204
}
192205
}
193206

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.
195208
///
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()`.
198211
///
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.
201214
///
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.
209222
fn done(&mut self, nodes: &Bound<PyAny>) -> PyResult<()> {
210223
if let Ok(node) = nodes.extract::<usize>() {
211224
self.done_single(nodes.py(), NodeIndex::new(node))

0 commit comments

Comments
 (0)