Skip to content

Commit bfeeb05

Browse files
authored
Fix unsqueeze permutation adaptation in RemovePermutesAroundElementwiseOps (#21551)
Differential Revision: D114508242 Pull Request resolved: #21551
1 parent d3be1f7 commit bfeeb05

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

backends/transforms/remove_permutes_around_elementwise_ops.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,15 @@ def _adapt_permute_across_view(
149149
dim = cast(int, node.args[1])
150150
rank = len(permute)
151151
index = dim if dim >= 0 else dim + rank + 1
152-
new_perm = [x + 1 if x >= index else x for x in permute]
153-
new_perm.insert(index, index)
152+
# `index` is a POSITION in the permuted output; the un-permuted
153+
# position the new dim lands at is the permutation VALUE there
154+
# (or the end, when appending). permute_subgraph rewrites the dim
155+
# arg to exactly this value, so the two must agree -- using `index`
156+
# here instead silently desynchronises them whenever
157+
# permute[index] != index.
158+
inserted_value = permute[index] if index < rank else rank
159+
new_perm = [x + 1 if x >= inserted_value else x for x in permute]
160+
new_perm.insert(index, inserted_value)
154161
return new_perm
155162

156163
# Handle explicit squeeze_copy(dim)

backends/transforms/test/test_permute_optimization_passes.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,39 @@ def test_permute_unsqueeze_copy_neg_dim_mul_squeeze_copy_permute(self) -> None:
10341034
"permute_unsqueeze_copy_neg_dim_mul_squeeze_copy_permute",
10351035
)
10361036

1037+
def test_unsqueeze_at_moved_position(self) -> None:
1038+
"""The permutation moves the unsqueeze position (P[index] != index), so
1039+
the adapted permutation must be built from P[index], not index."""
1040+
builder = GraphBuilder()
1041+
x_data = torch.randn(1, 8, 16)
1042+
x = builder.placeholder("x", x_data)
1043+
p1 = builder.call_operator(
1044+
op=exir_ops.edge.aten.permute_copy.default, args=(x, [0, 2, 1])
1045+
)
1046+
u = builder.call_operator(
1047+
op=exir_ops.edge.aten.unsqueeze_copy.default, args=(p1, 1)
1048+
)
1049+
mul = builder.call_operator(op=exir_ops.edge.aten.mul.Tensor, args=(u, u))
1050+
sq = builder.call_operator(
1051+
op=exir_ops.edge.aten.squeeze_copy.dim, args=(mul, 1)
1052+
)
1053+
p2 = builder.call_operator(
1054+
op=exir_ops.edge.aten.permute_copy.default, args=(sq, [0, 2, 1])
1055+
)
1056+
builder.output([p2])
1057+
original = builder.get_graph_module()
1058+
gm_before = copy.deepcopy(original)
1059+
1060+
p = RemovePermutesAroundElementwiseOps()
1061+
result = cast(PassResult, p(original))
1062+
self.assertTrue(result.modified)
1063+
self.assertEqual(
1064+
count_node(result.graph_module, exir_ops.edge.aten.permute_copy.default), 0
1065+
)
1066+
validate_numerics(
1067+
gm_before, result.graph_module, [x_data], "UnsqueezeAtMovedPosition"
1068+
)
1069+
10371070
def test_upstream_view_rank_mismatch_no_crash(self) -> None:
10381071
"""Regression test for IndexError when a squeeze/unsqueeze view_copy
10391072
is reached via upstream traversal with a permutation whose rank does

0 commit comments

Comments
 (0)