Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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