libmir-cuda 0.3.0

CUDA inference backend for libmir
use models::weights::{BlockQuantization, TensorBinding, TensorStorage};

use crate::{
    CudaBackend, CudaTensorSet, Error, NvFp4Config, NvFp4LinearWeight, NvFp4Tensors,
    NvFp4WeightOnlyWeight, Result,
};

pub(super) fn load_native(
    backend: &CudaBackend,
    tensors: &CudaTensorSet,
    binding: &TensorBinding,
) -> Result<NvFp4LinearWeight> {
    let TensorStorage::BlockQuantized {
        format: BlockQuantization::NVFP4,
        scales,
        global_scale: Some(global_scale),
        input_scale: Some(input_scale),
        ..
    } = &binding.storage
    else {
        return Err(Error::InvalidNvFp4("projection has no complete NVFP4 binding"));
    };
    let [output, input] = binding.logical_shape.as_deref().ok_or_else(|| {
        Error::UnsupportedDecoderLayer(format!(
            "NVFP4 projection has no logical matrix shape: {}",
            binding.source
        ))
    })?
    else {
        return Err(Error::UnsupportedDecoderLayer(format!(
            "NVFP4 projection is not a matrix: {}",
            binding.source
        )));
    };
    let get = |name: &str| tensors.get(name).ok_or_else(|| Error::MissingTensor(name.into()));
    backend.prepare_nvfp4_linear_weight(
        NvFp4Config::new(*input, *output),
        NvFp4Tensors {
            weight: get(&binding.source)?,
            weight_scale: get(scales)?,
            weight_scale_2: get(global_scale)?,
            input_scale: get(input_scale)?,
        },
    )
}

pub(super) fn load_weight_only(
    tensor_set: &CudaTensorSet,
    binding: &TensorBinding,
) -> Result<NvFp4WeightOnlyWeight> {
    let (config, tensors) = tensors(binding, tensor_set)?;
    NvFp4WeightOnlyWeight::load(config, tensors)
}

fn tensors<'a>(
    binding: &TensorBinding,
    tensors: &'a CudaTensorSet,
) -> Result<(NvFp4Config, NvFp4Tensors<'a>)> {
    let TensorStorage::BlockQuantized {
        format:
            BlockQuantization {
                format: models::weights::BlockFormat::NvFp4,
                ..
            },
        scales,
        global_scale: Some(global_scale),
        input_scale: Some(input_scale),
        ..
    } = &binding.storage
    else {
        return Err(Error::InvalidNvFp4("projection has no complete NVFP4 binding"));
    };
    let [output, input] = binding.logical_shape.as_deref().ok_or_else(|| {
        Error::UnsupportedDecoderLayer(format!(
            "NVFP4 projection has no logical matrix shape: {}",
            binding.source
        ))
    })?
    else {
        return Err(Error::UnsupportedDecoderLayer(format!(
            "NVFP4 projection is not a matrix: {}",
            binding.source
        )));
    };
    let get = |name: &str| tensors.get(name).ok_or_else(|| Error::MissingTensor(name.into()));
    Ok((
        NvFp4Config::new(*input, *output),
        NvFp4Tensors {
            weight: get(&binding.source)?,
            weight_scale: get(scales)?,
            weight_scale_2: get(global_scale)?,
            input_scale: get(input_scale)?,
        },
    ))
}