Skip to content

Commit 30d36cb

Browse files
committed
2 parents 1bf66aa + 70e8342 commit 30d36cb

5 files changed

Lines changed: 345 additions & 3 deletions

File tree

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
liberapay: octavonce

benches/benchmark.rs

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ fn bench_create(c: &mut Criterion) {
3333
// includes benches for :
3434
// 1. dfs(&self) -> Dfs<T>
3535
// 2. bfs(&self) -> Bfs<T>
36-
// 3. vertices(&self) -> VertexIter
37-
// 4. roots(&self) -> VertexIter
36+
// 3. topo(&self) -> Topo<T>
37+
// 4. vertices(&self) -> VertexIter
38+
// 5. roots(&self) -> VertexIter
3839
fn bench_iterators(c: &mut Criterion) {
3940
c.bench_function("dfs_10", |b| {
4041
let mut graph: Graph<usize> = Graph::new();
@@ -225,6 +226,100 @@ fn bench_iterators(c: &mut Criterion) {
225226
})
226227
});
227228

229+
c.bench_function("topo_10", |b| {
230+
let mut graph: Graph<usize> = Graph::new();
231+
let mut vertices = vec![];
232+
233+
let mut v1 = graph.add_vertex(0);
234+
235+
for i in 1..=10 {
236+
let v2 = graph.add_vertex(i);
237+
graph.add_edge(&v1, &v2);
238+
v1 = v2.clone();
239+
}
240+
b.iter(|| {
241+
for v in graph.topo() {
242+
vertices.push(v);
243+
}
244+
})
245+
});
246+
247+
c.bench_function("topo_100", |b| {
248+
let mut graph: Graph<usize> = Graph::new();
249+
let mut vertices = vec![];
250+
251+
let mut v1 = graph.add_vertex(0);
252+
253+
for i in 1..=100 {
254+
let v2 = graph.add_vertex(i);
255+
graph.add_edge(&v1, &v2);
256+
v1 = v2.clone();
257+
}
258+
259+
b.iter(|| {
260+
for v in graph.topo() {
261+
vertices.push(v);
262+
}
263+
})
264+
});
265+
266+
c.bench_function("topo_500", |b| {
267+
let mut graph: Graph<usize> = Graph::new();
268+
let mut vertices = vec![];
269+
270+
let mut v1 = graph.add_vertex(0);
271+
272+
for i in 1..=500 {
273+
let v2 = graph.add_vertex(i);
274+
graph.add_edge(&v1, &v2);
275+
v1 = v2.clone();
276+
}
277+
278+
b.iter(|| {
279+
for v in graph.topo() {
280+
vertices.push(v);
281+
}
282+
})
283+
});
284+
285+
c.bench_function("topo_1000", |b| {
286+
let mut graph: Graph<usize> = Graph::new();
287+
let mut vertices = vec![];
288+
289+
let mut v1 = graph.add_vertex(0);
290+
291+
for i in 1..=1000 {
292+
let v2 = graph.add_vertex(i);
293+
graph.add_edge(&v1, &v2);
294+
v1 = v2.clone();
295+
}
296+
297+
b.iter(|| {
298+
for v in graph.topo() {
299+
vertices.push(v);
300+
}
301+
})
302+
});
303+
#[cfg(feature = "sbench")]
304+
c.bench_function("topo_m", |b| {
305+
let mut graph: Graph<usize> = Graph::new();
306+
let mut vertices = vec![];
307+
308+
let mut v1 = graph.add_vertex(0);
309+
310+
for i in 1..=10_000_000 {
311+
let v2 = graph.add_vertex(i);
312+
graph.add_edge(&v1, &v2);
313+
v1 = v2.clone();
314+
}
315+
316+
b.iter(|| {
317+
for v in graph.topo() {
318+
vertices.push(v);
319+
}
320+
})
321+
});
322+
228323
c.bench_function("vertices_10", |b| {
229324
let mut graph: Graph<usize> = Graph::new();
230325
let mut vertices = vec![];

src/graph.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2019 Octavian Oncescu
22

33
use crate::edge::Edge;
4-
use crate::iterators::{Bfs, Dfs, VertexIter};
4+
use crate::iterators::{Bfs, Dfs, Topo, VertexIter};
55
use crate::vertex_id::VertexId;
66
use hashbrown::{HashMap, HashSet};
77

@@ -1039,6 +1039,52 @@ impl<T> Graph<T> {
10391039
Bfs::new(self)
10401040
}
10411041

1042+
/// Returns an iterator over the vertices
1043+
/// of the graph which follows a DFS based
1044+
/// topological order (Kahn's algorithm).
1045+
///
1046+
/// Topological sorting is not possible for
1047+
/// graphs which contain a cycle. You may
1048+
/// use topo.is_cylic() == false to verify
1049+
/// that your graph is a DAG.
1050+
///
1051+
/// If you attempt to use a topological
1052+
/// order without confirming that your graph
1053+
/// is a DAG, you may encounter a panic!().
1054+
///
1055+
/// The panic!() will be encountered when
1056+
/// the iterator detects that there are no
1057+
/// more vertices to visit, but all vertices
1058+
/// have not been visited.
1059+
///
1060+
/// ## Example
1061+
/// ```rust
1062+
/// # #[macro_use] extern crate graphlib; fn main() {
1063+
/// use graphlib::Graph;
1064+
/// use std::collections::HashSet;
1065+
///
1066+
/// let mut graph: Graph<usize> = Graph::new();
1067+
///
1068+
/// let v1 = graph.add_vertex(1);
1069+
/// let v2 = graph.add_vertex(2);
1070+
/// let v3 = graph.add_vertex(3);
1071+
/// let v4 = graph.add_vertex(4);
1072+
///
1073+
/// graph.add_edge(&v1, &v2).unwrap();
1074+
/// graph.add_edge(&v2, &v3).unwrap();
1075+
/// graph.add_edge(&v3, &v4).unwrap();
1076+
///
1077+
/// let mut topo = graph.topo();
1078+
///
1079+
/// assert_eq!(topo.next(), Some(&v1));
1080+
/// assert_eq!(topo.next(), Some(&v2));
1081+
/// assert!(set![&v3, &v4] == topo.collect());
1082+
/// # }
1083+
/// ```
1084+
pub fn topo(&self) -> Topo<'_, T> {
1085+
Topo::new(self)
1086+
}
1087+
10421088
/// Creates a file with the dot representation of the graph.
10431089
/// This method requires the `dot` feature.
10441090
///

src/iterators/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
mod bfs;
44
mod dfs;
5+
mod topo;
56
mod vertices;
67

78
pub use bfs::*;
89
pub use dfs::*;
10+
pub use topo::*;
911
pub use vertices::*;

src/iterators/topo.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright 2019 Gary Pennington
2+
3+
use crate::graph::Graph;
4+
use crate::vertex_id::VertexId;
5+
6+
use hashbrown::HashMap;
7+
8+
#[cfg(feature = "no_std")]
9+
extern crate alloc;
10+
#[cfg(feature = "no_std")]
11+
use alloc::vec::Vec;
12+
13+
const PANIC_MSG: &str = "graph contains cycle(s)";
14+
15+
#[derive(Debug)]
16+
/// Topological Iterator
17+
pub struct Topo<'a, T> {
18+
/// The Graph being iterated.
19+
iterable: &'a Graph<T>,
20+
/// Processed vertices
21+
vertices: Vec<&'a VertexId>,
22+
/// Working set of vertices
23+
roots: Vec<&'a VertexId>,
24+
/// Working set of vertex edges
25+
vertex_edges: HashMap<&'a VertexId, usize>,
26+
}
27+
28+
impl<'a, T> Topo<'a, T> {
29+
pub fn new(graph: &'a Graph<T>) -> Topo<'_, T> {
30+
let mut roots = vec![];
31+
for node in graph.roots() {
32+
roots.push(node);
33+
}
34+
35+
Topo {
36+
iterable: graph,
37+
vertices: vec![],
38+
roots,
39+
vertex_edges: HashMap::new(),
40+
}
41+
}
42+
43+
/// Processes the next vertex.
44+
///
45+
/// Will return None if:
46+
///
47+
/// * No vertices are left.
48+
fn process_vertex(&mut self, check_cyclic: bool) -> Option<&'a VertexId> {
49+
match self.roots.pop() {
50+
Some(node) => {
51+
self.vertices.push(node);
52+
for out in self.iterable.out_neighbors(node) {
53+
let count = match self.vertex_edges.get_mut(out) {
54+
Some(count) => count,
55+
None => {
56+
self.vertex_edges
57+
.insert(out, self.iterable.in_neighbors_count(out));
58+
self.vertex_edges.get_mut(out).unwrap()
59+
}
60+
};
61+
if *count == 1 {
62+
self.roots.push(out);
63+
} else {
64+
*count -= 1;
65+
}
66+
}
67+
Some(node)
68+
},
69+
None => {
70+
if check_cyclic && self.vertices.len() != self.iterable.vertex_count() {
71+
panic!(PANIC_MSG);
72+
}
73+
None
74+
},
75+
}
76+
}
77+
78+
///
79+
/// Returns true if the iterated graph has a cycle.
80+
///
81+
/// # Warning
82+
///
83+
/// It is a logic error to use this iterator after calling this function.
84+
pub fn is_cyclic(&mut self) -> bool {
85+
//Search until an answer is found.
86+
while self.process_vertex(false).is_some() {}
87+
88+
self.vertices.len() != self.iterable.vertex_count()
89+
}
90+
}
91+
92+
impl<'a, T> Iterator for Topo<'a, T> {
93+
type Item = &'a VertexId;
94+
95+
fn size_hint(&self) -> (usize, Option<usize>) {
96+
let remaining = self.iterable.vertex_count() - self.vertices.len();
97+
98+
(0, Some(remaining))
99+
}
100+
fn next(&mut self) -> Option<Self::Item> {
101+
(0..self.size_hint().1.unwrap())
102+
.filter_map(move |_| self.process_vertex(true))
103+
.next()
104+
}
105+
}
106+
107+
#[cfg(test)]
108+
mod tests {
109+
use super::*;
110+
111+
#[test]
112+
fn is_not_cyclic() {
113+
let mut graph: Graph<usize> = Graph::new();
114+
115+
let v1 = graph.add_vertex(1);
116+
let v2 = graph.add_vertex(2);
117+
let v3 = graph.add_vertex(3);
118+
let v4 = graph.add_vertex(4);
119+
let v5 = graph.add_vertex(5);
120+
let v6 = graph.add_vertex(6);
121+
let v7 = graph.add_vertex(7);
122+
123+
graph.add_edge(&v1, &v4).unwrap();
124+
graph.add_edge(&v2, &v4).unwrap();
125+
graph.add_edge(&v2, &v5).unwrap();
126+
graph.add_edge(&v3, &v5).unwrap();
127+
graph.add_edge(&v4, &v6).unwrap();
128+
graph.add_edge(&v4, &v7).unwrap();
129+
graph.add_edge(&v5, &v6).unwrap();
130+
graph.add_edge(&v6, &v7).unwrap();
131+
132+
let mut topo = graph.topo();
133+
134+
assert!(!topo.is_cyclic());
135+
}
136+
137+
#[test]
138+
fn is_cyclic() {
139+
let mut graph: Graph<usize> = Graph::new();
140+
141+
let v1 = graph.add_vertex(1);
142+
let v2 = graph.add_vertex(2);
143+
let v3 = graph.add_vertex(3);
144+
let v4 = graph.add_vertex(4);
145+
let v5 = graph.add_vertex(5);
146+
let v6 = graph.add_vertex(6);
147+
let v7 = graph.add_vertex(7);
148+
149+
graph.add_edge(&v1, &v4).unwrap();
150+
graph.add_edge(&v2, &v4).unwrap();
151+
graph.add_edge(&v2, &v5).unwrap();
152+
graph.add_edge(&v3, &v5).unwrap();
153+
graph.add_edge(&v4, &v6).unwrap();
154+
graph.add_edge(&v4, &v7).unwrap();
155+
graph.add_edge(&v5, &v6).unwrap();
156+
graph.add_edge(&v6, &v7).unwrap();
157+
graph.add_edge(&v7, &v2).unwrap();
158+
159+
let mut topo = graph.topo();
160+
161+
assert!(topo.is_cyclic());
162+
}
163+
164+
#[test]
165+
#[should_panic(expected = "graph contains cycle(s)")]
166+
fn is_cyclic_and_panic() {
167+
let mut graph: Graph<usize> = Graph::new();
168+
169+
let v1 = graph.add_vertex(1);
170+
let v2 = graph.add_vertex(2);
171+
let v3 = graph.add_vertex(3);
172+
let v4 = graph.add_vertex(4);
173+
let v5 = graph.add_vertex(5);
174+
let v6 = graph.add_vertex(6);
175+
let v7 = graph.add_vertex(7);
176+
177+
graph.add_edge(&v1, &v4).unwrap();
178+
graph.add_edge(&v2, &v4).unwrap();
179+
graph.add_edge(&v2, &v5).unwrap();
180+
graph.add_edge(&v3, &v5).unwrap();
181+
graph.add_edge(&v4, &v6).unwrap();
182+
graph.add_edge(&v4, &v7).unwrap();
183+
graph.add_edge(&v5, &v6).unwrap();
184+
graph.add_edge(&v6, &v7).unwrap();
185+
graph.add_edge(&v7, &v2).unwrap();
186+
187+
let mut topo = graph.topo();
188+
189+
topo.next();
190+
topo.next();
191+
topo.next();
192+
topo.next();
193+
topo.next();
194+
topo.next();
195+
topo.next();
196+
}
197+
198+
}

0 commit comments

Comments
 (0)