1use std::collections::HashMap;
7use std::io::BufReader;
8use std::path::Path;
9
10use anyhow::{Context, Result};
11use burn::tensor::{Device, Tensor, TensorData};
12use zsfm_checkpoint::cast::{decode_to_f32, f32_to_bytes, SrcDtype};
13use zsfm_gguf::{GGMLType, GGUFFile, GGUFMetaValue};
14
15pub struct GgufWeights {
17 tensors: HashMap<String, (Vec<u64>, Vec<u8>)>,
18 meta: Vec<(String, GGUFMetaValue)>,
19}
20
21fn device() -> Device {
22 Device::flex()
23}
24
25pub fn open_gguf_weights(path: &Path) -> Result<GgufWeights> {
27 let file = std::fs::File::open(path).with_context(|| format!("open {}", path.display()))?;
28 let mut reader = BufReader::with_capacity(zsfm_gguf::READ_BUF_CAPACITY, file);
29 let gguf = GGUFFile::read(&mut reader).context("parse GGUF header")?;
30 let mut tensors = HashMap::with_capacity(gguf.tensors.len());
31 for info in &gguf.tensors {
32 let shape: Vec<u64> = info.shape.iter().rev().copied().collect();
34 let bytes = match info.dtype {
35 GGMLType::F32 | GGMLType::F16 | GGMLType::BF16 => {
36 gguf.tensor_bytes(&mut reader, info)?
37 }
38 GGMLType::Q8_0 => {
39 let vals = gguf.tensor_f32(&mut reader, info)?;
40 f32_to_bytes(&vals)
41 }
42 };
43 let n: usize = shape.iter().product::<u64>() as usize;
45 let src = match info.dtype {
46 GGMLType::F32 => SrcDtype::F32,
47 GGMLType::F16 => SrcDtype::F16,
48 GGMLType::BF16 => SrcDtype::BF16,
49 GGMLType::Q8_0 => SrcDtype::F32,
50 };
51 let expect = n * src.bytes_per_elem();
52 anyhow::ensure!(
53 bytes.len() == expect,
54 "tensor {}: {} bytes, want {expect}",
55 info.name,
56 bytes.len()
57 );
58 tensors.insert(info.name.clone(), (shape, bytes));
59 }
60 Ok(GgufWeights {
61 tensors,
62 meta: gguf.metadata,
63 })
64}
65
66impl GgufWeights {
67 fn get(&self, name: &str) -> Result<&(Vec<u64>, Vec<u8>)> {
68 self.tensors
69 .get(name)
70 .with_context(|| format!("tensor '{name}' not found in GGUF"))
71 }
72
73 fn f32_vec(&self, name: &str) -> Result<Vec<f32>> {
74 let (shape, bytes) = self.get(name)?;
75 let n: usize = shape.iter().product::<u64>() as usize;
76 let mut vals = decode_to_f32(bytes, SrcDtype::F32)?;
77 anyhow::ensure!(
78 vals.len() == n,
79 "tensor {name}: decoded {} values, want {n}",
80 vals.len()
81 );
82 let _ = n;
84 Ok(std::mem::take(&mut vals))
85 }
86
87 pub fn meta_u32(&self, key: &str) -> Option<u32> {
89 use GGUFMetaValue::*;
90 self.meta
91 .iter()
92 .find(|(k, _)| k == key)
93 .and_then(|(_, v)| match v {
94 Uint32(x) => Some(*x),
95 Uint64(x) => u32::try_from(*x).ok(),
96 Int32(x) => u32::try_from(*x).ok(),
97 Int64(x) => u32::try_from(*x).ok(),
98 _ => None,
99 })
100 }
101
102 pub fn load_2d(&self, name: &str) -> Result<Tensor<2>> {
104 let (shape, _) = self.get(name)?;
105 anyhow::ensure!(shape.len() == 2, "tensor {name}: want 2D, got {shape:?}");
106 let data = self.f32_vec(name)?;
107 Ok(Tensor::<2>::from_data(
108 TensorData::new(data, [shape[0] as usize, shape[1] as usize]),
109 &device(),
110 ))
111 }
112
113 pub fn load_weight_2d(&self, name: &str, expected_d_out: usize) -> Result<Tensor<2>> {
117 let (shape, _) = self.get(name)?;
118 anyhow::ensure!(shape.len() == 2, "tensor {name}: want 2D, got {shape:?}");
119 let (r, c) = (shape[0] as usize, shape[1] as usize);
120 let data = self.f32_vec(name)?;
121 if r == expected_d_out {
122 return Ok(Tensor::<2>::from_data(
123 TensorData::new(data, [r, c]),
124 &device(),
125 ));
126 }
127 anyhow::ensure!(
128 c == expected_d_out,
129 "tensor {name}: dim(0) is {r}, want {expected_d_out} (transpose fallback needs {c})"
130 );
131 let mut t = vec![0.0f32; data.len()];
132 for i in 0..r {
133 for j in 0..c {
134 t[j * r + i] = data[i * c + j];
135 }
136 }
137 Ok(Tensor::<2>::from_data(
138 TensorData::new(t, [c, r]),
139 &device(),
140 ))
141 }
142
143 pub fn load_1d(&self, name: &str) -> Result<Tensor<1>> {
147 let (shape, _) = self.get(name)?;
148 let data = self.f32_vec(name)?;
149 let n: usize = shape.iter().product::<u64>() as usize;
150 anyhow::ensure!(
151 data.len() == n,
152 "tensor {name}: decoded {} values, want {n}",
153 data.len()
154 );
155 Ok(Tensor::<1>::from_data(
156 TensorData::new(data, [n]),
157 &device(),
158 ))
159 }
160
161 pub fn load_3d(&self, name: &str) -> Result<Tensor<3>> {
163 let (shape, _) = self.get(name)?;
164 anyhow::ensure!(shape.len() == 3, "tensor {name}: want 3D, got {shape:?}");
165 let data = self.f32_vec(name)?;
166 Ok(Tensor::<3>::from_data(
167 TensorData::new(
168 data,
169 [shape[0] as usize, shape[1] as usize, shape[2] as usize],
170 ),
171 &device(),
172 ))
173 }
174
175 pub fn load_flat(&self, name: &str) -> Result<Vec<f32>> {
177 self.f32_vec(name)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn missing_tensor_errors() {
187 let w = GgufWeights {
188 tensors: HashMap::new(),
189 meta: Vec::new(),
190 };
191 assert!(w.load_2d("nope").is_err());
192 }
193}