Skip to content

Commit 19432a5

Browse files
committed
WIP: toposort + folding over subsystems
1 parent 48feafd commit 19432a5

File tree

5 files changed

+769
-183
lines changed

5 files changed

+769
-183
lines changed

packages/catlog/src/one/graph.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,20 @@ of [`FinGraph`].
194194
*/
195195
pub trait ColumnarFinGraph:
196196
ColumnarGraph<
197-
Vertices: FinSet<Elem = Self::V>,
198-
Edges: FinSet<Elem = Self::E>,
199-
Src: Column<Dom = Self::E, Cod = Self::V>,
200-
Tgt: Column<Dom = Self::E, Cod = Self::V>,
201-
>
197+
Vertices: FinSet<Elem = Self::V>,
198+
Edges: FinSet<Elem = Self::E>,
199+
Src: Column<Dom = Self::E, Cod = Self::V>,
200+
Tgt: Column<Dom = Self::E, Cod = Self::V>,
201+
>
202202
{
203203
}
204204

205205
/// A columnar graph with mutable columns.
206206
pub trait MutColumnarGraph:
207207
ColumnarGraph<
208-
Src: MutMapping<Dom = Self::E, Cod = Self::V>,
209-
Tgt: MutMapping<Dom = Self::E, Cod = Self::V>,
210-
>
208+
Src: MutMapping<Dom = Self::E, Cod = Self::V>,
209+
Tgt: MutMapping<Dom = Self::E, Cod = Self::V>,
210+
>
211211
{
212212
/// Variant of [`src_map`](ColumnarGraph::src_map) that returns a mutable
213213
/// reference.

packages/catlog/src/one/graph_algorithms.rs

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Algorithms on graphs.
22
3-
use std::collections::{HashSet, VecDeque};
3+
use std::collections::{HashMap, HashSet, VecDeque};
44
use std::hash::Hash;
55

66
use super::graph::*;
@@ -168,6 +168,136 @@ where
168168
result
169169
}
170170

171+
fn out_neighbors<G>(graph: &G, v: &G::V) -> impl Iterator<Item = G::V>
172+
where
173+
G: FinGraph,
174+
G::V: Hash,
175+
{
176+
graph.out_edges(v).map(|e| graph.tgt(&e))
177+
}
178+
179+
fn in_neighbors<G>(graph: &G, v: &G::V) -> impl Iterator<Item = G::V>
180+
where
181+
G: FinGraph,
182+
G::V: Hash,
183+
{
184+
graph.in_edges(v).map(|e| graph.src(&e))
185+
}
186+
187+
#[derive(Clone, Debug)]
188+
struct VisitMap {
189+
visited: Vec<bool>,
190+
}
191+
192+
impl VisitMap {
193+
fn visit(&mut self, idx: usize) -> bool {
194+
let previous = self.visited[idx];
195+
self.visited[idx] = true;
196+
!previous
197+
}
198+
199+
fn is_visited(&self, idx: usize) -> bool {
200+
self.visited[idx]
201+
}
202+
}
203+
204+
#[derive(Clone, Debug)]
205+
pub struct Dfs<G>
206+
where
207+
G: FinGraph,
208+
G::V: Hash,
209+
{
210+
//
211+
pub stack: Vec<G::V>,
212+
//
213+
pub discovered: VisitMap,
214+
}
215+
216+
// TODO
217+
// 1. replace String with Cycle error
218+
/** Computes a topological sorting for a given graph.
219+
220+
This algorithm was borrowed from `petgraph`.
221+
*/
222+
pub fn toposort<G>(graph: &G) -> Result<Vec<G::V>, String>
223+
where
224+
G: FinGraph,
225+
G::V: Hash + std::fmt::Debug,
226+
{
227+
// XXX dont clone
228+
let n = graph.vertices().collect::<Vec<_>>().len();
229+
let mut discovered = VisitMap {
230+
visited: vec![false; n.clone()],
231+
};
232+
let mut finished = VisitMap {
233+
visited: vec![false; n.clone()],
234+
};
235+
let mut finish_stack: Vec<G::V> = Vec::new();
236+
let mut stack = Vec::new();
237+
238+
// we shouldn't need to do this
239+
let gmap: HashMap<_, _> = HashMap::from_iter(graph.vertices().enumerate().map(|(k, v)| (v, k)));
240+
241+
for (idx, v) in graph.vertices().enumerate() {
242+
if discovered.is_visited(idx) {
243+
continue;
244+
}
245+
stack.push(v);
246+
while let Some(nx) = stack.clone().last() {
247+
if discovered.visit(gmap[&nx]) {
248+
for succ in out_neighbors(graph, &nx) {
249+
if succ == *nx {
250+
return Err("self cycle".to_owned());
251+
}
252+
if !discovered.is_visited(gmap[&succ]) {
253+
stack.push(succ);
254+
}
255+
}
256+
} else {
257+
stack.pop();
258+
if finished.visit(gmap[&nx]) {
259+
finish_stack.push(nx.clone());
260+
}
261+
}
262+
}
263+
}
264+
finish_stack.reverse();
265+
266+
// dfs.reset(g);
267+
let mut discovered = VisitMap {
268+
visited: vec![false; n.clone()],
269+
};
270+
for i in &finish_stack {
271+
// dfs.move_to(i);
272+
stack.clear();
273+
stack.push(i.clone());
274+
//
275+
let mut cycle = false;
276+
while let Some(j) = {
277+
let mut out = None;
278+
while let Some(node) = stack.pop() {
279+
if discovered.visit(gmap[&node]) {
280+
for succ in in_neighbors(graph, &node) {
281+
if !discovered.is_visited(gmap[&succ]) {
282+
stack.push(succ);
283+
}
284+
}
285+
out = Some(node);
286+
break;
287+
}
288+
}
289+
out
290+
} {
291+
if cycle {
292+
return Err(format!("cycle detected involving node {:#?}", j).to_owned());
293+
}
294+
cycle = true;
295+
}
296+
}
297+
298+
Ok(finish_stack)
299+
}
300+
171301
#[cfg(test)]
172302
mod tests {
173303
use super::GraphElem::*;
@@ -231,4 +361,46 @@ mod tests {
231361
let g = SkelGraph::cycle(1);
232362
assert_eq!(spec_order_all(&g), vec![Vertex(0), Edge(0)]);
233363
}
364+
365+
#[test]
366+
fn toposorting() {
367+
let mut g = SkelGraph::path(5);
368+
assert_eq!(toposort(&g), Ok(vec![0, 1, 2, 3, 4]));
369+
370+
let mut g = SkelGraph::path(3);
371+
g.add_vertices(1);
372+
let _ = g.add_edge(2, 3);
373+
let _ = g.add_edge(3, 0);
374+
assert_eq!(toposort(&g), Err("cycle detected involving node 3".to_owned()));
375+
376+
let g = SkelGraph::triangle();
377+
assert_eq!(toposort(&g), Ok(vec![0, 1, 2]));
378+
379+
let mut g = SkelGraph::path(4);
380+
g.add_vertices(2);
381+
let _ = g.add_edge(1, 4);
382+
let _ = g.add_edge(4, 3);
383+
let _ = g.add_edge(5, 2);
384+
assert_eq!(toposort(&g), Ok(vec![5, 0, 1, 2, 4, 3]));
385+
386+
let mut g: HashGraph<u8, &str> = Default::default();
387+
g.add_vertices(vec![0, 1, 2, 3, 4, 5]);
388+
g.add_edge("0-1", 0, 1);
389+
g.add_edge("1-2", 1, 2);
390+
g.add_edge("2-3", 2, 3);
391+
g.add_edge("1-4", 1, 4);
392+
g.add_edge("4-3", 4, 3);
393+
g.add_edge("5-2", 5, 2);
394+
// TODO non-deterministic
395+
// assert_eq!(toposort(&g), Ok(vec![5, 0, 1, 2, 4, 3]));
396+
}
397+
398+
#[test]
399+
#[test]
400+
fn neighbors() {
401+
let g = SkelGraph::triangle();
402+
let out_neighbors = &g.out_edges(&1).map(|e| g.tgt(&e)).collect::<Vec<_>>();
403+
// dbg!(&out_neighbors);
404+
assert!(true);
405+
}
234406
}

0 commit comments

Comments
 (0)