1use crate::{Tensor, TensorStorage};
4use std::collections::HashMap;
5use std::sync::{Arc, RwLock, Weak};
6use torsh_core::sync::RwLockExt;
7use torsh_core::{
8 device::DeviceType,
9 dtype::TensorElement,
10 error::{Result, TorshError},
11 shape::Shape,
12};
13
14#[derive(Debug, Clone)]
16pub struct TensorView<T: TensorElement> {
17 storage: Arc<RwLock<ViewStorage<T>>>,
19 shape: Shape,
21 strides: Vec<usize>,
23 offset: usize,
25 device: DeviceType,
27}
28
29#[derive(Debug)]
31struct ViewStorage<T: TensorElement> {
32 #[allow(dead_code)]
34 parent: Weak<RwLock<Vec<T>>>,
35 data_ref: Option<Arc<RwLock<Vec<T>>>>,
37 view_cache: HashMap<ViewKey, Arc<TensorView<T>>>,
39 view_count: usize,
41}
42
43#[derive(Debug, Hash, PartialEq, Eq, Clone)]
45struct ViewKey {
46 shape: Vec<usize>,
47 strides: Vec<usize>,
48 offset: usize,
49}
50
51impl<T: TensorElement + Copy> Tensor<T> {
52 pub fn calculate_strides(&self) -> Vec<usize> {
54 let shape_binding = self.shape();
55 let dims = shape_binding.dims();
56 let mut strides = vec![1; dims.len()];
57 for i in (0..dims.len().saturating_sub(1)).rev() {
58 strides[i] = strides[i + 1] * dims[i + 1];
59 }
60 strides
61 }
62 pub fn create_view(&self, new_shape: &[usize]) -> Result<TensorView<T>> {
64 let new_numel = new_shape.iter().product::<usize>();
65 if new_numel != self.numel() {
66 return Err(TorshError::InvalidOperation(format!(
67 "View shape {:?} has {} elements, but tensor has {} elements",
68 new_shape,
69 new_numel,
70 self.numel()
71 )));
72 }
73
74 let mut strides = vec![1; new_shape.len()];
76 for i in (0..new_shape.len().saturating_sub(1)).rev() {
77 strides[i] = strides[i + 1] * new_shape[i + 1];
78 }
79
80 self.create_view_with_strides(new_shape, &strides, 0)
81 }
82
83 pub fn view_with_strides(
85 &self,
86 new_shape: &[usize],
87 strides: &[usize],
88 ) -> Result<TensorView<T>> {
89 if new_shape.len() != strides.len() {
90 return Err(TorshError::InvalidOperation(
91 "Shape and strides must have same length".to_string(),
92 ));
93 }
94
95 self.create_view_with_strides(new_shape, strides, 0)
96 }
97
98 pub fn slice(&self, dim: usize, start: usize, end: usize) -> Result<TensorView<T>> {
100 let shape_binding = self.shape();
101 let dims = shape_binding.dims();
102 if dim >= dims.len() {
103 return Err(TorshError::InvalidOperation(format!(
104 "Dimension {} out of bounds for tensor with {} dimensions",
105 dim,
106 dims.len()
107 )));
108 }
109
110 if start >= end || end > dims[dim] {
111 return Err(TorshError::InvalidOperation(format!(
112 "Invalid slice range [{}:{}] for dimension of size {}",
113 start, end, dims[dim]
114 )));
115 }
116
117 let mut new_shape = dims.to_vec();
119 new_shape[dim] = end - start;
120
121 let strides = self.calculate_strides();
123 let offset = start * strides[dim];
124
125 self.create_view_with_strides(&new_shape, &strides, offset)
126 }
127
128 fn create_view_with_strides(
130 &self,
131 shape: &[usize],
132 strides: &[usize],
133 offset: usize,
134 ) -> Result<TensorView<T>> {
135 let data_ref = match &self.storage {
137 TensorStorage::InMemory(data) => data.clone(),
138 TensorStorage::MemoryMapped(_) => {
139 let data = self.to_vec()?;
141 Arc::new(RwLock::new(data))
142 }
143 #[cfg(feature = "simd")]
144 TensorStorage::Aligned(data) => {
145 let aligned_data = data.read_or_recover();
147 let vec_data = aligned_data.as_slice().to_vec();
148 Arc::new(RwLock::new(vec_data))
149 }
150 #[cfg(feature = "simd")]
151 TensorStorage::SimdOptimized(storage) => {
152 let vec_data = storage.to_vec();
155 Arc::new(RwLock::new(vec_data))
156 }
157 #[cfg(feature = "gpu")]
158 TensorStorage::Device { .. } => {
159 let data = self.to_vec()?;
164 Arc::new(RwLock::new(data))
165 }
166 };
167
168 let view_storage = ViewStorage {
170 parent: Arc::downgrade(&data_ref),
171 data_ref: Some(data_ref),
172 view_cache: HashMap::new(),
173 view_count: 1,
174 };
175
176 Ok(TensorView {
177 storage: Arc::new(RwLock::new(view_storage)),
178 shape: Shape::new(shape.to_vec()),
179 strides: strides.to_vec(),
180 offset,
181 device: self.device,
182 })
183 }
184
185 pub fn alias(&self) -> TensorAlias<T> {
187 TensorAlias {
188 tensor: self.clone(),
189 is_mutable: false,
190 }
191 }
192
193 pub fn alias_mut(&mut self) -> TensorAlias<T> {
195 TensorAlias {
196 tensor: self.clone(),
197 is_mutable: true,
198 }
199 }
200}
201
202impl<T: TensorElement + Copy> TensorView<T> {
203 pub fn shape(&self) -> &Shape {
205 &self.shape
206 }
207
208 pub fn strides(&self) -> &[usize] {
210 &self.strides
211 }
212
213 pub fn offset(&self) -> usize {
215 self.offset
216 }
217
218 pub fn to_tensor(&self) -> Result<Tensor<T>> {
220 let data = self.to_vec()?;
221 Tensor::from_data(data, self.shape.dims().to_vec(), self.device)
222 }
223
224 pub fn to_vec(&self) -> Result<Vec<T>> {
226 let storage = self.storage.read_or_recover();
227 if let Some(data_ref) = &storage.data_ref {
228 let data = data_ref.read_or_recover();
229 let mut result = Vec::with_capacity(self.shape.numel());
230
231 self.extract_view_data(&data, &mut result, &mut vec![0; self.shape.ndim()], 0)?;
233
234 Ok(result)
235 } else {
236 Err(TorshError::InvalidOperation(
237 "View data no longer available".to_string(),
238 ))
239 }
240 }
241
242 fn extract_view_data(
244 &self,
245 data: &[T],
246 result: &mut Vec<T>,
247 indices: &mut [usize],
248 dim: usize,
249 ) -> Result<()> {
250 if dim == self.shape.ndim() {
251 let flat_index = self.offset
253 + indices
254 .iter()
255 .zip(self.strides.iter())
256 .map(|(&idx, &stride)| idx * stride)
257 .sum::<usize>();
258
259 if flat_index < data.len() {
260 result.push(data[flat_index]);
261 } else {
262 return Err(TorshError::InvalidOperation(
263 "View index out of bounds".to_string(),
264 ));
265 }
266 } else {
267 for i in 0..self.shape.dims()[dim] {
268 indices[dim] = i;
269 self.extract_view_data(data, result, indices, dim + 1)?;
270 }
271 }
272 Ok(())
273 }
274
275 pub fn is_contiguous(&self) -> bool {
277 let dims = self.shape.dims();
279 let mut expected_strides = vec![1; dims.len()];
280 for i in (0..dims.len().saturating_sub(1)).rev() {
281 expected_strides[i] = expected_strides[i + 1] * dims[i + 1];
282 }
283 self.strides == expected_strides
284 }
285
286 pub fn is_view(&self) -> bool {
288 true
289 }
290
291 pub fn get(&self, indices: &[usize]) -> Result<T> {
293 if indices.len() != self.shape.ndim() {
294 return Err(TorshError::InvalidOperation(format!(
295 "Expected {} indices, got {}",
296 self.shape.ndim(),
297 indices.len()
298 )));
299 }
300
301 for (i, &idx) in indices.iter().enumerate() {
302 if idx >= self.shape.dims()[i] {
303 return Err(TorshError::InvalidOperation(format!(
304 "Index {} out of bounds for dimension {} (size {})",
305 idx,
306 i,
307 self.shape.dims()[i]
308 )));
309 }
310 }
311
312 let storage = self.storage.read_or_recover();
313 if let Some(data_ref) = &storage.data_ref {
314 let data = data_ref.read_or_recover();
315
316 let flat_index = self.offset
318 + indices
319 .iter()
320 .zip(self.strides.iter())
321 .map(|(&idx, &stride)| idx * stride)
322 .sum::<usize>();
323
324 if flat_index < data.len() {
325 Ok(data[flat_index])
326 } else {
327 Err(TorshError::InvalidOperation(
328 "View index out of bounds".to_string(),
329 ))
330 }
331 } else {
332 Err(TorshError::InvalidOperation(
333 "View data no longer available".to_string(),
334 ))
335 }
336 }
337
338 pub fn view_memory_usage(&self) -> ViewMemoryUsage {
340 let storage = self.storage.read_or_recover();
341 ViewMemoryUsage {
342 view_elements: self.shape.numel(),
343 total_elements: storage
344 .data_ref
345 .as_ref()
346 .map(|data| data.read_or_recover().len())
347 .unwrap_or(0),
348 active_views: storage.view_count,
349 is_contiguous: self.is_contiguous(),
350 memory_efficiency: self.calculate_memory_efficiency(),
351 }
352 }
353
354 fn calculate_memory_efficiency(&self) -> f64 {
356 let view_size = self.shape.numel();
357 let storage = self.storage.read_or_recover();
358 let total_size = storage
359 .data_ref
360 .as_ref()
361 .map(|data| data.read_or_recover().len())
362 .unwrap_or(1);
363
364 view_size as f64 / total_size as f64
365 }
366}
367
368#[derive(Debug, Clone)]
370pub struct TensorAlias<T: TensorElement> {
371 tensor: Tensor<T>,
372 is_mutable: bool,
373}
374
375impl<T: TensorElement + Copy> TensorAlias<T> {
376 pub fn tensor(&self) -> &Tensor<T> {
378 &self.tensor
379 }
380
381 pub fn is_mutable(&self) -> bool {
383 self.is_mutable
384 }
385
386 pub fn to_owned(&self) -> Result<Tensor<T>> {
388 Ok(self.tensor.clone())
389 }
390
391 pub fn ref_count(&self) -> usize {
393 match &self.tensor.storage {
394 TensorStorage::InMemory(data) => Arc::strong_count(data),
395 TensorStorage::MemoryMapped(storage) => Arc::strong_count(storage),
396 #[cfg(feature = "simd")]
397 TensorStorage::Aligned(data) => Arc::strong_count(data),
398 #[cfg(feature = "simd")]
399 TensorStorage::SimdOptimized(storage) => Arc::strong_count(storage),
400 #[cfg(feature = "gpu")]
401 TensorStorage::Device { buffer, .. } => Arc::strong_count(buffer),
402 }
403 }
404
405 pub fn is_unique(&self) -> bool {
407 self.ref_count() == 1
408 }
409}
410
411#[derive(Debug, Clone)]
413pub struct ViewMemoryUsage {
414 pub view_elements: usize,
416 pub total_elements: usize,
418 pub active_views: usize,
420 pub is_contiguous: bool,
422 pub memory_efficiency: f64,
424}
425
426impl<T: TensorElement + Copy> Drop for ViewStorage<T> {
427 fn drop(&mut self) {
428 self.view_cache.clear();
430 self.view_count = 0;
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use crate::creation::*;
437
438 #[test]
439 fn test_tensor_view() {
440 let tensor = ones::<f32>(&[2, 3, 4]).expect("ones creation should succeed");
441 let view = tensor
442 .create_view(&[6, 4])
443 .expect("create_view should succeed");
444 assert_eq!(view.shape().dims(), &[6, 4]);
445 assert_eq!(view.shape().numel(), 24);
446 }
447
448 #[test]
449 fn test_tensor_slice() {
450 let tensor = arange(0.0f32, 12.0, 1.0).expect("arange should succeed");
451 let _reshaped = tensor
452 .create_view(&[3, 4])
453 .expect("create_view should succeed");
454 }
458
459 #[test]
460 fn test_tensor_squeeze_unsqueeze() {
461 let tensor = ones::<f32>(&[1, 3, 1, 4]).expect("ones creation should succeed");
462 let squeezed = tensor.squeeze(0).expect("squeeze should succeed");
463 assert_eq!(squeezed.shape().dims(), &[3, 1, 4]);
464
465 let squeezed_all = tensor.squeeze_all().expect("squeeze_all should succeed");
466 assert_eq!(squeezed_all.shape().dims(), &[3, 4]);
467
468 let unsqueezed = tensor.unsqueeze(2).expect("unsqueeze should succeed");
469 assert_eq!(unsqueezed.shape().dims(), &[1, 3, 1, 1, 4]);
470 }
471
472 #[test]
473 fn test_tensor_permute() {
474 let tensor = ones::<f32>(&[2, 3, 4]).expect("ones creation should succeed");
475 let permuted = tensor.permute(&[2, 0, 1]).expect("permute should succeed");
476 assert_eq!(permuted.shape().dims(), &[4, 2, 3]);
477 }
478
479 #[test]
480 fn test_tensor_alias() {
481 let tensor = ones::<f32>(&[10, 10]).expect("ones creation should succeed");
482 let alias = tensor.alias();
483 assert!(!alias.is_mutable());
484 assert!(alias.ref_count() >= 2); }
486
487 #[test]
488 fn test_view_memory_usage() {
489 let tensor = ones::<f32>(&[100, 100]).expect("ones creation should succeed");
490 let view = tensor
491 .create_view(&[1000, 10])
492 .expect("create_view should succeed");
493 let usage = view.view_memory_usage();
494 assert_eq!(usage.view_elements, 10000);
495 assert_eq!(usage.memory_efficiency, 1.0); }
497}