1 #![cfg(feature = "quickcheck")]
2 #[macro_use]
3 extern crate quickcheck;
4 extern crate petgraph;
5 extern crate rand;
6 #[macro_use]
7 extern crate defmac;
8
9 extern crate itertools;
10 extern crate odds;
11
12 mod utils;
13
14 use utils::{Small, Tournament};
15
16 use odds::prelude::*;
17 use std::collections::HashSet;
18 use std::hash::Hash;
19
20 use itertools::assert_equal;
21 use itertools::cloned;
22 use quickcheck::{Arbitrary, Gen};
23 use rand::Rng;
24
25 use petgraph::algo::{
26 bellman_ford, condensation, dijkstra, find_negative_cycle, floyd_warshall, ford_fulkerson,
27 greedy_feedback_arc_set, greedy_matching, is_cyclic_directed, is_cyclic_undirected,
28 is_isomorphic, is_isomorphic_matching, k_shortest_path, kosaraju_scc, maximum_matching,
29 min_spanning_tree, page_rank, tarjan_scc, toposort, Matching,
30 };
31 use petgraph::data::FromElements;
32 use petgraph::dot::{Config, Dot};
33 use petgraph::graph::{edge_index, node_index, IndexType};
34 use petgraph::graphmap::NodeTrait;
35 use petgraph::operator::complement;
36 use petgraph::prelude::*;
37 use petgraph::visit::{
38 EdgeFiltered, EdgeIndexable, IntoEdgeReferences, IntoEdges, IntoNeighbors, IntoNodeIdentifiers,
39 IntoNodeReferences, NodeCount, NodeIndexable, Reversed, Topo, VisitMap, Visitable,
40 };
41 use petgraph::EdgeType;
42
mst_graph<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> Graph<N, E, Undirected, Ix> where Ty: EdgeType, Ix: IndexType, N: Clone, E: Clone + PartialOrd,43 fn mst_graph<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> Graph<N, E, Undirected, Ix>
44 where
45 Ty: EdgeType,
46 Ix: IndexType,
47 N: Clone,
48 E: Clone + PartialOrd,
49 {
50 Graph::from_elements(min_spanning_tree(&g))
51 }
52
53 use std::fmt;
54
55 quickcheck! {
56 fn mst_directed(g: Small<Graph<(), u32>>) -> bool {
57 // filter out isolated nodes
58 let no_singles = g.filter_map(
59 |nx, w| g.neighbors_undirected(nx).next().map(|_| w),
60 |_, w| Some(w));
61 for i in no_singles.node_indices() {
62 assert!(no_singles.neighbors_undirected(i).count() > 0);
63 }
64 assert_eq!(no_singles.edge_count(), g.edge_count());
65 let mst = mst_graph(&no_singles);
66 assert!(!is_cyclic_undirected(&mst));
67 true
68 }
69 }
70
71 quickcheck! {
72 fn mst_undirected(g: Graph<(), u32, Undirected>) -> bool {
73 // filter out isolated nodes
74 let no_singles = g.filter_map(
75 |nx, w| g.neighbors_undirected(nx).next().map(|_| w),
76 |_, w| Some(w));
77 for i in no_singles.node_indices() {
78 assert!(no_singles.neighbors_undirected(i).count() > 0);
79 }
80 assert_eq!(no_singles.edge_count(), g.edge_count());
81 let mst = mst_graph(&no_singles);
82 assert!(!is_cyclic_undirected(&mst));
83 true
84 }
85 }
86
87 quickcheck! {
88 fn reverse_undirected(g: Small<UnGraph<(), ()>>) -> bool {
89 let mut h = (*g).clone();
90 h.reverse();
91 is_isomorphic(&*g, &h)
92 }
93 }
94
assert_graph_consistent<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) where Ty: EdgeType, Ix: IndexType,95 fn assert_graph_consistent<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>)
96 where
97 Ty: EdgeType,
98 Ix: IndexType,
99 {
100 assert_eq!(g.node_count(), g.node_indices().count());
101 assert_eq!(g.edge_count(), g.edge_indices().count());
102 for edge in g.raw_edges() {
103 assert!(
104 g.find_edge(edge.source(), edge.target()).is_some(),
105 "Edge not in graph! {:?} to {:?}",
106 edge.source(),
107 edge.target()
108 );
109 }
110 }
111
112 #[test]
reverse_directed()113 fn reverse_directed() {
114 fn prop<Ty: EdgeType>(mut g: Graph<(), (), Ty>) -> bool {
115 let node_outdegrees = g
116 .node_indices()
117 .map(|i| g.neighbors_directed(i, Outgoing).count())
118 .collect::<Vec<_>>();
119 let node_indegrees = g
120 .node_indices()
121 .map(|i| g.neighbors_directed(i, Incoming).count())
122 .collect::<Vec<_>>();
123
124 g.reverse();
125 let new_outdegrees = g
126 .node_indices()
127 .map(|i| g.neighbors_directed(i, Outgoing).count())
128 .collect::<Vec<_>>();
129 let new_indegrees = g
130 .node_indices()
131 .map(|i| g.neighbors_directed(i, Incoming).count())
132 .collect::<Vec<_>>();
133 assert_eq!(node_outdegrees, new_indegrees);
134 assert_eq!(node_indegrees, new_outdegrees);
135 assert_graph_consistent(&g);
136 true
137 }
138 quickcheck::quickcheck(prop as fn(Graph<_, _, Directed>) -> bool);
139 }
140
141 #[test]
graph_retain_nodes()142 fn graph_retain_nodes() {
143 fn prop<Ty: EdgeType>(mut g: Graph<i32, i32, Ty>) -> bool {
144 // Remove all negative nodes, these should be randomly spread
145 let og = g.clone();
146 let nodes = g.node_count();
147 let num_negs = g.raw_nodes().iter().filter(|n| n.weight < 0).count();
148 let mut removed = 0;
149 g.retain_nodes(|g, i| {
150 let keep = g[i] >= 0;
151 if !keep {
152 removed += 1;
153 }
154 keep
155 });
156 let num_negs_post = g.raw_nodes().iter().filter(|n| n.weight < 0).count();
157 let num_pos_post = g.raw_nodes().iter().filter(|n| n.weight >= 0).count();
158 assert_eq!(num_negs_post, 0);
159 assert_eq!(removed, num_negs);
160 assert_eq!(num_negs + g.node_count(), nodes);
161 assert_eq!(num_pos_post, g.node_count());
162
163 // check against filter_map
164 let filtered = og.filter_map(
165 |_, w| if *w >= 0 { Some(*w) } else { None },
166 |_, w| Some(*w),
167 );
168 assert_eq!(g.node_count(), filtered.node_count());
169 /*
170 println!("Iso of graph with nodes={}, edges={}",
171 g.node_count(), g.edge_count());
172 */
173 assert!(is_isomorphic_matching(
174 &filtered,
175 &g,
176 PartialEq::eq,
177 PartialEq::eq
178 ));
179
180 true
181 }
182 quickcheck::quickcheck(prop as fn(Graph<_, _, Directed>) -> bool);
183 quickcheck::quickcheck(prop as fn(Graph<_, _, Undirected>) -> bool);
184 }
185
186 #[test]
graph_retain_edges()187 fn graph_retain_edges() {
188 fn prop<Ty: EdgeType>(mut g: Graph<(), i32, Ty>) -> bool {
189 // Remove all negative edges, these should be randomly spread
190 let og = g.clone();
191 let edges = g.edge_count();
192 let num_negs = g.raw_edges().iter().filter(|n| n.weight < 0).count();
193 let mut removed = 0;
194 g.retain_edges(|g, i| {
195 let keep = g[i] >= 0;
196 if !keep {
197 removed += 1;
198 }
199 keep
200 });
201 let num_negs_post = g.raw_edges().iter().filter(|n| n.weight < 0).count();
202 let num_pos_post = g.raw_edges().iter().filter(|n| n.weight >= 0).count();
203 assert_eq!(num_negs_post, 0);
204 assert_eq!(removed, num_negs);
205 assert_eq!(num_negs + g.edge_count(), edges);
206 assert_eq!(num_pos_post, g.edge_count());
207 if og.edge_count() < 30 {
208 // check against filter_map
209 let filtered = og.filter_map(
210 |_, w| Some(*w),
211 |_, w| if *w >= 0 { Some(*w) } else { None },
212 );
213 assert_eq!(g.node_count(), filtered.node_count());
214 assert!(is_isomorphic(&filtered, &g));
215 }
216 true
217 }
218 quickcheck::quickcheck(prop as fn(Graph<_, _, Directed>) -> bool);
219 quickcheck::quickcheck(prop as fn(Graph<_, _, Undirected>) -> bool);
220 }
221
222 #[test]
stable_graph_retain_edges()223 fn stable_graph_retain_edges() {
224 fn prop<Ty: EdgeType>(mut g: StableGraph<(), i32, Ty>) -> bool {
225 // Remove all negative edges, these should be randomly spread
226 let og = g.clone();
227 let edges = g.edge_count();
228 let num_negs = g.edge_references().filter(|n| *n.weight() < 0).count();
229 let mut removed = 0;
230 g.retain_edges(|g, i| {
231 let keep = g[i] >= 0;
232 if !keep {
233 removed += 1;
234 }
235 keep
236 });
237 let num_negs_post = g.edge_references().filter(|n| *n.weight() < 0).count();
238 let num_pos_post = g.edge_references().filter(|n| *n.weight() >= 0).count();
239 assert_eq!(num_negs_post, 0);
240 assert_eq!(removed, num_negs);
241 assert_eq!(num_negs + g.edge_count(), edges);
242 assert_eq!(num_pos_post, g.edge_count());
243 if og.edge_count() < 30 {
244 // check against filter_map
245 let filtered = og.filter_map(
246 |_, w| Some(*w),
247 |_, w| if *w >= 0 { Some(*w) } else { None },
248 );
249 assert_eq!(g.node_count(), filtered.node_count());
250 }
251 true
252 }
253 quickcheck::quickcheck(prop as fn(StableGraph<_, _, Directed>) -> bool);
254 quickcheck::quickcheck(prop as fn(StableGraph<_, _, Undirected>) -> bool);
255 }
256
257 #[test]
isomorphism_1()258 fn isomorphism_1() {
259 // using small weights so that duplicates are likely
260 fn prop<Ty: EdgeType>(g: Small<Graph<i8, i8, Ty>>) -> bool {
261 let mut rng = rand::thread_rng();
262 // several trials of different isomorphisms of the same graph
263 // mapping of node indices
264 let mut map = g.node_indices().collect::<Vec<_>>();
265 let mut ng = Graph::<_, _, Ty>::with_capacity(g.node_count(), g.edge_count());
266 for _ in 0..1 {
267 rng.shuffle(&mut map);
268 ng.clear();
269
270 for _ in g.node_indices() {
271 ng.add_node(0);
272 }
273 // Assign node weights
274 for i in g.node_indices() {
275 ng[map[i.index()]] = g[i];
276 }
277 // Add edges
278 for i in g.edge_indices() {
279 let (s, t) = g.edge_endpoints(i).unwrap();
280 ng.add_edge(map[s.index()], map[t.index()], g[i]);
281 }
282 if g.node_count() < 20 && g.edge_count() < 50 {
283 assert!(is_isomorphic(&*g, &ng));
284 }
285 assert!(is_isomorphic_matching(
286 &*g,
287 &ng,
288 PartialEq::eq,
289 PartialEq::eq
290 ));
291 }
292 true
293 }
294 quickcheck::quickcheck(prop::<Undirected> as fn(_) -> bool);
295 quickcheck::quickcheck(prop::<Directed> as fn(_) -> bool);
296 }
297
298 #[test]
isomorphism_modify()299 fn isomorphism_modify() {
300 // using small weights so that duplicates are likely
301 fn prop<Ty: EdgeType>(g: Small<Graph<i16, i8, Ty>>, node: u8, edge: u8) -> bool {
302 println!("graph {:#?}", g);
303 let mut ng = (*g).clone();
304 let i = node_index(node as usize);
305 let j = edge_index(edge as usize);
306 if i.index() < g.node_count() {
307 ng[i] = (g[i] == 0) as i16;
308 }
309 if j.index() < g.edge_count() {
310 ng[j] = (g[j] == 0) as i8;
311 }
312 if i.index() < g.node_count() || j.index() < g.edge_count() {
313 assert!(!is_isomorphic_matching(
314 &*g,
315 &ng,
316 PartialEq::eq,
317 PartialEq::eq
318 ));
319 } else {
320 assert!(is_isomorphic_matching(
321 &*g,
322 &ng,
323 PartialEq::eq,
324 PartialEq::eq
325 ));
326 }
327 true
328 }
329 quickcheck::quickcheck(prop::<Undirected> as fn(_, _, _) -> bool);
330 quickcheck::quickcheck(prop::<Directed> as fn(_, _, _) -> bool);
331 }
332
333 #[test]
graph_remove_edge()334 fn graph_remove_edge() {
335 fn prop<Ty: EdgeType>(mut g: Graph<(), (), Ty>, a: u8, b: u8) -> bool {
336 let a = node_index(a as usize);
337 let b = node_index(b as usize);
338 let edge = g.find_edge(a, b);
339 if !g.is_directed() {
340 assert_eq!(edge.is_some(), g.find_edge(b, a).is_some());
341 }
342 if let Some(ex) = edge {
343 assert!(g.remove_edge(ex).is_some());
344 }
345 assert_graph_consistent(&g);
346 assert!(g.find_edge(a, b).is_none());
347 assert!(g.neighbors(a).find(|x| *x == b).is_none());
348 if !g.is_directed() {
349 assert!(g.neighbors(b).find(|x| *x == a).is_none());
350 }
351 true
352 }
353 quickcheck::quickcheck(prop as fn(Graph<_, _, Undirected>, _, _) -> bool);
354 quickcheck::quickcheck(prop as fn(Graph<_, _, Directed>, _, _) -> bool);
355 }
356
357 #[cfg(feature = "stable_graph")]
358 #[test]
stable_graph_remove_edge()359 fn stable_graph_remove_edge() {
360 fn prop<Ty: EdgeType>(mut g: StableGraph<(), (), Ty>, a: u8, b: u8) -> bool {
361 let a = node_index(a as usize);
362 let b = node_index(b as usize);
363 let edge = g.find_edge(a, b);
364 if !g.is_directed() {
365 assert_eq!(edge.is_some(), g.find_edge(b, a).is_some());
366 }
367 if let Some(ex) = edge {
368 assert!(g.remove_edge(ex).is_some());
369 }
370 //assert_graph_consistent(&g);
371 assert!(g.find_edge(a, b).is_none());
372 assert!(g.neighbors(a).find(|x| *x == b).is_none());
373 if !g.is_directed() {
374 assert!(g.find_edge(b, a).is_none());
375 assert!(g.neighbors(b).find(|x| *x == a).is_none());
376 }
377 true
378 }
379 quickcheck::quickcheck(prop as fn(StableGraph<_, _, Undirected>, _, _) -> bool);
380 quickcheck::quickcheck(prop as fn(StableGraph<_, _, Directed>, _, _) -> bool);
381 }
382
383 #[cfg(feature = "stable_graph")]
384 #[test]
stable_graph_add_remove_edges()385 fn stable_graph_add_remove_edges() {
386 fn prop<Ty: EdgeType>(mut g: StableGraph<(), (), Ty>, edges: Vec<(u8, u8)>) -> bool {
387 for &(a, b) in &edges {
388 let a = node_index(a as usize);
389 let b = node_index(b as usize);
390 let edge = g.find_edge(a, b);
391
392 if edge.is_none() && g.contains_node(a) && g.contains_node(b) {
393 let _index = g.add_edge(a, b, ());
394 continue;
395 }
396
397 if !g.is_directed() {
398 assert_eq!(edge.is_some(), g.find_edge(b, a).is_some());
399 }
400 if let Some(ex) = edge {
401 assert!(g.remove_edge(ex).is_some());
402 }
403 //assert_graph_consistent(&g);
404 assert!(
405 g.find_edge(a, b).is_none(),
406 "failed to remove edge {:?} from graph {:?}",
407 (a, b),
408 g
409 );
410 assert!(g.neighbors(a).find(|x| *x == b).is_none());
411 if !g.is_directed() {
412 assert!(g.find_edge(b, a).is_none());
413 assert!(g.neighbors(b).find(|x| *x == a).is_none());
414 }
415 }
416 true
417 }
418 quickcheck::quickcheck(prop as fn(StableGraph<_, _, Undirected>, _) -> bool);
419 quickcheck::quickcheck(prop as fn(StableGraph<_, _, Directed>, _) -> bool);
420 }
421
assert_graphmap_consistent<N, E, Ty>(g: &GraphMap<N, E, Ty>) where Ty: EdgeType, N: NodeTrait + fmt::Debug,422 fn assert_graphmap_consistent<N, E, Ty>(g: &GraphMap<N, E, Ty>)
423 where
424 Ty: EdgeType,
425 N: NodeTrait + fmt::Debug,
426 {
427 for (a, b, _weight) in g.all_edges() {
428 assert!(
429 g.contains_edge(a, b),
430 "Edge not in graph! {:?} to {:?}",
431 a,
432 b
433 );
434 assert!(
435 g.neighbors(a).find(|x| *x == b).is_some(),
436 "Edge {:?} not in neighbor list for {:?}",
437 (a, b),
438 a
439 );
440 if !g.is_directed() {
441 assert!(
442 g.neighbors(b).find(|x| *x == a).is_some(),
443 "Edge {:?} not in neighbor list for {:?}",
444 (b, a),
445 b
446 );
447 }
448 }
449 }
450
451 #[test]
graphmap_remove()452 fn graphmap_remove() {
453 fn prop<Ty: EdgeType>(mut g: GraphMap<i8, (), Ty>, a: i8, b: i8) -> bool {
454 //if g.edge_count() > 20 { return true; }
455 assert_graphmap_consistent(&g);
456 let contains = g.contains_edge(a, b);
457 if !g.is_directed() {
458 assert_eq!(contains, g.contains_edge(b, a));
459 }
460 assert_eq!(g.remove_edge(a, b).is_some(), contains);
461 assert!(!g.contains_edge(a, b) && g.neighbors(a).find(|x| *x == b).is_none());
462 //(g.is_directed() || g.neighbors(b).find(|x| *x == a).is_none()));
463 assert!(g.remove_edge(a, b).is_none());
464 assert_graphmap_consistent(&g);
465 true
466 }
467 quickcheck::quickcheck(prop as fn(DiGraphMap<_, _>, _, _) -> bool);
468 quickcheck::quickcheck(prop as fn(UnGraphMap<_, _>, _, _) -> bool);
469 }
470
471 #[test]
graphmap_add_remove()472 fn graphmap_add_remove() {
473 fn prop(mut g: UnGraphMap<i8, ()>, a: i8, b: i8) -> bool {
474 assert_eq!(g.contains_edge(a, b), g.add_edge(a, b, ()).is_some());
475 g.remove_edge(a, b);
476 !g.contains_edge(a, b)
477 && g.neighbors(a).find(|x| *x == b).is_none()
478 && g.neighbors(b).find(|x| *x == a).is_none()
479 }
480 quickcheck::quickcheck(prop as fn(_, _, _) -> bool);
481 }
482
sort_sccs<T: Ord>(v: &mut [Vec<T>])483 fn sort_sccs<T: Ord>(v: &mut [Vec<T>]) {
484 for scc in &mut *v {
485 scc.sort();
486 }
487 v.sort();
488 }
489
490 quickcheck! {
491 fn graph_sccs(g: Graph<(), ()>) -> bool {
492 let mut sccs = kosaraju_scc(&g);
493 let mut tsccs = tarjan_scc(&g);
494 sort_sccs(&mut sccs);
495 sort_sccs(&mut tsccs);
496 if sccs != tsccs {
497 println!("{:?}",
498 Dot::with_config(&g, &[Config::EdgeNoLabel,
499 Config::NodeIndexLabel]));
500 println!("Sccs {:?}", sccs);
501 println!("Sccs (Tarjan) {:?}", tsccs);
502 return false;
503 }
504 true
505 }
506 }
507
508 quickcheck! {
509 fn kosaraju_scc_is_topo_sort(g: Graph<(), ()>) -> bool {
510 let tsccs = kosaraju_scc(&g);
511 let firsts = tsccs.iter().rev().map(|v| v[0]).collect::<Vec<_>>();
512 subset_is_topo_order(&g, &firsts)
513 }
514 }
515
516 quickcheck! {
517 fn tarjan_scc_is_topo_sort(g: Graph<(), ()>) -> bool {
518 let tsccs = tarjan_scc(&g);
519 let firsts = tsccs.iter().rev().map(|v| v[0]).collect::<Vec<_>>();
520 subset_is_topo_order(&g, &firsts)
521 }
522 }
523
524 quickcheck! {
525 // Reversed edges gives the same sccs (when sorted)
526 fn graph_reverse_sccs(g: Graph<(), ()>) -> bool {
527 let mut sccs = kosaraju_scc(&g);
528 let mut tsccs = kosaraju_scc(Reversed(&g));
529 sort_sccs(&mut sccs);
530 sort_sccs(&mut tsccs);
531 if sccs != tsccs {
532 println!("{:?}",
533 Dot::with_config(&g, &[Config::EdgeNoLabel,
534 Config::NodeIndexLabel]));
535 println!("Sccs {:?}", sccs);
536 println!("Sccs (Reversed) {:?}", tsccs);
537 return false;
538 }
539 true
540 }
541 }
542
543 quickcheck! {
544 // Reversed edges gives the same sccs (when sorted)
545 fn graphmap_reverse_sccs(g: DiGraphMap<u16, ()>) -> bool {
546 let mut sccs = kosaraju_scc(&g);
547 let mut tsccs = kosaraju_scc(Reversed(&g));
548 sort_sccs(&mut sccs);
549 sort_sccs(&mut tsccs);
550 if sccs != tsccs {
551 println!("{:?}",
552 Dot::with_config(&g, &[Config::EdgeNoLabel,
553 Config::NodeIndexLabel]));
554 println!("Sccs {:?}", sccs);
555 println!("Sccs (Reversed) {:?}", tsccs);
556 return false;
557 }
558 true
559 }
560 }
561
562 #[test]
graph_condensation_acyclic()563 fn graph_condensation_acyclic() {
564 fn prop(g: Graph<(), ()>) -> bool {
565 !is_cyclic_directed(&condensation(g, /* make_acyclic */ true))
566 }
567 quickcheck::quickcheck(prop as fn(_) -> bool);
568 }
569
570 #[derive(Debug, Clone)]
571 struct DAG<N: Default + Clone + Send + 'static>(Graph<N, ()>);
572
573 impl<N: Default + Clone + Send + 'static> Arbitrary for DAG<N> {
arbitrary<G: Gen>(g: &mut G) -> Self574 fn arbitrary<G: Gen>(g: &mut G) -> Self {
575 let nodes = usize::arbitrary(g);
576 if nodes == 0 {
577 return DAG(Graph::with_capacity(0, 0));
578 }
579 let split = g.gen_range(0., 1.);
580 let max_width = f64::sqrt(nodes as f64) as usize;
581 let tall = (max_width as f64 * split) as usize;
582 let fat = max_width - tall;
583
584 let edge_prob = 1. - (1. - g.gen_range(0., 1.)) * (1. - g.gen_range(0., 1.));
585 let edges = ((nodes as f64).powi(2) * edge_prob) as usize;
586 let mut gr = Graph::with_capacity(nodes, edges);
587 let mut nodes = 0;
588 for _ in 0..tall {
589 let cur_nodes = g.gen_range(0, fat);
590 for _ in 0..cur_nodes {
591 gr.add_node(N::default());
592 }
593 for j in 0..nodes {
594 for k in 0..cur_nodes {
595 if g.gen_range(0., 1.) < edge_prob {
596 gr.add_edge(NodeIndex::new(j), NodeIndex::new(k + nodes), ());
597 }
598 }
599 }
600 nodes += cur_nodes;
601 }
602 DAG(gr)
603 }
604
605 // shrink the graph by splitting it in two by a very
606 // simple algorithm, just even and odd node indices
shrink(&self) -> Box<dyn Iterator<Item = Self>>607 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
608 let self_ = self.clone();
609 Box::new((0..2).filter_map(move |x| {
610 let gr = self_.0.filter_map(
611 |i, w| {
612 if i.index() % 2 == x {
613 Some(w.clone())
614 } else {
615 None
616 }
617 },
618 |_, w| Some(w.clone()),
619 );
620 // make sure we shrink
621 if gr.node_count() < self_.0.node_count() {
622 Some(DAG(gr))
623 } else {
624 None
625 }
626 }))
627 }
628 }
629
is_topo_order<N>(gr: &Graph<N, (), Directed>, order: &[NodeIndex]) -> bool630 fn is_topo_order<N>(gr: &Graph<N, (), Directed>, order: &[NodeIndex]) -> bool {
631 if gr.node_count() != order.len() {
632 println!(
633 "Graph ({}) and count ({}) had different amount of nodes.",
634 gr.node_count(),
635 order.len()
636 );
637 return false;
638 }
639 // check all the edges of the graph
640 for edge in gr.raw_edges() {
641 let a = edge.source();
642 let b = edge.target();
643 let ai = order.find(&a).unwrap();
644 let bi = order.find(&b).unwrap();
645 if ai >= bi {
646 println!("{:?} > {:?} ", a, b);
647 return false;
648 }
649 }
650 true
651 }
652
subset_is_topo_order<N>(gr: &Graph<N, (), Directed>, order: &[NodeIndex]) -> bool653 fn subset_is_topo_order<N>(gr: &Graph<N, (), Directed>, order: &[NodeIndex]) -> bool {
654 if gr.node_count() < order.len() {
655 println!(
656 "Graph (len={}) had less nodes than order (len={})",
657 gr.node_count(),
658 order.len()
659 );
660 return false;
661 }
662 // check all the edges of the graph
663 for edge in gr.raw_edges() {
664 let a = edge.source();
665 let b = edge.target();
666 if a == b {
667 continue;
668 }
669 // skip those that are not in the subset
670 let ai = match order.find(&a) {
671 Some(i) => i,
672 None => continue,
673 };
674 let bi = match order.find(&b) {
675 Some(i) => i,
676 None => continue,
677 };
678 if ai >= bi {
679 println!("{:?} > {:?} ", a, b);
680 return false;
681 }
682 }
683 true
684 }
685
686 #[test]
full_topo()687 fn full_topo() {
688 fn prop(DAG(gr): DAG<()>) -> bool {
689 let order = toposort(&gr, None).unwrap();
690 is_topo_order(&gr, &order)
691 }
692 quickcheck::quickcheck(prop as fn(_) -> bool);
693 }
694
695 #[test]
full_topo_generic()696 fn full_topo_generic() {
697 fn prop_generic(DAG(mut gr): DAG<usize>) -> bool {
698 assert!(!is_cyclic_directed(&gr));
699 let mut index = 0;
700 let mut topo = Topo::new(&gr);
701 while let Some(nx) = topo.next(&gr) {
702 gr[nx] = index;
703 index += 1;
704 }
705
706 let mut order = Vec::new();
707 index = 0;
708 let mut topo = Topo::new(&gr);
709 while let Some(nx) = topo.next(&gr) {
710 order.push(nx);
711 assert_eq!(gr[nx], index);
712 index += 1;
713 }
714 if !is_topo_order(&gr, &order) {
715 println!("{:?}", gr);
716 return false;
717 }
718
719 {
720 order.clear();
721 let mut topo = Topo::new(&gr);
722 while let Some(nx) = topo.next(&gr) {
723 order.push(nx);
724 }
725 if !is_topo_order(&gr, &order) {
726 println!("{:?}", gr);
727 return false;
728 }
729 }
730
731 {
732 order.clear();
733 let init_nodes = gr.node_identifiers().filter(|n| {
734 gr.neighbors_directed(n.clone(), Direction::Incoming)
735 .next()
736 .is_none()
737 });
738 let mut topo = Topo::with_initials(&gr, init_nodes);
739 while let Some(nx) = topo.next(&gr) {
740 order.push(nx);
741 }
742 if !is_topo_order(&gr, &order) {
743 println!("{:?}", gr);
744 return false;
745 }
746 }
747
748 {
749 order.clear();
750 let mut topo = Topo::with_initials(&gr, gr.node_identifiers());
751 while let Some(nx) = topo.next(&gr) {
752 order.push(nx);
753 }
754 if !is_topo_order(&gr, &order) {
755 println!("{:?}", gr);
756 return false;
757 }
758 }
759 true
760 }
761 quickcheck::quickcheck(prop_generic as fn(_) -> bool);
762 }
763
764 quickcheck! {
765 // checks that the distances computed by dijkstra satisfy the triangle
766 // inequality.
767 fn dijkstra_triangle_ineq(g: Graph<u32, u32>, node: usize) -> bool {
768 if g.node_count() == 0 {
769 return true;
770 }
771 let v = node_index(node % g.node_count());
772 let distances = dijkstra(&g, v, None, |e| *e.weight());
773 for v2 in distances.keys() {
774 let dv2 = distances[v2];
775 // triangle inequality:
776 // d(v,u) <= d(v,v2) + w(v2,u)
777 for edge in g.edges(*v2) {
778 let u = edge.target();
779 let w = edge.weight();
780 if distances.contains_key(&u) && distances[&u] > dv2 + w {
781 return false;
782 }
783 }
784 }
785 true
786 }
787 }
788
789 quickcheck! {
790 // checks that the distances computed by k'th shortest path is always greater or equal compared to their dijkstra computation
791 fn k_shortest_path_(g: Graph<u32, u32>, node: usize) -> bool {
792 if g.node_count() == 0 {
793 return true;
794 }
795 let v = node_index(node % g.node_count());
796 let second_best_distances = k_shortest_path(&g, v, None, 2, |e| *e.weight());
797 let dijkstra_distances = dijkstra(&g, v, None, |e| *e.weight());
798 for v in second_best_distances.keys() {
799 if second_best_distances[&v] < dijkstra_distances[&v] {
800 return false;
801 }
802 }
803 true
804 }
805 }
806
807 quickcheck! {
808 // checks floyd_warshall against dijkstra results
809 fn floyd_warshall_(g: Graph<u32, u32>) -> bool {
810 if g.node_count() == 0 {
811 return true;
812 }
813
814 let fw_res = floyd_warshall(&g, |e| *e.weight()).unwrap();
815
816 for node1 in g.node_identifiers() {
817 let dijkstra_res = dijkstra(&g, node1, None, |e| *e.weight());
818
819 for node2 in g.node_identifiers() {
820 // if dijkstra found a path then the results must be same
821 if let Some(distance) = dijkstra_res.get(&node2) {
822 let floyd_distance = fw_res.get(&(node1, node2)).unwrap();
823 if distance != floyd_distance {
824 return false;
825 }
826 } else {
827 // if there are no path between two nodes then floyd_warshall will return maximum value possible
828 if *fw_res.get(&(node1, node2)).unwrap() != u32::MAX {
829 return false;
830 }
831 }
832 }
833 }
834 true
835 }
836 }
837
838 quickcheck! {
839 // checks that the complement of the complement is the same as the input if the input does not contain self-loops
840 fn complement_(g: Graph<u32, u32>, _node: usize) -> bool {
841 if g.node_count() == 0 {
842 return true;
843 }
844 for x in g.node_indices() {
845 if g.contains_edge(x, x) {
846 return true;
847 }
848 }
849 let mut complement_graph: Graph<u32, u32> = Graph::new();
850 let mut result: Graph<u32, u32> = Graph::new();
851 complement(&g, &mut complement_graph, 0);
852 complement(&complement_graph, &mut result, 0);
853
854 for x in g.node_indices() {
855 for y in g.node_indices() {
856 if g.contains_edge(x, y) != result.contains_edge(x, y){
857 return false;
858 }
859 }
860 }
861 true
862 }
863 }
864
set<I>(iter: I) -> HashSet<I::Item> where I: IntoIterator, I::Item: Hash + Eq,865 fn set<I>(iter: I) -> HashSet<I::Item>
866 where
867 I: IntoIterator,
868 I::Item: Hash + Eq,
869 {
870 iter.into_iter().collect()
871 }
872
873 quickcheck! {
874 fn dfs_visit(gr: Graph<(), ()>, node: usize) -> bool {
875 use petgraph::visit::{Visitable, VisitMap};
876 use petgraph::visit::DfsEvent::*;
877 use petgraph::visit::{Time, depth_first_search};
878 if gr.node_count() == 0 {
879 return true;
880 }
881 let start_node = node_index(node % gr.node_count());
882
883 let invalid_time = Time(!0);
884 let mut discover_time = vec![invalid_time; gr.node_count()];
885 let mut finish_time = vec![invalid_time; gr.node_count()];
886 let mut has_tree_edge = gr.visit_map();
887 let mut edges = HashSet::new();
888 depth_first_search(&gr, Some(start_node).into_iter().chain(gr.node_indices()),
889 |evt| {
890 match evt {
891 Discover(n, t) => discover_time[n.index()] = t,
892 Finish(n, t) => finish_time[n.index()] = t,
893 TreeEdge(u, v) => {
894 // v is an ancestor of u
895 assert!(has_tree_edge.visit(v), "Two tree edges to {:?}!", v);
896 assert!(discover_time[v.index()] == invalid_time);
897 assert!(discover_time[u.index()] != invalid_time);
898 assert!(finish_time[u.index()] == invalid_time);
899 edges.insert((u, v));
900 }
901 BackEdge(u, v) => {
902 // u is an ancestor of v
903 assert!(discover_time[v.index()] != invalid_time);
904 assert!(finish_time[v.index()] == invalid_time);
905 edges.insert((u, v));
906 }
907 CrossForwardEdge(u, v) => {
908 edges.insert((u, v));
909 }
910 }
911 });
912 assert!(discover_time.iter().all(|x| *x != invalid_time));
913 assert!(finish_time.iter().all(|x| *x != invalid_time));
914 assert_eq!(edges.len(), gr.edge_count());
915 assert_eq!(edges, set(gr.edge_references().map(|e| (e.source(), e.target()))));
916 true
917 }
918 }
919
920 quickcheck! {
921 fn test_bellman_ford(gr: Graph<(), f32>) -> bool {
922 let mut gr = gr;
923 for elt in gr.edge_weights_mut() {
924 *elt = elt.abs();
925 }
926 if gr.node_count() == 0 {
927 return true;
928 }
929 for (i, start) in gr.node_indices().enumerate() {
930 if i >= 10 { break; } // testing all is too slow
931 bellman_ford(&gr, start).unwrap();
932 }
933 true
934 }
935 }
936
937 quickcheck! {
938 fn test_find_negative_cycle(gr: Graph<(), f32>) -> bool {
939 let gr = gr;
940 if gr.node_count() == 0 {
941 return true;
942 }
943 for (i, start) in gr.node_indices().enumerate() {
944 if i >= 10 { break; } // testing all is too slow
945 if let Some(path) = find_negative_cycle(&gr, start) {
946 assert!(path.len() >= 1);
947 }
948 }
949 true
950 }
951 }
952
953 quickcheck! {
954 fn test_bellman_ford_undir(gr: Graph<(), f32, Undirected>) -> bool {
955 let mut gr = gr;
956 for elt in gr.edge_weights_mut() {
957 *elt = elt.abs();
958 }
959 if gr.node_count() == 0 {
960 return true;
961 }
962 for (i, start) in gr.node_indices().enumerate() {
963 if i >= 10 { break; } // testing all is too slow
964 bellman_ford(&gr, start).unwrap();
965 }
966 true
967 }
968 }
969
970 defmac!(iter_eq a, b => a.eq(b));
971 defmac!(nodes_eq ref a, ref b => a.node_references().eq(b.node_references()));
972 defmac!(edgew_eq ref a, ref b => a.edge_references().eq(b.edge_references()));
973 defmac!(edges_eq ref a, ref b =>
974 iter_eq!(
975 a.edge_references().map(|e| (e.source(), e.target())),
976 b.edge_references().map(|e| (e.source(), e.target()))));
977
978 quickcheck! {
979 fn test_di_from(gr1: DiGraph<i32, i32>) -> () {
980 let sgr = StableGraph::from(gr1.clone());
981 let gr2 = Graph::from(sgr);
982
983 assert!(nodes_eq!(&gr1, &gr2));
984 assert!(edgew_eq!(&gr1, &gr2));
985 assert!(edges_eq!(&gr1, &gr2));
986 }
987 fn test_un_from(gr1: UnGraph<i32, i32>) -> () {
988 let sgr = StableGraph::from(gr1.clone());
989 let gr2 = Graph::from(sgr);
990
991 assert!(nodes_eq!(&gr1, &gr2));
992 assert!(edgew_eq!(&gr1, &gr2));
993 assert!(edges_eq!(&gr1, &gr2));
994 }
995
996 fn test_graph_from_stable_graph(gr1: StableDiGraph<usize, usize>) -> () {
997 let mut gr1 = gr1;
998 let gr2 = Graph::from(gr1.clone());
999
1000 // renumber the stablegraph nodes and put the new index in the
1001 // associated data
1002 let mut index = 0;
1003 for i in 0..gr1.node_bound() {
1004 let ni = node_index(i);
1005 if gr1.contains_node(ni) {
1006 gr1[ni] = index;
1007 index += 1;
1008 }
1009 }
1010 if let Some(edge_bound) = gr1.edge_references().next_back()
1011 .map(|ed| ed.id().index() + 1)
1012 {
1013 index = 0;
1014 for i in 0..edge_bound {
1015 let ni = edge_index(i);
1016 if gr1.edge_weight(ni).is_some() {
1017 gr1[ni] = index;
1018 index += 1;
1019 }
1020 }
1021 }
1022
1023 assert_equal(
1024 // Remap the stablegraph to compact indices
1025 gr1.edge_references().map(|ed| (edge_index(*ed.weight()), gr1[ed.source()], gr1[ed.target()])),
1026 gr2.edge_references().map(|ed| (ed.id(), ed.source().index(), ed.target().index()))
1027 );
1028 }
1029
1030 fn stable_di_graph_map_id(gr1: StableDiGraph<usize, usize>) -> () {
1031 let gr2 = gr1.map(|_, &nw| nw, |_, &ew| ew);
1032 assert!(nodes_eq!(&gr1, &gr2));
1033 assert!(edgew_eq!(&gr1, &gr2));
1034 assert!(edges_eq!(&gr1, &gr2));
1035 }
1036
1037 fn stable_un_graph_map_id(gr1: StableUnGraph<usize, usize>) -> () {
1038 let gr2 = gr1.map(|_, &nw| nw, |_, &ew| ew);
1039 assert!(nodes_eq!(&gr1, &gr2));
1040 assert!(edgew_eq!(&gr1, &gr2));
1041 assert!(edges_eq!(&gr1, &gr2));
1042 }
1043
1044 fn stable_di_graph_filter_map_id(gr1: StableDiGraph<usize, usize>) -> () {
1045 let gr2 = gr1.filter_map(|_, &nw| Some(nw), |_, &ew| Some(ew));
1046 assert!(nodes_eq!(&gr1, &gr2));
1047 assert!(edgew_eq!(&gr1, &gr2));
1048 assert!(edges_eq!(&gr1, &gr2));
1049 }
1050
1051 fn test_stable_un_graph_filter_map_id(gr1: StableUnGraph<usize, usize>) -> () {
1052 let gr2 = gr1.filter_map(|_, &nw| Some(nw), |_, &ew| Some(ew));
1053 assert!(nodes_eq!(&gr1, &gr2));
1054 assert!(edgew_eq!(&gr1, &gr2));
1055 assert!(edges_eq!(&gr1, &gr2));
1056 }
1057
1058 fn stable_di_graph_filter_map_remove(gr1: Small<StableDiGraph<i32, i32>>,
1059 nodes: Vec<usize>,
1060 edges: Vec<usize>) -> ()
1061 {
1062 let gr2 = gr1.filter_map(|ix, &nw| {
1063 if !nodes.contains(&ix.index()) { Some(nw) } else { None }
1064 },
1065 |ix, &ew| {
1066 if !edges.contains(&ix.index()) { Some(ew) } else { None }
1067 });
1068 let check_nodes = &set(gr1.node_indices()) - &set(cloned(&nodes).map(node_index));
1069 let mut check_edges = &set(gr1.edge_indices()) - &set(cloned(&edges).map(edge_index));
1070 // remove all edges with endpoint in removed nodes
1071 for edge in gr1.edge_references() {
1072 if nodes.contains(&edge.source().index()) ||
1073 nodes.contains(&edge.target().index()) {
1074 check_edges.remove(&edge.id());
1075 }
1076 }
1077 // assert maintained
1078 for i in check_nodes {
1079 assert_eq!(gr1[i], gr2[i]);
1080 }
1081 for i in check_edges {
1082 assert_eq!(gr1[i], gr2[i]);
1083 assert_eq!(gr1.edge_endpoints(i), gr2.edge_endpoints(i));
1084 }
1085
1086 // assert removals
1087 for i in nodes {
1088 assert!(gr2.node_weight(node_index(i)).is_none());
1089 }
1090 for i in edges {
1091 assert!(gr2.edge_weight(edge_index(i)).is_none());
1092 }
1093 }
1094 }
1095
naive_closure_foreach<G, F>(g: G, mut f: F) where G: Visitable + IntoNeighbors + IntoNodeIdentifiers, F: FnMut(G::NodeId, G::NodeId),1096 fn naive_closure_foreach<G, F>(g: G, mut f: F)
1097 where
1098 G: Visitable + IntoNeighbors + IntoNodeIdentifiers,
1099 F: FnMut(G::NodeId, G::NodeId),
1100 {
1101 let mut dfs = Dfs::empty(&g);
1102 for i in g.node_identifiers() {
1103 dfs.reset(&g);
1104 dfs.move_to(i);
1105 while let Some(nx) = dfs.next(&g) {
1106 if i != nx {
1107 f(i, nx);
1108 }
1109 }
1110 }
1111 }
1112
naive_closure<G>(g: G) -> Vec<(G::NodeId, G::NodeId)> where G: Visitable + IntoNodeIdentifiers + IntoNeighbors,1113 fn naive_closure<G>(g: G) -> Vec<(G::NodeId, G::NodeId)>
1114 where
1115 G: Visitable + IntoNodeIdentifiers + IntoNeighbors,
1116 {
1117 let mut res = Vec::new();
1118 naive_closure_foreach(g, |a, b| res.push((a, b)));
1119 res
1120 }
1121
naive_closure_edgecount<G>(g: G) -> usize where G: Visitable + IntoNodeIdentifiers + IntoNeighbors,1122 fn naive_closure_edgecount<G>(g: G) -> usize
1123 where
1124 G: Visitable + IntoNodeIdentifiers + IntoNeighbors,
1125 {
1126 let mut res = 0;
1127 naive_closure_foreach(g, |_, _| res += 1);
1128 res
1129 }
1130
1131 quickcheck! {
1132 fn test_tred(g: DAG<()>) -> bool {
1133 let acyclic = g.0;
1134 println!("acyclic graph {:#?}", &acyclic);
1135 let toposort = toposort(&acyclic, None).unwrap();
1136 println!("Toposort:");
1137 for (new, old) in toposort.iter().enumerate() {
1138 println!("{} -> {}", old.index(), new);
1139 }
1140 let (toposorted, revtopo): (petgraph::adj::List<(), usize>, _) =
1141 petgraph::algo::tred::dag_to_toposorted_adjacency_list(&acyclic, &toposort);
1142 println!("checking revtopo");
1143 for (i, ix) in toposort.iter().enumerate() {
1144 assert_eq!(i, revtopo[ix.index()]);
1145 }
1146 println!("toposorted adjacency list: {:#?}", &toposorted);
1147 let (tred, tclos) = petgraph::algo::tred::dag_transitive_reduction_closure(&toposorted);
1148 println!("tred: {:#?}", &tred);
1149 println!("tclos: {:#?}", &tclos);
1150 if tred.node_count() != tclos.node_count() {
1151 println!("Different node count");
1152 return false;
1153 }
1154 if acyclic.node_count() != tclos.node_count() {
1155 println!("Different node count from original graph");
1156 return false;
1157 }
1158 // check the closure
1159 let mut clos_edges: Vec<(_, _)> = tclos.edge_references().map(|i| (i.source(), i.target())).collect();
1160 clos_edges.sort();
1161 let mut tred_closure = naive_closure(&tred);
1162 tred_closure.sort();
1163 if tred_closure != clos_edges {
1164 println!("tclos is not the transitive closure of tred");
1165 return false
1166 }
1167 // check the transitive reduction is a transitive reduction
1168 for i in tred.edge_references() {
1169 let filtered = EdgeFiltered::from_fn(&tred, |edge| {
1170 edge.source() !=i.source() || edge.target() != i.target()
1171 });
1172 let new = naive_closure_edgecount(&filtered);
1173 if new >= clos_edges.len() {
1174 println!("when removing ({} -> {}) the transitive closure does not shrink",
1175 i.source().index(), i.target().index());
1176 return false
1177 }
1178 }
1179 // check that the transitive reduction is included in the original graph
1180 for i in tred.edge_references() {
1181 if acyclic.find_edge(toposort[i.source().index()], toposort[i.target().index()]).is_none() {
1182 println!("tred is not included in the original graph");
1183 return false
1184 }
1185 }
1186 println!("ok!");
1187 true
1188 }
1189 }
1190
1191 quickcheck! {
1192 fn greedy_fas_remaining_graph_is_acyclic(g: StableDiGraph<(), ()>) -> bool {
1193 let mut g = g;
1194 let fas: Vec<EdgeIndex> = greedy_feedback_arc_set(&g).map(|e| e.id()).collect();
1195
1196 for edge_id in fas {
1197 g.remove_edge(edge_id);
1198 }
1199
1200 !is_cyclic_directed(&g)
1201 }
1202
1203 /// Assert that the size of the feedback arc set of a tournament does not exceed
1204 /// **|E| / 2 - |V| / 6**
1205 fn greedy_fas_performance_within_bound(t: Tournament<(), ()>) -> bool {
1206 let Tournament(g) = t;
1207
1208 let expected_bound = if g.node_count() < 2 {
1209 0
1210 } else {
1211 ((g.edge_count() as f64) / 2.0 - (g.node_count() as f64) / 6.0) as usize
1212 };
1213
1214 let fas_size = greedy_feedback_arc_set(&g).count();
1215
1216 fas_size <= expected_bound
1217 }
1218 }
1219
is_valid_matching<G: NodeIndexable>(m: &Matching<G>) -> bool1220 fn is_valid_matching<G: NodeIndexable>(m: &Matching<G>) -> bool {
1221 // A set of edges is a matching if no two edges from the matching share an
1222 // endpoint.
1223 for (s1, t1) in m.edges() {
1224 for (s2, t2) in m.edges() {
1225 if s1 == s2 && t1 == t2 {
1226 continue;
1227 }
1228
1229 if s1 == s2 || s1 == t2 || t1 == s2 || t1 == t2 {
1230 // Two edges share an endpoint.
1231 return false;
1232 }
1233 }
1234 }
1235
1236 true
1237 }
1238
is_maximum_matching<G: NodeIndexable + IntoEdges + IntoNodeIdentifiers + Visitable>( g: G, m: &Matching<G>, ) -> bool1239 fn is_maximum_matching<G: NodeIndexable + IntoEdges + IntoNodeIdentifiers + Visitable>(
1240 g: G,
1241 m: &Matching<G>,
1242 ) -> bool {
1243 // Berge's lemma: a matching is maximum iff there is no augmenting path (a
1244 // path that starts and ends in unmatched vertices, and alternates between
1245 // matched and unmatched edges). Thus if we find an augmenting path, the
1246 // matching is not maximum.
1247 //
1248 // Start with an unmatched node and traverse the graph alternating matched
1249 // and unmatched edges. If an unmatched node is found, then an augmenting
1250 // path was found.
1251 for unmatched in g.node_identifiers().filter(|u| !m.contains_node(*u)) {
1252 let visited = &mut g.visit_map();
1253 let mut stack = Vec::new();
1254
1255 stack.push((unmatched, false));
1256 while let Some((u, do_matched_edges)) = stack.pop() {
1257 if visited.visit(u) {
1258 for e in g.edges(u) {
1259 if e.source() == e.target() {
1260 // Ignore self-loops.
1261 continue;
1262 }
1263
1264 let is_matched = m.contains_edge(e.source(), e.target());
1265
1266 if do_matched_edges && is_matched || !do_matched_edges && !is_matched {
1267 stack.push((e.target(), !do_matched_edges));
1268
1269 // Found another free node (other than the starting one)
1270 // that is unmatched - an augmenting path.
1271 if !is_matched && !m.contains_node(e.target()) && e.target() != unmatched {
1272 return false;
1273 }
1274 }
1275 }
1276 }
1277 }
1278 }
1279
1280 true
1281 }
1282
is_perfect_matching<G: NodeCount + NodeIndexable>(g: G, m: &Matching<G>) -> bool1283 fn is_perfect_matching<G: NodeCount + NodeIndexable>(g: G, m: &Matching<G>) -> bool {
1284 // By definition.
1285 g.node_count() % 2 == 0 && m.edges().count() == g.node_count() / 2
1286 }
1287
1288 quickcheck! {
1289 fn matching(g: Graph<(), (), Undirected>) -> bool {
1290 let m1 = greedy_matching(&g);
1291 let m2 = maximum_matching(&g);
1292
1293 assert!(is_valid_matching(&m1), "greedy_matching returned an invalid matching");
1294 assert!(is_valid_matching(&m2), "maximum_matching returned an invalid matching");
1295 assert!(is_maximum_matching(&g, &m2), "maximum_matching returned a matching that is not maximum");
1296 assert_eq!(m1.is_perfect(), is_perfect_matching(&g, &m1), "greedy_matching incorrectly determined whether the matching is perfect");
1297 assert_eq!(m2.is_perfect(), is_perfect_matching(&g, &m2), "maximum_matching incorrectly determined whether the matching is perfect");
1298
1299 true
1300 }
1301
1302 fn matching_in_stable_graph(g: StableGraph<(), (), Undirected>) -> bool {
1303 let m1 = greedy_matching(&g);
1304 let m2 = maximum_matching(&g);
1305
1306 assert!(is_valid_matching(&m1), "greedy_matching returned an invalid matching");
1307 assert!(is_valid_matching(&m2), "maximum_matching returned an invalid matching");
1308 assert!(is_maximum_matching(&g, &m2), "maximum_matching returned a matching that is not maximum");
1309 assert_eq!(m1.is_perfect(), is_perfect_matching(&g, &m1), "greedy_matching incorrectly determined whether the matching is perfect");
1310 assert_eq!(m2.is_perfect(), is_perfect_matching(&g, &m2), "maximum_matching incorrectly determined whether the matching is perfect");
1311
1312 true
1313 }
1314 }
1315
1316 quickcheck! {
1317 // The ranks are probabilities,
1318 // as such they are positive and they should sum up to 1.
1319 fn test_page_rank_proba(gr: Graph<(), f32>) -> bool {
1320 if gr.node_count() == 0 {
1321 return true;
1322 }
1323 let tol = 1e-10;
1324 let ranks: Vec<f64> = page_rank(&gr, 0.85_f64, 5);
1325 let at_least_one_neg_rank = ranks.iter().any(|rank| *rank < 0.);
1326 let not_sumup_to_one = (ranks.iter().sum::<f64>() - 1.).abs() > tol;
1327 if at_least_one_neg_rank | not_sumup_to_one{
1328 return false;
1329 }
1330 true
1331 }
1332 }
1333
sum_flows<N, F: std::iter::Sum + Copy>( gr: &Graph<N, F>, flows: &[F], node: NodeIndex, dir: Direction, ) -> F1334 fn sum_flows<N, F: std::iter::Sum + Copy>(
1335 gr: &Graph<N, F>,
1336 flows: &[F],
1337 node: NodeIndex,
1338 dir: Direction,
1339 ) -> F {
1340 gr.edges_directed(node, dir)
1341 .map(|edge| flows[EdgeIndexable::to_index(&gr, edge.id())])
1342 .sum::<F>()
1343 }
1344
1345 quickcheck! {
1346 // 1. (Capacity)
1347 // The flows should be <= capacities
1348 // 2. (Flow conservation)
1349 // For every internal node (i.e a node different from the
1350 // source node and the destination (or sink) node), the sum
1351 // of incoming flows (i.e flows of incoming edges) is equal
1352 // to the sum of the outgoing flows (i.e flows of outgoing edges).
1353 // 3. (Maximum flow)
1354 // It is equal to the sum of the destination node incoming flows and
1355 // also the sum of the outgoing flows of the source node.
1356 fn test_ford_fulkerson_flows(gr: Graph<usize, u32>) -> bool {
1357 if gr.node_count() <= 1 || gr.edge_count() == 0 {
1358 return true;
1359 }
1360 let source = NodeIndex::from(0);
1361 let destination = NodeIndex::from(gr.node_count() as u32 / 2);
1362 let (max_flow, flows) = ford_fulkerson(&gr, source, destination);
1363 let capacity_constraint = flows
1364 .iter()
1365 .enumerate()
1366 .all(|(ix, flow)| flow <= gr.edge_weight(EdgeIndexable::from_index(&gr, ix)).unwrap());
1367 let flow_conservation_constraint = (0..gr.node_count()).all(|ix| {
1368 let node = NodeIndexable::from_index(&gr, ix);
1369 if (node != source) && (node != destination){
1370 sum_flows(&gr, &flows, node, Direction::Outgoing)
1371 == sum_flows(&gr, &flows, node, Direction::Incoming)
1372 } else {true}
1373 });
1374 let max_flow_constaint = (sum_flows(&gr, &flows, source, Direction::Outgoing) == max_flow)
1375 && (sum_flows(&gr, &flows, destination, Direction::Incoming) == max_flow);
1376 return capacity_constraint && flow_conservation_constraint && max_flow_constaint;
1377 }
1378 }
1379