-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IR] Mutable IR implementation #1344
Conversation
onnxscript/ir/_protocols.py
Outdated
Args: | ||
node: The node to put before this node. | ||
""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1344 +/- ##
==========================================
+ Coverage 64.91% 65.32% +0.40%
==========================================
Files 140 142 +2
Lines 19974 20414 +440
Branches 3349 3464 +115
==========================================
+ Hits 12967 13336 +369
- Misses 6469 6521 +52
- Partials 538 557 +19 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lintrunner found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Test Results 23 files ± 0 23 suites ±0 19m 26s ⏱️ + 2m 25s For more details on these failures, see this check. Results for commit ae4838c. ± Comparison against base commit edb68ed. ♻️ This comment has been updated with latest results. |
Modification in double iteration on the same graph is safe, but moving nodes from one to another is not. How should we handle/guard against it? Use boxes? |
I would like to bring #1349 into this PR. Created as a separate PR for review. |
onnxscript/ir/_core.py
Outdated
node, new_nodes, property_modifier=self._add_graph_reference_to_node | ||
) | ||
|
||
def insert_before(self, node: Node, new_nodes: Iterable[Node]) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see torch using
with graph.inserting_after(node):
...
Cause they have the need of creating nodes on the fly. I guess we don't have this need? Other than that, I think the imple here is simpler.
I decided to remove the read-only protocols. I realized they are overly complicated as many object fields refer to them and it is very hard to stay consistent. |
def __getitem__(self, index: int) -> NodeProtocol: ... | ||
def __len__(self) -> int: ... | ||
def __iter__(self) -> Iterator[NodeProtocol]: ... | ||
def __reversed__(self) -> Iterator[NodeProtocol]: ... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def __getitem__(self, index: int) -> NodeProtocol: ... | ||
def __len__(self) -> int: ... | ||
def __iter__(self) -> Iterator[NodeProtocol]: ... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
def __getitem__(self, index: int) -> NodeProtocol: ... | ||
def __len__(self) -> int: ... | ||
def __iter__(self) -> Iterator[NodeProtocol]: ... | ||
def __reversed__(self) -> Iterator[NodeProtocol]: ... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
Use a dictionary to map the values to the boxes in the doubly linked list implementation to 1. Remove the private pointer from Node to LinkedBox 2. Simplify membership checks 3. Ensure elements in the list are unique Also changed: 1. The node <-> graph relationships are now managed by Graph directly and not by a property_modifier function. Meaning the user is responsible to remove nodes from other graphs before adding the same node to a new graph. This helps separate the concern of the graph from the linked list, allowing us to treat the linked list as a normal list. The list also doesn't need to handle exclusivity, meaning the same value will not be removed from the original list when being added to a new list and can appear in multiple lists, which is what we expect how lists behave.
Thanks all for reviewing. All comments are addressed. I will create follow-ups if there are more comments. |
# Mutation methods | ||
def append(self, node: NodeProtocol, /) -> None: | ||
"""Append a node to the graph.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def extend(self, nodes: Iterable[NodeProtocol], /) -> None: | ||
"""Extend the graph with the given nodes.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def remove(self, node: NodeProtocol, /) -> None: | ||
"""Remove a node from the graph.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def insert_after(self, node: NodeProtocol, new_nodes: Iterator[NodeProtocol], /) -> None: | ||
"""Insert new nodes after the given node.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
# End Block | ||
def append(self, node: NodeProtocol, /) -> None: | ||
"""Append a node to the function.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def extend(self, nodes: Iterable[NodeProtocol], /) -> None: | ||
"""Extend the function with the given nodes.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def remove(self, node: NodeProtocol, /) -> None: | ||
"""Remove a node from the function.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
|
||
def insert_after(self, node: NodeProtocol, new_nodes: Iterator[NodeProtocol], /) -> None: | ||
"""Insert new nodes after the given node.""" | ||
... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
Implement mutation methods on Graph and Function and a DoublyLinkedList to support safe mutation during iteration. Nodes in different graphs can be moved to other graphs safely during iteration.
Methods implemented
The mutation methods are inspired by the pytorch FX graph methods.
Safe Iterators
The behavior of the iterators is:
A node cannot be added to a graph if it belongs to another graph. It needs to be removed first. The user is responsible for removing nodes from other graphs before adding the same node to a new graph.
Linked list implementation
The doubly linked list implementation is inspired by the pytorch FX graph: They are both doubly linked lists with a dummy root node. Whereas in FX
graph
contains the root node andNode
s are the links; we create theDoublyLinkedOrderedSet
class to decouple the list implementation form the graph. Additionally, we created theLinkedBox
class as a data struct to hold the prev/next pointers for the Nodes to allow nodes to move across graphs. This is in contrast to FX restriction of all nodes must belong to the same graph in their lifecycle. By allowing the nodes to move to different graphs, we are able to flatten/unflatten graphs without copying.DoublyLinkedOrderedSet is unit tested.
Next steps