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;