Repository navigation
Tensor-carried dtype propagation, with quantized signals - #6220
Draft
nathanielsimard wants to merge 11 commits into
Draft
nathanielsimard wants to merge 11 commits into
nathanielsimard wants to merge 11 commits into
Conversation
nathanielsimard
force-pushed
the
feat/quant-propagation
branch
from
October 25, 2026 11:48
e667800 to
2250b23
Compare
… propagation A tensor carries whether its dtype decides the dtype of the operations it enters (DTypePropagation, beside the dtype in the bridge, never seen by a backend). One rule, TensorType::combine, resolves the output of every combining operation: the dtype of the propagating inputs, the widest on a tie, f32 for two different dtypes as wide. Writes into a tensor keep the target's type. Module parameters inhibit, so a weight never lends its format; inputs, created tensors and casts propagate, so an activation quantized once keeps every output it reaches quantized. QuantPropagation leaves QuantConfig and the device settings, and backends return float from quantized operations: the bridge requantizes when the resolved type is quantized. DTypeResolution::Strict turns a widening tie into a panic for test suites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hrough Autodiff's quantized primitive is now AutodiffQTensor: the packed values with the graph node gradients reach them through. Quantize and dequantize pass the gradient through unchanged (a straight-through estimator, cast back to the input's precision), layout operations undo themselves on the gradient, and every other quantized operation differentiates through its float composition. Dispatch carries a tracked quantized tensor as BackendTensor::AutodiffQuantized, and the routing macro sends an operation with a quantized input in an enabled context through the autodiff backend, lifting an untracked one to a leaf. Module parameters hold their float values inhibiting, so a weight never decides the dtype of the activations it meets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ResolvedOutput, the bridge's resolver, is public: a backend extension's kernel resolves its output type from its inputs' with ResolvedOutput::of (or written_into for an operation writing into a tensor), reads its float operands in the precision it computes in and writes its output back to the resolved type, so a custom operation keeps a quantized signal quantized as a built-in one does. Tensor::tensor_type exposes the dtype with its propagation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A block-quantized reshape whose blocks or packing cannot follow as metadata (a transposed view laid back in rows, a block split across rows) used to be unimplemented!: it now decodes, reshapes and quantizes again, the block along the innermost axis as an operation's output is. innermost_scheme is public for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single-level block scheme along the innermost axis (the MX formats, q8/q4 blocks) computes its symmetric block scales in one launch, a unit per block, instead of min-max calibration's chain of reshapes, reductions and arithmetic (about eight launches); the existing quantize kernel then packs. Fusion runs quantize_dynamic as one backend operation, so the backend's calibration is the one that runs. Every other scheme keeps the composed calibration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dispatch layer took QTensorOps' default, so every quantize_dynamic was decomposed into the composed calibration before fusion or a backend saw it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntiguously The block scale and the packed values come from one unit per block, in place of a scales launch then a quantize launch. The output is allocated contiguously, so merging an activation's heads back into rows stays a change of metadata instead of decoding and quantizing it again. Byte-identical to quantizing under the min-max calibration's scales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An MXFP8 or MXFP4 tensor read by an element-wise expression is decoded in the fused kernel, as an integer or float-scaled one already was, rather than by a dequantize launch of its own. A scale argument takes its type from the scheme, not from its handle, which carries only a byte scale's width: read as a u8, a ue8m0 code converts as the integer it is. ue4m3 scales stay declined: read as e4m3, the fused decode reconstructs a different value than the unfused one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slice that leaves the packed axis whole and cuts no axis a block spans narrows the values' region and the scales' region of the allocation, as a float slice narrows its one, where both still start on a boundary the device binds at. Splitting a quantized projection's heads no longer decodes the whole of it and quantizes each part again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nathanielsimard
force-pushed
the
feat/quant-propagation
branch
from
October 25, 2026 12:42
2250b23 to
74730d9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist
cargo run-checkshas been executed.changelog policy in CONTRIBUTING.md.
Related Issues/PRs
Used by Metabolic's signal quantization (activations stored as MXFP8 between kernels, NVFP4 weights).
Changes
A tensor's dtype now propagates to the outputs it reaches, so a quantized activation stays quantized through a model without each op being written for it.
DType, each tensor carries a flag:PropagateorInhibit. Every float op resolves its output type from its inputs' throughTensorType::combine. Only parameters inhibit, so a quantized weight doesn't turn its product quantized. Operands are decoded to f32 for compute and the output is quantized back to the resolved format.DTypeResolution::Strictpanics when two propagating inputs disagree.ResolvedOutputgives a backend extension's kernel the same resolution as a built-in op (of,written_into,read,write).quantize_dynamicis forwarded through dispatch.ue8m0block scales in the fused kernel (MXFP8). The fix is that the scale argument is typed from the scheme instead of the handle'su8storage; otherwise it multiplied by the raw code.ue4m3scales still decode wrong when fused, so they stay unfused.The
q_*methods are kept.Testing
Branched from
mainat dd31726.mainat dd31726:collective::emulated::should_gather_an_empty_dimension(an overflow atburn-cubecl/src/tensor/base.rs:404).*_with_indices, topk and maxpool3d. The reduce-with-indices autotune plan can't execute at f16.mainalso failsctcthere.nd_block_split_reshapequantizes again instead of panicking.cargo run-checks, CUDA, wgpu.AI Usage
Claude Code (Claude Opus 5.5) wrote the code, the tests and this description, and opened the PR, mostly on its own following a design agreed with the contributor. The contributor hasn't reviewed the final diff yet.
🤖 Generated with Claude Code