rust_igraph/algorithms/operators/
permute_vertices.rs1use crate::core::error::IgraphError;
8use crate::core::{Graph, IgraphResult, VertexId};
9
10pub fn invert_permutation(permutation: &[VertexId]) -> IgraphResult<Vec<VertexId>> {
39 let n = permutation.len();
40 let n_u32 = u32::try_from(n).map_err(|_| {
41 IgraphError::InvalidArgument("invert_permutation: length overflows u32".to_string())
42 })?;
43
44 let mut inverse = vec![u32::MAX; n];
45
46 for (i, &j) in permutation.iter().enumerate() {
47 if j >= n_u32 {
48 return Err(IgraphError::InvalidArgument(format!(
49 "invert_permutation: invalid index {j} in permutation (must be < {n_u32})"
50 )));
51 }
52 if inverse[j as usize] != u32::MAX {
53 return Err(IgraphError::InvalidArgument(format!(
54 "invert_permutation: duplicate entry {j} in permutation"
55 )));
56 }
57 #[allow(clippy::cast_possible_truncation)]
58 {
59 inverse[j as usize] = i as u32;
60 }
61 }
62
63 Ok(inverse)
64}
65
66pub fn permute_vertices(graph: &Graph, permutation: &[VertexId]) -> IgraphResult<Graph> {
102 let n = graph.vcount();
103
104 if permutation.len() != n as usize {
105 return Err(IgraphError::InvalidArgument(format!(
106 "permutation length {} does not match vertex count {n}",
107 permutation.len()
108 )));
109 }
110
111 let mut inverse = vec![u32::MAX; n as usize];
113 for (new_id, &old_id) in permutation.iter().enumerate() {
114 if old_id >= n {
115 return Err(IgraphError::InvalidArgument(format!(
116 "permutation contains invalid vertex id {old_id} (graph has {n} vertices)"
117 )));
118 }
119 if inverse[old_id as usize] != u32::MAX {
120 return Err(IgraphError::InvalidArgument(format!(
121 "duplicate entry {old_id} in permutation"
122 )));
123 }
124 #[allow(clippy::cast_possible_truncation)]
125 let new_id_u32 = new_id as u32;
126 inverse[old_id as usize] = new_id_u32;
127 }
128
129 let ecount = graph.ecount();
131 let directed = graph.is_directed();
132 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(ecount);
133
134 for eid in 0..ecount {
135 #[allow(clippy::cast_possible_truncation)]
136 let eid_u32 = eid as u32;
137 let (src, tgt) = graph.edge(eid_u32)?;
138 let new_src = inverse[src as usize];
139 let new_tgt = inverse[tgt as usize];
140 edges.push((new_src, new_tgt));
141 }
142
143 let mut result = Graph::new(n, directed)?;
144 result.add_edges(edges)?;
145 Ok(result)
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_identity_permutation() {
154 let mut g = Graph::with_vertices(4);
155 g.add_edge(0, 1).unwrap();
156 g.add_edge(1, 2).unwrap();
157 g.add_edge(2, 3).unwrap();
158
159 let pg = permute_vertices(&g, &[0, 1, 2, 3]).unwrap();
160 assert_eq!(pg.vcount(), 4);
161 assert_eq!(pg.ecount(), 3);
162 for eid in 0..3u32 {
163 assert_eq!(g.edge(eid).unwrap(), pg.edge(eid).unwrap());
164 }
165 }
166
167 #[test]
168 fn test_reverse_permutation() {
169 let mut g = Graph::with_vertices(3);
170 g.add_edge(0, 1).unwrap();
171 g.add_edge(1, 2).unwrap();
172
173 let pg = permute_vertices(&g, &[2, 1, 0]).unwrap();
175 assert_eq!(pg.vcount(), 3);
176 assert_eq!(pg.ecount(), 2);
177 assert_eq!(pg.edge(0).unwrap(), (1, 2));
179 assert_eq!(pg.edge(1).unwrap(), (0, 1));
181 }
182
183 #[test]
184 fn test_directed() {
185 let mut g = Graph::new(3, true).unwrap();
186 g.add_edge(0, 1).unwrap();
187 g.add_edge(1, 2).unwrap();
188
189 let pg = permute_vertices(&g, &[2, 0, 1]).unwrap();
190 assert!(pg.is_directed());
191 assert_eq!(pg.edge(0).unwrap(), (1, 2));
194 assert_eq!(pg.edge(1).unwrap(), (2, 0));
196 }
197
198 #[test]
199 fn test_empty_graph() {
200 let g = Graph::with_vertices(0);
201 let pg = permute_vertices(&g, &[]).unwrap();
202 assert_eq!(pg.vcount(), 0);
203 }
204
205 #[test]
206 fn test_wrong_length() {
207 let g = Graph::with_vertices(3);
208 let result = permute_vertices(&g, &[0, 1]);
209 assert!(result.is_err());
210 }
211
212 #[test]
213 fn test_out_of_range() {
214 let g = Graph::with_vertices(3);
215 let result = permute_vertices(&g, &[0, 1, 5]);
216 assert!(result.is_err());
217 }
218
219 #[test]
220 fn test_duplicate() {
221 let g = Graph::with_vertices(3);
222 let result = permute_vertices(&g, &[0, 1, 1]);
223 assert!(result.is_err());
224 }
225
226 #[test]
227 fn test_self_loop() {
228 let mut g = Graph::with_vertices(3);
229 g.add_edge(1, 1).unwrap();
230
231 let pg = permute_vertices(&g, &[2, 0, 1]).unwrap();
232 assert_eq!(pg.edge(0).unwrap(), (2, 2));
235 }
236
237 #[test]
238 fn test_cycle_permutation() {
239 let mut g = Graph::with_vertices(4);
240 g.add_edge(0, 1).unwrap();
241 g.add_edge(1, 2).unwrap();
242 g.add_edge(2, 3).unwrap();
243 g.add_edge(3, 0).unwrap();
244
245 let pg = permute_vertices(&g, &[1, 2, 3, 0]).unwrap();
247 assert_eq!(pg.vcount(), 4);
248 assert_eq!(pg.ecount(), 4);
249 assert_eq!(pg.edge(0).unwrap(), (0, 3));
252 assert_eq!(pg.edge(1).unwrap(), (0, 1));
254 }
255
256 #[test]
259 fn invert_identity() {
260 let inv = invert_permutation(&[0, 1, 2, 3]).unwrap();
261 assert_eq!(inv, vec![0, 1, 2, 3]);
262 }
263
264 #[test]
265 fn invert_reverse() {
266 let inv = invert_permutation(&[2, 1, 0]).unwrap();
267 assert_eq!(inv, vec![2, 1, 0]);
268 }
269
270 #[test]
271 fn invert_cycle() {
272 let inv = invert_permutation(&[1, 2, 0]).unwrap();
273 assert_eq!(inv, vec![2, 0, 1]);
274 }
275
276 #[test]
277 fn invert_empty() {
278 let inv = invert_permutation(&[]).unwrap();
279 assert!(inv.is_empty());
280 }
281
282 #[test]
283 fn invert_single() {
284 let inv = invert_permutation(&[0]).unwrap();
285 assert_eq!(inv, vec![0]);
286 }
287
288 #[test]
289 fn invert_roundtrip() {
290 let perm = [3, 0, 2, 1];
291 let inv = invert_permutation(&perm).unwrap();
292 let inv2 = invert_permutation(&inv).unwrap();
293 assert_eq!(inv2, perm.to_vec());
294 }
295
296 #[test]
297 fn invert_out_of_range() {
298 let err = invert_permutation(&[0, 5, 1]).unwrap_err();
299 assert!(matches!(err, IgraphError::InvalidArgument(_)));
300 }
301
302 #[test]
303 fn invert_duplicate() {
304 let err = invert_permutation(&[0, 1, 1]).unwrap_err();
305 assert!(matches!(err, IgraphError::InvalidArgument(_)));
306 }
307}