---
title: minimax_h3_nvfp4_decoder
---

<div class="python-reference">

<a id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder"></a>
<p>NVFP4 linears for the MiniMax-H3 ViT video decoder.</p>
<p>Training and inference share one numeric path, so a decoder distilled with
quantization-aware training runs at deployment exactly as it was trained:</p>
<ul>
<li>activations: per-16 E4M3 block scales under a per-tensor global scale that is
  <code>dynamic</code> (abs-max of every call), <code>unit</code> (1.0, the H3 DiT scheme), or
  <code>static</code> (a per-layer abs-max calibrated once and stored with the weights);</li>
<li>weights: the same scheme, quantized once at inference and cached;</li>
<li>GEMM: <code>flashinfer.mm_fp4</code> (<code>mm_fp4_backend</code>), bias added in the activation dtype.</li>
</ul>
<p>Training runs through <code>_NVFP4DecoderSTE</code>, whose forward <em>is</em> the inference
function and whose backward is full precision (straight-through). An optional block Hadamard rotation (the regular
Hadamard used by the INT8 ConvRot overlay) is folded into the weight along the
input dimension and applied to the activation at runtime; it is exact in full
precision and spreads activation outliers before FP4 rounding.</p>
<p>Only the transformer-block linears are replaced. <code>proj_in</code> (K = 24, not
FP4-blockable), <code>proj_out</code>, norms, register tokens and attention stay as they
are.</p>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.rotate_activation"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.rotate_activation</code></h2>
<pre class="doc-signature"><code>rotate_activation(x: torch.Tensor, group_size: int) -&gt; torch.Tensor</code></pre>

<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.mm_fp4_backend"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.mm_fp4_backend</code></h2>
<pre class="doc-signature"><code>mm_fp4_backend(device: torch.device) -&gt; str</code></pre>
<p>FlashInfer <code>mm_fp4</code> backend for one decoder GEMM; the backends return identical outputs.</p>
<p>DGX Spark (GB10, sm_121) uses cuDNN: up to 1.4x faster than CUTLASS on the tile-batched
FFN input projection and on par elsewhere. Other GPUs use CUTLASS.</p>
<details><summary>Source</summary><pre><code>def mm_fp4_backend(device: torch.device) -&gt; str:
    &quot;&quot;&quot;FlashInfer ``mm_fp4`` backend for one decoder GEMM; the backends return identical outputs.

    DGX Spark (GB10, sm_121) uses cuDNN: up to 1.4x faster than CUTLASS on the tile-batched
    FFN input projection and on par elsewhere. Other GPUs use CUTLASS.
    &quot;&quot;&quot;
    from fastvideo.layers.quantization.nvfp4_config import _is_dgx_spark

    if device.type != &quot;cuda&quot;:
        return &quot;cutlass&quot;
    index = device.index if device.index is not None else torch.cuda.current_device()
    return &quot;cudnn&quot; if _is_dgx_spark(index) else &quot;cutlass&quot;</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.nvfp4_linear_inference"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.nvfp4_linear_inference</code></h2>
<pre class="doc-signature"><code>nvfp4_linear_inference(x: torch.Tensor, packed: torch.Tensor, inv_scale: torch.Tensor, global_sf_w: torch.Tensor, bias: torch.Tensor | None, out_features: int, global_sf_x: torch.Tensor | None) -&gt; torch.Tensor</code></pre>
<p>FP4 GEMM. <code>global_sf_x=None</code> computes the activation scale dynamically.</p>
<details><summary>Source</summary><pre><code>def nvfp4_linear_inference(x: torch.Tensor, packed: torch.Tensor, inv_scale: torch.Tensor, global_sf_w: torch.Tensor,
                           bias: torch.Tensor | None, out_features: int,
                           global_sf_x: torch.Tensor | None) -&gt; torch.Tensor:
    &quot;&quot;&quot;FP4 GEMM. ``global_sf_x=None`` computes the activation scale dynamically.&quot;&quot;&quot;
    from fastvideo.layers.fp4linear import _global_sf, _require_flashinfer
    from fastvideo.layers.quantization.nvfp4_config import nvfp4_quantize_fenced

    flashinfer_mod = _require_flashinfer()
    orig_shape = x.shape
    x2d = x.reshape(-1, orig_shape&#91;-1&#93;).contiguous()
    if global_sf_x is None:
        global_sf_x = _global_sf(x2d)
    x_fp4, x_inv_scale = nvfp4_quantize_fenced(x2d, global_sf_x, flashinfer_mod.SfLayout.layout_128x4.value)
    out = torch.empty((x2d.shape&#91;0&#93;, out_features), device=x.device, dtype=x.dtype)
    flashinfer_mod.mm_fp4(
        x_fp4,
        packed.T,
        x_inv_scale,
        inv_scale.T,
        1.0 / (global_sf_x * global_sf_w),
        x.dtype,
        out,
        block_size=NVFP4_BLOCK_SIZE,
        use_8x4_sf_layout=False,
        backend=mm_fp4_backend(x.device),
    )
    if bias is not None:
        out.add_(bias.to(x.dtype))
    return out.reshape(*orig_shape&#91;:-1&#93;, out_features)</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4FusedState"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4FusedState</code></h2>
<pre class="doc-signature"><code>NVFP4FusedState()</code></pre>
<p>Everything the fused inference kernels need from one <code>NVFP4DecoderLinear</code>.</p>
<p>Each tensor is computed with the same expression as <code>nvfp4_linear_inference</code>, so a GEMM
fed from here matches the eager linear bit for bit.</p>
<details><summary>Source</summary><pre><code>class NVFP4FusedState(NamedTuple):
    &quot;&quot;&quot;Everything the fused inference kernels need from one ``NVFP4DecoderLinear``.

    Each tensor is computed with the same expression as ``nvfp4_linear_inference``, so a GEMM
    fed from here matches the eager linear bit for bit.
    &quot;&quot;&quot;
    packed: torch.Tensor
    inv_scale: torch.Tensor
    global_sf_x: torch.Tensor
    alpha: torch.Tensor
    bias: torch.Tensor
    out_features: int</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear</code></h2>
<pre class="doc-signature"><code>NVFP4DecoderLinear(weight: torch.Tensor, bias: torch.Tensor | None, rotation_group: int | None, compute_dtype: torch.dtype, act_scale: str = &#x27;dynamic&#x27;)</code></pre>
<p>Drop-in for an <code>nn.Linear</code> in the H3 decoder blocks.</p>
<p><code>weight</code> is the full-precision master (already rotated when
<code>rotation_group</code> is set). With gradients enabled the forward is the STE;
otherwise the cached packed weight is used, so call <code>invalidate()</code> after
any weight update (the trainer does after each optimizer step).</p>
<p><code>act_scale="static"</code> uses the persistent <code>input_amax</code> buffer. Set
<code>calibrating = True</code> to run dynamically while folding each call's
abs-max into it.</p>
<p><code>freeze()</code> turns the layer inference-only: the packed weight becomes a
buffer and the master <code>weight</code> is dropped (<code>weight is None</code>).</p>

<details><summary>Source</summary><pre><code>class NVFP4DecoderLinear(nn.Module):
    &quot;&quot;&quot;Drop-in for an ``nn.Linear`` in the H3 decoder blocks.

    ``weight`` is the full-precision master (already rotated when
    ``rotation_group`` is set). With gradients enabled the forward is the STE;
    otherwise the cached packed weight is used, so call ``invalidate()`` after
    any weight update (the trainer does after each optimizer step).

    ``act_scale=&quot;static&quot;`` uses the persistent ``input_amax`` buffer. Set
    ``calibrating = True`` to run dynamically while folding each call&#x27;s
    abs-max into it.

    ``freeze()`` turns the layer inference-only: the packed weight becomes a
    buffer and the master ``weight`` is dropped (``weight is None``).
    &quot;&quot;&quot;

    def __init__(self, weight: torch.Tensor, bias: torch.Tensor | None, rotation_group: int | None,
                 compute_dtype: torch.dtype, act_scale: str = &quot;dynamic&quot;) -&gt; None:
        super().__init__()
        out_features, in_features = weight.shape
        if in_features % NVFP4_BLOCK_SIZE:
            raise ValueError(f&quot;NVFP4 needs in_features divisible by &#123;NVFP4_BLOCK_SIZE&#125;, got &#123;in_features&#125;&quot;)
        if rotation_group is not None and in_features % rotation_group:
            raise ValueError(f&quot;rotation group &#123;rotation_group&#125; does not divide in_features &#123;in_features&#125;&quot;)
        if act_scale not in ACT_SCALES:
            raise ValueError(f&quot;act_scale must be one of &#123;ACT_SCALES&#125;, got &#123;act_scale!r&#125;&quot;)
        self.in_features = in_features
        self.out_features = out_features
        self.rotation_group = rotation_group
        self.compute_dtype = compute_dtype
        self.act_scale = act_scale
        self.calibrating = False
        # Run the master weight densely (bf16) instead of NVFP4; for sensitivity analysis and mixed precision.
        self.dense_bypass = False
        self.weight = nn.Parameter(weight.detach().clone())
        self.bias = nn.Parameter(bias.detach().clone()) if bias is not None else None
        self.register_buffer(&quot;input_amax&quot;, torch.zeros((), dtype=torch.float32, device=weight.device))
        self._packed: tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93; | None = None
        self._fused: tuple&#91;tuple&#91;int, int&#93;, NVFP4FusedState&#93; | None = None

    @classmethod
    def from_linear(cls, linear: nn.Linear, *, rotation_group: int | None, compute_dtype: torch.dtype,
                    act_scale: str = &quot;dynamic&quot;) -&gt; NVFP4DecoderLinear:
        weight = linear.weight.detach().float()
        if rotation_group is not None:
            # y = x W^T = (x H)(W H)^T for orthonormal symmetric H: rotate W along its input dim.
            weight = rotate_activation(weight, rotation_group)
        bias = linear.bias.detach().float() if linear.bias is not None else None
        return cls(weight, bias, rotation_group, compute_dtype, act_scale)

    @classmethod
    def from_packed(cls, packed: torch.Tensor, inv_scale: torch.Tensor, global_sf_w: torch.Tensor,
                    bias: torch.Tensor | None, input_amax: torch.Tensor, *, in_features: int,
                    rotation_group: int | None, compute_dtype: torch.dtype, act_scale: str) -&gt; NVFP4DecoderLinear:
        &quot;&quot;&quot;A frozen layer built from already packed tensors (``freeze()``&#x27;s buffers), with no master weight.

        ``packed`` holds two FP4 codes per byte, so its shape is ``&#91;out_features, in_features // 2&#93;``.
        Nothing full-precision is allocated: the constructor&#x27;s placeholder weight lives on ``meta``.
        &quot;&quot;&quot;
        out_features = packed.shape&#91;0&#93;
        if packed.dtype != torch.uint8 or packed.shape&#91;1&#93; * 2 != in_features:
            raise ValueError(f&quot;packed NVFP4 weight must be uint8 &#91;&#123;out_features&#125;, &#123;in_features // 2&#125;&#93;, &quot;
                             f&quot;got &#123;packed.dtype&#125; &#123;list(packed.shape)&#125;&quot;)
        layer = cls(torch.empty(out_features, in_features, device=&quot;meta&quot;), None, rotation_group, compute_dtype,
                    act_scale)
        del layer.weight
        layer.register_parameter(&quot;weight&quot;, None)
        layer.bias = nn.Parameter(bias, requires_grad=False) if bias is not None else None
        layer.register_buffer(&quot;input_amax&quot;, input_amax.to(torch.float32))
        layer.register_buffer(&quot;packed_weight&quot;, packed, persistent=False)
        layer.register_buffer(&quot;weight_inv_scale&quot;, inv_scale, persistent=False)
        layer.register_buffer(&quot;weight_global_sf&quot;, global_sf_w, persistent=False)
        return layer

    @property
    def frozen(self) -&gt; bool:
        return self.weight is None

    def invalidate(self) -&gt; None:
        if self.frozen:
            raise RuntimeError(&quot;a frozen NVFP4DecoderLinear has no master weight to re-quantize&quot;)
        self._packed = None
        self._fused = None

    def _packed_weight(self) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;:
        if self.frozen:
            return self.packed_weight, self.weight_inv_scale, self.weight_global_sf
        if self._packed is None:
            self._packed = _quantized_weight(self.weight.detach().to(self.compute_dtype))
        return self._packed

    @torch.no_grad()
    def freeze(self) -&gt; None:
        &quot;&quot;&quot;Pack the weight once and drop the full-precision master (inference only, irreversible).

        Resident memory falls to the packed FP4 weight and its scales. Outputs are unchanged: the
        same packed tensors feed the eager GEMM and ``fused_state()``. Buffers are non-persistent,
        so a frozen layer&#x27;s ``state_dict()`` holds only ``bias`` and ``input_amax``.
        &quot;&quot;&quot;
        if self.frozen:
            return
        if self.dense_bypass or self.calibrating:
            raise RuntimeError(&quot;cannot freeze an NVFP4DecoderLinear in dense-bypass or calibration mode&quot;)
        packed, inv_scale, global_sf_w = self._packed_weight()
        del self.weight
        self.register_parameter(&quot;weight&quot;, None)
        self.register_buffer(&quot;packed_weight&quot;, packed, persistent=False)
        self.register_buffer(&quot;weight_inv_scale&quot;, inv_scale, persistent=False)
        self.register_buffer(&quot;weight_global_sf&quot;, global_sf_w, persistent=False)
        self._packed = None
        self._fused = None

    def fused_state(self) -&gt; NVFP4FusedState | None:
        &quot;&quot;&quot;Cached inference state for the fused decoder kernels, or None when they cannot reproduce this layer.

        The fused kernels quantize with a precomputed global scale, so a dynamic (per-call abs-max)
        scale, calibration, and the activation rotation keep the eager path.
        &quot;&quot;&quot;
        if (self.dense_bypass or self.rotation_group is not None or self.calibrating or self.act_scale == &quot;dynamic&quot;
                or self.compute_dtype != torch.bfloat16 or self.bias is None):
            return None
        packed, inv_scale, global_sf_w = self._packed_weight()
        # The data pointer re-keys the cache when a frozen layer&#x27;s buffers move (``module.to``).
        version = (self.input_amax._version, packed.data_ptr())
        if self._fused is None or self._fused&#91;0&#93; != version:
            global_sf_x = self._activation_global_sf(self.input_amax)
            alpha = 1.0 / (global_sf_x * global_sf_w)
            state = NVFP4FusedState(packed, inv_scale, global_sf_x, alpha, self.bias.detach().to(self.compute_dtype),
                                    self.out_features)
            self._fused = (version, state)
        return self._fused&#91;1&#93;

    def clear_offload_caches(self) -&gt; None:
        &quot;&quot;&quot;Drop the packed and fused caches, which are plain attributes and so invisible to offload.

        ``pinned_offload`` moves parameters and buffers only; these caches hold device
        tensors, so without this they stay on the GPU after an unload and accumulate
        across clips. The data-pointer keys re-create them on the next forward.
        &quot;&quot;&quot;
        self._packed = None
        self._fused = None

    def extra_repr(self) -&gt; str:
        return (f&quot;in_features=&#123;self.in_features&#125;, out_features=&#123;self.out_features&#125;, &quot;
                f&quot;rotation_group=&#123;self.rotation_group&#125;, act_scale=&#123;self.act_scale&#125;, frozen=&#123;self.frozen&#125;&quot;)

    def _activation_global_sf(self, x: torch.Tensor) -&gt; torch.Tensor | None:
        if self.calibrating:
            self.input_amax.copy_(torch.maximum(self.input_amax, x.detach().abs().amax().float()))
            return None
        if self.act_scale == &quot;dynamic&quot;:
            return None
        if self.act_scale == &quot;unit&quot;:
            return torch.ones((), dtype=torch.float32, device=x.device)
        return FP4_E4M3_RANGE / self.input_amax.clamp(min=1e-12)

    def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
        x = x.to(self.compute_dtype)
        if self.rotation_group is not None:
            x = rotate_activation(x, self.rotation_group)
        if self.dense_bypass:
            if self.frozen:
                raise RuntimeError(&quot;a frozen NVFP4DecoderLinear has no master weight for the dense bypass&quot;)
            bias = self.bias.to(x.dtype) if self.bias is not None else None
            return F.linear(x, self.weight.to(x.dtype), bias)
        global_sf_x = self._activation_global_sf(x)
        if torch.is_grad_enabled() and not self.frozen and self.weight.requires_grad:
            self.invalidate()
            return _NVFP4DecoderSTE.apply(x, self.weight, self.bias, global_sf_x)
        return nvfp4_linear_inference(x, *self._packed_weight(), self.bias, self.out_features, global_sf_x)</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.from_linear"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.from_linear</code></h3>
<pre class="doc-signature"><code>from_linear(linear: nn.Linear, *, rotation_group: int | None, compute_dtype: torch.dtype, act_scale: str = &#x27;dynamic&#x27;) -&gt; NVFP4DecoderLinear</code></pre>

<details><summary>Source</summary><pre><code>@classmethod
def from_linear(cls, linear: nn.Linear, *, rotation_group: int | None, compute_dtype: torch.dtype,
                act_scale: str = &quot;dynamic&quot;) -&gt; NVFP4DecoderLinear:
    weight = linear.weight.detach().float()
    if rotation_group is not None:
        # y = x W^T = (x H)(W H)^T for orthonormal symmetric H: rotate W along its input dim.
        weight = rotate_activation(weight, rotation_group)
    bias = linear.bias.detach().float() if linear.bias is not None else None
    return cls(weight, bias, rotation_group, compute_dtype, act_scale)</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.from_packed"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.from_packed</code></h3>
<pre class="doc-signature"><code>from_packed(packed: torch.Tensor, inv_scale: torch.Tensor, global_sf_w: torch.Tensor, bias: torch.Tensor | None, input_amax: torch.Tensor, *, in_features: int, rotation_group: int | None, compute_dtype: torch.dtype, act_scale: str) -&gt; NVFP4DecoderLinear</code></pre>
<p>A frozen layer built from already packed tensors (<code>freeze()</code>'s buffers), with no master weight.</p>
<p><code>packed</code> holds two FP4 codes per byte, so its shape is <code>[out_features, in_features // 2]</code>.
Nothing full-precision is allocated: the constructor's placeholder weight lives on <code>meta</code>.</p>
<details><summary>Source</summary><pre><code>@classmethod
def from_packed(cls, packed: torch.Tensor, inv_scale: torch.Tensor, global_sf_w: torch.Tensor,
                bias: torch.Tensor | None, input_amax: torch.Tensor, *, in_features: int,
                rotation_group: int | None, compute_dtype: torch.dtype, act_scale: str) -&gt; NVFP4DecoderLinear:
    &quot;&quot;&quot;A frozen layer built from already packed tensors (``freeze()``&#x27;s buffers), with no master weight.

    ``packed`` holds two FP4 codes per byte, so its shape is ``&#91;out_features, in_features // 2&#93;``.
    Nothing full-precision is allocated: the constructor&#x27;s placeholder weight lives on ``meta``.
    &quot;&quot;&quot;
    out_features = packed.shape&#91;0&#93;
    if packed.dtype != torch.uint8 or packed.shape&#91;1&#93; * 2 != in_features:
        raise ValueError(f&quot;packed NVFP4 weight must be uint8 &#91;&#123;out_features&#125;, &#123;in_features // 2&#125;&#93;, &quot;
                         f&quot;got &#123;packed.dtype&#125; &#123;list(packed.shape)&#125;&quot;)
    layer = cls(torch.empty(out_features, in_features, device=&quot;meta&quot;), None, rotation_group, compute_dtype,
                act_scale)
    del layer.weight
    layer.register_parameter(&quot;weight&quot;, None)
    layer.bias = nn.Parameter(bias, requires_grad=False) if bias is not None else None
    layer.register_buffer(&quot;input_amax&quot;, input_amax.to(torch.float32))
    layer.register_buffer(&quot;packed_weight&quot;, packed, persistent=False)
    layer.register_buffer(&quot;weight_inv_scale&quot;, inv_scale, persistent=False)
    layer.register_buffer(&quot;weight_global_sf&quot;, global_sf_w, persistent=False)
    return layer</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.invalidate"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.invalidate</code></h3>
<pre class="doc-signature"><code>invalidate() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def invalidate(self) -&gt; None:
    if self.frozen:
        raise RuntimeError(&quot;a frozen NVFP4DecoderLinear has no master weight to re-quantize&quot;)
    self._packed = None
    self._fused = None</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.freeze"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.freeze</code></h3>
<pre class="doc-signature"><code>freeze() -&gt; None</code></pre>
<p>Pack the weight once and drop the full-precision master (inference only, irreversible).</p>
<p>Resident memory falls to the packed FP4 weight and its scales. Outputs are unchanged: the
same packed tensors feed the eager GEMM and <code>fused_state()</code>. Buffers are non-persistent,
so a frozen layer's <code>state_dict()</code> holds only <code>bias</code> and <code>input_amax</code>.</p>
<details><summary>Source</summary><pre><code>@torch.no_grad()
def freeze(self) -&gt; None:
    &quot;&quot;&quot;Pack the weight once and drop the full-precision master (inference only, irreversible).

    Resident memory falls to the packed FP4 weight and its scales. Outputs are unchanged: the
    same packed tensors feed the eager GEMM and ``fused_state()``. Buffers are non-persistent,
    so a frozen layer&#x27;s ``state_dict()`` holds only ``bias`` and ``input_amax``.
    &quot;&quot;&quot;
    if self.frozen:
        return
    if self.dense_bypass or self.calibrating:
        raise RuntimeError(&quot;cannot freeze an NVFP4DecoderLinear in dense-bypass or calibration mode&quot;)
    packed, inv_scale, global_sf_w = self._packed_weight()
    del self.weight
    self.register_parameter(&quot;weight&quot;, None)
    self.register_buffer(&quot;packed_weight&quot;, packed, persistent=False)
    self.register_buffer(&quot;weight_inv_scale&quot;, inv_scale, persistent=False)
    self.register_buffer(&quot;weight_global_sf&quot;, global_sf_w, persistent=False)
    self._packed = None
    self._fused = None</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.fused_state"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.fused_state</code></h3>
<pre class="doc-signature"><code>fused_state() -&gt; NVFP4FusedState | None</code></pre>
<p>Cached inference state for the fused decoder kernels, or None when they cannot reproduce this layer.</p>
<p>The fused kernels quantize with a precomputed global scale, so a dynamic (per-call abs-max)
scale, calibration, and the activation rotation keep the eager path.</p>
<details><summary>Source</summary><pre><code>def fused_state(self) -&gt; NVFP4FusedState | None:
    &quot;&quot;&quot;Cached inference state for the fused decoder kernels, or None when they cannot reproduce this layer.

    The fused kernels quantize with a precomputed global scale, so a dynamic (per-call abs-max)
    scale, calibration, and the activation rotation keep the eager path.
    &quot;&quot;&quot;
    if (self.dense_bypass or self.rotation_group is not None or self.calibrating or self.act_scale == &quot;dynamic&quot;
            or self.compute_dtype != torch.bfloat16 or self.bias is None):
        return None
    packed, inv_scale, global_sf_w = self._packed_weight()
    # The data pointer re-keys the cache when a frozen layer&#x27;s buffers move (``module.to``).
    version = (self.input_amax._version, packed.data_ptr())
    if self._fused is None or self._fused&#91;0&#93; != version:
        global_sf_x = self._activation_global_sf(self.input_amax)
        alpha = 1.0 / (global_sf_x * global_sf_w)
        state = NVFP4FusedState(packed, inv_scale, global_sf_x, alpha, self.bias.detach().to(self.compute_dtype),
                                self.out_features)
        self._fused = (version, state)
    return self._fused&#91;1&#93;</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.clear_offload_caches"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.clear_offload_caches</code></h3>
<pre class="doc-signature"><code>clear_offload_caches() -&gt; None</code></pre>
<p>Drop the packed and fused caches, which are plain attributes and so invisible to offload.</p>
<p><code>pinned_offload</code> moves parameters and buffers only; these caches hold device
tensors, so without this they stay on the GPU after an unload and accumulate
across clips. The data-pointer keys re-create them on the next forward.</p>
<details><summary>Source</summary><pre><code>def clear_offload_caches(self) -&gt; None:
    &quot;&quot;&quot;Drop the packed and fused caches, which are plain attributes and so invisible to offload.

    ``pinned_offload`` moves parameters and buffers only; these caches hold device
    tensors, so without this they stay on the GPU after an unload and accumulate
    across clips. The data-pointer keys re-create them on the next forward.
    &quot;&quot;&quot;
    self._packed = None
    self._fused = None</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.extra_repr"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.extra_repr</code></h3>
<pre class="doc-signature"><code>extra_repr() -&gt; str</code></pre>

<details><summary>Source</summary><pre><code>def extra_repr(self) -&gt; str:
    return (f&quot;in_features=&#123;self.in_features&#125;, out_features=&#123;self.out_features&#125;, &quot;
            f&quot;rotation_group=&#123;self.rotation_group&#125;, act_scale=&#123;self.act_scale&#125;, frozen=&#123;self.frozen&#125;&quot;)</code></pre></details>
<h3 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.forward"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.NVFP4DecoderLinear.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
    x = x.to(self.compute_dtype)
    if self.rotation_group is not None:
        x = rotate_activation(x, self.rotation_group)
    if self.dense_bypass:
        if self.frozen:
            raise RuntimeError(&quot;a frozen NVFP4DecoderLinear has no master weight for the dense bypass&quot;)
        bias = self.bias.to(x.dtype) if self.bias is not None else None
        return F.linear(x, self.weight.to(x.dtype), bias)
    global_sf_x = self._activation_global_sf(x)
    if torch.is_grad_enabled() and not self.frozen and self.weight.requires_grad:
        self.invalidate()
        return _NVFP4DecoderSTE.apply(x, self.weight, self.bias, global_sf_x)
    return nvfp4_linear_inference(x, *self._packed_weight(), self.bias, self.out_features, global_sf_x)</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.nvfp4_decoder_linear_names"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.nvfp4_decoder_linear_names</code></h2>
<pre class="doc-signature"><code>nvfp4_decoder_linear_names(decoder: nn.Module, skip_blocks: tuple&#91;int, ...&#93; = ()) -&gt; list&#91;str&#93;</code></pre>
<p>Names (relative to the decoder) of the block linears to replace.</p>
<details><summary>Source</summary><pre><code>def nvfp4_decoder_linear_names(decoder: nn.Module, skip_blocks: tuple&#91;int, ...&#93; = ()) -&gt; list&#91;str&#93;:
    &quot;&quot;&quot;Names (relative to the decoder) of the block linears to replace.&quot;&quot;&quot;
    names = &#91;&#93;
    for name, module in decoder.named_modules():
        match = DECODER_BLOCK_LINEAR.match(name)
        if match and type(module) is nn.Linear and int(match.group(1)) not in skip_blocks:
            names.append(name)
    return names</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.convert_decoder_to_nvfp4"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.convert_decoder_to_nvfp4</code></h2>
<pre class="doc-signature"><code>convert_decoder_to_nvfp4(decoder: nn.Module, *, rotation_group: int | None = None, skip_blocks: tuple&#91;int, ...&#93; = (), compute_dtype: torch.dtype = torch.bfloat16, act_scale: str = &#x27;dynamic&#x27;) -&gt; list&#91;str&#93;</code></pre>
<p>Replace the decoder's block linears with <code>NVFP4DecoderLinear</code>; return the replaced names.</p>
<details><summary>Source</summary><pre><code>def convert_decoder_to_nvfp4(decoder: nn.Module,
                             *,
                             rotation_group: int | None = None,
                             skip_blocks: tuple&#91;int, ...&#93; = (),
                             compute_dtype: torch.dtype = torch.bfloat16,
                             act_scale: str = &quot;dynamic&quot;) -&gt; list&#91;str&#93;:
    &quot;&quot;&quot;Replace the decoder&#x27;s block linears with ``NVFP4DecoderLinear``; return the replaced names.&quot;&quot;&quot;
    names = nvfp4_decoder_linear_names(decoder, skip_blocks)
    if not names:
        raise ValueError(&quot;No MiniMax-H3 decoder block linears found to convert&quot;)
    for name in names:
        parent_name, _, child = name.rpartition(&quot;.&quot;)
        parent = decoder.get_submodule(parent_name)
        original = getattr(parent, child) if not child.isdigit() else parent&#91;int(child)&#93;
        replacement = NVFP4DecoderLinear.from_linear(original,
                                                     rotation_group=rotation_group,
                                                     compute_dtype=compute_dtype,
                                                     act_scale=act_scale)
        if child.isdigit():
            parent&#91;int(child)&#93; = replacement
        else:
            setattr(parent, child, replacement)
    enable_fused_blocks(decoder)
    return names</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.enable_fused_blocks"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.enable_fused_blocks</code></h2>
<pre class="doc-signature"><code>enable_fused_blocks(decoder: nn.Module) -&gt; None</code></pre>
<p>Run eligible NVFP4 block stacks through the bit-exact fused kernels (see that module).</p>
<details><summary>Source</summary><pre><code>def enable_fused_blocks(decoder: nn.Module) -&gt; None:
    &quot;&quot;&quot;Run eligible NVFP4 block stacks through the bit-exact fused kernels (see that module).&quot;&quot;&quot;
    try:
        from fastvideo.models.vaes.minimax_h3_nvfp4_fused import fused_nvfp4_blocks_forward
    except ImportError:
        # Triton-less hosts (CPU, macOS) keep the eager NVFP4 path; the fused kernels are an exact speedup only.
        return
    decoder.fused_blocks_forward = fused_nvfp4_blocks_forward</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.nvfp4_linears"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.nvfp4_linears</code></h2>
<pre class="doc-signature"><code>nvfp4_linears(module: nn.Module) -&gt; list&#91;NVFP4DecoderLinear&#93;</code></pre>
<p>Every NVFP4 decoder linear under <code>module</code>; empty for a stub that is not an <code>nn.Module</code>.</p>
<details><summary>Source</summary><pre><code>def nvfp4_linears(module: nn.Module) -&gt; list&#91;NVFP4DecoderLinear&#93;:
    &quot;&quot;&quot;Every NVFP4 decoder linear under ``module``; empty for a stub that is not an ``nn.Module``.&quot;&quot;&quot;
    if not isinstance(module, nn.Module):
        return &#91;&#93;
    return &#91;sub for sub in module.modules() if isinstance(sub, NVFP4DecoderLinear)&#93;</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.calibrate_static_scales"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.calibrate_static_scales</code></h2>
<pre class="doc-signature"><code>calibrate_static_scales(module: nn.Module, run_batches, margin: float = 1.0) -&gt; int</code></pre>
<p>Record each NVFP4 linear's activation abs-max over <code>run_batches()</code> (dynamic numerics while recording).</p>
<p><code>margin</code> &gt; 1 widens the stored range. Returns the number of calibrated linears.</p>
<details><summary>Source</summary><pre><code>@torch.no_grad()
def calibrate_static_scales(module: nn.Module, run_batches, margin: float = 1.0) -&gt; int:
    &quot;&quot;&quot;Record each NVFP4 linear&#x27;s activation abs-max over ``run_batches()`` (dynamic numerics while recording).

    ``margin`` &gt; 1 widens the stored range. Returns the number of calibrated linears.
    &quot;&quot;&quot;
    layers = nvfp4_linears(module)
    for layer in layers:
        layer.input_amax.zero_()
        layer.calibrating = True
    try:
        run_batches()
    finally:
        for layer in layers:
            layer.calibrating = False
    for layer in layers:
        if layer.input_amax.item() &lt;= 0:
            raise RuntimeError(&quot;an NVFP4 decoder linear saw no activations during calibration&quot;)
        layer.input_amax.mul_(margin)
    return len(layers)</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.freeze_nvfp4_linears"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.freeze_nvfp4_linears</code></h2>
<pre class="doc-signature"><code>freeze_nvfp4_linears(module: nn.Module) -&gt; int</code></pre>
<p><code>freeze()</code> every NVFP4 linear under <code>module</code> (inference only); return how many.</p>
<details><summary>Source</summary><pre><code>def freeze_nvfp4_linears(module: nn.Module) -&gt; int:
    &quot;&quot;&quot;``freeze()`` every NVFP4 linear under ``module`` (inference only); return how many.&quot;&quot;&quot;
    layers = nvfp4_linears(module)
    for layer in layers:
        layer.freeze()
    return len(layers)</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.freeze_nvfp4_linears_on_device"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.freeze_nvfp4_linears_on_device</code></h2>
<pre class="doc-signature"><code>freeze_nvfp4_linears_on_device(module: nn.Module) -&gt; int</code></pre>
<p><code>freeze_nvfp4_linears</code> once <code>module</code> is on an accelerator; 0 when it is not.</p>
<p><code>_apply_h3_nvfp4_decoder</code> cannot freeze a VAE that was built on the CPU,
because packing needs the CUDA FP4 kernels. With VAE offload that is every
load, so the layers keep their fp32 masters and a plain-attribute packed
cache for the whole decode. Freeze at the first CUDA load instead: the packed
weight is then a buffer, which is both smaller and movable by <code>pinned_offload</code>.</p>
<details><summary>Source</summary><pre><code>def freeze_nvfp4_linears_on_device(module: nn.Module) -&gt; int:
    &quot;&quot;&quot;``freeze_nvfp4_linears`` once ``module`` is on an accelerator; 0 when it is not.

    ``_apply_h3_nvfp4_decoder`` cannot freeze a VAE that was built on the CPU,
    because packing needs the CUDA FP4 kernels. With VAE offload that is every
    load, so the layers keep their fp32 masters and a plain-attribute packed
    cache for the whole decode. Freeze at the first CUDA load instead: the packed
    weight is then a buffer, which is both smaller and movable by ``pinned_offload``.
    &quot;&quot;&quot;
    if not isinstance(module, nn.Module):
        return 0
    layers = &#91;layer for layer in nvfp4_linears(module)
              if not layer.frozen and not layer.dense_bypass and not layer.calibrating&#93;
    parameters = next(module.parameters(), None)
    if not layers or parameters is None or parameters.device.type != &quot;cuda&quot;:
        return 0
    for layer in layers:
        layer.freeze()
    return len(layers)</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.keep_evenly_spaced_blocks"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.keep_evenly_spaced_blocks</code></h2>
<pre class="doc-signature"><code>keep_evenly_spaced_blocks(decoder: nn.Module, count: int) -&gt; list&#91;int&#93;</code></pre>
<p>Depth-cut the decoder to <code>count</code> evenly spaced blocks (first and last kept); return the kept indices.</p>
<details><summary>Source</summary><pre><code>def keep_evenly_spaced_blocks(decoder: nn.Module, count: int) -&gt; list&#91;int&#93;:
    &quot;&quot;&quot;Depth-cut the decoder to ``count`` evenly spaced blocks (first and last kept); return the kept indices.&quot;&quot;&quot;
    total = len(decoder.transformer_blocks)
    if not 1 &lt;= count &lt;= total:
        raise ValueError(f&quot;cannot keep &#123;count&#125; of &#123;total&#125; decoder blocks&quot;)
    indices = sorted(&#123;round(i * (total - 1) / max(count - 1, 1)) for i in range(count)&#125;)
    decoder.transformer_blocks = nn.ModuleList(decoder.transformer_blocks&#91;i&#93; for i in indices)
    return indices</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.load_nvfp4_decoder_checkpoint"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.load_nvfp4_decoder_checkpoint</code></h2>
<pre class="doc-signature"><code>load_nvfp4_decoder_checkpoint(path: str, device: torch.device | str = &#x27;cpu&#x27;) -&gt; dict&#91;str, Any&#93;</code></pre>
<p>Read an exported NVFP4 decoder (tensors, plain metadata) and check its format.</p>
<p><code>.safetensors</code> files may hold bf16 masters (deploy format) or pre-packed NVFP4 weights;
packed tensors are read straight onto <code>device</code>. <code>.pt</code> files are deploy format only.</p>
<details><summary>Source</summary><pre><code>def load_nvfp4_decoder_checkpoint(path: str, device: torch.device | str = &quot;cpu&quot;) -&gt; dict&#91;str, Any&#93;:
    &quot;&quot;&quot;Read an exported NVFP4 decoder (tensors, plain metadata) and check its format.

    ``.safetensors`` files may hold bf16 masters (deploy format) or pre-packed NVFP4 weights;
    packed tensors are read straight onto ``device``. ``.pt`` files are deploy format only.
    &quot;&quot;&quot;
    if str(path).endswith(&quot;.safetensors&quot;):
        from fastvideo.models.vaes.minimax_h3_nvfp4_checkpoint import read_nvfp4_decoder_safetensors

        return read_nvfp4_decoder_safetensors(path, device)
    checkpoint = torch.load(path, map_location=&quot;cpu&quot;, mmap=True, weights_only=True)
    if not isinstance(checkpoint, dict) or checkpoint.get(&quot;format&quot;) != NVFP4_DECODER_DEPLOY_FORMAT:
        found = checkpoint.get(&quot;format&quot;) if isinstance(checkpoint, dict) else type(checkpoint).__name__
        raise ValueError(f&quot;&#123;path&#125; is not a &#123;NVFP4_DECODER_DEPLOY_FORMAT&#125; checkpoint (format: &#123;found!r&#125;)&quot;)
    missing = &#123;&quot;decoder&quot;, &quot;post_quant_conv&quot;, &quot;metadata&quot;&#125; - checkpoint.keys()
    if missing:
        raise ValueError(f&quot;&#123;path&#125; is missing &#123;sorted(missing)&#125;&quot;)
    return checkpoint</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.apply_nvfp4_decoder_checkpoint"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.apply_nvfp4_decoder_checkpoint</code></h2>
<pre class="doc-signature"><code>apply_nvfp4_decoder_checkpoint(vae: nn.Module, checkpoint: dict&#91;str, Any&#93;, *, freeze: bool) -&gt; dict&#91;str, Any&#93;</code></pre>
<p>Turn a dense <code>AutoencoderKLMiniMaxH3</code>'s decoder into an exported NVFP4 decoder, in place.</p>
<p>The VAE is depth-cut to the checkpoint's <code>student_layers</code> blocks when it has more (a light
or full VAE can host a shallower student), converted with the checkpoint's NVFP4 settings,
and loaded strictly, so an architecture mismatch fails here instead of decoding garbage.
<code>freeze</code> packs the weights and drops the masters (needs the VAE on a CUDA device). A
pre-packed checkpoint is always loaded frozen, without materializing any master weight.
Sets <code>vae.decode_autocast_dtype</code> to bf16, the dtype these decoders are trained and
validated under. Returns the checkpoint metadata.</p>
<details><summary>Source</summary><pre><code>def apply_nvfp4_decoder_checkpoint(vae: nn.Module, checkpoint: dict&#91;str, Any&#93;, *, freeze: bool) -&gt; dict&#91;str, Any&#93;:
    &quot;&quot;&quot;Turn a dense ``AutoencoderKLMiniMaxH3``&#x27;s decoder into an exported NVFP4 decoder, in place.

    The VAE is depth-cut to the checkpoint&#x27;s ``student_layers`` blocks when it has more (a light
    or full VAE can host a shallower student), converted with the checkpoint&#x27;s NVFP4 settings,
    and loaded strictly, so an architecture mismatch fails here instead of decoding garbage.
    ``freeze`` packs the weights and drops the masters (needs the VAE on a CUDA device). A
    pre-packed checkpoint is always loaded frozen, without materializing any master weight.
    Sets ``vae.decode_autocast_dtype`` to bf16, the dtype these decoders are trained and
    validated under. Returns the checkpoint metadata.
    &quot;&quot;&quot;
    metadata = checkpoint&#91;&quot;metadata&quot;&#93;
    decoder = vae.decoder
    _depth_cut_for(decoder, metadata)
    if checkpoint&#91;&quot;format&quot;&#93; != NVFP4_DECODER_DEPLOY_FORMAT:
        from fastvideo.models.vaes.minimax_h3_nvfp4_checkpoint import apply_packed_nvfp4_decoder

        apply_packed_nvfp4_decoder(vae, checkpoint)
        vae.decode_autocast_dtype = torch.bfloat16
        return metadata
    act_scale = metadata.get(&quot;act_scale&quot;, &quot;dynamic&quot;)
    names = convert_decoder_to_nvfp4(decoder,
                                     rotation_group=metadata.get(&quot;rotation_group&quot;),
                                     skip_blocks=tuple(metadata.get(&quot;skip_blocks&quot;) or ()),
                                     compute_dtype=torch.bfloat16,
                                     act_scale=act_scale)
    check_num_linears(metadata, len(names))
    try:
        decoder.load_state_dict(checkpoint&#91;&quot;decoder&quot;&#93;, strict=True)
        vae.post_quant_conv.load_state_dict(checkpoint&#91;&quot;post_quant_conv&quot;&#93;, strict=True)
    except RuntimeError as error:
        raise ValueError(f&quot;the NVFP4 decoder does not fit this VAE&#x27;s architecture: &#123;error&#125;&quot;) from error
    vae.requires_grad_(False)
    if freeze:
        freeze_nvfp4_linears(decoder)
    vae.decode_autocast_dtype = torch.bfloat16
    return metadata</code></pre></details>
<h2 id="fastvideo.models.vaes.minimax_h3_nvfp4_decoder.check_num_linears"><code>fastvideo.models.vaes.minimax_h3_nvfp4_decoder.check_num_linears</code></h2>
<pre class="doc-signature"><code>check_num_linears(metadata: dict&#91;str, Any&#93;, found: int) -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def check_num_linears(metadata: dict&#91;str, Any&#93;, found: int) -&gt; None:
    expected = metadata.get(&quot;num_linears&quot;)
    if expected is not None and int(expected) != found:
        raise ValueError(f&quot;the NVFP4 decoder has &#123;expected&#125; NVFP4 linears, the converted VAE &#123;found&#125;&quot;)</code></pre></details>
</div>
