vt::Conv1d and vt::ConvTranspose1d accumulate every output cell in f64 (src/vt/cpu/cpu_conv1d_general.cpp::Conv1dKernel at alignas(64) double acc[kConv1dPosTile], ::ConvTranspose1dKernel at std::vector<double> acc(full), and the CUDA provider's __dmul_rn/__dadd_rn in src/vt/cuda/cuda_conv1d_general.cu). include/vt/ops.h at vt::Conv1d clause (1) and .agents/specs/minimax-music3.md §13.2/§18.3 record that width as a deliberate divergence from torch, justified by the goldens. The justification does not hold, and one half of it is a citation to a directory that has no such golden in it.
1. vLLM does not own these ops, so the reference is torch. At the parity pin 555967922 there is no Conv1dLayer and no ConvTranspose*Layer; the only ConvTranspose string in the tree is a list of torch.nn classes in an NVTX annotation hook (vllm/utils/nvtx_pytorch_hooks.py:47-49). vLLM deliberately drops the vocoder it would otherwise own — vllm/model_executor/models/qwen3_omni_moe_thinker.py:1975 loads with skip_prefixes=["talker.", "code2wav."]. The one convolution vLLM does own states its own polarity in a comment: csrc/cpu/mamba_kernels.hpp — // Accumulate in float32 for precision. Ownership therefore falls to torch through the per-consumer secondary oracles.
2. torch accumulates an f32 convolution in f32, measured not read. torch 2.11.0+cu130, a 27-tap probe [+1e8, 0.1 x 25, -1e8] over a uniform-1.0 input, where every partial sum 1e8 + j*0.1 for j <= 25 is below half an ulp of 1e8 so an f32 accumulator lands on exactly 0.0 in any summation order and an f64 one lands on ~2.5:
| call |
dtype |
result |
F.conv1d |
f32 |
0.0 |
F.conv1d |
bf16 |
0.0 |
F.conv1d |
f64 |
2.4999998510 |
F.conv_transpose1d |
f32 |
0.0 |
F.conv_transpose1d |
f64 |
2.4999998510 |
3. The stated provenance of the f64 is factually wrong. §13.2 and §18.3 both say f64 "is what every committed golden for all four consumers was taken with". The generators run torch in f32: scripts/gen-bigvgan-goldens.py:48 builds f64 and then calls .float(); scripts/gen-ltx2-vae-goldens.py:223,234 casts every parameter and every input with .astype(np.float32); scripts/gen-minimax-music3-acoustic-goldens.py:81,134 does the same. The goldens are the output of an f32-accumulating reference, and the f64 arm merely passes a tolerance against f32-derived values.
4. The ops.h numeric contract cites a directory that contains no such golden. Clause (1) says "every committed golden under tests/parity/goldens/ for all four consumers was taken through it". That directory holds 101 entries and not one of them is a vocoder, BigVGAN, LTX-2.5 VAE, FVQ or general-conv1d golden. The goldens for all four consumers are .inc headers next to their tests — tests/vllm/models/bigvgan_goldens.inc, ltx2_vae_goldens.inc, minimax_music3_acoustic_goldens.inc.
5. The precedent is landed, inside one of the same models. #1008 / LTX25-DECODE-DTYPE narrowed LTX-2.5's conv video VAE from f64 to f32 on this exact argument (d1b0ea3a8, PR #1036), and its outcome table ends "all 13 audio arms | unchanged". LTX-2.5 therefore ships f32 video decode and f64 audio decode in one model today. #1208 flags the same polarity on its text tower.
The measured risk, which is why this is not a one-line change. In #1008, narrowing the width while keeping the naive per-cell serial order pushed one golden from 2.09e-6 to 5.00679e-6 against a 5e-6 tolerance — a genuine red. The remedy was upstream's own summation order: per-input-channel blocked partial sums, which is what torch's blocked-GEMM convolution does. That remedy is half-built here: #1334's tile hoists the (ic, k) sweep outside the position loop but does not block the reduction across ic. Tightest exposure in order: LTX-2.5's 13 audio arms at 5e-6 (the same constant #1008 nearly broke), then MiniMax-Music3 at rel 1e-5 / abs 1e-6, then BigVGAN at 2e-4.
Two self-referential bitwise gates narrow in lockstep, and their standing legitimately drops. SerialConv1d/SerialConvTranspose1d in tests/vt/test_ops_conv1d_general.cpp and SerialConvTranspose1d in tests/vllm/models/test_host_parallel.cpp are verbatim copies of the pre-op host loops (at 8fa405bb7 and d9441ef3). Once the width moves they no longer assert "identical to the pre-op host loop"; they assert "the order did not move". §18.9 already concedes this.
Owned by VT-CONV1D-F32-ACC, spec .agents/specs/vt-conv1d-f32-accumulator.md. Also corrects the two false records above in the same flow.
vt::Conv1dandvt::ConvTranspose1daccumulate every output cell in f64 (src/vt/cpu/cpu_conv1d_general.cpp::Conv1dKernelatalignas(64) double acc[kConv1dPosTile],::ConvTranspose1dKernelatstd::vector<double> acc(full), and the CUDA provider's__dmul_rn/__dadd_rninsrc/vt/cuda/cuda_conv1d_general.cu).include/vt/ops.hatvt::Conv1dclause (1) and.agents/specs/minimax-music3.md§13.2/§18.3 record that width as a deliberate divergence from torch, justified by the goldens. The justification does not hold, and one half of it is a citation to a directory that has no such golden in it.1. vLLM does not own these ops, so the reference is torch. At the parity pin
555967922there is noConv1dLayerand noConvTranspose*Layer; the onlyConvTransposestring in the tree is a list oftorch.nnclasses in an NVTX annotation hook (vllm/utils/nvtx_pytorch_hooks.py:47-49). vLLM deliberately drops the vocoder it would otherwise own —vllm/model_executor/models/qwen3_omni_moe_thinker.py:1975loads withskip_prefixes=["talker.", "code2wav."]. The one convolution vLLM does own states its own polarity in a comment:csrc/cpu/mamba_kernels.hpp—// Accumulate in float32 for precision. Ownership therefore falls to torch through the per-consumer secondary oracles.2. torch accumulates an f32 convolution in f32, measured not read. torch 2.11.0+cu130, a 27-tap probe
[+1e8, 0.1 x 25, -1e8]over a uniform-1.0 input, where every partial sum1e8 + j*0.1forj <= 25is below half an ulp of1e8so an f32 accumulator lands on exactly0.0in any summation order and an f64 one lands on ~2.5:F.conv1dF.conv1dF.conv1dF.conv_transpose1dF.conv_transpose1d3. The stated provenance of the f64 is factually wrong. §13.2 and §18.3 both say f64 "is what every committed golden for all four consumers was taken with". The generators run torch in f32:
scripts/gen-bigvgan-goldens.py:48builds f64 and then calls.float();scripts/gen-ltx2-vae-goldens.py:223,234casts every parameter and every input with.astype(np.float32);scripts/gen-minimax-music3-acoustic-goldens.py:81,134does the same. The goldens are the output of an f32-accumulating reference, and the f64 arm merely passes a tolerance against f32-derived values.4. The
ops.hnumeric contract cites a directory that contains no such golden. Clause (1) says "every committed golden undertests/parity/goldens/for all four consumers was taken through it". That directory holds 101 entries and not one of them is a vocoder, BigVGAN, LTX-2.5 VAE, FVQ or general-conv1d golden. The goldens for all four consumers are.incheaders next to their tests —tests/vllm/models/bigvgan_goldens.inc,ltx2_vae_goldens.inc,minimax_music3_acoustic_goldens.inc.5. The precedent is landed, inside one of the same models. #1008 /
LTX25-DECODE-DTYPEnarrowed LTX-2.5's conv video VAE from f64 to f32 on this exact argument (d1b0ea3a8, PR #1036), and its outcome table ends "all 13 audio arms | unchanged". LTX-2.5 therefore ships f32 video decode and f64 audio decode in one model today. #1208 flags the same polarity on its text tower.The measured risk, which is why this is not a one-line change. In #1008, narrowing the width while keeping the naive per-cell serial order pushed one golden from 2.09e-6 to 5.00679e-6 against a 5e-6 tolerance — a genuine red. The remedy was upstream's own summation order: per-input-channel blocked partial sums, which is what torch's blocked-GEMM convolution does. That remedy is half-built here: #1334's tile hoists the
(ic, k)sweep outside the position loop but does not block the reduction acrossic. Tightest exposure in order: LTX-2.5's 13 audio arms at5e-6(the same constant #1008 nearly broke), then MiniMax-Music3 at rel1e-5/ abs1e-6, then BigVGAN at2e-4.Two self-referential bitwise gates narrow in lockstep, and their standing legitimately drops.
SerialConv1d/SerialConvTranspose1dintests/vt/test_ops_conv1d_general.cppandSerialConvTranspose1dintests/vllm/models/test_host_parallel.cppare verbatim copies of the pre-op host loops (at8fa405bb7andd9441ef3). Once the width moves they no longer assert "identical to the pre-op host loop"; they assert "the order did not move". §18.9 already concedes this.Owned by
VT-CONV1D-F32-ACC, spec.agents/specs/vt-conv1d-f32-accumulator.md. Also corrects the two false records above in the same flow.