tensor4all_tensorbackend/
tensor_element.rs1use anyhow::{anyhow, ensure, Result};
2use num_complex::{Complex32, Complex64};
3use tenferro::{DType, Tensor as NativeTensor, TensorScalar};
4
5pub trait TensorElement: TensorScalar + Copy + Send + Sync + 'static {
21 fn dense_native_tensor_from_col_major(data: &[Self], dims: &[usize]) -> Result<NativeTensor>;
28
29 fn dense_native_tensor_from_col_major_owned(
64 data: Vec<Self>,
65 dims: &[usize],
66 ) -> Result<NativeTensor>;
67
68 fn diag_native_tensor_from_col_major(
75 data: &[Self],
76 logical_rank: usize,
77 ) -> Result<NativeTensor>;
78
79 fn scalar_native_tensor(value: Self) -> Result<NativeTensor>;
86
87 fn dense_values_from_native_col_major(tensor: &NativeTensor) -> Result<Vec<Self>>;
94
95 fn diag_values_from_native_temp(tensor: &NativeTensor) -> Result<Vec<Self>>;
102}
103
104fn tensor_dtype_name(dtype: DType) -> &'static str {
105 match dtype {
106 DType::F32 => "f32",
107 DType::F64 => "f64",
108 DType::I32 => "i32",
109 DType::I64 => "i64",
110 DType::Bool => "bool",
111 DType::C32 => "c32",
112 DType::C64 => "c64",
113 }
114}
115
116fn checked_product(dims: &[usize]) -> Result<usize> {
117 dims.iter().try_fold(1usize, |acc, &dim| {
118 acc.checked_mul(dim)
119 .ok_or_else(|| anyhow::anyhow!("dimension product overflow"))
120 })
121}
122
123fn dense_diagonal_values<T: Copy + Default>(diag: &[T], logical_rank: usize) -> Result<Vec<T>> {
124 ensure!(
125 logical_rank >= 1,
126 "diagonal tensor construction requires at least one logical axis"
127 );
128 let diag_len = diag.len();
129 let dims = vec![diag_len; logical_rank];
130 let total_len = checked_product(&dims)?;
131 let mut dense = vec![T::default(); total_len];
132 let diagonal_stride = (0..logical_rank)
133 .scan(1usize, |stride, _| {
134 let current = *stride;
135 *stride = stride.saturating_mul(diag_len);
136 Some(current)
137 })
138 .sum::<usize>();
139 for (i, value) in diag.iter().copied().enumerate() {
140 dense[i * diagonal_stride] = value;
141 }
142 Ok(dense)
143}
144
145macro_rules! impl_tensor_element {
146 ($ty:ty, $dtype:expr) => {
147 impl TensorElement for $ty {
148 fn dense_native_tensor_from_col_major(
149 data: &[Self],
150 dims: &[usize],
151 ) -> Result<NativeTensor> {
152 let expected_len: usize = checked_product(dims)?;
153 ensure!(
154 data.len() == expected_len,
155 "dense tensor len {} does not match dims {:?} (expected {})",
156 data.len(),
157 dims,
158 expected_len
159 );
160 Ok(NativeTensor::from_vec_col_major(
161 dims.to_vec(),
162 data.to_vec(),
163 )?)
164 }
165
166 fn dense_native_tensor_from_col_major_owned(
167 data: Vec<Self>,
168 dims: &[usize],
169 ) -> Result<NativeTensor> {
170 let expected_len: usize = checked_product(dims)?;
171 ensure!(
172 data.len() == expected_len,
173 "dense tensor len {} does not match dims {:?} (expected {})",
174 data.len(),
175 dims,
176 expected_len
177 );
178 Ok(NativeTensor::from_vec_col_major(dims.to_vec(), data)?)
179 }
180
181 fn diag_native_tensor_from_col_major(
182 data: &[Self],
183 logical_rank: usize,
184 ) -> Result<NativeTensor> {
185 let dims = vec![data.len(); logical_rank];
186 let dense = dense_diagonal_values(data, logical_rank)?;
187 Self::dense_native_tensor_from_col_major(&dense, &dims)
188 }
189
190 fn scalar_native_tensor(value: Self) -> Result<NativeTensor> {
191 Ok(NativeTensor::from_vec_col_major(vec![], vec![value])?)
192 }
193
194 fn dense_values_from_native_col_major(tensor: &NativeTensor) -> Result<Vec<Self>> {
195 tensor
196 .as_slice::<Self>()
197 .map(|values| values.to_vec())
198 .map_err(|_| {
199 anyhow!(
200 "tensor dtype mismatch: expected {}, got {}",
201 tensor_dtype_name($dtype),
202 tensor_dtype_name(tensor.dtype())
203 )
204 })
205 }
206
207 fn diag_values_from_native_temp(tensor: &NativeTensor) -> Result<Vec<Self>> {
208 let shape = tensor.shape();
209 ensure!(
210 !shape.is_empty(),
211 "diagonal extraction requires rank >= 1, got scalar tensor"
212 );
213 let diag_len = shape[0];
214 ensure!(
215 shape.iter().all(|&dim| dim == diag_len),
216 "expected square/equal dims for diagonal extraction, got {:?}",
217 shape
218 );
219 let dense = Self::dense_values_from_native_col_major(tensor)?;
220 let diagonal_stride = (0..shape.len())
221 .scan(1usize, |stride, _| {
222 let current = *stride;
223 *stride = stride.saturating_mul(diag_len);
224 Some(current)
225 })
226 .sum::<usize>();
227 Ok((0..diag_len).map(|i| dense[i * diagonal_stride]).collect())
228 }
229 }
230 };
231}
232
233impl_tensor_element!(f32, DType::F32);
234impl_tensor_element!(f64, DType::F64);
235impl_tensor_element!(Complex32, DType::C32);
236impl_tensor_element!(Complex64, DType::C64);