/home/noah/src/realizar/src/tensor.rs
Line | Count | Source |
1 | | //! Tensor implementation |
2 | | //! |
3 | | //! This module provides the core `Tensor` type, which is an N-dimensional array |
4 | | //! with automatic backend selection for optimal performance. |
5 | | |
6 | | use std::fmt; |
7 | | |
8 | | use num_traits::Num; |
9 | | use serde::{Deserialize, Serialize}; |
10 | | |
11 | | use crate::error::{RealizarError, Result}; |
12 | | |
13 | | /// N-dimensional tensor with automatic backend dispatch |
14 | | /// |
15 | | /// The tensor automatically selects the optimal execution backend (SIMD, GPU, WASM) |
16 | | /// based on operation type, data size, and available hardware. |
17 | | /// |
18 | | /// # Examples |
19 | | /// |
20 | | /// ``` |
21 | | /// use realizar::Tensor; |
22 | | /// |
23 | | /// // Create a 2×3 tensor |
24 | | /// let t = Tensor::from_vec(vec![2, 3], vec![ |
25 | | /// 1.0, 2.0, 3.0, |
26 | | /// 4.0, 5.0, 6.0, |
27 | | /// ]).expect("test"); |
28 | | /// |
29 | | /// assert_eq!(t.shape(), &[2, 3]); |
30 | | /// assert_eq!(t.ndim(), 2); |
31 | | /// assert_eq!(t.size(), 6); |
32 | | /// ``` |
33 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
34 | | pub struct Tensor<T: Num> { |
35 | | /// Flattened data in row-major order |
36 | | data: Vec<T>, |
37 | | /// Shape of the tensor |
38 | | shape: Vec<usize>, |
39 | | } |
40 | | |
41 | | impl<T: Num + Clone> Tensor<T> { |
42 | | /// Create a new tensor from a vector and shape |
43 | | /// |
44 | | /// # Arguments |
45 | | /// |
46 | | /// * `shape` - Dimensions of the tensor |
47 | | /// * `data` - Flattened data in row-major order |
48 | | /// |
49 | | /// # Errors |
50 | | /// |
51 | | /// Returns `Err` if: |
52 | | /// - Shape is empty |
53 | | /// - Data size doesn't match shape |
54 | | /// - Shape contains zero |
55 | | /// |
56 | | /// # Examples |
57 | | /// |
58 | | /// ``` |
59 | | /// use realizar::Tensor; |
60 | | /// |
61 | | /// let t = Tensor::from_vec(vec![2, 2], vec![1.0, 2.0, 3.0, 4.0]).expect("test"); |
62 | | /// assert_eq!(t.shape(), &[2, 2]); |
63 | | /// ``` |
64 | 47.9k | pub fn from_vec(shape: Vec<usize>, data: Vec<T>) -> Result<Self> { |
65 | | // Validate shape |
66 | 47.9k | if shape.is_empty() { |
67 | 6 | return Err(RealizarError::InvalidShape { |
68 | 6 | reason: "Shape cannot be empty".to_string(), |
69 | 6 | }); |
70 | 47.9k | } |
71 | | |
72 | 47.9k | if shape.contains(&0) { |
73 | 4 | return Err(RealizarError::InvalidShape { |
74 | 4 | reason: "Shape dimensions cannot be zero".to_string(), |
75 | 4 | }); |
76 | 47.9k | } |
77 | | |
78 | | // Calculate expected size |
79 | 47.9k | let expected_size = shape.iter().product(); |
80 | | |
81 | | // Validate data size |
82 | 47.9k | if data.len() != expected_size { |
83 | 2 | return Err(RealizarError::DataShapeMismatch { |
84 | 2 | data_size: data.len(), |
85 | 2 | shape, |
86 | 2 | expected: expected_size, |
87 | 2 | }); |
88 | 47.9k | } |
89 | | |
90 | 47.9k | Ok(Self { data, shape }) |
91 | 47.9k | } |
92 | | |
93 | | /// Get the shape of the tensor |
94 | | /// |
95 | | /// # Examples |
96 | | /// |
97 | | /// ``` |
98 | | /// use realizar::Tensor; |
99 | | /// |
100 | | /// let t = Tensor::from_vec(vec![3, 4], vec![0.0; 12]).expect("test"); |
101 | | /// assert_eq!(t.shape(), &[3, 4]); |
102 | | /// ``` |
103 | | #[must_use] |
104 | 36.8k | pub fn shape(&self) -> &[usize] { |
105 | 36.8k | &self.shape |
106 | 36.8k | } |
107 | | |
108 | | /// Get the number of dimensions |
109 | | /// |
110 | | /// # Examples |
111 | | /// |
112 | | /// ``` |
113 | | /// use realizar::Tensor; |
114 | | /// |
115 | | /// let t = Tensor::from_vec(vec![2, 3, 4], vec![0.0; 24]).expect("test"); |
116 | | /// assert_eq!(t.ndim(), 3); |
117 | | /// ``` |
118 | | #[must_use] |
119 | 1 | pub fn ndim(&self) -> usize { |
120 | 1 | self.shape.len() |
121 | 1 | } |
122 | | |
123 | | /// Get the total number of elements |
124 | | /// |
125 | | /// # Examples |
126 | | /// |
127 | | /// ``` |
128 | | /// use realizar::Tensor; |
129 | | /// |
130 | | /// let t = Tensor::from_vec(vec![2, 3], vec![0.0; 6]).expect("test"); |
131 | | /// assert_eq!(t.size(), 6); |
132 | | /// ``` |
133 | | #[must_use] |
134 | 1.21k | pub fn size(&self) -> usize { |
135 | 1.21k | self.data.len() |
136 | 1.21k | } |
137 | | |
138 | | /// Get a reference to the underlying data |
139 | | /// |
140 | | /// # Examples |
141 | | /// |
142 | | /// ``` |
143 | | /// use realizar::Tensor; |
144 | | /// |
145 | | /// let t = Tensor::from_vec(vec![2], vec![1.0, 2.0]).expect("test"); |
146 | | /// assert_eq!(t.data(), &[1.0, 2.0]); |
147 | | /// ``` |
148 | | #[must_use] |
149 | 7.63M | pub fn data(&self) -> &[T] { |
150 | 7.63M | &self.data |
151 | 7.63M | } |
152 | | } |
153 | | |
154 | | impl<T: Num + Clone + fmt::Display> fmt::Display for Tensor<T> { |
155 | 1 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
156 | 1 | write!(f, "Tensor(shape={:?}, data=[", self.shape)?0 ; |
157 | 2 | for (i, val) in self.data.iter()1 .enumerate1 () { |
158 | 2 | if i > 0 { |
159 | 1 | write!(f, ", ")?0 ; |
160 | 1 | } |
161 | 2 | write!(f, "{val}")?0 ; |
162 | | } |
163 | 1 | write!(f, "])") |
164 | 1 | } |
165 | | } |
166 | | |
167 | | #[cfg(test)] |
168 | | mod tests { |
169 | | use super::*; |
170 | | |
171 | | #[test] |
172 | 1 | fn test_create_tensor() { |
173 | 1 | let t = Tensor::from_vec(vec![2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).expect("test"); |
174 | 1 | assert_eq!(t.shape(), &[2, 3]); |
175 | 1 | assert_eq!(t.ndim(), 2); |
176 | 1 | assert_eq!(t.size(), 6); |
177 | 1 | } |
178 | | |
179 | | #[test] |
180 | 1 | fn test_empty_shape_error() { |
181 | 1 | let result = Tensor::from_vec(vec![], vec![1.0, 2.0]); |
182 | 1 | assert!(result.is_err()); |
183 | 1 | assert!(matches!0 ( |
184 | 1 | result.unwrap_err(), |
185 | | RealizarError::InvalidShape { .. } |
186 | | )); |
187 | 1 | } |
188 | | |
189 | | #[test] |
190 | 1 | fn test_zero_dimension_error() { |
191 | 1 | let result = Tensor::<f32>::from_vec(vec![2, 0], vec![]); |
192 | 1 | assert!(result.is_err()); |
193 | 1 | } |
194 | | |
195 | | #[test] |
196 | 1 | fn test_size_mismatch_error() { |
197 | 1 | let result = Tensor::from_vec(vec![2, 3], vec![1.0, 2.0]); |
198 | 1 | assert!(result.is_err()); |
199 | 1 | assert!(matches!0 ( |
200 | 1 | result.unwrap_err(), |
201 | | RealizarError::DataShapeMismatch { .. } |
202 | | )); |
203 | 1 | } |
204 | | |
205 | | #[test] |
206 | 1 | fn test_display() { |
207 | 1 | let t = Tensor::from_vec(vec![2], vec![1.0, 2.0]).expect("test"); |
208 | 1 | let display = format!("{t}"); |
209 | 1 | assert!(display.contains("shape=[2]")); |
210 | 1 | assert!(display.contains('1')); |
211 | 1 | assert!(display.contains('2')); |
212 | 1 | } |
213 | | } |