1use std::{collections::HashMap, ffi::OsStr, fs::File, io::Seek, path::Path};
5
6use crate::{DType, Map, 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 set_params(&mut self, params: &mut HashMap<String, Tensor>) {
24 for (label, tensor) in self.iter_tensors_mut() {
25 if let Some(param) = params.remove(&label) {
26 *tensor = param;
27 }
28 }
29 }
30
31 fn save(&self, path: impl AsRef<Path>) -> Result<(), ZyxError> {
38 use std::fmt::Write;
39 use std::io::Write as IOWrite;
40 let mut f = File::create(path)?;
41 let mut header = String::from("{");
42 let mut begin = 0;
43 for (label, tensor) in self.iter_tensors() {
44 let dtype = tensor.dtype();
45 write!(header, "\"{label}\":{{").unwrap();
46 write!(header, "\"dtype\":\"{}\",", dtype.safetensors()).unwrap();
47 let mut st_shape = format!("{:?}", tensor.resolve_shape());
48 st_shape.retain(|c| !c.is_whitespace());
49 write!(header, "\"shape\":{st_shape},").unwrap();
50 let size = tensor.numel().item::<Dim>() * Dim::from(dtype.bit_size() / 8);
51 write!(header, "\"data_offsets\":[{},{}]", begin, begin + size).unwrap();
52 begin += size;
53 write!(header, "}},").unwrap();
54 }
55 header.pop();
56 write!(header, "}}").unwrap();
57 let header_bytes = header.as_bytes();
58 f.write_all(&(header_bytes.len() as i64).to_le_bytes())?;
59 f.write_all(header_bytes)?;
60 for tensor in self.iter() {
61 f.write_all(&tensor.to_le_bytes()?)?;
62 }
63 Ok(())
64 }
65
66 fn save_numpy(&self, path: impl AsRef<Path>) -> Result<(), ZyxError> {
77 use std::io::Write as IOWrite;
78 let mut tensors = self.iter_tensors();
79 let (label, tensor) = match (tensors.next(), tensors.next()) {
80 (Some((label, tensor)), None) => (label, tensor),
81 (None, _) => return Err(ZyxError::parse_error("Cannot save empty module to numpy: no tensors.".into())),
82 (Some((l0, _)), Some((l1, _))) => {
83 return Err(ZyxError::parse_error(
84 format!(
85 "Cannot save module to numpy: numpy files hold a single array, module has tensors '{l0}' and '{l1}' (and possibly more)."
86 )
87 .into(),
88 ));
89 }
90 };
91 let _ = label;
92 let descr = match tensor.dtype() {
93 DType::F32 => "<f4",
94 DType::F64 => "<f8",
95 DType::F16 => "<f2",
96 DType::I8 => "|i1",
97 DType::I16 => "<i2",
98 DType::I32 => "<i4",
99 DType::I64 => "<i8",
100 DType::U8 => "|u1",
101 DType::U16 => "<u2",
102 DType::BF16 => todo!("BF16 has no numpy dtype"),
103 DType::U32 => todo!("u4 numpy arrays"),
104 DType::U64 => todo!("u8 numpy arrays"),
105 DType::Bool => todo!("Bool numpy arrays"),
106 DType::F8E4M3 => todo!("F8E4M3 numpy arrays"),
107 DType::F8E5M2 => todo!("F8E5M2 numpy arrays"),
108 };
109 let dims = tensor.resolve_shape();
110 let shape_str = format!("({})", dims.iter().map(|d| d.to_string()).collect::<Vec<_>>().join(", "));
111 let mut header = format!("{{'descr': '{descr}', 'fortran_order': False, 'shape': {shape_str}, }}");
112 let total = 6 + 2 + 2 + header.len() + 1;
115 header.extend(core::iter::repeat(' ').take((64 - total % 64) % 64));
116 header.push('\n');
117 let mut f = File::create(path)?;
118 f.write_all(b"\x93NUMPY")?;
119 f.write_all(&[1u8, 0u8])?;
120 f.write_all(&(header.len() as u16).to_le_bytes())?;
121 f.write_all(header.as_bytes())?;
122 f.write_all(&tensor.to_le_bytes()?)?;
123 Ok(())
124 }
125}
126
127#[allow(unused)]
134pub enum GGUFMetadataValue {
135 Uint8(u8),
137 Int8(i8),
139 Uint16(u16),
141 Int16(i16),
143 Uint32(u32),
145 Int32(i32),
147 Uint64(u64),
149 Int64(i64),
151 Float32(f32),
153 Float64(f64),
155 Bool(bool),
157 String(String),
159 Array(Box<[GGUFMetadataValue]>),
161}
162
163impl<S: std::hash::BuildHasher + Default> Module for HashMap<String, Tensor, S> {
164 fn iter(&self) -> impl Iterator<Item = &Tensor> {
165 self.values()
166 }
167
168 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor> {
169 self.values_mut()
170 }
171
172 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)> {
173 self.iter().map(|(k, v): (&String, &Tensor)| (k.clone(), v))
174 }
175
176 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)> {
177 self.iter_mut().map(|(k, v): (&String, &mut Tensor)| (k.clone(), v))
178 }
179}
180
181impl Module for Vec<Tensor> {
182 #[allow(clippy::into_iter_on_ref)] fn iter(&self) -> impl Iterator<Item = &Tensor> {
184 self.into_iter()
185 }
186
187 #[allow(clippy::into_iter_on_ref)] fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor> {
189 self.into_iter()
190 }
191
192 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)> {
193 self.iter().map(|t: &Tensor| (format!("{}", t.id()), t))
194 }
195
196 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)> {
197 self.iter_mut().map(|t: &mut Tensor| (format!("{}", t.id()), t))
198 }
199}
200
201impl<M0: Module, M1: Module> Module for (M0, M1) {
202 fn iter(&self) -> impl Iterator<Item = &Tensor> {
203 self.0.iter().chain(self.1.iter())
204 }
205
206 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tensor> {
207 self.0.iter_mut().chain(self.1.iter_mut())
208 }
209
210 fn iter_tensors(&self) -> impl Iterator<Item = (String, &Tensor)> {
211 self.0.iter_tensors().chain(self.1.iter_tensors())
212 }
213
214 fn iter_tensors_mut(&mut self) -> impl Iterator<Item = (String, &mut Tensor)> {
215 self.0.iter_tensors_mut().chain(self.1.iter_tensors_mut())
216 }
217}
218
219impl Tensor {
220 pub fn load(path: impl AsRef<Path>) -> Result<HashMap<String, Tensor>, ZyxError>
227 where
228 Self: Sized,
229 {
230 let e = path.as_ref().extension().and_then(OsStr::to_str);
231 match e {
232 Some("safetensors") => Self::load_safetensors(path),
233 Some("gguf") => Ok(Self::load_gguf(path)?.1),
234 Some(other) => Err(ZyxError::parse_error(
235 format!("Unknown file extension '{other}'. Zyx currently supports only safetensors and gguf formats.").into(),
236 )),
237 None => Err(ZyxError::parse_error(
238 format!("Cannot determine file type: '{}' has no extension. Zyx currently supports only safetensors and gguf formats.", path.as_ref().display()).into(),
239 )),
240 }
241 }
242
243 #[allow(clippy::missing_panics_doc)]
248 #[allow(clippy::type_complexity)]
249 pub fn load_gguf(path: impl AsRef<Path>) -> Result<(HashMap<String, GGUFMetadataValue>, HashMap<String, Tensor>), ZyxError> {
250 use std::io::Read;
251 let mut f = std::fs::File::open(&path)?;
252 let mut magic = [0; 4];
253 f.read_exact(&mut magic)?;
254 if magic != *b"GGUF" {
255 if magic == *b"FUGG" {
256 return Err(ZyxError::parse_error(
257 "GGUF data seems to be stored in big endian order. Only little endian is supported for GGUF in zyx.".into(),
258 ));
259 }
260 return Err(ZyxError::parse_error(format!("Unknown GGUF magic: {magic:?}. Please check your file.").into()));
261 }
262 let mut version_bytes = [0; 4];
263 f.read_exact(&mut version_bytes)?;
264 let version = u32::from_le_bytes(version_bytes);
265 let mut tensor_count = [0u8; 8];
267 f.read_exact(&mut tensor_count)?;
268 let tensor_count = u64::from_le_bytes(tensor_count);
269 let mut metadata_kv_count = [0u8; 8];
270 f.read_exact(&mut metadata_kv_count)?;
271 let metadata_kv_count = usize::try_from(u64::from_le_bytes(metadata_kv_count))
272 .map_err(|e| ZyxError::parse_error(format!("Failed to parse tensor count in GGUF file. {e}").into()))?;
273
274 let mut metadata = HashMap::new();
275 for _ in 0..metadata_kv_count {
276 let mut metadata_key_len = [0; 8];
278 f.read_exact(&mut metadata_key_len)?;
279 let metadata_key_len = u64::from_le_bytes(metadata_key_len);
280 let mut metadata_key_bytes = vec![0u8; usize::try_from(metadata_key_len).unwrap()];
281 f.read_exact(&mut metadata_key_bytes)?;
282 let metadata_key = String::from_utf8(metadata_key_bytes)
283 .map_err(|e| ZyxError::parse_error(format!("GGUF metadata key is not valid UTF-8: {e}").into()))?;
284
285 let metadata_value_type = if version >= 3 {
288 let mut buf = [0; 4];
289 f.read_exact(&mut buf)?;
290 u32::from_le_bytes(buf)
291 } else {
292 let mut buf = [0; 1];
293 f.read_exact(&mut buf)?;
294 u32::from(u8::from_le_bytes(buf))
295 };
296 let metadata_value = match metadata_value_type {
297 0 => {
298 let mut buf = [0; 1];
299 f.read_exact(&mut buf)?;
300 GGUFMetadataValue::Uint8(u8::from_le_bytes(buf))
301 }
302 1 => {
303 let mut buf = [0; 1];
304 f.read_exact(&mut buf)?;
305 GGUFMetadataValue::Int8(i8::from_le_bytes(buf))
306 }
307 2 => {
308 let mut buf = [0; 2];
309 f.read_exact(&mut buf)?;
310 GGUFMetadataValue::Uint16(u16::from_le_bytes(buf))
311 }
312 3 => {
313 let mut buf = [0; 2];
314 f.read_exact(&mut buf)?;
315 GGUFMetadataValue::Int16(i16::from_le_bytes(buf))
316 }
317 4 => {
318 let mut buf = [0; 4];
319 f.read_exact(&mut buf)?;
320 GGUFMetadataValue::Uint32(u32::from_le_bytes(buf))
321 }
322 5 => {
323 let mut buf = [0; 4];
324 f.read_exact(&mut buf)?;
325 GGUFMetadataValue::Int32(i32::from_le_bytes(buf))
326 }
327 6 => {
328 let mut buf = [0; 4];
329 f.read_exact(&mut buf)?;
330 GGUFMetadataValue::Float32(f32::from_le_bytes(buf))
331 }
332 7 => {
333 let mut buf = [0; 1];
334 f.read_exact(&mut buf)?;
335 GGUFMetadataValue::Bool(buf[0] != 0)
336 }
337 8 => {
338 let mut str_len = [0; 8];
339 f.read_exact(&mut str_len)?;
340 let str_len = u64::from_le_bytes(str_len);
341 let mut s_bytes = vec![0u8; usize::try_from(str_len).unwrap()];
342 f.read_exact(&mut s_bytes)?;
343 let s = String::from_utf8(s_bytes)
344 .map_err(|e| ZyxError::parse_error(format!("GGUF metadata string is not valid UTF-8: {e}").into()))?;
345 GGUFMetadataValue::String(s)
346 }
347 9 => {
348 let mut arr_type_buf = [0; 4];
349 f.read_exact(&mut arr_type_buf)?;
350 let elem_type = u32::from_le_bytes(arr_type_buf);
351 let mut arr_len_buf = [0; 8];
352 f.read_exact(&mut arr_len_buf)?;
353 let arr_len = u64::from_le_bytes(arr_len_buf);
354 let mut items = Vec::with_capacity(usize::try_from(arr_len).unwrap());
355 for _ in 0..arr_len {
356 let item = match elem_type {
357 0 => {
358 let mut buf = [0; 1];
359 f.read_exact(&mut buf)?;
360 GGUFMetadataValue::Uint8(u8::from_le_bytes(buf))
361 }
362 1 => {
363 let mut buf = [0; 1];
364 f.read_exact(&mut buf)?;
365 GGUFMetadataValue::Int8(i8::from_le_bytes(buf))
366 }
367 2 => {
368 let mut buf = [0; 2];
369 f.read_exact(&mut buf)?;
370 GGUFMetadataValue::Uint16(u16::from_le_bytes(buf))
371 }
372 3 => {
373 let mut buf = [0; 2];
374 f.read_exact(&mut buf)?;
375 GGUFMetadataValue::Int16(i16::from_le_bytes(buf))
376 }
377 4 => {
378 let mut buf = [0; 4];
379 f.read_exact(&mut buf)?;
380 GGUFMetadataValue::Uint32(u32::from_le_bytes(buf))
381 }
382 5 => {
383 let mut buf = [0; 4];
384 f.read_exact(&mut buf)?;
385 GGUFMetadataValue::Int32(i32::from_le_bytes(buf))
386 }
387 6 => {
388 let mut buf = [0; 4];
389 f.read_exact(&mut buf)?;
390 GGUFMetadataValue::Float32(f32::from_le_bytes(buf))
391 }
392 7 => {
393 let mut buf = [0; 1];
394 f.read_exact(&mut buf)?;
395 GGUFMetadataValue::Bool(buf[0] != 0)
396 }
397 8 => {
398 let mut item_len = [0; 8];
399 f.read_exact(&mut item_len)?;
400 let item_len = u64::from_le_bytes(item_len);
401 let mut item_bytes = vec![0u8; usize::try_from(item_len).unwrap()];
402 f.read_exact(&mut item_bytes)?;
403 let item = String::from_utf8(item_bytes).map_err(|e| {
404 ZyxError::parse_error(format!("GGUF array element string is not valid UTF-8: {e}").into())
405 })?;
406 GGUFMetadataValue::String(item)
407 }
408 10 => {
409 let mut buf = [0; 8];
410 f.read_exact(&mut buf)?;
411 GGUFMetadataValue::Uint64(u64::from_le_bytes(buf))
412 }
413 11 => {
414 let mut buf = [0; 8];
415 f.read_exact(&mut buf)?;
416 GGUFMetadataValue::Int64(i64::from_le_bytes(buf))
417 }
418 12 => {
419 let mut buf = [0; 8];
420 f.read_exact(&mut buf)?;
421 GGUFMetadataValue::Float64(f64::from_le_bytes(buf))
422 }
423 x => todo!("GGUF array element type {x} not supported"),
424 };
425 items.push(item);
426 }
427 GGUFMetadataValue::Array(items.into_boxed_slice())
428 }
429 10 => {
430 let mut buf = [0; 8];
431 f.read_exact(&mut buf)?;
432 GGUFMetadataValue::Uint64(u64::from_le_bytes(buf))
433 }
434 11 => {
435 let mut buf = [0; 8];
436 f.read_exact(&mut buf)?;
437 GGUFMetadataValue::Int64(i64::from_le_bytes(buf))
438 }
439 12 => {
440 let mut buf = [0; 8];
441 f.read_exact(&mut buf)?;
442 GGUFMetadataValue::Float64(f64::from_le_bytes(buf))
443 }
444 x => todo!("GGUF metadata type {x} not supported"),
445 };
446 metadata.insert(metadata_key, metadata_value);
447 }
448
449 let mut tensor_header = Map::default();
451 for _ in 0..tensor_count {
452 let mut tensor_name_len = [0; 8];
454 f.read_exact(&mut tensor_name_len)?;
455 let tensor_name_len = u64::from_le_bytes(tensor_name_len);
456 let mut tensor_name_bytes = vec![0u8; usize::try_from(tensor_name_len).unwrap()];
457 f.read_exact(&mut tensor_name_bytes)?;
458 let tensor_name = String::from_utf8(tensor_name_bytes)
459 .map_err(|e| ZyxError::parse_error(format!("GGUF tensor name is not valid UTF-8: {e}").into()))?;
460
461 let mut rank = [0; 4];
463 f.read_exact(&mut rank)?;
464 let rank = u32::from_le_bytes(rank);
465
466 let mut shape = vec![0u8; rank as usize * 8];
468 f.read_exact(&mut shape)?;
469 let shape: Vec<Dim> =
470 shape.chunks_exact(8).map(|x| i64::from_le_bytes([x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]])).collect();
471
472 let mut dtype = [0; 4];
474 f.read_exact(&mut dtype)?;
475 let dtype = u32::from_le_bytes(dtype);
476 let (dtype, shape) = match dtype {
482 0 => (DType::F32, shape),
483 1 => (DType::F16, shape),
484 24 => (DType::I8, shape),
485 25 => (DType::I16, shape),
486 26 => (DType::I32, shape),
487 27 => (DType::I64, shape),
488 28 => (DType::F64, shape),
489 12 => {
490 let numel: Dim = shape.iter().product();
491 debug_assert!(numel % 256 == 0, "Q4_K tensor {tensor_name} has {numel} elements, not a multiple of 256");
492 (DType::U8, vec![numel / 256, 144])
493 }
494 8 => {
496 let numel: Dim = shape.iter().product();
497 debug_assert!(numel % 32 == 0, "Q8_0 tensor {tensor_name} has {numel} elements, not a multiple of 32");
498 (DType::U8, vec![numel / 32, 34])
499 }
500 11 => {
502 let numel: Dim = shape.iter().product();
503 debug_assert!(numel % 256 == 0, "Q3_K tensor {tensor_name} has {numel} elements, not a multiple of 256");
504 (DType::U8, vec![numel / 256, 110])
505 }
506 13 => {
508 let numel: Dim = shape.iter().product();
509 debug_assert!(numel % 256 == 0, "Q5_K tensor {tensor_name} has {numel} elements, not a multiple of 256");
510 (DType::U8, vec![numel / 256, 176])
511 }
512 14 => {
514 let numel: Dim = shape.iter().product();
515 debug_assert!(numel % 256 == 0, "Q6_K tensor {tensor_name} has {numel} elements, not a multiple of 256");
516 (DType::U8, vec![numel / 256, 210])
517 }
518 20 => {
520 let numel: Dim = shape.iter().product();
521 debug_assert!(numel % 32 == 0, "IQ4_NL tensor {tensor_name} has {numel} elements, not a multiple of 32");
522 (DType::U8, vec![numel / 32, 18])
523 }
524 21 => {
526 let numel: Dim = shape.iter().product();
527 debug_assert!(numel % 256 == 0, "IQ3_S tensor {tensor_name} has {numel} elements, not a multiple of 256");
528 (DType::U8, vec![numel / 256, 110])
529 }
530 23 => {
532 let numel: Dim = shape.iter().product();
533 debug_assert!(numel % 256 == 0, "IQ4_XS tensor {tensor_name} has {numel} elements, not a multiple of 256");
534 (DType::U8, vec![numel / 256, 136])
535 }
536 2 => {
540 let numel: Dim = shape.iter().product();
541 debug_assert!(numel % 32 == 0, "Q4_0 tensor {tensor_name} has {numel} elements, not a multiple of 32");
542 (DType::U8, vec![numel / 32, 18])
543 }
544 3 => {
546 let numel: Dim = shape.iter().product();
547 debug_assert!(numel % 32 == 0, "Q4_1 tensor {tensor_name} has {numel} elements, not a multiple of 32");
548 (DType::U8, vec![numel / 32, 20])
549 }
550 6 => {
552 let numel: Dim = shape.iter().product();
553 debug_assert!(numel % 32 == 0, "Q5_0 tensor {tensor_name} has {numel} elements, not a multiple of 32");
554 (DType::U8, vec![numel / 32, 22])
555 }
556 7 => {
558 let numel: Dim = shape.iter().product();
559 debug_assert!(numel % 32 == 0, "Q5_1 tensor {tensor_name} has {numel} elements, not a multiple of 32");
560 (DType::U8, vec![numel / 32, 24])
561 }
562 9 => {
564 let numel: Dim = shape.iter().product();
565 debug_assert!(numel % 32 == 0, "Q8_1 tensor {tensor_name} has {numel} elements, not a multiple of 32");
566 (DType::U8, vec![numel / 32, 36])
567 }
568 10 => {
570 let numel: Dim = shape.iter().product();
571 debug_assert!(numel % 256 == 0, "Q2_K tensor {tensor_name} has {numel} elements, not a multiple of 256");
572 (DType::U8, vec![numel / 256, 84])
573 }
574 15 => {
576 let numel: Dim = shape.iter().product();
577 debug_assert!(numel % 256 == 0, "Q8_K tensor {tensor_name} has {numel} elements, not a multiple of 256");
578 (DType::U8, vec![numel / 256, 292])
579 }
580 16 => {
582 let numel: Dim = shape.iter().product();
583 debug_assert!(numel % 256 == 0, "IQ2_XXS tensor {tensor_name} has {numel} elements, not a multiple of 256");
584 (DType::U8, vec![numel / 256, 66])
585 }
586 17 => {
588 let numel: Dim = shape.iter().product();
589 debug_assert!(numel % 256 == 0, "IQ2_XS tensor {tensor_name} has {numel} elements, not a multiple of 256");
590 (DType::U8, vec![numel / 256, 74])
591 }
592 18 => {
594 let numel: Dim = shape.iter().product();
595 debug_assert!(numel % 256 == 0, "IQ3_XXS tensor {tensor_name} has {numel} elements, not a multiple of 256");
596 (DType::U8, vec![numel / 256, 98])
597 }
598 19 => {
600 let numel: Dim = shape.iter().product();
601 debug_assert!(numel % 256 == 0, "IQ1_S tensor {tensor_name} has {numel} elements, not a multiple of 256");
602 (DType::U8, vec![numel / 256, 50])
603 }
604 22 => {
606 let numel: Dim = shape.iter().product();
607 debug_assert!(numel % 256 == 0, "IQ2_S tensor {tensor_name} has {numel} elements, not a multiple of 256");
608 (DType::U8, vec![numel / 256, 82])
609 }
610 29 => {
612 let numel: Dim = shape.iter().product();
613 debug_assert!(numel % 256 == 0, "IQ1_M tensor {tensor_name} has {numel} elements, not a multiple of 256");
614 (DType::U8, vec![numel / 256, 56])
615 }
616 x => todo!("GGUF dtype {x} is not supported by zyx yet."),
617 };
618
619 let mut offset = [0; 8];
621 f.read_exact(&mut offset)?;
622 let offset = u64::from_le_bytes(offset);
623
624 tensor_header.insert(tensor_name, (shape, dtype, offset));
625 }
626
627 let alignment = match metadata.get("general.alignment") {
632 Some(GGUFMetadataValue::Uint32(a)) => (*a as usize).max(1),
633 Some(_) => todo!("general.alignment must be Uint32"),
634 None => 32,
635 };
636 let data_start = f.stream_position()? as usize;
637 let data_start = data_start.div_ceil(alignment) * alignment;
638
639 let mut progress_bar = if crate::debug_mask().dev() {
640 println!("Loading tensors from safetensors file");
641 let bar = crate::progress::ProgressBar::new(tensor_count);
642 Some(bar)
643 } else {
644 None
645 };
646
647 let mut tensors = HashMap::new();
648 for (name, (shape, dtype, offset)) in tensor_header {
649 if let Some(progress_bar) = &mut progress_bar {
650 progress_bar.inc(1, &format!("{name}, {shape:?}, {dtype}"));
651 }
652 tensors.insert(name, Tensor::from_path(shape, dtype, &path, (data_start as u64) + offset)?);
653 }
654 Ok((metadata, tensors))
655 }
656
657 pub fn load_numpy(path: impl AsRef<Path>) -> Result<Tensor, ZyxError> {
668 use std::io::Read;
669 let path = path.as_ref();
670 let mut f = File::open(path)?;
671 let mut magic = [0; 6];
672 f.read_exact(&mut magic)?;
673 if magic != *b"\x93NUMPY" {
674 return Err(ZyxError::parse_error(format!("Unknown numpy magic: {magic:?} in {path:?}").into()));
675 }
676 let mut ver = [0; 2];
677 f.read_exact(&mut ver)?;
678 let header_len = match ver[0] {
680 1 => {
681 let mut buf = [0; 2];
682 f.read_exact(&mut buf)?;
683 u16::from_le_bytes(buf) as usize
684 }
685 2 | 3 => {
686 let mut buf = [0; 4];
687 f.read_exact(&mut buf)?;
688 u32::from_le_bytes(buf) as usize
689 }
690 x => return Err(ZyxError::parse_error(format!("Unsupported numpy version {x} in {path:?}").into())),
691 };
692 let mut header = vec![0u8; header_len];
693 f.read_exact(&mut header)?;
694 let header = String::from_utf8(header)
695 .map_err(|e| ZyxError::parse_error(format!("numpy header is not valid UTF-8: {e} in {path:?}").into()))?;
696 let field = |key: &str| -> Option<String> {
698 let start = header.find(&format!("'{key}':"))? + key.len() + 4;
699 let rest = &header[start..];
701 let end = rest.find(|c| c == ',' || c == '}').unwrap_or(rest.len());
702 Some(rest[..end].trim().to_string())
703 };
704 let descr =
705 field("descr").ok_or_else(|| ZyxError::parse_error(format!("numpy header missing 'descr' in {path:?}").into()))?;
706 let descr = descr.trim_matches(|c| c == '\'' || c == '"').to_string();
707 let fortran = field("fortran_order").unwrap_or_default();
708 if fortran.contains("True") {
709 return Err(ZyxError::parse_error(format!("Fortran-order numpy arrays are not supported: {path:?}").into()));
710 }
711 let shape_start = header
715 .find("'shape':")
716 .ok_or_else(|| ZyxError::parse_error(format!("numpy header missing 'shape' in {path:?}").into()))?
717 + 8;
718 let rest = &header[shape_start..];
719 let end = rest.find('}').unwrap_or(rest.len());
720 let shape_str = rest[..end].trim().trim_end_matches(',').trim();
721 let shape_str = shape_str.trim_matches(|c| c == '(' || c == ')');
722 let shape: Vec<Dim> = shape_str
723 .split(',')
724 .filter(|d| !d.trim().is_empty())
725 .map(|d| {
726 d.trim()
727 .parse::<Dim>()
728 .map_err(|e| ZyxError::parse_error(format!("Cannot parse numpy shape '{shape_str}': {e} in {path:?}").into()))
729 })
730 .collect::<Result<_, ZyxError>>()?;
731 let dtype = match descr.as_str() {
732 "<f4" | "|f4" | "f4" => DType::F32,
733 "<f2" | "|f2" | "f2" => DType::F16,
734 "<f8" | "|f8" | "f8" => DType::F64,
735 "<i1" | "|i1" => DType::I8,
736 "<i2" | "|i2" => DType::I16,
737 "<i4" | "|i4" => DType::I32,
738 "<i8" | "|i8" => DType::I64,
739 "|u1" | "<u1" | "u1" => DType::U8,
740 "<u2" | "|u2" => DType::U16,
741 "<u4" | "|u4" => todo!("u4 numpy arrays"),
742 "<u8" | "|u8" => todo!("u8 numpy arrays"),
743 x => todo!("numpy dtype '{x}' is not supported ({path:?})"),
744 };
745 let data_start = f.stream_position()?;
749 Tensor::from_path(shape, dtype, path, data_start)
750 }
751
752 #[allow(clippy::missing_panics_doc)]
757 pub fn load_safetensors(path: impl AsRef<Path>) -> Result<HashMap<String, Tensor>, ZyxError> {
758 use std::io::Read;
759 let mut f = std::fs::File::open(&path)?;
760 let mut header_len = [0u8; 8];
762 f.read_exact(&mut header_len)?;
763 let n = usize::try_from(u64::from_le_bytes(header_len))
764 .map_err(|e| ZyxError::parse_error(format!("Failed to parse header len in safetensors file. {e}").into()))?;
765 let mut header = vec![0u8; n];
766 f.read_exact(&mut header)?;
767 let header = core::str::from_utf8(&header).map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
768 let mut text = String::with_capacity(10);
769 let mut begin_str = false;
770 let mut i = 0;
771 let mut tensors = HashMap::default();
772 let mut dtype = DType::F32;
773 let mut shape = vec![1i64];
774 let mut label = String::new();
775 let mut metadata = true;
776 let mut progress_bar = if crate::debug_mask().dev() {
777 println!("Loading tensors from safetensors file");
778 let bar = crate::progress::ProgressBar::new(u64::try_from(header.chars().filter(|&c| c == '[').count()).unwrap() / 2);
779 Some(bar)
780 } else {
781 None
782 };
783 let mut offset = (8 + header.len()) as i64;
787 for x in header.chars() {
788 if metadata && text.starts_with("__metadata__") {
790 if x == '}' {
791 text.clear();
792 begin_str = false;
793 metadata = false;
794 }
795 continue;
796 }
797 if ['"', '[', ']'].contains(&x) {
798 if begin_str {
799 if i % 7 == 0 {
801 #[allow(clippy::assigning_clones)]
802 {
803 label = text.clone();
804 }
805 } else if i % 7 == 2 {
806 dtype = DType::from_safetensors(&text)?;
807 } else if i % 7 == 4 {
808 shape = text
809 .split(',')
810 .map(|d| {
811 d.parse::<Dim>()
812 .map_err(|err| ZyxError::parse_error(format!("Cannot parse safetensors shape: {err}").into()))
813 })
814 .collect::<Result<_, ZyxError>>()?;
815 } else if i % 7 == 6 {
816 let offsets = text
819 .split(',')
820 .map(|offset| {
821 offset.trim().parse::<u64>().map_err(|err| {
824 ZyxError::parse_error(format!("Could not parse safetensors offset: {err}").into())
825 })
826 })
827 .collect::<Result<Vec<_>, ZyxError>>()?;
828 let bytes = shape.iter().product::<Dim>() * Dim::from(dtype.bit_size() / 8);
830 if offsets[1] - offsets[0] != bytes as u64 {
831 return Err(ZyxError::parse_error("Safetensors shapes and offsets are incorrect.".into()));
832 }
833 if let Some(bar) = &mut progress_bar {
834 bar.inc(1, &format!("{label}, {shape:?}, {dtype:?}"));
835 }
836 let tensor = Tensor::from_path(shape.clone(), dtype, &path, offset as u64)?;
837 offset += bytes as i64;
838 tensors.insert(label.clone(), tensor);
839 }
840 i += 1;
841 text.clear();
842 begin_str = false;
843 } else {
844 text.clear();
845 begin_str = true;
846 }
847 } else {
848 text.push(x);
849 }
850 }
851 Ok(tensors)
852 }
853}