Skip to content
40 changes: 36 additions & 4 deletions onnxscript/ir/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
Hashable,
Iterable,
Iterator,
NamedTuple,
OrderedDict,
Sequence,
SupportsInt,
Expand Down Expand Up @@ -1055,6 +1056,18 @@ def _quoted(string: str) -> str:
return f'"{string}"'


class Usage(NamedTuple):
"""A usage of a value in a node.

Attributes:
node: The node that uses the value.
index: The input index of the value in the node.
"""

node: Node
index: int
Comment thread Fixed


class Node(_protocols.NodeProtocol, _display.PrettyPrintable):
"""IR Node.

Expand Down Expand Up @@ -1293,6 +1306,25 @@ def inputs(self, _: Any) -> None:
"Directly mutating the input sequence is unsupported. Please use Node.replace_input_with() instead."
)

def predecessors(self) -> Sequence[Node]:
"""Return the predecessor nodes of the node, deduplicated, in a determinsitic order."""
Comment thread
justinchuby marked this conversation as resolved.
Outdated
# Use the ordered nature of a dictionary to deduplicate the nodes
predecessors = {}
Comment thread Fixed
for value in self.inputs:
if value is not None and (producer := value.producer()) is not None:
predecessors[producer] = None
return tuple(predecessors)
Comment thread
justinchuby marked this conversation as resolved.

def successors(self) -> Sequence[Node]:
"""Return the successor nodes of the node, deduplicated, in a determinsitic order."""
Comment thread
justinchuby marked this conversation as resolved.
Outdated
# Use the ordered nature of a dictionary to deduplicate the nodes
successors = {}
Comment thread Fixed
for value in self.outputs:
assert value is not None, "Bug: Output values are not expected to be None"
for usage in value.uses():
successors[usage.node] = None
return tuple(successors)
Comment thread
justinchuby marked this conversation as resolved.

def replace_input_with(self, index: int, value: Value | None) -> None:
"""Replace an input with a new value."""
if index < 0 or index >= len(self.inputs):
Expand Down Expand Up @@ -1564,7 +1596,7 @@ def __init__(
# Use a collection of (Node, int) to store uses. This is needed
# because a single use can use the same value multiple times.
# Use a dictionary to preserve insertion order so that the visiting order is deterministic
self._uses: dict[tuple[Node, int], None] = {}
self._uses: dict[Usage, None] = {}
self.doc_string = doc_string

def __repr__(self) -> str:
Expand Down Expand Up @@ -1599,7 +1631,7 @@ def index(self) -> int | None:
"""The index of the output of the defining node."""
return self._index

def uses(self) -> Collection[tuple[Node, int]]:
def uses(self) -> Collection[Usage]:
"""Return a set of uses of the value.

The set contains tuples of ``(Node, index)`` where the index is the index of the input
Expand All @@ -1612,14 +1644,14 @@ def _add_usage(self, use: Node, index: int) -> None:

This is an internal method. It should only be called by the Node class.
"""
self._uses[(use, index)] = None
self._uses[Usage(use, index)] = None

def _remove_usage(self, use: Node, index: int) -> None:
"""Remove a node from the uses of this value.

This is an internal method. It should only be called by the Node class.
"""
self._uses.pop((use, index))
self._uses.pop(Usage(use, index))

@property
def name(self) -> str | None:
Expand Down