libmir-metal 0.3.0

Metal inference backend for libmir
use crate::engine::{
    Array, NormWeight, Result, Stream,
    binding::BoundLinear,
    fused_gate_up::{split_interleaved_last, split_last},
};

mod load;

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

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

#[derive(Debug)]
pub(super) struct RouterWeights {
    pub(super) projection: BoundLinear,
    pub(super) norm_scale: Array,
    pub(super) expert_scale: Array,
}

#[derive(Debug)]
pub(super) struct ExpertWeights {
    pub(super) gate_up: ExpertGateUpWeights,
    pub(super) down: BoundLinear,
}

#[derive(Debug)]
pub(super) enum ExpertGateUpWeights {
    Separate {
        gate: BoundLinear,
        up: BoundLinear,
    },
    Fused {
        projection: BoundLinear,
        width: usize,
        interleaved: bool,
    },
}

impl ExpertWeights {
    pub(super) fn gather_gate_up(
        &self,
        input: &Array,
        indices: &Array,
        sorted: bool,
        stream: &Stream,
    ) -> Result<(Array, Array)> {
        match &self.gate_up {
            ExpertGateUpWeights::Separate { gate, up } => Ok((
                gate.gather(input, indices, sorted, stream)?,
                up.gather(input, indices, sorted, stream)?,
            )),
            ExpertGateUpWeights::Fused { projection, width, interleaved } => {
                let output = projection.gather(input, indices, sorted, stream)?;
                if *interleaved {
                    split_interleaved_last(&output, *width, stream)
                } else {
                    split_last(&output, *width, stream)
                }
            },
        }
    }

    pub(super) const fn separate(&self) -> Option<(&BoundLinear, &BoundLinear)> {
        match &self.gate_up {
            ExpertGateUpWeights::Separate { gate, up } => Some((gate, up)),
            ExpertGateUpWeights::Fused { .. } => None,
        }
    }

    pub(super) fn tuning_format(&self) -> (i32, i32) {
        match &self.gate_up {
            ExpertGateUpWeights::Separate { gate, .. } => gate.tuning_format(),
            ExpertGateUpWeights::Fused { projection, .. } => projection.tuning_format(),
        }
    }
}

#[derive(Debug)]
pub(super) struct LayerWeights {
    pub(super) input_norm: NormWeight,
    pub(super) post_attention_norm: NormWeight,
    pub(super) pre_dense_norm: NormWeight,
    pub(super) post_dense_norm: NormWeight,
    pub(super) pre_expert_norm: NormWeight,
    pub(super) post_expert_norm: NormWeight,
    pub(super) post_feed_forward_norm: NormWeight,
    pub(super) layer_scalar: Array,
    pub(super) attention: AttentionWeights,
    pub(super) dense: DenseWeights,
    pub(super) router: RouterWeights,
    pub(super) experts: ExpertWeights,
}