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 anyhow::Result;
9use std::fmt::Debug;
10
11/// Trait for objects that have external indices and support index operations.
12///
13/// This is a minimal trait that can be implemented by:
14/// - Dense tensors (`TensorDynLen`)
15/// - Tensor networks (`TreeTN`)
16/// - Any other structure that organizes tensors with indices
17///
18/// # Design
19///
20/// This trait is separate from `TensorLike` to allow tensor networks to implement
21/// index operations without needing to implement contraction/factorization operations.
22pub trait TensorIndex: Sized + Clone + Debug + Send + Sync {
23 /// The index type used by this object.
24 type Index: IndexLike;
25
26 /// Return flattened external indices for this object.
27 ///
28 /// # Ordering
29 ///
30 /// The ordering MUST be stable (deterministic). Implementations should:
31 /// - Sort indices by their `id` field, or
32 /// - Use insertion-ordered storage
33 ///
34 /// This ensures consistent behavior for hashing, serialization, and comparison.
35 fn external_indices(&self) -> Vec<Self::Index>;
36
37 /// Number of external indices.
38 ///
39 /// Default implementation calls `external_indices().len()`, but implementations
40 /// SHOULD override this for efficiency when the count can be computed without
41 /// allocating the full index list.
42 fn num_external_indices(&self) -> usize {
43 self.external_indices().len()
44 }
45
46 /// Replace an index in this object.
47 ///
48 /// This replaces the index matching `old_index` by ID with `new_index`.
49 /// The storage data is not modified, only the index metadata is changed.
50 ///
51 /// # Arguments
52 ///
53 /// * `old_index` - The index to replace (matched by ID)
54 /// * `new_index` - The new index to use
55 ///
56 /// # Returns
57 ///
58 /// A new object with the index replaced.
59 fn replaceind(&self, old_index: &Self::Index, new_index: &Self::Index) -> Result<Self>;
60
61 /// Replace multiple indices in this object.
62 ///
63 /// This replaces each index in `old_indices` (matched by ID) with the
64 /// corresponding index in `new_indices`. The storage data is not modified.
65 ///
66 /// # Arguments
67 ///
68 /// * `old_indices` - The indices to replace (matched by ID)
69 /// * `new_indices` - The new indices to use
70 ///
71 /// # Returns
72 ///
73 /// A new object with the indices replaced.
74 fn replaceinds(&self, old_indices: &[Self::Index], new_indices: &[Self::Index])
75 -> Result<Self>;
76
77 /// Replace indices using pairs of (old, new).
78 ///
79 /// This is a convenience method that wraps `replaceinds`.
80 ///
81 /// # Arguments
82 ///
83 /// * `pairs` - Pairs of (old_index, new_index) to replace
84 ///
85 /// # Returns
86 ///
87 /// A new object with the indices replaced.
88 fn replaceinds_pairs(&self, pairs: &[(Self::Index, Self::Index)]) -> Result<Self> {
89 let (old, new): (Vec<_>, Vec<_>) = pairs.iter().cloned().unzip();
90 self.replaceinds(&old, &new)
91 }
92}