libmir-metal 0.2.0

Metal inference backend for libmir
use super::attention::EncoderAttention;
use crate::engine::{Array, DenseLinear, LayerNorm, ModelTensors, Result, Stream};

#[derive(Debug)]
pub(super) struct EncoderLayer {
    attention: EncoderAttention,
    attention_norm: LayerNorm,
    up_gate: DenseLinear,
    down: DenseLinear,
    mlp_norm: LayerNorm,
    intermediate: usize,
}

impl EncoderLayer {
    pub fn load(
        tensors: &ModelTensors,
        index: usize,
        config: &models::layout::EncoderConfig,
        stream: &Stream,
    ) -> Result<Self> {
        let prefix = format!("new.encoder.layer.{index}");
        let eps = config.layer_norm_eps.to_string().parse()?;
        Ok(Self {
            attention: EncoderAttention::load(
                tensors,
                &format!("{prefix}.attention"),
                config,
                stream,
            )?,
            attention_norm: LayerNorm::load(tensors, &format!("{prefix}.attn_ln"), eps)?,
            up_gate: DenseLinear::load(tensors, &format!("{prefix}.mlp.up_gate_proj"), stream)?,
            down: DenseLinear::load(tensors, &format!("{prefix}.mlp.down_proj"), stream)?,
            mlp_norm: LayerNorm::load(tensors, &format!("{prefix}.mlp_ln"), eps)?,
            intermediate: config.intermediate_size,
        })
    }

    pub fn forward(&self, input: &Array, stream: &Stream) -> Result<Array> {
        let hidden = input.add(&self.attention.forward(input, stream)?, stream)?;
        let hidden = self.attention_norm.forward(&hidden, stream)?;
        let projected = self.up_gate.forward(&hidden, stream)?;
        let shape = projected.shape()?;
        let rows = usize::try_from(shape[1])?;
        let up = projected.slice(&[0, 0, 0], &[1, rows, self.intermediate], stream)?;
        let gate = projected.slice(
            &[0, 0, self.intermediate],
            &[1, rows, self.intermediate * 2],
            stream,
        )?;
        let activated = gate.gelu(stream)?.multiply(&up, stream)?;
        self.mlp_norm
            .forward(&hidden.add(&self.down.forward(&activated, stream)?, stream)?, stream)
    }
}