Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions pytools/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,17 @@ class CycleError(Exception):
Raised when a topological ordering cannot be computed due to a cycle.

:attr node: Node in a directed graph that is part of a cycle.

:attr nodes: List of nodes in a directed graph that are part of a cycle.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe path is a better name. Also state that we claim that node[i+1] in path is a successor of node[i] (and test that!).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to paths and added comment regarding the successors in 47aa316.
Testing this is done in #167 right?

"""
def __init__(self, node: T) -> None:
self.node = node
def __init__(self, nodes: List[T]) -> None:
self.node = nodes[0]
self.nodes = nodes
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks backward compatibility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I avoid that - maybe by providing a nodes parameter (with a default of None) in addition to the node parameter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of c39afca?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better approach would be to make node a @property that spews a DeprecationWarning.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in 8823bca

Comment thread
inducer marked this conversation as resolved.
Outdated

def __str__(self) -> str:
le = len(self.nodes)
mlen = 10
return f"{[n for n in self.nodes[:mlen]] + (['...'] if le > mlen else [])}"


class HeapEntry:
Expand Down Expand Up @@ -299,8 +307,8 @@ def compute_topological_order(graph: Mapping[T, Collection[T]],

if len(order) != total_num_nodes:
# any node which has a predecessor left is a part of a cycle
raise CycleError(next(iter(n for n, num_preds in
nodes_to_num_predecessors.items() if num_preds != 0)))
raise CycleError(list(n for n, num_preds in
nodes_to_num_predecessors.items() if num_preds != 0))
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to provide this in traversable (rather than arbitrary) order, which is doable at $O(n)$ cost.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to do that - how would we provide this in case there are multiple cycles in the graph?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#167 has some work in this direction


return order

Expand Down
4 changes: 4 additions & 0 deletions test/test_graph_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def test_compute_topological_order():
with pytest.raises(CycleError):
compute_topological_order(cycle)

cycle2 = {0: [2], 1: [2], 2: [3], 3: [1]}
with pytest.raises(CycleError, match=r"\[1, 2, 3\]"):
compute_topological_order(cycle2)


def test_transitive_closure():
from pytools.graph import compute_transitive_closure
Expand Down