use half::f16;
use model_io::ResidentIndex;
use crate::families::qwen4::state::RealQwen4State;
use crate::families::qwen4::{layer_tensor, RMS_EPS};
use crate::real_forward_dispatch::encode_gemv_any;
use crate::real_forward_types::RealForwardError;
use crate::real_forward_utils::norm_view;
fn ple_conv_shape(wide_dim: usize) -> Result<gpu::GdnShape, RealForwardError> {
const KEY_HEAD_DIM: usize = 32;
let two_k = 2 * KEY_HEAD_DIM;
if wide_dim <= two_k || (wide_dim - two_k) % 4 != 0 {
return Err(RealForwardError::Unsupported(format!(
"qwen4_exp's wide residual ({wide_dim}) does not fit the dilated conv's shape \
adapter; it must exceed {two_k} by a multiple of 4"
)));
}
let shape = gpu::GdnShape {
num_k_heads: 1,
num_v_heads: 1,
key_head_dim: KEY_HEAD_DIM as u32,
value_head_dim: (wide_dim - two_k) as u32,
conv_kernel_size: 4,
};
shape.validate().map_err(RealForwardError::Gpu)?;
Ok(shape)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_ple_layer(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
qwen4: &mut RealQwen4State,
wide: (&gpu::MetalBuffer, u64),
hidden: usize,
heads_per_ngram: usize,
token: i32,
emb_row_offset: u64,
) -> Result<(), RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let hc_count = qwen4.hc_count;
let wide_dim = hidden * hc_count;
let layer = qwen4.ple_layer;
let name = |suffix: &str| layer_tensor(layer, &format!("ple.{suffix}"));
let ctx = qwen4.ngram_context.step(token as i64, qwen4.eos_token_id);
let rows = model_io::ple_ngram_rows(
&ctx,
heads_per_ngram as i64,
&qwen4.ngram_layout.multipliers,
&qwen4.ngram_layout.head_vocab_sizes,
&qwen4.ngram_layout.head_offsets,
);
let head_dim = qwen4.ngram_layout.head_dim as usize;
let mut emb = Vec::with_capacity(hidden);
let table = qwen4.ngram_table.data();
for &gid in &rows {
let offset = qwen4.ngram_layout.row_offset(gid).ok_or_else(|| {
RealForwardError::Unsupported(format!(
"PLE row id {gid} is outside the n-gram table's {} rows",
qwen4.ngram_layout.rows
))
})? as usize;
let record_bytes = qwen4.ngram_layout.record_bytes as usize;
let record = &table[offset..offset + record_bytes];
let weight_bytes = qwen4.ngram_layout.weight_bytes as usize;
let scale_bytes = qwen4.ngram_layout.scale_bytes as usize;
let packed = &record[..weight_bytes];
let dequanted = match qwen4.ngram_layout.ggml_type.as_deref() {
Some("iq4_nl") => compute::quant_gguf_iq::dequantize_iq4_nl(packed, head_dim),
None => {
let rest = &record[weight_bytes..];
let (scales, biases) = rest.split_at(scale_bytes);
compute::dequant_ngram_row(
packed,
scales,
biases,
head_dim,
qwen4.ngram_layout.group_size as usize,
)
}
Some(other) => {
return Err(RealForwardError::Unsupported(format!(
"PLE n-gram row type {other:?} has no decoder"
)))
}
};
emb.extend_from_slice(&dequanted);
}
if emb.len() != hidden {
return Err(RealForwardError::Unsupported(format!(
"PLE's 16-head concat produced {} values, expected hidden ({hidden})",
emb.len()
)));
}
let emb_f16: Vec<u8> = emb
.iter()
.flat_map(|&v| f16::from_f32(v).to_bits().to_le_bytes())
.collect();
gpu::write_buffer_bytes(&qwen4.ngram_emb, emb_row_offset as usize, &emb_f16);
encode_gemv_any(
context,
pass,
weights,
index,
&name("key_proj.weight"),
wide_dim,
hidden,
(&qwen4.ngram_emb, emb_row_offset),
(&qwen4.ple_key, 0),
)?;
let norm_key_w = norm_view(weights, index, &name("norm_key.weight"), wide_dim)?;
gpu::encode_rms_norm_bf16w_grouped(
context,
pass,
(&qwen4.ple_key, 0),
norm_key_w,
(&qwen4.ple_key, 0),
hc_count as u32,
hidden as u32,
RMS_EPS,
)
.map_err(gpu_err)?;
encode_gemv_any(
context,
pass,
weights,
index,
&name("value_proj.weight"),
hidden,
hidden,
(&qwen4.ngram_emb, emb_row_offset),
(&qwen4.ple_value, 0),
)?;
let norm_query_w = norm_view(weights, index, &name("norm_query.weight"), wide_dim)?;
gpu::encode_rms_norm_bf16w_grouped(
context,
pass,
wide,
norm_query_w,
(&qwen4.ple_query, 0),
hc_count as u32,
hidden as u32,
RMS_EPS,
)
.map_err(gpu_err)?;
gpu::encode_ple_gate(
context,
pass,
(&qwen4.ple_key, 0),
(&qwen4.ple_query, 0),
(&qwen4.ple_value, 0),
(&qwen4.ple_gv, 0),
hc_count as u32,
hidden as u32,
)
.map_err(gpu_err)?;
let norm_conv_w = norm_view(weights, index, &name("norm_conv.weight"), wide_dim)?;
gpu::encode_rms_norm_bf16w_grouped(
context,
pass,
(&qwen4.ple_gv, 0),
norm_conv_w,
(&qwen4.ple_conv_normed, 0),
hc_count as u32,
hidden as u32,
RMS_EPS,
)
.map_err(gpu_err)?;
let conv_shape = ple_conv_shape(wide_dim)?;
let conv_w = norm_view(weights, index, &name("conv1d.weight"), wide_dim * 4)?;
gpu::encode_gdn_conv_decode(
context,
pass,
conv_shape,
(&qwen4.ple_conv_tail, 0),
(&qwen4.ple_conv_normed, 0),
conv_w,
(&qwen4.ple_conv_out, 0),
(qwen4.ngram_context_len + 1) as u32,
)
.map_err(gpu_err)?;
gpu::encode_residual_add(context, pass, wide, (&qwen4.ple_gv, 0), wide_dim as u32)
.map_err(gpu_err)?;
gpu::encode_residual_add(
context,
pass,
wide,
(&qwen4.ple_conv_out, 0),
wide_dim as u32,
)
.map_err(gpu_err)?;
Ok(())
}