Skip to main content

ariadnetor_tensor/
lib.rs

1//! Tensor storage library
2//!
3//! Provides backend-agnostic data structures for tensor storage and
4//! the user-facing [`Tensor<St, L>`] type that joins them. The tensor
5//! carries no backend; operations take it explicitly at the call site.
6
7#![deny(missing_docs)]
8
9mod block_sparse;
10mod capability;
11mod dense;
12mod error;
13mod layout;
14mod ops;
15mod reorder;
16mod sector;
17mod storage;
18mod tensor;
19mod tensor_data;
20
21// Shared BlockSparse test fixtures: `QNIndex` leg builders and densify /
22// template helpers. Reachable by this crate's own unit tests under
23// `cfg(test)`, and by other crates' tests via the `test-fixtures`
24// dev-dependency feature. See the module docs for why a separate crate cannot
25// serve the in-lib unit tests.
26#[cfg(any(test, feature = "test-fixtures"))]
27pub mod test_fixtures;
28
29// Re-export from ariadnetor-core
30pub use ariadnetor_core::{
31    Complex, ComputeBackend, ContractionError, ContractionPlan, EinsumExpr, MemoryOrder, Scalar,
32    compute_permutation,
33};
34
35pub use block_sparse::{
36    BlockCoord, BlockMeta, BlockSparseLayout, BlockSparseStorage, BlockSparseTensorData, Direction,
37    QNIndex,
38};
39pub use capability::{Host, OpsFor};
40pub use dense::{DenseLayout, DenseStorage, DenseTensorData};
41pub use error::TensorError;
42pub use layout::{StorageFor, TensorLayout};
43pub use ops::{add_all, linear_combine};
44pub use reorder::{flat_index, normalize_to_data, reorder_data};
45pub use sector::{Sector, U1Sector, Z2Sector};
46pub use storage::Storage;
47pub use tensor::{BlockSparseTensor, DenseTensor, Tensor};
48pub use tensor_data::TensorData;
49
50// Re-export the native backend so users importing ariadnetor_tensor for
51// `DenseTensor` / `BlockSparseTensor` constructors get its default
52// backend without a separate crate import.
53pub use ariadnetor_native::NativeBackend;
54
55/// Extension trait for backend-aware tensor construction.
56///
57/// Provides tensor constructors on any `ComputeBackend`. The constructed
58/// tensors have `order()` matching the backend's `preferred_order()`, so
59/// downstream linalg operations driven by that backend find the storage
60/// already in the layout they expect. Most constructors return the
61/// Mid-layer `DenseTensorData<T>` for kernel-output paths; `dense`
62/// returns the wrapped `DenseTensor<T>` for the input-fabrication case.
63pub trait ComputeBackendTensorExt: ComputeBackend {
64    /// Construct a `DenseTensorData` from data in this backend's
65    /// preferred memory order.
66    ///
67    /// The caller must arrange `data` in this backend's
68    /// `preferred_order()`. The resulting tensor data has
69    /// `order() == self.preferred_order()`.
70    fn make_tensor<T: Clone>(&self, data: Vec<T>, shape: Vec<usize>) -> DenseTensorData<T> {
71        DenseTensorData::from_raw_parts(data, shape, self.preferred_order())
72    }
73
74    /// Construct a `DenseTensor` from data in this backend's preferred
75    /// memory order.
76    ///
77    /// The caller must arrange `data` in this backend's
78    /// `preferred_order()`. The resulting tensor has
79    /// `order() == self.preferred_order()`.
80    ///
81    /// One-call entry for fabricating an input tensor from flat parts:
82    /// fuses [`make_tensor`](Self::make_tensor) — which yields the
83    /// Mid-layer `DenseTensorData`, kept for kernel-output paths — with
84    /// the `DenseTensor` wrap, so a caller that just wants a tensor need
85    /// not reach across the Data layer. The backend stays explicit at
86    /// the call site, so this is not a host-hardcoded constructor.
87    fn dense<T: Clone>(&self, data: Vec<T>, shape: Vec<usize>) -> DenseTensor<T> {
88        DenseTensor::from_data(self.make_tensor(data, shape))
89    }
90
91    /// Create a zero-filled tensor whose `order()` matches this backend.
92    fn zeros<T: Clone + num_traits::Zero>(&self, shape: Vec<usize>) -> DenseTensorData<T> {
93        DenseTensorData::zeros_in_order(shape, self.preferred_order())
94    }
95
96    /// Create a ones-filled tensor whose `order()` matches this backend.
97    fn ones<T: Clone + num_traits::Zero + num_traits::One>(
98        &self,
99        shape: Vec<usize>,
100    ) -> DenseTensorData<T> {
101        DenseTensorData::ones_in_order(shape, self.preferred_order())
102    }
103
104    /// Create a tensor filled with `value` whose `order()` matches this backend.
105    fn filled<T: Clone>(&self, shape: Vec<usize>, value: T) -> DenseTensorData<T> {
106        DenseTensorData::filled_in_order(shape, value, self.preferred_order())
107    }
108
109    /// Create an identity matrix whose `order()` matches this backend.
110    ///
111    /// The identity matrix is symmetric, so its flat data layout is the same
112    /// regardless of memory order; only the `order()` field differs.
113    fn eye<T: Clone + num_traits::Zero + num_traits::One>(&self, n: usize) -> DenseTensorData<T> {
114        DenseTensorData::eye_in_order(n, self.preferred_order())
115    }
116}
117
118impl<B: ComputeBackend> ComputeBackendTensorExt for B {}