libmir-cuda 0.2.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, bf16};

use super::{
    CudaTextEmbeddingModel,
    execute::{epsilon, project, rope, theta},
    scratch::Scratch,
};
use crate::{
    Result,
    kernels::{TextAttention, TextAttentionSpec},
};

impl CudaTextEmbeddingModel {
    #[expect(clippy::too_many_lines, reason = "keeps one decoder layer contract contiguous")]
    pub(super) fn layer(
        &self,
        layer: usize,
        input: &DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
        scratch: &mut Scratch,
    ) -> Result<()> {
        let config = &self.config;
        let count = scratch.tokens;
        let hidden = config.hidden_size;
        let prefix = self.layout.name(format!("layers.{layer}"));
        let backend = &self.backend;
        backend.prepare_rms_norm_bf16(count, hidden, epsilon(config)?)?.execute(
            input,
            self.tensor(&format!("{prefix}.input_layernorm.weight"))?,
            &mut scratch.normalized,
        )?;
        project(
            backend,
            count,
            hidden,
            scratch.query_width,
            &scratch.normalized,
            self.tensor(&format!("{prefix}.self_attn.q_proj.weight"))?,
            &mut scratch.query,
        )?;
        project(
            backend,
            count,
            hidden,
            scratch.kv_width,
            &scratch.normalized,
            self.tensor(&format!("{prefix}.self_attn.k_proj.weight"))?,
            &mut scratch.key,
        )?;
        project(
            backend,
            count,
            hidden,
            scratch.kv_width,
            &scratch.normalized,
            self.tensor(&format!("{prefix}.self_attn.v_proj.weight"))?,
            &mut scratch.value,
        )?;
        let head = config.head_dim;
        backend
            .prepare_rms_norm_bf16(count * config.num_attention_heads, head, epsilon(config)?)?
            .execute(
                &scratch.query,
                self.tensor(&format!("{prefix}.self_attn.q_norm.weight"))?,
                &mut scratch.query_norm,
            )?;
        backend
            .prepare_rms_norm_bf16(count * config.num_key_value_heads, head, epsilon(config)?)?
            .execute(
                &scratch.key,
                self.tensor(&format!("{prefix}.self_attn.k_norm.weight"))?,
                &mut scratch.key_norm,
            )?;
        rope(backend, count, config.num_attention_heads, head, theta(config)?)?.execute(
            &scratch.query_norm,
            &mut scratch.query_rope,
            0,
        )?;
        rope(backend, count, config.num_key_value_heads, head, theta(config)?)?.execute(
            &scratch.key_norm,
            &mut scratch.key_rope,
            0,
        )?;
        TextAttention::compile(
            &backend.inner.compiler,
            TextAttentionSpec {
                tokens: count,
                query_heads: config.num_attention_heads,
                kv_heads: config.num_key_value_heads,
                head_dim: head,
                scale: head.to_string().parse::<f32>()?.sqrt().recip(),
                causal: true,
            },
        )?
        .execute(
            &backend.inner.stream,
            &scratch.query_rope,
            &scratch.key_rope,
            &scratch.value,
            &mut scratch.attention,
        )?;
        project(
            backend,
            count,
            scratch.query_width,
            hidden,
            &scratch.attention,
            self.tensor(&format!("{prefix}.self_attn.o_proj.weight"))?,
            &mut scratch.projection,
        )?;
        scratch.hidden_ops.add(
            &backend.inner.stream,
            input,
            &scratch.projection,
            &mut scratch.residual,
        )?;
        backend.prepare_rms_norm_bf16(count, hidden, epsilon(config)?)?.execute(
            &scratch.residual,
            self.tensor(&format!("{prefix}.post_attention_layernorm.weight"))?,
            &mut scratch.normalized,
        )?;
        project(
            backend,
            count,
            hidden,
            config.intermediate_size,
            &scratch.normalized,
            self.tensor(&format!("{prefix}.mlp.gate_proj.weight"))?,
            &mut scratch.gate,
        )?;
        project(
            backend,
            count,
            hidden,
            config.intermediate_size,
            &scratch.normalized,
            self.tensor(&format!("{prefix}.mlp.up_proj.weight"))?,
            &mut scratch.up,
        )?;
        scratch.gated.execute_separate(
            &backend.inner.stream,
            &scratch.gate,
            &scratch.up,
            &mut scratch.activated,
            crate::GatedActivation::Silu.into(),
        )?;
        project(
            backend,
            count,
            config.intermediate_size,
            hidden,
            &scratch.activated,
            self.tensor(&format!("{prefix}.mlp.down_proj.weight"))?,
            &mut scratch.mlp,
        )?;
        scratch
            .hidden_ops
            .add(&backend.inner.stream, &scratch.residual, &scratch.mlp, output)
    }
}