spark_model/quant_format/modelopt.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! NVIDIA TensorRT ModelOpt NVFP4 serialization.
4//!
5//! ModelOpt exports are emitted by NVIDIA's `nvidia-modelopt` toolkit and
6//! appear on HuggingFace under `nvidia/*` and downstream community
7//! re-quants (`lukealonso/*`, `saricles/*`, `NVIDIA/*`). The tensor-name
8//! convention is distinct from compressed-tensors:
9//!
10//! | field | tensor name | dtype |
11//! | -------------------- | -------------------- | ------------ |
12//! | packed FP4 payload | `.weight` | uint8 packed |
13//! | per-group FP8 scales | `.weight_scale` | float8_e4m3 |
14//! | per-tensor scalar | `.weight_scale_2` | f32 scalar |
15//! | activation scale | `.input_scale` | f32 scalar |
16//!
17//! Unquantized modules (typically `lm_head`, embeddings, and the full
18//! attention tower on smaller re-quants) are declared in the top-level
19//! `ignore` array of `hf_quant_config.json` / `config.json`'s
20//! `quantization_config` block. Those modules ship as plain BF16 and
21//! must NOT be run through the NVFP4 loader — reading uint8-packed FP4
22//! at BF16 stride is a 4× byte overrun that lands as
23//! `CUDA_ERROR_ILLEGAL_ADDRESS` later on, which is the bug this module
24//! was written to eliminate.
25//!
26//! This maps to the existing [`Nvfp4Variant::Standard`] dispatch in
27//! `weight_map.rs` (which was always the ModelOpt path — the misleading
28//! name predates support for the compressed-tensors split).
29
30use crate::quant_format::{QuantFormat, module_matches_pattern};
31use crate::weight_map::Nvfp4Variant;
32
33/// ModelOpt-style NVFP4 checkpoint.
34#[derive(Debug)]
35pub struct ModeloptFormat {
36 /// `quant_algo` declared in config (`"NVFP4"`, `"FP8"`, …). Used for
37 /// diagnostic logging — the actual dispatch uses `base_variant`.
38 pub algo: String,
39 /// Module-path globs to load as dense BF16 rather than NVFP4.
40 pub ignore_modules: Vec<String>,
41}
42
43impl ModeloptFormat {
44 pub fn new(algo: String, ignore_modules: Vec<String>) -> Self {
45 Self {
46 algo,
47 ignore_modules,
48 }
49 }
50}
51
52impl QuantFormat for ModeloptFormat {
53 fn name(&self) -> &'static str {
54 "modelopt"
55 }
56
57 fn base_variant(&self) -> Nvfp4Variant {
58 // `Standard` is the legacy name for ModelOpt NVFP4 layout.
59 Nvfp4Variant::Standard
60 }
61
62 fn is_ignored(&self, module_path: &str) -> bool {
63 self.ignore_modules
64 .iter()
65 .any(|pat| module_matches_pattern(module_path, pat))
66 }
67}