---
title: attn_qat_infer
---

<div class="python-reference">
<a id="fastvideo.attention.backends.attn_qat_infer-classes"></a>
<a id="fastvideo.attention.backends.attn_qat_infer-functions"></a>
<a id="fastvideo.attention.backends.attn_qat_infer"></a>

<h2 id="fastvideo.attention.backends.attn_qat_infer.AttentionBackend"><code>fastvideo.attention.backends.attn_qat_infer.AttentionBackend</code></h2>
<pre class="doc-signature"><code>AttentionBackend()</code></pre>
<p>Abstract class for attention backends.</p>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_name"><code>fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_name</code></h3>
<pre class="doc-signature"><code>get_name() -&gt; str</code></pre>

<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_impl_cls"><code>fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_impl_cls</code></h3>
<pre class="doc-signature"><code>get_impl_cls() -&gt; type&#91;AttentionImpl&#93;</code></pre>

<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_metadata_cls"><code>fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_metadata_cls</code></h3>
<pre class="doc-signature"><code>get_metadata_cls() -&gt; type&#91;AttentionMetadata&#93;</code></pre>

<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_builder_cls"><code>fastvideo.attention.backends.attn_qat_infer.AttentionBackend.get_builder_cls</code></h3>
<pre class="doc-signature"><code>get_builder_cls() -&gt; type&#91;AttentionMetadataBuilder&#93;</code></pre>

<h2 id="fastvideo.attention.backends.attn_qat_infer.AttentionImpl"><code>fastvideo.attention.backends.attn_qat_infer.AttentionImpl</code></h2>
<pre class="doc-signature"><code>AttentionImpl(num_heads: int, head_size: int, softmax_scale: float, causal: bool = False, num_kv_heads: int | None = None, prefix: str = &#x27;&#x27;, **extra_impl_args)</code></pre>


<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionImpl.preprocess_qkv"><code>fastvideo.attention.backends.attn_qat_infer.AttentionImpl.preprocess_qkv</code></h3>
<pre class="doc-signature"><code>preprocess_qkv(qkv: torch.Tensor, attn_metadata: T) -&gt; torch.Tensor</code></pre>
<p>Preprocess QKV tensor before performing attention operation.</p>
<p>Default implementation returns the tensor unchanged.
Subclasses can override this to implement custom preprocessing
like reshaping, tiling, scaling, or other transformations.</p>
<p>Called AFTER all_to_all for distributed attention</p>
<h4>Parameters</h4>
<dl>
<dt><code>qkv: torch.Tensor</code></dt><dd><p>The query-key-value tensor</p></dd>
<dt><code>attn_metadata: T</code></dt><dd><p>Metadata for the attention operation</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>torch.Tensor</code></dt><dd><p>Processed QKV tensor</p></dd>
</dl>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionImpl.postprocess_output"><code>fastvideo.attention.backends.attn_qat_infer.AttentionImpl.postprocess_output</code></h3>
<pre class="doc-signature"><code>postprocess_output(output: torch.Tensor, attn_metadata: T) -&gt; torch.Tensor</code></pre>
<p>Postprocess the output tensor after the attention operation.</p>
<p>Default implementation returns the tensor unchanged.
Subclasses can override this to implement custom postprocessing
like untiling, scaling, or other transformations.</p>
<p>Called BEFORE all_to_all for distributed attention</p>
<h4>Parameters</h4>
<dl>
<dt><code>output: torch.Tensor</code></dt><dd><p>The output tensor from the attention operation</p></dd>
<dt><code>attn_metadata: T</code></dt><dd><p>Metadata for the attention operation</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>torch.Tensor</code></dt><dd><p>Postprocessed output tensor</p></dd>
</dl>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionImpl.forward"><code>fastvideo.attention.backends.attn_qat_infer.AttentionImpl.forward</code></h3>
<pre class="doc-signature"><code>forward(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, attn_metadata: T) -&gt; torch.Tensor</code></pre>

<h2 id="fastvideo.attention.backends.attn_qat_infer.AttentionMetadata"><code>fastvideo.attention.backends.attn_qat_infer.AttentionMetadata</code></h2>
<pre class="doc-signature"><code>AttentionMetadata(current_timestep: int, *, VSA_sparsity: float = 0.0)</code></pre>
<p>Attention metadata for prefill and decode batched together.</p>

<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionMetadata.asdict_zerocopy"><code>fastvideo.attention.backends.attn_qat_infer.AttentionMetadata.asdict_zerocopy</code></h3>
<pre class="doc-signature"><code>asdict_zerocopy(skip_fields: set&#91;str&#93; | None = None) -&gt; dict&#91;str, Any&#93;</code></pre>
<p>Similar to dataclasses.asdict, but avoids deepcopying.</p>
<h2 id="fastvideo.attention.backends.attn_qat_infer.AttentionMetadataBuilder"><code>fastvideo.attention.backends.attn_qat_infer.AttentionMetadataBuilder</code></h2>
<pre class="doc-signature"><code>AttentionMetadataBuilder()</code></pre>
<p>Abstract class for attention metadata builders.</p>
<p>Create the builder, remember some configuration and parameters.</p>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionMetadataBuilder.prepare"><code>fastvideo.attention.backends.attn_qat_infer.AttentionMetadataBuilder.prepare</code></h3>
<pre class="doc-signature"><code>prepare() -&gt; None</code></pre>
<p>Prepare for one batch.</p>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttentionMetadataBuilder.build"><code>fastvideo.attention.backends.attn_qat_infer.AttentionMetadataBuilder.build</code></h3>
<pre class="doc-signature"><code>build(**kwargs: Any) -&gt; AttentionMetadata</code></pre>
<p>Build attention metadata with on-device tensors.</p>
<h2 id="fastvideo.attention.backends.attn_qat_infer.init_logger"><code>fastvideo.attention.backends.attn_qat_infer.init_logger</code></h2>
<pre class="doc-signature"><code>init_logger(name: str) -&gt; _FastvideoLogger</code></pre>
<p>The main purpose of this function is to ensure that loggers are
retrieved in such a way that we can be sure the root fastvideo logger has
already been configured.</p>
<h2 id="fastvideo.attention.backends.attn_qat_infer.validate_fa4_pv_mode"><code>fastvideo.attention.backends.attn_qat_infer.validate_fa4_pv_mode</code></h2>
<pre class="doc-signature"><code>validate_fa4_pv_mode(mode: str) -&gt; str</code></pre>

<details><summary>Source</summary><pre><code>def validate_fa4_pv_mode(mode: str) -&gt; str:
    # Fail fast at impl construction so a typo never survives to the first
    # forward on a Blackwell box.
    if mode not in _FA4_PV_MODES:
        raise ValueError(f&quot;fa4_pv_mode must be one of &#123;_FA4_PV_MODES&#125;, got &#123;mode!r&#125;&quot;)
    return mode</code></pre></details>
<h2 id="fastvideo.attention.backends.attn_qat_infer.attn_qat_infer_receipt"><code>fastvideo.attention.backends.attn_qat_infer.attn_qat_infer_receipt</code></h2>
<pre class="doc-signature"><code>attn_qat_infer_receipt() -&gt; str</code></pre>
<p>One-line receipt of the resolution decision (arch + kernel + quant
knobs), for the selection log and for tooling. qk_mode=nvfp4 (per-16 E4M3
SFs) is the repo's tuned default passed through verbatim; pv_mode is the
<em>last configured</em> fa4_pv_mode (process-wide, recorded at impl
construction -- not a per-layer value), and the dtype actually fed to the
kernel is logged once on the first FA4 forward -- see
flash_attn/cute/README.md in the kernel repo.</p>
<details><summary>Source</summary><pre><code>def attn_qat_infer_receipt() -&gt; str:
    &quot;&quot;&quot;One-line receipt of the resolution decision (arch + kernel + quant
    knobs), for the selection log and for tooling. qk_mode=nvfp4 (per-16 E4M3
    SFs) is the repo&#x27;s tuned default passed through verbatim; pv_mode is the
    *last configured* fa4_pv_mode (process-wide, recorded at impl
    construction -- not a per-layer value), and the dtype actually fed to the
    kernel is logged once on the first FA4 forward -- see
    flash_attn/cute/README.md in the kernel repo.&quot;&quot;&quot;
    cap = _active_capability()
    arch = f&quot;sm_&#123;cap&#91;0&#93;&#125;&#123;cap&#91;1&#93;&#125;&quot; if cap is not None else &quot;no-cuda&quot;
    kernel = _resolved_kernel()
    if kernel == &quot;cutlass_sm12x&quot;:
        return f&quot;arch=&#123;arch&#125; kernel=fastvideo-kernel-cutlass scheme=sage3-fp4-sm120&quot;
    if kernel == &quot;fa4_fp4&quot;:
        return (f&quot;arch=&#123;arch&#125; kernel=flash-attention-fp4 qk_mode=nvfp4(per-16-e4m3-sf) &quot;
                f&quot;pv_mode=&#123;_current_fa4_pv_mode()&#125; train_sim_mismatch=measured&quot;)
    supported = &quot;sm_120a/sm_121a via fastvideo-kernel build.sh; sm_100a/sm_103a via flash-attention-fp4&quot;
    if cap is not None and cap in _FA4_FP4_CAPABILITIES:
        return f&quot;arch=&#123;arch&#125; kernel=none (flash_attn.cute not importable -- &#123;_FA4_INSTALL_HINT&#125;)&quot;
    return f&quot;arch=&#123;arch&#125; kernel=none (supported: &#123;supported&#125;)&quot;</code></pre></details>
<h2 id="fastvideo.attention.backends.attn_qat_infer.is_attn_qat_infer_available"><code>fastvideo.attention.backends.attn_qat_infer.is_attn_qat_infer_available</code></h2>
<pre class="doc-signature"><code>is_attn_qat_infer_available() -&gt; bool</code></pre>
<p>True only when the active device has a built ATTN_QAT_INFER kernel.</p>
<p>The import check alone is not sufficient: CUDA 13 wheel builds can
carry the sm_12x extension on any host (e.g. H100, GB200), where the
import succeeds, backend selection picks this backend, and the first
kernel call then fails with an unsupported-capability error instead of
ever reaching the documented FlashAttention fallback in
fastvideo.platforms.cuda. Gating on the active device's capability
keeps that fallback working on every unsupported GPU, while
sm_100a/sm_103a now resolve to the FP4 FA4 kernel (#1221).</p>
<details><summary>Source</summary><pre><code>def is_attn_qat_infer_available() -&gt; bool:
    &quot;&quot;&quot;True only when the active device has a built ATTN_QAT_INFER kernel.

    The import check alone is not sufficient: CUDA 13 wheel builds can
    carry the sm_12x extension on any host (e.g. H100, GB200), where the
    import succeeds, backend selection picks this backend, and the first
    kernel call then fails with an unsupported-capability error instead of
    ever reaching the documented FlashAttention fallback in
    fastvideo.platforms.cuda. Gating on the active device&#x27;s capability
    keeps that fallback working on every unsupported GPU, while
    sm_100a/sm_103a now resolve to the FP4 FA4 kernel (#1221).
    &quot;&quot;&quot;
    return _resolved_kernel() is not None</code></pre></details>
<h2 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend</code></h2>
<pre class="doc-signature"><code>AttnQatInferBackend()</code></pre>

<details><summary>Source</summary><pre><code>class AttnQatInferBackend(AttentionBackend):

    accept_output_buffer: bool = True

    @staticmethod
    def get_supported_head_sizes() -&gt; list&#91;int&#93;:
        return &#91;64, 128&#93;

    @staticmethod
    def get_name() -&gt; str:
        return &quot;ATTN_QAT_INFER&quot;

    @staticmethod
    def get_impl_cls() -&gt; type&#91;&quot;AttnQatInferImpl&quot;&#93;:
        return AttnQatInferImpl

    @staticmethod
    def get_metadata_cls() -&gt; type&#91;&quot;AttentionMetadata&quot;&#93;:
        raise NotImplementedError

    @staticmethod
    def get_builder_cls() -&gt; type&#91;&quot;AttentionMetadataBuilder&#91;AttentionMetadata&#93;&quot;&#93;:
        raise NotImplementedError</code></pre></details>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_supported_head_sizes"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_supported_head_sizes</code></h3>
<pre class="doc-signature"><code>get_supported_head_sizes() -&gt; list&#91;int&#93;</code></pre>

<details><summary>Source</summary><pre><code>@staticmethod
def get_supported_head_sizes() -&gt; list&#91;int&#93;:
    return &#91;64, 128&#93;</code></pre></details>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_name"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_name</code></h3>
<pre class="doc-signature"><code>get_name() -&gt; str</code></pre>

<details><summary>Source</summary><pre><code>@staticmethod
def get_name() -&gt; str:
    return &quot;ATTN_QAT_INFER&quot;</code></pre></details>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_impl_cls"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_impl_cls</code></h3>
<pre class="doc-signature"><code>get_impl_cls() -&gt; type&#91;AttnQatInferImpl&#93;</code></pre>

<details><summary>Source</summary><pre><code>@staticmethod
def get_impl_cls() -&gt; type&#91;&quot;AttnQatInferImpl&quot;&#93;:
    return AttnQatInferImpl</code></pre></details>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_metadata_cls"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_metadata_cls</code></h3>
<pre class="doc-signature"><code>get_metadata_cls() -&gt; type&#91;AttentionMetadata&#93;</code></pre>

<details><summary>Source</summary><pre><code>@staticmethod
def get_metadata_cls() -&gt; type&#91;&quot;AttentionMetadata&quot;&#93;:
    raise NotImplementedError</code></pre></details>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_builder_cls"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend.get_builder_cls</code></h3>
<pre class="doc-signature"><code>get_builder_cls() -&gt; type&#91;AttentionMetadataBuilder&#91;AttentionMetadata&#93;&#93;</code></pre>

<details><summary>Source</summary><pre><code>@staticmethod
def get_builder_cls() -&gt; type&#91;&quot;AttentionMetadataBuilder&#91;AttentionMetadata&#93;&quot;&#93;:
    raise NotImplementedError</code></pre></details>
<h2 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferImpl"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferImpl</code></h2>
<pre class="doc-signature"><code>AttnQatInferImpl(num_heads: int, head_size: int, causal: bool, softmax_scale: float, num_kv_heads: int | None = None, prefix: str = &#x27;&#x27;, **extra_impl_args)</code></pre>


<details><summary>Source</summary><pre><code>class AttnQatInferImpl(AttentionImpl&#91;AttentionMetadata&#93;):

    def __init__(
        self,
        num_heads: int,
        head_size: int,
        causal: bool,
        softmax_scale: float,
        num_kv_heads: int | None = None,
        prefix: str = &quot;&quot;,
        **extra_impl_args,
    ) -&gt; None:
        self.causal = causal
        self.softmax_scale = softmax_scale
        dropout_p = extra_impl_args.get(&quot;dropout_p&quot;, 0.0)
        if dropout_p &gt; 0:
            raise NotImplementedError(f&quot;attn_qat_infer does not support dropout (got dropout_p=&#123;dropout_p&#125;). &quot;
                                      &quot;The QAT inference kernel applies no stochastic dropout.&quot;)
        self.fa4_pv_mode = _resolve_fa4_pv_mode(extra_impl_args)
        # Kernel resolution is per-forward, not per-construction: callers
        # (the validation swap, backend selection) gate on
        # is_attn_qat_infer_available() first, and constructing an impl on a
        # host without the kernel must stay legal (pre-existing contract the
        # validation-swap test pins).
        _log_receipt_once()

    def forward(
        self,
        query: torch.Tensor,
        key: torch.Tensor,
        value: torch.Tensor,
        attn_metadata: AttentionMetadata,
    ) -&gt; torch.Tensor:
        # Dispatch on the single per-arch resolution: importability of the
        # bundled sm_12x extension is NOT sufficient (CUDA 13 wheels carry it
        # on unsupported hosts, where calling it is the wrong binary).
        kernel = _resolved_kernel()
        if kernel == &quot;fa4_fp4&quot;:
            return self._forward_fa4_fp4(query, key, value)
        if kernel is None:
            raise ImportError(f&quot;attn_qat_infer is not available (&#123;attn_qat_infer_receipt()&#125;). &quot;
                              &quot;Please ensure an ATTN_QAT_INFER kernel is installed for this device.&quot;)

        attn_qat_infer = _get_attn_qat_infer()
        assert attn_qat_infer is not None  # kernel == &quot;cutlass_sm12x&quot; implies the import succeeded

        query = query.transpose(1, 2).contiguous()
        key = key.transpose(1, 2).contiguous()
        value = value.transpose(1, 2).contiguous()

        output = attn_qat_infer(
            query,
            key,
            value,
            attn_mask=None,
            is_causal=self.causal,
            sm_scale=self.softmax_scale,
        )
        return output.transpose(1, 2).contiguous()

    def _forward_fa4_fp4(
        self,
        query: torch.Tensor,
        key: torch.Tensor,
        value: torch.Tensor,
    ) -&gt; torch.Tensor:
        &quot;&quot;&quot;sm_100a/sm_103a path: FP4 FA4 (NVFP4 per-16 block-scaled Q/K; V in
        BF16 by default or fp8 e4m3 per the fa4_pv_mode knob) -- mirrors
        FlashAttentionImpl._forward_nvfp4 (#1221). Inputs/outputs are
        (batch, seqlen, nheads, headdim); no transpose.&quot;&quot;&quot;
        _nvfp4_quantize_for_fa4, flash_attn_fp4_func = _resolve_fa4_route_ops()

        orig_seqlen_q = query.shape&#91;1&#93;
        orig_seqlen_k = key.shape&#91;1&#93;

        q_fp4, q_sf = _nvfp4_quantize_for_fa4(query)
        k_fp4, k_sf = _nvfp4_quantize_for_fa4(key)

        # fp8 PV: unscaled e4m3 cast (no mSFV/v_descale); output stays BF16.
        if self.fa4_pv_mode == &quot;fp8&quot;:
            value = value.to(torch.float8_e4m3fn)
        # The once-log runs on the compiled path too: dynamo cannot trace
        # `logging.Logger` methods, so the helper is registered as a
        # reorderable logging function (see _log_pv_dtype_once).
        _log_pv_dtype_once(value.dtype)

        # FP4/SF buffers are padded to a 128 multiple; FA4 masks to the
        # original lengths so padding never biases the softmax.
        q_fp4 = q_fp4&#91;:, :orig_seqlen_q&#93;
        k_fp4 = k_fp4&#91;:, :orig_seqlen_k&#93;

        output = flash_attn_fp4_func(
            q_fp4,
            k_fp4,
            value,
            q_sf,
            k_sf,
            softmax_scale=self.softmax_scale,
            causal=self.causal,
        )
        if isinstance(output, tuple):
            output = output&#91;0&#93;
        return output</code></pre></details>
<h3 id="fastvideo.attention.backends.attn_qat_infer.AttnQatInferImpl.forward"><code>fastvideo.attention.backends.attn_qat_infer.AttnQatInferImpl.forward</code></h3>
<pre class="doc-signature"><code>forward(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, attn_metadata: AttentionMetadata) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(
    self,
    query: torch.Tensor,
    key: torch.Tensor,
    value: torch.Tensor,
    attn_metadata: AttentionMetadata,
) -&gt; torch.Tensor:
    # Dispatch on the single per-arch resolution: importability of the
    # bundled sm_12x extension is NOT sufficient (CUDA 13 wheels carry it
    # on unsupported hosts, where calling it is the wrong binary).
    kernel = _resolved_kernel()
    if kernel == &quot;fa4_fp4&quot;:
        return self._forward_fa4_fp4(query, key, value)
    if kernel is None:
        raise ImportError(f&quot;attn_qat_infer is not available (&#123;attn_qat_infer_receipt()&#125;). &quot;
                          &quot;Please ensure an ATTN_QAT_INFER kernel is installed for this device.&quot;)

    attn_qat_infer = _get_attn_qat_infer()
    assert attn_qat_infer is not None  # kernel == &quot;cutlass_sm12x&quot; implies the import succeeded

    query = query.transpose(1, 2).contiguous()
    key = key.transpose(1, 2).contiguous()
    value = value.transpose(1, 2).contiguous()

    output = attn_qat_infer(
        query,
        key,
        value,
        attn_mask=None,
        is_causal=self.causal,
        sm_scale=self.softmax_scale,
    )
    return output.transpose(1, 2).contiguous()</code></pre></details>
</div>
