Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f98cc73
Support antialiased bicubic resize in to_edge
shoumikhin Nov 1, 2026
3730081
Drop the unused bilinear decomposition
shoumikhin Nov 1, 2026
c482697
Cap the antialiased resize tap count at the input size
shoumikhin Nov 1, 2026
fa09c80
Run antialiased bicubic resize on a portable kernel
shoumikhin Nov 1, 2026
47a9462
Update the llava note about the bicubic resize
shoumikhin Nov 1, 2026
033d4e2
Ignore non-positive upsample scales, as ATen does
shoumikhin Nov 1, 2026
27d6112
Keep the bicubic resize kernel safe for any scale and size
shoumikhin Nov 1, 2026
aa013dc
Cover more of the bicubic resize kernel in its tests
shoumikhin Nov 1, 2026
ba8088a
Count the bicubic resize in the Arm all-ops priority list
shoumikhin Nov 1, 2026
6e1f05a
Rename a bicubic resize template parameter that Windows defines
shoumikhin Nov 1, 2026
a72ed9f
Initialize the bicubic resize test's temporary buffer
shoumikhin Nov 1, 2026
6bd911a
Reject integer antialiased bicubic resizes when lowering
shoumikhin Nov 1, 2026
f571d30
Link the upstream issue for the bicubic resize width-one difference
shoumikhin Nov 1, 2026
5734481
Tighten the bicubic resize kernel tests
shoumikhin Nov 1, 2026
9a4ab0d
Compare the lowered bicubic resize with eager over more sizes
shoumikhin Nov 1, 2026
593a22b
Count the bicubic resize in the Arm all-ops totals
shoumikhin Nov 1, 2026
c8b09c6
Point the bicubic resize dtype switch at its verifier check
shoumikhin Nov 1, 2026
3b127e4
Support uint8 in the bicubic resize kernel
shoumikhin Nov 1, 2026
8c7174c
Drop the integer-input check from the edge verifier
shoumikhin Nov 1, 2026
51234a8
Test the bicubic resize row buffer failure and empty height windows
shoumikhin Nov 1, 2026
13c0d61
Reject uint8 resize weights that do not fit in fixed point
shoumikhin Nov 1, 2026
29394ef
Seed the inputs of the bicubic resize to_edge test
shoumikhin Nov 1, 2026
512aeb4
Reject uint8 resize weights outside the int16 range
shoumikhin Nov 1, 2026
b02654b
Keep one window of double weights in the uint8 resize path
shoumikhin Nov 1, 2026
544f466
Add up uint8 resize windows in 64 bits
shoumikhin Nov 1, 2026
3dfe2be
Test the uint8 precision check and height-only resize
shoumikhin Nov 1, 2026
89dba5b
Drop the half-type resize checks that repeat another case
shoumikhin Nov 1, 2026
b73cce8
Check that uint8 resize fails without temporary memory
shoumikhin Nov 1, 2026
f689f45
Size bicubic resize windows the way ATen does
shoumikhin Nov 2, 2026
3d46451
Keep only the bicubic resize test cases that catch a distinct bug
shoumikhin Nov 2, 2026
31bf43b
Run the dynamic to_edge resize test on two sizes
shoumikhin Nov 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions backends/arm/cmsis_pack/test/all_ops/SKIPPED_OPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This test gives **every** operator the pack ships two kinds of coverage:

- **Build / link** — `gen_cproject.py` selects all 198 operator components, so the
- **Build / link** — `gen_cproject.py` selects all 199 operator components, so the
consumer firmware links every op's generated registration, forward declaration and
kernel source. *No operator is exempt from this.*
- **Execution** — `generate_test_models.py` turns each op's recipe in `op_recipes.py`
Expand All @@ -11,7 +11,7 @@ This test gives **every** operator the pack ships two kinds of coverage:
A **skip** only means an op is not *executed*; it is still build/link-covered. Every skip
carries a reason (enforced by `test_op_recipes.py::test_no_silent_coverage_gaps`), so
there are no silent gaps. Current status on the CPU target (Corstone-315): **167 executed
(incl. 2 shape variants), 33 skipped** of 198 operator components, all passing.
(incl. 2 shape variants), 34 skipped** of 199 operator components, all passing.

The Ethos-U variant has a separate, weaker status — see
[Ethos-U variant status](#ethos-u-variant-status) before citing its results.
Expand Down Expand Up @@ -107,7 +107,7 @@ APIs to force the conversion into the graph.

---

## 7. Just needs a bespoke recipe — `grid_sampler_2d`, `cdist_forward`, `pdist_forward`, `upsample_bilinear2d_aa`
## 7. Just needs a bespoke recipe — `grid_sampler_2d`, `cdist_forward`, `pdist_forward`, `upsample_bicubic2d_aa`, `upsample_bilinear2d_aa`

**Why:** no fundamental blocker — they only need specific inputs the bulk recipes don't
provide (a sampling grid in `[-1, 1]`; two point sets for the distance ops; the
Expand All @@ -117,6 +117,7 @@ provide (a sampling grid in `[-1, 1]`; two point sets for the distance ops; the
- `grid_sampler_2d`: `F.grid_sample(input, grid)` with `grid` in `[-1, 1]`.
- `cdist_forward`: `torch.cdist(a, b)` with `a:(B,N,D)`, `b:(B,M,D)`.
- `pdist_forward`: `torch.pdist(x)` with `x:(N,D)`.
- `upsample_bicubic2d_aa`: `F.interpolate(x, scale_factor=2, mode="bicubic", antialias=True)`.
- `upsample_bilinear2d_aa`: `F.interpolate(x, scale_factor=2, mode="bilinear", antialias=True)`.

**Effort:** small — a few lines each in `op_recipes.py`.
Expand Down Expand Up @@ -195,7 +196,7 @@ The Docker image used for the consumer build already provides this, so they expo

## Priority to raise execution coverage

1. **§7 + §8 bespoke recipes** (5 ops) — trivial, immediate wins.
1. **§7 + §8 bespoke recipes** (6 ops) — trivial, immediate wins.
2. **§1 + §2 structural-assertion mode** (5 ops) — one small runner/manifest feature.
3. **Cortex-M env** — install `cmsis_nn`; unlocks 16 ops with no new code.
4. **§10 quantized path** (9 ops) — one quantized export path.
Expand Down
1 change: 1 addition & 0 deletions backends/arm/cmsis_pack/test/all_ops/op_recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ def _skip(category: str, name: str, reason: str) -> None:
"grid_sampler_2d": "recipe TODO (sampler grid setup)",
"cdist_forward": "recipe TODO",
"pdist_forward": "recipe TODO",
"upsample_bicubic2d_aa": "antialiased upsample; recipe TODO",
"upsample_bilinear2d_aa": "antialiased upsample; recipe TODO",
"view_as_real_copy": "complex dtype unsupported on target",
"allclose": "returns scalar bool; ET out-variant shape TODO",
Expand Down
6 changes: 3 additions & 3 deletions examples/models/llava/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ def image_preprocess(self, img: torch.Tensor) -> torch.Tensor:
# fill=tuple(int(x * 255) for x in self.image_mean),
# )

# TODO: implement _upsample_bicubic_aa.out in portable kernel library.
# here padded shape should be max(h, w) x max(h, w)
# skipping resize for now due to missing _upsample_bicubic_aa kernel in portable
# prepare_image() already resized the image to fit in the crop size,
# so only the padding is exported. The original pads to
# max(h, w) x max(h, w) and then resizes:
# resized = resize(
# padded,
# size=[
Expand Down
60 changes: 60 additions & 0 deletions exir/program/test/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,66 @@ def forward(self, x):

to_edge(export(M(), (torch.randn(4),))).to_executorch()

def test_antialiased_resize(self):
class Resize(torch.nn.Module):
def forward(self, x):
return (
F.interpolate(x, size=(24, 18), mode="bicubic", antialias=True),
F.interpolate(
x, scale_factor=(1.75, 0.45), mode="bicubic", antialias=True
),
F.interpolate(
x,
scale_factor=(0.4, 1.3),
mode="bicubic",
align_corners=True,
antialias=True,
),
)

generator = torch.Generator().manual_seed(0)

def image(h, w, dtype, memory_format=torch.contiguous_format):
if dtype == torch.uint8:
t = torch.randint(
0, 256, (1, 3, h, w), dtype=dtype, generator=generator
)
else:
t = torch.randn(1, 3, h, w, generator=generator)
return t.to(memory_format=memory_format)

dims = {"x": {2: Dim("h", min=8, max=64), 3: Dim("w", min=8, max=64)}}
sizes = [(24, 32), (64, 64)]
cases = []
for dtype in (torch.float32, torch.uint8):
cases.append((None, [image(24, 32, dtype)]))
for f in (torch.contiguous_format, torch.channels_last):
cases.append((dims, [image(h, w, dtype, f) for h, w in sizes]))
for dynamic_shapes, inputs in cases:
edge = to_edge(
export(Resize(), (inputs[0],), dynamic_shapes=dynamic_shapes)
)
# The op stays whole and runs on its portable kernel.
self.assertEqual(
self._count_nodes(
edge.exported_program().graph_module,
[exir_ops.edge.aten._upsample_bicubic2d_aa.default],
),
3,
)
program = _load_for_executorch_from_buffer(edge.to_executorch().buffer)
for t in inputs:
with self.subTest(
dtype=t.dtype,
dynamic=dynamic_shapes is not None,
shape=t.shape,
channels_last=t.is_contiguous(memory_format=torch.channels_last),
):
# uint8 follows ATen's fixed-point path exactly.
tol = 0 if t.dtype == torch.uint8 else 1e-5
for actual, expected in zip(program.forward((t,)), Resize()(t)):
torch.testing.assert_close(actual, expected, atol=tol, rtol=tol)

def test_float_sym_max_min_rejected(self):
# The runtime's sym_max and sym_min kernels only take ints.
def model(sym_op):
Expand Down
1 change: 1 addition & 0 deletions exir/verification/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def _get_core_aten_ops_exception_list(self) -> List[torch._ops.OpOverload]:
exception_list = (
[
torch.ops.aten.mkldnn_rnn_layer.default,
torch.ops.aten._upsample_bicubic2d_aa.default,
torch.ops.aten._upsample_bilinear2d_aa.default,
torch.ops.aten.quantize_per_tensor.default,
torch.ops.aten.dequantize.self,
Expand Down
2 changes: 2 additions & 0 deletions kernels/aten/functions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

- op: _unique2.out

- op: _upsample_bicubic2d_aa.out

- op: _upsample_bilinear2d_aa.out

- op: abs.out
Expand Down
Loading
Loading