Skip to content

Add a portable kernel for antialiased bicubic resize - #24444

Closed
pytorchbot wants to merge 1 commit into
release/1.6from
cherry-pick-24400-by-pytorch_bot_bot_
Closed

pytorchbot wants to merge 1 commit into
release/1.6from
cherry-pick-24400-by-pytorch_bot_bot_

Conversation

@pytorchbot

@pytorchbot pytorchbot commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

to_edge cannot lower F.interpolate(x, mode="bicubic", antialias=True) today. It exports to aten._upsample_bicubic2d_aa, which is not a core ATen op, so to_edge stops with:

SpecViolationError: Operator torch._ops.aten._upsample_bicubic2d_aa.default is not in Core ATen opset

ExecuTorch also has no kernel for it.

What this change does

It adds a portable kernel for _upsample_bicubic2d_aa.out and lists the op as an exception in the edge verifier, as the antialiased bilinear resize does.

Like ATen's CPU kernel, it uses the Keys cubic filter (a = -0.5), widened when shrinking, resizes the width, then the height, and copies a dim that keeps its size. float16 and bfloat16, which ATen's CPU kernel lacks, are computed in float32 and rounded once. uint8 follows ATen's fixed-point path: 16-bit integer weights, and each pass rounds and clamps. Other integer types fail in the kernel, as they do in ATen.

Between passes it keeps only the rows one output row reads, so shrinking 1080x1920 to 224x224 needs about 95 KB of temporary memory (43 KB for uint8). It rejects scales F.interpolate never produces: too small for the input, or (uint8 only) with weights too large for 16 bits. Where ATen's generic CPU path is wrong (contiguous input, output width 1, changing height; PyTorch issue 200568), it differs from eager on purpose.

The shared upsample helper now ignores a non-positive scale, as ATen does; of the other upsample kernels, only antialiased bilinear can pass one. A stale llava comment is updated, and the Arm all-ops test marks the kernel as needing a recipe. Like several portable kernels, it has no experimental CPU backend route yet.

Testing

  • New kernel tests check against ATen: shrinking, align_corners, scales, windows past the input, kept dims, both layouts, a batch, all float types, uint8 bit for bit, a dynamic output, running out of temporary memory, and error cases. Each case catches a bug that no other case catches.
  • The to_edge test compares static and dynamic programs (both layouts) with eager in float32 and, bit for bit, in uint8. It fails without the kernel or the verifier exception.
  • Outside the suite, a random sweep matched ATen's uint8 results exactly on x86 and Arm, on both its vectorized and generic paths; float32 within 7e-6 for unit-scale inputs, half types within one unit in the last place.
  • With XNNPACK, the resize stays outside the delegate. On one laptop thread, shrinking a contiguous 1080x1920 input to 224x224 takes about 7 ms (12 ms channels last).

cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani

`to_edge` cannot lower `F.interpolate(x, mode="bicubic",
antialias=True)` today. It exports to `aten._upsample_bicubic2d_aa`,
which is not a core ATen op, so `to_edge` stops with:

```
SpecViolationError: Operator torch._ops.aten._upsample_bicubic2d_aa.default is not in Core ATen opset
```

ExecuTorch also has no kernel for it.

## What this change does

It adds a portable kernel for `_upsample_bicubic2d_aa.out` and lists the
op as an exception in the edge verifier, as the antialiased bilinear
resize does.

Like ATen's CPU kernel, it uses the Keys cubic filter (a = -0.5),
widened when shrinking, resizes the width, then the height, and copies a
dim that keeps its size. float16 and bfloat16, which ATen's CPU kernel
lacks, are computed in float32 and rounded once. uint8 follows ATen's
fixed-point path: 16-bit integer weights, and each pass rounds and
clamps. Other integer types fail in the kernel, as they do in ATen.

Between passes it keeps only the rows one output row reads, so shrinking
1080x1920 to 224x224 needs about 95 KB of temporary memory (43 KB for
uint8). It rejects scales `F.interpolate` never produces: too small for
the input, or (uint8 only) with weights too large for 16 bits. Where
ATen's generic CPU path is wrong (contiguous input, output width 1,
changing height; [PyTorch issue
200568](pytorch/pytorch#200568)), it differs
from eager on purpose.

The shared upsample helper now ignores a non-positive scale, as ATen
does; of the other upsample kernels, only antialiased bilinear can pass
one. A stale llava comment is updated, and the Arm all-ops test marks
the kernel as needing a recipe. Like several portable kernels, it has no
experimental CPU backend route yet.

## Testing

- New kernel tests check against ATen: shrinking, `align_corners`,
scales, windows past the input, kept dims, both layouts, a batch, all
float types, uint8 bit for bit, a dynamic output, running out of
temporary memory, and error cases. Each case catches a bug that no other
case catches.
- The `to_edge` test compares static and dynamic programs (both layouts)
with eager in float32 and, bit for bit, in uint8. It fails without the
kernel or the verifier exception.
- Outside the suite, a random sweep matched ATen's uint8 results exactly
on x86 and Arm, on both its vectorized and generic paths; float32 within
7e-6 for unit-scale inputs, half types within one unit in the last
place.
- With XNNPACK, the resize stays outside the delegate. On one laptop
thread, shrinking a contiguous 1080x1920 input to 224x224 takes about 7
ms (12 ms channels last).

(cherry picked from commit a5476a5)
@pytorch-bot

pytorch-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24444

Note: Links to docs will display an error until the docs builds have been completed.

⏳ No Failures, 349 Pending

As of commit e1d0517 with merge base 4db778e (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 2, 2026
@github-actions github-actions Bot added ciflow/trunk module: arm Issues related to arm backend labels Nov 2, 2026
@shoumikhin shoumikhin closed this Nov 2, 2026
@shoumikhin
shoumikhin deleted the cherry-pick-24400-by-pytorch_bot_bot_ branch November 2, 2026 17:06
@shoumikhin

Copy link
Copy Markdown
Contributor

Replaced by #24447, which is stacked on #24298 so it carries only this change.

This branch was successfully deployed

1 active deployment
cadence — e1d05170 Deployed Nov 2, 2026 by pytorchbot via hifi-op-test / hifi4 #35966
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/trunk CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. module: arm Issues related to arm backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants