fn apr_qtype_to_dtype(qtype: u32) -> Result<&'static str> {
crate::gguf::GgmlQuantType::from_id(qtype)
.map(crate::gguf::GgmlQuantType::as_str)
.ok_or_else(|| RealizarError::FormatError {
reason: format!(
"unknown GGML quant type {qtype}: refusing to write it to APR as F32, \
which would emit quantized bytes that the reader decodes as raw floats"
),
})
}
#[inline]
#[must_use]
pub(crate) fn gpu_unsupported_quant_qtype(qtype: u32) -> bool {
!matches!(qtype, 0 | 2 | 3 | 6 | 8 | 12 | 13 | 14)
}
pub(crate) const HYBRID_GPU_PROJECTIONS: [&str; 12] = [
"attn_qkv",
"ssm_alpha",
"ssm_beta",
"attn_gate",
"ssm_out",
"attn_q",
"attn_k",
"attn_v",
"attn_output",
"ffn_gate",
"ffn_up",
"ffn_down",
];
fn is_hybrid_gpu_projection(name: &str) -> bool {
name.split('.')
.any(|part| HYBRID_GPU_PROJECTIONS.contains(&part))
}
pub(crate) fn hybrid_gpu_unsupported_quant_tensor<'n>(
tensors: impl IntoIterator<Item = (&'n str, u32)>,
) -> Option<(String, u32)> {
tensors
.into_iter()
.find(|&(name, qtype)| is_hybrid_gpu_projection(name) && gpu_unsupported_quant_qtype(qtype))
.map(|(name, qtype)| (name.to_string(), qtype))
}
#[cfg(test)]
mod hybrid_gpu_unsupported_quant_tests {
use super::{hybrid_gpu_unsupported_quant_tensor, is_hybrid_gpu_projection};
#[test]
fn every_deltanet_projection_is_gpu_unsupported_quant_checked() {
for stem in ["attn_qkv", "ssm_alpha", "ssm_beta", "attn_gate", "ssm_out"] {
let name = format!("blk.7.{stem}.weight");
assert_eq!(
hybrid_gpu_unsupported_quant_tensor([(name.as_str(), 10u32)]),
Some((name.clone(), 10)),
"{stem}: Q2_K has no GPU GEMV kernel"
);
assert_eq!(
hybrid_gpu_unsupported_quant_tensor([(name.as_str(), 12u32)]),
None,
"{stem}: Q4_K does have one"
);
}
}
#[test]
fn non_projection_tensors_are_not_gpu_unsupported_quant_vetoes() {
for name in [
"blk.0.ssm_conv1d.weight",
"blk.0.ssm_a",
"blk.0.ssm_dt_bias",
"blk.0.ssm_norm.weight",
"blk.0.attn_norm.weight",
"token_embd.weight",
] {
assert!(
!is_hybrid_gpu_projection(name),
"{name} is not a GEMV projection"
);
assert_eq!(hybrid_gpu_unsupported_quant_tensor([(name, 11u32)]), None);
}
}
#[test]
fn gpu_unsupported_quant_stems_match_whole_name_parts_only() {
assert!(is_hybrid_gpu_projection("blk.0.attn_qkv.weight"));
assert!(is_hybrid_gpu_projection("blk.0.attn_q.weight"));
assert!(!is_hybrid_gpu_projection("blk.0.attn_q_norm.weight"));
assert!(!is_hybrid_gpu_projection("blk.0.ssm_outer.weight"));
}
#[test]
fn gpu_unsupported_quant_reports_the_first_offender() {
assert_eq!(
hybrid_gpu_unsupported_quant_tensor([
("blk.0.attn_qkv.weight", 8u32),
("blk.0.ssm_beta.weight", 15),
("blk.1.ssm_out.weight", 7),
]),
Some(("blk.0.ssm_beta.weight".to_string(), 15)),
"Q8_K (15) is the first tensor without a kernel"
);
assert_eq!(
hybrid_gpu_unsupported_quant_tensor([
("blk.0.attn_qkv.weight", 8u32),
("blk.0.ssm_beta.weight", 13),
("blk.0.ffn_down.weight", 14),
("output.weight", 12),
]),
None
);
assert_eq!(
hybrid_gpu_unsupported_quant_tensor(Vec::<(&str, u32)>::new()),
None
);
}
}
fn apr_dtype_to_byte(dtype: &str) -> u8 {
crate::gguf::GgmlQuantType::from_str_lossy(dtype).map_or_else(
|| {
eprintln!(
"WARN: Unknown dtype '{}' in dtype_to_byte, writing as F32",
dtype
);
0
},
crate::gguf::GgmlQuantType::as_byte,
)
}
fn write_apr_tensor_entry(
name: &str,
dtype: &str,
shape: &[usize],
offset: u64,
size: u64,
) -> Vec<u8> {
let mut entry = Vec::new();
let name_bytes = name.as_bytes();
entry.extend_from_slice(&(name_bytes.len() as u16).to_le_bytes());
entry.extend_from_slice(name_bytes);
entry.push(apr_dtype_to_byte(dtype));
entry.push(shape.len() as u8);
for &dim in shape {
entry.extend_from_slice(&(dim as u64).to_le_bytes());
}
entry.extend_from_slice(&offset.to_le_bytes());
entry.extend_from_slice(&size.to_le_bytes());
entry
}
impl OwnedQuantizedModel {
#[must_use]
pub(crate) fn has_gpu_unsupported_quant(&self) -> bool {
if gpu_unsupported_quant_qtype(self.lm_head_weight.qtype) {
return true;
}
self.layers.iter().any(|l| {
let qkv_bad = match &l.qkv_weight {
OwnedQKVWeights::Fused(t) => gpu_unsupported_quant_qtype(t.qtype),
OwnedQKVWeights::Separate { q, k, v } => {
gpu_unsupported_quant_qtype(q.qtype)
|| gpu_unsupported_quant_qtype(k.qtype)
|| gpu_unsupported_quant_qtype(v.qtype)
},
};
qkv_bad
|| gpu_unsupported_quant_qtype(l.attn_output_weight.qtype)
|| gpu_unsupported_quant_qtype(l.ffn_up_weight.qtype)
|| gpu_unsupported_quant_qtype(l.ffn_down_weight.qtype)
|| l.ffn_gate_weight
.as_ref()
.is_some_and(|g| gpu_unsupported_quant_qtype(g.qtype))
})
}
#[allow(clippy::disallowed_methods)]
#[allow(clippy::cast_possible_truncation)]
pub fn to_apr_bytes(&self) -> Result<Vec<u8>> {
use crate::apr::{ALIGNMENT, HEADER_SIZE, MAGIC};
let tensors = self.collect_apr_model_tensors()?;
let metadata = serde_json::json!({
"model_type": "transformer_lm",
"architecture": self.config.architecture,
"vocab_size": self.config.vocab_size,
"hidden_size": self.config.hidden_dim,
"num_layers": self.config.num_layers,
"num_heads": self.config.num_heads,
"num_kv_heads": self.config.num_kv_heads,
"intermediate_size": self.config.intermediate_dim,
"rms_norm_eps": self.config.eps,
"rope_theta": self.config.rope_theta,
"context_length": self.config.context_length,
});
let metadata_bytes =
serde_json::to_vec(&metadata).map_err(|e| RealizarError::FormatError {
reason: format!("Failed to serialize metadata: {e}"),
})?;
let metadata_padded_len = metadata_bytes.len().div_ceil(ALIGNMENT) * ALIGNMENT;
let mut tensor_index_bytes: Vec<u8> = Vec::new();
let mut tensor_data_bytes: Vec<u8> = Vec::new();
for (name, dtype, shape, data) in &tensors {
let padding = (ALIGNMENT - (tensor_data_bytes.len() % ALIGNMENT)) % ALIGNMENT;
tensor_data_bytes.extend(std::iter::repeat_n(0u8, padding));
let offset = tensor_data_bytes.len() as u64;
let size = data.len() as u64;
tensor_index_bytes.extend(write_apr_tensor_entry(name, dtype, shape, offset, size));
tensor_data_bytes.extend_from_slice(data);
}
let metadata_offset = HEADER_SIZE as u64;
let tensor_index_offset = metadata_offset + metadata_padded_len as u64;
let data_offset = tensor_index_offset + tensor_index_bytes.len() as u64;
let mut header = vec![0u8; HEADER_SIZE];
header[0..4].copy_from_slice(&MAGIC);
header[4] = 2; header[5] = 0; header[6..8].copy_from_slice(&0u16.to_le_bytes()); header[8..12].copy_from_slice(&(tensors.len() as u32).to_le_bytes());
header[12..20].copy_from_slice(&metadata_offset.to_le_bytes());
header[20..24].copy_from_slice(&(metadata_bytes.len() as u32).to_le_bytes());
header[24..32].copy_from_slice(&tensor_index_offset.to_le_bytes());
header[32..40].copy_from_slice(&data_offset.to_le_bytes());
let total_size =
HEADER_SIZE + metadata_padded_len + tensor_index_bytes.len() + tensor_data_bytes.len();
let mut result = Vec::with_capacity(total_size);
result.extend_from_slice(&header);
result.extend_from_slice(&metadata_bytes);
result.resize(HEADER_SIZE + metadata_padded_len, 0); result.extend_from_slice(&tensor_index_bytes);
result.extend_from_slice(&tensor_data_bytes);
Ok(result)
}
#[allow(clippy::cast_possible_truncation)]
fn collect_apr_model_tensors(&self) -> Result<Vec<(String, String, Vec<usize>, Vec<u8>)>> {
let mut tensors = Vec::new();
let embed_bytes: Vec<u8> = self
.token_embedding
.iter()
.flat_map(|f| f.to_le_bytes())
.collect();
tensors.push((
"token_embd.weight".to_string(),
"F32".to_string(),
vec![self.config.vocab_size, self.config.hidden_dim],
embed_bytes,
));
let head_dim = self.config.head_dim();
let kv_dim = self.config.num_kv_heads * head_dim;
for (layer_idx, layer) in self.layers.iter().enumerate() {
self.collect_apr_layer_tensors(&mut tensors, layer_idx, layer, kv_dim)?;
}
let output_norm_bytes: Vec<u8> = self
.output_norm_weight
.iter()
.flat_map(|f| f.to_le_bytes())
.collect();
tensors.push((
"output_norm.weight".to_string(),
"F32".to_string(),
vec![self.config.hidden_dim],
output_norm_bytes,
));
tensors.push((
"output.weight".to_string(),
apr_qtype_to_dtype(self.lm_head_weight.qtype)?.to_string(),
vec![self.config.vocab_size, self.config.hidden_dim],
self.lm_head_weight.data.clone(),
));
Ok(tensors)
}
fn collect_apr_layer_tensors(
&self,
tensors: &mut Vec<(String, String, Vec<usize>, Vec<u8>)>,
layer_idx: usize,
layer: &OwnedQuantizedLayer,
kv_dim: usize,
) -> Result<()> {
let norm_bytes: Vec<u8> = layer
.attn_norm_weight
.iter()
.flat_map(|f| f.to_le_bytes())
.collect();
tensors.push((
format!("blk.{layer_idx}.attn_norm.weight"),
"F32".to_string(),
vec![self.config.hidden_dim],
norm_bytes,
));
match &layer.qkv_weight {
OwnedQKVWeights::Separate { q, k, v } => {
tensors.push((
format!("blk.{layer_idx}.attn_q.weight"),
apr_qtype_to_dtype(q.qtype)?.to_string(),
vec![self.config.hidden_dim, self.config.hidden_dim],
q.data.clone(),
));
tensors.push((
format!("blk.{layer_idx}.attn_k.weight"),
apr_qtype_to_dtype(k.qtype)?.to_string(),
vec![kv_dim, self.config.hidden_dim],
k.data.clone(),
));
tensors.push((
format!("blk.{layer_idx}.attn_v.weight"),
apr_qtype_to_dtype(v.qtype)?.to_string(),
vec![kv_dim, self.config.hidden_dim],
v.data.clone(),
));
},
OwnedQKVWeights::Fused(t) => {
tensors.push((
format!("blk.{layer_idx}.attn_qkv.weight"),
apr_qtype_to_dtype(t.qtype)?.to_string(),
vec![t.out_dim, t.in_dim],
t.data.clone(),
));
},
}
tensors.push((
format!("blk.{layer_idx}.attn_output.weight"),
apr_qtype_to_dtype(layer.attn_output_weight.qtype)?.to_string(),
vec![self.config.hidden_dim, self.config.hidden_dim],
layer.attn_output_weight.data.clone(),
));
if let Some(ref ffn_norm) = layer.ffn_norm_weight {
let norm_bytes: Vec<u8> = ffn_norm.iter().flat_map(|f| f.to_le_bytes()).collect();
tensors.push((
format!("blk.{layer_idx}.ffn_norm.weight"),
"F32".to_string(),
vec![self.config.hidden_dim],
norm_bytes,
));
}
if let Some(ref gate) = layer.ffn_gate_weight {
tensors.push((
format!("blk.{layer_idx}.ffn_gate.weight"),
apr_qtype_to_dtype(gate.qtype)?.to_string(),
vec![self.config.intermediate_dim, self.config.hidden_dim],
gate.data.clone(),
));
}
tensors.push((
format!("blk.{layer_idx}.ffn_up.weight"),
apr_qtype_to_dtype(layer.ffn_up_weight.qtype)?.to_string(),
vec![self.config.intermediate_dim, self.config.hidden_dim],
layer.ffn_up_weight.data.clone(),
));
tensors.push((
format!("blk.{layer_idx}.ffn_down.weight"),
apr_qtype_to_dtype(layer.ffn_down_weight.qtype)?.to_string(),
vec![self.config.hidden_dim, self.config.intermediate_dim],
layer.ffn_down_weight.data.clone(),
));
Ok(())
}
}
include!("embedding.rs");
include!("loader_apr_quantized.rs");