use model_io::ResidentIndex;
use crate::real_forward_layout::{
DTYPE_GGUF_IQ1_M, DTYPE_GGUF_IQ1_S, DTYPE_GGUF_IQ2_S, DTYPE_GGUF_IQ2_XS, DTYPE_GGUF_IQ2_XXS,
DTYPE_GGUF_IQ3_S, DTYPE_GGUF_Q2_K, DTYPE_GGUF_Q3_K, DTYPE_GGUF_Q4_K, DTYPE_GGUF_Q5_K,
DTYPE_GGUF_Q6_K, DTYPE_GGUF_Q8_0, DTYPE_INT1_AFFINE, DTYPE_INT2_AFFINE, DTYPE_RAW_BF16,
};
use crate::real_forward_types::RealForwardError;
use crate::real_forward_utils::{affine_group_size, entry, resident_matrix};
pub(crate) use crate::real_forward_dispatch_moe::{encode_moe_phase1_any, encode_moe_phase2_any};
fn validate_block_quant_embedding(
e: &model_io::ResidentIndexEntry,
name: &str,
token: u32,
hidden: u32,
block_elems: usize,
block_bytes: usize,
type_name: &str,
) -> Result<(), RealForwardError> {
let (rows, cols, depth, planes) = e.shape;
if cols != hidden || depth != 0 || planes != 0 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: {type_name} shape {:?} does not match [vocab, {hidden}]",
e.shape
)));
}
if token >= rows {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: token {token} is outside {rows} rows"
)));
}
let hidden = u64::from(hidden);
let block_elems = block_elems as u64;
if hidden == 0 || hidden % block_elems != 0 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: {type_name} width {hidden} is not a whole number of {block_elems}-element blocks"
)));
}
let row_bytes = (hidden / block_elems)
.checked_mul(block_bytes as u64)
.ok_or_else(|| {
RealForwardError::Unsupported(format!(
"embedding table {name}: {type_name} row size overflows"
))
})?;
let expected = u64::from(rows).checked_mul(row_bytes).ok_or_else(|| {
RealForwardError::Unsupported(format!(
"embedding table {name}: {type_name} table size overflows"
))
})?;
let selected_end = u64::from(token)
.checked_add(1)
.and_then(|row| row.checked_mul(row_bytes))
.ok_or_else(|| {
RealForwardError::Unsupported(format!(
"embedding table {name}: {type_name} selected row overflows"
))
})?;
if e.size_bytes != expected || selected_end > e.size_bytes {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: {type_name} packed size {} does not contain {rows}x{hidden} bytes ({expected}) or selected row {token}",
e.size_bytes
)));
}
Ok(())
}
fn validate_q4_k_embedding(
e: &model_io::ResidentIndexEntry,
name: &str,
token: u32,
hidden: u32,
) -> Result<(), RealForwardError> {
validate_block_quant_embedding(
e,
name,
token,
hidden,
gpu::Q4_K_BLOCK_ELEMS,
gpu::Q4_K_BLOCK_BYTES,
"Q4_K",
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_embed_any(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
name: &str,
out: (&gpu::MetalBuffer, u64),
token: u32,
hidden: u32,
expected_rows: usize,
embed_scale: f32,
) -> Result<(), RealForwardError> {
if token as usize >= expected_rows {
return Err(RealForwardError::Unsupported(format!(
"token id {token} outside embedding table with {expected_rows} rows"
)));
}
let e = entry(index, name)?;
let shape = e.shape;
if shape.0 == 0 || shape.1 != hidden || shape.2 != 0 || shape.3 != 0 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: expected [rows, {hidden}], got {shape:?}"
)));
}
if token >= shape.0 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: token {token} is outside {} rows",
shape.0
)));
}
let row_block = match e.dtype {
DTYPE_GGUF_Q8_0 => Some(gpu::Q8_0_BLOCK_ELEMS),
DTYPE_GGUF_Q4_K => Some(gpu::Q4_K_BLOCK_ELEMS),
DTYPE_GGUF_Q6_K => Some(gpu::Q6_K_BLOCK_ELEMS),
DTYPE_GGUF_Q3_K => Some(gpu::Q3_K_BLOCK_ELEMS),
DTYPE_GGUF_IQ1_M => Some(gpu::IQ1_M_BLOCK_ELEMS),
_ => None,
};
if let Some(block) = row_block {
if hidden as usize % block != 0 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: row width {hidden} is not divisible by its \
{block}-element block"
)));
}
}
let base = index.header.index_size;
let table = (weights.buffer(), weights.gpu_offset(e.file_offset - base));
match e.dtype {
DTYPE_GGUF_Q8_0 => {
gpu::encode_embed_lookup_q8_0(context, pass, table, out, token, hidden, embed_scale)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q4_K => {
validate_q4_k_embedding(e, name, token, hidden)?;
gpu::encode_embed_lookup_q4_k(context, pass, table, out, token, hidden, embed_scale)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q3_K => {
validate_block_quant_embedding(
e,
name,
token,
hidden,
gpu::Q3_K_BLOCK_ELEMS,
gpu::Q3_K_BLOCK_BYTES,
"Q3_K",
)?;
gpu::encode_embed_lookup_q3_k(context, pass, table, out, token, hidden, embed_scale)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q6_K => {
gpu::encode_embed_lookup_q6_k(context, pass, table, out, token, hidden, embed_scale)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_IQ1_M => {
gpu::encode_embed_lookup_iq1_m(context, pass, table, out, token, hidden, embed_scale)
.map_err(RealForwardError::Gpu)
}
DTYPE_INT1_AFFINE | DTYPE_INT2_AFFINE => {
let bits = if e.dtype == DTYPE_INT1_AFFINE { 1 } else { 2 };
let d = hidden as usize;
let group_size = affine_group_size(e, name, expected_rows, d, bits)?;
let scales = (weights.buffer(), weights.gpu_offset(e.scale_offset - base));
let biases = (weights.buffer(), weights.gpu_offset(e.bias_offset - base));
let encode = if bits == 1 {
gpu::encode_embed_lookup_int1
} else {
gpu::encode_embed_lookup_int2
};
encode(
context,
pass,
table,
scales,
biases,
out,
token,
hidden,
group_size as u32,
embed_scale,
)
.map_err(RealForwardError::Gpu)
}
4 => {
let scales = (weights.buffer(), weights.gpu_offset(e.scale_offset - base));
let biases = (weights.buffer(), weights.gpu_offset(e.bias_offset - base));
let d = hidden as usize;
if d == 0 || (e.size_bytes as usize * 2) % d != 0 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: {} INT4 bytes is not a whole number of \
{d}-element rows",
e.size_bytes
)));
}
let rows = e.size_bytes as usize * 2 / d;
let group_size = affine_group_size(e, name, rows, d, 4)?;
if group_size != 64 {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: INT4 group size {group_size} does not match kernel \
group size 64"
)));
}
if token as usize >= rows {
return Err(RealForwardError::Unsupported(format!(
"embedding table {name}: token id {token} outside {rows} rows"
)));
}
gpu::encode_embed_lookup_int4(
context,
pass,
table,
scales,
biases,
out,
token,
hidden,
embed_scale,
)
.map_err(RealForwardError::Gpu)
}
other => Err(RealForwardError::Unsupported(format!(
"embedding table {name}: dtype {other} has no dispatched lookup kernel"
))),
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_gemv_any(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
name: &str,
rows: usize,
cols: usize,
x: (&gpu::MetalBuffer, u64),
y: (&gpu::MetalBuffer, u64),
) -> Result<(), RealForwardError> {
let e = entry(index, name)?;
let base = index.header.index_size;
match e.dtype {
5 => {
let group_size = affine_group_size(e, name, rows, cols, 8)?;
if group_size != 64 {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: INT8 group size {group_size} does not match kernel group size 64"
)));
}
let w = gpu::Int8ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
scales_offset: weights.gpu_offset(e.scale_offset - base),
biases_offset: weights.gpu_offset(e.bias_offset - base),
rows,
cols,
};
gpu::encode_dequant_int8_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
4 => {
let w = resident_matrix(weights, index, name, rows, cols)?;
gpu::encode_dequant_int4_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_INT1_AFFINE => {
let group_size = affine_group_size(e, name, rows, cols, 1)?;
let w = gpu::Int1ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
scales_offset: weights.gpu_offset(e.scale_offset - base),
biases_offset: weights.gpu_offset(e.bias_offset - base),
rows,
cols,
group_size,
};
gpu::encode_dequant_int1_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_INT2_AFFINE => {
let group_size = affine_group_size(e, name, rows, cols, 2)?;
let w = gpu::Int2ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
scales_offset: weights.gpu_offset(e.scale_offset - base),
biases_offset: weights.gpu_offset(e.bias_offset - base),
rows,
cols,
group_size,
};
gpu::encode_dequant_int2_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q8_0 => {
let expected = gpu::q8_0_row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: Q8_0 packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Q8_0ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_dequant_q8_0_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q2_K => {
let expected = gpu::q2_k_row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: Q2_K packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Q2KResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_dequant_q2_k_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q4_K => {
let expected = gpu::q4_k_row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: Q4_K packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Q4KResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_dequant_q4_k_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q5_K => {
let expected = gpu::q5_k_row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: Q5_K packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Q5KResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_dequant_q5_k_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q6_K => {
let expected = gpu::q6_k_row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: Q6_K packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Q6KResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_dequant_q6_k_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_GGUF_Q3_K => {
let expected = gpu::q3_k_row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: Q3_K packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Q3KResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_dequant_q3_k_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
dtype @ (DTYPE_GGUF_IQ2_XXS | DTYPE_GGUF_IQ2_XS | DTYPE_GGUF_IQ1_S | DTYPE_GGUF_IQ3_S
| DTYPE_GGUF_IQ2_S | DTYPE_GGUF_IQ1_M) => {
let kind = match dtype {
DTYPE_GGUF_IQ2_XXS => gpu::IqBlockType::Iq2Xxs,
DTYPE_GGUF_IQ2_XS => gpu::IqBlockType::Iq2Xs,
DTYPE_GGUF_IQ1_S => gpu::IqBlockType::Iq1S,
DTYPE_GGUF_IQ3_S => gpu::IqBlockType::Iq3S,
DTYPE_GGUF_IQ2_S => gpu::IqBlockType::Iq2S,
DTYPE_GGUF_IQ1_M => gpu::IqBlockType::Iq1M,
_ => unreachable!(),
};
let expected = kind.row_bytes(cols) * rows;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: {kind:?} packed size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::IqResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
kind,
};
gpu::encode_dequant_iq_gemv_resident(context, pass, &w, x, y)
.map_err(RealForwardError::Gpu)
}
DTYPE_RAW_BF16 => {
let expected = rows * cols * 2;
if e.size_bytes as usize != expected {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: BF16 size {} does not match {rows}x{cols} ({expected})",
e.size_bytes
)));
}
let w = gpu::Bf16ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
rows,
cols,
};
gpu::encode_bf16_gemv_resident(context, pass, &w, x, y).map_err(RealForwardError::Gpu)
}
other => Err(RealForwardError::Unsupported(format!(
"tensor {name}: dtype {other} has no dispatched GEMV kernel"
))),
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_gemm_any(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
name: &str,
rows: usize,
cols: usize,
x: (&gpu::MetalBuffer, u64),
y: (&gpu::MetalBuffer, u64),
batch: usize,
) -> Result<(), RealForwardError> {
let base = index.header.index_size;
if !(1..=gpu::MAX_BATCH_ROWS).contains(&batch) {
return Err(RealForwardError::Unsupported(format!(
"tensor {name}: batch {batch} outside 1..={} (the batched INT4 kernel's \
accumulators are a per-thread register array, so the cap is the register \
file rather than a tuning choice)",
gpu::MAX_BATCH_ROWS
)));
}
let e = entry(index, name)?;
match e.dtype {
4 => {
let w = resident_matrix(weights, index, name, rows, cols)?;
gpu::encode_dequant_int4_gemm_resident(context, pass, &w, x, y, batch)
.map_err(RealForwardError::Gpu)
}
DTYPE_INT1_AFFINE => {
let group_size = affine_group_size(e, name, rows, cols, 1)?;
let w = gpu::Int1ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
scales_offset: weights.gpu_offset(e.scale_offset - base),
biases_offset: weights.gpu_offset(e.bias_offset - base),
rows,
cols,
group_size,
};
gpu::encode_dequant_int1_gemm_resident(context, pass, &w, x, y, batch)
.map_err(RealForwardError::Gpu)
}
DTYPE_INT2_AFFINE => {
let group_size = affine_group_size(e, name, rows, cols, 2)?;
let w = gpu::Int2ResidentMatrix {
buffer: weights.buffer(),
weights_offset: weights.gpu_offset(e.file_offset - base),
scales_offset: weights.gpu_offset(e.scale_offset - base),
biases_offset: weights.gpu_offset(e.bias_offset - base),
rows,
cols,
group_size,
};
gpu::encode_dequant_int2_gemm_resident(context, pass, &w, x, y, batch)
.map_err(RealForwardError::Gpu)
}
other => Err(RealForwardError::Unsupported(format!(
"tensor {name}: dtype {other} has no BATCHED kernel; INT4-affine (4), \
1-bit affine (15) and 2-bit affine (16) do. Refused rather than looped: \
a sequential fallback here is numerically identical and would silently \
measure the unbatched engine"
))),
}
}
pub(crate) fn router_topk_gemma4(
logits: &[f32],
k: usize,
per_expert_scale: &[f32],
) -> (Vec<usize>, Vec<f32>) {
if logits.is_empty() || k == 0 {
return (Vec::new(), Vec::new());
}
let mut order: Vec<usize> = (0..logits.len()).collect();
order.sort_by(|&a, &b| logits[b].total_cmp(&logits[a]).then(a.cmp(&b)));
let selected: Vec<usize> = order.into_iter().take(k).collect();
let max = logits[selected[0]];
let exps: Vec<f32> = selected.iter().map(|&i| (logits[i] - max).exp()).collect();
let sum: f32 = exps.iter().sum();
let weights = selected
.iter()
.zip(exps.iter())
.map(|(&i, e)| {
let scale = per_expert_scale.get(i).copied().unwrap_or(1.0);
e / sum * scale
})
.collect();
(selected, weights)
}
#[cfg(test)]
mod tests {
use super::*;
fn q4_k_embedding(rows: u32, cols: u32) -> model_io::ResidentIndexEntry {
model_io::ResidentIndexEntry {
name: "token_embd.weight".to_string(),
dtype: DTYPE_GGUF_Q4_K,
file_offset: 4096,
size_bytes: u64::from(rows)
* (u64::from(cols) / gpu::Q4_K_BLOCK_ELEMS as u64)
* gpu::Q4_K_BLOCK_BYTES as u64,
shape: (rows, cols, 0, 0),
scale_offset: 0,
scale_size: 0,
bias_offset: 0,
bias_size: 0,
}
}
#[test]
fn q4_k_embedding_validation_bounds_shape_size_and_selected_row() {
let valid = q4_k_embedding(256, 256);
validate_q4_k_embedding(&valid, &valid.name, 255, 256).unwrap();
let mut wrong_shape = valid.clone();
wrong_shape.shape.1 = 512;
assert!(validate_q4_k_embedding(&wrong_shape, &wrong_shape.name, 0, 256).is_err());
let mut short = valid.clone();
short.size_bytes -= gpu::Q4_K_BLOCK_BYTES as u64;
assert!(validate_q4_k_embedding(&short, &short.name, 255, 256).is_err());
assert!(validate_q4_k_embedding(&valid, &valid.name, 256, 256).is_err());
}
#[test]
fn router_topk_gemma4_handles_empty_or_zero_k() {
let (sel, w) = router_topk_gemma4(&[], 4, &[]);
assert!(sel.is_empty() && w.is_empty());
let (sel, w) = router_topk_gemma4(&[1.0, 2.0, 3.0], 0, &[1.0, 1.0, 1.0]);
assert!(sel.is_empty() && w.is_empty());
}
}