Skip to content

Commit 8ea99d7

Browse files
internal developerjedrzejmyrcha
authored andcommitted
subgraph rewriter supports matched pattern with no users (#4)
cherry-pick from upstream: pytorch#143842
1 parent 342d6d5 commit 8ea99d7

2 files changed

Lines changed: 65 additions & 3 deletions

File tree

test/fx/test_subgraph_rewriter.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,51 @@ def comparison(x1, x2, x3):
678678
test_outs = traced.forward(x1, x2, x3)
679679
self.assertEqual(ref_outs, test_outs)
680680

681+
def test_subgraph_rewriter_with_unused_results(self):
682+
class M(torch.nn.Module):
683+
def forward(self, x, y, cache):
684+
m = torch.mul(x, y)
685+
n = cache.index_copy(0, torch.tensor([0]), m)
686+
p = torch.ops.aten.copy.default(cache, n)
687+
q = torch.ops.aten.copy_.default(cache, p) # noqa: F841
688+
u = torch.relu(cache)
689+
# check the result to ensure cache is updated before relu op
690+
return u
691+
692+
def pattern(self_tensor, src_tensor):
693+
p = torch.ops.aten.copy.default(self_tensor, src_tensor)
694+
q = torch.ops.aten.copy_.default(self_tensor, p)
695+
return q
696+
697+
def replacement(self_tensor, src_tensor):
698+
q = torch.ops.aten.copy_.default(self_tensor, src_tensor)
699+
return q
700+
701+
def comparison(x, y, cache):
702+
m = torch.mul(x, y)
703+
n = cache.index_copy(0, torch.tensor([0]), m)
704+
q = torch.ops.aten.copy_.default(cache, n) # noqa: F841
705+
u = torch.relu(cache)
706+
return u
707+
708+
traced = symbolic_trace(M())
709+
comparison_fn = symbolic_trace(comparison)
710+
711+
subgraph_rewriter.replace_pattern(traced, pattern, replacement)
712+
713+
traced.graph.lint()
714+
715+
x = torch.randn(1, 8)
716+
y = torch.randn(1, 8)
717+
cache = torch.randn(2, 8)
718+
x_clone = x.clone()
719+
y_clone = y.clone()
720+
cache_clone = cache.clone()
721+
722+
ref_outs = comparison_fn(x, y, cache)
723+
test_outs = traced.forward(x_clone, y_clone, cache_clone)
724+
self.assertEqual(ref_outs, test_outs)
725+
681726
def test_subgraph_rewriter_call_method(self):
682727
class M(torch.nn.Module):
683728
def forward(self, x):

torch/fx/subgraph_rewriter.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,11 @@ def _replace_pattern(
364364
user_nodes: Set[Node] = set()
365365
for n in match.returning_nodes:
366366
user_nodes.update(n.users)
367-
assert user_nodes, "The returning_nodes should have at least one user node"
368367

369-
if len(user_nodes) == 1:
368+
first_user_node = None
369+
if len(user_nodes) == 0:
370+
first_user_node = None
371+
elif len(user_nodes) == 1:
370372
first_user_node = next(iter(user_nodes))
371373
else:
372374
# If there are multiple user nodes, we need to find the first user node
@@ -376,7 +378,22 @@ def _replace_pattern(
376378
first_user_node = n
377379
break
378380

379-
with original_graph.inserting_before(first_user_node): # type: ignore[possibly-undefined]
381+
first_next_node = None
382+
if first_user_node is None:
383+
# no users, so we insert the replacement graph before the first next
384+
# node of returning nodes
385+
next_node = None
386+
for n in reversed(original_graph.nodes):
387+
if n in match.returning_nodes:
388+
first_next_node = next_node
389+
break
390+
else:
391+
next_node = n
392+
insert_point = (
393+
first_user_node if first_user_node is not None else first_next_node
394+
)
395+
assert insert_point is not None, "The insert point can't be None"
396+
with original_graph.inserting_before(insert_point):
380397
copied_returning_nodes = original_graph.graph_copy(
381398
replacement_graph, val_map
382399
)

0 commit comments

Comments
 (0)