1use crate::block::Block;
26use crate::error::{Error, Result};
27use bytes::Bytes;
28use serde::{Deserialize, Serialize};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32pub enum TensorDtype {
33 F32,
35 F16,
37 F64,
39 I8,
41 I32,
43 I64,
45 U8,
47 U32,
49 Bool,
51}
52
53impl TensorDtype {
54 #[inline]
56 pub fn size_bytes(&self) -> usize {
57 match self {
58 TensorDtype::F32 => 4,
59 TensorDtype::F16 => 2,
60 TensorDtype::F64 => 8,
61 TensorDtype::I8 => 1,
62 TensorDtype::I32 => 4,
63 TensorDtype::I64 => 8,
64 TensorDtype::U8 => 1,
65 TensorDtype::U32 => 4,
66 TensorDtype::Bool => 1,
67 }
68 }
69
70 #[inline]
72 pub fn name(&self) -> &'static str {
73 match self {
74 TensorDtype::F32 => "float32",
75 TensorDtype::F16 => "float16",
76 TensorDtype::F64 => "float64",
77 TensorDtype::I8 => "int8",
78 TensorDtype::I32 => "int32",
79 TensorDtype::I64 => "int64",
80 TensorDtype::U8 => "uint8",
81 TensorDtype::U32 => "uint32",
82 TensorDtype::Bool => "bool",
83 }
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
89pub struct TensorShape {
90 dims: Vec<usize>,
91}
92
93impl TensorShape {
94 pub fn new(dims: Vec<usize>) -> Self {
96 Self { dims }
97 }
98
99 pub fn scalar() -> Self {
101 Self { dims: vec![] }
102 }
103
104 #[inline]
106 pub fn dims(&self) -> &[usize] {
107 &self.dims
108 }
109
110 #[inline]
112 pub fn rank(&self) -> usize {
113 self.dims.len()
114 }
115
116 #[inline]
118 pub fn element_count(&self) -> usize {
119 if self.dims.is_empty() {
120 1
121 } else {
122 self.dims.iter().product()
123 }
124 }
125
126 #[inline]
128 pub fn is_scalar(&self) -> bool {
129 self.dims.is_empty()
130 }
131
132 #[inline]
134 pub fn is_vector(&self) -> bool {
135 self.dims.len() == 1
136 }
137
138 #[inline]
140 pub fn is_matrix(&self) -> bool {
141 self.dims.len() == 2
142 }
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct TensorMetadata {
148 pub shape: TensorShape,
150 pub dtype: TensorDtype,
152 pub name: Option<String>,
154 pub metadata: std::collections::BTreeMap<String, String>,
156}
157
158impl TensorMetadata {
159 pub fn new(shape: TensorShape, dtype: TensorDtype) -> Self {
161 Self {
162 shape,
163 dtype,
164 name: None,
165 metadata: std::collections::BTreeMap::new(),
166 }
167 }
168
169 pub fn with_name(mut self, name: String) -> Self {
171 self.name = Some(name);
172 self
173 }
174
175 pub fn with_metadata(mut self, key: String, value: String) -> Self {
177 self.metadata.insert(key, value);
178 self
179 }
180
181 pub fn expected_size(&self) -> usize {
183 self.shape.element_count() * self.dtype.size_bytes()
184 }
185}
186
187#[derive(Debug, Clone)]
193pub struct TensorBlock {
194 block: Block,
196 metadata: TensorMetadata,
198}
199
200impl TensorBlock {
201 pub fn new(data: Bytes, shape: TensorShape, dtype: TensorDtype) -> Result<Self> {
231 let metadata = TensorMetadata::new(shape, dtype);
232
233 let expected_size = metadata.expected_size();
235 if data.len() != expected_size {
236 return Err(Error::InvalidData(format!(
237 "Tensor data size mismatch: expected {} bytes, got {}",
238 expected_size,
239 data.len()
240 )));
241 }
242
243 let block = Block::new(data)?;
245
246 Ok(Self { block, metadata })
247 }
248
249 pub fn with_metadata(data: Bytes, metadata: TensorMetadata) -> Result<Self> {
251 let expected_size = metadata.expected_size();
252 if data.len() != expected_size {
253 return Err(Error::InvalidData(format!(
254 "Tensor data size mismatch: expected {} bytes, got {}",
255 expected_size,
256 data.len()
257 )));
258 }
259
260 let block = Block::new(data)?;
261 Ok(Self { block, metadata })
262 }
263
264 pub fn block(&self) -> &Block {
266 &self.block
267 }
268
269 pub fn metadata(&self) -> &TensorMetadata {
271 &self.metadata
272 }
273
274 pub fn shape(&self) -> &TensorShape {
276 &self.metadata.shape
277 }
278
279 pub fn dtype(&self) -> TensorDtype {
281 self.metadata.dtype
282 }
283
284 pub fn element_count(&self) -> usize {
286 self.metadata.shape.element_count()
287 }
288
289 pub fn cid(&self) -> &crate::cid::Cid {
291 self.block.cid()
292 }
293
294 pub fn data(&self) -> &Bytes {
296 self.block.data()
297 }
298
299 pub fn into_parts(self) -> (Block, TensorMetadata) {
301 (self.block, self.metadata)
302 }
303
304 pub fn verify(&self) -> Result<bool> {
306 self.block.verify()
307 }
308
309 pub fn reshape(&self, new_shape: TensorShape) -> Result<Self> {
311 if new_shape.element_count() != self.element_count() {
312 return Err(Error::InvalidInput(format!(
313 "Cannot reshape tensor with {} elements to shape with {} elements",
314 self.element_count(),
315 new_shape.element_count()
316 )));
317 }
318
319 let new_metadata = TensorMetadata {
320 shape: new_shape,
321 dtype: self.metadata.dtype,
322 name: self.metadata.name.clone(),
323 metadata: self.metadata.metadata.clone(),
324 };
325
326 Ok(Self {
327 block: self.block.clone(),
328 metadata: new_metadata,
329 })
330 }
331
332 pub fn size_bytes(&self) -> usize {
334 self.data().len()
335 }
336
337 pub fn is_scalar(&self) -> bool {
339 self.shape().is_scalar()
340 }
341
342 pub fn is_vector(&self) -> bool {
344 self.shape().is_vector()
345 }
346
347 pub fn is_matrix(&self) -> bool {
349 self.shape().is_matrix()
350 }
351}
352
353impl TensorBlock {
355 pub fn from_f32_slice(data: &[f32], shape: TensorShape) -> Result<Self> {
357 if data.len() != shape.element_count() {
358 return Err(Error::InvalidInput(format!(
359 "Data length {} doesn't match shape element count {}",
360 data.len(),
361 shape.element_count()
362 )));
363 }
364
365 let bytes: Vec<u8> = data.iter().flat_map(|&f| f.to_le_bytes()).collect();
366 Self::new(Bytes::from(bytes), shape, TensorDtype::F32)
367 }
368
369 pub fn from_f64_slice(data: &[f64], shape: TensorShape) -> Result<Self> {
371 if data.len() != shape.element_count() {
372 return Err(Error::InvalidInput(format!(
373 "Data length {} doesn't match shape element count {}",
374 data.len(),
375 shape.element_count()
376 )));
377 }
378
379 let bytes: Vec<u8> = data.iter().flat_map(|&f| f.to_le_bytes()).collect();
380 Self::new(Bytes::from(bytes), shape, TensorDtype::F64)
381 }
382
383 pub fn from_i32_slice(data: &[i32], shape: TensorShape) -> Result<Self> {
385 if data.len() != shape.element_count() {
386 return Err(Error::InvalidInput(format!(
387 "Data length {} doesn't match shape element count {}",
388 data.len(),
389 shape.element_count()
390 )));
391 }
392
393 let bytes: Vec<u8> = data.iter().flat_map(|&i| i.to_le_bytes()).collect();
394 Self::new(Bytes::from(bytes), shape, TensorDtype::I32)
395 }
396
397 pub fn from_i64_slice(data: &[i64], shape: TensorShape) -> Result<Self> {
399 if data.len() != shape.element_count() {
400 return Err(Error::InvalidInput(format!(
401 "Data length {} doesn't match shape element count {}",
402 data.len(),
403 shape.element_count()
404 )));
405 }
406
407 let bytes: Vec<u8> = data.iter().flat_map(|&i| i.to_le_bytes()).collect();
408 Self::new(Bytes::from(bytes), shape, TensorDtype::I64)
409 }
410
411 pub fn from_u8_slice(data: &[u8], shape: TensorShape) -> Result<Self> {
413 if data.len() != shape.element_count() {
414 return Err(Error::InvalidInput(format!(
415 "Data length {} doesn't match shape element count {}",
416 data.len(),
417 shape.element_count()
418 )));
419 }
420
421 Self::new(Bytes::copy_from_slice(data), shape, TensorDtype::U8)
422 }
423
424 pub fn to_f32_vec(&self) -> Result<Vec<f32>> {
426 if self.dtype() != TensorDtype::F32 {
427 return Err(Error::InvalidInput(format!(
428 "Cannot convert {} tensor to f32",
429 self.dtype().name()
430 )));
431 }
432
433 let data = self.data();
434 let mut result = Vec::with_capacity(self.element_count());
435
436 for chunk in data.chunks_exact(4) {
437 let bytes: [u8; 4] = chunk
438 .try_into()
439 .expect("chunks_exact(4) guarantees exactly 4 bytes");
440 result.push(f32::from_le_bytes(bytes));
441 }
442
443 Ok(result)
444 }
445
446 pub fn to_f64_vec(&self) -> Result<Vec<f64>> {
448 if self.dtype() != TensorDtype::F64 {
449 return Err(Error::InvalidInput(format!(
450 "Cannot convert {} tensor to f64",
451 self.dtype().name()
452 )));
453 }
454
455 let data = self.data();
456 let mut result = Vec::with_capacity(self.element_count());
457
458 for chunk in data.chunks_exact(8) {
459 let bytes: [u8; 8] = chunk
460 .try_into()
461 .expect("chunks_exact(8) guarantees exactly 8 bytes");
462 result.push(f64::from_le_bytes(bytes));
463 }
464
465 Ok(result)
466 }
467
468 pub fn to_i32_vec(&self) -> Result<Vec<i32>> {
470 if self.dtype() != TensorDtype::I32 {
471 return Err(Error::InvalidInput(format!(
472 "Cannot convert {} tensor to i32",
473 self.dtype().name()
474 )));
475 }
476
477 let data = self.data();
478 let mut result = Vec::with_capacity(self.element_count());
479
480 for chunk in data.chunks_exact(4) {
481 let bytes: [u8; 4] = chunk
482 .try_into()
483 .expect("chunks_exact(4) guarantees exactly 4 bytes");
484 result.push(i32::from_le_bytes(bytes));
485 }
486
487 Ok(result)
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 #[test]
496 fn test_tensor_dtype_sizes() {
497 assert_eq!(TensorDtype::F32.size_bytes(), 4);
498 assert_eq!(TensorDtype::F16.size_bytes(), 2);
499 assert_eq!(TensorDtype::I8.size_bytes(), 1);
500 assert_eq!(TensorDtype::I32.size_bytes(), 4);
501 }
502
503 #[test]
504 fn test_tensor_shape() {
505 let shape = TensorShape::new(vec![2, 3, 4]);
506 assert_eq!(shape.rank(), 3);
507 assert_eq!(shape.element_count(), 24);
508 assert!(!shape.is_scalar());
509 assert!(!shape.is_vector());
510 assert!(!shape.is_matrix());
511
512 let scalar = TensorShape::scalar();
513 assert!(scalar.is_scalar());
514 assert_eq!(scalar.element_count(), 1);
515 }
516
517 #[test]
518 fn test_tensor_block_creation() {
519 let shape = TensorShape::new(vec![2, 2]);
520 let data: Vec<u8> = [1.0f32, 2.0, 3.0, 4.0]
521 .iter()
522 .flat_map(|f| f.to_le_bytes())
523 .collect();
524
525 let tensor = TensorBlock::new(Bytes::from(data), shape, TensorDtype::F32).unwrap();
526
527 assert_eq!(tensor.element_count(), 4);
528 assert_eq!(tensor.dtype(), TensorDtype::F32);
529 assert_eq!(tensor.shape().dims(), &[2, 2]);
530 }
531
532 #[test]
533 fn test_tensor_size_validation() {
534 let shape = TensorShape::new(vec![2, 2]);
535 let data: Vec<u8> = [1.0f32, 2.0, 3.0]
537 .iter()
538 .flat_map(|f| f.to_le_bytes())
539 .collect();
540
541 let result = TensorBlock::new(Bytes::from(data), shape, TensorDtype::F32);
542 assert!(result.is_err());
543 }
544
545 #[test]
546 fn test_tensor_metadata() {
547 let shape = TensorShape::new(vec![10, 20]);
548 let metadata = TensorMetadata::new(shape, TensorDtype::F32)
549 .with_name("layer1.weight".to_string())
550 .with_metadata("requires_grad".to_string(), "true".to_string());
551
552 assert_eq!(metadata.name, Some("layer1.weight".to_string()));
553 assert_eq!(metadata.expected_size(), 10 * 20 * 4); }
555
556 #[test]
557 fn test_tensor_from_f32_slice() {
558 let data = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
559 let shape = TensorShape::new(vec![2, 3]);
560
561 let tensor = TensorBlock::from_f32_slice(&data, shape).unwrap();
562 assert_eq!(tensor.element_count(), 6);
563 assert_eq!(tensor.dtype(), TensorDtype::F32);
564
565 let recovered = tensor.to_f32_vec().unwrap();
567 assert_eq!(recovered, data);
568 }
569
570 #[test]
571 fn test_tensor_from_i32_slice() {
572 let data = vec![10i32, 20, 30, 40];
573 let shape = TensorShape::new(vec![2, 2]);
574
575 let tensor = TensorBlock::from_i32_slice(&data, shape).unwrap();
576 assert_eq!(tensor.element_count(), 4);
577
578 let recovered = tensor.to_i32_vec().unwrap();
579 assert_eq!(recovered, data);
580 }
581
582 #[test]
583 fn test_tensor_reshape() {
584 let data = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
585 let shape = TensorShape::new(vec![2, 3]);
586 let tensor = TensorBlock::from_f32_slice(&data, shape).unwrap();
587
588 let reshaped = tensor.reshape(TensorShape::new(vec![3, 2])).unwrap();
590 assert_eq!(reshaped.shape().dims(), &[3, 2]);
591 assert_eq!(reshaped.element_count(), 6);
592
593 let recovered = reshaped.to_f32_vec().unwrap();
595 assert_eq!(recovered, data);
596 }
597
598 #[test]
599 fn test_tensor_reshape_invalid() {
600 let data = vec![1.0f32, 2.0, 3.0, 4.0];
601 let shape = TensorShape::new(vec![2, 2]);
602 let tensor = TensorBlock::from_f32_slice(&data, shape).unwrap();
603
604 let result = tensor.reshape(TensorShape::new(vec![3, 2])); assert!(result.is_err());
607 }
608
609 #[test]
610 fn test_tensor_type_checks() {
611 let data = vec![1.0f32, 2.0];
612 let tensor = TensorBlock::from_f32_slice(&data, TensorShape::new(vec![2])).unwrap();
613 assert!(tensor.is_vector());
614 assert!(!tensor.is_matrix());
615 assert!(!tensor.is_scalar());
616
617 let matrix = TensorBlock::from_f32_slice(&data, TensorShape::new(vec![1, 2])).unwrap();
618 assert!(matrix.is_matrix());
619 }
620
621 #[test]
622 fn test_tensor_to_vec_wrong_dtype() {
623 let data = vec![1i32, 2, 3];
624 let tensor = TensorBlock::from_i32_slice(&data, TensorShape::new(vec![3])).unwrap();
625
626 let result = tensor.to_f32_vec();
628 assert!(result.is_err());
629 }
630}