Skip to main content

tenflowers_core/tensor/
creation.rs

1//! Tensor Creation and Construction
2//!
3//! This module contains all tensor constructor methods, including creation
4//! from various data sources, initialization with specific patterns,
5//! and range generation functions.
6
7use super::core::{Tensor, TensorStorage};
8use crate::{Device, Result, Shape, TensorError};
9use scirs2_core::ndarray::{ArrayD, IxDyn};
10
11// Impl block for constructors that need Default
12impl<T: Clone + Default> Tensor<T> {
13    /// Create a tensor filled with zeros
14    pub fn zeros(shape: &[usize]) -> Self
15    where
16        T: scirs2_core::num_traits::Zero,
17    {
18        let array = ArrayD::zeros(IxDyn(shape));
19        Self {
20            storage: TensorStorage::Cpu(array),
21            shape: Shape::from_slice(shape),
22            device: Device::Cpu,
23            requires_grad: false,
24            grad: None,
25        }
26    }
27
28    /// Create a tensor filled with ones
29    pub fn ones(shape: &[usize]) -> Self
30    where
31        T: scirs2_core::num_traits::One,
32    {
33        let array = ArrayD::ones(IxDyn(shape));
34        Self {
35            storage: TensorStorage::Cpu(array),
36            shape: Shape::from_slice(shape),
37            device: Device::Cpu,
38            requires_grad: false,
39            grad: None,
40        }
41    }
42
43    /// Create a tensor from raw data vector with specified shape
44    pub fn from_data(data: Vec<T>, shape: &[usize]) -> Result<Self> {
45        let total_elements: usize = shape.iter().product();
46        if data.len() != total_elements {
47            return Err(TensorError::invalid_shape_simple(format!(
48                "Data length {} does not match shape {:?} (expected {} elements)",
49                data.len(),
50                shape,
51                total_elements
52            )));
53        }
54
55        let array = ArrayD::from_shape_vec(IxDyn(shape), data)
56            .map_err(|e| TensorError::invalid_shape_simple(e.to_string()))?;
57
58        Ok(Self {
59            storage: TensorStorage::Cpu(array),
60            shape: Shape::from_slice(shape),
61            device: Device::Cpu,
62            requires_grad: false,
63            grad: None,
64        })
65    }
66
67    /// Create a tensor from an existing ndarray
68    pub fn from_array(array: ArrayD<T>) -> Self {
69        let shape = Shape::from_slice(array.shape());
70        Self {
71            storage: TensorStorage::Cpu(array),
72            shape,
73            device: Device::Cpu,
74            requires_grad: false,
75            grad: None,
76        }
77    }
78
79    /// Create a tensor filled with random values from normal distribution
80    pub fn randn(shape: &[usize]) -> Result<Self>
81    where
82        T: Clone + Default + From<f32>,
83    {
84        let total_elements: usize = shape.iter().product();
85        let mut data = Vec::with_capacity(total_elements);
86
87        // Simple random number generation (placeholder for proper implementation)
88        for i in 0..total_elements {
89            // Use a simple pseudo-random approach for now
90            let val = ((i as f32 * 17.0 + 7.0).sin() * 10000.0).fract() - 0.5;
91            data.push(T::from(val));
92        }
93
94        Self::from_data(data, shape)
95    }
96
97    /// Create a tensor from storage and device
98    pub fn from_storage(storage: TensorStorage<T>, device: Device) -> Self {
99        let shape = match &storage {
100            TensorStorage::Cpu(array) => Shape::from_slice(array.shape()),
101            #[cfg(feature = "gpu")]
102            TensorStorage::Gpu(_buffer) => {
103                // GPU buffers don't store shape information directly
104                // This should use from_gpu_buffer instead which takes shape as parameter
105                panic!("from_storage not supported for GPU buffers - use from_gpu_buffer instead")
106            }
107        };
108        Self {
109            storage,
110            shape,
111            device,
112            requires_grad: false,
113            grad: None,
114        }
115    }
116
117    /// Create a tensor from a GPU buffer
118    #[cfg(feature = "gpu")]
119    pub fn from_gpu_buffer(buffer: crate::gpu::buffer::GpuBuffer<T>, shape: Shape) -> Self {
120        // Default to GPU device 0 - in a full implementation, this should be passed as parameter
121        let device = crate::Device::Gpu(0);
122        Self {
123            storage: TensorStorage::Gpu(buffer),
124            shape,
125            device,
126            requires_grad: false,
127            grad: None,
128        }
129    }
130
131    /// Create a scalar tensor from a single value
132    pub fn from_scalar(value: T) -> Self {
133        Self::from_array(ArrayD::from_elem(IxDyn(&[]), value))
134    }
135
136    /// Create a tensor from a vector of data with specified shape
137    pub fn from_vec(data: Vec<T>, shape: &[usize]) -> Result<Self> {
138        let total_size: usize = shape.iter().product();
139        if data.len() != total_size {
140            return Err(TensorError::invalid_shape_simple(format!(
141                "Data length {} doesn't match shape {:?} (size {})",
142                data.len(),
143                shape,
144                total_size
145            )));
146        }
147
148        let array = ArrayD::from_shape_vec(IxDyn(shape), data)
149            .map_err(|e| TensorError::invalid_shape_simple(e.to_string()))?;
150
151        Ok(Self::from_array(array))
152    }
153
154    /// Create a tensor filled with a specific value
155    pub fn full(shape: &[usize], value: T) -> Self
156    where
157        T: Clone,
158    {
159        let array = ArrayD::from_elem(IxDyn(shape), value);
160        Self {
161            storage: TensorStorage::Cpu(array),
162            shape: Shape::from_slice(shape),
163            device: Device::Cpu,
164            requires_grad: false,
165            grad: None,
166        }
167    }
168
169    /// Create an identity matrix tensor
170    pub fn eye(n: usize) -> Self
171    where
172        T: scirs2_core::num_traits::Zero + scirs2_core::num_traits::One + Clone,
173    {
174        let mut array = ArrayD::zeros(IxDyn(&[n, n]));
175        for i in 0..n {
176            array[[i, i]] = T::one();
177        }
178        Self {
179            storage: TensorStorage::Cpu(array),
180            shape: Shape::from_slice(&[n, n]),
181            device: Device::Cpu,
182            requires_grad: false,
183            grad: None,
184        }
185    }
186
187    /// Create a tensor with evenly spaced values in a given interval
188    pub fn arange(start: T, end: T, step: T) -> Result<Self>
189    where
190        T: scirs2_core::num_traits::Float
191            + scirs2_core::num_traits::ToPrimitive
192            + scirs2_core::num_traits::FromPrimitive,
193    {
194        let start_f = start.to_f64().ok_or_else(|| {
195            crate::TensorError::invalid_argument("Invalid start value".to_string())
196        })?;
197        let end_f = end
198            .to_f64()
199            .ok_or_else(|| crate::TensorError::invalid_argument("Invalid end value".to_string()))?;
200        let step_f = step.to_f64().ok_or_else(|| {
201            crate::TensorError::invalid_argument("Invalid step value".to_string())
202        })?;
203
204        if step_f == 0.0 {
205            return Err(crate::TensorError::invalid_argument(
206                "Step cannot be zero".to_string(),
207            ));
208        }
209
210        let size = ((end_f - start_f) / step_f).ceil() as usize;
211        let mut data = Vec::with_capacity(size);
212
213        let mut current = start_f;
214        while (step_f > 0.0 && current < end_f) || (step_f < 0.0 && current > end_f) {
215            if let Some(val) = T::from_f64(current) {
216                data.push(val);
217            } else {
218                return Err(crate::TensorError::invalid_argument(
219                    "Failed to convert value".to_string(),
220                ));
221            }
222            current += step_f;
223        }
224
225        let size = data.len();
226        Self::from_vec(data, &[size])
227    }
228
229    /// Create a tensor with linearly spaced values between start and end
230    pub fn linspace(start: T, end: T, steps: usize) -> Result<Self>
231    where
232        T: scirs2_core::num_traits::Float
233            + scirs2_core::num_traits::ToPrimitive
234            + scirs2_core::num_traits::FromPrimitive,
235    {
236        if steps == 0 {
237            return Err(crate::TensorError::invalid_argument(
238                "Steps must be greater than 0".to_string(),
239            ));
240        }
241
242        let start_f = start.to_f64().ok_or_else(|| {
243            crate::TensorError::invalid_argument("Invalid start value".to_string())
244        })?;
245        let end_f = end
246            .to_f64()
247            .ok_or_else(|| crate::TensorError::invalid_argument("Invalid end value".to_string()))?;
248
249        if steps == 1 {
250            let val = T::from_f64(start_f).ok_or_else(|| {
251                crate::TensorError::invalid_argument("Failed to convert value".to_string())
252            })?;
253            return Self::from_vec(vec![val], &[1]);
254        }
255
256        let step = (end_f - start_f) / (steps - 1) as f64;
257        let mut data = Vec::with_capacity(steps);
258
259        for i in 0..steps {
260            let val = start_f + step * i as f64;
261            let tensor_val = T::from_f64(val).ok_or_else(|| {
262                crate::TensorError::invalid_argument("Failed to convert value".to_string())
263            })?;
264            data.push(tensor_val);
265        }
266
267        Self::from_vec(data, &[steps])
268    }
269}