#[test]
fn test_phase35_transformer_from_minimal_llama() {
let data = build_minimal_llama_gguf(
100, 64, 128, 4, 4, );
let model = GGUFModel::from_bytes(&data).expect("Should parse minimal LLaMA GGUF");
let transformer =
QuantizedGGUFTransformer::from_gguf(&model, &data).expect("Should load transformer");
assert_eq!(transformer.config.architecture, "llama");
assert_eq!(transformer.config.hidden_dim, 64);
assert_eq!(transformer.config.num_layers, 1);
assert_eq!(transformer.config.num_heads, 4);
assert_eq!(transformer.config.num_kv_heads, 4);
assert_eq!(transformer.token_embedding.len(), 100 * 64);
assert_eq!(transformer.output_norm_weight.len(), 64);
assert_eq!(transformer.layers.len(), 1);
let layer = &transformer.layers[0];
assert_eq!(layer.attn_norm_weight.len(), 64);
match &layer.qkv_weight {
QKVWeights::Separate { q, k, v } => {
assert_eq!(q.qtype, GGUF_TYPE_Q4_K);
assert_eq!(k.qtype, GGUF_TYPE_Q4_K);
assert_eq!(v.qtype, GGUF_TYPE_Q4_K);
assert_eq!(q.num_elements, 64 * 64);
assert_eq!(k.num_elements, 64 * 64);
assert_eq!(v.num_elements, 64 * 64);
},
QKVWeights::Fused(_) => panic!("Expected Separate QKV for LLaMA style"),
}
assert_eq!(layer.ffn_up_weight.qtype, GGUF_TYPE_Q4_K);
assert_eq!(layer.ffn_down_weight.qtype, GGUF_TYPE_Q4_K);
assert!(layer.ffn_gate_weight.is_some(), "LLaMA should have gate");
}
#[test]
fn test_phase35_transformer_from_minimal_phi2() {
let data = build_minimal_phi2_gguf(
100, 64, 128, 4, );
let model = GGUFModel::from_bytes(&data).expect("Should parse minimal Phi-2 GGUF");
let transformer =
QuantizedGGUFTransformer::from_gguf(&model, &data).expect("Should load transformer");
assert_eq!(transformer.config.architecture, "phi2");
assert_eq!(transformer.config.hidden_dim, 64);
assert_eq!(transformer.config.num_layers, 1);
assert_eq!(transformer.config.num_heads, 4);
let layer = &transformer.layers[0];
match &layer.qkv_weight {
QKVWeights::Fused(fused) => {
assert_eq!(fused.qtype, GGUF_TYPE_Q4_K);
assert_eq!(fused.num_elements, 64 * (3 * 64));
},
QKVWeights::Separate { .. } => panic!("Expected Fused QKV for Phi-2 style"),
}
assert!(layer.ffn_gate_weight.is_none());
}
#[test]
fn test_phase35_transformer_with_gqa() {
let hidden_dim = 64usize;
let num_heads = 8usize;
let num_kv_heads = 2usize; let head_dim = hidden_dim / num_heads; let kv_dim = num_kv_heads * head_dim; let vocab_size = 100usize;
let intermediate_dim = 128usize;
let embed_data = create_f32_embedding_data(vocab_size, hidden_dim);
let norm_data = create_f32_norm_weights(hidden_dim);
let q_data = create_q4_k_data(hidden_dim * hidden_dim);
let k_data = create_q4_k_data(hidden_dim * kv_dim);
let v_data = create_q4_k_data(hidden_dim * kv_dim);
let attn_out_data = create_q4_k_data(hidden_dim * hidden_dim);
let ffn_up_data = create_q4_k_data(hidden_dim * intermediate_dim);
let ffn_down_data = create_q4_k_data(intermediate_dim * hidden_dim);
let ffn_gate_data = create_q4_k_data(hidden_dim * intermediate_dim);
let data = GGUFBuilder::new()
.architecture("llama")
.hidden_dim("llama", hidden_dim as u32)
.num_layers("llama", 1)
.num_heads("llama", num_heads as u32)
.num_kv_heads("llama", num_kv_heads as u32)
.context_length("llama", 256)
.rope_freq_base("llama", 10000.0)
.rms_epsilon("llama", 1e-5)
.ffn_hidden_dim("llama", intermediate_dim as u32)
.add_f32_tensor(
"token_embd.weight",
&[vocab_size as u64, hidden_dim as u64],
&embed_data,
)
.add_f32_tensor("blk.0.attn_norm.weight", &[hidden_dim as u64], &norm_data)
.add_q4_k_tensor(
"blk.0.attn_q.weight",
&[hidden_dim as u64, hidden_dim as u64],
&q_data,
)
.add_q4_k_tensor(
"blk.0.attn_k.weight",
&[hidden_dim as u64, kv_dim as u64],
&k_data,
)
.add_q4_k_tensor(
"blk.0.attn_v.weight",
&[hidden_dim as u64, kv_dim as u64],
&v_data,
)
.add_q4_k_tensor(
"blk.0.attn_output.weight",
&[hidden_dim as u64, hidden_dim as u64],
&attn_out_data,
)
.add_f32_tensor("blk.0.ffn_norm.weight", &[hidden_dim as u64], &norm_data)
.add_q4_k_tensor(
"blk.0.ffn_up.weight",
&[hidden_dim as u64, intermediate_dim as u64],
&ffn_up_data,
)
.add_q4_k_tensor(
"blk.0.ffn_down.weight",
&[intermediate_dim as u64, hidden_dim as u64],
&ffn_down_data,
)
.add_q4_k_tensor(
"blk.0.ffn_gate.weight",
&[hidden_dim as u64, intermediate_dim as u64],
&ffn_gate_data,
)
.add_f32_tensor("output_norm.weight", &[hidden_dim as u64], &norm_data)
.build();
let model = GGUFModel::from_bytes(&data).expect("Should parse GQA model");
let transformer =
QuantizedGGUFTransformer::from_gguf(&model, &data).expect("Should load GQA transformer");
assert_eq!(transformer.config.num_heads, 8);
assert_eq!(transformer.config.num_kv_heads, 2);
match &transformer.layers[0].qkv_weight {
QKVWeights::Separate { q, k, v } => {
assert_eq!(q.num_elements, 64 * 64); assert_eq!(k.num_elements, 64 * 16); assert_eq!(v.num_elements, 64 * 16); },
_ => panic!("Expected Separate"),
}
}
#[test]
fn test_phase35_transformer_multiple_layers() {
let hidden_dim = 64usize;
let intermediate_dim = 128usize;
let vocab_size = 100usize;
let embed_data = create_f32_embedding_data(vocab_size, hidden_dim);
let norm_data = create_f32_norm_weights(hidden_dim);
let q_data = create_q4_k_data(hidden_dim * hidden_dim);
let k_data = create_q4_k_data(hidden_dim * hidden_dim);
let v_data = create_q4_k_data(hidden_dim * hidden_dim);
let attn_out_data = create_q4_k_data(hidden_dim * hidden_dim);
let ffn_up_data = create_q4_k_data(hidden_dim * intermediate_dim);
let ffn_down_data = create_q4_k_data(intermediate_dim * hidden_dim);
let ffn_gate_data = create_q4_k_data(hidden_dim * intermediate_dim);
let mut builder = GGUFBuilder::new()
.architecture("llama")
.hidden_dim("llama", hidden_dim as u32)
.num_layers("llama", 2) .num_heads("llama", 4)
.num_kv_heads("llama", 4)
.context_length("llama", 256)
.rope_freq_base("llama", 10000.0)
.rms_epsilon("llama", 1e-5)
.ffn_hidden_dim("llama", intermediate_dim as u32)
.add_f32_tensor(
"token_embd.weight",
&[vocab_size as u64, hidden_dim as u64],
&embed_data,
);
for layer_idx in 0..2 {
builder = builder
.add_f32_tensor(
&format!("blk.{}.attn_norm.weight", layer_idx),
&[hidden_dim as u64],
&norm_data,
)
.add_q4_k_tensor(
&format!("blk.{}.attn_q.weight", layer_idx),
&[hidden_dim as u64, hidden_dim as u64],
&q_data,
)
.add_q4_k_tensor(
&format!("blk.{}.attn_k.weight", layer_idx),
&[hidden_dim as u64, hidden_dim as u64],
&k_data,
)
.add_q4_k_tensor(
&format!("blk.{}.attn_v.weight", layer_idx),
&[hidden_dim as u64, hidden_dim as u64],
&v_data,
)
.add_q4_k_tensor(
&format!("blk.{}.attn_output.weight", layer_idx),
&[hidden_dim as u64, hidden_dim as u64],
&attn_out_data,
)
.add_f32_tensor(
&format!("blk.{}.ffn_norm.weight", layer_idx),
&[hidden_dim as u64],
&norm_data,
)
.add_q4_k_tensor(
&format!("blk.{}.ffn_up.weight", layer_idx),
&[hidden_dim as u64, intermediate_dim as u64],
&ffn_up_data,
)
.add_q4_k_tensor(
&format!("blk.{}.ffn_down.weight", layer_idx),
&[intermediate_dim as u64, hidden_dim as u64],
&ffn_down_data,
)
.add_q4_k_tensor(
&format!("blk.{}.ffn_gate.weight", layer_idx),
&[hidden_dim as u64, intermediate_dim as u64],
&ffn_gate_data,
);
}
let data = builder
.add_f32_tensor("output_norm.weight", &[hidden_dim as u64], &norm_data)
.build();
let model = GGUFModel::from_bytes(&data).expect("Should parse multi-layer model");
let transformer =
QuantizedGGUFTransformer::from_gguf(&model, &data).expect("Should load multi-layer");
assert_eq!(transformer.config.num_layers, 2);
assert_eq!(transformer.layers.len(), 2);
for (idx, layer) in transformer.layers.iter().enumerate() {
assert_eq!(layer.attn_norm_weight.len(), 64, "Layer {} norm", idx);
assert!(layer.ffn_gate_weight.is_some(), "Layer {} gate", idx);
}
}
#[test]
fn test_phase35_get_tensor_ref_q4_0() {
let hidden_dim = 64usize;
let vocab_size = 100usize;
let embed_data = create_f32_embedding_data(vocab_size, hidden_dim);
let norm_data = create_f32_norm_weights(hidden_dim);
let q4_0_data = create_q4_0_data(hidden_dim * hidden_dim);
let data = GGUFBuilder::new()
.architecture("llama")
.hidden_dim("llama", hidden_dim as u32)
.num_layers("llama", 1)
.num_heads("llama", 4)
.num_kv_heads("llama", 4)
.context_length("llama", 256)
.rope_freq_base("llama", 10000.0)
.rms_epsilon("llama", 1e-5)
.ffn_hidden_dim("llama", 128)
.add_f32_tensor(
"token_embd.weight",
&[vocab_size as u64, hidden_dim as u64],
&embed_data,
)
.add_f32_tensor("blk.0.attn_norm.weight", &[hidden_dim as u64], &norm_data)
.add_q4_0_tensor(
"blk.0.attn_q.weight",
&[hidden_dim as u64, hidden_dim as u64],
&q4_0_data,
)
.add_q4_0_tensor(
"blk.0.attn_k.weight",
&[hidden_dim as u64, hidden_dim as u64],
&q4_0_data,
)
.add_q4_0_tensor(
"blk.0.attn_v.weight",
&[hidden_dim as u64, hidden_dim as u64],
&q4_0_data,
)
.add_q4_0_tensor(
"blk.0.attn_output.weight",
&[hidden_dim as u64, hidden_dim as u64],
&q4_0_data,
)
.add_f32_tensor("blk.0.ffn_norm.weight", &[hidden_dim as u64], &norm_data)
.add_q4_0_tensor(
"blk.0.ffn_up.weight",
&[hidden_dim as u64, 128],
&create_q4_0_data(hidden_dim * 128),
)
.add_q4_0_tensor(
"blk.0.ffn_down.weight",
&[128, hidden_dim as u64],
&create_q4_0_data(128 * hidden_dim),
)
.add_q4_0_tensor(
"blk.0.ffn_gate.weight",
&[hidden_dim as u64, 128],
&create_q4_0_data(hidden_dim * 128),
)
.add_f32_tensor("output_norm.weight", &[hidden_dim as u64], &norm_data)
.build();
let model = GGUFModel::from_bytes(&data).expect("Should parse Q4_0 model");
let transformer = QuantizedGGUFTransformer::from_gguf(&model, &data).expect("Should load Q4_0");
match &transformer.layers[0].qkv_weight {
QKVWeights::Separate { q, k, v } => {
assert_eq!(q.qtype, GGUF_TYPE_Q4_0);
assert_eq!(k.qtype, GGUF_TYPE_Q4_0);
assert_eq!(v.qtype, GGUF_TYPE_Q4_0);
assert_eq!(q.byte_size, 128 * 18);
},
_ => panic!("Expected Separate"),
}
}