Skip to content

Commit 0295293

Browse files
authored
Arm backend: Support dynamic negative pad values (#24084)
For both cases, negative values on both sides mean that the pad is just turned into a slice. When just one side is negative: - Reflective and replicate mode: The original border is reflected/replicated, then the negative side is sliced. This means values that are sliced away can appear in the padded border. - Circluar mode: The negative side is sliced, then the resulting slice is rotated. Add regression cases for cropping on either side, mixed signs, shifts, and multiple dimensions. Cover reflection and replication where cropping removes the original dimension but leaves padded elements. This commit was authored with Codex. cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani Signed-off-by: Erik Lundell <erik.lundell@arm.com>
1 parent b578b2e commit 0295293

2 files changed

Lines changed: 52 additions & 5 deletions

File tree

‎backends/arm/_passes/rewrite_pad.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,24 @@ def _rewrite_non_constant_pad(
141141
)
142142
# F.pad pad tuples are ordered from the innermost dimension outward.
143143
dim = rank - 1 - pair_idx
144-
output = self._pad_along_dim(output, dim, left, right, mode, meta)
144+
# Reflect and replicate use the original borders; circular wraps
145+
# the cropped input.
146+
if mode != "circular":
147+
output = self._pad_along_dim(
148+
output, dim, max(left, 0), max(right, 0), mode, meta
149+
)
150+
if left < 0 or right < 0:
151+
output = super().call_operator(
152+
exir_ops.edge.aten.slice_copy.Tensor,
153+
(output, dim, max(-left, 0), right if right < 0 else None),
154+
{},
155+
meta,
156+
True,
157+
)
158+
if mode == "circular":
159+
output = self._pad_along_dim(
160+
output, dim, max(left, 0), max(right, 0), mode, meta
161+
)
145162
return output
146163

147164
def call_operator(self, op, args, kwargs, meta, updated=False):

‎backends/arm/test/ops/test_constant_pad_nd.py‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,38 @@
100100
None,
101101
"circular",
102102
),
103+
**{
104+
f"{mode}_{case}": lambda mode=mode, shape=shape, pad=pad: (
105+
torch.rand(shape),
106+
pad,
107+
None,
108+
mode,
109+
)
110+
for mode in ("reflect", "replicate", "circular")
111+
for case, (shape, pad) in {
112+
"crop_end": ((1, 3, 6), (0, -2)),
113+
"crop_start": ((1, 3, 6), (-2, 0)),
114+
"crop_both": ((1, 3, 6), (-1, -2)),
115+
"crop_and_pad": ((1, 3, 6), (-2, 3)),
116+
"pad_and_crop": ((1, 3, 6), (3, -2)),
117+
"crop_shift": ((1, 3, 6), (-1, 1)),
118+
"crop_multidim": ((1, 3, 6, 6), (-1, 2, 3, -2)),
119+
"crop_penultimate_dim": ((1, 3, 6, 6), (0, 0, -2, 1)),
120+
}.items()
121+
},
122+
**{
123+
f"{mode}_{case}": lambda mode=mode, pad=pad: (
124+
torch.rand(1, 3, 6),
125+
pad,
126+
None,
127+
mode,
128+
)
129+
for mode in ("reflect", "replicate")
130+
for case, pad in {
131+
"crop_all_left": (-6, 2),
132+
"crop_all_right": (2, -6),
133+
}.items()
134+
},
103135
}
104136

105137
test_data_suite_bf16 = {
@@ -159,10 +191,8 @@ def __init__(
159191
self.value = value
160192
self.mode = mode
161193
nonzero_idx = len(pad)
162-
for i in range(0, len(pad), 2):
163-
if pad[i] + pad[i + 1] == 0:
164-
nonzero_idx = i
165-
break
194+
while nonzero_idx >= 2 and pad[nonzero_idx - 2 : nonzero_idx] == (0, 0):
195+
nonzero_idx -= 2
166196
self.pad = pad[:nonzero_idx]
167197

168198
def forward(self, x: torch.Tensor):

0 commit comments

Comments
 (0)