libmir-metal 0.1.0

Metal inference backend for libmir
use super::config::DenseSwiGluLayerConfig;
use crate::engine::{Array, ModelTensors, NormWeight, QuantizedLinear, Result, Stream};

#[derive(Debug)]
pub(super) struct AttentionWeights {
    pub(super) query: QuantizedLinear,
    pub(super) key: QuantizedLinear,
    pub(super) value: QuantizedLinear,
    pub(super) output: QuantizedLinear,
    pub(super) query_norm: Option<NormWeight>,
    pub(super) key_norm: Option<NormWeight>,
    pub(super) rope_frequencies: Option<Array>,
}

#[derive(Debug)]
pub(super) struct MlpWeights {
    pub(super) gate: QuantizedLinear,
    pub(super) up: QuantizedLinear,
    pub(super) down: QuantizedLinear,
}

#[derive(Debug)]
pub(super) struct DenseSwiGluWeights {
    pub(super) input_norm: NormWeight,
    pub(super) post_attention_norm: NormWeight,
    pub(super) attention: AttentionWeights,
    pub(super) mlp: MlpWeights,
}

impl DenseSwiGluWeights {
    pub(super) fn load(
        tensors: &ModelTensors,
        config: DenseSwiGluLayerConfig,
        stream: &Stream,
    ) -> Result<Self> {
        let layer = format!("model.layers.{}", config.index);
        let attention = format!("{layer}.self_attn");
        let mlp = format!("{layer}.mlp");
        Ok(Self {
            input_norm: NormWeight::load(tensors, &format!("{layer}.input_layernorm"))?,
            post_attention_norm: NormWeight::load(
                tensors,
                &format!("{layer}.post_attention_layernorm"),
            )?,
            attention: AttentionWeights {
                query: linear(tensors, &attention, "q_proj", config.group_size)?,
                key: linear(tensors, &attention, "k_proj", config.group_size)?,
                value: linear(tensors, &attention, "v_proj", config.group_size)?,
                output: linear(tensors, &attention, "o_proj", config.group_size)?,
                query_norm: NormWeight::load_optional(tensors, &format!("{attention}.q_norm"))?,
                key_norm: NormWeight::load_optional(tensors, &format!("{attention}.k_norm"))?,
                rope_frequencies: rope_frequencies(config, stream)?,
            },
            mlp: MlpWeights {
                gate: linear(tensors, &mlp, "gate_proj", config.group_size)?,
                up: linear(tensors, &mlp, "up_proj", config.group_size)?,
                down: linear(tensors, &mlp, "down_proj", config.group_size)?,
            },
        })
    }
}

fn rope_frequencies(config: DenseSwiGluLayerConfig, stream: &Stream) -> Result<Option<Array>> {
    config
        .rope_scaling
        .map(|scaling| {
            let (factor, low_frequency_factor, high_frequency_factor, original_context_len) =
                scaling.piecewise_frequency();
            Array::piecewise_rope_frequencies(
                config.head_dim,
                config.rope_base,
                factor.to_string().parse()?,
                low_frequency_factor.to_string().parse()?,
                high_frequency_factor.to_string().parse()?,
                i32::try_from(original_context_len)?,
                stream,
            )
        })
        .transpose()
}

fn linear(
    tensors: &ModelTensors,
    prefix: &str,
    name: &str,
    group_size: i32,
) -> Result<QuantizedLinear> {
    QuantizedLinear::load(tensors, &format!("{prefix}.{name}"), group_size)
}