fast_paths/
input_graph.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20use std::cmp;
21use std::fmt;
22use std::fs::File;
23use std::io::{BufRead, BufReader, BufWriter, Write};
24
25#[cfg(test)]
26use rand::rngs::StdRng;
27#[cfg(test)]
28use rand::Rng;
29
30use serde::{Deserialize, Serialize};
31
32use crate::constants::NodeId;
33use crate::constants::Weight;
34
35#[derive(Serialize, Deserialize, Clone)]
36pub struct InputGraph {
37    edges: Vec<Edge>,
38    num_nodes: usize,
39    frozen: bool,
40}
41
42impl InputGraph {
43    pub fn new() -> Self {
44        InputGraph {
45            edges: Vec::new(),
46            num_nodes: 0,
47            frozen: false,
48        }
49    }
50
51    /// Builds a random input graph, mostly used for testing purposes
52    #[cfg(test)]
53    pub fn random(rng: &mut StdRng, num_nodes: usize, mean_degree: f32) -> Self {
54        InputGraph::build_random_graph(rng, num_nodes, mean_degree)
55    }
56
57    /// Reads an input graph from a text file, using the following format:
58    /// a <from> <to> <weight>
59    /// where <from>,<to> and <weight> must be >= 0.
60    /// All other lines are ignored.
61    /// This function is compatible to DIMACS files, but consider using InputGraph::from_dimacs
62    /// for these instead.
63    /// Mostly used for performance testing.
64    pub fn from_file(filename: &str) -> Self {
65        InputGraph::read_from_file(filename)
66    }
67
68    /// Writes the input graph to a text file, using the following format:
69    /// a <from> <to> <weight>
70    /// Mostly used for performance testing.
71    pub fn to_file(&self, filename: &str) -> Result<(), std::io::Error> {
72        let mut f = BufWriter::new(File::create(filename)?);
73        for edge in self.get_edges() {
74            writeln!(f, "a {} {} {}", edge.from, edge.to, edge.weight)?;
75        }
76        Ok(())
77    }
78
79    /// Reads an input graph from a text file, using the DIMACS format:
80    /// http://users.diag.uniroma1.it/challenge9/format.shtml#graph
81    ///
82    /// * empty lines and lines starting with 'c' are ignored:
83    ///   c <comment>
84    /// * the 'problem line' states the number of nodes and edges of the graph:
85    ///   it must be written before any arc line
86    ///   p <num_nodes> <num_edges>
87    /// * there is one line per (directed) edge:
88    ///   a <from> <to> <weight>
89    ///   where <from> and <to> must be >= 1 and <weight> must be >= 0
90    ///   Note that here, in contrast to InputGraph::from_file, the node IDs are 1-based, not
91    ///   0-based. They will be converted to 0-based IDs internally.
92    ///
93    /// Mostly used for performance testing.
94    pub fn from_dimacs_file(filename: &str) -> Self {
95        InputGraph::read_from_dimacs(filename)
96    }
97
98    /// Writes the input graph to a text file, using the DIMACS format:
99    /// p sp <num_nodes> <num_edges>
100    /// a <from> <to> <weight>
101    /// Note that <from> and <to> are 1-based, so they are incremented by one compared to the
102    /// node IDs used by internally.
103    /// Mostly used for performance testing.
104    pub fn to_dimacs_file(&self, filename: &str) -> Result<(), std::io::Error> {
105        let mut f = BufWriter::new(File::create(filename)?);
106        writeln!(f, "p sp {} {}", self.get_num_nodes(), self.get_num_edges())?;
107        for edge in self.get_edges() {
108            writeln!(f, "a {} {} {}", edge.from + 1, edge.to + 1, edge.weight)?;
109        }
110        Ok(())
111    }
112
113    pub fn add_edge(&mut self, from: NodeId, to: NodeId, weight: Weight) -> usize {
114        self.do_add_edge(from, to, weight, false)
115    }
116
117    pub fn add_edge_bidir(&mut self, from: NodeId, to: NodeId, weight: Weight) -> usize {
118        self.do_add_edge(from, to, weight, true)
119    }
120
121    pub fn get_edges(&self) -> &Vec<Edge> {
122        self.check_frozen();
123        &self.edges
124    }
125
126    pub fn get_num_nodes(&self) -> usize {
127        self.check_frozen();
128        self.num_nodes
129    }
130
131    pub fn get_num_edges(&self) -> usize {
132        self.check_frozen();
133        self.edges.len()
134    }
135
136    pub fn freeze(&mut self) {
137        if self.frozen {
138            panic!("Input graph is already frozen");
139        }
140        self.sort();
141        self.remove_duplicate_edges();
142        self.frozen = true;
143    }
144
145    pub fn thaw(&mut self) {
146        self.frozen = false;
147    }
148
149    fn sort(&mut self) {
150        self.edges.sort_unstable_by(|a, b| {
151            a.from
152                .cmp(&b.from)
153                .then(a.to.cmp(&b.to))
154                .then(a.weight.cmp(&&b.weight))
155        });
156    }
157
158    fn remove_duplicate_edges(&mut self) {
159        // we go through (already sorted!) list of edges and remove duplicates
160        let len_before = self.edges.len();
161        self.edges.dedup_by(|a, b| a.from == b.from && a.to == b.to);
162        if len_before != self.edges.len() {
163            warn!(
164                "There were {} duplicate edges, only the ones with lowest weight were kept",
165                len_before - self.edges.len()
166            );
167        }
168    }
169
170    pub fn unit_test_output_string(&self) -> String {
171        return self
172            .edges
173            .iter()
174            .map(|e| e.unit_test_output_string())
175            .collect::<Vec<String>>()
176            .join("\n")
177            + "\n";
178    }
179
180    fn check_frozen(&self) {
181        if !self.frozen {
182            panic!("You need to call freeze() before using the input graph")
183        }
184    }
185
186    fn do_add_edge(&mut self, from: NodeId, to: NodeId, weight: Weight, bidir: bool) -> usize {
187        if self.frozen {
188            panic!("Graph is frozen already, for further changes first use thaw()");
189        }
190        if from == to {
191            warn!(
192                "Loop edges are not allowed. Skipped edge! from: {}, to: {}, weight: {}",
193                from, to, weight
194            );
195            return 0;
196        }
197        if weight < 1 {
198            warn!(
199                "Zero weight edges are not allowed. Skipped edge! from: {}, to: {}, weight: {}",
200                from, to, weight
201            );
202            return 0;
203        }
204        self.num_nodes = cmp::max(self.num_nodes, cmp::max(from, to) + 1);
205        self.edges.push(Edge::new(from, to, weight));
206        if bidir {
207            self.edges.push(Edge::new(to, from, weight));
208        }
209        if bidir {
210            2
211        } else {
212            1
213        }
214    }
215
216    #[cfg(test)]
217    fn build_random_graph(rng: &mut StdRng, num_nodes: usize, mean_degree: f32) -> InputGraph {
218        let num_edges = (mean_degree * num_nodes as f32) as usize;
219        let mut result = InputGraph::new();
220        let mut edge_count = 0;
221        loop {
222            let head = rng.gen_range(0, num_nodes);
223            let tail = rng.gen_range(0, num_nodes);
224            // limit max weight, but otherwise allow duplicates, loops etc. to make sure clean-up
225            // inside InputGraph works correctly
226            let weight = rng.gen_range(1, 100);
227            edge_count += result.add_edge(tail, head, weight);
228            if edge_count == num_edges {
229                break;
230            }
231        }
232        result.freeze();
233        result
234    }
235
236    fn read_from_file(filename: &str) -> Self {
237        let file = File::open(filename).unwrap();
238        let reader = BufReader::new(file);
239        let mut g = InputGraph::new();
240        for (index, line) in reader.lines().enumerate() {
241            let s: String = line.unwrap();
242            if s.starts_with("a ") {
243                let (from, to, weight) = InputGraph::read_arc_line(index, &s);
244                g.add_edge(from, to, weight);
245            } else {
246                continue;
247            }
248        }
249        g.freeze();
250        g
251    }
252
253    fn read_from_dimacs(filename: &str) -> Self {
254        let file = File::open(filename).unwrap();
255        let reader = BufReader::new(file);
256        let mut g = InputGraph::new();
257        let mut nodes = 0;
258        let mut edges = 0;
259        let mut curr_edges = 0;
260        let mut found_problem_line = false;
261        for (index, line) in reader.lines().enumerate() {
262            let s: String = line.unwrap();
263            if s.is_empty() || s.starts_with("c") {
264                continue;
265            } else if s.starts_with("p sp ") {
266                if found_problem_line {
267                    panic!(
268                        "There should be only one problem line, but found: {} | {}",
269                        index + 1,
270                        s
271                    );
272                }
273                let mut split = s[5..].split_whitespace();
274                nodes = split.next().unwrap().parse::<usize>().unwrap();
275                edges = split.next().unwrap().parse::<usize>().unwrap();
276                assert!(split.next().is_none(), "Invalid problem line: {}", s);
277                found_problem_line = true;
278            } else if s.starts_with("a ") {
279                assert!(
280                    found_problem_line,
281                    "The problem line must be written before the arc lines"
282                );
283                let (from, to, weight) = InputGraph::read_arc_line(index, &s);
284                assert!(
285                    from <= nodes && to <= nodes,
286                    "Invalid nodes in line: {} | {}",
287                    index + 1,
288                    s
289                );
290                assert!(
291                    curr_edges < edges,
292                    "Too many arc lines: {}, expected: {}",
293                    curr_edges + 1,
294                    edges
295                );
296
297                assert!(
298                    from > 0 && to > 0,
299                    "Invalid arc line: {} | {}",
300                    index + 1,
301                    s
302                );
303                // we convert 1-based node IDs from DIMACS to 0-based node IDs
304                g.add_edge(from - 1, to - 1, weight);
305                curr_edges += 1;
306            } else {
307                panic!(
308                    "Invalid line: {} {}\nAll non-empty lines must start with 'c', 'p' or 'a'",
309                    index, s
310                );
311            }
312        }
313        assert_eq!(
314            curr_edges, edges,
315            "Not enough arc lines: {}, expected: {}",
316            curr_edges, edges
317        );
318        g.freeze();
319        g
320    }
321
322    fn read_arc_line(index: usize, line: &String) -> (usize, usize, usize) {
323        let mut split = line[2..].split_whitespace();
324        let from = split.next().unwrap().parse::<usize>().unwrap();
325        let to = split.next().unwrap().parse::<usize>().unwrap();
326        let weight = split.next().unwrap().parse::<usize>().unwrap();
327        assert!(
328            split.next().is_none(),
329            "Invalid arc line: {} | {}",
330            index + 1,
331            line
332        );
333        (from, to, weight)
334    }
335}
336
337impl fmt::Debug for InputGraph {
338    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
339        write!(f, "{}", self.unit_test_output_string())
340    }
341}
342
343impl Default for InputGraph {
344    fn default() -> Self {
345        Self::new()
346    }
347}
348
349#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
350pub struct Edge {
351    pub from: NodeId,
352    pub to: NodeId,
353    pub weight: Weight,
354}
355
356impl Edge {
357    pub fn new(from: NodeId, to: NodeId, weight: Weight) -> Edge {
358        Edge { from, to, weight }
359    }
360
361    pub fn unit_test_output_string(&self) -> String {
362        return format!("g.add_edge({}, {}, {});", self.from, self.to, self.weight);
363    }
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369
370    #[test]
371    #[should_panic]
372    fn panic_if_not_frozen_get_edges() {
373        let mut g = InputGraph::new();
374        g.add_edge(0, 1, 3);
375        g.get_edges();
376    }
377
378    #[test]
379    #[should_panic]
380    fn panic_if_not_frozen_get_num_edges() {
381        let mut g = InputGraph::new();
382        g.add_edge(0, 1, 3);
383        g.get_num_edges();
384    }
385
386    #[test]
387    #[should_panic]
388    fn panic_if_not_frozen_get_num_nodes() {
389        let mut g = InputGraph::new();
390        g.add_edge(0, 1, 3);
391        g.get_num_nodes();
392    }
393
394    #[test]
395    #[should_panic]
396    fn panic_if_frozen_add_edge() {
397        let mut g = InputGraph::new();
398        g.add_edge(0, 1, 3);
399        g.freeze();
400        g.add_edge(2, 5, 4);
401    }
402
403    #[test]
404    fn freeze_and_thaw() {
405        let mut g = InputGraph::new();
406        g.add_edge(0, 5, 10);
407        g.add_edge(0, 5, 5);
408        g.freeze();
409        assert_eq!(1, g.get_num_edges());
410        g.thaw();
411        g.add_edge(0, 5, 1);
412        g.freeze();
413        assert_eq!(1, g.get_num_edges());
414        assert_eq!(1, g.get_edges()[0].weight);
415    }
416
417    #[test]
418    fn num_nodes() {
419        let mut g = InputGraph::new();
420        g.add_edge(7, 1, 2);
421        g.add_edge(5, 6, 4);
422        g.add_edge(11, 8, 3);
423        g.freeze();
424        assert_eq!(12, g.get_num_nodes());
425    }
426
427    #[test]
428    fn skips_loops() {
429        let mut g = InputGraph::new();
430        g.add_edge(0, 1, 3);
431        g.add_edge(4, 4, 2);
432        g.add_edge(2, 5, 4);
433        g.freeze();
434        assert_eq!(2, g.get_num_edges());
435    }
436
437    #[test]
438    fn skips_zero_weight_edges() {
439        let mut g = InputGraph::new();
440        g.add_edge(0, 1, 5);
441        g.add_edge(1, 2, 0);
442        g.add_edge(2, 3, 3);
443        g.freeze();
444        assert_eq!(2, g.get_num_edges());
445    }
446
447    #[test]
448    fn skips_duplicate_edges() {
449        let mut g = InputGraph::new();
450        g.add_edge(0, 1, 7);
451        g.add_edge(2, 3, 5);
452        g.add_edge(0, 2, 3);
453        g.add_edge(0, 1, 2);
454        g.add_edge(4, 6, 9);
455        g.add_edge(0, 1, 4);
456        g.freeze();
457        assert_eq!(4, g.get_num_edges());
458        // edges should be sorted and duplicates should be removed keeping only the ones with
459        // lowest weight
460        let weights = g
461            .get_edges()
462            .iter()
463            .map(|e| e.weight)
464            .collect::<Vec<Weight>>();
465        assert_eq!(vec![2, 3, 5, 9], weights);
466    }
467
468    #[test]
469    fn skips_duplicate_edges_more() {
470        let mut g = InputGraph::new();
471        g.add_edge(1, 3, 43);
472        g.add_edge(3, 2, 90);
473        g.add_edge(3, 2, 88);
474        g.add_edge(2, 3, 87);
475        g.add_edge(3, 0, 75);
476        g.add_edge(0, 2, 45);
477        g.add_edge(1, 3, 71);
478        g.add_edge(4, 3, 5);
479        g.add_edge(1, 3, 91);
480        g.freeze();
481        assert_eq!(6, g.get_num_edges());
482        let weights = g
483            .get_edges()
484            .iter()
485            .map(|e| e.weight)
486            .collect::<Vec<Weight>>();
487        assert_eq!(vec![45, 43, 87, 75, 88, 5], weights);
488    }
489}