1use std::fs::File;
41use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
42use std::path::Path;
43
44use presk_syntax::ast::{self, AxisSpec, Expr, Lit};
45use presk_syntax::{SourceMap, parse_type};
46use rand_chacha::ChaCha20Rng;
47use rand_core::{RngCore, SeedableRng};
48
49pub const MAGIC: &[u8; 4] = b"PSKD";
50pub const VERSION: u16 = 1;
52pub const VERSION_ENCODED: u16 = 2;
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum Enc {
59 F64,
60 F32,
61 Bf16,
62}
63
64impl Enc {
65 pub fn code(self) -> u8 {
66 match self {
67 Enc::F64 => 0,
68 Enc::F32 => 1,
69 Enc::Bf16 => 2,
70 }
71 }
72
73 pub fn of_code(c: u8) -> Option<Enc> {
74 Some(match c {
75 0 => Enc::F64,
76 1 => Enc::F32,
77 2 => Enc::Bf16,
78 _ => return None,
79 })
80 }
81
82 pub fn name(self) -> &'static str {
83 match self {
84 Enc::F64 => "f64",
85 Enc::F32 => "f32",
86 Enc::Bf16 => "bf16",
87 }
88 }
89
90 pub fn width(self) -> usize {
92 match self {
93 Enc::F64 => 8,
94 Enc::F32 => 4,
95 Enc::Bf16 => 2,
96 }
97 }
98
99 fn encode(self, x: f64, out: &mut Vec<u8>) -> Result<()> {
101 match self {
102 Enc::F64 => out.extend_from_slice(&x.to_le_bytes()),
103 Enc::F32 => {
104 let y = x as f32;
105 if f64::from(y).to_bits() != x.to_bits() && !(x.is_nan() && y.is_nan()) {
106 return err("E9004", format!("{x} is not a number of f32"));
107 }
108 out.extend_from_slice(&y.to_le_bytes());
109 }
110 Enc::Bf16 => {
111 let y = x as f32;
112 let bits = y.to_bits();
113 if f64::from(y).to_bits() != x.to_bits() && !(x.is_nan() && y.is_nan()) || bits & 0xffff != 0 {
114 return err("E9004", format!("{x} is not a number of bf16"));
115 }
116 out.extend_from_slice(&((bits >> 16) as u16).to_le_bytes());
117 }
118 }
119 Ok(())
120 }
121
122 fn decode(self, b: &[u8]) -> f64 {
124 match self {
125 Enc::F64 => f64::from_le_bytes(b[..8].try_into().unwrap()),
126 Enc::F32 => f64::from(f32::from_le_bytes(b[..4].try_into().unwrap())),
127 Enc::Bf16 => f64::from(f32::from_bits(u32::from(u16::from_le_bytes(b[..2].try_into().unwrap())) << 16)),
128 }
129 }
130}
131
132#[derive(Clone, Debug)]
133pub struct DataError {
134 pub code: &'static str,
135 pub message: String,
136}
137
138impl std::fmt::Display for DataError {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 write!(f, "[{}] {}", self.code, self.message)
141 }
142}
143
144impl std::error::Error for DataError {}
145
146pub type Result<T> = std::result::Result<T, DataError>;
147
148fn err<T>(code: &'static str, message: impl Into<String>) -> Result<T> {
149 Err(DataError { code, message: message.into() })
150}
151
152fn io(e: std::io::Error) -> DataError {
153 DataError { code: "E9003", message: e.to_string() }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
160pub enum Leaf {
161 Idx { bound: u64, shape: Vec<usize> },
163 Real { shape: Vec<usize> },
165}
166
167impl Leaf {
168 pub fn numel(&self) -> usize {
169 match self {
170 Leaf::Idx { shape, .. } | Leaf::Real { shape } => shape.iter().product(),
171 }
172 }
173
174 pub fn width(&self) -> usize {
176 match self {
177 Leaf::Idx { bound, .. } => idx_width(*bound),
178 Leaf::Real { .. } => 8,
179 }
180 }
181
182 pub fn bytes(&self) -> usize {
183 self.numel() * self.width()
184 }
185}
186
187pub fn idx_width(bound: u64) -> usize {
189 match bound.saturating_sub(1) {
190 n if n <= u8::MAX as u64 => 1,
191 n if n <= u16::MAX as u64 => 2,
192 n if n <= u32::MAX as u64 => 4,
193 _ => 8,
194 }
195}
196
197#[derive(Clone, Debug, PartialEq, Eq)]
199pub enum Layout {
200 Leaf(Leaf),
201 Tuple(Vec<Layout>),
202 Record(Vec<(String, Layout)>),
204}
205
206impl Layout {
207 pub fn from_type_text(text: &str) -> Result<Layout> {
209 let mut sm = SourceMap::new();
210 let id = sm.add("<type>", text);
211 let (ty, diags) = parse_type(&sm, id);
212 if let Some(d) = diags.iter().find(|d| d.is_error()) {
213 return err("E9001", format!("`{text}` is not a type: {}", d.message));
214 }
215 let Some(ty) = ty else { return err("E9001", format!("`{text}` is not a type")) };
216 Self::from_ast(&ty, text)
217 }
218
219 fn from_ast(ty: &ast::Ty, text: &str) -> Result<Layout> {
220 match ty {
221 ast::Ty::Real { axes, .. } => Ok(Layout::Leaf(Leaf::Real { shape: Self::shape(axes, text)? })),
222 ast::Ty::Idx { bound, axes, .. } => {
223 let bound = Self::nat(bound, text)?;
224 if bound == 0 {
225 return err("E9001", format!("`{text}`: an `idx[0]` has no values"));
226 }
227 Ok(Layout::Leaf(Leaf::Idx { bound, shape: Self::shape(axes, text)? }))
228 }
229 ast::Ty::Bool { axes, .. } => Ok(Layout::Leaf(Leaf::Idx { bound: 2, shape: Self::shape(axes, text)? })),
232 ast::Ty::Param { inner, .. } => Self::from_ast(inner, text),
233 ast::Ty::Tuple { elems, .. } => Ok(Layout::Tuple(elems.iter().map(|t| Self::from_ast(t, text)).collect::<Result<_>>()?)),
234 ast::Ty::Params { module, .. } => {
235 err("E9001", format!("`{text}`: the layout of `params {}` needs the program (open the file from `presk train`)", module.dotted()))
236 }
237 ast::Ty::Named { path, .. } => err("E9001", format!("`{text}`: `{}` is not a data type (only `ℝ[…]`, `idx[N][…]`, `bool[…]` and tuples of them, with literal dimensions)", path.dotted())),
238 }
239 }
240
241 fn shape(axes: &[AxisSpec], text: &str) -> Result<Vec<usize>> {
242 axes.iter()
243 .map(|a| match a {
244 AxisSpec::Axis { dim, .. } => Self::nat(dim, text).map(|n| n as usize),
245 AxisSpec::Ellipsis { .. } => err("E9001", format!("`{text}`: a sample type cannot have an open prefix `...`")),
246 })
247 .collect()
248 }
249
250 fn nat(e: &Expr, text: &str) -> Result<u64> {
251 match e {
252 Expr::Lit { lit: Lit::Nat(n), .. } => n.parse().map_err(|_| DataError { code: "E9001", message: format!("`{text}`: `{n}` is too large") }),
253 _ => err("E9001", format!("`{text}`: every dimension of a sample type must be a literal (a `.psk.d` holds concrete tensors)")),
254 }
255 }
256
257 pub fn leaves(&self) -> Vec<&Leaf> {
259 let mut out = Vec::new();
260 self.collect(&mut out);
261 out
262 }
263
264 fn collect<'a>(&'a self, out: &mut Vec<&'a Leaf>) {
265 match self {
266 Layout::Leaf(l) => out.push(l),
267 Layout::Tuple(ts) => ts.iter().for_each(|t| t.collect(out)),
268 Layout::Record(fs) => fs.iter().for_each(|(_, t)| t.collect(out)),
269 }
270 }
271
272 pub fn sample_bytes(&self) -> usize {
274 self.leaves().iter().map(|l| l.bytes()).sum()
275 }
276
277 pub fn sample_bytes_enc(&self, encs: &[Enc]) -> usize {
280 self.leaves().iter().enumerate().map(|(i, l)| leaf_bytes(l, encs.get(i).copied().unwrap_or(Enc::F64))).sum()
281 }
282}
283
284fn leaf_bytes(l: &Leaf, e: Enc) -> usize {
285 match l {
286 Leaf::Real { .. } => l.numel() * e.width(),
287 Leaf::Idx { .. } => l.bytes(),
288 }
289}
290
291#[derive(Clone, Debug, PartialEq)]
295pub enum Value {
296 Idx(Vec<u64>),
297 Real(Vec<f64>),
298 Tuple(Vec<Value>),
299 Record(Vec<(String, Value)>),
300}
301
302impl Value {
303 fn leaves(&self) -> Vec<&Value> {
304 let mut out = Vec::new();
305 self.collect(&mut out);
306 out
307 }
308
309 fn collect<'a>(&'a self, out: &mut Vec<&'a Value>) {
310 match self {
311 Value::Idx(_) | Value::Real(_) => out.push(self),
312 Value::Tuple(ts) => ts.iter().for_each(|t| t.collect(out)),
313 Value::Record(fs) => fs.iter().for_each(|(_, t)| t.collect(out)),
314 }
315 }
316
317 pub fn check(&self, layout: &Layout) -> Result<()> {
319 let ls = layout.leaves();
320 let vs = self.leaves();
321 if ls.len() != vs.len() {
322 return err("E9005", format!("sample has {} leaves, the type has {}", vs.len(), ls.len()));
323 }
324 for (i, (l, v)) in ls.iter().zip(&vs).enumerate() {
325 match (l, v) {
326 (Leaf::Idx { bound, .. }, Value::Idx(d)) => {
327 if d.len() != l.numel() {
328 return err("E9005", format!("leaf {i}: {} values, {} expected", d.len(), l.numel()));
329 }
330 if let Some(x) = d.iter().find(|x| **x >= *bound) {
331 return err("E9004", format!("leaf {i}: index {x} is not below the bound {bound}"));
332 }
333 }
334 (Leaf::Real { .. }, Value::Real(d)) => {
335 if d.len() != l.numel() {
336 return err("E9005", format!("leaf {i}: {} values, {} expected", d.len(), l.numel()));
337 }
338 }
339 (Leaf::Idx { .. }, _) => return err("E9005", format!("leaf {i}: indices expected, reals found")),
340 (Leaf::Real { .. }, _) => return err("E9005", format!("leaf {i}: reals expected, indices found")),
341 }
342 }
343 Ok(())
344 }
345
346 pub fn parse_text(layout: &Layout, line: &str) -> Result<Value> {
348 let mut toks = line.split_whitespace();
349 let v = Self::parse_tokens(layout, &mut toks)?;
350 if let Some(extra) = toks.next() {
351 return err("E9004", format!("extra value `{extra}` after a complete sample"));
352 }
353 Ok(v)
354 }
355
356 fn parse_tokens<'a>(layout: &Layout, toks: &mut impl Iterator<Item = &'a str>) -> Result<Value> {
357 match layout {
358 Layout::Leaf(Leaf::Idx { bound, shape }) => {
359 let n: usize = shape.iter().product();
360 let mut d = Vec::with_capacity(n);
361 for _ in 0..n {
362 let t = toks.next().ok_or_else(|| DataError { code: "E9004", message: "sample ends before all values are given".into() })?;
363 let x: u64 = t.parse().map_err(|_| DataError { code: "E9004", message: format!("`{t}` is not an index") })?;
364 if x >= *bound {
365 return err("E9004", format!("index {x} is not below the bound {bound}"));
366 }
367 d.push(x);
368 }
369 Ok(Value::Idx(d))
370 }
371 Layout::Leaf(Leaf::Real { shape }) => {
372 let n: usize = shape.iter().product();
373 let mut d = Vec::with_capacity(n);
374 for _ in 0..n {
375 let t = toks.next().ok_or_else(|| DataError { code: "E9004", message: "sample ends before all values are given".into() })?;
376 let x: f64 = t.parse().map_err(|_| DataError { code: "E9004", message: format!("`{t}` is not a real") })?;
377 d.push(x);
378 }
379 Ok(Value::Real(d))
380 }
381 Layout::Tuple(ts) => Ok(Value::Tuple(ts.iter().map(|t| Self::parse_tokens(t, toks)).collect::<Result<_>>()?)),
382 Layout::Record(fs) => Ok(Value::Record(fs.iter().map(|(n, t)| Self::parse_tokens(t, toks).map(|v| (n.clone(), v))).collect::<Result<_>>()?)),
383 }
384 }
385
386 pub fn to_json(&self, layout: &Layout) -> serde_json::Value {
388 match (self, layout) {
389 (Value::Idx(d), Layout::Leaf(Leaf::Idx { shape, .. })) => nest(shape, &d.iter().map(|x| serde_json::json!(x)).collect::<Vec<_>>()),
390 (Value::Real(d), Layout::Leaf(Leaf::Real { shape })) => nest(shape, &d.iter().map(|x| serde_json::json!(x)).collect::<Vec<_>>()),
391 (Value::Tuple(vs), Layout::Tuple(ts)) => serde_json::Value::Array(vs.iter().zip(ts).map(|(v, t)| v.to_json(t)).collect()),
392 (Value::Record(fs), Layout::Record(ls)) => serde_json::Value::Object(fs.iter().zip(ls).map(|((n, v), (_, t))| (n.clone(), v.to_json(t))).collect()),
393 _ => serde_json::Value::Null,
394 }
395 }
396
397 pub fn render(&self, layout: &Layout) -> String {
399 match (self, layout) {
400 (Value::Idx(d), Layout::Leaf(Leaf::Idx { shape, .. })) => nest_text(shape, &d.iter().map(|x| x.to_string()).collect::<Vec<_>>()),
401 (Value::Real(d), Layout::Leaf(Leaf::Real { shape })) => nest_text(shape, &d.iter().map(|x| format!("{x}")).collect::<Vec<_>>()),
402 (Value::Tuple(vs), Layout::Tuple(ts)) => format!("({})", vs.iter().zip(ts).map(|(v, t)| v.render(t)).collect::<Vec<_>>().join(", ")),
403 (Value::Record(fs), Layout::Record(ls)) => format!("{{{}}}", fs.iter().zip(ls).map(|((n, v), (_, t))| format!("{n} = {}", v.render(t))).collect::<Vec<_>>().join(", ")),
404 _ => "?".into(),
405 }
406 }
407}
408
409fn nest(shape: &[usize], flat: &[serde_json::Value]) -> serde_json::Value {
410 match shape {
411 [] => flat.first().cloned().unwrap_or(serde_json::Value::Null),
412 [_] => serde_json::Value::Array(flat.to_vec()),
413 [n, rest @ ..] => {
414 let chunk = flat.len() / n.max(&1);
415 serde_json::Value::Array((0..*n).map(|i| nest(rest, &flat[i * chunk..(i + 1) * chunk])).collect())
416 }
417 }
418}
419
420fn nest_text(shape: &[usize], flat: &[String]) -> String {
421 match shape {
422 [] => flat.first().cloned().unwrap_or_default(),
423 [_] => format!("[{}]", flat.join(", ")),
424 [n, rest @ ..] => {
425 let chunk = flat.len() / n.max(&1);
426 format!("[{}]", (0..*n).map(|i| nest_text(rest, &flat[i * chunk..(i + 1) * chunk])).collect::<Vec<_>>().join(", "))
427 }
428 }
429}
430
431#[derive(Clone, Debug, PartialEq, Eq)]
434pub struct Header {
435 pub version: u16,
436 pub sample_ty: String,
438 pub count: u64,
439 pub encodings: Vec<Enc>,
442}
443
444impl Header {
445 #[allow(clippy::len_without_is_empty)]
447 pub fn len(&self) -> u64 {
448 let v2 = if self.version == VERSION_ENCODED { 4 + self.encodings.len() } else { 0 };
449 (4 + 2 + 4 + self.sample_ty.len() + 8 + v2) as u64
450 }
451
452 pub fn encoding(&self, i: usize) -> Enc {
454 self.encodings.get(i).copied().unwrap_or(Enc::F64)
455 }
456}
457
458pub fn write<'a>(path: &Path, sample_ty: &str, layout: &Layout, samples: impl IntoIterator<Item = &'a Value>) -> Result<u64> {
461 write_encoded(path, sample_ty, layout, &[], samples)
462}
463
464pub fn write_encoded<'a>(path: &Path, sample_ty: &str, layout: &Layout, encs: &[Enc], samples: impl IntoIterator<Item = &'a Value>) -> Result<u64> {
470 let leaves = layout.leaves();
471 if !encs.is_empty() && encs.len() != leaves.len() {
472 return err("E9005", format!("{} encodings for {} leaves", encs.len(), leaves.len()));
473 }
474 let enc = |i: usize| encs.get(i).copied().unwrap_or(Enc::F64);
475 let narrow = leaves.iter().enumerate().any(|(i, l)| matches!(l, Leaf::Real { .. }) && enc(i) != Enc::F64);
476 if let Some((i, _)) = leaves.iter().enumerate().find(|(i, l)| matches!(l, Leaf::Idx { .. }) && enc(*i) != Enc::F64) {
477 return err("E9005", format!("leaf {i} holds indices, which have no real encoding"));
478 }
479 let f = File::create(path).map_err(io)?;
480 let mut w = BufWriter::new(f);
481 w.write_all(MAGIC).map_err(io)?;
482 w.write_all(&(if narrow { VERSION_ENCODED } else { VERSION }).to_le_bytes()).map_err(io)?;
483 w.write_all(&(sample_ty.len() as u32).to_le_bytes()).map_err(io)?;
484 w.write_all(sample_ty.as_bytes()).map_err(io)?;
485 let count_pos = 4 + 2 + 4 + sample_ty.len() as u64;
486 w.write_all(&0u64.to_le_bytes()).map_err(io)?;
487 if narrow {
488 w.write_all(&(leaves.len() as u32).to_le_bytes()).map_err(io)?;
489 let codes: Vec<u8> = (0..leaves.len()).map(|i| enc(i).code()).collect();
490 w.write_all(&codes).map_err(io)?;
491 }
492 let mut count = 0u64;
493 for s in samples {
494 s.check(layout)?;
495 for (li, (leaf, v)) in leaves.iter().zip(s.leaves()).enumerate() {
496 match (leaf, v) {
497 (Leaf::Idx { .. }, Value::Idx(d)) => {
498 for x in d {
499 let b = x.to_le_bytes();
500 w.write_all(&b[..leaf.width()]).map_err(io)?;
501 }
502 }
503 (Leaf::Real { .. }, Value::Real(d)) => {
504 let e = enc(li);
505 let mut bytes = Vec::with_capacity(d.len() * e.width());
506 for x in d {
507 e.encode(*x, &mut bytes).map_err(|m| DataError { code: m.code, message: format!("leaf {li}: {}", m.message) })?;
508 }
509 w.write_all(&bytes).map_err(io)?;
510 }
511 _ => unreachable!("checked"),
512 }
513 }
514 count += 1;
515 }
516 w.flush().map_err(io)?;
517 let mut f = w.into_inner().map_err(|e| io(e.into_error()))?;
518 f.seek(SeekFrom::Start(count_pos)).map_err(io)?;
519 f.write_all(&count.to_le_bytes()).map_err(io)?;
520 Ok(count)
521}
522
523#[derive(Debug)]
525pub struct Reader {
526 file: BufReader<File>,
527 header: Header,
528 layout: Option<Layout>,
529 data_start: u64,
530 file_len: u64,
531}
532
533impl Reader {
534 pub fn open(path: &Path) -> Result<Reader> {
537 let f = File::open(path).map_err(|e| DataError { code: "E9002", message: format!("{}: {e}", path.display()) })?;
538 let file_len = f.metadata().map_err(io)?.len();
539 let mut r = BufReader::new(f);
540 let mut magic = [0u8; 4];
541 r.read_exact(&mut magic).map_err(|_| DataError { code: "E9002", message: format!("{}: not a .psk.d file (too short)", path.display()) })?;
542 if &magic != MAGIC {
543 return err("E9002", format!("{}: not a .psk.d file (bad magic)", path.display()));
544 }
545 let mut b2 = [0u8; 2];
546 r.read_exact(&mut b2).map_err(io)?;
547 let version = u16::from_le_bytes(b2);
548 if version != VERSION && version != VERSION_ENCODED {
549 return err("E9002", format!("{}: .psk.d version {version}, this tool reads versions {VERSION} and {VERSION_ENCODED}", path.display()));
550 }
551 let mut b4 = [0u8; 4];
552 r.read_exact(&mut b4).map_err(io)?;
553 let tlen = u32::from_le_bytes(b4) as usize;
554 let mut tbuf = vec![0u8; tlen];
555 r.read_exact(&mut tbuf).map_err(|_| DataError { code: "E9003", message: format!("{}: truncated header", path.display()) })?;
556 let sample_ty = String::from_utf8(tbuf).map_err(|_| DataError { code: "E9002", message: format!("{}: the sample type is not UTF-8", path.display()) })?;
557 let mut b8 = [0u8; 8];
558 r.read_exact(&mut b8).map_err(|_| DataError { code: "E9003", message: format!("{}: truncated header", path.display()) })?;
559 let count = u64::from_le_bytes(b8);
560 let mut encodings = Vec::new();
561 if version == VERSION_ENCODED {
562 r.read_exact(&mut b4).map_err(|_| DataError { code: "E9003", message: format!("{}: truncated header", path.display()) })?;
563 let n = u32::from_le_bytes(b4) as usize;
564 let mut codes = vec![0u8; n];
565 r.read_exact(&mut codes).map_err(|_| DataError { code: "E9003", message: format!("{}: truncated header", path.display()) })?;
566 for (i, c) in codes.iter().enumerate() {
567 encodings.push(Enc::of_code(*c).ok_or_else(|| DataError { code: "E9002", message: format!("{}: leaf {i}: unknown encoding {c}", path.display()) })?);
568 }
569 }
570 let header = Header { version, sample_ty, count, encodings };
571 let data_start = header.len();
572 let layout = Layout::from_type_text(&header.sample_ty).ok();
573 if let Some(l) = &layout {
574 check_encodings(l, &header, path)?;
575 }
576 let mut reader = Reader { file: r, header, layout, data_start, file_len };
577 reader.check_len(path)?;
578 Ok(reader)
579 }
580
581 fn check_len(&mut self, path: &Path) -> Result<()> {
582 if let Some(l) = &self.layout {
583 let size = l.sample_bytes_enc(&self.header.encodings);
584 let need = self.data_start + self.header.count * size as u64;
585 if self.file_len < need {
586 return err("E9003", format!("{}: truncated: {} samples of {} bytes announced, {} bytes of data present", path.display(), self.header.count, size, self.file_len.saturating_sub(self.data_start)));
587 }
588 }
589 Ok(())
590 }
591
592 pub fn with_layout(mut self, layout: Layout) -> Result<Reader> {
602 check_encodings(&layout, &self.header, Path::new("the file"))?;
603 let size = layout.sample_bytes_enc(&self.header.encodings);
604 let need = self.data_start + self.header.count * size as u64;
605 if self.file_len < need {
606 return err("E9003", format!("truncated: {} samples of {} bytes announced, {} bytes of data present", self.header.count, size, self.file_len.saturating_sub(self.data_start)));
607 }
608 if self.file_len > need {
609 return err("E9005", format!("{} bytes of data where {} samples of {} bytes were expected: the file holds a model of other dimensions (a parameter shaped by a dimension, such as `pos : ℝ[T, D]`, at another `T`?)", self.file_len.saturating_sub(self.data_start), self.header.count, size));
610 }
611 self.layout = Some(layout);
612 Ok(self)
613 }
614
615 pub fn params_reals(&mut self) -> Result<Vec<f64>> {
620 if !self.header.sample_ty.starts_with("params ") {
621 return err("E9005", format!("`{}` is not a parameter tree", self.header.sample_ty));
622 }
623 let e = self.header.encodings.first().copied().unwrap_or(Enc::F64);
626 if self.header.encodings.iter().any(|x| *x != e) {
627 return err("E9005", "the leaves of the file have several encodings: their sizes need the program");
628 }
629 let n = self.file_len.saturating_sub(self.data_start);
630 if n % e.width() as u64 != 0 {
631 return err("E9003", format!("{n} bytes of parameters, not a whole number of {}", e.name()));
632 }
633 self.file.seek(SeekFrom::Start(self.data_start)).map_err(io)?;
634 let mut buf = vec![0u8; n as usize];
635 self.file.read_exact(&mut buf).map_err(io)?;
636 Ok(buf.chunks_exact(e.width()).map(|c| e.decode(c)).collect())
637 }
638
639 pub fn header(&self) -> &Header {
640 &self.header
641 }
642
643 pub fn layout(&self) -> Result<&Layout> {
645 match &self.layout {
646 Some(l) => Ok(l),
647 None => Layout::from_type_text(&self.header.sample_ty).map(|_| unreachable!()),
648 }
649 }
650
651 pub fn len(&self) -> usize {
652 self.header.count as usize
653 }
654
655 pub fn is_empty(&self) -> bool {
656 self.header.count == 0
657 }
658
659 pub fn get(&mut self, i: usize) -> Result<Value> {
661 let layout = self.layout()?.clone();
662 if i >= self.len() {
663 return err("E9003", format!("sample {i} requested, {} in the file", self.len()));
664 }
665 let size = layout.sample_bytes_enc(&self.header.encodings);
666 self.file.seek(SeekFrom::Start(self.data_start + (i * size) as u64)).map_err(io)?;
667 let mut buf = vec![0u8; size];
668 self.file.read_exact(&mut buf).map_err(|_| DataError { code: "E9003", message: format!("truncated at sample {i}") })?;
669 let (mut pos, mut leaf) = (0, 0);
670 Ok(decode(&layout, &self.header.encodings, &buf, &mut pos, &mut leaf))
671 }
672
673 pub fn read_all(&mut self) -> Result<Vec<Value>> {
675 (0..self.len()).map(|i| self.get(i)).collect()
676 }
677}
678
679fn check_encodings(layout: &Layout, header: &Header, path: &Path) -> Result<()> {
682 if header.version != VERSION_ENCODED {
683 return Ok(());
684 }
685 let leaves = layout.leaves();
686 if header.encodings.len() != leaves.len() {
687 return err("E9005", format!("{}: {} encodings for the {} leaves of `{}`", path.display(), header.encodings.len(), leaves.len(), header.sample_ty));
688 }
689 if let Some((i, _)) = leaves.iter().enumerate().find(|(i, l)| matches!(l, Leaf::Idx { .. }) && header.encodings[*i] != Enc::F64) {
690 return err("E9005", format!("{}: leaf {i} holds indices and has a real encoding", path.display()));
691 }
692 Ok(())
693}
694
695fn decode(layout: &Layout, encs: &[Enc], buf: &[u8], pos: &mut usize, leaf_no: &mut usize) -> Value {
696 match layout {
697 Layout::Leaf(leaf @ Leaf::Idx { .. }) => {
698 *leaf_no += 1;
699 let w = leaf.width();
700 let d = (0..leaf.numel())
701 .map(|_| {
702 let mut b = [0u8; 8];
703 b[..w].copy_from_slice(&buf[*pos..*pos + w]);
704 *pos += w;
705 u64::from_le_bytes(b)
706 })
707 .collect();
708 Value::Idx(d)
709 }
710 Layout::Leaf(leaf @ Leaf::Real { .. }) => {
711 let e = encs.get(*leaf_no).copied().unwrap_or(Enc::F64);
712 *leaf_no += 1;
713 let w = e.width();
714 let d = (0..leaf.numel())
715 .map(|_| {
716 let x = e.decode(&buf[*pos..*pos + w]);
717 *pos += w;
718 x
719 })
720 .collect();
721 Value::Real(d)
722 }
723 Layout::Tuple(ts) => Value::Tuple(ts.iter().map(|t| decode(t, encs, buf, pos, leaf_no)).collect()),
724 Layout::Record(fs) => Value::Record(fs.iter().map(|(n, t)| (n.clone(), decode(t, encs, buf, pos, leaf_no))).collect()),
725 }
726}
727
728#[derive(Clone, Debug, PartialEq, Eq)]
732pub struct Pipeline {
733 pub shuffle: Option<u64>,
735 pub batch: usize,
737 pub take: Option<usize>,
739}
740
741impl Pipeline {
742 pub fn batches(&self, count: usize, epoch: u64) -> Vec<Vec<usize>> {
744 let order: Vec<usize> = match self.shuffle {
745 Some(seed) => shuffle_indices(count, seed, epoch),
746 None => (0..count).collect(),
747 };
748 let b = self.batch.max(1);
749 let mut out: Vec<Vec<usize>> = order.chunks_exact(b).map(|c| c.to_vec()).collect();
750 if let Some(n) = self.take {
751 out.truncate(n);
752 }
753 out
754 }
755
756 pub fn batches_per_epoch(&self, count: usize) -> usize {
758 let n = count / self.batch.max(1);
759 self.take.map_or(n, |t| n.min(t))
760 }
761}
762
763pub fn shuffle_indices(n: usize, seed: u64, epoch: u64) -> Vec<usize> {
766 let mut master = ChaCha20Rng::seed_from_u64(seed);
767 let mut key = [0u8; 32];
768 for _ in 0..epoch.max(1) {
769 master.fill_bytes(&mut key);
770 }
771 let mut rng = ChaCha20Rng::from_seed(key);
772 let mut v: Vec<usize> = (0..n).collect();
773 for i in (1..n).rev() {
774 let j = (rng.next_u64() % (i as u64 + 1)) as usize;
775 v.swap(i, j);
776 }
777 v
778}
779
780pub fn char_vocab(text: &str) -> Vec<String> {
786 let mut chars: Vec<char> = text.chars().collect();
787 chars.sort_unstable();
788 chars.dedup();
789 chars.into_iter().map(String::from).collect()
790}
791
792pub fn encode(text: &str, vocab: &[String]) -> Result<Vec<u64>> {
796 let mut map = std::collections::HashMap::with_capacity(vocab.len());
797 for (i, t) in vocab.iter().enumerate() {
798 let mut cs = t.chars();
799 let (Some(c), None) = (cs.next(), cs.next()) else {
800 return err("E9004", format!("the vocabulary holds the multi-character token `{t}`; only character vocabularies can be encoded in this version"));
801 };
802 map.insert(c, i as u64);
803 }
804 text.chars()
805 .map(|c| map.get(&c).copied().ok_or_else(|| DataError { code: "E9004", message: format!("the character `{}` (U+{:04X}) is not in the vocabulary", c.escape_debug(), c as u32) }))
806 .collect()
807}
808
809pub fn decode_text(tokens: &[u64], vocab: &[String]) -> Result<String> {
811 tokens
812 .iter()
813 .map(|t| vocab.get(*t as usize).map(String::as_str).ok_or_else(|| DataError { code: "E9004", message: format!("token {t} is beyond the vocabulary of {}", vocab.len()) }))
814 .collect()
815}
816
817pub fn lm_windows(tokens: &[u64], vocab: u64, seq: usize, stride: usize) -> Result<(String, Layout, Vec<Value>)> {
823 if seq == 0 || stride == 0 {
824 return err("E9004", "`--seq` and `--stride` must be at least 1");
825 }
826 if let Some(x) = tokens.iter().find(|x| **x >= vocab) {
827 return err("E9004", format!("token {x} is not below the vocabulary size {vocab}"));
828 }
829 let ty = format!("(idx[{vocab}][seq: {seq}], idx[{vocab}][seq: {seq}])");
830 let layout = Layout::from_type_text(&ty)?;
831 let mut out = Vec::new();
832 let mut k = 0;
833 while k + seq < tokens.len() {
834 out.push(Value::Tuple(vec![Value::Idx(tokens[k..k + seq].to_vec()), Value::Idx(tokens[k + 1..k + 1 + seq].to_vec())]));
835 k += stride;
836 }
837 Ok((ty, layout, out))
838}
839
840pub fn info(reader: &mut Reader, n: usize) -> Result<String> {
842 let h = reader.header().clone();
843 let mut s = format!("type {}\ncount {}\nversion {}\n", h.sample_ty, h.count, h.version);
844 if !h.encodings.is_empty() {
845 let mut runs: Vec<(Enc, usize)> = Vec::new();
847 for e in &h.encodings {
848 match runs.last_mut() {
849 Some((f, n)) if f == e => *n += 1,
850 _ => runs.push((*e, 1)),
851 }
852 }
853 s.push_str(&format!("leaves {}\n", runs.iter().map(|(e, n)| format!("{n} × {}", e.name())).collect::<Vec<_>>().join(", ")));
854 }
855 match reader.layout() {
856 Ok(l) => {
857 let l = l.clone();
858 s.push_str(&format!("sample {} bytes, {} leaves\n", l.sample_bytes_enc(&h.encodings), l.leaves().len()));
859 for i in 0..n.min(reader.len()) {
860 let v = reader.get(i)?;
861 s.push_str(&format!("[{i}] {}\n", v.render(&l)));
862 }
863 }
864 Err(e) => s.push_str(&format!("sample layout not derivable from the type ({}): open it from the program\n", e.message)),
865 }
866 Ok(s)
867}