Skip to main content

tensor4all_treetn/
link_index_network.rs

1//! Link Index Network for bond/link index management.
2//!
3//! Provides efficient reverse lookup from index ID to edge.
4//! This complements `SiteIndexNetwork` (which handles site/physical indices)
5//! by handling link/bond indices between nodes.
6
7use petgraph::stable_graph::EdgeIndex;
8use std::collections::HashMap;
9use std::fmt::Debug;
10use tensor4all_core::IndexLike;
11
12/// Link Index Network: manages bond/link indices with reverse lookup.
13///
14/// Provides O(1) lookup from index ID to the edge containing that index.
15/// This is essential for efficient `replaceind` operations on TreeTN.
16///
17/// # Type Parameters
18/// - `I`: Index type (must implement `IndexLike`)
19#[derive(Debug, Clone)]
20pub struct LinkIndexNetwork<I>
21where
22    I: IndexLike,
23{
24    /// Reverse lookup: index ID → edge containing this index
25    index_to_edge: HashMap<I::Id, EdgeIndex>,
26}
27
28impl<I> Default for LinkIndexNetwork<I>
29where
30    I: IndexLike,
31{
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37impl<I> LinkIndexNetwork<I>
38where
39    I: IndexLike,
40{
41    /// Create a new empty LinkIndexNetwork.
42    pub fn new() -> Self {
43        Self {
44            index_to_edge: HashMap::new(),
45        }
46    }
47
48    /// Create with initial capacity.
49    pub fn with_capacity(edges: usize) -> Self {
50        Self {
51            index_to_edge: HashMap::with_capacity(edges),
52        }
53    }
54
55    /// Register a link index for an edge.
56    ///
57    /// # Arguments
58    /// * `edge` - The edge index
59    /// * `index` - The link index on this edge
60    pub fn insert(&mut self, edge: EdgeIndex, index: &I) {
61        self.index_to_edge.insert(index.id().clone(), edge);
62    }
63
64    /// Remove a link index registration.
65    ///
66    /// # Arguments
67    /// * `index` - The link index to remove
68    ///
69    /// # Returns
70    /// The edge that was associated with this index, if any.
71    pub fn remove(&mut self, index: &I) -> Option<EdgeIndex> {
72        self.index_to_edge.remove(index.id())
73    }
74
75    /// Find the edge containing a given index.
76    ///
77    /// # Arguments
78    /// * `index` - The index to look up
79    ///
80    /// # Returns
81    /// The edge containing this index, or None if not found.
82    pub fn find_edge(&self, index: &I) -> Option<EdgeIndex> {
83        self.index_to_edge.get(index.id()).copied()
84    }
85
86    /// Find the edge containing an index by ID.
87    pub fn find_edge_by_id(&self, id: &I::Id) -> Option<EdgeIndex> {
88        self.index_to_edge.get(id).copied()
89    }
90
91    /// Check if an index is registered.
92    pub fn contains(&self, index: &I) -> bool {
93        self.index_to_edge.contains_key(index.id())
94    }
95
96    /// Check if an index ID is registered.
97    pub fn contains_id(&self, id: &I::Id) -> bool {
98        self.index_to_edge.contains_key(id)
99    }
100
101    /// Update the index for an edge (e.g., after SVD creates new bond index).
102    ///
103    /// # Arguments
104    /// * `old_index` - The old index to replace
105    /// * `new_index` - The new index
106    /// * `edge` - The edge (for validation)
107    ///
108    /// # Returns
109    /// Ok if successful, Err if old_index was not registered or edge mismatch.
110    pub fn replace_index(
111        &mut self,
112        old_index: &I,
113        new_index: &I,
114        edge: EdgeIndex,
115    ) -> Result<(), String> {
116        match self.index_to_edge.remove(old_index.id()) {
117            Some(old_edge) => {
118                if old_edge != edge {
119                    // Restore and return error
120                    self.index_to_edge.insert(old_index.id().clone(), old_edge);
121                    return Err(format!(
122                        "Edge mismatch: old_index was on edge {:?}, not {:?}",
123                        old_edge, edge
124                    ));
125                }
126                self.index_to_edge.insert(new_index.id().clone(), edge);
127                Ok(())
128            }
129            None => Err(format!(
130                "Index {:?} not found in link_index_network",
131                old_index.id()
132            )),
133        }
134    }
135
136    /// Number of registered link indices.
137    pub fn len(&self) -> usize {
138        self.index_to_edge.len()
139    }
140
141    /// Check if empty.
142    pub fn is_empty(&self) -> bool {
143        self.index_to_edge.is_empty()
144    }
145
146    /// Clear all registrations.
147    pub fn clear(&mut self) {
148        self.index_to_edge.clear();
149    }
150
151    /// Iterate over all (index_id, edge) pairs.
152    pub fn iter(&self) -> impl Iterator<Item = (&I::Id, &EdgeIndex)> {
153        self.index_to_edge.iter()
154    }
155}
156
157#[cfg(test)]
158mod tests;