use std::path::Path;
use half::bf16;
use oxibonsai_core::gguf::metadata::MetadataStore;
use oxibonsai_core::gguf::reader::GgufFile;
use oxibonsai_core::gguf::tensor_info::{TensorInfo, TensorStore};
use oxibonsai_core::gguf::types::GgufTensorType;
use oxibonsai_core::quant_ternary::{BlockTQ2_0_g128, QK_TQ2_0_G128};
use crate::config::DitConfig;
use crate::error::{DitError, DitResult};
const WEIGHT_SUFFIX: &str = ".weight";
enum Backing {
Mmap(memmap2::Mmap),
Owned(Vec<u8>),
}
impl Backing {
fn as_bytes(&self) -> &[u8] {
match self {
Self::Mmap(m) => &m[..],
Self::Owned(v) => v.as_slice(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct QuantizedLinear<'a> {
pub blocks: &'a [BlockTQ2_0_g128],
pub out_features: u64,
pub in_features: u64,
}
impl QuantizedLinear<'_> {
pub fn expected_block_count(&self) -> u64 {
self.out_features * (self.in_features / QK_TQ2_0_G128 as u64)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Bf16Tensor<'a> {
pub bytes: &'a [u8],
shape_rev: &'a [u64],
}
impl<'a> Bf16Tensor<'a> {
pub fn shape(&self) -> Vec<u64> {
let mut s: Vec<u64> = self.shape_rev.to_vec();
s.reverse();
s
}
pub fn element_count(&self) -> u64 {
self.shape_rev.iter().product()
}
pub fn bits(&self) -> Option<Vec<u16>> {
if self.bytes.len() % 2 != 0 {
return None;
}
Some(
self.bytes
.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect(),
)
}
pub fn to_f32_vec(&self) -> Vec<f32> {
self.bytes
.chunks_exact(2)
.map(|c| bf16::from_le_bytes([c[0], c[1]]).to_f32())
.collect()
}
}
pub struct DitWeights {
backing: Backing,
data_offset: usize,
metadata: MetadataStore,
tensors: TensorStore,
config: DitConfig,
}
impl DitWeights {
pub fn open(path: &Path) -> DitResult<Self> {
let file = std::fs::File::open(path).map_err(|source| DitError::Io {
path: path.display().to_string(),
source,
})?;
let mmap = unsafe { memmap2::Mmap::map(&file) }.map_err(|source| DitError::Io {
path: path.display().to_string(),
source,
})?;
Self::from_backing(Backing::Mmap(mmap))
}
pub fn from_bytes(bytes: Vec<u8>) -> DitResult<Self> {
Self::from_backing(Backing::Owned(bytes))
}
fn from_backing(backing: Backing) -> DitResult<Self> {
let (metadata, tensors, data_offset) = {
let file = GgufFile::parse(backing.as_bytes())?;
(file.metadata, file.tensors, file.data_offset)
};
let config = DitConfig::from_metadata(&metadata)?;
Ok(Self {
backing,
data_offset,
metadata,
tensors,
config,
})
}
pub fn config(&self) -> &DitConfig {
&self.config
}
pub fn metadata(&self) -> &MetadataStore {
&self.metadata
}
pub fn tensors(&self) -> &TensorStore {
&self.tensors
}
pub fn tensor_count(&self) -> usize {
self.tensors.len()
}
pub fn quantized_names(&self) -> Vec<&str> {
self.names_of_type(GgufTensorType::TQ2_0_g128)
}
pub fn bf16_names(&self) -> Vec<&str> {
self.names_of_type(GgufTensorType::BF16)
}
fn names_of_type(&self, ty: GgufTensorType) -> Vec<&str> {
let mut names: Vec<&str> = self
.tensors
.iter()
.filter(|(_, info)| info.tensor_type == ty)
.map(|(name, _)| name.as_str())
.collect();
names.sort_unstable();
names
}
fn raw_bytes(&self, info: &TensorInfo) -> DitResult<&[u8]> {
let bytes = self.backing.as_bytes();
let start = self.data_offset + info.offset as usize;
let size = info.data_size() as usize;
let end = start
.checked_add(size)
.ok_or_else(|| DitError::InvalidMetadata {
key: info.name.clone(),
reason: "tensor extent overflows usize".to_string(),
})?;
if end > bytes.len() {
return Err(DitError::Gguf(
oxibonsai_core::error::BonsaiError::UnexpectedEof { offset: end as u64 },
));
}
Ok(&bytes[start..end])
}
pub fn quantized_linear(&self, name: &str) -> DitResult<QuantizedLinear<'_>> {
let info = self.lookup_quantized_info(name)?;
if info.tensor_type != GgufTensorType::TQ2_0_g128 {
return Err(DitError::WrongTensorType {
name: info.name.clone(),
found: info.tensor_type.to_string(),
expected: GgufTensorType::TQ2_0_g128.to_string(),
});
}
if info.shape.len() != 2 {
return Err(DitError::WrongRank {
name: info.name.clone(),
found: info.shape.len(),
expected: 2,
});
}
let in_features = info.shape[0];
let out_features = info.shape[1];
let bytes = self.raw_bytes(info)?;
let blocks = BlockTQ2_0_g128::slice_from_bytes(bytes)?;
Ok(QuantizedLinear {
blocks,
out_features,
in_features,
})
}
fn lookup_quantized_info(&self, name: &str) -> DitResult<&TensorInfo> {
if let Some(info) = self.tensors.get(name) {
return Ok(info);
}
if let Some(base) = name.strip_suffix(WEIGHT_SUFFIX) {
if let Some(info) = self.tensors.get(base) {
return Ok(info);
}
}
Err(DitError::Gguf(
oxibonsai_core::error::BonsaiError::TensorNotFound {
name: name.to_string(),
},
))
}
pub fn bf16_tensor(&self, name: &str) -> DitResult<Bf16Tensor<'_>> {
let info = self.tensors.require(name)?;
if info.tensor_type != GgufTensorType::BF16 {
return Err(DitError::WrongTensorType {
name: info.name.clone(),
found: info.tensor_type.to_string(),
expected: GgufTensorType::BF16.to_string(),
});
}
let bytes = self.raw_bytes(info)?;
Ok(Bf16Tensor {
bytes,
shape_rev: &info.shape,
})
}
}