rust_igraph/algorithms/properties/
is_simple.rs1use crate::core::graph::EdgeId;
16use crate::core::{Graph, IgraphError, IgraphResult};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum SimpleMode {
24 DirectedAsDirected,
27 DirectedAsUndirected,
30}
31
32pub fn is_simple(graph: &Graph) -> IgraphResult<bool> {
51 is_simple_with_mode(graph, SimpleMode::DirectedAsDirected)
52}
53
54pub fn is_simple_with_mode(graph: &Graph, mode: SimpleMode) -> IgraphResult<bool> {
75 let n = graph.vcount();
76 let m = graph.ecount();
77 if n == 0 || m == 0 {
78 return Ok(true);
79 }
80
81 if !graph.is_directed() || mode == SimpleMode::DirectedAsDirected {
89 for v in 0..n {
90 let neis = graph.neighbors(v)?;
91 let mut prev: Option<u32> = None;
92 for &u in &neis {
93 if u == v {
94 return Ok(false);
95 }
96 if Some(u) == prev {
97 return Ok(false);
98 }
99 prev = Some(u);
100 }
101 }
102 return Ok(true);
103 }
104
105 let m_u32 = u32::try_from(m).map_err(|_| IgraphError::Internal("ecount exceeds u32::MAX"))?;
109 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(m);
110 for eid in 0..m_u32 {
111 let (src, tgt) = graph.edge(eid as EdgeId)?;
112 if src == tgt {
113 return Ok(false);
114 }
115 pairs.push(if src < tgt { (src, tgt) } else { (tgt, src) });
116 }
117 pairs.sort_unstable();
118 for w in pairs.windows(2) {
119 if w[0] == w[1] {
120 return Ok(false);
121 }
122 }
123 Ok(true)
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn empty_graph_is_simple() {
132 let g = Graph::with_vertices(0);
133 assert!(is_simple(&g).unwrap());
134 }
135
136 #[test]
137 fn no_edge_graph_is_simple() {
138 let g = Graph::with_vertices(5);
139 assert!(is_simple(&g).unwrap());
140 }
141
142 #[test]
143 fn path_is_simple() {
144 let mut g = Graph::with_vertices(4);
145 g.add_edge(0, 1).unwrap();
146 g.add_edge(1, 2).unwrap();
147 g.add_edge(2, 3).unwrap();
148 assert!(is_simple(&g).unwrap());
149 }
150
151 #[test]
152 fn self_loop_breaks_simplicity() {
153 let mut g = Graph::with_vertices(2);
154 g.add_edge(0, 0).unwrap();
155 g.add_edge(0, 1).unwrap();
156 assert!(!is_simple(&g).unwrap());
157 }
158
159 #[test]
160 fn parallel_edge_breaks_simplicity_undirected() {
161 let mut g = Graph::with_vertices(2);
162 g.add_edge(0, 1).unwrap();
163 g.add_edge(0, 1).unwrap();
164 assert!(!is_simple(&g).unwrap());
165 }
166
167 #[test]
168 fn reversed_parallel_undirected_breaks_simplicity() {
169 let mut g = Graph::with_vertices(2);
171 g.add_edge(0, 1).unwrap();
172 g.add_edge(1, 0).unwrap();
173 assert!(!is_simple(&g).unwrap());
174 }
175
176 #[test]
177 fn directed_mutual_pair_is_simple() {
178 let mut g = Graph::new(2, true).unwrap();
181 g.add_edge(0, 1).unwrap();
182 g.add_edge(1, 0).unwrap();
183 assert!(is_simple(&g).unwrap());
184 }
185
186 #[test]
187 fn directed_parallel_breaks_simplicity() {
188 let mut g = Graph::new(2, true).unwrap();
189 g.add_edge(0, 1).unwrap();
190 g.add_edge(0, 1).unwrap();
191 assert!(!is_simple(&g).unwrap());
192 }
193
194 #[test]
195 fn directed_self_loop_breaks_simplicity() {
196 let mut g = Graph::new(2, true).unwrap();
197 g.add_edge(0, 0).unwrap();
198 assert!(!is_simple(&g).unwrap());
199 }
200
201 #[test]
204 fn directed_mutual_pair_not_simple_in_undirected_mode() {
205 let mut g = Graph::new(2, true).unwrap();
206 g.add_edge(0, 1).unwrap();
207 g.add_edge(1, 0).unwrap();
208 assert!(is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap());
209 assert!(!is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap());
210 }
211
212 #[test]
213 fn directed_mode_default_matches_is_simple() {
214 let mut g = Graph::new(3, true).unwrap();
215 g.add_edge(0, 1).unwrap();
216 g.add_edge(1, 2).unwrap();
217 g.add_edge(2, 0).unwrap();
218 assert_eq!(
219 is_simple(&g).unwrap(),
220 is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap()
221 );
222 }
223
224 #[test]
225 fn directed_3_cycle_simple_in_undirected_mode() {
226 let mut g = Graph::new(3, true).unwrap();
229 g.add_edge(0, 1).unwrap();
230 g.add_edge(1, 2).unwrap();
231 g.add_edge(2, 0).unwrap();
232 assert!(is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap());
233 }
234
235 #[test]
236 fn directed_self_loop_not_simple_in_either_mode() {
237 let mut g = Graph::new(2, true).unwrap();
238 g.add_edge(0, 0).unwrap();
239 assert!(!is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap());
240 assert!(!is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap());
241 }
242
243 #[test]
244 fn undirected_graph_modes_are_equivalent() {
245 let mut g = Graph::with_vertices(3);
246 g.add_edge(0, 1).unwrap();
247 g.add_edge(1, 2).unwrap();
248 let a = is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap();
249 let b = is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap();
250 assert_eq!(a, b);
251 assert!(a);
252 }
253
254 #[test]
255 fn simplify_makes_graph_simple() {
256 let mut g = Graph::with_vertices(3);
258 g.add_edge(0, 0).unwrap();
259 g.add_edge(0, 1).unwrap();
260 g.add_edge(0, 1).unwrap();
261 g.add_edge(1, 2).unwrap();
262 assert!(!is_simple(&g).unwrap());
263 let s = crate::simplify(&g, true, true).unwrap();
264 assert!(is_simple(&s).unwrap());
265 }
266}