Skip to content

Do not delegate CoreML reflect/replicate pads beyond the last two dims - #23687

Open
hemanth1999k wants to merge 2 commits into
pytorch:mainfrom
hemanth1999k:coreml-reject-nonconstant-pad-beyond-last-two-dims
Open

hemanth1999k wants to merge 2 commits into
pytorch:mainfrom
hemanth1999k:coreml-reject-nonconstant-pad-beyond-last-two-dims

Conversation

@hemanth1999k

@hemanth1999k hemanth1999k commented Oct 11, 2026 •

Copy link
Copy Markdown

Summary

Core ML only supports reflect and replicate padding on the last two dimensions. ReflectionPad3d, ReplicationPad3d, or a reflect pad of only the depth dimension of a 5D input currently lower to Core ML without complaint, then fail when the program loads:

Padding for more than two dimensions only supports `constant` mode

CoreMLPartitioner now overrides support for these pads (aten.pad in reflect/replicate mode with a nonzero pad outside the last two dims, plus reflection_pad3d / replication_pad3d). ops_to_not_decompose therefore stops keeping them whole, they decompose into ops Core ML runs, and the model stays delegated and matches eager. 1D/2D reflect and replicate pads and constant pads are unchanged.

Fixes #11708
Fixes #11709

Test plan

Added test_nonconstant_pad_beyond_last_two_dims_is_decomposed to backends/apple/coreml/test/test_coreml_partitioner.py. It covers ReflectionPad3d, ReplicationPad3d, and a depth-only reflect pad. For each it checks that aten.pad.default is not in ops_to_not_decompose, lowers with to_edge_transform_and_lower, runs the program through executorch.runtime.Runtime, and compares with eager (atol/rtol 1e-2 because Core ML computes in fp16). It also checks that a 2D reflect pad is still kept whole.

python -m pytest backends/apple/coreml/test/test_coreml_partitioner.py

13/13 pass with the change; on main only the new test fails. Run on macOS 26.2, executorch 1.5.1 / main, torch 2.14.0, coremltools 9.0. ufmt and flake8 are clean.

Circular 3D pads still raise a ValueError at lowering, as they do on main; that is a separate issue and out of scope here.

cc @nil-is-all

Core ML only supports reflect and replicate padding on the last two
dimensions. ReflectionPad3d, ReplicationPad3d, or a reflect pad of just
the depth dimension of a 5D input lowered fine, then failed to load:

  Padding for more than two dimensions only supports `constant` mode

The partitioner now overrides support for those pads, so
ops_to_not_decompose no longer keeps them whole. They decompose into
ops Core ML runs, and the model stays delegated and matches eager.

Fixes pytorch#11708
Fixes pytorch#11709
@pytorch-bot

pytorch-bot Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

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

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

⚠️ 16 Awaiting Approval

As of commit d12c798 with merge base f2575be (image):

AWAITING APPROVAL - The following workflows need approval before CI can run:

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

@meta-cla

meta-cla Bot commented Oct 11, 2026

Copy link
Copy Markdown

Hi @hemanth1999k!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@linux-foundation-easycla

linux-foundation-easycla Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: hemanth1999k / name: hemanth1999k (5e92fb7)

@hemanth1999k

Copy link
Copy Markdown
Author

@pytorchbot label "release notes: apple"

@pytorch-bot pytorch-bot Bot added the release notes: apple Changes to the Apple backend delegate label Oct 11, 2026
@meta-cla

meta-cla Bot commented Oct 11, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@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 Oct 11, 2026
@executorch-triage executorch-triage Bot added the community: contribution PRs coming from community (excluding hardware partners) label Oct 11, 2026
@metascroy

Copy link
Copy Markdown
Contributor

@claude review this code. Do they test that supported padding lower to coreml delegate (call delegate node) and non-supported do not lower (op left in graph with no call deelgate node)

@claude

claude Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Claude finished @metascroy's task in 1m 14s —— View job


Short answer: no. The test never looks at the lowered graph, so it does not check that supported pads end up in an executorch_call_delegate node, or that unsupported pads stay in the graph outside a delegate. It needs graph assertions like the ones the argmax test right above it already has.

Detailed review

PR Review: #23687

Summary

The PR makes CoreMLPartitioner reject reflect/replicate pads that touch more than the last two dims. The partitioner logic looks right, but the test can't tell where those pads actually end up, and the PR description's claim that "the model stays delegated" doesn't hold up.

Testing

1. No check on the lowered graph (backends/apple/coreml/test/test_coreml_partitioner.py:466-485)

For the three unsupported cases, the test checks two things:

  • aten.pad.default is not in ops_to_not_decompose.
  • If _TEST_RUNTIME is set, the output matches eager.

Neither check shows where the op runs. The numeric check would pass even if nothing were delegated, and _TEST_RUNTIME is off on Linux CI, so there it only checks ops_to_not_decompose.

For the supported 2D reflect case (:482-486), the test only checks that aten.pad.default is kept whole by ops_to_not_decompose. It never lowers the model, so nothing confirms the 2D pad actually reaches Core ML.

test_argmax_argmin_dim_none_falls_back_to_portable (:406-434) in the same file already shows the pattern to copy. Collect n.target.__name__ from edge.exported_program().graph.nodes and assert:

  • Unsupported pads (3D reflect, 3D replicate, depth-only reflect): the pad op is in the top-level graph and is not inside a delegate. The expected name is probably aten.reflection_pad3d.default / aten.replication_pad3d.default (see point 2).
  • Supported 2D reflect pad: run to_edge_transform_and_lower, then assert executorch_call_delegate is in op_names and aten.pad.default is not.

Fix this →

2. The decomposition probably falls back to portable, not Core ML (backends/apple/coreml/partition/coreml_partitioner.py:91-96, 109)

Once aten.pad is no longer kept whole, it most likely decomposes to aten.reflection_pad3d / aten.replication_pad3d. I think these are core ATen ops, but I haven't confirmed that. _PAD_3D_TARGETS explicitly rejects the edge versions of both, so the op would run on the portable kernels (kernels/portable/functions.yaml:818, 833).

For the single-op models in this test, that would mean the whole program runs on portable with no delegate. The fallback is still correct, since it fixes the load failure. But the PR description says the pads "decompose into ops Core ML runs, and the model stays delegated", and that is probably wrong.

I couldn't run Python in this environment, so I haven't confirmed this. The assertions in point 1 would answer it for certain, and the description should be updated to match what happens.

Recommendation

Request Changes. Add graph-level assertions: a delegate for the 2D reflect case, and the pad op left outside any delegate for the 3D cases. Then correct the description to match what actually happens.


The pad regression test only checked ops_to_not_decompose and, on
macOS, the numerics, so it never showed where the pad ran.  Lower every
case and assert on the graph: pads Core ML cannot load are decomposed
(no pad left in the graph or in the delegate, the index-based
decomposition is delegated), and 2D reflect / replicate pads are
delegated whole as pad.
@hemanth1999k

Copy link
Copy Markdown
Author

Added graph assertions modeled on the argmax test.

  • 2D reflect/replicate pads: the test asserts executorch_call_delegate is present, aten.pad.default is not in the top-level graph, and the pad is inside the delegate.
  • Pads Core ML can't load (3D reflect/replicate, depth-only reflect): the pad isn't left in the graph for portable. It decomposes (to abs/arange/index/sub for reflect, arange/clamp/index for replicate), and that decomposition is still delegated. So the test asserts a delegate call is present, aten.pad.default is in neither the top-level graph nor the delegate, and aten.index.Tensor is inside the delegate.

Without the partitioner change the pad is delegated whole, these assertions fail, and on macOS the program fails to load with "Padding for more than two dimensions only supports constant mode". (The bot guessed the decomposed op would fall back to portable, but running it shows the decomposition stays on Core ML, so the model does stay fully delegated.)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. community: contribution PRs coming from community (excluding hardware partners) release notes: apple Changes to the Apple backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CoreML ReplicationPad3d fails to load at runtime CoreML ReflectionPad3d fails to load at runtime

3 participants