rust_sugiyama/
lib.rs

1use std::collections::HashMap;
2
3use algorithm::{Edge, Vertex};
4
5use configure::Config;
6use log::info;
7use petgraph::{graph::NodeIndex, stable_graph::StableDiGraph};
8
9mod algorithm;
10pub mod configure;
11mod util;
12
13type Layout = (Vec<(usize, (f64, f64))>, f64, f64);
14type Layouts<T> = Vec<(Vec<(T, (f64, f64))>, f64, f64)>;
15
16/// Creates a graph layout from edges, which are given as a `&[(u32, u32)]`.
17///
18/// The layouts are returned as a list of disjoint subgraphs containing the
19/// subgraph layout, the width, and the height. The layout of a subgraph is a
20/// list of the vertex number (as specified in the edges) and its x and y
21/// position respectively.
22pub fn from_edges(edges: &[(u32, u32)], config: &Config) -> Layouts<usize> {
23    info!(target: "initializing", "Creating new layout from edges, containing {} edges", edges.len());
24    let graph = StableDiGraph::from_edges(edges);
25    algorithm::start(graph, config)
26}
27
28/// Creates a graph layout from a preexisting [StableDiGraph<V, E>].
29///
30/// The layouts are returned as a list of disjoint subgraphs containing the
31/// subgraph layout, the width, and the height. The layout of a subgraph is a
32/// list of the [NodeIndex] and its x and y position respectively.
33pub fn from_graph<V, E>(
34    graph: &StableDiGraph<V, E>,
35    vertex_size: &impl Fn(NodeIndex, &V) -> (f64, f64),
36    config: &Config,
37) -> Layouts<NodeIndex> {
38    info!(target: "initializing", 
39        "Creating new layout from existing graph, containing {} vertices and {} edges.", 
40        graph.node_count(), 
41        graph.edge_count());
42
43    let graph = graph.map(
44        |id, v| Vertex::new(id.index(), vertex_size(id, v)),
45        |_, _| Edge::default(),
46    );
47
48    algorithm::start(graph, config)
49        .into_iter()
50        .map(|(l, w, h)| {
51            (
52                l.into_iter()
53                    .map(|(id, coords)| (NodeIndex::from(id as u32), coords))
54                    .collect(),
55                w,
56                h,
57            )
58        })
59        .collect()
60}
61
62/// Creates a graph layout from `&[(u32, (f64, f64))]` (vertices as vertex id
63/// and vertex size) and `&[(u32, u32)]` (edges).
64///
65/// The layouts are returned as a list of disjoint subgraphs containing the
66/// subgraph layout, the width, and the height. The layout of a subgraph is a
67/// list of the vertex number and its x and y position respectively.
68///
69/// # Panics
70///
71/// Panics if `edges` contain vertices which are not contained in `vertices`
72pub fn from_vertices_and_edges<'a>(
73    vertices: &'a [(u32, (f64, f64))],
74    edges: &'a [(u32, u32)],
75    config: &Config,
76) -> Layouts<usize> {
77    info!(target: "initializing", 
78        "Creating new layout from existing graph, containing {} vertices and {} edges.", 
79        vertices.len(), 
80        edges.len());
81
82    let mut graph = StableDiGraph::new();
83    let mut id_map = HashMap::new();
84    for &(v, size) in vertices {
85        let id = graph.add_node(Vertex::new(v as usize, size));
86        id_map.insert(v, id);
87    }
88
89    for (tail, head) in edges {
90        graph.add_edge(
91            *id_map.get(tail).unwrap(),
92            *id_map.get(head).unwrap(),
93            Edge::default(),
94        );
95    }
96
97    algorithm::start(graph, config)
98}
99
100#[test]
101fn run_algo_empty_graph() {
102    let edges = [];
103    let g = from_edges(&edges, &Config::default());
104    assert!(g.is_empty());
105}
106
107#[cfg(test)]
108mod benchmark {
109    use crate::configure::Config;
110
111    use super::from_edges;
112
113    #[test]
114    fn r_100() {
115        let edges = graph_generator::RandomLayout::new(100)
116            .build_edges()
117            .into_iter()
118            .map(|(r, l)| (r as u32, l as u32))
119            .collect::<Vec<(u32, u32)>>();
120        let start = std::time::Instant::now();
121        let _ = from_edges(&edges, &Config::default());
122        println!("Random 100 edges: {}ms", start.elapsed().as_millis());
123    }
124
125    #[test]
126    fn r_1000() {
127        let edges = graph_generator::RandomLayout::new(1000)
128            .build_edges()
129            .into_iter()
130            .map(|(r, l)| (r as u32, l as u32))
131            .collect::<Vec<(u32, u32)>>();
132        let start = std::time::Instant::now();
133        let _ = from_edges(&edges, &Config::default());
134        println!("Random 1000 edges: {}ms", start.elapsed().as_millis());
135    }
136
137    #[test]
138    fn r_2000() {
139        let edges = graph_generator::RandomLayout::new(2000).build_edges();
140        let start = std::time::Instant::now();
141        let _ = from_edges(&edges, &Config::default());
142        println!("Random 2000 edges: {}ms", start.elapsed().as_millis());
143    }
144
145    #[test]
146    fn r_4000() {
147        let edges = graph_generator::RandomLayout::new(4000).build_edges();
148        let start = std::time::Instant::now();
149        let _ = from_edges(&edges, &Config::default());
150        println!("Random 4000 edges: {}ms", start.elapsed().as_millis());
151    }
152
153    #[test]
154    fn l_1000_2() {
155        let n = 1000;
156        let e = 2;
157        let edges = graph_generator::GraphLayout::new_from_num_nodes(n, e).build_edges();
158        let start = std::time::Instant::now();
159        let _ = from_edges(&edges, &Config::default());
160        println!(
161            "{n} nodes, {e} edges per node: {}ms",
162            start.elapsed().as_millis()
163        );
164    }
165
166    #[test]
167    fn l_2000_2() {
168        let n = 2000;
169        let e = 2;
170        let edges = graph_generator::GraphLayout::new_from_num_nodes(n, e).build_edges();
171        let start = std::time::Instant::now();
172        let _ = from_edges(&edges, &Config::default());
173        println!(
174            "{n} nodes, {e} edges per node: {}ms",
175            start.elapsed().as_millis()
176        );
177    }
178
179    #[test]
180    fn l_4000_2() {
181        let n = 4000;
182        let e = 2;
183        let edges = graph_generator::GraphLayout::new_from_num_nodes(n, e).build_edges();
184        let start = std::time::Instant::now();
185        let _ = from_edges(&edges, &Config::default());
186        println!(
187            "{n} nodes, {e} edges per node: {}ms",
188            start.elapsed().as_millis()
189        );
190    }
191
192    #[test]
193    fn l_8000_2() {
194        let n = 8000;
195        let e = 2;
196        let edges = graph_generator::GraphLayout::new_from_num_nodes(n, e).build_edges();
197        let start = std::time::Instant::now();
198        let _ = from_edges(&edges, &Config::default());
199        println!(
200            "{n} nodes, {e} edges per node: {}ms",
201            start.elapsed().as_millis()
202        );
203    }
204}
205
206#[cfg(test)]
207mod check_visuals {
208
209    use crate::{
210        configure::{Config, RankingType},
211        from_vertices_and_edges,
212    };
213
214    use super::from_edges;
215
216    #[test]
217    fn test_no_dummies() {
218        let vertices = [
219            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
220        ];
221        let edges = [
222            (1, 2),
223            (1, 3),
224            (2, 5),
225            (2, 16),
226            (4, 5),
227            (4, 6),
228            (4, 7),
229            (6, 17),
230            (6, 3),
231            (6, 18),
232            (8, 3),
233            (8, 9),
234            (8, 10),
235            (9, 16),
236            (9, 7),
237            (9, 19),
238            (11, 7),
239            (11, 12),
240            (11, 13),
241            (12, 18),
242            (12, 10),
243            (12, 20),
244            (14, 10),
245            (14, 15),
246            (15, 19),
247            (15, 13),
248        ];
249        let _ = from_vertices_and_edges(
250            &vertices
251                .into_iter()
252                .map(|v| (v, (0.0, 0.0)))
253                .collect::<Vec<_>>(),
254            &edges,
255            &Config {
256                dummy_vertices: true,
257                ..Default::default()
258            },
259        );
260    }
261    #[test]
262    fn verify_looks_good() {
263        // NOTE: This test might fail eventually, since the order of lements in a row canot be guaranteed;
264        let edges = [
265            (0, 1),
266            (1, 2),
267            (2, 3),
268            (2, 4),
269            (3, 5),
270            (3, 6),
271            (3, 7),
272            (3, 8),
273            (4, 5),
274            (4, 6),
275            (4, 7),
276            (4, 8),
277            (5, 9),
278            (6, 9),
279            (7, 9),
280            (8, 9),
281        ];
282        let (layout, width, height) = &mut from_edges(&edges, &Config::default())[0];
283        layout.sort_by(|a, b| a.0.cmp(&b.0));
284
285        assert_eq!(*width, 4.0);
286        assert_eq!(*height, 6.0);
287        println!("{:?}", layout);
288    }
289
290    #[test]
291    fn root_vertices_on_top_disabled() {
292        let edges = [(1, 0), (2, 1), (3, 0), (4, 0)];
293        let layout = from_edges(&edges, &Config::default());
294        for (id, (_, y)) in layout[0].0.clone() {
295            if id == 2 {
296                assert_eq!(y, 0.0);
297            } else if id == 3 || id == 4 || id == 1 {
298                assert_eq!(y, 10.0);
299            } else {
300                assert_eq!(y, 20.0)
301            }
302        }
303    }
304
305    #[test]
306    fn check_coords_2() {
307        let edges = [
308            (0, 1),
309            (0, 2),
310            (0, 3),
311            (1, 4),
312            (4, 5),
313            (5, 6),
314            (2, 6),
315            (3, 6),
316            (3, 7),
317            (3, 8),
318            (3, 9),
319        ];
320        let layout = from_edges(&edges, &Config::default());
321        println!("{:?}", layout);
322    }
323
324    #[test]
325    fn hlrs_ping() {
326        let _nodes = [
327            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
328        ];
329        let edges = [
330            (1, 2),
331            (1, 4),
332            (1, 5),
333            (1, 3),
334            (2, 4),
335            (2, 5),
336            (3, 9),
337            (3, 10),
338            (3, 8),
339            (4, 6),
340            (4, 9),
341            (4, 8),
342            (5, 6),
343            (5, 10),
344            (5, 8),
345            (6, 7),
346            (7, 9),
347            (7, 10),
348            (8, 14),
349            (8, 15),
350            (8, 13),
351            (9, 11),
352            (9, 14),
353            (9, 13),
354            (10, 11),
355            (10, 15),
356            (10, 13),
357            (11, 12),
358            (12, 14),
359            (12, 15),
360            (13, 18),
361            (13, 19),
362            (13, 20),
363            (14, 16),
364            (14, 18),
365            (14, 20),
366            (15, 16),
367            (15, 19),
368            (15, 20),
369            (16, 17),
370            (17, 18),
371            (17, 19),
372            (18, 21),
373            (19, 21),
374        ]
375        .into_iter()
376        .map(|(t, h)| (t - 1, h - 1))
377        .collect::<Vec<_>>();
378
379        let layout = from_edges(
380            &edges,
381            &Config {
382                ranking_type: RankingType::Up,
383                ..Default::default()
384            },
385        );
386        println!("{layout:?}");
387    }
388
389    #[test]
390    fn run_algo_empty_graph() {
391        use super::from_edges;
392        let edges = [];
393        let g = from_edges(&edges, &Config::default());
394        assert!(g.is_empty());
395    }
396
397    #[test]
398    fn run_algo_with_duplicate_edges() {
399        let edges = [
400            (1, 2),
401            (2, 5),
402            (2, 6),
403            (2, 3),
404            (3, 4),
405            (4, 3),
406            (4, 8),
407            (8, 4),
408            (8, 7),
409            (3, 7),
410            (6, 7),
411            (7, 6),
412            (5, 6),
413            (5, 1),
414        ];
415
416        let layout = from_edges(&edges, &Config::default());
417        println!("{layout:?}");
418    }
419}