libmir-cuda 0.3.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, bf16};
use models::weights::{TensorBinding, TensorStorage};

use super::{
    CompressedInt8Bf16Linear, CompressedInt8Embedding, CompressedInt8Weight,
    awq::{AwqBf16Linear, AwqWeight},
    bitsandbytes::{BitsAndBytes4BitBf16Linear, BitsAndBytes4BitWeight},
    gptq::{GptqBf16Linear, GptqWeight},
};
use crate::{CudaBackend, CudaTensorSet, Error, Result};

#[derive(Clone, Debug)]
pub enum PackedIntegerWeight {
    Compressed(CompressedInt8Weight),
    Awq(AwqWeight),
    Gptq(GptqWeight),
    BitsAndBytes4Bit(BitsAndBytes4BitWeight),
}

impl PackedIntegerWeight {
    pub(crate) fn load_binding(
        tensors: &CudaTensorSet,
        binding: &TensorBinding,
        input: usize,
        output: usize,
    ) -> Result<Self> {
        match binding.storage {
            TensorStorage::PackedInt8 { .. } | TensorStorage::PackedInt4 { .. } => {
                CompressedInt8Weight::load_binding(tensors, binding, input, output)
                    .map(Self::Compressed)
            },
            TensorStorage::Awq { .. } => {
                AwqWeight::load_binding(tensors, binding, input, output).map(Self::Awq)
            },
            TensorStorage::Gptq { .. } => {
                GptqWeight::load_binding(tensors, binding, input, output).map(Self::Gptq)
            },
            TensorStorage::BitsAndBytes4Bit { .. } => {
                BitsAndBytes4BitWeight::load_binding(tensors, binding, input, output)
                    .map(Self::BitsAndBytes4Bit)
            },
            _ => Err(Error::InvalidQuantizedGemv("binding is not a packed integer weight")),
        }
    }

    pub(in crate::backend) fn validate(&self, input: usize, output: usize) -> Result<()> {
        match self {
            Self::Compressed(weight) => weight.validate(input, output),
            Self::Awq(weight) => weight.validate(input, output),
            Self::Gptq(weight) => weight.validate(input, output),
            Self::BitsAndBytes4Bit(weight) => weight.validate(),
        }
    }
}

#[derive(Clone, Debug)]
pub enum PackedIntegerBf16Linear {
    Compressed(CompressedInt8Bf16Linear),
    Awq(AwqBf16Linear),
    Gptq(GptqBf16Linear),
    BitsAndBytes4Bit(BitsAndBytes4BitBf16Linear),
}

impl PackedIntegerBf16Linear {
    pub(in crate::backend) fn new(
        backend: &CudaBackend,
        tokens: usize,
        input: usize,
        output: usize,
        weight: &PackedIntegerWeight,
    ) -> Result<Self> {
        match weight {
            PackedIntegerWeight::Compressed(weight) => {
                CompressedInt8Bf16Linear::new(backend, tokens, input, output, weight)
                    .map(Self::Compressed)
            },
            PackedIntegerWeight::Awq(weight) => {
                AwqBf16Linear::new(backend, tokens, input, output, weight).map(Self::Awq)
            },
            PackedIntegerWeight::Gptq(weight) => {
                GptqBf16Linear::new(backend, tokens, input, output, weight).map(Self::Gptq)
            },
            PackedIntegerWeight::BitsAndBytes4Bit(weight) => {
                BitsAndBytes4BitBf16Linear::new(backend, tokens, weight).map(Self::BitsAndBytes4Bit)
            },
        }
    }

    pub(in crate::backend) fn execute(
        &self,
        input: &DeviceBuffer<bf16>,
        weight: &PackedIntegerWeight,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        match (self, weight) {
            (Self::Compressed(operation), PackedIntegerWeight::Compressed(weight)) => {
                operation.execute(input, weight, output)
            },
            (Self::Awq(operation), PackedIntegerWeight::Awq(weight)) => {
                operation.execute(input, weight, output)
            },
            (Self::Gptq(operation), PackedIntegerWeight::Gptq(weight)) => {
                operation.execute(input, weight, output)
            },
            (Self::BitsAndBytes4Bit(operation), PackedIntegerWeight::BitsAndBytes4Bit(weight)) => {
                operation.execute(input, weight, output)
            },
            _ => Err(Error::InvalidExecutionPlan("packed integer operation/weight mismatch")),
        }
    }
}

pub(in crate::backend) fn embedding(
    backend: &CudaBackend,
    vocab: usize,
    hidden: usize,
    output_scale: f32,
    weight: PackedIntegerWeight,
) -> Result<CompressedInt8Embedding> {
    match weight {
        PackedIntegerWeight::Compressed(weight) => {
            CompressedInt8Embedding::new(backend, vocab, hidden, output_scale, weight)
        },
        PackedIntegerWeight::Awq(_)
        | PackedIntegerWeight::Gptq(_)
        | PackedIntegerWeight::BitsAndBytes4Bit(_) => Err(Error::InvalidQuantizedGemv(
            "AWQ/GPTQ storage cannot be used as an embedding table",
        )),
    }
}