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}