Uh oh!
There was an error while loading. Please reload this page.
Core ML: stop the gather opt-out from failing tied embeddings - #21969
Conversation
The linear quantizer config opts gathers out so embedding tables are not compressed by it. When a model ties its embedding table to its output projection, that one constant reaches the compressor configured two ways at once — quantized as a linear weight, exempt as a gather table — and coremltools refuses the conflict rather than resolving it. The opt-out then does not skip the table, it fails the whole lowering, which is how a common LLM shape becomes unlowerable with coreml_quantize set. Gathers that share their constant with an op of another type are now configured the same way as the op they share it with; every other gather keeps the opt-out. A model whose program is unavailable is left exactly as before. Fixespytorch#21856
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21969
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New Failure, 1 Cancelled Job, 2 Unclassified FailuresAs of commit 02f2e1e with merge base 7c3cdbb ( NEW FAILURE - The following job has failed:
UNCLASSIFIED FAILURES - DrCI could not classify the following jobs because the workflow did not run on the merge base. The failures may be pre-existing on trunk or introduced by this PR:
CANCELLED JOB - The following job was cancelled. Please retry:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a |
metascroy
commented
Aug 25, 2026
@claude review this code |
Claude finished @metascroy's task in 1m 53s —— View job Code review
The change is small, well-scoped, and the reasoning in the comments is excellent — it explains why coremltools rejects the tied constant rather than just what the code does. The graceful degradation (empty list when CorrectnessThe detection logic is sound. Behavioral trade-off worth calling out (not a bug). The resolution quantizes the tied table (names the gather with Minor points
Test
Overall this is a clean, minimal fix for a real and common failure. The two things I'd most want addressed are surfacing the "tied tables now get quantized" behavior change and tightening the test to match its name; everything else is optional polish. |
| return mlmodel | ||
| @staticmethod | ||
| def gathers_sharing_a_weight(mlmodel: ct.models.MLModel) -> List[str]: |
There was a problem hiding this comment.
Does this need to be a public API on CoreMLBackend?
Or just a private helper in preproces?
There was a problem hiding this comment.
You're right — it has exactly one caller, _convert_to_mlmodel in the same file, so nothing needs it public. Renamed to _gathers_sharing_a_weight, matching the class's underscore convention.
There was a problem hiding this comment.
Can you make it a private free function in the file, not a static function on the class?
There was a problem hiding this comment.
Done in 02f2e1e — it's now a module-level private function. The scan never read anything from the class, so the body is unchanged; _convert_to_mlmodel calls it directly.
It has exactly one caller, _convert_to_mlmodel in the same file, so it follows the class's underscore convention instead of reading as public API on CoreMLBackend.
Review asked for a private free function in the file rather than a static method on CoreMLBackend. The scan reads nothing from the class, so it moves to module level unchanged.
john-rocky
commented
Aug 28, 2026
On the attempt-2 CI failures — none of them reaches this PR's code. |
metascroy
commented
Aug 28, 2026
Looks good, thanks for the contribution @john-rocky! |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#21856.
What happens today
op_linear_quantizer_configis applied withop_type_configs={"gather": None}, so thatembedding tables are not compressed by the linear quantizer. When a model ties its embedding
table to its output projection — one weight, two consumers — that constant reaches the
compressor configured two ways at once: quantized as a
linearweight, exempt as agathertable. coremltools refuses the conflict rather than resolving it:
So the opt-out does not skip the table. It fails the whole lowering, and a very common LLM
shape cannot be lowered at all with
coreml_quantizeset.The change
Gathers whose constant is also consumed by an op of another type are named in
op_name_configswith the same config as the op they share it with, so the constant is seenone way. Every other gather keeps the opt-out, and a model whose MIL program is not available
returns no names, which leaves the behaviour exactly as it was.
Checking it
The new test in
test_coreml_partitioner.pylowers a two-layer model twice, tied and untied.Against
mainthe tied case raises the error above; with this change both lower. The untiedcase passes either way, which is the point — the opt-out still applies where it always did.
Measured on macOS arm64, coremltools 9.0.