Coverage Report

Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}