use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GgmlDType {
F32,
F16,
BF16,
Q4_0,
Q4_1,
Q5_0,
Q5_1,
Q8_0,
Q8_1,
Q2K,
Q3K,
Q4K,
Q5K,
Q6K,
Q8K,
Iq4Nl,
Iq4Xs,
Iq2Xxs,
Iq2Xs,
Iq3Xxs,
Iq1S,
Iq3S,
Iq2S,
Iq1M,
Tq1_0,
Tq2_0,
Mxfp4,
Nvfp4,
}
impl GgmlDType {
pub fn from_u32(u: u32) -> Result<Self> {
let dtype = match u {
0 => Self::F32,
1 => Self::F16,
2 => Self::Q4_0,
3 => Self::Q4_1,
6 => Self::Q5_0,
7 => Self::Q5_1,
8 => Self::Q8_0,
9 => Self::Q8_1,
10 => Self::Q2K,
11 => Self::Q3K,
12 => Self::Q4K,
13 => Self::Q5K,
14 => Self::Q6K,
15 => Self::Q8K,
20 => Self::Iq4Nl,
23 => Self::Iq4Xs,
16 => Self::Iq2Xxs,
17 => Self::Iq2Xs,
18 => Self::Iq3Xxs,
19 => Self::Iq1S,
21 => Self::Iq3S,
22 => Self::Iq2S,
29 => Self::Iq1M,
34 => Self::Tq1_0,
35 => Self::Tq2_0,
39 => Self::Mxfp4,
40 => Self::Nvfp4,
30 => Self::BF16,
_ => return Err(crate::Error::Msg(format!("unknown dtype for tensor {u}")).bt()),
};
Ok(dtype)
}
pub fn to_u32(self) -> u32 {
match self {
Self::F32 => 0,
Self::F16 => 1,
Self::Q4_0 => 2,
Self::Q4_1 => 3,
Self::Q5_0 => 6,
Self::Q5_1 => 7,
Self::Q8_0 => 8,
Self::Q8_1 => 9,
Self::Q2K => 10,
Self::Q3K => 11,
Self::Q4K => 12,
Self::Q5K => 13,
Self::Q6K => 14,
Self::Q8K => 15,
Self::Iq4Nl => 20,
Self::Iq4Xs => 23,
Self::Iq2Xxs => 16,
Self::Iq2Xs => 17,
Self::Iq3Xxs => 18,
Self::Iq1S => 19,
Self::Iq3S => 21,
Self::Iq2S => 22,
Self::Iq1M => 29,
Self::Tq1_0 => 34,
Self::Tq2_0 => 35,
Self::Mxfp4 => 39,
Self::Nvfp4 => 40,
Self::BF16 => 30,
}
}
pub fn type_size(&self) -> usize {
match self {
Self::F32 => 4,
Self::F16 | Self::BF16 => 2,
Self::Q4_0 => 18, Self::Q4_1 => 20, Self::Q5_0 => 22, Self::Q5_1 => 24, Self::Q8_0 => 34, Self::Q8_1 => 36, Self::Q2K => 84, Self::Q3K => 110, Self::Q4K => 144, Self::Q5K => 176, Self::Q6K => 210, Self::Q8K => 292, Self::Iq4Nl => 18, Self::Iq4Xs => 136, Self::Iq2Xxs => 66,
Self::Iq2Xs => 74,
Self::Iq3Xxs => 98,
Self::Iq1S => 50,
Self::Iq3S => 110,
Self::Iq2S => 82,
Self::Iq1M => 56,
Self::Tq1_0 => 54, Self::Tq2_0 => 66, Self::Mxfp4 => 17, Self::Nvfp4 => 36, }
}
pub fn block_size(&self) -> usize {
match self {
Self::F32 | Self::F16 | Self::BF16 => 1,
Self::Q4_0
| Self::Q4_1
| Self::Q5_0
| Self::Q5_1
| Self::Q8_0
| Self::Q8_1
| Self::Iq4Nl => 32,
Self::Q2K
| Self::Q3K
| Self::Q4K
| Self::Q5K
| Self::Q6K
| Self::Q8K
| Self::Iq4Xs
| Self::Iq2Xxs
| Self::Iq2Xs
| Self::Iq3Xxs
| Self::Iq1S
| Self::Iq3S
| Self::Iq2S
| Self::Iq1M
| Self::Tq1_0
| Self::Tq2_0 => 256,
Self::Mxfp4 => 32,
Self::Nvfp4 => 64,
}
}
pub fn supports_quantize(self) -> bool {
!matches!(
self,
Self::Iq2Xxs
| Self::Iq2Xs
| Self::Iq2S
| Self::Iq3Xxs
| Self::Iq3S
| Self::Iq1S
| Self::Iq1M
| Self::Tq1_0
| Self::Tq2_0
| Self::Mxfp4
| Self::Nvfp4
)
}
pub fn check_quantize(self) -> Result<()> {
if self.supports_quantize() {
Ok(())
} else {
Err(crate::Error::Msg(format!(
"cannot quantize to {self:?}: Fuel decodes this format but has no quantizer for it (GAP-211 B2/C)"
))
.bt())
}
}
pub fn check_dequant_count(self, op_label: &str, numel: usize, byte_len: usize) -> Result<()> {
let block = self.block_size();
if !numel.is_multiple_of(block) {
return Err(crate::Error::Msg(format!(
"{op_label}: element count {numel} is not a multiple of {self:?}'s block size {block}",
))
.bt());
}
match (numel / block).checked_mul(self.type_size()) {
Some(need) if need <= byte_len => Ok(()),
Some(need) => Err(crate::Error::Msg(format!(
"{op_label}: {numel} elements need {need} bytes of {self:?} blocks, but the \
buffer holds {byte_len}",
))
.bt()),
None => Err(crate::Error::Msg(format!(
"{op_label}: {numel} {self:?} elements overflow a byte count"
))
.bt()),
}
}
}
#[cfg(test)]
mod tests {
use super::GgmlDType;
#[track_caller]
fn declined(dtype: GgmlDType, numel: usize, byte_len: usize, needle: &str) {
let msg = dtype
.check_dequant_count("t", numel, byte_len)
.expect_err("expected a decline")
.to_string();
assert!(msg.contains(needle), "wrong decline: {msg}");
}
#[test]
fn whole_blocks_that_fit_are_accepted() {
GgmlDType::Q4_0.check_dequant_count("t", 64, 36).unwrap();
GgmlDType::Q4K.check_dequant_count("t", 256, 144).unwrap();
GgmlDType::F32.check_dequant_count("t", 3, 12).unwrap();
GgmlDType::Q4_0.check_dequant_count("t", 0, 0).unwrap();
}
#[test]
fn a_partial_block_is_declined() {
declined(
GgmlDType::Q4_0,
65,
1_000,
"element count 65 is not a multiple of Q4_0's block size 32",
);
declined(
GgmlDType::Q4K,
128,
1_000,
"element count 128 is not a multiple of Q4K's block size 256",
);
}
#[test]
fn blocks_one_byte_past_the_buffer_are_declined() {
declined(
GgmlDType::Q4_0,
64,
35,
"64 elements need 36 bytes of Q4_0 blocks, but the buffer holds 35",
);
declined(
GgmlDType::F16,
3,
5,
"3 elements need 6 bytes of F16 blocks",
);
GgmlDType::F16.check_dequant_count("t", 3, 6).unwrap();
}
#[test]
fn an_overflowing_count_is_a_typed_error() {
declined(
GgmlDType::F32,
usize::MAX,
usize::MAX,
"overflow a byte count",
);
}
}