libmir-metal 0.3.0

Metal inference backend for libmir
use super::projection::BoundLinear;
use crate::engine::{Array, Result, RouterOutput, Stream, kernels::MxFp4Shape};

pub(super) fn dense_experts(
    input: &Array,
    routing: &RouterOutput,
    projections: [&BoundLinear; 3],
    limit: &Array,
    shape: MxFp4Shape,
    stream: &Stream,
) -> Result<Array> {
    let [gate, up, down] = projections;
    let input = expert_input(input, shape, stream)?;
    let gate = gate.gather(&input, &routing.indices, false, stream)?;
    let up = up.gather(&input, &routing.indices, false, stream)?;
    activate_and_down(&gate, &up, down, routing, limit, stream)
}

#[allow(clippy::too_many_arguments)]
pub(super) fn fused_dense_experts(
    input: &Array,
    routing: &RouterOutput,
    gate_up: &BoundLinear,
    down: &BoundLinear,
    interleaved: bool,
    limit: &Array,
    shape: MxFp4Shape,
    stream: &Stream,
) -> Result<Array> {
    let input = expert_input(input, shape, stream)?;
    let gate_up = gate_up.gather(&input, &routing.indices, false, stream)?;
    let (gate, up) = if interleaved {
        super::super::fused_gate_up::split_interleaved_last(&gate_up, shape.intermediate, stream)?
    } else {
        super::super::fused_gate_up::split_last(&gate_up, shape.intermediate, stream)?
    };
    activate_and_down(&gate, &up, down, routing, limit, stream)
}

fn expert_input(input: &Array, shape: MxFp4Shape, stream: &Stream) -> Result<Array> {
    input.reshape(&[i32::try_from(shape.tokens)?, 1, 1, i32::try_from(shape.hidden)?], stream)
}

fn activate_and_down(
    gate: &Array,
    up: &Array,
    down: &BoundLinear,
    routing: &RouterOutput,
    limit: &Array,
    stream: &Stream,
) -> Result<Array> {
    let minimum = limit.multiply_scalar(-1.0, stream)?;
    let graph = stream.native().graph();
    let gate = Array::from_native(graph.minimum(gate.native(), limit.native())?)?;
    let up = up.clip(&minimum, limit, stream)?.add_scalar(1.0, stream)?;
    let scaled = gate.multiply_scalar(1.702, stream)?;
    let silu =
        Array::from_native(graph.multiply(gate.native(), &graph.sigmoid(scaled.native())?)?)?;
    let activated = silu.multiply(&up, stream)?;
    down.gather(&activated, &routing.indices, false, stream)?
        .squeeze_axis(-2, stream)?
        .weighted_sum(&routing.weights, -2, stream)
}