flashinfer.gemm.mm_nvfp4_lora¶
- flashinfer.gemm.mm_nvfp4_lora(a: Tensor, b: Tensor, a_sf: Tensor, b_sf: Tensor, alpha: Tensor, d: Tensor | None = None, lora_b: Tensor | None = None, lora_scale: Tensor | None = None, *, lora_adapter: PreparedNvFp4LoraAdapter | None = None, bias: Tensor | None = None, out: Tensor | None = None, out_dtype: dtype = torch.bfloat16, backend: Literal['cake', 'auto'] = 'cake', enable_pdl: bool | None = None) Tensor¶
NVFP4 base GEMM with a runtime BF16 LoRA correction.
out = alpha * (dequant(a) @ dequant(b)^T) + lora_scale * (d @ lora_b^T) [+ bias]The NVFP4 operands are consumed exactly as
mm_fp4()does (E2M1 codes packed two per byte, UE4M3 block scales of 16 along K in the 128x4 swizzled layout produced byfp4_quantize()); the correction is computed in fp32 inside the same kernel and the result is rounded once to bf16. The adapter arrives in one of two forms:Raw path (
lora_b+lora_scale): the BF16 factors are consumed as given –d(the LoRA-down outputx @ A^T) andlora_b(the LoRA-up weight) are never re-rounded or pre-scaled – andalpha/lora_scaleare independent fp32 device scalars (no folding of either into the weights). Swapping the adapter is swapping pointers (0-preparation switching).Prepared path (
lora_adapter=, fromprepare_nvfp4_lora_adapter()): the adapter was folded once at the switch,b_folded = bf16(lora_b * lora_scale / alpha); the kernel accumulates the base product first and appliesalphaonce in the fp32 epilogue – the numerics ofmm_nvfp4_svdquant()(lora_bre-rounded once to bf16; on the measured contract rows SQNR 53.8 dB vs 55.6 dB on the raw path, max abs error vs fp64 0.038-0.042 vs 0.031). Every prepared call runs the prepared-path program (the raw dispatch table is untouched) and keeps the snapshot checks ofPreparedNvFp4LoraAdapter. Passing both forms raisesValueError; neither form is the adapter-off call.
- Parameters:
a (torch.Tensor) – Quantized activations,
uint8 [M, K // 2](two E2M1 codes per byte), row-major.b (torch.Tensor) – Quantized base weights,
uint8 [N, K // 2], row-major (a @ b^T).a_sf (torch.Tensor) – UE4M3 block scales of
a/basuint8in the 128x4 swizzled layout (rows padded to 128); larger contiguous backing buffers are accepted.b_sf (torch.Tensor) – UE4M3 block scales of
a/basuint8in the 128x4 swizzled layout (rows padded to 128); larger contiguous backing buffers are accepted.alpha (torch.Tensor) – fp32 device tensor whose first element is the base scale
alpha_base / (a_global_scale * b_global_scale).d (torch.Tensor or None) – LoRA-down output,
bf16 [M, r]row-major, contiguous (16-byte aligned rows).lora_b (torch.Tensor or None) – LoRA-up weight,
bf16 [N, r]row-major, contiguous. A[r, N]factor must be transposed and made contiguous by the caller.lora_scale (torch.Tensor or None) – One-element fp32 device tensor
s_lora(the LoRAalpha / rand any other multiplier), independent ofalpha.0andNoneboth take the zero-adapter fast path (no LoRA work, bitwise identical outputs):lora_scale=Nonedisables the adapter even ifd/lora_bare passed; pass a device scalar to apply it.lora_adapter (PreparedNvFp4LoraAdapter, optional) – The prepared path: an adapter from
prepare_nvfp4_lora_adapter()(lora_bandlora_scalemust then beNone;dis still required). Itsalphamust be this call’salphatensor (re-prepare when alpha changes) and its source tensors must not have moved since the prepare (StaleAdapterErrorotherwise). A prepared adapter withlora_scale == 0produces the adapter-off output, bitwise.bias (torch.Tensor, optional) –
bf16 [N], added in fp32 before the single rounding.out (torch.Tensor, optional) – Caller-owned
bf16 [M, N]output; allocated whenNone.out_dtype (torch.dtype) – Only
torch.bfloat16is supported.backend ({"cake", "auto"}) – The generated Cake programs are the only implementation.
enable_pdl (bool, optional) – The programs are built with programmatic dependent launch;
NoneorTrue.
- Returns:
out(bf16 [M, N]).- Return type:
torch.Tensor
Notes
Architectures: SM100 (B200) and SM103 (B300) only; SM120 / SM121 are not supported (the kernel uses tcgen05 / TMEM).
Shape rules:
M >= 1,N >= 1,Ka multiple of 256; the LoRA rankr = d.shape[1]must be one of(32, 64, 128). Violations raiseValueErrornaming the violated requirement.Tensor parallelism (host contract): column-parallel (
Nsharded) ranks each hold theirNshard ofb/b_sf/lora_b/biasand the fulld, and each adds its own correction; row-parallel (Ksharded) ranks hold theirKshard ofa/b(codes and scales), the fulld(reduced by the caller or computed from the fullx) and the fulllora_b, and only the owner rank passeslora_scale/bias(the others passlora_scale = 0andbias = None) so the correction is counted exactly once before the all-reduce of the base partials. On the prepared path every rank prepares its own adapter from its shard (the non-owner ranks of a row-parallel layout withlora_scale = 0, which folds to the zero adapter).CUDA graphs, raw path: a captured call keeps the adapter pointers captured. Swapping
d/lora_b/lora_scalefor new tensors after capture does not affect replays; write the new adapter into the captured tensors (copy_) instead. Prepared path: a captured call keeps the prepared adapter captured; the adapter switch isprepare_nvfp4_lora_adapter(lora_b, lora_scale, alpha, out=adapter)captured right before the call (an in-place refold without a host read), after which acopy_into the capturedlora_b/lora_scaleis folded by every replay. Outside a graph, acopy_into a source tensor makes the adapter stale (StaleAdapterError) until it is re-prepared.