1use std::{collections::HashMap, ffi::OsStr, fs::File, path::Path};
5
6use crate::{DType, Map, RT, Tensor, ZyxError, shape::Dim};
7
8pub trait Module {
10 fn iter(&self) -> impl Iterator<Item = &Tensor>;
12
13 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor>;
15
16 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)>;
18
19 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)>;
21
22 fn realize(&self) -> Result<(), ZyxError> {
28 Tensor::realize(self.iter())
29 }
30
31 fn set_params(&mut self, params: &mut HashMap<String, Tensor>) {
33 for (label, tensor) in self.iter_tensors_mut() {
34 if let Some(param) = params.remove(&label) {
35 *tensor = param;
36 }
37 }
38 }
39
40 fn save(&self, path: impl AsRef<Path>) -> Result<(), ZyxError> {
47 use std::fmt::Write;
48 use std::io::Write as IOWrite;
49 let mut f = File::create(path)?;
50 let mut header = String::from("{");
51 let mut begin = 0;
52 for (label, tensor) in self.iter_tensors() {
53 let dtype = tensor.dtype();
54 write!(header, "\"{label}\":{{").unwrap();
55 write!(header, "\"dtype\":\"{}\",", dtype.safetensors()).unwrap();
56 let mut st_shape = format!("{:?}", tensor.shape());
57 st_shape.retain(|c| !c.is_whitespace());
58 write!(header, "\"shape\":{st_shape},").unwrap();
59 let size = tensor.numel() * Dim::from(dtype.bit_size() / 8);
60 write!(header, "\"data_offsets\":[{},{}]", begin, begin + size).unwrap();
61 begin += size;
62 write!(header, "}},").unwrap();
63 }
64 header.pop();
65 write!(header, "}}").unwrap();
66 let header_bytes = header.as_bytes();
67 f.write_all(&(header_bytes.len() as u64).to_le_bytes())?;
68 f.write_all(header_bytes)?;
69 for tensor in self.iter() {
70 f.write_all(&tensor.to_le_bytes()?)?;
71 }
72 Ok(())
73 }
74}
75
76#[allow(unused)]
78pub enum GGUFMetadataValue {
79 Uint8(u8),
80 Int8(i8),
81 Uint16(u16),
82 Int16(i16),
83 Uint32(u32),
84 Int32(i32),
85 Uint64(u64),
86 Int64(i64),
87 Float32(f32),
88 Float64(f64),
89 Bool(bool),
90 String(String),
91 Array(Box<[GGUFMetadataValue]>),
92}
93
94impl<S: std::hash::BuildHasher + Default> Module for HashMap<String, Tensor, S> {
95 fn iter(&self) -> impl Iterator<Item = &Tensor> {
96 self.values()
97 }
98
99 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor> {
100 self.values_mut()
101 }
102
103 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)> {
104 self.iter().map(|(k, v): (&String, &Tensor)| (k.clone(), v))
105 }
106
107 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)> {
108 self.iter_mut().map(|(k, v): (&String, &mut Tensor)| (k.clone(), v))
109 }
110}
111
112impl Module for Vec<Tensor> {
113 fn iter(&self) -> impl Iterator<Item = &Tensor> {
114 self.into_iter()
115 }
116
117 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor> {
118 self.into_iter()
119 }
120
121 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)> {
122 self.iter().map(|t: &Tensor| (format!("{}", t.id()), t))
123 }
124
125 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)> {
126 self.iter_mut().map(|t: &mut Tensor| (format!("{}", t.id()), t))
127 }
128}
129
130impl<M0: Module, M1: Module> Module for (M0, M1) {
131 fn iter(&self) -> impl Iterator<Item = &Tensor> {
132 self.0.iter().chain(self.1.iter())
133 }
134
135 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor> {
136 self.0.iter_mut().chain(self.1.iter_mut())
137 }
138
139 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)> {
140 self.0.iter_tensors().chain(self.1.iter_tensors())
141 }
142
143 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)> {
144 self.0.iter_tensors_mut().chain(self.1.iter_tensors_mut())
145 }
146}
147
148impl Tensor {
149 #[allow(clippy::missing_panics_doc)]
155 pub fn load(path: impl AsRef<Path>) -> Result<HashMap<String, Tensor>, ZyxError>
156 where
157 Self: Sized,
158 {
159 RT.lock().initialize_devices()?; let e = path.as_ref().extension().and_then(OsStr::to_str).unwrap();
161 match e {
162 "safetensors" => Self::load_safetensors(path),
163 "gguf" => Ok(Self::load_gguf(path)?.1),
164 _ => panic!("Unknown file extension. Zyx currently supports only safetensors format."),
165 }
166 }
167
168 #[allow(clippy::missing_panics_doc)]
173 #[allow(clippy::type_complexity)]
174 pub fn load_gguf(path: impl AsRef<Path>) -> Result<(HashMap<String, GGUFMetadataValue>, HashMap<String, Tensor>), ZyxError> {
175 use std::io::Read;
176 let mut f = std::fs::File::open(&path)?;
177 let mut magic = [0; 4];
178 f.read_exact(&mut magic)?;
179 if magic != [b'G', b'G', b'U', b'F'] {
180 if magic == [b'F', b'U', b'G', b'G'] {
181 return Err(ZyxError::parse_error(
182 "GGUF data seems to be stored in big endian order. Only little endian is supported for GGUF in zyx.".into(),
183 ));
184 }
185 return Err(ZyxError::parse_error(
186 format!("Unknown GGUF magic: {magic:?}. Please check your file.").into(),
187 ));
188 }
189 let mut version_bytes = [0; 4];
190 f.read_exact(&mut version_bytes)?;
191 let version = u32::from_le_bytes(version_bytes);
192 let mut tensor_count = [0u8; 8];
194 f.read_exact(&mut tensor_count)?;
195 let tensor_count = u64::from_le_bytes(tensor_count);
196 let mut metadata_kv_count = [0u8; 8];
197 f.read_exact(&mut metadata_kv_count)?;
198 let metadata_kv_count = usize::try_from(u64::from_le_bytes(metadata_kv_count))
199 .map_err(|e| ZyxError::parse_error(format!("Failed to parse tensor count in GGUF file. {e}").into()))?;
200
201 let mut metadata = HashMap::new();
202 for _ in 0..metadata_kv_count {
203 let mut metadata_key_len = [0; 8];
205 f.read_exact(&mut metadata_key_len)?;
206 let metadata_key_len = u64::from_le_bytes(metadata_key_len);
207 let mut metadata_key_bytes = vec![0u8; usize::try_from(metadata_key_len).unwrap()];
208 f.read_exact(&mut metadata_key_bytes)?;
209 let metadata_key = String::from_utf8(metadata_key_bytes)
210 .map_err(|e| ZyxError::parse_error(format!("GGUF metadata key is not valid UTF-8: {e}").into()))?;
211
212 let metadata_value_type = if version >= 3 {
215 let mut buf = [0; 4];
216 f.read_exact(&mut buf)?;
217 u32::from_le_bytes(buf)
218 } else {
219 let mut buf = [0; 1];
220 f.read_exact(&mut buf)?;
221 u32::from(u8::from_le_bytes(buf))
222 };
223 let metadata_value = match metadata_value_type {
224 0 => {
225 let mut buf = [0; 1];
226 f.read_exact(&mut buf)?;
227 GGUFMetadataValue::Uint8(u8::from_le_bytes(buf))
228 }
229 1 => {
230 let mut buf = [0; 1];
231 f.read_exact(&mut buf)?;
232 GGUFMetadataValue::Int8(i8::from_le_bytes(buf))
233 }
234 2 => {
235 let mut buf = [0; 2];
236 f.read_exact(&mut buf)?;
237 GGUFMetadataValue::Uint16(u16::from_le_bytes(buf))
238 }
239 3 => {
240 let mut buf = [0; 2];
241 f.read_exact(&mut buf)?;
242 GGUFMetadataValue::Int16(i16::from_le_bytes(buf))
243 }
244 4 => {
245 let mut buf = [0; 4];
246 f.read_exact(&mut buf)?;
247 GGUFMetadataValue::Uint32(u32::from_le_bytes(buf))
248 }
249 5 => {
250 let mut buf = [0; 4];
251 f.read_exact(&mut buf)?;
252 GGUFMetadataValue::Int32(i32::from_le_bytes(buf))
253 }
254 6 => {
255 let mut buf = [0; 4];
256 f.read_exact(&mut buf)?;
257 GGUFMetadataValue::Float32(f32::from_le_bytes(buf))
258 }
259 7 => {
260 let mut buf = [0; 1];
261 f.read_exact(&mut buf)?;
262 GGUFMetadataValue::Bool(buf[0] != 0)
263 }
264 8 => {
265 let mut str_len = [0; 8];
266 f.read_exact(&mut str_len)?;
267 let str_len = u64::from_le_bytes(str_len);
268 let mut s_bytes = vec![0u8; usize::try_from(str_len).unwrap()];
269 f.read_exact(&mut s_bytes)?;
270 let s = String::from_utf8(s_bytes)
271 .map_err(|e| ZyxError::parse_error(format!("GGUF metadata string is not valid UTF-8: {e}").into()))?;
272 GGUFMetadataValue::String(s)
273 }
274 9 => {
275 let mut arr_type_buf = [0; 4];
276 f.read_exact(&mut arr_type_buf)?;
277 let elem_type = u32::from_le_bytes(arr_type_buf);
278 let mut arr_len_buf = [0; 8];
279 f.read_exact(&mut arr_len_buf)?;
280 let arr_len = u64::from_le_bytes(arr_len_buf);
281 let mut items = Vec::with_capacity(usize::try_from(arr_len).unwrap());
282 for _ in 0..arr_len {
283 let item = match elem_type {
284 0 => {
285 let mut buf = [0; 1];
286 f.read_exact(&mut buf)?;
287 GGUFMetadataValue::Uint8(u8::from_le_bytes(buf))
288 }
289 1 => {
290 let mut buf = [0; 1];
291 f.read_exact(&mut buf)?;
292 GGUFMetadataValue::Int8(i8::from_le_bytes(buf))
293 }
294 2 => {
295 let mut buf = [0; 2];
296 f.read_exact(&mut buf)?;
297 GGUFMetadataValue::Uint16(u16::from_le_bytes(buf))
298 }
299 3 => {
300 let mut buf = [0; 2];
301 f.read_exact(&mut buf)?;
302 GGUFMetadataValue::Int16(i16::from_le_bytes(buf))
303 }
304 4 => {
305 let mut buf = [0; 4];
306 f.read_exact(&mut buf)?;
307 GGUFMetadataValue::Uint32(u32::from_le_bytes(buf))
308 }
309 5 => {
310 let mut buf = [0; 4];
311 f.read_exact(&mut buf)?;
312 GGUFMetadataValue::Int32(i32::from_le_bytes(buf))
313 }
314 6 => {
315 let mut buf = [0; 4];
316 f.read_exact(&mut buf)?;
317 GGUFMetadataValue::Float32(f32::from_le_bytes(buf))
318 }
319 7 => {
320 let mut buf = [0; 1];
321 f.read_exact(&mut buf)?;
322 GGUFMetadataValue::Bool(buf[0] != 0)
323 }
324 8 => {
325 let mut item_len = [0; 8];
326 f.read_exact(&mut item_len)?;
327 let item_len = u64::from_le_bytes(item_len);
328 let mut item_bytes = vec![0u8; usize::try_from(item_len).unwrap()];
329 f.read_exact(&mut item_bytes)?;
330 let item = String::from_utf8(item_bytes).map_err(|e| {
331 ZyxError::parse_error(format!("GGUF array element string is not valid UTF-8: {e}").into())
332 })?;
333 GGUFMetadataValue::String(item)
334 }
335 10 => {
336 let mut buf = [0; 8];
337 f.read_exact(&mut buf)?;
338 GGUFMetadataValue::Uint64(u64::from_le_bytes(buf))
339 }
340 11 => {
341 let mut buf = [0; 8];
342 f.read_exact(&mut buf)?;
343 GGUFMetadataValue::Int64(i64::from_le_bytes(buf))
344 }
345 12 => {
346 let mut buf = [0; 8];
347 f.read_exact(&mut buf)?;
348 GGUFMetadataValue::Float64(f64::from_le_bytes(buf))
349 }
350 x => todo!("GGUF array element type {x} not supported"),
351 };
352 items.push(item);
353 }
354 GGUFMetadataValue::Array(items.into_boxed_slice())
355 }
356 10 => {
357 let mut buf = [0; 8];
358 f.read_exact(&mut buf)?;
359 GGUFMetadataValue::Uint64(u64::from_le_bytes(buf))
360 }
361 11 => {
362 let mut buf = [0; 8];
363 f.read_exact(&mut buf)?;
364 GGUFMetadataValue::Int64(i64::from_le_bytes(buf))
365 }
366 12 => {
367 let mut buf = [0; 8];
368 f.read_exact(&mut buf)?;
369 GGUFMetadataValue::Float64(f64::from_le_bytes(buf))
370 }
371 x => todo!("GGUF metadata type {x} not supported"),
372 };
373 metadata.insert(metadata_key, metadata_value);
374 }
375
376 let mut tensor_header = Map::default();
378 for _ in 0..tensor_count {
379 let mut tensor_name_len = [0; 8];
381 f.read_exact(&mut tensor_name_len)?;
382 let tensor_name_len = u64::from_le_bytes(tensor_name_len);
383 let mut tensor_name_bytes = vec![0u8; usize::try_from(tensor_name_len).unwrap()];
384 f.read_exact(&mut tensor_name_bytes)?;
385 let tensor_name = String::from_utf8(tensor_name_bytes)
386 .map_err(|e| ZyxError::parse_error(format!("GGUF tensor name is not valid UTF-8: {e}").into()))?;
387
388 let mut rank = [0; 4];
390 f.read_exact(&mut rank)?;
391 let rank = u32::from_le_bytes(rank);
392
393 let mut shape = vec![0u8; rank as usize * 8];
395 f.read_exact(&mut shape)?;
396 let shape: Vec<Dim> = shape
397 .chunks_exact(8)
398 .map(|x| u64::from_le_bytes([x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]]))
399 .collect();
400
401 let mut dtype = [0; 4];
403 f.read_exact(&mut dtype)?;
404 let dtype = u32::from_le_bytes(dtype);
405 let dtype = match dtype {
406 0 => DType::F32,
407 1 => DType::F16,
408 24 => DType::I8,
409 25 => DType::I16,
410 26 => DType::I32,
411 27 => DType::I64,
412 28 => DType::F64,
413 x => todo!("GGUF dtype {x} is not supported by zyx yet."),
414 };
415
416 let mut offset = [0; 8];
418 f.read_exact(&mut offset)?;
419 let offset = u64::from_le_bytes(offset);
420
421 tensor_header.insert(tensor_name, (shape, dtype, offset));
422 }
423
424 let mut progress_bar = if RT.lock().debug.dev() {
425 println!("Loading tensors from safetensors file");
426 let bar = crate::prog_bar::ProgressBar::new(tensor_count);
427 Some(bar)
428 } else {
429 None
430 };
431
432 let mut tensors = HashMap::new();
433 for (name, (shape, dtype, offset)) in tensor_header {
434 if let Some(progress_bar) = &mut progress_bar {
435 progress_bar.inc(1, &format!("{name}, {shape:?}, {dtype}"));
436 }
437 tensors.insert(name, Tensor::from_path(shape, dtype, &path, offset)?);
438 }
439 Ok((metadata, tensors))
440 }
441
442 #[allow(clippy::missing_panics_doc)]
447 pub fn load_safetensors(path: impl AsRef<Path>) -> Result<HashMap<String, Tensor>, ZyxError> {
448 use std::io::Read;
449 let mut f = std::fs::File::open(&path)?;
450 let mut header_len = [0u8; 8];
452 f.read_exact(&mut header_len)?;
453 let n = usize::try_from(u64::from_le_bytes(header_len))
454 .map_err(|e| ZyxError::parse_error(format!("Failed to parse header len in safetensors file. {e}").into()))?;
455 let mut header = vec![0u8; n];
456 f.read_exact(&mut header)?;
457 let header = core::str::from_utf8(&header).map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
458 let mut text = String::with_capacity(10);
459 let mut begin_str = false;
460 let mut i = 0;
461 let mut tensors = HashMap::default();
462 let mut dtype = DType::F32;
463 let mut shape = vec![1];
464 let mut label = String::new();
465 let mut metadata = true;
466 let mut progress_bar = if RT.lock().debug.dev() {
467 println!("Loading tensors from safetensors file");
468 let bar = crate::prog_bar::ProgressBar::new(u64::try_from(header.chars().filter(|&c| c == '[').count()).unwrap() / 2);
469 Some(bar)
470 } else {
471 None
472 };
473 let mut offset = (8 + header.len()) as u64;
477 for x in header.chars() {
478 if metadata && text.starts_with("__metadata__") {
480 if x == '}' {
481 text.clear();
482 begin_str = false;
483 metadata = false;
484 }
485 continue;
486 }
487 if ['"', '[', ']'].contains(&x) {
488 if begin_str {
489 if i % 7 == 0 {
491 #[allow(clippy::assigning_clones)]
492 {
493 label = text.clone();
494 }
495 } else if i % 7 == 2 {
496 dtype = DType::from_safetensors(&text)?;
497 } else if i % 7 == 4 {
498 shape = text
499 .split(',')
500 .map(|d| {
501 d.parse::<u64>()
502 .map_err(|err| ZyxError::parse_error(format!("Cannot parse safetensors shape: {err}").into()))
503 })
504 .collect::<Result<_, ZyxError>>()?;
505 } else if i % 7 == 6 {
506 let offsets = text
509 .split(',')
510 .map(|offset| {
511 offset.parse::<u64>().map_err(|err| {
512 ZyxError::parse_error(format!("Could not parse safetensors offset: {err}").into())
513 })
514 })
515 .collect::<Result<Vec<_>, ZyxError>>()?;
516 let bytes = shape.iter().product::<Dim>() * Dim::from(dtype.bit_size() / 8);
518 if offsets[1] - offsets[0] != bytes {
519 return Err(ZyxError::parse_error("Safetensors shapes and offsets are incorrect.".into()));
520 }
521 if let Some(bar) = &mut progress_bar {
522 bar.inc(1, &format!("{label}, {shape:?}, {dtype:?}"));
523 }
524 let tensor = Tensor::from_path(shape.clone(), dtype, &path, offset)?;
525 offset += bytes as u64;
526 tensors.insert(label.clone(), tensor);
527 }
528 i += 1;
529 text.clear();
530 begin_str = false;
531 } else {
532 text.clear();
533 begin_str = true;
534 }
535 } else {
536 text.push(x);
537 }
538 }
539 Ok(tensors)
540 }
541}