1#[cfg(not(any(feature = "cpu-faer", feature = "cpu-blas")))]
20compile_error!("enable at least one CPU backend: cpu-faer or cpu-blas");
21
22#[cfg(all(feature = "provider-inject", not(feature = "cpu-blas")))]
23compile_error!("provider-inject requires cpu-blas");
24
25#[cfg(any(
26 all(feature = "blas-openblas", feature = "blas-accelerate"),
27 all(feature = "blas-openblas", feature = "blas-mkl"),
28 all(feature = "blas-accelerate", feature = "blas-mkl"),
29))]
30compile_error!(
31 "enable at most one explicit BLAS provider feature: blas-openblas, blas-accelerate, or blas-mkl"
32);
33
34#[cfg(all(
35 feature = "provider-inject",
36 any(
37 feature = "blas-openblas",
38 feature = "blas-accelerate",
39 feature = "blas-mkl"
40 )
41))]
42compile_error!("provider-inject cannot be combined with explicit BLAS provider features");
43
44pub mod affinity;
45mod analytic;
46pub mod backend;
47mod buffer_pool;
48mod capability;
49pub mod context;
50mod elementwise;
51mod exec_session;
52mod gemm;
53mod indexing;
54mod indexing_alloc;
55#[cfg(feature = "provider-inject")]
56pub mod inject;
57mod reduction;
58mod structural;
59
60use strided_kernel::{col_major_strides as kernel_col_major_strides, StridedArray, StridedView};
61
62use crate::buffer_pool::{BufferPool, PoolScalar};
63pub(crate) use tenferro_tensor::*;
64
65#[cfg(feature = "provider-src")]
66extern crate blas_src as _;
67#[cfg(feature = "provider-inject")]
68extern crate cblas_inject as _;
69#[cfg(feature = "provider-src")]
70extern crate cblas_src as _;
71#[cfg(feature = "provider-inject")]
72extern crate lapack_inject as _;
73#[cfg(feature = "provider-src")]
74extern crate lapack_src as _;
75
76pub use affinity::{available_parallelism, process_cpu_affinity_count};
77pub use analytic::pow;
78pub use backend::{CpuBackend, CpuBackendKind};
79pub use buffer_pool::BufferPoolStats;
80pub use capability::cpu_capabilities;
81pub use context::CpuContext;
82pub use elementwise::{
83 abs, add, clamp, compare, conj, div, maximum, minimum, mul, neg, rem, select, sign, sub,
84};
85pub use indexing::{dynamic_slice, dynamic_update_slice, gather, pad, scatter};
86pub use reduction::{reduce_max, reduce_min, reduce_prod, reduce_sum};
87pub use structural::{
88 broadcast_in_dim, convert, embed_diagonal, extract_diagonal, reshape, transpose, tril, triu,
89};
90
91#[doc(hidden)]
97pub mod linalg_interop {
98 pub use crate::buffer_pool::{BufferPool, PoolScalar};
99}
100
101pub(crate) fn cpu_backend_buffer_error(op: &'static str) -> crate::Error {
102 crate::Error::backend_failure(
103 op,
104 "CPU backend received backend buffer; download to host before CPU execution",
105 )
106}
107
108pub(crate) trait ConjElem {
109 fn conj_elem(self) -> Self;
110}
111
112impl ConjElem for f32 {
113 fn conj_elem(self) -> Self {
114 self
115 }
116}
117
118impl ConjElem for f64 {
119 fn conj_elem(self) -> Self {
120 self
121 }
122}
123
124impl ConjElem for num_complex::Complex32 {
125 fn conj_elem(self) -> Self {
126 self.conj()
127 }
128}
129
130impl ConjElem for num_complex::Complex64 {
131 fn conj_elem(self) -> Self {
132 self.conj()
133 }
134}
135
136pub(crate) fn typed_host_data<'a, T>(
137 op: &'static str,
138 tensor: &'a TypedTensor<T>,
139) -> crate::Result<&'a [T]> {
140 match tensor.buffer() {
141 Buffer::Host(data) => Ok(data.as_slice()),
142 Buffer::Backend(_) => Err(cpu_backend_buffer_error(op)),
143 }
144}
145
146pub(crate) fn typed_view<'a, T: Copy>(
147 op: &'static str,
148 tensor: &'a TypedTensor<T>,
149) -> crate::Result<StridedView<'a, T>> {
150 match tensor.buffer() {
151 Buffer::Host(data) => {
152 let strides = kernel_col_major_strides(tensor.shape());
153 StridedView::new(data.as_slice(), tensor.shape(), &strides, 0)
154 .map_err(|err| crate::Error::backend_failure(op, err))
155 }
156 Buffer::Backend(_) => Err(cpu_backend_buffer_error(op)),
157 }
158}
159
160pub(crate) fn typed_view_from_view<'a, T: Copy + 'static, R: TensorRank>(
161 op: &'static str,
162 view: &TypedTensorView<'a, T, R>,
163) -> crate::Result<StridedView<'a, T>> {
164 if view.backend_buffer().is_some() {
165 return Err(cpu_backend_buffer_error(op));
166 }
167 StridedView::new(
168 view.host_storage()?,
169 view.shape(),
170 view.strides(),
171 view.offset(),
172 )
173 .map_err(|err| crate::Error::backend_failure(op, err))
174}
175
176pub(crate) fn materialize_tensor_read(
177 op: &'static str,
178 input: TensorRead<'_>,
179) -> crate::Result<Tensor> {
180 match input {
181 TensorRead::Tensor(tensor) => clone_host_tensor_read(op, tensor),
182 TensorRead::View(view) => materialize_tensor_view(op, view),
183 }
184}
185
186fn clone_host_tensor_read(op: &'static str, tensor: &Tensor) -> crate::Result<Tensor> {
187 macro_rules! clone_host {
188 ($variant:ident, $tensor:expr) => {{
189 typed_host_data(op, $tensor)?;
190 Ok(Tensor::$variant($tensor.clone()))
191 }};
192 }
193
194 match tensor {
195 Tensor::F32(tensor) => clone_host!(F32, tensor),
196 Tensor::F64(tensor) => clone_host!(F64, tensor),
197 Tensor::I32(tensor) => clone_host!(I32, tensor),
198 Tensor::I64(tensor) => clone_host!(I64, tensor),
199 Tensor::Bool(tensor) => clone_host!(Bool, tensor),
200 Tensor::C32(tensor) => clone_host!(C32, tensor),
201 Tensor::C64(tensor) => clone_host!(C64, tensor),
202 }
203}
204
205fn materialize_tensor_view(op: &'static str, view: TensorView<'_>) -> crate::Result<Tensor> {
206 macro_rules! materialize {
207 ($variant:ident, $view:expr) => {{
208 if $view.backend_buffer().is_some() {
209 return Err(cpu_backend_buffer_error(op));
210 }
211 Ok(Tensor::$variant($view.to_contiguous()?))
212 }};
213 }
214
215 match view {
216 TensorView::F32(view) => materialize!(F32, view),
217 TensorView::F64(view) => materialize!(F64, view),
218 TensorView::I32(view) => materialize!(I32, view),
219 TensorView::I64(view) => materialize!(I64, view),
220 TensorView::Bool(view) => materialize!(Bool, view),
221 TensorView::C32(view) => materialize!(C32, view),
222 TensorView::C64(view) => materialize!(C64, view),
223 }
224}
225
226#[allow(clippy::uninit_vec)]
232#[cfg(test)]
233pub(crate) unsafe fn typed_array_uninit<T>(shape: &[usize]) -> StridedArray<T> {
234 let total: usize = shape.iter().product();
235 let strides = kernel_col_major_strides(shape);
236 let mut data = Vec::with_capacity(total);
237 unsafe { data.set_len(total) };
239 StridedArray::from_parts(data, shape, &strides, 0).expect("column-major output array")
242}
243
244pub(crate) unsafe fn typed_array_uninit_from_pool<T>(
250 buffers: &mut BufferPool,
251 shape: &[usize],
252) -> crate::Result<StridedArray<T>>
253where
254 T: PoolScalar,
255{
256 let total = tenferro_tensor::validate::checked_shape_product(
257 "typed_array_uninit_from_pool",
258 "shape",
259 shape,
260 )?;
261 let strides = kernel_col_major_strides(shape);
262 let data = unsafe { T::pool_acquire(buffers, total) };
264 StridedArray::from_parts(data, shape, &strides, 0)
267 .map_err(|err| crate::Error::backend_failure("typed_array_uninit_from_pool", err))
268}
269
270pub(crate) fn tensor_from_array<T: Clone>(array: StridedArray<T>) -> TypedTensor<T> {
271 TypedTensor::from_vec_col_major(array.dims().to_vec(), array.into_data())
273 .expect("strided array dimensions match owned data length")
274}
275
276pub(crate) fn default_placement() -> Placement {
277 Placement {
278 memory_kind: MemoryKind::UnpinnedHost,
279 device: None,
280 }
281}
282
283pub(crate) fn flat_to_multi(mut flat: usize, shape: &[usize], out: &mut [usize]) {
284 assert_eq!(shape.len(), out.len());
285 for (axis, &dim) in shape.iter().enumerate() {
286 if dim == 0 {
287 out[axis] = 0;
288 } else {
289 out[axis] = flat % dim;
290 flat /= dim;
291 }
292 }
293}
294
295#[cfg(test)]
296mod tests;