---
title: nvfp4_config
---

<div class="python-reference">
<a id="fastvideo.layers.quantization.nvfp4_config-classes"></a>
<a id="fastvideo.layers.quantization.nvfp4_config-functions"></a>
<a id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod-functions"></a>
<a id="fastvideo.layers.quantization.nvfp4_config"></a>
<p>NVFP4 linear quantization for supported transformer layer sets.</p>
<p>NVFP4 is NVIDIA's block-scaled FP4 format (e2m1 mantissa, fp32 alpha,
<code>layout_128x4</code> scale layout, group size 16) — distinct from
generic FP4 / OCP-FP4 / MX-FP4. We name the public surface <code>NVFP4</code>
explicitly so downstream callers don't conflate it with other FP4
variants that may land later (e.g. AMD's MX-FP4 or vendor-neutral
e3m0).</p>
<p>The registered config targets the curated LTX-2 deployment set, the
main MiniMax-H3 transformer-block FFN linears, and the packed MiniMax-H3
DiT export (<code>layer_profile="h3_dit"</code>) covering attention plus FFN.</p>
<p><code>flashinfer</code> is imported lazily inside the call paths that need it.
This keeps <code>import fastvideo</code> cheap on hosts where flashinfer is
not installed; only the actual NVFP4 quantize / matmul ops fail at
use time, with a clear error.</p>
<h2 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig</code></h2>
<pre class="doc-signature"><code>QuantizationConfig()</code></pre>
<p>Base class for quantization configs.</p>

<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_name"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_name</code></h3>
<pre class="doc-signature"><code>get_name() -&gt; QuantizationMethods</code></pre>
<p>Name of the quantization method.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_supported_act_dtypes"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_supported_act_dtypes</code></h3>
<pre class="doc-signature"><code>get_supported_act_dtypes() -&gt; list&#91;torch.dtype&#93;</code></pre>
<p>List of supported activation dtypes.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_min_capability"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_min_capability</code></h3>
<pre class="doc-signature"><code>get_min_capability() -&gt; int</code></pre>
<p>Minimum GPU capability to support the quantization method.</p>
<p>E.g., 70 for Volta, 75 for Turing, 80 for Ampere.
This requirement is due to the custom CUDA kernels used by the
quantization method.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_config_filenames"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_config_filenames</code></h3>
<pre class="doc-signature"><code>get_config_filenames() -&gt; list&#91;str&#93;</code></pre>
<p>List of filenames to search for in the model directory.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.from_config"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.from_config</code></h3>
<pre class="doc-signature"><code>from_config(config: dict&#91;str, Any&#93;) -&gt; QuantizationConfig</code></pre>
<p>Create a config class from the model's quantization config.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.override_quantization_method"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.override_quantization_method</code></h3>
<pre class="doc-signature"><code>override_quantization_method(hf_quant_cfg, user_quant) -&gt; QuantizationMethods | None</code></pre>
<p>Detects if this quantization method can support a given checkpoint
format by overriding the user specified quantization method -- 
this method should only be overwritten by subclasses in exceptional 
circumstances</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_from_keys"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_from_keys</code></h3>
<pre class="doc-signature"><code>get_from_keys(config: dict&#91;str, Any&#93;, keys: list&#91;str&#93;) -&gt; Any</code></pre>
<p>Get a value from the model's quantization config.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_from_keys_or"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_from_keys_or</code></h3>
<pre class="doc-signature"><code>get_from_keys_or(config: dict&#91;str, Any&#93;, keys: list&#91;str&#93;, default: Any) -&gt; Any</code></pre>
<p>Get a optional value from the model's quantization config.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_quant_method"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_quant_method</code></h3>
<pre class="doc-signature"><code>get_quant_method(layer: torch.nn.Module, prefix: str) -&gt; QuantizeMethodBase | None</code></pre>
<p>Get the quantize method to use for the quantized layer.</p>
<h4>Parameters</h4>
<dl>
<dt><code>layer: torch.nn.Module</code></dt><dd><p>The layer for the quant method.</p></dd>
<dt><code>prefix: str</code></dt><dd><p>The full name of the layer in the state dict</p></dd>
</dl>
<p>Returns:
    The quantize method. None if the given layer doesn't support quant
    method.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.validate_runtime"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.validate_runtime</code></h3>
<pre class="doc-signature"><code>validate_runtime(device: torch.device) -&gt; None</code></pre>
<p>Fail fast on a device the scheme cannot execute on.</p>
<p>Called once by loaders that select a serialized checkpoint scheme,
before any weight is read. The default accepts every device; schemes
with kernel or capability requirements override it.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_cache_scale"><code>fastvideo.layers.quantization.nvfp4_config.QuantizationConfig.get_cache_scale</code></h3>
<pre class="doc-signature"><code>get_cache_scale(name: str) -&gt; str | None</code></pre>

<h2 id="fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase"><code>fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase</code></h2>
<pre class="doc-signature"><code>QuantizeMethodBase()</code></pre>
<p>Base class for different quantized methods.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.create_weights"><code>fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.create_weights</code></h3>
<pre class="doc-signature"><code>create_weights(layer: torch.nn.Module, *weight_args, **extra_weight_attrs)</code></pre>
<p>Create weights for a layer.</p>
<p>The weights will be set as attributes of the layer.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.apply"><code>fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.apply</code></h3>
<pre class="doc-signature"><code>apply(layer: torch.nn.Module, *args, **kwargs) -&gt; torch.Tensor</code></pre>
<p>Apply the weights in layer to the input tensor.</p>
<p>Expects create_weights to have been called before on the layer.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.embedding"><code>fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.embedding</code></h3>
<pre class="doc-signature"><code>embedding(layer: torch.nn.Module, *args, **kwargs) -&gt; torch.Tensor</code></pre>
<p>Gather embeddings in the layer based on indices in the input tensor.</p>
<p>Expects create_weights to have been called before on the layer.</p>
<h3 id="fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.process_weights_after_loading"><code>fastvideo.layers.quantization.nvfp4_config.QuantizeMethodBase.process_weights_after_loading</code></h3>
<pre class="doc-signature"><code>process_weights_after_loading(layer: nn.Module) -&gt; None</code></pre>
<p>Process the weight after loading.</p>
<p>This can be used for example, to transpose weights for computation.</p>
<h2 id="fastvideo.layers.quantization.nvfp4_config.set_weight_attrs"><code>fastvideo.layers.quantization.nvfp4_config.set_weight_attrs</code></h2>
<pre class="doc-signature"><code>set_weight_attrs(weight: torch.Tensor, weight_attrs: dict&#91;str, Any&#93; | None)</code></pre>
<p>Set attributes on a weight tensor.</p>
<p>This method is used to set attributes on a weight tensor. This method
will not overwrite existing attributes.</p>
<h3>Parameters</h3>
<dl>
<dt><code>weight: torch.Tensor</code></dt><dd><p>The weight tensor.</p></dd>
<dt><code>weight_attrs: dict[str, Any] | None</code></dt><dd><p>A dictionary of attributes to set on the weight tensor.</p></dd>
</dl>
<h2 id="fastvideo.layers.quantization.nvfp4_config.minimax_h3_block_index"><code>fastvideo.layers.quantization.nvfp4_config.minimax_h3_block_index</code></h2>
<pre class="doc-signature"><code>minimax_h3_block_index(prefix: str) -&gt; int | None</code></pre>
<p>Main transformer-block index of a MiniMax-H3 module prefix, or None outside the main blocks.</p>
<details><summary>Source</summary><pre><code>def minimax_h3_block_index(prefix: str) -&gt; int | None:
    &quot;&quot;&quot;Main transformer-block index of a MiniMax-H3 module prefix, or None outside the main blocks.&quot;&quot;&quot;
    match = _H3_MAIN_BLOCK.search(prefix)
    return int(match.group(1)) if match is not None else None</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.read_minimax_h3_nvfp4_dit_skip_blocks"><code>fastvideo.layers.quantization.nvfp4_config.read_minimax_h3_nvfp4_dit_skip_blocks</code></h2>
<pre class="doc-signature"><code>read_minimax_h3_nvfp4_dit_skip_blocks(path: str) -&gt; tuple&#91;int, ...&#93;</code></pre>
<p>The bf16 main blocks a packed NVFP4H3 export declares (empty for exports that quantize every block).</p>
<details><summary>Source</summary><pre><code>def read_minimax_h3_nvfp4_dit_skip_blocks(path: str) -&gt; tuple&#91;int, ...&#93;:
    &quot;&quot;&quot;The bf16 main blocks a packed NVFP4H3 export declares (empty for exports that quantize every block).&quot;&quot;&quot;
    import json

    from safetensors import safe_open

    with safe_open(path, framework=&quot;pt&quot;, device=&quot;cpu&quot;) as reader:
        raw = (reader.metadata() or &#123;&#125;).get(H3_NVFP4_DIT_SKIP_BLOCKS_KEY)
    if raw is None:
        return ()
    blocks = json.loads(raw)
    if not isinstance(blocks, list) or not all(
            isinstance(b, int) and not isinstance(b, bool) and b &gt;= 0 for b in blocks):
        raise ValueError(f&quot;&#123;path&#125;: metadata &#123;H3_NVFP4_DIT_SKIP_BLOCKS_KEY!r&#125; must be a JSON list of block indices&quot;)
    return tuple(sorted(set(blocks)))</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.is_ltx2_nvfp4_linear_prefix"><code>fastvideo.layers.quantization.nvfp4_config.is_ltx2_nvfp4_linear_prefix</code></h2>
<pre class="doc-signature"><code>is_ltx2_nvfp4_linear_prefix(prefix: str) -&gt; bool</code></pre>
<p>Return whether <em>prefix</em> belongs to the LTX-2 NVFP4 deployment set.</p>
<details><summary>Source</summary><pre><code>def is_ltx2_nvfp4_linear_prefix(prefix: str) -&gt; bool:
    &quot;&quot;&quot;Return whether *prefix* belongs to the LTX-2 NVFP4 deployment set.&quot;&quot;&quot;
    return prefix in _LTX2_NVFP4_LINEAR_PREFIXES</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.is_minimax_h3_nvfp4_linear_prefix"><code>fastvideo.layers.quantization.nvfp4_config.is_minimax_h3_nvfp4_linear_prefix</code></h2>
<pre class="doc-signature"><code>is_minimax_h3_nvfp4_linear_prefix(prefix: str) -&gt; bool</code></pre>

<details><summary>Source</summary><pre><code>def is_minimax_h3_nvfp4_linear_prefix(prefix: str) -&gt; bool:
    return _MINIMAX_H3_NVFP4_FF_PREFIX.search(prefix) is not None</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.is_minimax_h3_nvfp4_dit_linear_prefix"><code>fastvideo.layers.quantization.nvfp4_config.is_minimax_h3_nvfp4_dit_linear_prefix</code></h2>
<pre class="doc-signature"><code>is_minimax_h3_nvfp4_dit_linear_prefix(prefix: str) -&gt; bool</code></pre>
<p>Return whether <em>prefix</em> is a MiniMax-H3 DiT attention or FFN linear.</p>
<p>This is the packed NVFP4H3 export set: <code>attn.to_&#123;q,k,v,out&#125;</code> and
<code>ff.&#123;fc_in,fc_out&#125;</code> in each main transformer block. Token-refiner,
AdaLN, and embedding linears stay dense.</p>
<details><summary>Source</summary><pre><code>def is_minimax_h3_nvfp4_dit_linear_prefix(prefix: str) -&gt; bool:
    &quot;&quot;&quot;Return whether *prefix* is a MiniMax-H3 DiT attention or FFN linear.

    This is the packed NVFP4H3 export set: ``attn.to_&#123;q,k,v,out&#125;`` and
    ``ff.&#123;fc_in,fc_out&#125;`` in each main transformer block. Token-refiner,
    AdaLN, and embedding linears stay dense.
    &quot;&quot;&quot;
    return _MINIMAX_H3_NVFP4_DIT_PREFIX.search(prefix) is not None</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.is_minimax_h3_nvfp4_dit_export_path"><code>fastvideo.layers.quantization.nvfp4_config.is_minimax_h3_nvfp4_dit_export_path</code></h2>
<pre class="doc-signature"><code>is_minimax_h3_nvfp4_dit_export_path(path: str) -&gt; bool</code></pre>

<details><summary>Source</summary><pre><code>def is_minimax_h3_nvfp4_dit_export_path(path: str) -&gt; bool:
    return os.path.basename(path) == H3_NVFP4_DIT_EXPORT_FILENAME</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.find_minimax_h3_nvfp4_dit_export"><code>fastvideo.layers.quantization.nvfp4_config.find_minimax_h3_nvfp4_dit_export</code></h2>
<pre class="doc-signature"><code>find_minimax_h3_nvfp4_dit_export(weight_paths: list&#91;str&#93;) -&gt; str | None</code></pre>

<details><summary>Source</summary><pre><code>def find_minimax_h3_nvfp4_dit_export(weight_paths: list&#91;str&#93;) -&gt; str | None:
    seen: list&#91;str&#93; = &#91;&#93;
    for path in weight_paths:
        if is_minimax_h3_nvfp4_dit_export_path(path) and os.path.isfile(path):
            return path
        directory = path if os.path.isdir(path) else os.path.dirname(path)
        if directory and directory not in seen:
            seen.append(directory)
    for directory in seen:
        candidate = os.path.join(directory, H3_NVFP4_DIT_EXPORT_FILENAME)
        if os.path.isfile(candidate):
            return candidate
    return None</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.dense_transformer_safetensors"><code>fastvideo.layers.quantization.nvfp4_config.dense_transformer_safetensors</code></h2>
<pre class="doc-signature"><code>dense_transformer_safetensors(weight_paths: list&#91;str&#93;) -&gt; list&#91;str&#93;</code></pre>
<p>Drop the packed NVFP4 DiT export so it is not loaded as bf16 weights.</p>
<details><summary>Source</summary><pre><code>def dense_transformer_safetensors(weight_paths: list&#91;str&#93;) -&gt; list&#91;str&#93;:
    &quot;&quot;&quot;Drop the packed NVFP4 DiT export so it is not loaded as bf16 weights.&quot;&quot;&quot;
    return &#91;path for path in weight_paths if not is_minimax_h3_nvfp4_dit_export_path(path)&#93;</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.nvfp4_quantize_fenced"><code>fastvideo.layers.quantization.nvfp4_config.nvfp4_quantize_fenced</code></h2>
<pre class="doc-signature"><code>nvfp4_quantize_fenced(x: torch.Tensor, global_sf: torch.Tensor, sf_layout: int, do_shuffle: bool = False) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;</code></pre>
<p>FlashInfer NVFP4 quantization with the DGX Spark (GB10) ordering fence.</p>
<p>Every FastVideo NVFP4 quantization goes through here, so the GB10
workaround covers inference and the QAT straight-through linear alike.</p>
<details><summary>Source</summary><pre><code>def nvfp4_quantize_fenced(
    x: torch.Tensor,
    global_sf: torch.Tensor,
    sf_layout: int,
    do_shuffle: bool = False,
) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
    &quot;&quot;&quot;FlashInfer NVFP4 quantization with the DGX Spark (GB10) ordering fence.

    Every FastVideo NVFP4 quantization goes through here, so the GB10
    workaround covers inference and the QAT straight-through linear alike.
    &quot;&quot;&quot;
    device_index = x.device.index if x.device.index is not None else torch.cuda.current_device()
    spark = _is_dgx_spark(device_index)
    if spark and torch.cuda.is_current_stream_capturing():
        raise RuntimeError(&quot;NVFP4 activation quantization on DGX Spark requires a completion fence; &quot;
                           &quot;disable CUDA graph capture.&quot;)
    SfLayout, _, nvfp4_quantize = _require_flashinfer()
    if not spark:
        return nvfp4_quantize(x, global_sf, sfLayout=SfLayout(sf_layout), do_shuffle=do_shuffle)
    # FlashInfer&#x27;s PDL kernel reads the global scale before its
    # dependency wait. Fresh dynamic scales require normal ordering.
    quantized, scales = nvfp4_quantize(x,
                                       global_sf,
                                       sfLayout=SfLayout(sf_layout),
                                       do_shuffle=do_shuffle,
                                       enable_pdl=False)
    # With FlashInfer 0.6.18 and 0.7.0 on GB10, queued activation
    # quantization plus GEMM can diverge: identical seeded H3 requests
    # produce different latents. Completing each quantization on the
    # host prevents it, at no measurable denoise cost.
    torch.cuda.current_stream(x.device).synchronize()
    return quantized, scales</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod</code></h2>
<pre class="doc-signature"><code>NVFP4QuantizeMethod(layer_prefix: str = &#x27;&#x27;)</code></pre>


<details><summary>Source</summary><pre><code>class NVFP4QuantizeMethod(QuantizeMethodBase):

    # Lazily resolved by _static_activation_global_sf; class defaults also cover object.__new__ test doubles.
    _static_sf_checked: bool = False
    _static_sf: torch.Tensor | None = None

    def __init__(self, layer_prefix: str = &quot;&quot;):
        super().__init__()
        self.weight_fp4 = None
        self.weight_scale = None
        self.x_global_sf = torch.tensor(1.0, device=&quot;cuda&quot;, dtype=torch.float32)
        self.layer_prefix = layer_prefix
        self._is_refine_only_layer = _is_ltx2_refine_only_prefix(layer_prefix)
        # Set from NVFP4Config.retain_original_weights in get_quant_method:
        # True = retain every original bf16 weight; None/False (default) =
        # purge the purgeable set. Refine-only layers are always retained --
        # the base stage profile runs them dense by deployment contract.
        self._retain_original_weights: bool | None = None

    def create_weights(self, layer: torch.nn.Module, input_size_per_partition: int, output_partition_sizes: list&#91;int&#93;,
                       input_size: int, output_size: int, params_dtype: torch.dtype, **extra_weight_attrs):
        weight = Parameter(torch.empty(
            sum(output_partition_sizes),
            input_size_per_partition,
            dtype=params_dtype,
        ),
                           requires_grad=False)
        set_weight_attrs(weight, &#123;&quot;input_dim&quot;: 1, &quot;output_dim&quot;: 0&#125;)
        layer.register_parameter(&quot;weight&quot;, weight)
        set_weight_attrs(weight, extra_weight_attrs)

    def _static_activation_global_sf(self) -&gt; torch.Tensor | None:
        &quot;&quot;&quot;FASTVIDEO_NVFP4_ACT_AMAX: JSON of calibrated input amax per layer (&quot;b&lt;block&gt;.&lt;sub&gt;&quot; or full prefix).&quot;&quot;&quot;
        if getattr(self, &quot;_static_sf_checked&quot;, False):
            return self._static_sf
        self._static_sf_checked, self._static_sf = True, None
        path = envs.FASTVIDEO_NVFP4_ACT_AMAX.get()
        if path:
            table = _load_amax_table(path)
            prefix = self.layer_prefix or &quot;&quot;
            match = re.search(r&quot;transformer_blocks\.(\d+)\.(.+)$&quot;, prefix)
            keys = &#91;prefix&#93; + (&#91;f&quot;b&#123;match.group(1)&#125;.&#123;match.group(2)&#125;&quot;&#93; if match else &#91;&#93;)
            amax = next((table&#91;k&#93; for k in keys if k in table), None)
            if amax is not None:
                self._static_sf = torch.tensor((448.0 * 6.0) / max(amax, 1e-12), dtype=torch.float32, device=&quot;cuda&quot;)
        return self._static_sf

    def _dynamic_activation_scale(self) -&gt; bool:
        &quot;&quot;&quot;FASTVIDEO_NVFP4_DYNAMIC_ACT: &quot;all&quot;, or comma-separated layer-name suffixes (e.g. &quot;ff.fc_out&quot;).&quot;&quot;&quot;
        cached = getattr(self, &quot;_dynamic_act_cached&quot;, None)
        if cached is None:
            selected = envs.FASTVIDEO_NVFP4_DYNAMIC_ACT.get()
            suffixes = &#91;part.strip() for part in selected.split(&quot;,&quot;) if part.strip()&#93;
            prefix = self.layer_prefix or &quot;&quot;
            cached = &quot;all&quot; in suffixes or any(prefix.endswith(suffix) for suffix in suffixes)
            self._dynamic_act_cached = cached
        return cached

    def uses_unit_activation_scale(self, layer: torch.nn.Module) -&gt; bool:
        &quot;&quot;&quot;Whether ``apply`` quantizes this layer&#x27;s input with the unit global scale.

        False when a calibrated scale (env table or the export&#x27;s ``_nvfp4_input_global_sf``) or a dynamic
        per-call scale applies; such inputs cannot share one pre-quantized copy across layers.
        &quot;&quot;&quot;
        return (self._static_activation_global_sf() is None and getattr(layer, H3_NVFP4_DIT_INPUT_SF_NAME, None) is None
                and not self._dynamic_activation_scale())

    def quantize_input(self, x: torch.Tensor) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;:
        SfLayout, _, _ = _require_flashinfer()
        x = _coerce_fp4_input_dtype(x)
        x_2d = x.view(-1, x.shape&#91;-1&#93;)
        x_fp4, x_scale = _nvfp4_quantize(
            x_2d,
            self.x_global_sf,
            sfLayout=SfLayout.layout_128x4,
            do_shuffle=False,
        )
        return x_fp4, x_scale, self.x_global_sf

    def wants_prequantized_input(self) -&gt; bool:
        if not self._is_refine_only_layer:
            return True
        stage_profile = _get_ltx2_fp4_stage_profile(default=&quot;refine&quot;)
        return stage_profile != &quot;base&quot;

    def apply(
        self,
        layer: torch.nn.Module,
        x: torch.Tensor,
        bias: torch.Tensor | None = None,
        pre_quantized: tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;
        | None = None,
    ) -&gt; torch.Tensor:
        SfLayout, _, _ = _require_flashinfer()
        # The original bf16 weight may have been purged after FP4 conversion
        # (see convert_model_to_nvfp4); the packed FP4 weight keeps the
        # output dim as its first dimension (only K is packed 2-per-byte).
        weight = getattr(layer, &quot;weight&quot;, None)
        out_dim = weight.shape&#91;0&#93; if weight is not None else layer._nvfp4_weight.shape&#91;0&#93;
        original_shape = x.shape

        # Stage-aware profile: keep refine-only FP4 layers in dense mode
        # during stage-1 denoising so the base path doesn&#x27;t pay the
        # quantize/dequantize tax for layers it never touches.
        stage_profile = _get_ltx2_fp4_stage_profile(default=&quot;refine&quot;)
        if self._is_refine_only_layer and stage_profile == &quot;base&quot;:
            if weight is None:
                raise RuntimeError(f&quot;NVFP4 layer &#123;self.layer_prefix!r&#125; hit the stage-profile dense path, &quot;
                                   &quot;but its original weights were purged &quot;
                                   &quot;(NVFP4Config(retain_original_weights=False)). Streaming/two-stage &quot;
                                   &quot;deploys must load with retain_original_weights left unset (auto) or True.&quot;)
            out = (F.linear(x, weight, bias) if torch.cuda.is_available() or bias is None else F.linear(
                x, weight, bias.to(x.dtype)))
            return out.view(*original_shape&#91;:-1&#93;, out_dim)
        if pre_quantized is not None:
            x_fp4, x_scale, x_global_sf = pre_quantized
            # FlashInfer fused norm+quant APIs may return 3D tensors for
            # 3D inputs. mm_fp4 only accepts 2D tensors, so flatten
            # batch/sequence dims here.
            if x_fp4.dim() &gt; 2:
                x_fp4 = x_fp4.view(-1, x_fp4.shape&#91;-1&#93;)
            if x_scale.dim() &gt; 2:
                x_scale = x_scale.view(-1, x_scale.shape&#91;-1&#93;)
        else:
            x = _coerce_fp4_input_dtype(x)
            x = x.view(-1, x.shape&#91;-1&#93;)
            static_sf = self._static_activation_global_sf()
            if static_sf is None:
                static_sf = getattr(layer, H3_NVFP4_DIT_INPUT_SF_NAME, None)
            if static_sf is not None:
                x_global_sf = static_sf
            elif self._dynamic_activation_scale():
                # A unit global scale caps FP8 block scales at |x| = 6 * 448; inputs such as H3&#x27;s ff.fc_out
                # (post-SwiGLU) exceed that, so derive the global scale from this call&#x27;s amax.
                x_global_sf = (448.0 * 6.0) / x.abs().amax().float().clamp(min=1e-12)
            else:
                x_global_sf = self.x_global_sf
            x_fp4, x_scale = _nvfp4_quantize(
                x,
                x_global_sf,
                sfLayout=SfLayout.layout_128x4,
                do_shuffle=False,
            )

        weight_fp4 = layer._nvfp4_weight
        weight_scale = layer._nvfp4_weight_scale
        weight_global_sf = layer._weight_global_sf

        if hasattr(layer, &quot;_nvfp4_alpha&quot;):
            alpha = layer._nvfp4_alpha / x_global_sf
        else:
            alpha = 1.0 / (x_global_sf * weight_global_sf)

        out = _mm_fp4(
            x_fp4,
            weight_fp4.T,
            x_scale,
            weight_scale.T,
            alpha,
            torch.bfloat16,
            None,
            backend=_mm_fp4_backend(),
        )

        if bias is not None:
            out = out + bias
        out = out.view(*original_shape&#91;:-1&#93;, out_dim)
        return out</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.create_weights"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.create_weights</code></h3>
<pre class="doc-signature"><code>create_weights(layer: torch.nn.Module, input_size_per_partition: int, output_partition_sizes: list&#91;int&#93;, input_size: int, output_size: int, params_dtype: torch.dtype, **extra_weight_attrs)</code></pre>

<details><summary>Source</summary><pre><code>def create_weights(self, layer: torch.nn.Module, input_size_per_partition: int, output_partition_sizes: list&#91;int&#93;,
                   input_size: int, output_size: int, params_dtype: torch.dtype, **extra_weight_attrs):
    weight = Parameter(torch.empty(
        sum(output_partition_sizes),
        input_size_per_partition,
        dtype=params_dtype,
    ),
                       requires_grad=False)
    set_weight_attrs(weight, &#123;&quot;input_dim&quot;: 1, &quot;output_dim&quot;: 0&#125;)
    layer.register_parameter(&quot;weight&quot;, weight)
    set_weight_attrs(weight, extra_weight_attrs)</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.uses_unit_activation_scale"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.uses_unit_activation_scale</code></h3>
<pre class="doc-signature"><code>uses_unit_activation_scale(layer: torch.nn.Module) -&gt; bool</code></pre>
<p>Whether <code>apply</code> quantizes this layer's input with the unit global scale.</p>
<p>False when a calibrated scale (env table or the export's <code>_nvfp4_input_global_sf</code>) or a dynamic
per-call scale applies; such inputs cannot share one pre-quantized copy across layers.</p>
<details><summary>Source</summary><pre><code>def uses_unit_activation_scale(self, layer: torch.nn.Module) -&gt; bool:
    &quot;&quot;&quot;Whether ``apply`` quantizes this layer&#x27;s input with the unit global scale.

    False when a calibrated scale (env table or the export&#x27;s ``_nvfp4_input_global_sf``) or a dynamic
    per-call scale applies; such inputs cannot share one pre-quantized copy across layers.
    &quot;&quot;&quot;
    return (self._static_activation_global_sf() is None and getattr(layer, H3_NVFP4_DIT_INPUT_SF_NAME, None) is None
            and not self._dynamic_activation_scale())</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.quantize_input"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.quantize_input</code></h3>
<pre class="doc-signature"><code>quantize_input(x: torch.Tensor) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;</code></pre>

<details><summary>Source</summary><pre><code>def quantize_input(self, x: torch.Tensor) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;:
    SfLayout, _, _ = _require_flashinfer()
    x = _coerce_fp4_input_dtype(x)
    x_2d = x.view(-1, x.shape&#91;-1&#93;)
    x_fp4, x_scale = _nvfp4_quantize(
        x_2d,
        self.x_global_sf,
        sfLayout=SfLayout.layout_128x4,
        do_shuffle=False,
    )
    return x_fp4, x_scale, self.x_global_sf</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.wants_prequantized_input"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.wants_prequantized_input</code></h3>
<pre class="doc-signature"><code>wants_prequantized_input() -&gt; bool</code></pre>

<details><summary>Source</summary><pre><code>def wants_prequantized_input(self) -&gt; bool:
    if not self._is_refine_only_layer:
        return True
    stage_profile = _get_ltx2_fp4_stage_profile(default=&quot;refine&quot;)
    return stage_profile != &quot;base&quot;</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.apply"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4QuantizeMethod.apply</code></h3>
<pre class="doc-signature"><code>apply(layer: torch.nn.Module, x: torch.Tensor, bias: torch.Tensor | None = None, pre_quantized: tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93; | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def apply(
    self,
    layer: torch.nn.Module,
    x: torch.Tensor,
    bias: torch.Tensor | None = None,
    pre_quantized: tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;
    | None = None,
) -&gt; torch.Tensor:
    SfLayout, _, _ = _require_flashinfer()
    # The original bf16 weight may have been purged after FP4 conversion
    # (see convert_model_to_nvfp4); the packed FP4 weight keeps the
    # output dim as its first dimension (only K is packed 2-per-byte).
    weight = getattr(layer, &quot;weight&quot;, None)
    out_dim = weight.shape&#91;0&#93; if weight is not None else layer._nvfp4_weight.shape&#91;0&#93;
    original_shape = x.shape

    # Stage-aware profile: keep refine-only FP4 layers in dense mode
    # during stage-1 denoising so the base path doesn&#x27;t pay the
    # quantize/dequantize tax for layers it never touches.
    stage_profile = _get_ltx2_fp4_stage_profile(default=&quot;refine&quot;)
    if self._is_refine_only_layer and stage_profile == &quot;base&quot;:
        if weight is None:
            raise RuntimeError(f&quot;NVFP4 layer &#123;self.layer_prefix!r&#125; hit the stage-profile dense path, &quot;
                               &quot;but its original weights were purged &quot;
                               &quot;(NVFP4Config(retain_original_weights=False)). Streaming/two-stage &quot;
                               &quot;deploys must load with retain_original_weights left unset (auto) or True.&quot;)
        out = (F.linear(x, weight, bias) if torch.cuda.is_available() or bias is None else F.linear(
            x, weight, bias.to(x.dtype)))
        return out.view(*original_shape&#91;:-1&#93;, out_dim)
    if pre_quantized is not None:
        x_fp4, x_scale, x_global_sf = pre_quantized
        # FlashInfer fused norm+quant APIs may return 3D tensors for
        # 3D inputs. mm_fp4 only accepts 2D tensors, so flatten
        # batch/sequence dims here.
        if x_fp4.dim() &gt; 2:
            x_fp4 = x_fp4.view(-1, x_fp4.shape&#91;-1&#93;)
        if x_scale.dim() &gt; 2:
            x_scale = x_scale.view(-1, x_scale.shape&#91;-1&#93;)
    else:
        x = _coerce_fp4_input_dtype(x)
        x = x.view(-1, x.shape&#91;-1&#93;)
        static_sf = self._static_activation_global_sf()
        if static_sf is None:
            static_sf = getattr(layer, H3_NVFP4_DIT_INPUT_SF_NAME, None)
        if static_sf is not None:
            x_global_sf = static_sf
        elif self._dynamic_activation_scale():
            # A unit global scale caps FP8 block scales at |x| = 6 * 448; inputs such as H3&#x27;s ff.fc_out
            # (post-SwiGLU) exceed that, so derive the global scale from this call&#x27;s amax.
            x_global_sf = (448.0 * 6.0) / x.abs().amax().float().clamp(min=1e-12)
        else:
            x_global_sf = self.x_global_sf
        x_fp4, x_scale = _nvfp4_quantize(
            x,
            x_global_sf,
            sfLayout=SfLayout.layout_128x4,
            do_shuffle=False,
        )

    weight_fp4 = layer._nvfp4_weight
    weight_scale = layer._nvfp4_weight_scale
    weight_global_sf = layer._weight_global_sf

    if hasattr(layer, &quot;_nvfp4_alpha&quot;):
        alpha = layer._nvfp4_alpha / x_global_sf
    else:
        alpha = 1.0 / (x_global_sf * weight_global_sf)

    out = _mm_fp4(
        x_fp4,
        weight_fp4.T,
        x_scale,
        weight_scale.T,
        alpha,
        torch.bfloat16,
        None,
        backend=_mm_fp4_backend(),
    )

    if bias is not None:
        out = out + bias
    out = out.view(*original_shape&#91;:-1&#93;, out_dim)
    return out</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config</code></h2>
<pre class="doc-signature"><code>NVFP4Config(layer_profile: str = &#x27;refine&#x27;, retain_original_weights: bool | None = None, skip_blocks: tuple&#91;int, ...&#93; | list&#91;int&#93; = ())</code></pre>
<p>Select NVFP4 for the supported LTX-2 and MiniMax-H3 linear sets.</p>
<p>NVFP4 is NVIDIA's block-scaled FP4 (e2m1 mantissa, fp32 alpha,
<code>layout_128x4</code> scale layout, group size 16). LTX-2 uses its curated
attention and FFN deployment set. MiniMax-H3's default profile uses only
<code>fc_in</code> and <code>fc_out</code> in each main transformer-block FFN.
<code>layer_profile="h3_dit"</code> expands that to the packed NVFP4H3 DiT set
(attention <code>to_&#123;q,k,v,out&#125;</code> plus those FFN linears).
<code>layer_profile="h3_dit_ffn"</code> loads a packed export of the FFN linears
only, keeping attention projections dense (e.g. calibrated FFN-only
checkpoints such as FastH3 V2 NVFP4).
<code>layer_profile="h3_dit_vsa"</code> is <code>h3_dit</code> plus each block's VSA
compression gate <code>attn.to_gate_compress</code> (VSA-distilled students).
<code>skip_blocks</code> keeps every linear of those MiniMax-H3 main blocks in
bf16 (mixed exports such as FastH3 OmniRef QAD, blocks 42-49). A packed
export records its own list, which the loader adopts when this is empty.</p>

<details><summary>Source</summary><pre><code>class NVFP4Config(QuantizationConfig):
    &quot;&quot;&quot;Select NVFP4 for the supported LTX-2 and MiniMax-H3 linear sets.

    NVFP4 is NVIDIA&#x27;s block-scaled FP4 (e2m1 mantissa, fp32 alpha,
    ``layout_128x4`` scale layout, group size 16). LTX-2 uses its curated
    attention and FFN deployment set. MiniMax-H3&#x27;s default profile uses only
    ``fc_in`` and ``fc_out`` in each main transformer-block FFN.
    ``layer_profile=&quot;h3_dit&quot;`` expands that to the packed NVFP4H3 DiT set
    (attention ``to_&#123;q,k,v,out&#125;`` plus those FFN linears).
    ``layer_profile=&quot;h3_dit_ffn&quot;`` loads a packed export of the FFN linears
    only, keeping attention projections dense (e.g. calibrated FFN-only
    checkpoints such as FastH3 V2 NVFP4).
    ``layer_profile=&quot;h3_dit_vsa&quot;`` is ``h3_dit`` plus each block&#x27;s VSA
    compression gate ``attn.to_gate_compress`` (VSA-distilled students).
    ``skip_blocks`` keeps every linear of those MiniMax-H3 main blocks in
    bf16 (mixed exports such as FastH3 OmniRef QAD, blocks 42-49). A packed
    export records its own list, which the loader adopts when this is empty.
    &quot;&quot;&quot;

    def __init__(self,
                 layer_profile: str = &quot;refine&quot;,
                 retain_original_weights: bool | None = None,
                 skip_blocks: tuple&#91;int, ...&#93; | list&#91;int&#93; = ()):
        super().__init__()
        if any(isinstance(b, bool) or not isinstance(b, int) or b &lt; 0 for b in skip_blocks):
            raise ValueError(f&quot;NVFP4Config.skip_blocks must be non-negative block indices, got &#123;skip_blocks!r&#125;&quot;)
        self.skip_blocks: tuple&#91;int, ...&#93; = tuple(sorted(set(skip_blocks)))
        if layer_profile not in (&quot;base&quot;, &quot;refine&quot;, &quot;h3_dit&quot;, &quot;h3_dit_ffn&quot;, &quot;h3_dit_vsa&quot;):
            raise ValueError(&quot;NVFP4Config.layer_profile must be one of &#x27;base&#x27;, &#x27;refine&#x27;, &#x27;h3_dit&#x27;, &quot;
                             f&quot;&#x27;h3_dit_ffn&#x27;, or &#x27;h3_dit_vsa&#x27;, got &#123;layer_profile!r&#125;&quot;)
        self.layer_profile = layer_profile
        # Original bf16 ``layer.weight`` retention after FP4 conversion.
        # Default (None/False): purge the purgeable originals -- every
        # always-FP4 layer. Refine-only layers (the cross-modal AV
        # projections) are ALWAYS retained: the ``base`` stage profile runs
        # them dense by deployment contract, including the distilled
        # single-stage deploy. True: retain everything (debugging /
        # pre-purge behavior).
        self.retain_original_weights = retain_original_weights

    def get_name(self):
        return &quot;nvfp4&quot;

    def get_supported_act_dtypes(self):
        return &#91;torch.bfloat16, torch.float16&#93;

    @classmethod
    def get_min_capability(cls):
        return 100

    @staticmethod
    def get_config_filenames():
        return &#91;&#93;

    @classmethod
    def from_config(cls, config: dict&#91;str, Any&#93;) -&gt; NVFP4Config:
        return cls(
            layer_profile=config.get(&quot;layer_profile&quot;, &quot;refine&quot;),
            retain_original_weights=config.get(&quot;retain_original_weights&quot;),
            skip_blocks=tuple(config.get(&quot;skip_blocks&quot;, ())),
        )

    def with_skip_blocks(self, skip_blocks: tuple&#91;int, ...&#93;) -&gt; NVFP4Config:
        &quot;&quot;&quot;A copy of this config with ``skip_blocks`` replaced.&quot;&quot;&quot;
        return NVFP4Config(layer_profile=self.layer_profile,
                           retain_original_weights=self.retain_original_weights,
                           skip_blocks=skip_blocks)

    def get_quant_method(self, layer: torch.nn.Module, prefix: str):
        from fastvideo.layers.linear import LinearBase

        if not isinstance(layer, LinearBase):
            return None
        if self.skip_blocks and minimax_h3_block_index(prefix) in self.skip_blocks:
            return None
        if self.layer_profile == &quot;h3_dit&quot;:
            tagged = is_minimax_h3_nvfp4_dit_linear_prefix(prefix)
        elif self.layer_profile == &quot;h3_dit_vsa&quot;:
            tagged = (is_minimax_h3_nvfp4_dit_linear_prefix(prefix)
                      or _MINIMAX_H3_NVFP4_VSA_GATE_PREFIX.search(prefix) is not None)
        elif self.layer_profile == &quot;h3_dit_ffn&quot;:
            tagged = is_minimax_h3_nvfp4_linear_prefix(prefix)
        else:
            tagged = is_ltx2_nvfp4_linear_prefix(prefix) or is_minimax_h3_nvfp4_linear_prefix(prefix)
        if tagged:
            method = NVFP4QuantizeMethod(layer_prefix=prefix)
            method._retain_original_weights = self.retain_original_weights
            return method
        if (self.layer_profile == &quot;h3_dit_ffn&quot; and envs.FASTVIDEO_H3_FP8_ATTENTION.get()
                and _H3_BLOCK_ATTN_PROJ.search(prefix) is not None):
            # Mixed precision: NVFP4 MLPs, FP8 (per-tensor weight, dynamic per-tensor activation) attention.
            from fastvideo.layers.quantization.fp8_config import FP8QuantizeMethod
            return FP8QuantizeMethod(granularity=envs.FASTVIDEO_H3_FP8_GRANULARITY.get())
        return None</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_name"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_name</code></h3>
<pre class="doc-signature"><code>get_name()</code></pre>

<details><summary>Source</summary><pre><code>def get_name(self):
    return &quot;nvfp4&quot;</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_supported_act_dtypes"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_supported_act_dtypes</code></h3>
<pre class="doc-signature"><code>get_supported_act_dtypes()</code></pre>

<details><summary>Source</summary><pre><code>def get_supported_act_dtypes(self):
    return &#91;torch.bfloat16, torch.float16&#93;</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_min_capability"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_min_capability</code></h3>
<pre class="doc-signature"><code>get_min_capability()</code></pre>

<details><summary>Source</summary><pre><code>@classmethod
def get_min_capability(cls):
    return 100</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_config_filenames"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_config_filenames</code></h3>
<pre class="doc-signature"><code>get_config_filenames()</code></pre>

<details><summary>Source</summary><pre><code>@staticmethod
def get_config_filenames():
    return &#91;&#93;</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.from_config"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.from_config</code></h3>
<pre class="doc-signature"><code>from_config(config: dict&#91;str, Any&#93;) -&gt; NVFP4Config</code></pre>

<details><summary>Source</summary><pre><code>@classmethod
def from_config(cls, config: dict&#91;str, Any&#93;) -&gt; NVFP4Config:
    return cls(
        layer_profile=config.get(&quot;layer_profile&quot;, &quot;refine&quot;),
        retain_original_weights=config.get(&quot;retain_original_weights&quot;),
        skip_blocks=tuple(config.get(&quot;skip_blocks&quot;, ())),
    )</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.with_skip_blocks"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.with_skip_blocks</code></h3>
<pre class="doc-signature"><code>with_skip_blocks(skip_blocks: tuple&#91;int, ...&#93;) -&gt; NVFP4Config</code></pre>
<p>A copy of this config with <code>skip_blocks</code> replaced.</p>
<details><summary>Source</summary><pre><code>def with_skip_blocks(self, skip_blocks: tuple&#91;int, ...&#93;) -&gt; NVFP4Config:
    &quot;&quot;&quot;A copy of this config with ``skip_blocks`` replaced.&quot;&quot;&quot;
    return NVFP4Config(layer_profile=self.layer_profile,
                       retain_original_weights=self.retain_original_weights,
                       skip_blocks=skip_blocks)</code></pre></details>
<h3 id="fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_quant_method"><code>fastvideo.layers.quantization.nvfp4_config.NVFP4Config.get_quant_method</code></h3>
<pre class="doc-signature"><code>get_quant_method(layer: torch.nn.Module, prefix: str)</code></pre>

<details><summary>Source</summary><pre><code>def get_quant_method(self, layer: torch.nn.Module, prefix: str):
    from fastvideo.layers.linear import LinearBase

    if not isinstance(layer, LinearBase):
        return None
    if self.skip_blocks and minimax_h3_block_index(prefix) in self.skip_blocks:
        return None
    if self.layer_profile == &quot;h3_dit&quot;:
        tagged = is_minimax_h3_nvfp4_dit_linear_prefix(prefix)
    elif self.layer_profile == &quot;h3_dit_vsa&quot;:
        tagged = (is_minimax_h3_nvfp4_dit_linear_prefix(prefix)
                  or _MINIMAX_H3_NVFP4_VSA_GATE_PREFIX.search(prefix) is not None)
    elif self.layer_profile == &quot;h3_dit_ffn&quot;:
        tagged = is_minimax_h3_nvfp4_linear_prefix(prefix)
    else:
        tagged = is_ltx2_nvfp4_linear_prefix(prefix) or is_minimax_h3_nvfp4_linear_prefix(prefix)
    if tagged:
        method = NVFP4QuantizeMethod(layer_prefix=prefix)
        method._retain_original_weights = self.retain_original_weights
        return method
    if (self.layer_profile == &quot;h3_dit_ffn&quot; and envs.FASTVIDEO_H3_FP8_ATTENTION.get()
            and _H3_BLOCK_ATTN_PROJ.search(prefix) is not None):
        # Mixed precision: NVFP4 MLPs, FP8 (per-tensor weight, dynamic per-tensor activation) attention.
        from fastvideo.layers.quantization.fp8_config import FP8QuantizeMethod
        return FP8QuantizeMethod(granularity=envs.FASTVIDEO_H3_FP8_GRANULARITY.get())
    return None</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.convert_model_to_nvfp4"><code>fastvideo.layers.quantization.nvfp4_config.convert_model_to_nvfp4</code></h2>
<pre class="doc-signature"><code>convert_model_to_nvfp4(model: torch.nn.Module) -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def convert_model_to_nvfp4(model: torch.nn.Module) -&gt; None:
    SfLayout, _, _ = _require_flashinfer()
    from torch.distributed.tensor import DTensor  # type: ignore

    purged = 0
    retained = 0
    purged_bytes = 0
    for mod in model.modules():
        qm = getattr(mod, &quot;quant_method&quot;, None)
        if isinstance(qm, NVFP4QuantizeMethod):
            weight = getattr(mod, &quot;weight&quot;, None)
            if weight is None:
                continue
            weight_local = weight.to_local() if isinstance(weight, DTensor) else weight  # type: ignore&#91;arg-type&#93;
            weight_global_sf = (448 * 6) / weight_local.float().abs().nan_to_num().max()
            fp4_w, fp4_s = _nvfp4_quantize(
                weight_local,
                weight_global_sf,
                sfLayout=SfLayout.layout_128x4,
                do_shuffle=False,
            )
            weight_global_sf_t = torch.as_tensor(
                weight_global_sf,
                device=weight_local.device,
                dtype=torch.float32,
            )
            mod.register_buffer(&quot;_nvfp4_weight&quot;, fp4_w, persistent=False)
            mod.register_buffer(&quot;_nvfp4_weight_scale&quot;, fp4_s, persistent=False)
            mod.register_buffer(
                &quot;_weight_global_sf&quot;,
                weight_global_sf_t.to(dtype=torch.bfloat16),
                persistent=False,
            )
            mod.register_buffer(
                &quot;_nvfp4_alpha&quot;,
                (1.0 / weight_global_sf_t).to(dtype=torch.float32),
                persistent=False,
            )

            retain_flag = getattr(qm, &quot;_retain_original_weights&quot;, None)
            # Refine-only layers are NEVER purgeable: the &quot;base&quot; stage profile
            # runs them dense by deployment contract (the distilled
            # single-stage deploy included — its forward context is the base
            # profile, so e.g. audio_to_video_attn routes dense every step).
            # retain_original_weights therefore only widens retention
            # (True = keep everything); it cannot narrow it below the
            # dense-capable set.
            retain = qm._is_refine_only_layer or retain_flag is True
            if retain:
                retained += 1
            elif isinstance(weight, DTensor):
                raise RuntimeError(&quot;NVFP4 cannot purge FSDP-sharded bf16 weights. Use a packed NVFP4 &quot;
                                   &quot;export, or convert without FSDP sharding.&quot;)
            else:
                purged_bytes += weight.numel() * weight.element_size()
                purged += 1
                mod.register_parameter(&quot;weight&quot;, None)

    if purged or retained:
        logger.info(
            &quot;NVFP4 weight purge receipt: purged %d original bf16 weight tensors &quot;
            &quot;(%.2f GiB freed); retained %d (refine-only dense fallback or &quot;
            &quot;retain_original_weights).&quot;,
            purged,
            purged_bytes / (1 &lt;&lt; 30),
            retained,
        )</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.nvfp4_linear_weight_param_names"><code>fastvideo.layers.quantization.nvfp4_config.nvfp4_linear_weight_param_names</code></h2>
<pre class="doc-signature"><code>nvfp4_linear_weight_param_names(model: torch.nn.Module) -&gt; set&#91;str&#93;</code></pre>
<p>State-dict names of <code>weight</code> on layers tagged with <code>NVFP4QuantizeMethod</code>.</p>
<details><summary>Source</summary><pre><code>def nvfp4_linear_weight_param_names(model: torch.nn.Module) -&gt; set&#91;str&#93;:
    &quot;&quot;&quot;State-dict names of ``weight`` on layers tagged with ``NVFP4QuantizeMethod``.&quot;&quot;&quot;
    names: set&#91;str&#93; = set()
    for module_name, module in model.named_modules():
        if isinstance(getattr(module, &quot;quant_method&quot;, None), NVFP4QuantizeMethod):
            names.add(f&quot;&#123;module_name&#125;.weight&quot; if module_name else &quot;weight&quot;)
    return names</code></pre></details>
<h2 id="fastvideo.layers.quantization.nvfp4_config.load_minimax_h3_nvfp4_dit_export"><code>fastvideo.layers.quantization.nvfp4_config.load_minimax_h3_nvfp4_dit_export</code></h2>
<pre class="doc-signature"><code>load_minimax_h3_nvfp4_dit_export(model: torch.nn.Module, path: str, device: torch.device | str) -&gt; int</code></pre>
<p>Load a packed NVFP4H3 DiT export onto already-tagged NVFP4 linears.</p>
<p>Keys are <code>&lt;module&gt;::&lt;buffer&gt;</code> with the four buffers
<code>convert_model_to_nvfp4</code> registers, plus an optional calibrated
<code>_nvfp4_input_global_sf</code>. Every export prefix must match an
NVFP4 linear, and every NVFP4 linear must appear in the export.</p>
<details><summary>Source</summary><pre><code>def load_minimax_h3_nvfp4_dit_export(
    model: torch.nn.Module,
    path: str,
    device: torch.device | str,
) -&gt; int:
    &quot;&quot;&quot;Load a packed NVFP4H3 DiT export onto already-tagged NVFP4 linears.

    Keys are ``&lt;module&gt;::&lt;buffer&gt;`` with the four buffers
    ``convert_model_to_nvfp4`` registers, plus an optional calibrated
    ``_nvfp4_input_global_sf``. Every export prefix must match an
    NVFP4 linear, and every NVFP4 linear must appear in the export.
    &quot;&quot;&quot;
    from safetensors import safe_open

    modules = dict(model.named_modules())
    tagged = &#123;
        name
        for name, module in modules.items() if isinstance(getattr(module, &quot;quant_method&quot;, None), NVFP4QuantizeMethod)
    &#125;
    groups: dict&#91;str, dict&#91;str, str&#93;&#93; = &#123;&#125;
    with safe_open(path, framework=&quot;pt&quot;, device=&quot;cpu&quot;) as reader:
        for key in reader.keys():  # noqa: SIM118
            if H3_NVFP4_DIT_KEY_SEP not in key:
                raise ValueError(f&quot;MiniMax-H3 NVFP4 DiT export key &#123;key!r&#125; is missing &#123;H3_NVFP4_DIT_KEY_SEP!r&#125;&quot;)
            prefix, buffer_name = key.split(H3_NVFP4_DIT_KEY_SEP, 1)
            groups.setdefault(prefix, &#123;&#125;)&#91;buffer_name&#93; = key

        loaded_names: set&#91;str&#93; = set()
        for prefix, buffers in groups.items():
            missing_buffers = &#91;name for name in H3_NVFP4_DIT_BUFFER_NAMES if name not in buffers&#93;
            if missing_buffers:
                raise ValueError(f&quot;MiniMax-H3 NVFP4 DiT export layer &#123;prefix!r&#125; is missing &#123;missing_buffers&#125;&quot;)
            extra_buffers = sorted(set(buffers) - set(H3_NVFP4_DIT_BUFFER_NAMES) - &#123;H3_NVFP4_DIT_INPUT_SF_NAME&#125;)
            if extra_buffers:
                raise ValueError(f&quot;MiniMax-H3 NVFP4 DiT export layer &#123;prefix!r&#125; has unknown buffers &#123;extra_buffers&#125;&quot;)
            module = _module_by_nvfp4_export_prefix(modules, prefix)
            if module is None:
                raise ValueError(f&quot;MiniMax-H3 NVFP4 DiT export layer &#123;prefix!r&#125; is not in the model&quot;)
            if not isinstance(getattr(module, &quot;quant_method&quot;, None), NVFP4QuantizeMethod):
                raise RuntimeError(&quot;MiniMax-H3 NVFP4 DiT export layer &quot;
                                   f&quot;&#123;prefix!r&#125; is not an NVFP4 linear; set NVFP4Config(layer_profile=&#x27;h3_dit&#x27;)&quot;)
            for buffer_name in H3_NVFP4_DIT_BUFFER_NAMES + (H3_NVFP4_DIT_INPUT_SF_NAME, ):
                if buffer_name not in buffers:
                    continue
                tensor = reader.get_tensor(buffers&#91;buffer_name&#93;).to(device=device)
                module.register_buffer(buffer_name, tensor, persistent=False)
            module.register_parameter(&quot;weight&quot;, None)
            loaded_names.add(next(name for name, candidate in modules.items() if candidate is module))

    missing_layers = tagged - loaded_names
    extra_layers = loaded_names - tagged
    if missing_layers or extra_layers:
        raise RuntimeError(&quot;MiniMax-H3 NVFP4 DiT export does not cover the tagged linear set; &quot;
                           f&quot;missing=&#123;sorted(missing_layers)&#91;:8&#93;&#125; extra=&#123;sorted(extra_layers)&#91;:8&#93;&#125;&quot;)
    logger.info(&quot;Loaded MiniMax-H3 NVFP4 DiT export: %d linears from %s&quot;, len(loaded_names), path)
    return len(loaded_names)</code></pre></details>
</div>
