Skip to content

[CoreML] Fall back for ML Program MatMul with a dynamic K on only one input - #34195

Open
Vijay Kapse (vijay-kapse) wants to merge 1 commit into
microsoft:mainfrom
vijay-kapse:fix/coreml-matmul-dynamic-k
Open

Vijay Kapse (vijay-kapse) wants to merge 1 commit into
microsoft:mainfrom
vijay-kapse:fix/coreml-matmul-dynamic-k

Conversation

@vijay-kapse

Copy link
Copy Markdown
Contributor

Description

In ML Program format, the CoreML EP now leaves a MatMul on the CPU EP when its K dimension is static on one input and dynamic on the other. Two exceptions stay on CoreML: a static K of 1, and a constant 2D B (a linear layer), because both still load. The check is in GemmOpBuilder::IsOpSupportedImpl.

Motivation and Context

Fixes #34194.

ModelBuilder::RegisterModelInputOutput declares every dynamic input dim with a default size of 1, and CoreML runs shape inference on the default shapes when it loads the model. For A[b, 2, t, 96] @ B[p, q, r, s] the defaults are [1, 2, 1, 96] @ [1, 1, 1, 1], so the load fails and session creation fails with:

Failed to PropagateInputTensorShapes: std::invalid_argument during type inference for ios18.matmul: shapes of x and y are not broadcastable.
Failed to create MLModel, error: Failed to build the model execution plan using a model architecture file '.../model.mil' with error code: -14.

This is the relative-position attention MatMul in piper (VITS) models.

I checked which shapes fail with the 1.31.0 wheel (ML Program, single MatMul, output compared against the CPU EP):

A B before
[b,2,t,96] [p,q,r,s] (input) fails to load
[b,2,t,k] [p,q,96,s] (input) fails to load
[m,k] / [m,96] [96,n] / [k,n] (input) fails to load
[b,t,96] [b2,k,n] (input) fails to load
[b,2,t,k] [1,1,96,9] (constant) fails to load
[b,t,k] [1,96,8] (constant) fails to load
[b,2,t,k] [p,q,r,s] (K dynamic on both) ok
[m,k] [1,n] (static K = 1) ok
[m,k], [b,t,k], [b,2,t,k] [96,8] (constant 2D) ok

The new check rejects exactly the rows that fail and keeps the ones that work on CoreML, so no MatMul that loads today moves to the CPU. For a constant 2D B, CoreML's E5 compiler still logs the same shape-inference error, but CoreML falls back to another engine and the results match the CPU EP, so I left that case alone. Gemm is unchanged: its B is always a constant 2D tensor.

Testing

  • New CoreMLExecutionProviderTest.MatMulStaticKOnADynamicKOnB and MatMulDynamicKOnAStaticKOnB check that the MatMul falls back and the output matches the CPU EP. Both fail on main with the load error above and pass with this change. MatMulDynamicKSupportedCases checks that the three supported cases stay on CoreML; it passes both before and after.
  • 1584 CoreML / MatMul / Gemm / Attention / Einsum tests pass in onnxruntime_provider_test (macOS arm64, Release, --use_coreml).
  • piper en_US-amy-medium.onnx with ML Program: with this change and [CoreML] Saturate out-of-range int64 constants to int32 instead of throwing #34193 together, session creation now succeeds. The first run then stops on a separate limitation: a NonZero in the duration predictor returns an empty tensor, which the CoreML EP rejects as a partition input. That's not part of this change.

… input

The CoreML EP declares dynamic input dims with a default size of 1, and
CoreML runs shape inference on those default shapes when it loads the model.
For a MatMul where K is static on one input and dynamic on the other, the
default shapes give mismatched K values and the model fails to load with
"shapes of x and y are not broadcastable".

Leave those MatMuls to the CPU EP. A static K of 1 matches the default, and a
constant 2D B (a linear layer) still loads, so both stay on CoreML.

Fixes microsoft#34194
Copilot AI balanced review requested due to automatic review settings November 13, 2026 15:37
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The MatMul test repeats the static-A/dynamic-B K configuration, leaving the intended dynamic-A/static-B fallback with a nonconstant B untested.

1 open issue
What changed in this PR

This PR addresses #34194 by routing MatMul operations that can fail CoreML ML Program loading to the CPU execution provider.

Changes:

  • Rejects MatMul when K is dynamic on only one input, except when the static K is 1 or B is a constant 2D tensor.
  • Adds tests for CPU fallback and supported CoreML cases, checking provider assignment and outputs against CPU results.
  • Leaves Gemm and NeuralNetwork-format behavior unchanged.
File Description
onnxruntime/​test/​providers/​coreml/​coreml_basic_test.cc Adds a dynamic-K test helper and fallback and supported-case tests.
onnxruntime/​core/​providers/​coreml/​builders/​impl/​gemm_op_builder.cc Adds the ML Program MatMul support check and preserves supported exceptions.

Review effort: Balanced

}

TEST(CoreMLExecutionProviderTest, MatMulDynamicKOnAStaticKOnB) {
RunMatMulDynamicKTest({-1, -1, 96}, {-1, -1, -1}, {1, 5, 96}, {1, 96, 8}, false, ExpectedEPNodeAssignment::None);

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CoreML EP] MLProgram: MatMul with a static K on one input and a dynamic K on the other fails to load (default dynamic dim of 1)

2 participants