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}