rust_igraph/algorithms/properties/
unfold_tree.rs1use std::collections::VecDeque;
10
11use crate::core::graph::EdgeId;
12use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
13
14#[derive(Debug, Clone)]
16pub struct UnfoldTreeResult {
17 pub tree: Graph,
19 pub vertex_index: Vec<VertexId>,
22}
23
24pub fn unfold_tree(
57 graph: &Graph,
58 roots: &[VertexId],
59 mode: crate::algorithms::properties::degree::DegreeMode,
60) -> IgraphResult<UnfoldTreeResult> {
61 let n = graph.vcount();
62 let ecount = graph.ecount();
63
64 for &r in roots {
65 if r >= n {
66 return Err(IgraphError::InvalidArgument(format!(
67 "unfold_tree: root {r} out of range (vcount = {n})"
68 )));
69 }
70 }
71
72 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(ecount);
73 let mut vertex_index: Vec<VertexId> = (0..n).collect();
74 let mut seen_vertices = vec![false; n as usize];
75 let mut seen_edges = vec![false; ecount];
76 let mut queue: VecDeque<VertexId> = VecDeque::new();
77 let mut tree_vcount = n;
78
79 for &root in roots {
80 if seen_vertices[root as usize] {
81 continue;
82 }
83 seen_vertices[root as usize] = true;
84 queue.push_back(root);
85
86 while let Some(actnode) = queue.pop_front() {
87 let incident_edges = get_incident(graph, actnode, mode)?;
88
89 for eid in incident_edges {
90 if seen_edges[eid as usize] {
91 continue;
92 }
93 seen_edges[eid as usize] = true;
94
95 let (from, to) = graph.edge(eid)?;
96 let nei = if from == actnode { to } else { from };
97
98 if seen_vertices[nei as usize] {
99 let new_vid = tree_vcount;
100 tree_vcount += 1;
101 vertex_index.push(nei);
102
103 if from == nei {
104 edges.push((new_vid, to));
105 } else {
106 edges.push((from, new_vid));
107 }
108 } else {
109 edges.push((from, to));
110 seen_vertices[nei as usize] = true;
111 queue.push_back(nei);
112 }
113 }
114 }
115 }
116
117 let mut tree = Graph::new(tree_vcount, graph.is_directed())?;
118 tree.add_edges(edges)?;
119
120 Ok(UnfoldTreeResult { tree, vertex_index })
121}
122
123fn get_incident(
124 graph: &Graph,
125 v: VertexId,
126 mode: crate::algorithms::properties::degree::DegreeMode,
127) -> IgraphResult<Vec<EdgeId>> {
128 use crate::algorithms::properties::degree::DegreeMode;
129
130 match mode {
131 DegreeMode::Out => graph.incident(v),
132 DegreeMode::In => graph.incident_in(v),
133 DegreeMode::All => {
134 let mut out = graph.incident(v)?;
135 if graph.is_directed() {
136 let in_edges = graph.incident_in(v)?;
137 for eid in in_edges {
138 if !out.contains(&eid) {
139 out.push(eid);
140 }
141 }
142 }
143 Ok(out)
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::algorithms::properties::degree::DegreeMode;
152
153 #[test]
154 fn empty_graph() {
155 let g = Graph::with_vertices(0);
156 let result = unfold_tree(&g, &[], DegreeMode::All).unwrap();
157 assert_eq!(result.tree.vcount(), 0);
158 assert_eq!(result.tree.ecount(), 0);
159 }
160
161 #[test]
162 fn single_vertex() {
163 let g = Graph::with_vertices(1);
164 let result = unfold_tree(&g, &[0], DegreeMode::All).unwrap();
165 assert_eq!(result.tree.vcount(), 1);
166 assert_eq!(result.tree.ecount(), 0);
167 }
168
169 #[test]
170 fn tree_stays_tree() {
171 let mut g = Graph::with_vertices(4);
172 g.add_edge(0, 1).unwrap();
173 g.add_edge(0, 2).unwrap();
174 g.add_edge(0, 3).unwrap();
175 let result = unfold_tree(&g, &[0], DegreeMode::All).unwrap();
176 assert_eq!(result.tree.vcount(), 4);
177 assert_eq!(result.tree.ecount(), 3);
178 assert_eq!(result.vertex_index, vec![0, 1, 2, 3]);
179 }
180
181 #[test]
182 fn triangle_unfolds() {
183 let mut g = Graph::with_vertices(3);
184 g.add_edge(0, 1).unwrap();
185 g.add_edge(1, 2).unwrap();
186 g.add_edge(2, 0).unwrap();
187 let result = unfold_tree(&g, &[0], DegreeMode::All).unwrap();
188 assert_eq!(result.tree.vcount(), 4);
190 assert_eq!(result.tree.ecount(), 3);
191 assert_eq!(result.vertex_index[0], 0);
193 assert_eq!(result.vertex_index[1], 1);
194 assert_eq!(result.vertex_index[2], 2);
195 }
196
197 #[test]
198 fn k4_unfolds() {
199 let mut g = Graph::with_vertices(4);
200 g.add_edge(0, 1).unwrap();
201 g.add_edge(0, 2).unwrap();
202 g.add_edge(0, 3).unwrap();
203 g.add_edge(1, 2).unwrap();
204 g.add_edge(1, 3).unwrap();
205 g.add_edge(2, 3).unwrap();
206 let result = unfold_tree(&g, &[0], DegreeMode::All).unwrap();
207 assert_eq!(result.tree.ecount(), 6);
209 assert_eq!(result.tree.vcount(), 7);
210 }
211
212 #[test]
213 fn two_components() {
214 let mut g = Graph::with_vertices(4);
215 g.add_edge(0, 1).unwrap();
216 g.add_edge(2, 3).unwrap();
217 let result = unfold_tree(&g, &[0, 2], DegreeMode::All).unwrap();
218 assert_eq!(result.tree.vcount(), 4);
220 assert_eq!(result.tree.ecount(), 2);
221 }
222
223 #[test]
224 fn directed_out_mode() {
225 let mut g = Graph::new(3, true).unwrap();
226 g.add_edge(0, 1).unwrap();
227 g.add_edge(0, 2).unwrap();
228 g.add_edge(1, 2).unwrap();
229 let result = unfold_tree(&g, &[0], DegreeMode::Out).unwrap();
230 assert_eq!(result.tree.vcount(), 4);
232 assert_eq!(result.tree.ecount(), 3);
233 }
234
235 #[test]
236 fn root_out_of_range() {
237 let g = Graph::with_vertices(3);
238 assert!(unfold_tree(&g, &[5], DegreeMode::All).is_err());
239 }
240
241 #[test]
242 fn vertex_index_correctness() {
243 let mut g = Graph::with_vertices(3);
244 g.add_edge(0, 1).unwrap();
245 g.add_edge(1, 2).unwrap();
246 g.add_edge(2, 0).unwrap();
247 let result = unfold_tree(&g, &[0], DegreeMode::All).unwrap();
248 for &orig in &result.vertex_index {
250 assert!(orig < 3);
251 }
252 }
253
254 #[test]
255 fn cycle_5() {
256 let mut g = Graph::with_vertices(5);
257 g.add_edge(0, 1).unwrap();
258 g.add_edge(1, 2).unwrap();
259 g.add_edge(2, 3).unwrap();
260 g.add_edge(3, 4).unwrap();
261 g.add_edge(4, 0).unwrap();
262 let result = unfold_tree(&g, &[0], DegreeMode::All).unwrap();
263 assert_eq!(result.tree.vcount(), 6);
265 assert_eq!(result.tree.ecount(), 5);
266 }
267}