Fix quantized Conv1d same padding with even kernels in XNNPACK#20734
Fix quantized Conv1d same padding with even kernels in XNNPACK#20734SakshamKapoor2911 wants to merge 9 commits into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20734
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 Cancelled JobAs of commit e0975ed with merge base a6aaeff ( CANCELLED JOB - The following job was cancelled. Please retry:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
|
There was a problem hiding this comment.
Pull request overview
This PR fixes an XNNPACK delegation/runtime correctness issue for statically-quantized nn.Conv1d(padding="same") with even kernel sizes by folding the explicit temporal constant_pad_nd into asymmetric Conv2d padding during Conv1d→Conv2d lowering, and ensuring the serialized graph’s metadata matches the folded asymmetric padding.
Changes:
- Extend XNNPACK partitioning and Conv1d unsqueeze lowering to absorb temporal-only zero
constant_pad_ndinto the quantized Conv1d partition and fold it into asymmetricxnnpack_input_padding. - Teach Conv2d serialization to use folded asymmetric padding metadata when present, and add a late pass to repair Conv1d folded-pad tensor metadata after retracing passes.
- Add a regression test covering quantized Conv1d even-kernel
padding="same".
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| backends/xnnpack/test/ops/test_conv1d.py | Adds a regression test for quantized Conv1d even-kernel padding="same" and factors out calibration sample generation. |
| backends/xnnpack/partition/config/gemm_configs.py | Extends convolution activation-dependency discovery to include temporal-only zero constant_pad_nd for quantized Conv1d so it can be partitioned/delegated. |
| backends/xnnpack/operators/op_conv2d.py | Uses folded asymmetric xnnpack_input_padding metadata (top/right/bottom/left) when present instead of symmetric ATen padding args. |
| backends/xnnpack/_passes/conv1d_unsqueeze_pass.py | Folds eligible temporal-only constant_pad_nd into asymmetric input padding during Conv1d→Conv2d lowering and adds a pass to restore correct tensor metadata for folded-pad Conv1d. |
| backends/xnnpack/_passes/init.py | Registers the new folded-pad metadata repair pass in the default XNNPACK pass pipeline. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
❌ 🤖 pytorchbot command failed: |
|
@pytorchbot label "release notes: xnnpack" |
|
|
43c6c72 to
529a792
Compare
529a792 to
619ae4c
Compare
|
The PR for 2D has landed: You can take the same approach here; have an explicit PAD op, which gets executed in XNNPACK. We can then later fold that XNNPACK pad in the delegated graph, this is a follow up to both of these PRs. As is, this code will also fail on: |
Refactor InsertPadQDQPass with guard checks (pad_value, pad_amounts, negative amounts) and correct idempotency (re-add is_quant check). Use is_dequant instead of matching specific dequant target. Add super().call() retrace to update graph_module metadata. Merge duplicated _get_act_deps in ConvolutionConfig — handle both 1D and 2D convs with their respective pad constraints. Also follow QDQ chain (dequant -> pad -> q -> dq -> conv) when InsertPadQDQPass has already inserted a quantize/dequantize pair after the pad. Restore XNNPACK pass ordering in __init__.py and Conv1dUnsqueezePass variable scoping to match origin/main (no functional change). Add regression test for quantized Conv1d even-kernel same-padding covering both symmetric and asymmetric pad cases. Co-authored-by: Claude
|
Addressed the remaining review points: Lint: Gemm config
|
|
@claude please review |
|
Claude finished @JakeStevens's task in 7m 9s —— View job Review — PR #20734: Fix quantized Conv1d same padding with even kernels
Overall this is a clean adoption of the explicit-PAD approach from #20553 and reads much better than the earlier pad-folding version. The transposed-conv gating and idempotency concerns from earlier rounds are addressed. A few things worth a look before merge: 1.
|
… cleanups - insert_pad_qdq.py: restore is_per_tensor guard dropped during rewrite; track modified flag accurately - gemm_configs.py: remove dead QDQ-chain branch; remove redundant len%2==0 and duplicate fallthrough - lint fixes applied
57a48c1 to
a4be095
Compare
|
Addressed all 3 items from Claude review: 1. Per-tensor guard restored ( 2. Removed unreachable QDQ-chain branch ( 3. Minor cleanups
|
|
@JakeStevens All 3 items from the Claude review above are addressed and pushed. Could you take another look? |
| ): | ||
| continue | ||
|
|
||
| # Only per-tensor static activations are handled: _insert_qdq_after |
There was a problem hiding this comment.
These comments were removed but what they were explaining was not fully followed.
In the original, we are allowing only _per_tensor.default, and then using that in the create_node. So, everything matched. This is no longer the case in the PR, see below
| pad_input = node.args[0] | ||
| if not ( | ||
| isinstance(pad_input, torch.fx.Node) | ||
| and is_dequant(pad_input) |
There was a problem hiding this comment.
is_dequant now matches:
_DQ_OPS = {
"dequantize_per_tensor.tensor",
"dequantize_per_tensor.default",
"dequantize_per_channel.default",
"dequantize_per_channel_group.default",
"dequantize_per_token.default",
"dequantize_affine.default",
}
but then below, we only use the _per_tensor.default when rebuilding the graph.
is_per_tensor already verifies the node is a quant/dequant with per_tensor in its target name. The broader is_dequant check was confusing and created a mismatch with the create_node targets below. Co-authored-by: Claude
|
Addressed the review feedback: removed the redundant - is_dequant(pad_input)
- and is_per_tensor(pad_input)
+ and is_per_tensor(pad_input)@JakeStevens — ready for re-review when you get a chance. |
|
I'm sorry if I was not clear; the issue is not the redundant quant check, the issue is that before we checked on the full signature, and then replaced with that exact signature. Now, we match on both; but insert only the .default version. So, you can have a _per_tensor.tensor quantized node and then we make the padding .default Let us stick with just static/.default for now as that is what is covered by tests etc, we can land and if youd like follow up to see if .tensor can be supported too. |
…fault only is_per_tensor() matches both dequantize_per_tensor.default and dequantize_per_tensor.tensor, but we always insert quantize_per_tensor.default. Using .tensor q_params to build a .default node would produce a malformed node. Restore the original exact-signature guard from main (pad_input.target == .default) with an added is_dequant fast-path gate.
|
@JakeStevens Feedback addressed -- tightened the dequant check in InsertPadQDQPass back to exact signature matching.
The .tensor variant can be supported in a follow-up if needed -- for now, only the covered/tested .default path is matched. Ready for re-review when you get a chance. |
Fixes #20558.
Related to #20553.
Summary
Quantized
nn.Conv1d(..., padding="same")with an even kernel exports withasymmetric padding (unequal left/right amounts), which cannot be folded into
the convolution's symmetric padding field. The original pad-folding approach
in this PR was replaced with the explicit-PAD approach from #20553:
constant_pad_nd nodes in quantized contexts so they serialize as quantized
static pads. Refactored with additional guard checks (pad_value, pad_amounts,
negative amounts) and correct idempotency.
(and their QDQ chain if InsertPadQDQPass already ran) into the convolution's
partition for both 1D and 2D convs. The merged method replaces separate 1D
and 2D implementations that existed on this branch.
symmetric and asymmetric pad cases, validated by numerical comparison.
changes.
With these changes, an even-kernel
padding="same"conv1d graph:becomes (after XNNPACK preprocessing and partitioning):
Test plan
cc @GregoryComer @digantdesai @cbilgin @JakeStevens @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani