Skip to main content

tensor4all_core/
tensor_index.rs

1//! TensorIndex trait for index operations on tensor-like objects.
2//!
3//! This trait provides a minimal interface for objects that have external indices
4//! and support index replacement operations. It is a subset of `TensorLike` that
5//! can be implemented by both dense tensors and tensor networks (like TreeTN).
6
7use crate::IndexLike;
8use std::fmt::Debug;
9
10/// Trait for objects that have external indices and support index operations.
11///
12/// This is a minimal trait that can be implemented by:
13/// - Dense tensors (`IdxTensor`)
14/// - Tensor networks (`TreeTN`)
15/// - Any other structure that organizes tensors with indices
16///
17/// # Design
18///
19/// This trait is separate from `TensorLike` to allow tensor networks to implement
20/// index operations without needing to implement contraction/factorization operations.
21pub trait TensorIndex: Sized + Clone + Debug + Send + Sync {
22    /// The index type used by this object.
23    type Index: IndexLike;
24
25    /// Error type for fallible index operations.
26    ///
27    /// Implementations map their internal diagnostics into a typed,
28    /// source-preserving error; callers propagating into `anyhow::Result`
29    /// keep working through `From`.
30    type Error: std::error::Error + Send + Sync + 'static + From<anyhow::Error>;
31
32    /// Return flattened external indices for this object.
33    ///
34    /// # Ordering
35    ///
36    /// The ordering MUST be stable (deterministic). Implementations should:
37    /// - Sort indices by their full index identity, or
38    /// - Use insertion-ordered storage
39    ///
40    /// This ensures consistent behavior for hashing, serialization, and comparison.
41    fn external_indices(&self) -> Vec<Self::Index>;
42
43    /// Number of external indices.
44    ///
45    /// Default implementation calls `external_indices().len()`, but implementations
46    /// SHOULD override this for efficiency when the count can be computed without
47    /// allocating the full index list.
48    fn num_external_indices(&self) -> usize {
49        self.external_indices().len()
50    }
51
52    /// Replace an index in this object.
53    ///
54    /// This replaces the index equal to `old_index` (including dimension,
55    /// prime level, tags, and other identity metadata) with `new_index`.
56    /// The storage data is not modified, only the index metadata is changed.
57    ///
58    /// # Arguments
59    ///
60    /// * `old_index` - The full index identity to replace
61    /// * `new_index` - The new index to use
62    ///
63    /// # Returns
64    ///
65    /// A new object with the index replaced.
66    ///
67    /// # Errors
68    ///
69    /// Returns `Self::Error` when `old_index` is not present (a
70    /// missing-index failure) or the replacement violates an index identity
71    /// invariant (an invalid replacement).
72    fn replaceind(
73        &self,
74        old_index: &Self::Index,
75        new_index: &Self::Index,
76    ) -> std::result::Result<Self, Self::Error>;
77
78    /// Replace multiple indices in this object.
79    ///
80    /// This replaces each full index identity in `old_indices` with the
81    /// corresponding index in `new_indices`. The storage data is not modified.
82    ///
83    /// # Arguments
84    ///
85    /// * `old_indices` - The full index identities to replace
86    /// * `new_indices` - The new indices to use
87    ///
88    /// # Returns
89    ///
90    /// A new object with the indices replaced.
91    ///
92    /// # Errors
93    ///
94    /// Returns `Self::Error` when an `old_indices` entry is not present
95    /// (a missing-index failure), when `old_indices` and `new_indices` differ
96    /// in length (a length mismatch), or when the replacement violates an
97    /// index identity invariant (an invalid replacement).
98    fn replace_indices(
99        &self,
100        old_indices: &[Self::Index],
101        new_indices: &[Self::Index],
102    ) -> std::result::Result<Self, Self::Error>;
103
104    /// Replace indices using pairs of (old, new).
105    ///
106    /// This is a convenience method that wraps `replace_indices`.
107    ///
108    /// # Arguments
109    ///
110    /// * `pairs` - Pairs of (old_index, new_index) to replace
111    ///
112    /// # Returns
113    ///
114    /// A new object with the indices replaced.
115    ///
116    /// # Errors
117    ///
118    /// Returns `Self::Error` when an `old` entry is not present (a
119    /// missing-index failure) or when the replacement violates an index
120    /// identity invariant (an invalid replacement); propagates failures from
121    /// [`Self::replace_indices`].
122    fn replace_indices_pairs(
123        &self,
124        pairs: &[(Self::Index, Self::Index)],
125    ) -> std::result::Result<Self, Self::Error> {
126        let (old, new): (Vec<_>, Vec<_>) = pairs.iter().cloned().unzip();
127        self.replace_indices(&old, &new)
128    }
129}